1use serde::{Deserialize, Serialize};
30use std::ops::{Mul, Neg};
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
34#[repr(i8)]
35pub enum Trit {
36 Neg = -1,
37 Zero = 0,
38 Pos = 1,
39}
40
41impl Trit {
42 #[inline]
44 #[allow(clippy::should_implement_trait)]
45 pub fn mul(self, other: Trit) -> Trit {
46 match (self, other) {
47 (Trit::Zero, _) | (_, Trit::Zero) => Trit::Zero,
48 (Trit::Pos, Trit::Pos) | (Trit::Neg, Trit::Neg) => Trit::Pos,
49 (Trit::Pos, Trit::Neg) | (Trit::Neg, Trit::Pos) => Trit::Neg,
50 }
51 }
52
53 #[inline]
55 pub fn add_with_carry(self, other: Trit, carry_in: Trit) -> (Trit, Trit) {
56 let sum = (self as i8) + (other as i8) + (carry_in as i8);
57 match sum {
58 -3 => (Trit::Zero, Trit::Neg),
59 -2 => (Trit::Pos, Trit::Neg),
60 -1 => (Trit::Neg, Trit::Zero),
61 0 => (Trit::Zero, Trit::Zero),
62 1 => (Trit::Pos, Trit::Zero),
63 2 => (Trit::Neg, Trit::Pos),
64 3 => (Trit::Zero, Trit::Pos),
65 _ => unreachable!(),
66 }
67 }
68
69 #[inline]
71 #[allow(clippy::should_implement_trait)]
72 pub fn neg(self) -> Trit {
73 match self {
74 Trit::Neg => Trit::Pos,
75 Trit::Zero => Trit::Zero,
76 Trit::Pos => Trit::Neg,
77 }
78 }
79
80 #[inline]
82 pub fn from_i8(v: i8) -> Self {
83 match v.signum() {
84 -1 => Trit::Neg,
85 0 => Trit::Zero,
86 1 => Trit::Pos,
87 _ => unreachable!(),
88 }
89 }
90
91 #[inline]
93 pub fn to_i8(self) -> i8 {
94 self as i8
95 }
96}
97
98impl Neg for Trit {
99 type Output = Trit;
100 fn neg(self) -> Trit {
101 Trit::neg(self)
102 }
103}
104
105impl Mul for Trit {
106 type Output = Trit;
107 fn mul(self, rhs: Trit) -> Trit {
108 Trit::mul(self, rhs)
109 }
110}
111
112#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
115pub struct Tryte {
116 pub trits: Vec<Trit>,
118}
119
120impl Tryte {
121 pub fn zero(num_trits: usize) -> Self {
123 Tryte {
124 trits: vec![Trit::Zero; num_trits],
125 }
126 }
127
128 pub fn from_i64(mut value: i64, num_trits: usize) -> Self {
130 let mut trits = Vec::with_capacity(num_trits);
131 let negative = value < 0;
132 if negative {
133 value = -value;
134 }
135
136 for _ in 0..num_trits {
137 let remainder = (value % 3) as i8;
138 value /= 3;
139
140 let trit = match remainder {
142 0 => Trit::Zero,
143 1 => Trit::Pos,
144 2 => {
145 value += 1; Trit::Neg
147 }
148 _ => unreachable!(),
149 };
150 trits.push(if negative { -trit } else { trit });
151 }
152
153 Tryte { trits }
154 }
155
156 pub fn to_i64(&self) -> i64 {
158 let mut result: i64 = 0;
159 let mut power: i64 = 1;
160
161 for trit in &self.trits {
162 result += trit.to_i8() as i64 * power;
163 power *= 3;
164 }
165
166 result
167 }
168
169 pub fn len(&self) -> usize {
171 self.trits.len()
172 }
173
174 pub fn is_empty(&self) -> bool {
176 self.trits.is_empty()
177 }
178
179 pub fn max_value(num_trits: usize) -> i64 {
181 (3i64.pow(num_trits as u32) - 1) / 2
182 }
183
184 pub fn min_value(num_trits: usize) -> i64 {
186 -Self::max_value(num_trits)
187 }
188
189 pub fn bind(&self, other: &Tryte) -> Tryte {
191 assert_eq!(self.len(), other.len(), "Tryte sizes must match for bind");
192 let trits = self
193 .trits
194 .iter()
195 .zip(other.trits.iter())
196 .map(|(&a, &b)| a * b)
197 .collect();
198 Tryte { trits }
199 }
200
201 pub fn bundle(&self, other: &Tryte) -> Tryte {
203 assert_eq!(self.len(), other.len(), "Tryte sizes must match for bundle");
204 let trits = self
205 .trits
206 .iter()
207 .zip(other.trits.iter())
208 .map(|(&a, &b)| {
209 let sum = a.to_i8() + b.to_i8();
210 Trit::from_i8(sum.signum())
211 })
212 .collect();
213 Tryte { trits }
214 }
215
216 pub fn dot(&self, other: &Tryte) -> i64 {
218 self.trits
219 .iter()
220 .zip(other.trits.iter())
221 .map(|(&a, &b)| (a.to_i8() * b.to_i8()) as i64)
222 .sum()
223 }
224
225 pub fn norm_squared(&self) -> i64 {
227 self.trits
228 .iter()
229 .map(|&t| if t == Trit::Zero { 0 } else { 1 })
230 .sum()
231 }
232}
233
234#[derive(Clone, Debug, Serialize, Deserialize)]
236pub struct DimensionalConfig {
237 pub num_dimensions: usize,
239
240 pub trit_depth: TritDepthConfig,
242
243 pub target_sparsity: f64,
245
246 pub adaptive_precision: bool,
248}
249
250#[derive(Clone, Debug, Serialize, Deserialize)]
252pub enum TritDepthConfig {
253 Uniform(u8),
255
256 Variable(Vec<u8>),
258
259 Adaptive { base_depth: u8, max_depth: u8 },
261}
262
263impl Default for DimensionalConfig {
264 fn default() -> Self {
265 DimensionalConfig {
266 num_dimensions: 10_000,
267 trit_depth: TritDepthConfig::Uniform(6), target_sparsity: 0.02, adaptive_precision: false,
270 }
271 }
272}
273
274impl DimensionalConfig {
275 pub fn high_precision() -> Self {
277 DimensionalConfig {
278 num_dimensions: 100_000,
279 trit_depth: TritDepthConfig::Uniform(8), target_sparsity: 0.002,
281 adaptive_precision: true,
282 }
283 }
284
285 pub fn compact() -> Self {
287 DimensionalConfig {
288 num_dimensions: 4_096,
289 trit_depth: TritDepthConfig::Uniform(4), target_sparsity: 0.05,
291 adaptive_precision: false,
292 }
293 }
294
295 pub fn adaptive(num_dims: usize, base_depth: u8, max_depth: u8) -> Self {
297 DimensionalConfig {
298 num_dimensions: num_dims,
299 trit_depth: TritDepthConfig::Adaptive {
300 base_depth,
301 max_depth,
302 },
303 target_sparsity: 200.0 / num_dims as f64,
304 adaptive_precision: true,
305 }
306 }
307
308 pub fn depth_for_dimension(&self, dim: usize) -> u8 {
310 match &self.trit_depth {
311 TritDepthConfig::Uniform(d) => *d,
312 TritDepthConfig::Variable(depths) => depths.get(dim).copied().unwrap_or(6),
313 TritDepthConfig::Adaptive { base_depth, .. } => *base_depth,
314 }
315 }
316
317 pub fn total_capacity_bits(&self) -> f64 {
319 let log2_3: f64 = 3.0f64.log2(); match &self.trit_depth {
322 TritDepthConfig::Uniform(d) => self.num_dimensions as f64 * (*d as f64) * log2_3,
323 TritDepthConfig::Variable(depths) => depths.iter().map(|&d| d as f64 * log2_3).sum(),
324 TritDepthConfig::Adaptive { base_depth, .. } => {
325 self.num_dimensions as f64 * (*base_depth as f64) * log2_3
326 }
327 }
328 }
329
330 pub fn expected_storage_bytes(&self) -> usize {
332 let non_zero_dims = (self.num_dimensions as f64 * self.target_sparsity) as usize;
333 let avg_depth = match &self.trit_depth {
334 TritDepthConfig::Uniform(d) => *d as usize,
335 TritDepthConfig::Variable(depths) => {
336 depths.iter().map(|&d| d as usize).sum::<usize>() / depths.len().max(1)
337 }
338 TritDepthConfig::Adaptive { base_depth, .. } => *base_depth as usize,
339 };
340
341 non_zero_dims * (4 + (avg_depth * 2).div_ceil(8))
343 }
344}
345
346#[derive(Clone, Debug, Serialize, Deserialize)]
348pub struct HyperVec {
349 pub config: DimensionalConfig,
351
352 pub dimensions: std::collections::BTreeMap<usize, Tryte>,
355}
356
357impl HyperVec {
358 pub fn new(config: DimensionalConfig) -> Self {
360 HyperVec {
361 config,
362 dimensions: std::collections::BTreeMap::new(),
363 }
364 }
365
366 pub fn from_dense(config: DimensionalConfig, values: &[i64]) -> Self {
368 let mut vec = HyperVec::new(config.clone());
369
370 for (dim, &value) in values.iter().enumerate() {
371 if dim >= config.num_dimensions {
372 break;
373 }
374 if value != 0 {
375 let depth = config.depth_for_dimension(dim);
376 let tryte = Tryte::from_i64(value, depth as usize);
377 vec.dimensions.insert(dim, tryte);
378 }
379 }
380
381 vec
382 }
383
384 pub fn get(&self, dim: usize) -> i64 {
386 self.dimensions.get(&dim).map(|t| t.to_i64()).unwrap_or(0)
387 }
388
389 pub fn set(&mut self, dim: usize, value: i64) {
391 if value == 0 {
392 self.dimensions.remove(&dim);
393 } else {
394 let depth = self.config.depth_for_dimension(dim);
395 let tryte = Tryte::from_i64(value, depth as usize);
396 self.dimensions.insert(dim, tryte);
397 }
398 }
399
400 pub fn nnz(&self) -> usize {
402 self.dimensions.len()
403 }
404
405 pub fn sparsity(&self) -> f64 {
407 self.dimensions.len() as f64 / self.config.num_dimensions as f64
408 }
409
410 pub fn bundle(&self, other: &HyperVec) -> HyperVec {
417 assert_eq!(
418 self.config.num_dimensions, other.config.num_dimensions,
419 "Dimension count must match for bundle"
420 );
421
422 let mut result = HyperVec::new(self.config.clone());
423
424 let all_dims: std::collections::BTreeSet<_> = self
426 .dimensions
427 .keys()
428 .chain(other.dimensions.keys())
429 .copied()
430 .collect();
431
432 for dim in all_dims {
433 let depth = self.config.depth_for_dimension(dim);
434 let zero = Tryte::zero(depth as usize);
435
436 let a = self.dimensions.get(&dim).unwrap_or(&zero);
437 let b = other.dimensions.get(&dim).unwrap_or(&zero);
438
439 let bundled = a.bundle(b);
440
441 if bundled.to_i64() != 0 {
443 result.dimensions.insert(dim, bundled);
444 }
445 }
446
447 result
448 }
449
450 pub fn bind(&self, other: &HyperVec) -> HyperVec {
457 assert_eq!(
458 self.config.num_dimensions, other.config.num_dimensions,
459 "Dimension count must match for bind"
460 );
461
462 let mut result = HyperVec::new(self.config.clone());
463
464 for (dim, tryte_a) in &self.dimensions {
466 if let Some(tryte_b) = other.dimensions.get(dim) {
467 let bound = tryte_a.bind(tryte_b);
468 if bound.to_i64() != 0 {
469 result.dimensions.insert(*dim, bound);
470 }
471 }
472 }
473
474 result
475 }
476
477 pub fn permute(&self, shift: usize) -> HyperVec {
484 let mut result = HyperVec::new(self.config.clone());
485 let n = self.config.num_dimensions;
486
487 for (&dim, tryte) in &self.dimensions {
488 let new_dim = (dim + shift) % n;
489 result.dimensions.insert(new_dim, tryte.clone());
490 }
491
492 result
493 }
494
495 pub fn inverse_permute(&self, shift: usize) -> HyperVec {
497 let n = self.config.num_dimensions;
498 self.permute(n - (shift % n))
499 }
500
501 pub fn cosine(&self, other: &HyperVec) -> f64 {
508 let mut dot: i64 = 0;
509 let mut norm_a: i64 = 0;
510 let mut norm_b: i64 = 0;
511
512 for tryte in self.dimensions.values() {
514 norm_a += tryte.norm_squared();
515 }
516 for tryte in other.dimensions.values() {
517 norm_b += tryte.norm_squared();
518 }
519
520 for (dim, tryte_a) in &self.dimensions {
522 if let Some(tryte_b) = other.dimensions.get(dim) {
523 dot += tryte_a.dot(tryte_b);
524 }
525 }
526
527 if norm_a == 0 || norm_b == 0 {
528 return 0.0;
529 }
530
531 dot as f64 / ((norm_a as f64).sqrt() * (norm_b as f64).sqrt())
532 }
533
534 pub fn thin(&self, target_nnz: usize) -> HyperVec {
538 if self.nnz() <= target_nnz {
539 return self.clone();
540 }
541
542 let mut result = HyperVec::new(self.config.clone());
543
544 let mut indexed: Vec<_> = self
546 .dimensions
547 .iter()
548 .map(|(&d, t)| (d, t.clone(), t.to_i64().abs()))
549 .collect();
550
551 indexed.sort_by(|a, b| b.2.cmp(&a.2));
552
553 for (dim, tryte, _) in indexed.into_iter().take(target_nnz) {
554 result.dimensions.insert(dim, tryte);
555 }
556
557 result
558 }
559
560 pub fn expand_precision(&mut self, dim: usize, new_depth: u8) {
562 if let TritDepthConfig::Adaptive { max_depth, .. } = &self.config.trit_depth {
563 if new_depth > *max_depth {
564 return; }
566 }
567
568 if let Some(tryte) = self.dimensions.get(&dim) {
569 let value = tryte.to_i64();
570 let new_tryte = Tryte::from_i64(value, new_depth as usize);
571 self.dimensions.insert(dim, new_tryte);
572 }
573 }
574
575 pub fn pack(&self) -> Vec<u8> {
577 let mut bytes = Vec::new();
580
581 bytes.extend((self.dimensions.len() as u32).to_le_bytes());
583
584 for (&dim, tryte) in &self.dimensions {
585 bytes.extend((dim as u32).to_le_bytes());
587 bytes.push(tryte.len() as u8);
589 let packed_trits = pack_trits(&tryte.trits);
591 bytes.extend(packed_trits);
592 }
593
594 bytes
595 }
596
597 pub fn unpack(config: DimensionalConfig, bytes: &[u8]) -> Option<Self> {
599 if bytes.len() < 4 {
600 return None;
601 }
602
603 let num_entries = u32::from_le_bytes(bytes[0..4].try_into().ok()?) as usize;
604 let mut vec = HyperVec::new(config);
605 let mut offset = 4;
606
607 for _ in 0..num_entries {
608 if offset + 5 > bytes.len() {
609 return None;
610 }
611
612 let dim = u32::from_le_bytes(bytes[offset..offset + 4].try_into().ok()?) as usize;
613 offset += 4;
614 let num_trits = bytes[offset] as usize;
615 offset += 1;
616
617 let packed_bytes = (num_trits).div_ceil(5); if offset + packed_bytes > bytes.len() {
619 return None;
620 }
621
622 let trits = unpack_trits(&bytes[offset..offset + packed_bytes], num_trits);
623 offset += packed_bytes;
624
625 vec.dimensions.insert(dim, Tryte { trits });
626 }
627
628 Some(vec)
629 }
630}
631
632fn pack_trits(trits: &[Trit]) -> Vec<u8> {
634 let mut bytes = Vec::new();
635
636 for chunk in trits.chunks(5) {
637 let mut byte: u8 = 0;
638 let mut power: u8 = 1;
639
640 for &trit in chunk {
641 let encoded = match trit {
643 Trit::Neg => 2,
644 Trit::Zero => 0,
645 Trit::Pos => 1,
646 };
647 byte += encoded * power;
648 power *= 3;
649 }
650
651 bytes.push(byte);
652 }
653
654 bytes
655}
656
657fn unpack_trits(bytes: &[u8], num_trits: usize) -> Vec<Trit> {
659 let mut trits = Vec::with_capacity(num_trits);
660
661 for &byte in bytes {
662 let mut remaining = byte;
663 for _ in 0..5 {
664 if trits.len() >= num_trits {
665 break;
666 }
667
668 let encoded = remaining % 3;
669 remaining /= 3;
670
671 let trit = match encoded {
673 2 => Trit::Neg,
674 0 => Trit::Zero,
675 1 => Trit::Pos,
676 _ => unreachable!(),
677 };
678 trits.push(trit);
679 }
680 }
681
682 trits
683}
684
685#[derive(Clone, Debug, Serialize, Deserialize)]
687pub struct DifferentialEncoding {
688 pub coefficients: HyperVec,
690
691 pub residual: HyperVec,
693
694 pub expanded_dims: Vec<(usize, u8)>,
696
697 pub quality: f64,
699}
700
701pub struct DifferentialEncoder {
703 pub config: DimensionalConfig,
705
706 pub basis: Vec<HyperVec>,
708
709 pub match_threshold: f64,
711
712 pub precision_threshold: f64,
714}
715
716impl DifferentialEncoder {
717 pub fn new(config: DimensionalConfig) -> Self {
719 DifferentialEncoder {
720 config,
721 basis: Vec::new(),
722 match_threshold: 0.3,
723 precision_threshold: 0.001,
724 }
725 }
726
727 pub fn add_basis(&mut self, vector: HyperVec) {
729 self.basis.push(vector);
730 }
731
732 pub fn encode(&self, data: &HyperVec) -> DifferentialEncoding {
734 let mut coefficients = HyperVec::new(self.config.clone());
735 let mut expanded_dims = Vec::new();
736
737 for (basis_idx, basis_vec) in self.basis.iter().enumerate() {
739 let similarity = data.cosine(basis_vec);
740
741 if similarity.abs() > self.match_threshold {
742 let coef_value = (similarity * 100.0) as i64;
744 coefficients.set(basis_idx, coef_value);
745 }
746 }
747
748 let reconstructed = self.reconstruct_from_coefficients(&coefficients);
750 let mut residual = HyperVec::new(self.config.clone());
751
752 for dim in 0..self.config.num_dimensions {
753 let original_val = data.get(dim);
754 let reconstructed_val = reconstructed.get(dim);
755 let diff = original_val - reconstructed_val;
756
757 if diff.abs() > 0 {
758 residual.set(dim, diff);
759
760 let relative_error = if original_val != 0 {
762 (diff as f64).abs() / (original_val as f64).abs()
763 } else {
764 0.0
765 };
766
767 if relative_error > self.precision_threshold {
768 if let TritDepthConfig::Adaptive {
769 base_depth,
770 max_depth,
771 } = &self.config.trit_depth
772 {
773 let new_depth = (*base_depth + 2).min(*max_depth);
774 expanded_dims.push((dim, new_depth));
775 }
776 }
777 }
778 }
779
780 let quality = self.calculate_quality(data, &coefficients, &residual);
782
783 DifferentialEncoding {
784 coefficients,
785 residual,
786 expanded_dims,
787 quality,
788 }
789 }
790
791 fn reconstruct_from_coefficients(&self, coefficients: &HyperVec) -> HyperVec {
793 let mut result = HyperVec::new(self.config.clone());
794
795 for (&basis_idx, coef_tryte) in &coefficients.dimensions {
796 if let Some(basis_vec) = self.basis.get(basis_idx) {
797 let coef = coef_tryte.to_i64() as f64 / 100.0;
798
799 for (&dim, tryte) in &basis_vec.dimensions {
801 let scaled = (tryte.to_i64() as f64 * coef) as i64;
802 let current = result.get(dim);
803 result.set(dim, current + scaled);
804 }
805 }
806 }
807
808 result
809 }
810
811 pub fn decode(&self, encoding: &DifferentialEncoding) -> HyperVec {
813 let mut result = self.reconstruct_from_coefficients(&encoding.coefficients);
814
815 for (&dim, tryte) in &encoding.residual.dimensions {
817 let current = result.get(dim);
818 result.set(dim, current + tryte.to_i64());
819 }
820
821 result
822 }
823
824 fn calculate_quality(
826 &self,
827 original: &HyperVec,
828 coefficients: &HyperVec,
829 residual: &HyperVec,
830 ) -> f64 {
831 let reconstructed = self.reconstruct_from_coefficients(coefficients);
832
833 let mut total_error: f64 = 0.0;
834 let mut total_energy: f64 = 0.0;
835
836 for dim in 0..self.config.num_dimensions {
837 let orig = original.get(dim) as f64;
838 let recon = reconstructed.get(dim) as f64;
839 let res = residual.get(dim) as f64;
840
841 total_energy += orig * orig;
842 total_error += (orig - recon - res).powi(2);
843 }
844
845 if total_energy == 0.0 {
846 return 1.0;
847 }
848
849 1.0 - (total_error / total_energy).sqrt()
850 }
851}
852
853#[cfg(test)]
854mod tests {
855 use super::*;
856
857 #[test]
858 fn test_trit_multiplication() {
859 assert_eq!(Trit::Pos * Trit::Pos, Trit::Pos);
860 assert_eq!(Trit::Pos * Trit::Neg, Trit::Neg);
861 assert_eq!(Trit::Neg * Trit::Neg, Trit::Pos);
862 assert_eq!(Trit::Zero * Trit::Pos, Trit::Zero);
863 }
864
865 #[test]
866 fn test_tryte_roundtrip() {
867 let test_values = [0i64, 1, -1, 42, -42, 100, -100, 364, -364];
868
869 for &value in &test_values {
870 let tryte = Tryte::from_i64(value, 6);
871 let decoded = tryte.to_i64();
872 assert_eq!(value, decoded, "Roundtrip failed for {}", value);
873 }
874 }
875
876 #[test]
877 fn test_tryte_max_values() {
878 assert_eq!(Tryte::max_value(6), 364);
880 assert_eq!(Tryte::min_value(6), -364);
881
882 assert_eq!(Tryte::max_value(8), 3280);
884 }
885
886 #[test]
887 fn test_hypervec_bundle_commutative() {
888 let config = DimensionalConfig::default();
889 let a = HyperVec::from_dense(config.clone(), &[1, 0, -1, 1, 0]);
890 let b = HyperVec::from_dense(config.clone(), &[0, 1, -1, 0, 1]);
891
892 let ab = a.bundle(&b);
893 let ba = b.bundle(&a);
894
895 assert_eq!(ab.dimensions.len(), ba.dimensions.len());
896 for (dim, tryte) in &ab.dimensions {
897 assert_eq!(tryte.to_i64(), ba.get(*dim));
898 }
899 }
900
901 #[test]
902 fn test_hypervec_bind_self_inverse() {
903 let config = DimensionalConfig::default();
904 let mut a = HyperVec::new(config.clone());
905 a.set(0, 1);
906 a.set(1, -1);
907 a.set(2, 1);
908
909 let aa = a.bind(&a);
911
912 for tryte in aa.dimensions.values() {
913 assert_eq!(tryte.to_i64(), 1, "Self-bind should produce +1");
914 }
915 }
916
917 #[test]
918 fn test_hypervec_permute_inverse() {
919 let config = DimensionalConfig::compact();
920 let mut a = HyperVec::new(config.clone());
921 a.set(0, 1);
922 a.set(100, -1);
923 a.set(500, 1);
924
925 let permuted = a.permute(1000);
926 let recovered = permuted.inverse_permute(1000);
927
928 assert_eq!(a.get(0), recovered.get(0));
929 assert_eq!(a.get(100), recovered.get(100));
930 assert_eq!(a.get(500), recovered.get(500));
931 }
932
933 #[test]
934 fn test_hypervec_cosine_identical() {
935 let config = DimensionalConfig::default();
936 let mut a = HyperVec::new(config.clone());
937 a.set(0, 1);
938 a.set(1, -1);
939 a.set(2, 1);
940
941 let similarity = a.cosine(&a);
942 assert!(
943 (similarity - 1.0).abs() < 0.001,
944 "Self-similarity should be 1.0"
945 );
946 }
947
948 #[test]
949 fn test_pack_unpack_roundtrip() {
950 let config = DimensionalConfig::compact();
951 let mut vec = HyperVec::new(config.clone());
952 vec.set(0, 42);
953 vec.set(100, -17);
954 vec.set(1000, 100);
955
956 let packed = vec.pack();
957 let unpacked = HyperVec::unpack(config, &packed)
958 .expect("HyperVec unpack must succeed for valid packed data");
959
960 assert_eq!(vec.get(0), unpacked.get(0));
961 assert_eq!(vec.get(100), unpacked.get(100));
962 assert_eq!(vec.get(1000), unpacked.get(1000));
963 }
964
965 #[test]
966 fn test_differential_encoding() {
967 let config = DimensionalConfig::compact();
968 let mut encoder = DifferentialEncoder::new(config.clone());
969
970 let mut basis1 = HyperVec::new(config.clone());
972 basis1.set(0, 1);
973 basis1.set(1, 1);
974 encoder.add_basis(basis1);
975
976 let mut data = HyperVec::new(config.clone());
978 data.set(0, 1);
979 data.set(1, 1);
980 data.set(2, 1); let encoding = encoder.encode(&data);
983 let decoded = encoder.decode(&encoding);
984
985 assert_eq!(data.get(0), decoded.get(0));
987 assert_eq!(data.get(1), decoded.get(1));
988 assert_eq!(data.get(2), decoded.get(2));
989 }
990}