1use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub struct HolographicCoords {
17 pub galaxy: u8,
19 pub sector: u16,
21 pub radial: f32,
23 pub angular: f32,
25 pub temporal: u64,
27 pub consciousness: f32,
29}
30
31impl HolographicCoords {
32 #[must_use]
34 pub fn new(galaxy: crate::Galaxy, temporal: u64) -> Self {
35 use std::time::{SystemTime, UNIX_EPOCH};
36 let now = SystemTime::now()
37 .duration_since(UNIX_EPOCH)
38 .unwrap_or_default()
39 .as_micros() as u64;
40 Self {
41 galaxy: galaxy as u8,
42 sector: 0,
43 radial: 0.5,
44 angular: 0.0,
45 temporal: temporal.max(now),
46 consciousness: 0.5,
47 }
48 }
49
50 #[must_use]
55 pub fn encode_key(&self) -> Vec<u8> {
56 let mut key = Vec::with_capacity(23);
57 key.push(self.galaxy);
58 key.extend_from_slice(&self.sector.to_be_bytes());
59 key.extend_from_slice(&self.temporal.to_be_bytes());
60 key.extend_from_slice(&self.radial.to_be_bytes());
61 key.extend_from_slice(&self.angular.to_be_bytes());
62 key.extend_from_slice(&self.consciousness.to_be_bytes());
63 key
64 }
65
66 #[must_use]
68 pub const fn decode_key(key: &[u8]) -> Option<Self> {
69 if key.len() < 23 {
70 return None;
71 }
72 Some(Self {
73 galaxy: key[0],
74 sector: u16::from_be_bytes([key[1], key[2]]),
75 temporal: u64::from_be_bytes([
76 key[3], key[4], key[5], key[6], key[7], key[8], key[9], key[10],
77 ]),
78 radial: f32::from_be_bytes([key[11], key[12], key[13], key[14]]),
79 angular: f32::from_be_bytes([key[15], key[16], key[17], key[18]]),
80 consciousness: f32::from_be_bytes([key[19], key[20], key[21], key[22]]),
81 })
82 }
83
84 #[must_use]
89 pub fn distance_to(&self, other: &Self) -> f32 {
90 let radial_diff = (self.radial - other.radial).abs();
91 let angular_diff = {
92 let d = (self.angular - other.angular).abs();
93 let pi2 = std::f32::consts::TAU;
94 d.min(pi2 - d) / pi2 };
96 let temporal_diff = {
97 let max_ts = self.temporal.max(other.temporal);
98 let min_ts = self.temporal.min(other.temporal);
99 if max_ts == 0 {
100 0.0
101 } else {
102 (max_ts - min_ts) as f32 / max_ts as f32
103 }
104 };
105 let consciousness_diff = (self.consciousness - other.consciousness).abs();
106
107 consciousness_diff.mul_add(
109 0.2,
110 radial_diff.mul_add(0.3, angular_diff * 0.2) + temporal_diff * 0.3,
111 )
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
122pub enum Zone {
123 Core,
125 InnerRing,
127 MidRing,
129 OuterRing,
131 FarEdge,
133}
134
135impl Zone {
136 #[must_use]
138 pub fn from_radial(radial: f32) -> Self {
139 match radial {
140 r if r < 0.2 => Self::Core,
141 r if r < 0.4 => Self::InnerRing,
142 r if r < 0.6 => Self::MidRing,
143 r if r < 0.8 => Self::OuterRing,
144 _ => Self::FarEdge,
145 }
146 }
147
148 #[must_use]
150 pub const fn index(self) -> u8 {
151 self as u8
152 }
153
154 #[must_use]
156 pub const fn name(self) -> &'static str {
157 match self {
158 Self::Core => "core",
159 Self::InnerRing => "inner_ring",
160 Self::MidRing => "mid_ring",
161 Self::OuterRing => "outer_ring",
162 Self::FarEdge => "far_edge",
163 }
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct Coordinate5D {
180 pub x: f32,
182 pub y: f32,
184 pub z: f32,
186 pub w: f32,
188 pub v: f32,
190}
191
192impl Coordinate5D {
193 #[must_use]
195 pub const fn new(x: f32, y: f32, z: f32, w: f32, v: f32) -> Self {
196 Self {
197 x: x.clamp(0.0, 1.0),
198 y: y.clamp(0.0, 1.0),
199 z: z.clamp(0.0, 1.0),
200 w: w.clamp(0.0, 1.0),
201 v: v.clamp(0.0, 1.0),
202 }
203 }
204
205 #[must_use]
212 pub fn encode(text: &str) -> Self {
213 use sha2::{Digest, Sha256};
214 let hash = Sha256::digest(text.as_bytes());
215 let x = u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) as f32 / u32::MAX as f32;
216 let y = u32::from_be_bytes([hash[4], hash[5], hash[6], hash[7]]) as f32 / u32::MAX as f32;
217 let z = u32::from_be_bytes([hash[8], hash[9], hash[10], hash[11]]) as f32 / u32::MAX as f32;
218 Self {
219 x,
220 y,
221 z,
222 w: 0.5,
223 v: 0.5,
224 }
225 }
226
227 #[must_use]
234 pub const fn from_semantic(
235 x: f32,
236 y: f32,
237 z: f32,
238 temporal_weight: f32,
239 importance: f32,
240 ) -> Self {
241 Self {
242 x: x.clamp(0.0, 1.0),
243 y: y.clamp(0.0, 1.0),
244 z: z.clamp(0.0, 1.0),
245 w: temporal_weight.clamp(0.0, 1.0),
246 v: importance.clamp(0.0, 1.0),
247 }
248 }
249
250 #[must_use]
252 pub fn encode_with_context(text: &str, temporal_weight: f32, importance: f32) -> Self {
253 let mut coord = Self::encode(text);
254 coord.w = temporal_weight.clamp(0.0, 1.0);
255 coord.v = importance.clamp(0.0, 1.0);
256 coord
257 }
258
259 #[must_use]
261 pub fn distance_to(&self, other: &Self) -> f32 {
262 let dx = self.x - other.x;
263 let dy = self.y - other.y;
264 let dz = self.z - other.z;
265 let dw = self.w - other.w;
266 let dv = self.v - other.v;
267 dv.mul_add(dv, dw.mul_add(dw, dz.mul_add(dz, dx.mul_add(dx, dy * dy))))
268 .sqrt()
269 }
270
271 #[must_use]
274 pub fn semantic_distance_to(&self, other: &Self) -> f32 {
275 let dx = (self.x - other.x) * 0.35;
276 let dy = (self.y - other.y) * 0.35;
277 let dz = (self.z - other.z) * 0.20;
278 let dw = (self.w - other.w) * 0.05;
279 let dv = (self.v - other.v) * 0.05;
280 (dx * dx + dy * dy + dz * dz + dw * dw + dv * dv).sqrt()
281 }
282
283 #[must_use]
286 pub fn zone(&self) -> Zone {
287 let center = Self::new(0.5, 0.5, 0.5, 0.5, 0.5);
288 let radial = self.distance_to(¢er) / 1.118; Zone::from_radial(radial)
290 }
291
292 #[must_use]
294 pub fn encode_key(&self) -> Vec<u8> {
295 let mut key = Vec::with_capacity(20);
296 key.extend_from_slice(&self.x.to_be_bytes());
297 key.extend_from_slice(&self.y.to_be_bytes());
298 key.extend_from_slice(&self.z.to_be_bytes());
299 key.extend_from_slice(&self.w.to_be_bytes());
300 key.extend_from_slice(&self.v.to_be_bytes());
301 key
302 }
303
304 #[must_use]
306 pub const fn decode_key(key: &[u8]) -> Option<Self> {
307 if key.len() < 20 {
308 return None;
309 }
310 Some(Self {
311 x: f32::from_be_bytes([key[0], key[1], key[2], key[3]]),
312 y: f32::from_be_bytes([key[4], key[5], key[6], key[7]]),
313 z: f32::from_be_bytes([key[8], key[9], key[10], key[11]]),
314 w: f32::from_be_bytes([key[12], key[13], key[14], key[15]]),
315 v: f32::from_be_bytes([key[16], key[17], key[18], key[19]]),
316 })
317 }
318}
319
320#[must_use]
325pub fn find_nearby(
326 center: &Coordinate5D,
327 candidates: &[(usize, Coordinate5D)],
328 radius: f32,
329) -> Vec<(usize, f32)> {
330 let mut results: Vec<(usize, f32)> = candidates
331 .iter()
332 .map(|(idx, coord)| (*idx, center.distance_to(coord)))
333 .filter(|(_, dist)| *dist <= radius)
334 .collect();
335 results.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
336 results
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn coords_encode_decode_roundtrip() {
345 let coords = HolographicCoords {
346 galaxy: 3,
347 sector: 42,
348 radial: 0.75,
349 angular: 1.5,
350 temporal: 1700000000,
351 consciousness: 0.8,
352 };
353 let key = coords.encode_key();
354 let decoded = HolographicCoords::decode_key(&key).unwrap();
355 assert_eq!(coords.galaxy, decoded.galaxy);
356 assert_eq!(coords.sector, decoded.sector);
357 assert_eq!(coords.temporal, decoded.temporal);
358 assert!((coords.radial - decoded.radial).abs() < f32::EPSILON);
359 }
360
361 #[test]
362 fn coords_distance_identical() {
363 let coords = HolographicCoords {
364 galaxy: 0,
365 sector: 0,
366 radial: 0.5,
367 angular: 0.0,
368 temporal: 1000,
369 consciousness: 0.5,
370 };
371 assert_eq!(coords.distance_to(&coords), 0.0);
372 }
373
374 #[test]
375 fn coords_distance_different_galaxies() {
376 let a = HolographicCoords {
377 galaxy: 0,
378 sector: 0,
379 radial: 0.5,
380 angular: 0.0,
381 temporal: 1000,
382 consciousness: 0.5,
383 };
384 let b = HolographicCoords {
385 galaxy: 1,
386 sector: 0,
387 radial: 0.5,
388 angular: 0.0,
389 temporal: 1000,
390 consciousness: 0.5,
391 };
392 assert_eq!(a.distance_to(&b), 0.0);
395 }
396
397 #[test]
400 fn coord5d_encode_deterministic() {
401 let a = Coordinate5D::encode("hello world");
402 let b = Coordinate5D::encode("hello world");
403 assert_eq!(a, b);
404 }
405
406 #[test]
407 fn coord5d_encode_different_text() {
408 let a = Coordinate5D::encode("hello world");
409 let b = Coordinate5D::encode("goodbye world");
410 assert_ne!(a, b);
411 }
412
413 #[test]
414 fn coord5d_encode_in_range() {
415 let coord = Coordinate5D::encode("test content");
416 assert!(coord.x >= 0.0 && coord.x <= 1.0);
417 assert!(coord.y >= 0.0 && coord.y <= 1.0);
418 assert!(coord.z >= 0.0 && coord.z <= 1.0);
419 }
420
421 #[test]
422 fn coord5d_encode_with_context() {
423 let coord = Coordinate5D::encode_with_context("test", 0.8, 0.9);
424 assert!((coord.w - 0.8).abs() < f32::EPSILON);
425 assert!((coord.v - 0.9).abs() < f32::EPSILON);
426 }
427
428 #[test]
429 fn coord5d_distance_identical() {
430 let coord = Coordinate5D::encode("test");
431 assert!((coord.distance_to(&coord)).abs() < f32::EPSILON);
432 }
433
434 #[test]
435 fn coord5d_distance_positive() {
436 let a = Coordinate5D::new(0.0, 0.0, 0.0, 0.0, 0.0);
437 let b = Coordinate5D::new(1.0, 1.0, 1.0, 1.0, 1.0);
438 let dist = a.distance_to(&b);
439 assert!(dist > 0.0);
440 assert!((dist - 2.236068).abs() < 0.001);
442 }
443
444 #[test]
445 fn coord5d_semantic_distance_weighted() {
446 let a = Coordinate5D::new(0.0, 0.0, 0.0, 0.0, 0.0);
447 let b = Coordinate5D::new(1.0, 0.0, 0.0, 0.0, 0.0);
448 let c = Coordinate5D::new(0.0, 0.0, 0.0, 1.0, 0.0);
449 let dist_x = a.semantic_distance_to(&b);
450 let dist_w = a.semantic_distance_to(&c);
451 assert!(dist_x > dist_w);
453 }
454
455 #[test]
456 fn coord5d_key_roundtrip() {
457 let coord = Coordinate5D::new(0.1, 0.2, 0.3, 0.4, 0.5);
458 let key = coord.encode_key();
459 let decoded = Coordinate5D::decode_key(&key).unwrap();
460 assert!((coord.x - decoded.x).abs() < f32::EPSILON);
461 assert!((coord.y - decoded.y).abs() < f32::EPSILON);
462 assert!((coord.z - decoded.z).abs() < f32::EPSILON);
463 assert!((coord.w - decoded.w).abs() < f32::EPSILON);
464 assert!((coord.v - decoded.v).abs() < f32::EPSILON);
465 }
466
467 #[test]
468 fn zone_from_radial() {
469 assert_eq!(Zone::from_radial(0.1), Zone::Core);
470 assert_eq!(Zone::from_radial(0.3), Zone::InnerRing);
471 assert_eq!(Zone::from_radial(0.5), Zone::MidRing);
472 assert_eq!(Zone::from_radial(0.7), Zone::OuterRing);
473 assert_eq!(Zone::from_radial(0.9), Zone::FarEdge);
474 }
475
476 #[test]
477 fn zone_name_and_index() {
478 assert_eq!(Zone::Core.name(), "core");
479 assert_eq!(Zone::Core.index(), 0);
480 assert_eq!(Zone::FarEdge.name(), "far_edge");
481 assert_eq!(Zone::FarEdge.index(), 4);
482 }
483
484 #[test]
485 fn coord5d_zone_classification() {
486 let center = Coordinate5D::new(0.5, 0.5, 0.5, 0.5, 0.5);
487 assert_eq!(center.zone(), Zone::Core);
488 let edge = Coordinate5D::new(0.0, 0.0, 0.0, 0.0, 0.0);
489 assert_eq!(edge.zone(), Zone::FarEdge);
490 }
491
492 #[test]
493 fn find_nearby_returns_sorted() {
494 let center = Coordinate5D::new(0.5, 0.5, 0.5, 0.5, 0.5);
495 let candidates = vec![
496 (0, Coordinate5D::new(0.9, 0.5, 0.5, 0.5, 0.5)), (1, Coordinate5D::new(0.51, 0.5, 0.5, 0.5, 0.5)), (2, Coordinate5D::new(0.0, 0.0, 0.0, 0.0, 0.0)), (3, Coordinate5D::new(0.5, 0.5, 0.5, 0.5, 0.5)), ];
501 let results = find_nearby(¢er, &candidates, 1.0);
502 assert_eq!(results.len(), 3); assert_eq!(results[0].0, 3); assert_eq!(results[1].0, 1); assert_eq!(results[2].0, 0); }
507
508 #[test]
509 fn find_nearby_empty_for_tiny_radius() {
510 let center = Coordinate5D::new(0.5, 0.5, 0.5, 0.5, 0.5);
511 let candidates = vec![(0, Coordinate5D::new(0.9, 0.5, 0.5, 0.5, 0.5))];
512 let results = find_nearby(¢er, &candidates, 0.01);
513 assert!(results.is_empty());
514 }
515}