use super::super::PrivacyBudget;
use crate::error::{OptimError, Result};
use scirs2_core::numeric::Float;
use std::collections::HashMap;
use std::fmt::Debug;
#[derive(Debug, Clone, Default)]
pub struct CrossDeviceConfig {
pub user_level_privacy: bool,
pub device_clustering: bool,
pub temporal_privacy: bool,
pub geographic_privacy: bool,
pub demographic_privacy: bool,
}
pub struct CrossDevicePrivacyManager<T: Float + Debug + Send + Sync + 'static> {
config: CrossDeviceConfig,
user_clusters: HashMap<String, Vec<String>>,
device_profiles: HashMap<String, DeviceProfile<T>>,
temporal_correlations: HashMap<String, Vec<TemporalEvent>>,
}
#[derive(Debug, Clone)]
pub struct DeviceProfile<T: Float + Debug + Send + Sync + 'static> {
pub device_id: String,
pub user_id: String,
pub device_type: DeviceType,
pub location_cluster: String,
pub participation_frequency: f64,
pub local_privacy_budget: PrivacyBudget,
pub sensitivity_estimate: T,
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Default)]
pub enum DeviceType {
#[default]
Mobile,
Desktop,
IoT,
Edge,
Server,
}
#[derive(Debug, Clone)]
pub struct TemporalEvent {
pub timestamp: u64,
pub event_type: TemporalEventType,
pub privacy_impact: f64,
}
#[derive(Debug, Clone)]
pub enum TemporalEventType {
ClientParticipation,
ModelUpdate,
PrivacyBudgetConsumption,
AggregationEvent,
}
impl<T: Float + Debug + Send + Sync + 'static> CrossDevicePrivacyManager<T> {
pub fn new(config: CrossDeviceConfig) -> Self {
Self {
config,
user_clusters: HashMap::new(),
device_profiles: HashMap::new(),
temporal_correlations: HashMap::new(),
}
}
pub fn update_participation(&mut self, clientid: String, round: usize) {
if let Some(profile) = self.device_profiles.get_mut(&clientid) {
profile.participation_frequency += 0.1; } else {
let profile = DeviceProfile {
device_id: clientid.clone(),
user_id: clientid.clone(), device_type: DeviceType::Mobile, location_cluster: "default".to_string(),
participation_frequency: 1.0,
local_privacy_budget: PrivacyBudget::default(),
sensitivity_estimate: T::one(),
};
self.device_profiles.insert(clientid.clone(), profile);
}
self.temporal_correlations
.entry(clientid)
.or_default()
.push(TemporalEvent {
timestamp: round as u64, event_type: TemporalEventType::ClientParticipation,
privacy_impact: 1.0,
});
}
pub fn get_device_profile(&self, client_id: &str) -> Option<&DeviceProfile<T>> {
self.device_profiles.get(client_id)
}
pub fn get_temporal_correlations(&self, client_id: &str) -> Option<&Vec<TemporalEvent>> {
self.temporal_correlations.get(client_id)
}
pub fn create_user_cluster(&mut self, user_id: String, device_ids: Vec<String>) {
self.user_clusters.insert(user_id, device_ids);
}
pub fn get_user_cluster(&self, user_id: &str) -> Option<&Vec<String>> {
self.user_clusters.get(user_id)
}
pub fn is_user_level_privacy_enabled(&self) -> bool {
self.config.user_level_privacy
}
pub fn is_device_clustering_enabled(&self) -> bool {
self.config.device_clustering
}
pub fn is_temporal_privacy_enabled(&self) -> bool {
self.config.temporal_privacy
}
pub fn config(&self) -> &CrossDeviceConfig {
&self.config
}
pub fn device_count(&self) -> usize {
self.device_profiles.len()
}
pub fn get_participation_frequency(&self, client_id: &str) -> Option<f64> {
self.device_profiles
.get(client_id)
.map(|p| p.participation_frequency)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cross_device_config() {
let config = CrossDeviceConfig::default();
assert!(!config.user_level_privacy);
assert!(!config.device_clustering);
assert!(!config.temporal_privacy);
}
#[test]
fn test_device_profile_creation() {
let profile = DeviceProfile {
device_id: "device_1".to_string(),
user_id: "user_1".to_string(),
device_type: DeviceType::Mobile,
location_cluster: "cluster_a".to_string(),
participation_frequency: 0.5,
local_privacy_budget: PrivacyBudget::default(),
sensitivity_estimate: 1.0f64,
};
assert_eq!(profile.device_id, "device_1");
assert!(matches!(profile.device_type, DeviceType::Mobile));
}
#[test]
fn test_cross_device_manager() {
let config = CrossDeviceConfig::default();
let mut manager = CrossDevicePrivacyManager::<f64>::new(config);
assert_eq!(manager.device_count(), 0);
manager.update_participation("device1".to_string(), 1);
assert_eq!(manager.device_count(), 1);
let frequency = manager.get_participation_frequency("device1");
assert!(frequency.is_some());
assert_eq!(frequency.expect("unwrap failed"), 1.0);
}
#[test]
fn test_user_clustering() {
let config = CrossDeviceConfig::default();
let mut manager = CrossDevicePrivacyManager::<f64>::new(config);
let user_id = "user1".to_string();
let devices = vec!["device1".to_string(), "device2".to_string()];
manager.create_user_cluster(user_id.clone(), devices.clone());
let cluster = manager.get_user_cluster(&user_id);
assert!(cluster.is_some());
assert_eq!(cluster.expect("unwrap failed").len(), 2);
}
#[test]
fn test_temporal_events() {
let config = CrossDeviceConfig::default();
let mut manager = CrossDevicePrivacyManager::<f64>::new(config);
manager.update_participation("device1".to_string(), 1);
manager.update_participation("device1".to_string(), 2);
let events = manager.get_temporal_correlations("device1");
assert!(events.is_some());
assert_eq!(events.expect("unwrap failed").len(), 2);
}
}