1use crate::coding::{checked_count, checked_decode_count};
36use crate::delta::{BitReader, BitWriter};
37use crate::error::{EncodingError, Result};
38
39#[derive(Debug, Clone, Copy)]
41pub struct ChimpEncoder;
42
43#[derive(Debug, Clone, Copy)]
45pub struct ChimpDecoder;
46
47const NUM_BUCKETS: u8 = 8;
49
50#[inline]
52fn leading_zeros_to_bucket(lz: u32) -> u8 {
53 (lz as u8 / 8).min(NUM_BUCKETS - 1)
54}
55
56#[inline]
58fn bucket_to_leading_zeros(bucket: u8) -> u32 {
59 u32::from(bucket) * 8
60}
61
62impl ChimpEncoder {
63 pub fn encode(values: &[f64]) -> Result<Vec<u8>> {
69 if values.is_empty() {
70 return Err(EncodingError::EmptyInput {
71 context: "chimp encoder",
72 });
73 }
74
75 let mut writer = BitWriter::with_capacity(12 + values.len() * 12);
77
78 Self::encode_core(values, &mut writer)?;
79
80 Ok(writer.finish())
81 }
82
83 fn encode_core(values: &[f64], writer: &mut BitWriter) -> Result<()> {
90 let count = checked_count(values.len())?;
92 writer.write_bits(u64::from(count), 32);
93
94 let mut prev_bits = values[0].to_bits();
96 writer.write_bits(prev_bits, 64);
97
98 let mut prev_leading = u8::MAX; let mut prev_trailing = 0_u8;
100
101 #[allow(clippy::explicit_counter_loop)]
102 for &val in &values[1..] {
104 let bits = val.to_bits();
105 let xor = prev_bits ^ bits;
106
107 if xor == 0 {
108 writer.write_bit(false);
110 } else {
111 let leading = xor.leading_zeros();
112 let trailing = xor.trailing_zeros();
113
114 let bucket = leading_zeros_to_bucket(leading);
115 let bucket_lz = bucket_to_leading_zeros(bucket);
116
117 let can_reuse = prev_leading != u8::MAX
119 && leading >= u32::from(prev_leading)
120 && trailing >= u32::from(prev_trailing);
121
122 if can_reuse {
123 writer.write_bits(0b10, 2);
126 let prev_meaningful = 64 - u32::from(prev_leading) - u32::from(prev_trailing);
127 let shifted = xor >> u32::from(prev_trailing);
128 writer.write_bits(shifted, prev_meaningful as u8);
129 } else {
130 writer.write_bits(0b11, 2);
133 writer.write_bits(u64::from(bucket), 3);
135 writer.write_bits(u64::from(trailing), 6);
137 let adj_leading = bucket_lz;
139 let adj_meaningful = 64 - adj_leading - trailing;
140 let shifted = xor >> trailing;
141 writer.write_bits(u64::from(adj_meaningful.saturating_sub(1)), 6);
143 writer.write_bits(shifted, adj_meaningful as u8);
144
145 prev_leading = bucket_lz as u8;
146 prev_trailing = trailing as u8;
147 }
148 }
149
150 prev_bits = bits;
151 }
152
153 Ok(())
154 }
155}
156
157impl ChimpDecoder {
158 pub fn decode(data: &[u8]) -> Result<Vec<f64>> {
164 if data.len() < 4 {
165 return Err(EncodingError::CorruptData {
166 detail: "chimp data too short for header".to_string(),
167 });
168 }
169
170 let mut reader = BitReader::new(data);
171
172 let count = checked_decode_count(reader.read_bits(32)? as usize, "Chimp")?;
173 if count == 0 {
174 return Ok(Vec::new());
175 }
176
177 let mut result = Vec::with_capacity(count);
178 Self::decode_core(&mut reader, count, &mut result)?;
179 Ok(result)
180 }
181
182 pub fn decode_into(data: &[u8], output: &mut Vec<f64>) -> Result<()> {
188 if data.len() < 4 {
189 return Err(EncodingError::CorruptData {
190 detail: "chimp data too short for header".to_string(),
191 });
192 }
193
194 let mut reader = BitReader::new(data);
195 let count = checked_decode_count(reader.read_bits(32)? as usize, "Chimp")?;
196 if count == 0 {
197 return Ok(());
198 }
199
200 output.clear();
201 output.reserve(count);
202 Self::decode_core(&mut reader, count, output)
203 }
204
205 fn decode_core(reader: &mut BitReader<'_>, count: usize, result: &mut Vec<f64>) -> Result<()> {
207 let first_bits = reader.read_bits(64)?;
208 result.push(f64::from_bits(first_bits));
209
210 let mut prev_bits = first_bits;
211 let mut prev_leading: u8 = u8::MAX;
212 let mut prev_trailing: u8 = 0;
213
214 for _ in 1..count {
215 if !reader.read_bit()? {
216 result.push(f64::from_bits(prev_bits));
218 continue;
219 }
220
221 if reader.read_bit()? {
222 let bucket = reader.read_bits(3)? as u8;
224 let trailing = reader.read_bits(6)? as u32;
225 let adj_leading = bucket_to_leading_zeros(bucket);
226 let meaningful = reader.read_bits(6)? as u32 + 1; if adj_leading + meaningful + trailing > 64 {
228 return Err(EncodingError::CorruptData {
229 detail: format!(
230 "chimp case-2: leading ({adj_leading}) + meaningful ({meaningful}) + trailing ({trailing}) exceeds 64"
231 ),
232 });
233 }
234 let shifted = reader.read_bits(meaningful as u8)?;
235 let xor = shifted << trailing;
236 let bits = prev_bits ^ xor;
237 result.push(f64::from_bits(bits));
238 prev_bits = bits;
239
240 prev_leading = adj_leading as u8;
241 prev_trailing = trailing as u8;
242 } else {
243 if prev_leading == u8::MAX {
245 return Err(EncodingError::CorruptData {
246 detail: "chimp case-1 encountered before any case-2 window".to_string(),
247 });
248 }
249 let meaningful = 64 - u32::from(prev_leading) - u32::from(prev_trailing);
250 let shifted = reader.read_bits(meaningful as u8)?;
251 let xor = shifted << u32::from(prev_trailing);
252 let bits = prev_bits ^ xor;
253 result.push(f64::from_bits(bits));
254 prev_bits = bits;
255 }
256 }
257
258 Ok(())
259 }
260}
261
262const RING_SIZE: usize = 128;
264
265#[derive(Debug, Clone, Copy)]
272pub struct Chimp128Encoder;
273
274#[derive(Debug, Clone, Copy)]
276pub struct Chimp128Decoder;
277
278impl Chimp128Encoder {
279 pub fn encode(values: &[f64]) -> Result<Vec<u8>> {
285 if values.is_empty() {
286 return Err(EncodingError::EmptyInput {
287 context: "chimp128 encoder",
288 });
289 }
290
291 let mut writer = BitWriter::with_capacity(12 + values.len() * 12);
292
293 let count = checked_count(values.len())?;
295 writer.write_bits(u64::from(count), 32);
296
297 let first_bits = values[0].to_bits();
299 writer.write_bits(first_bits, 64);
300
301 let mut ring = [0u64; RING_SIZE];
303 ring[0] = first_bits;
304 let mut ring_len: usize = 1; let mut ring_pos: usize = 1; let mut ring_hash = [u8::MAX; 256];
318 ring_hash[(first_bits >> 56) as usize] = 0;
319
320 let mut prev_bits = first_bits;
321 let mut prev_leading: u8 = u8::MAX;
322 let mut prev_trailing: u8 = 0;
323
324 #[allow(clippy::explicit_counter_loop)]
325 for &val in &values[1..] {
327 let bits = val.to_bits();
328 let seq_xor = prev_bits ^ bits;
329
330 if seq_xor == 0 {
331 writer.write_bit(false);
333 } else {
334 let seq_leading = seq_xor.leading_zeros();
338 let mut best_leading = seq_leading;
339 let mut best_xor = seq_xor;
340 let mut best_idx: Option<u8> = None;
341
342 let hash_key = (bits >> 56) as usize;
346 let hash_candidate = ring_hash[hash_key];
347 let mut skip_scan = false;
348
349 if hash_candidate != u8::MAX {
350 let ci = hash_candidate as usize;
351 let ref_xor = ring[ci] ^ bits;
352 if ref_xor == 0 {
353 best_leading = 64;
355 best_xor = 0;
356 best_idx = Some(ci as u8);
357 skip_scan = true;
358 } else {
359 let rl = ref_xor.leading_zeros();
360 if rl > best_leading {
361 best_leading = rl;
362 best_xor = ref_xor;
363 best_idx = Some(ci as u8);
364 }
365 if best_leading >= 24 {
369 skip_scan = true;
370 }
371 }
372 }
373
374 if !skip_scan {
376 let search_len = ring_len.min(RING_SIZE);
377 #[allow(clippy::needless_range_loop)] for i in 0..search_len {
379 let ref_xor = ring[i] ^ bits;
380 if ref_xor == 0 {
381 best_xor = 0;
382 best_idx = Some(i as u8);
383 break;
384 }
385 let rl = ref_xor.leading_zeros();
386 if rl > best_leading {
387 best_leading = rl;
388 best_xor = ref_xor;
389 best_idx = Some(i as u8);
390 }
391 }
392 }
393
394 writer.write_bit(true);
396
397 if best_xor == 0 {
398 writer.write_bit(true);
402 writer.write_bits(u64::from(best_idx.unwrap_or(0)), 7);
403 writer.write_bit(false); } else {
408 let xor = best_xor;
409 let leading = xor.leading_zeros();
410 let trailing = xor.trailing_zeros();
411 let bucket = leading_zeros_to_bucket(leading);
412 let bucket_lz = bucket_to_leading_zeros(bucket);
413
414 if let Some(idx) = best_idx {
415 writer.write_bit(true); writer.write_bits(u64::from(idx), 7);
418 writer.write_bit(true); Self::encode_xor(
422 &mut writer,
423 xor,
424 leading,
425 trailing,
426 bucket,
427 bucket_lz,
428 &mut prev_leading,
429 &mut prev_trailing,
430 );
431 } else {
432 writer.write_bit(false); let can_reuse = prev_leading != u8::MAX
437 && leading >= u32::from(prev_leading)
438 && trailing >= u32::from(prev_trailing);
439
440 if can_reuse {
441 writer.write_bit(false); let prev_meaningful =
444 64 - u32::from(prev_leading) - u32::from(prev_trailing);
445 let shifted = xor >> u32::from(prev_trailing);
446 writer.write_bits(shifted, prev_meaningful as u8);
447 } else {
448 writer.write_bit(true); writer.write_bits(u64::from(bucket), 3);
451 writer.write_bits(u64::from(trailing), 6);
452 let adj_leading = bucket_lz;
453 let adj_meaningful = 64 - adj_leading - trailing;
454 let shifted = xor >> trailing;
455 writer.write_bits(u64::from(adj_meaningful.saturating_sub(1)), 6);
456 writer.write_bits(shifted, adj_meaningful as u8);
457
458 prev_leading = bucket_lz as u8;
459 prev_trailing = trailing as u8;
460 }
461 }
462 }
463 }
464
465 ring[ring_pos % RING_SIZE] = bits;
467 ring_hash[(bits >> 56) as usize] = (ring_pos % RING_SIZE) as u8;
468 ring_pos += 1;
469 if ring_len < RING_SIZE {
470 ring_len += 1;
471 }
472
473 prev_bits = bits;
474 }
475
476 Ok(writer.finish())
477 }
478
479 #[inline]
482 fn encode_xor(
483 writer: &mut BitWriter,
484 xor: u64,
485 _leading: u32,
486 trailing: u32,
487 bucket: u8,
488 bucket_lz: u32,
489 _prev_leading: &mut u8,
490 _prev_trailing: &mut u8,
491 ) {
492 writer.write_bits(u64::from(bucket), 3);
496 writer.write_bits(u64::from(trailing), 6);
497 let adj_leading = bucket_lz;
498 let adj_meaningful = 64 - adj_leading - trailing;
499 let shifted = xor >> trailing;
500 writer.write_bits(u64::from(adj_meaningful.saturating_sub(1)), 6);
501 writer.write_bits(shifted, adj_meaningful as u8);
502
503 }
510}
511
512impl Chimp128Decoder {
513 pub fn decode(data: &[u8]) -> Result<Vec<f64>> {
519 if data.len() < 4 {
520 return Err(EncodingError::CorruptData {
521 detail: "chimp128 data too short for header".to_string(),
522 });
523 }
524
525 let mut reader = BitReader::new(data);
526 let count = checked_decode_count(reader.read_bits(32)? as usize, "Chimp128")?;
527 if count == 0 {
528 return Ok(Vec::new());
529 }
530
531 let mut result = Vec::with_capacity(count);
532 Self::decode_core(&mut reader, count, &mut result)?;
533 Ok(result)
534 }
535
536 pub fn decode_into(data: &[u8], output: &mut Vec<f64>) -> Result<()> {
542 if data.len() < 4 {
543 return Err(EncodingError::CorruptData {
544 detail: "chimp128 data too short for header".to_string(),
545 });
546 }
547
548 let mut reader = BitReader::new(data);
549 let count = checked_decode_count(reader.read_bits(32)? as usize, "Chimp128")?;
550 if count == 0 {
551 return Ok(());
552 }
553
554 output.clear();
555 output.reserve(count);
556 Self::decode_core(&mut reader, count, output)
557 }
558
559 fn decode_core(reader: &mut BitReader<'_>, count: usize, result: &mut Vec<f64>) -> Result<()> {
561 let first_bits = reader.read_bits(64)?;
562 result.push(f64::from_bits(first_bits));
563
564 let mut ring = [0u64; RING_SIZE];
565 ring[0] = first_bits;
566 let mut ring_len: usize = 1;
567 let mut ring_pos: usize = 1;
568
569 let mut prev_bits = first_bits;
570 let mut prev_leading: u8 = u8::MAX;
571 let mut prev_trailing: u8 = 0;
572
573 for _ in 1..count {
574 if !reader.read_bit()? {
575 result.push(f64::from_bits(prev_bits));
577 ring[ring_pos % RING_SIZE] = prev_bits;
579 ring_pos += 1;
580 if ring_len < RING_SIZE {
581 ring_len += 1;
582 }
583 continue;
584 }
585
586 let use_ring = reader.read_bit()?;
588
589 if use_ring {
590 let idx = reader.read_bits(7)? as usize;
591 if idx >= ring_len.min(RING_SIZE) {
592 return Err(EncodingError::CorruptData {
593 detail: format!(
594 "chimp128: ring index {idx} out of range (ring_len={})",
595 ring_len.min(RING_SIZE)
596 ),
597 });
598 }
599 let ref_bits = ring[idx];
600
601 let ring_xor_nonzero = reader.read_bit()?;
602 if !ring_xor_nonzero {
603 let bits = ref_bits;
605 result.push(f64::from_bits(bits));
606 prev_bits = bits;
607 } else {
608 let bucket = reader.read_bits(3)? as u8;
610 let trailing = reader.read_bits(6)? as u32;
611 let adj_leading = bucket_to_leading_zeros(bucket);
612 let meaningful = reader.read_bits(6)? as u32 + 1;
613 if adj_leading + meaningful + trailing > 64 {
614 return Err(EncodingError::CorruptData {
615 detail: format!(
616 "chimp128 ring-ref: leading ({adj_leading}) + meaningful ({meaningful}) + trailing ({trailing}) exceeds 64"
617 ),
618 });
619 }
620 let shifted = reader.read_bits(meaningful as u8)?;
621 let xor = shifted << trailing;
622 let bits = ref_bits ^ xor;
623 result.push(f64::from_bits(bits));
624 prev_bits = bits;
625
626 }
630 } else {
631 let new_window = reader.read_bit()?;
633 if new_window {
634 let bucket = reader.read_bits(3)? as u8;
636 let trailing = reader.read_bits(6)? as u32;
637 let adj_leading = bucket_to_leading_zeros(bucket);
638 let meaningful = reader.read_bits(6)? as u32 + 1;
639 if adj_leading + meaningful + trailing > 64 {
640 return Err(EncodingError::CorruptData {
641 detail: format!(
642 "chimp128 case-2: leading ({adj_leading}) + meaningful ({meaningful}) + trailing ({trailing}) exceeds 64"
643 ),
644 });
645 }
646 let shifted = reader.read_bits(meaningful as u8)?;
647 let xor = shifted << trailing;
648 let bits = prev_bits ^ xor;
649 result.push(f64::from_bits(bits));
650 prev_bits = bits;
651
652 prev_leading = adj_leading as u8;
653 prev_trailing = trailing as u8;
654 } else {
655 if prev_leading == u8::MAX {
657 return Err(EncodingError::CorruptData {
658 detail: "chimp128 case-1 encountered before any case-2 window"
659 .to_string(),
660 });
661 }
662 let meaningful = 64 - u32::from(prev_leading) - u32::from(prev_trailing);
663 let shifted = reader.read_bits(meaningful as u8)?;
664 let xor = shifted << u32::from(prev_trailing);
665 let bits = prev_bits ^ xor;
666 result.push(f64::from_bits(bits));
667 prev_bits = bits;
668 }
669 }
670
671 ring[ring_pos % RING_SIZE] = prev_bits;
673 ring_pos += 1;
674 if ring_len < RING_SIZE {
675 ring_len += 1;
676 }
677 }
678
679 Ok(())
680 }
681}
682
683#[cfg(test)]
684mod tests {
685 use super::*;
686
687 #[test]
688 fn roundtrip_constant() {
689 let values = vec![42.0_f64; 100];
690 let encoded = ChimpEncoder::encode(&values).unwrap();
691 let decoded = ChimpDecoder::decode(&encoded).unwrap();
692 assert_eq!(values.len(), decoded.len());
693 for (a, b) in values.iter().zip(decoded.iter()) {
694 assert_eq!(a.to_bits(), b.to_bits());
695 }
696 }
697
698 #[test]
699 fn roundtrip_sinwave() {
700 let values: Vec<f64> = (0..1000)
701 .map(|i| (i as f64 * 0.01).sin() * 100.0 + 50.0)
702 .collect();
703 let encoded = ChimpEncoder::encode(&values).unwrap();
704 let decoded = ChimpDecoder::decode(&encoded).unwrap();
705 assert_eq!(values.len(), decoded.len());
706 for (a, b) in values.iter().zip(decoded.iter()) {
707 assert_eq!(a.to_bits(), b.to_bits());
708 }
709 }
710
711 #[test]
712 fn roundtrip_special_values() {
713 let values = vec![
714 0.0_f64,
715 -0.0,
716 f64::NAN,
717 f64::INFINITY,
718 f64::NEG_INFINITY,
719 f64::MIN,
720 f64::MAX,
721 f64::MIN_POSITIVE,
722 f64::EPSILON,
723 1.0,
724 -1.0,
725 ];
726 let encoded = ChimpEncoder::encode(&values).unwrap();
727 let decoded = ChimpDecoder::decode(&encoded).unwrap();
728 assert_eq!(values.len(), decoded.len());
729 for (a, b) in values.iter().zip(decoded.iter()) {
730 assert_eq!(a.to_bits(), b.to_bits(), "mismatch for value {a}");
731 }
732 }
733
734 #[test]
735 fn roundtrip_single() {
736 let values = vec![std::f64::consts::PI];
737 let encoded = ChimpEncoder::encode(&values).unwrap();
738 let decoded = ChimpDecoder::decode(&encoded).unwrap();
739 assert_eq!(values[0].to_bits(), decoded[0].to_bits());
740 }
741
742 #[test]
743 fn empty_error() {
744 assert!(ChimpEncoder::encode(&[]).is_err());
745 }
746
747 #[test]
748 fn compression_ratio_regular_metrics() {
749 let values: Vec<f64> = (0..10_000)
752 .map(|i| {
753 50.0 + (i / 100) as f64 * 0.1
755 })
756 .collect();
757 let raw_size = values.len() * 8;
758 let encoded = ChimpEncoder::encode(&values).unwrap();
759 #[allow(clippy::cast_precision_loss)]
760 let ratio = raw_size as f64 / encoded.len() as f64;
761 assert!(
763 ratio > 2.0,
764 "Expected ratio > 2x, got {ratio:.1}x ({raw_size} raw, {} encoded)",
765 encoded.len()
766 );
767 }
768
769 #[test]
770 fn decode_into_reuses_buffer() {
771 let values: Vec<f64> = (0..500).map(|i| i as f64 * 0.5).collect();
772 let encoded = ChimpEncoder::encode(&values).unwrap();
773
774 let mut output = Vec::new();
775 ChimpDecoder::decode_into(&encoded, &mut output).unwrap();
776 assert_eq!(values.len(), output.len());
777 for (a, b) in values.iter().zip(output.iter()) {
778 assert_eq!(a.to_bits(), b.to_bits());
779 }
780
781 let values2: Vec<f64> = (0..300).map(|i| i as f64 * 1.1).collect();
783 let encoded2 = ChimpEncoder::encode(&values2).unwrap();
784 ChimpDecoder::decode_into(&encoded2, &mut output).unwrap();
785 assert_eq!(values2.len(), output.len());
786 for (a, b) in values2.iter().zip(output.iter()) {
787 assert_eq!(a.to_bits(), b.to_bits());
788 }
789 }
790
791 #[test]
792 fn roundtrip_1m_floats() {
793 let values: Vec<f64> = (0..1_000_000)
795 .map(|i| 50.0 + (i as f64 * 0.001).sin() * 10.0)
796 .collect();
797 let encoded = ChimpEncoder::encode(&values).unwrap();
798 let decoded = ChimpDecoder::decode(&encoded).unwrap();
799 assert_eq!(values.len(), decoded.len());
800 assert_eq!(values[0].to_bits(), decoded[0].to_bits());
802 assert_eq!(values[500_000].to_bits(), decoded[500_000].to_bits());
803 assert_eq!(values[999_999].to_bits(), decoded[999_999].to_bits());
804 }
805
806 #[test]
809 fn chimp128_roundtrip_constant() {
810 let values = vec![42.0_f64; 200];
811 let encoded = Chimp128Encoder::encode(&values).unwrap();
812 let decoded = Chimp128Decoder::decode(&encoded).unwrap();
813 assert_eq!(values.len(), decoded.len());
814 for (a, b) in values.iter().zip(decoded.iter()) {
815 assert_eq!(a.to_bits(), b.to_bits());
816 }
817 }
818
819 #[test]
820 fn chimp128_roundtrip_sinwave() {
821 let values: Vec<f64> = (0..2000)
824 .map(|i| (i as f64 * 0.01).sin() * 100.0 + 50.0)
825 .collect();
826 let encoded = Chimp128Encoder::encode(&values).unwrap();
827 let decoded = Chimp128Decoder::decode(&encoded).unwrap();
828 assert_eq!(values.len(), decoded.len());
829 for (a, b) in values.iter().zip(decoded.iter()) {
830 assert_eq!(a.to_bits(), b.to_bits());
831 }
832 }
833
834 #[test]
835 fn chimp128_roundtrip_special_values() {
836 let values = vec![
837 0.0_f64,
838 -0.0,
839 f64::NAN,
840 f64::INFINITY,
841 f64::NEG_INFINITY,
842 f64::MIN,
843 f64::MAX,
844 f64::MIN_POSITIVE,
845 f64::EPSILON,
846 1.0,
847 -1.0,
848 ];
849 let encoded = Chimp128Encoder::encode(&values).unwrap();
850 let decoded = Chimp128Decoder::decode(&encoded).unwrap();
851 assert_eq!(values.len(), decoded.len());
852 for (a, b) in values.iter().zip(decoded.iter()) {
853 assert_eq!(a.to_bits(), b.to_bits(), "mismatch for value {a}");
854 }
855 }
856
857 #[test]
858 fn chimp128_beats_chimp_on_periodic() {
859 let base_values: Vec<f64> = (0..64).map(|i| 20.0 + (i as f64) * 0.5).collect();
863 let values: Vec<f64> = base_values.iter().cycle().take(5000).copied().collect();
864
865 let chimp_encoded = ChimpEncoder::encode(&values).unwrap();
866 let chimp128_encoded = Chimp128Encoder::encode(&values).unwrap();
867
868 let decoded = Chimp128Decoder::decode(&chimp128_encoded).unwrap();
870 assert_eq!(values.len(), decoded.len());
871 for (a, b) in values.iter().zip(decoded.iter()) {
872 assert_eq!(a.to_bits(), b.to_bits());
873 }
874
875 assert!(
877 chimp128_encoded.len() <= chimp_encoded.len(),
878 "Expected Chimp128 ({} bytes) <= Chimp ({} bytes) on periodic data",
879 chimp128_encoded.len(),
880 chimp_encoded.len(),
881 );
882 }
883
884 #[test]
885 fn chimp128_empty_error() {
886 assert!(Chimp128Encoder::encode(&[]).is_err());
887 }
888
889 #[test]
890 fn chimp128_single_value() {
891 let values = vec![std::f64::consts::PI];
892 let encoded = Chimp128Encoder::encode(&values).unwrap();
893 let decoded = Chimp128Decoder::decode(&encoded).unwrap();
894 assert_eq!(values[0].to_bits(), decoded[0].to_bits());
895 }
896
897 #[test]
898 fn chimp128_decode_into() {
899 let values: Vec<f64> = (0..500).map(|i| i as f64 * 0.5).collect();
900 let encoded = Chimp128Encoder::encode(&values).unwrap();
901 let mut output = Vec::new();
902 Chimp128Decoder::decode_into(&encoded, &mut output).unwrap();
903 assert_eq!(values.len(), output.len());
904 for (a, b) in values.iter().zip(output.iter()) {
905 assert_eq!(a.to_bits(), b.to_bits());
906 }
907 }
908}
909
910#[cfg(test)]
911mod proptests {
912 use super::*;
913 use proptest::prelude::*;
914
915 proptest! {
916 #[test]
917 fn roundtrip_arbitrary(values in proptest::collection::vec(any::<f64>(), 1..500)) {
918 let encoded = ChimpEncoder::encode(&values).unwrap();
919 let decoded = ChimpDecoder::decode(&encoded).unwrap();
920 prop_assert_eq!(values.len(), decoded.len());
921 for (a, b) in values.iter().zip(decoded.iter()) {
922 prop_assert_eq!(a.to_bits(), b.to_bits());
923 }
924 }
925
926 #[test]
927 fn chimp128_roundtrip_arbitrary(values in proptest::collection::vec(any::<f64>(), 1..500)) {
928 let encoded = Chimp128Encoder::encode(&values).unwrap();
929 let decoded = Chimp128Decoder::decode(&encoded).unwrap();
930 prop_assert_eq!(values.len(), decoded.len());
931 for (a, b) in values.iter().zip(decoded.iter()) {
932 prop_assert_eq!(a.to_bits(), b.to_bits());
933 }
934 }
935 }
936}