use crate::error::PrivacyError;
use crate::types::{FiveDimCoord, Hyperedge, HypergraphVertex, PrivacyProof, ProofScheme};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
extern crate alloc;
use alloc::{collections::BTreeMap, string::{String, ToString}, vec, vec::Vec};
pub const TSIRELSON_BOUND: f64 = 2.828_427;
pub const CHSH_THRESHOLD: f64 = 2.8;
pub const LYAPUNOV_MIN: f64 = 4.5;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrivacyHypergraph {
pub vertices: BTreeMap<String, HypergraphVertex>,
pub hyperedges: BTreeMap<String, Hyperedge>,
pub genesis: Option<String>,
pub kk_factor: f64,
}
impl PrivacyHypergraph {
pub fn new(chaos_seed: f64) -> Self {
let genesis_id = "genesis".to_string();
let genesis = HypergraphVertex {
id: genesis_id.clone(),
coord: FiveDimCoord::zero(),
commitment: hex::encode(Sha256::digest(b"5d-ezph-genesis")),
expiry_ms: 0,
};
let mut vertices = BTreeMap::new();
vertices.insert(genesis_id.clone(), genesis);
Self {
vertices,
hyperedges: BTreeMap::new(),
genesis: Some(genesis_id),
kk_factor: chaos_seed,
}
}
#[allow(clippy::too_many_arguments)] pub fn encode_vertex(
&mut self,
id: impl Into<String>,
spatial: f64,
temporal: f64,
dp_epsilon: f64,
phase_angle: f64,
chaos_traj: f64,
expiry_ms: u64,
) -> Result<String, PrivacyError> {
let id = id.into();
let coord = FiveDimCoord {
spatial,
temporal,
probabilistic: dp_epsilon,
quantum: phase_angle,
chaotic: chaos_traj,
};
let mut hasher = Sha256::new();
hasher.update(id.as_bytes());
hasher.update(spatial.to_le_bytes());
hasher.update(temporal.to_le_bytes());
hasher.update(dp_epsilon.to_le_bytes());
hasher.update(phase_angle.to_le_bytes());
hasher.update(chaos_traj.to_le_bytes());
let commitment = hex::encode(hasher.finalize());
let vertex = HypergraphVertex { id: id.clone(), coord, commitment, expiry_ms };
self.vertices.insert(id.clone(), vertex);
Ok(id)
}
pub fn form_hyperedge(
&mut self,
id: impl Into<String>,
vertex_ids: Vec<String>,
chaos_perturbation: f64,
) -> Result<String, PrivacyError> {
if vertex_ids.len() < 2 {
return Err(PrivacyError::InvalidDimension(vertex_ids.len()));
}
for vid in &vertex_ids {
if !self.vertices.contains_key(vid) {
return Err(PrivacyError::VertexNotFound(vid.clone()));
}
}
let k = vertex_ids.len() as f64;
let phase_product: f64 = vertex_ids
.iter()
.filter_map(|vid| self.vertices.get(vid))
.map(|v| v.coord.quantum)
.fold(1.0_f64, |acc, phi| acc * phi.cos());
let chsh = (2.0_f64.powf(k / 2.0 - 1.0))
* phase_product.abs()
* self.kk_factor
* (1.0 + chaos_perturbation.abs() * 0.1);
let chsh = chsh.min(TSIRELSON_BOUND);
let id = id.into();
let edge = Hyperedge {
id: id.clone(),
vertices: vertex_ids,
kk_metric: self.kk_factor,
chsh_value: chsh,
};
self.hyperedges.insert(id.clone(), edge);
Ok(id)
}
pub fn traverse_non_local(
&self,
start: &str,
max_hops: usize,
) -> Result<Vec<String>, PrivacyError> {
let mut path = vec![start.to_string()];
let mut current = start.to_string();
for _ in 0..max_hops {
let next_edge = self
.hyperedges
.values()
.filter(|e| e.vertices.contains(¤t) && e.chsh_value > CHSH_THRESHOLD)
.max_by(|a, b| a.chsh_value.partial_cmp(&b.chsh_value).unwrap());
match next_edge {
Some(edge) => {
let next = edge
.vertices
.iter()
.filter(|v| **v != current)
.max_by(|a, b| {
let pa = self.vertices.get(*a).map(|v| v.coord.quantum).unwrap_or(0.0);
let pb = self.vertices.get(*b).map(|v| v.coord.quantum).unwrap_or(0.0);
pa.partial_cmp(&pb).unwrap()
});
match next {
Some(n) => {
current = n.clone();
path.push(current.clone());
}
None => break,
}
}
None => break,
}
}
Ok(path)
}
pub fn prove_non_locality(&self) -> Result<PrivacyProof, PrivacyError> {
let max_chsh = self
.hyperedges
.values()
.map(|e| e.chsh_value)
.fold(0.0_f64, f64::max);
if max_chsh <= CHSH_THRESHOLD {
return Err(PrivacyError::ChshBoundViolation {
got: max_chsh,
threshold: CHSH_THRESHOLD,
});
}
let mut hasher = Sha256::new();
hasher.update((self.vertices.len() as u64).to_le_bytes());
hasher.update((self.hyperedges.len() as u64).to_le_bytes());
hasher.update(max_chsh.to_le_bytes());
hasher.update(self.kk_factor.to_le_bytes());
let commitment = hex::encode(hasher.finalize());
Ok(PrivacyProof {
proof_bytes: commitment.clone(),
public_inputs: hex::encode(max_chsh.to_le_bytes()),
scheme: ProofScheme::Snark,
security_bits: 128,
proof_size: commitment.len(),
chsh_value: max_chsh,
lyapunov: LYAPUNOV_MIN,
})
}
pub fn prune_expired(&mut self, now_ms: u64) {
let expired: Vec<String> = self
.vertices
.iter()
.filter(|(_, v)| v.expiry_ms > 0 && now_ms > v.expiry_ms)
.map(|(id, _)| id.clone())
.collect();
for id in &expired {
self.vertices.remove(id);
self.hyperedges.retain(|_, e| !e.vertices.contains(id));
}
}
pub fn vertex_count(&self) -> usize {
self.vertices.len()
}
pub fn edge_count(&self) -> usize {
self.hyperedges.len()
}
}
pub fn hypergraph_chsh_value(graph_json: &str) -> Result<f64, PrivacyError> {
let graph: PrivacyHypergraph = serde_json::from_str(graph_json)
.map_err(|e| PrivacyError::SerializationError(e.to_string()))?;
let mut chsh_vals: Vec<f64> = graph
.hyperedges
.values()
.map(|e| e.chsh_value)
.collect();
chsh_vals.sort_by(|a, b| b.partial_cmp(a).unwrap_or(core::cmp::Ordering::Equal));
const BELL: f64 = core::f64::consts::FRAC_1_SQRT_2;
while chsh_vals.len() < 4 {
chsh_vals.push(BELL);
}
let e_ab = chsh_vals[0];
let e_ab2 = chsh_vals[1];
let e_a2b = chsh_vals[2];
let e_a2b2 = chsh_vals[3];
let s = (e_ab + e_ab2 + e_a2b - e_a2b2).abs();
Ok(s)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_genesis_vertex() {
let g = PrivacyHypergraph::new(1.0);
assert_eq!(g.vertex_count(), 1);
assert!(g.genesis.is_some());
}
#[test]
fn test_encode_vertex() {
let mut g = PrivacyHypergraph::new(1.0);
let id = g.encode_vertex("v1", 0.5, 1000.0, 1e-6, 1.2, 0.8, 0).unwrap();
assert_eq!(id, "v1");
assert_eq!(g.vertex_count(), 2);
}
#[test]
fn test_hyperedge_chsh() {
let mut g = PrivacyHypergraph::new(3.0);
g.encode_vertex("v1", 0.5, 1000.0, 1e-6, core::f64::consts::PI / 4.0, 0.8, 0).unwrap();
g.encode_vertex("v2", 0.7, 1001.0, 1e-6, core::f64::consts::PI / 3.0, 0.9, 0).unwrap();
let eid = g.form_hyperedge("e1", vec!["v1".into(), "v2".into()], 0.5).unwrap();
let edge = g.hyperedges.get(&eid).unwrap();
assert!(edge.chsh_value > 0.0);
}
}