Skip to main content

buff_rs/
codec.rs

1//! BUFF codec for encoding and decoding bounded floating-point arrays.
2//!
3//! This module implements the BUFF (Decomposed Bounded Floats for Fast compression
4//! and queries) algorithm from VLDB 2021.
5//!
6//! The key insight is to store floating-point values as byte-sliced fixed-point
7//! integers, enabling:
8//! 1. Efficient compression through delta encoding and bit packing
9//! 2. Fast predicate evaluation directly on compressed data (with SIMD)
10//!
11//! ## Encoding Format (v1 - without special values)
12//!
13//! The encoded format consists of:
14//! - Base integer (8 bytes, i64)
15//! - Number of values (4 bytes, u32)
16//! - Integer bits (4 bytes, u32)
17//! - Decimal bits (4 bytes, u32)
18//! - Byte slices (column-oriented storage)
19//!
20//! ## Encoding Format (v2 - with special values)
21//!
22//! The v2 format adds a header byte and special value section:
23//! - Version/flags (1 byte): 0x02 for v2 with special values
24//! - Base integer (8 bytes, i64)
25//! - Number of regular values (4 bytes, u32)
26//! - Integer bits (4 bytes, u32)
27//! - Decimal bits (4 bytes, u32)
28//! - Number of special values (4 bytes, u32)
29//! - Special value entries: (index: u32, type: u8) where type is 1=+Inf, 2=-Inf, 3=NaN
30//! - Byte slices (column-oriented storage)
31//!
32//! Each value is converted to a fixed-point representation relative to the base,
33//! then stored in byte-sliced format where each byte position across all values
34//! is stored contiguously.
35
36use crate::bitpack::BitPack;
37use crate::error::BuffError;
38use crate::precision::{get_decimal_length, get_precision_bound, PrecisionBound};
39
40/// Format version byte for encoding without special values (legacy).
41const FORMAT_V1: u8 = 0x01;
42
43/// Format version byte for encoding with special values support.
44const FORMAT_V2: u8 = 0x02;
45
46/// Special value type: positive infinity.
47const SPECIAL_POS_INF: u8 = 1;
48
49/// Special value type: negative infinity.
50const SPECIAL_NEG_INF: u8 = 2;
51
52/// Special value type: NaN.
53const SPECIAL_NAN: u8 = 3;
54
55/// Represents a special floating-point value with its position.
56#[derive(Debug, Clone, Copy, PartialEq)]
57pub struct SpecialValue {
58    /// Index in the original array.
59    pub index: u32,
60    /// Type of special value.
61    pub kind: SpecialValueKind,
62}
63
64/// Types of special floating-point values.
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum SpecialValueKind {
67    /// Positive infinity (+∞).
68    PositiveInfinity,
69    /// Negative infinity (-∞).
70    NegativeInfinity,
71    /// Not a Number (NaN).
72    NaN,
73}
74
75impl SpecialValueKind {
76    fn to_byte(self) -> u8 {
77        match self {
78            SpecialValueKind::PositiveInfinity => SPECIAL_POS_INF,
79            SpecialValueKind::NegativeInfinity => SPECIAL_NEG_INF,
80            SpecialValueKind::NaN => SPECIAL_NAN,
81        }
82    }
83
84    fn from_byte(b: u8) -> Option<Self> {
85        match b {
86            SPECIAL_POS_INF => Some(SpecialValueKind::PositiveInfinity),
87            SPECIAL_NEG_INF => Some(SpecialValueKind::NegativeInfinity),
88            SPECIAL_NAN => Some(SpecialValueKind::NaN),
89            _ => None,
90        }
91    }
92
93    /// Convert to f64 value.
94    pub fn to_f64(self) -> f64 {
95        match self {
96            SpecialValueKind::PositiveInfinity => f64::INFINITY,
97            SpecialValueKind::NegativeInfinity => f64::NEG_INFINITY,
98            SpecialValueKind::NaN => f64::NAN,
99        }
100    }
101}
102
103/// Classify a float value as regular or special.
104pub fn classify_float(v: f64) -> Option<SpecialValueKind> {
105    if v.is_nan() {
106        Some(SpecialValueKind::NaN)
107    } else if v == f64::INFINITY {
108        Some(SpecialValueKind::PositiveInfinity)
109    } else if v == f64::NEG_INFINITY {
110        Some(SpecialValueKind::NegativeInfinity)
111    } else {
112        None
113    }
114}
115
116/// Flip the sign bit of a byte for proper ordering.
117///
118/// This ensures that negative values sort before positive values
119/// when comparing bytes lexicographically.
120#[inline]
121pub fn flip(x: u8) -> u8 {
122    x ^ (1u8 << 7)
123}
124
125/// Compute the ceiling of x/y.
126#[inline]
127fn ceil_div(x: u32, y: u32) -> u32 {
128    (x.saturating_sub(1)) / y + 1
129}
130
131/// BUFF compressor/decompressor for bounded floating-point arrays.
132///
133/// This implements byte-sliced storage where each bit position across
134/// all values is stored contiguously, enabling efficient compression
135/// and SIMD-accelerated queries.
136#[derive(Clone, Debug)]
137pub struct BuffCodec {
138    /// Decimal scale (e.g., 1000 for 3 decimal places).
139    scale: usize,
140}
141
142impl BuffCodec {
143    /// Create a new BUFF codec with the given scale.
144    ///
145    /// # Arguments
146    /// * `scale` - The decimal scale (e.g., 1000 for 3 decimal places, 10000 for 4)
147    ///
148    /// # Example
149    /// ```
150    /// use buff_rs::BuffCodec;
151    ///
152    /// // For 3 decimal places of precision
153    /// let codec = BuffCodec::new(1000);
154    /// ```
155    pub fn new(scale: usize) -> Self {
156        BuffCodec { scale }
157    }
158
159    /// Get the precision (number of decimal places) from the scale.
160    pub fn precision(&self) -> i32 {
161        if self.scale == 0 {
162            0
163        } else {
164            (self.scale as f32).log10() as i32
165        }
166    }
167
168    /// Encode an array of f64 values using BUFF byte-sliced encoding.
169    ///
170    /// # Arguments
171    /// * `data` - Slice of f64 values to encode
172    ///
173    /// # Returns
174    /// A `Vec<u8>` containing the encoded data, or an error.
175    ///
176    /// # Example
177    /// ```
178    /// use buff_rs::BuffCodec;
179    ///
180    /// let codec = BuffCodec::new(1000);
181    /// let data = vec![1.234, 5.678, 9.012];
182    /// let encoded = codec.encode(&data).unwrap();
183    /// ```
184    pub fn encode(&self, data: &[f64]) -> Result<Vec<u8>, BuffError> {
185        if data.is_empty() {
186            return Err(BuffError::EmptyInput);
187        }
188
189        let len = data.len() as u32;
190        let prec = self.precision();
191        let prec_delta = get_precision_bound(prec);
192        let dec_len = get_decimal_length(prec);
193
194        let mut bound = PrecisionBound::new(prec_delta);
195        bound.set_length(0, dec_len);
196
197        // First pass: convert to fixed-point and find min/max
198        let mut fixed_vec = Vec::with_capacity(data.len());
199        let mut min = i64::MAX;
200        let mut max = i64::MIN;
201
202        for &val in data {
203            let fixed = bound.fetch_fixed_aligned(val);
204            if fixed < min {
205                min = fixed;
206            }
207            if fixed > max {
208                max = fixed;
209            }
210            fixed_vec.push(fixed);
211        }
212
213        let delta = max - min;
214        let base_fixed = min;
215
216        // Calculate bit width needed
217        // We need enough bits to represent values 0..=delta (inclusive)
218        // So we need ceil(log2(delta + 1)) bits
219        let cal_int_length = if delta == 0 {
220            0.0
221        } else {
222            ((delta + 1) as f64).log2().ceil()
223        };
224
225        let fixed_len = cal_int_length as u32;
226        let ilen = fixed_len.saturating_sub(dec_len as u32);
227        let dlen = dec_len as u32;
228
229        // Write header
230        let mut bitpack = BitPack::<Vec<u8>>::with_capacity(20 + data.len() * 8);
231        let ubase_fixed = base_fixed as u64;
232
233        bitpack.write(ubase_fixed as u32, 32)?;
234        bitpack.write((ubase_fixed >> 32) as u32, 32)?;
235        bitpack.write(len, 32)?;
236        bitpack.write(ilen, 32)?;
237        bitpack.write(dlen, 32)?;
238
239        // Write byte-sliced data
240        let total_bits = ilen + dlen;
241        let mut remain = total_bits;
242
243        if remain < 8 {
244            // Less than one byte per value
245            let padding = 8 - remain;
246            for &fixed in &fixed_vec {
247                let val = (fixed - base_fixed) as u64;
248                bitpack.write_byte(flip(((val << padding) & 0xFF) as u8))?;
249            }
250        } else {
251            // First byte slice
252            remain -= 8;
253            let fixed_u64: Vec<u64> = if remain > 0 {
254                fixed_vec
255                    .iter()
256                    .map(|&x| {
257                        let val = (x - base_fixed) as u64;
258                        bitpack
259                            .write_byte(flip((val >> remain) as u8))
260                            .expect("write failed");
261                        val
262                    })
263                    .collect()
264            } else {
265                fixed_vec
266                    .iter()
267                    .map(|&x| {
268                        let val = (x - base_fixed) as u64;
269                        bitpack.write_byte(flip(val as u8)).expect("write failed");
270                        val
271                    })
272                    .collect()
273            };
274
275            // Remaining full byte slices
276            while remain >= 8 {
277                remain -= 8;
278                if remain > 0 {
279                    for &d in &fixed_u64 {
280                        bitpack.write_byte(flip((d >> remain) as u8))?;
281                    }
282                } else {
283                    for &d in &fixed_u64 {
284                        bitpack.write_byte(flip(d as u8))?;
285                    }
286                }
287            }
288
289            // Remaining partial byte
290            if remain > 0 {
291                let padding = 8 - remain;
292                for &d in &fixed_u64 {
293                    bitpack.write_byte(flip(((d << padding) & 0xFF) as u8))?;
294                }
295            }
296        }
297
298        Ok(bitpack.into_vec())
299    }
300
301    /// Encode an array of f64 values, including special values (Infinity, NaN).
302    ///
303    /// This method handles special floating-point values by storing them separately
304    /// from regular values. The encoded format uses version 2 which includes a
305    /// special values section.
306    ///
307    /// # Arguments
308    /// * `data` - Slice of f64 values to encode (may contain Infinity, -Infinity, NaN)
309    ///
310    /// # Returns
311    /// A `Vec<u8>` containing the encoded data, or an error.
312    ///
313    /// # Example
314    /// ```
315    /// use buff_rs::BuffCodec;
316    ///
317    /// let codec = BuffCodec::new(1000);
318    /// let data = vec![1.234, f64::INFINITY, 5.678, f64::NAN, -f64::INFINITY];
319    /// let encoded = codec.encode_with_special(&data).unwrap();
320    /// let decoded = codec.decode(&encoded).unwrap();
321    /// assert!(decoded[1].is_infinite() && decoded[1].is_sign_positive());
322    /// assert!(decoded[3].is_nan());
323    /// ```
324    pub fn encode_with_special(&self, data: &[f64]) -> Result<Vec<u8>, BuffError> {
325        if data.is_empty() {
326            return Err(BuffError::EmptyInput);
327        }
328
329        // Separate regular values from special values
330        let mut regular_values: Vec<f64> = Vec::with_capacity(data.len());
331        let mut special_values: Vec<SpecialValue> = Vec::new();
332        let mut index_map: Vec<usize> = Vec::with_capacity(data.len()); // Maps result index to regular_values index
333
334        for (i, &val) in data.iter().enumerate() {
335            if let Some(kind) = classify_float(val) {
336                special_values.push(SpecialValue {
337                    index: i as u32,
338                    kind,
339                });
340            } else {
341                index_map.push(regular_values.len());
342                regular_values.push(val);
343            }
344        }
345
346        // If no special values, use the regular encode (more compact)
347        if special_values.is_empty() {
348            return self.encode(data);
349        }
350
351        // If all values are special
352        if regular_values.is_empty() {
353            let mut result = Vec::with_capacity(1 + 4 + 5 * special_values.len());
354            result.push(FORMAT_V2);
355
356            // Write empty regular section header
357            result.extend_from_slice(&0u64.to_le_bytes()); // base
358            result.extend_from_slice(&0u32.to_le_bytes()); // count
359            result.extend_from_slice(&0u32.to_le_bytes()); // ilen
360            result.extend_from_slice(&0u32.to_le_bytes()); // dlen
361
362            // Write special values
363            result.extend_from_slice(&(special_values.len() as u32).to_le_bytes());
364            for sv in &special_values {
365                result.extend_from_slice(&sv.index.to_le_bytes());
366                result.push(sv.kind.to_byte());
367            }
368
369            return Ok(result);
370        }
371
372        // Encode regular values
373        let prec = self.precision();
374        let prec_delta = get_precision_bound(prec);
375        let dec_len = get_decimal_length(prec);
376
377        let mut bound = PrecisionBound::new(prec_delta);
378        bound.set_length(0, dec_len);
379
380        let mut fixed_vec = Vec::with_capacity(regular_values.len());
381        let mut min = i64::MAX;
382        let mut max = i64::MIN;
383
384        for &val in &regular_values {
385            let fixed = bound.fetch_fixed_aligned(val);
386            if fixed < min {
387                min = fixed;
388            }
389            if fixed > max {
390                max = fixed;
391            }
392            fixed_vec.push(fixed);
393        }
394
395        let delta = max - min;
396        let base_fixed = min;
397
398        let cal_int_length = if delta == 0 {
399            0.0
400        } else {
401            ((delta + 1) as f64).log2().ceil()
402        };
403
404        let fixed_len = cal_int_length as u32;
405        let ilen = fixed_len.saturating_sub(dec_len as u32);
406        let dlen = dec_len as u32;
407
408        // Build result
409        let mut result =
410            Vec::with_capacity(1 + 20 + 4 + 5 * special_values.len() + regular_values.len() * 8);
411
412        // Version byte
413        result.push(FORMAT_V2);
414
415        // Regular values header
416        let ubase_fixed = base_fixed as u64;
417        result.extend_from_slice(&ubase_fixed.to_le_bytes());
418        result.extend_from_slice(&(regular_values.len() as u32).to_le_bytes());
419        result.extend_from_slice(&ilen.to_le_bytes());
420        result.extend_from_slice(&dlen.to_le_bytes());
421
422        // Special values section
423        result.extend_from_slice(&(special_values.len() as u32).to_le_bytes());
424        for sv in &special_values {
425            result.extend_from_slice(&sv.index.to_le_bytes());
426            result.push(sv.kind.to_byte());
427        }
428
429        // Write byte-sliced data for regular values
430        let total_bits = ilen + dlen;
431        let mut remain = total_bits;
432
433        if remain == 0 {
434            // All same value, no data needed
435        } else if remain < 8 {
436            let padding = 8 - remain;
437            for &fixed in &fixed_vec {
438                let val = (fixed - base_fixed) as u64;
439                result.push(flip(((val << padding) & 0xFF) as u8));
440            }
441        } else {
442            remain -= 8;
443            let fixed_u64: Vec<u64> = fixed_vec
444                .iter()
445                .map(|&x| {
446                    let val = (x - base_fixed) as u64;
447                    if remain > 0 {
448                        result.push(flip((val >> remain) as u8));
449                    } else {
450                        result.push(flip(val as u8));
451                    }
452                    val
453                })
454                .collect();
455
456            while remain >= 8 {
457                remain -= 8;
458                if remain > 0 {
459                    for &d in &fixed_u64 {
460                        result.push(flip((d >> remain) as u8));
461                    }
462                } else {
463                    for &d in &fixed_u64 {
464                        result.push(flip(d as u8));
465                    }
466                }
467            }
468
469            if remain > 0 {
470                let padding = 8 - remain;
471                for &d in &fixed_u64 {
472                    result.push(flip(((d << padding) & 0xFF) as u8));
473                }
474            }
475        }
476
477        Ok(result)
478    }
479
480    /// Check if encoded data contains special values (v2 format).
481    pub fn has_special_values(&self, bytes: &[u8]) -> bool {
482        !bytes.is_empty() && bytes[0] == FORMAT_V2
483    }
484
485    /// Decode BUFF-encoded data back to f64 values.
486    ///
487    /// This method automatically detects the format version and handles both
488    /// legacy (v1) and special values (v2) formats.
489    ///
490    /// # Arguments
491    /// * `bytes` - The encoded byte array
492    ///
493    /// # Returns
494    /// A `Vec<f64>` containing the decoded values, or an error.
495    ///
496    /// # Example
497    /// ```
498    /// use buff_rs::BuffCodec;
499    ///
500    /// let codec = BuffCodec::new(1000);
501    /// let data = vec![1.234, 5.678, 9.012];
502    /// let encoded = codec.encode(&data).unwrap();
503    /// let decoded = codec.decode(&encoded).unwrap();
504    /// ```
505    pub fn decode(&self, bytes: &[u8]) -> Result<Vec<f64>, BuffError> {
506        // Check for v2 format (with special values)
507        if !bytes.is_empty() && bytes[0] == FORMAT_V2 {
508            return self.decode_v2(bytes);
509        }
510
511        // Legacy v1 format
512        self.decode_v1(bytes)
513    }
514
515    /// Decode v1 format (legacy, no special values).
516    fn decode_v1(&self, bytes: &[u8]) -> Result<Vec<f64>, BuffError> {
517        if bytes.len() < 20 {
518            return Err(BuffError::InvalidData("header too short".into()));
519        }
520
521        let prec = self.precision();
522        let prec_delta = get_precision_bound(prec);
523
524        let mut bitpack = BitPack::<&[u8]>::new(bytes);
525        let _bound = PrecisionBound::new(prec_delta);
526
527        // Read header
528        let lower = bitpack.read(32)?;
529        let higher = bitpack.read(32)?;
530        let ubase_int = (lower as u64) | ((higher as u64) << 32);
531        let base_int = ubase_int as i64;
532
533        let len = bitpack.read(32)? as usize;
534        let ilen = bitpack.read(32)?;
535        let dlen = bitpack.read(32)?;
536
537        let dec_scl = 2.0f64.powi(dlen as i32);
538        let remain = dlen + ilen;
539        let num_chunks = ceil_div(remain, 8);
540        let padding = num_chunks * 8 - ilen - dlen;
541
542        let mut result = Vec::with_capacity(len);
543
544        match num_chunks {
545            0 => {
546                // All values are the base
547                result.resize(len, base_int as f64 / dec_scl);
548            }
549            1 => {
550                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
551                for &byte in chunk0.iter().take(len) {
552                    let val = (flip(byte) as u64) >> padding;
553                    result.push((base_int + val as i64) as f64 / dec_scl);
554                }
555            }
556            2 => {
557                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
558                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
559                for (&b0, &b1) in chunk0.iter().zip(chunk1.iter()).take(len) {
560                    let val = ((flip(b0) as u64) << (8 - padding)) | ((flip(b1) as u64) >> padding);
561                    result.push((base_int + val as i64) as f64 / dec_scl);
562                }
563            }
564            3 => {
565                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
566                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
567                let chunk2 = bitpack.read_n_byte_unmut(2 * len, len)?;
568                for ((&b0, &b1), &b2) in chunk0
569                    .iter()
570                    .zip(chunk1.iter())
571                    .zip(chunk2.iter())
572                    .take(len)
573                {
574                    let val = ((flip(b0) as u64) << (16 - padding))
575                        | ((flip(b1) as u64) << (8 - padding))
576                        | ((flip(b2) as u64) >> padding);
577                    result.push((base_int + val as i64) as f64 / dec_scl);
578                }
579            }
580            4 => {
581                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
582                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
583                let chunk2 = bitpack.read_n_byte_unmut(2 * len, len)?;
584                let chunk3 = bitpack.read_n_byte_unmut(3 * len, len)?;
585                for (((&b0, &b1), &b2), &b3) in chunk0
586                    .iter()
587                    .zip(chunk1.iter())
588                    .zip(chunk2.iter())
589                    .zip(chunk3.iter())
590                    .take(len)
591                {
592                    let val = ((flip(b0) as u64) << (24 - padding))
593                        | ((flip(b1) as u64) << (16 - padding))
594                        | ((flip(b2) as u64) << (8 - padding))
595                        | ((flip(b3) as u64) >> padding);
596                    result.push((base_int + val as i64) as f64 / dec_scl);
597                }
598            }
599            _ => {
600                return Err(BuffError::InvalidData(format!(
601                    "bit length {} (>{} chunks) not supported",
602                    remain, num_chunks
603                )));
604            }
605        }
606
607        Ok(result)
608    }
609
610    /// Decode v2 format (with special values).
611    fn decode_v2(&self, bytes: &[u8]) -> Result<Vec<f64>, BuffError> {
612        if bytes.len() < 22 {
613            // 1 (version) + 8 (base) + 4 (count) + 4 (ilen) + 4 (dlen) + 4 (special count) = 25 min
614            return Err(BuffError::InvalidData("v2 header too short".into()));
615        }
616
617        let mut pos = 1; // Skip version byte
618
619        // Read regular values header
620        let base_int = i64::from_le_bytes(bytes[pos..pos + 8].try_into().unwrap());
621        pos += 8;
622
623        let regular_count = u32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap()) as usize;
624        pos += 4;
625
626        let ilen = u32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap());
627        pos += 4;
628
629        let dlen = u32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap());
630        pos += 4;
631
632        // Read special values count
633        let special_count = u32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap()) as usize;
634        pos += 4;
635
636        // Read special values
637        let mut special_values: Vec<SpecialValue> = Vec::with_capacity(special_count);
638        for _ in 0..special_count {
639            if pos + 5 > bytes.len() {
640                return Err(BuffError::InvalidData("truncated special values".into()));
641            }
642            let index = u32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap());
643            pos += 4;
644            let kind = SpecialValueKind::from_byte(bytes[pos])
645                .ok_or_else(|| BuffError::InvalidData("invalid special value type".into()))?;
646            pos += 1;
647            special_values.push(SpecialValue { index, kind });
648        }
649
650        // Calculate total count
651        let total_count = regular_count + special_count;
652
653        // If no regular values, just return special values
654        if regular_count == 0 {
655            let mut result = vec![0.0f64; total_count];
656            for sv in &special_values {
657                result[sv.index as usize] = sv.kind.to_f64();
658            }
659            return Ok(result);
660        }
661
662        // Decode regular values from byte-sliced data
663        let dec_scl = 2.0f64.powi(dlen as i32);
664        let remain = dlen + ilen;
665        let num_chunks = ceil_div(remain, 8);
666        let padding = num_chunks * 8 - ilen - dlen;
667
668        let data_start = pos;
669        let mut regular_values: Vec<f64> = Vec::with_capacity(regular_count);
670
671        match num_chunks {
672            0 => {
673                regular_values.resize(regular_count, base_int as f64 / dec_scl);
674            }
675            1 => {
676                let chunk0 = &bytes[data_start..data_start + regular_count];
677                for &byte in chunk0.iter() {
678                    let val = (flip(byte) as u64) >> padding;
679                    regular_values.push((base_int + val as i64) as f64 / dec_scl);
680                }
681            }
682            2 => {
683                let chunk0 = &bytes[data_start..data_start + regular_count];
684                let chunk1 = &bytes[data_start + regular_count..data_start + 2 * regular_count];
685                for (&b0, &b1) in chunk0.iter().zip(chunk1.iter()) {
686                    let val = ((flip(b0) as u64) << (8 - padding)) | ((flip(b1) as u64) >> padding);
687                    regular_values.push((base_int + val as i64) as f64 / dec_scl);
688                }
689            }
690            3 => {
691                let chunk0 = &bytes[data_start..data_start + regular_count];
692                let chunk1 = &bytes[data_start + regular_count..data_start + 2 * regular_count];
693                let chunk2 = &bytes[data_start + 2 * regular_count..data_start + 3 * regular_count];
694                for ((&b0, &b1), &b2) in chunk0.iter().zip(chunk1.iter()).zip(chunk2.iter()) {
695                    let val = ((flip(b0) as u64) << (16 - padding))
696                        | ((flip(b1) as u64) << (8 - padding))
697                        | ((flip(b2) as u64) >> padding);
698                    regular_values.push((base_int + val as i64) as f64 / dec_scl);
699                }
700            }
701            4 => {
702                let chunk0 = &bytes[data_start..data_start + regular_count];
703                let chunk1 = &bytes[data_start + regular_count..data_start + 2 * regular_count];
704                let chunk2 = &bytes[data_start + 2 * regular_count..data_start + 3 * regular_count];
705                let chunk3 = &bytes[data_start + 3 * regular_count..data_start + 4 * regular_count];
706                for (((&b0, &b1), &b2), &b3) in chunk0
707                    .iter()
708                    .zip(chunk1.iter())
709                    .zip(chunk2.iter())
710                    .zip(chunk3.iter())
711                {
712                    let val = ((flip(b0) as u64) << (24 - padding))
713                        | ((flip(b1) as u64) << (16 - padding))
714                        | ((flip(b2) as u64) << (8 - padding))
715                        | ((flip(b3) as u64) >> padding);
716                    regular_values.push((base_int + val as i64) as f64 / dec_scl);
717                }
718            }
719            _ => {
720                return Err(BuffError::InvalidData(format!(
721                    "bit length {} not supported",
722                    remain
723                )));
724            }
725        }
726
727        // Merge regular and special values into final result
728        let mut result = vec![0.0f64; total_count];
729        let mut regular_idx = 0;
730
731        // Create a set of special value indices for O(1) lookup
732        let special_indices: std::collections::HashSet<u32> =
733            special_values.iter().map(|sv| sv.index).collect();
734
735        for (i, slot) in result.iter_mut().enumerate() {
736            if special_indices.contains(&(i as u32)) {
737                // Find the special value for this index
738                if let Some(sv) = special_values.iter().find(|sv| sv.index == i as u32) {
739                    *slot = sv.kind.to_f64();
740                }
741            } else {
742                *slot = regular_values[regular_idx];
743                regular_idx += 1;
744            }
745        }
746
747        Ok(result)
748    }
749
750    /// Compute the sum of all values in the encoded data.
751    ///
752    /// This operates directly on the compressed data without full decompression.
753    pub fn sum(&self, bytes: &[u8]) -> Result<f64, BuffError> {
754        if bytes.len() < 20 {
755            return Err(BuffError::InvalidData("header too short".into()));
756        }
757
758        let prec = self.precision();
759        let prec_delta = get_precision_bound(prec);
760
761        let mut bitpack = BitPack::<&[u8]>::new(bytes);
762        let _bound = PrecisionBound::new(prec_delta);
763
764        // Read header
765        let lower = bitpack.read(32)?;
766        let higher = bitpack.read(32)?;
767        let ubase_int = (lower as u64) | ((higher as u64) << 32);
768        let base_int = ubase_int as i64;
769
770        let len = bitpack.read(32)? as usize;
771        let ilen = bitpack.read(32)?;
772        let dlen = bitpack.read(32)?;
773
774        let dec_scl = 2.0f64.powi(dlen as i32);
775        let remain = dlen + ilen;
776        let num_chunks = ceil_div(remain, 8);
777        let padding = num_chunks * 8 - ilen - dlen;
778
779        let mut sum = 0.0f64;
780
781        match num_chunks {
782            0 => {
783                sum = (base_int as f64 / dec_scl) * len as f64;
784            }
785            1 => {
786                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
787                for &byte in chunk0.iter().take(len) {
788                    let val = (flip(byte) as u64) >> padding;
789                    sum += (base_int + val as i64) as f64 / dec_scl;
790                }
791            }
792            2 => {
793                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
794                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
795                for (&b0, &b1) in chunk0.iter().zip(chunk1.iter()).take(len) {
796                    let val = ((flip(b0) as u64) << (8 - padding)) | ((flip(b1) as u64) >> padding);
797                    sum += (base_int + val as i64) as f64 / dec_scl;
798                }
799            }
800            3 => {
801                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
802                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
803                let chunk2 = bitpack.read_n_byte_unmut(2 * len, len)?;
804                for ((&b0, &b1), &b2) in chunk0
805                    .iter()
806                    .zip(chunk1.iter())
807                    .zip(chunk2.iter())
808                    .take(len)
809                {
810                    let val = ((flip(b0) as u64) << (16 - padding))
811                        | ((flip(b1) as u64) << (8 - padding))
812                        | ((flip(b2) as u64) >> padding);
813                    sum += (base_int + val as i64) as f64 / dec_scl;
814                }
815            }
816            4 => {
817                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
818                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
819                let chunk2 = bitpack.read_n_byte_unmut(2 * len, len)?;
820                let chunk3 = bitpack.read_n_byte_unmut(3 * len, len)?;
821                for (((&b0, &b1), &b2), &b3) in chunk0
822                    .iter()
823                    .zip(chunk1.iter())
824                    .zip(chunk2.iter())
825                    .zip(chunk3.iter())
826                    .take(len)
827                {
828                    let val = ((flip(b0) as u64) << (24 - padding))
829                        | ((flip(b1) as u64) << (16 - padding))
830                        | ((flip(b2) as u64) << (8 - padding))
831                        | ((flip(b3) as u64) >> padding);
832                    sum += (base_int + val as i64) as f64 / dec_scl;
833                }
834            }
835            _ => {
836                return Err(BuffError::InvalidData(format!(
837                    "bit length {} not supported",
838                    remain
839                )));
840            }
841        }
842
843        Ok(sum)
844    }
845
846    /// Find the maximum value in the encoded data.
847    ///
848    /// This operates directly on the compressed data without full decompression.
849    pub fn max(&self, bytes: &[u8]) -> Result<f64, BuffError> {
850        if bytes.len() < 20 {
851            return Err(BuffError::InvalidData("header too short".into()));
852        }
853
854        let prec = self.precision();
855        let prec_delta = get_precision_bound(prec);
856
857        let mut bitpack = BitPack::<&[u8]>::new(bytes);
858        let _bound = PrecisionBound::new(prec_delta);
859
860        // Read header
861        let lower = bitpack.read(32)?;
862        let higher = bitpack.read(32)?;
863        let ubase_int = (lower as u64) | ((higher as u64) << 32);
864        let base_int = ubase_int as i64;
865
866        let len = bitpack.read(32)? as usize;
867        let ilen = bitpack.read(32)?;
868        let dlen = bitpack.read(32)?;
869
870        let dec_scl = 2.0f64.powi(dlen as i32);
871        let remain = dlen + ilen;
872        let num_chunks = ceil_div(remain, 8);
873        let padding = num_chunks * 8 - ilen - dlen;
874
875        let mut max_val = f64::MIN;
876
877        match num_chunks {
878            0 => {
879                max_val = base_int as f64 / dec_scl;
880            }
881            1 => {
882                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
883                for &byte in chunk0.iter().take(len) {
884                    let val = (flip(byte) as u64) >> padding;
885                    let f = (base_int + val as i64) as f64 / dec_scl;
886                    if f > max_val {
887                        max_val = f;
888                    }
889                }
890            }
891            2 => {
892                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
893                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
894                for (&b0, &b1) in chunk0.iter().zip(chunk1.iter()).take(len) {
895                    let val = ((flip(b0) as u64) << (8 - padding)) | ((flip(b1) as u64) >> padding);
896                    let f = (base_int + val as i64) as f64 / dec_scl;
897                    if f > max_val {
898                        max_val = f;
899                    }
900                }
901            }
902            3 => {
903                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
904                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
905                let chunk2 = bitpack.read_n_byte_unmut(2 * len, len)?;
906                for ((&b0, &b1), &b2) in chunk0
907                    .iter()
908                    .zip(chunk1.iter())
909                    .zip(chunk2.iter())
910                    .take(len)
911                {
912                    let val = ((flip(b0) as u64) << (16 - padding))
913                        | ((flip(b1) as u64) << (8 - padding))
914                        | ((flip(b2) as u64) >> padding);
915                    let f = (base_int + val as i64) as f64 / dec_scl;
916                    if f > max_val {
917                        max_val = f;
918                    }
919                }
920            }
921            4 => {
922                let chunk0 = bitpack.read_n_byte_unmut(0, len)?;
923                let chunk1 = bitpack.read_n_byte_unmut(len, len)?;
924                let chunk2 = bitpack.read_n_byte_unmut(2 * len, len)?;
925                let chunk3 = bitpack.read_n_byte_unmut(3 * len, len)?;
926                for (((&b0, &b1), &b2), &b3) in chunk0
927                    .iter()
928                    .zip(chunk1.iter())
929                    .zip(chunk2.iter())
930                    .zip(chunk3.iter())
931                    .take(len)
932                {
933                    let val = ((flip(b0) as u64) << (24 - padding))
934                        | ((flip(b1) as u64) << (16 - padding))
935                        | ((flip(b2) as u64) << (8 - padding))
936                        | ((flip(b3) as u64) >> padding);
937                    let f = (base_int + val as i64) as f64 / dec_scl;
938                    if f > max_val {
939                        max_val = f;
940                    }
941                }
942            }
943            _ => {
944                return Err(BuffError::InvalidData(format!(
945                    "bit length {} not supported",
946                    remain
947                )));
948            }
949        }
950
951        Ok(max_val)
952    }
953
954    /// Count values greater than a threshold.
955    ///
956    /// This operates directly on the compressed data using early termination
957    /// when possible.
958    pub fn count_greater_than(&self, bytes: &[u8], threshold: f64) -> Result<usize, BuffError> {
959        // For now, use decode + filter. SIMD optimization can be added later.
960        let decoded = self.decode(bytes)?;
961        Ok(decoded.iter().filter(|&&v| v > threshold).count())
962    }
963
964    /// Count values equal to a target.
965    pub fn count_equal(&self, bytes: &[u8], target: f64) -> Result<usize, BuffError> {
966        let decoded = self.decode(bytes)?;
967        Ok(decoded
968            .iter()
969            .filter(|&&v| (v - target).abs() < f64::EPSILON)
970            .count())
971    }
972
973    /// Get metadata about the encoded data without decoding.
974    pub fn metadata(&self, bytes: &[u8]) -> Result<BuffMetadata, BuffError> {
975        if bytes.len() < 20 {
976            return Err(BuffError::InvalidData("header too short".into()));
977        }
978
979        let mut bitpack = BitPack::<&[u8]>::new(bytes);
980
981        let lower = bitpack.read(32)?;
982        let higher = bitpack.read(32)?;
983        let ubase_int = (lower as u64) | ((higher as u64) << 32);
984        let base_int = ubase_int as i64;
985
986        let len = bitpack.read(32)?;
987        let ilen = bitpack.read(32)?;
988        let dlen = bitpack.read(32)?;
989
990        Ok(BuffMetadata {
991            base_value: base_int,
992            count: len,
993            integer_bits: ilen,
994            decimal_bits: dlen,
995            total_bytes: bytes.len(),
996        })
997    }
998}
999
1000/// Metadata about BUFF-encoded data.
1001#[derive(Debug, Clone, PartialEq, Eq)]
1002pub struct BuffMetadata {
1003    /// The base value (minimum fixed-point value).
1004    pub base_value: i64,
1005    /// Number of encoded values.
1006    pub count: u32,
1007    /// Number of bits for the integer part.
1008    pub integer_bits: u32,
1009    /// Number of bits for the decimal part.
1010    pub decimal_bits: u32,
1011    /// Total size of the encoded data in bytes.
1012    pub total_bytes: usize,
1013}
1014
1015impl BuffMetadata {
1016    /// Calculate the compression ratio (compressed size / original size).
1017    pub fn compression_ratio(&self) -> f64 {
1018        let original_size = self.count as usize * std::mem::size_of::<f64>();
1019        self.total_bytes as f64 / original_size as f64
1020    }
1021}
1022
1023#[cfg(test)]
1024mod tests {
1025    use super::*;
1026
1027    #[test]
1028    fn test_flip() {
1029        assert_eq!(flip(0), 128);
1030        assert_eq!(flip(128), 0);
1031        assert_eq!(flip(255), 127);
1032    }
1033
1034    #[test]
1035    fn test_encode_decode_roundtrip() {
1036        let codec = BuffCodec::new(1000);
1037        let data = vec![1.234, 5.678, 9.012, -3.456, 0.0];
1038        let encoded = codec.encode(&data).unwrap();
1039        let decoded = codec.decode(&encoded).unwrap();
1040
1041        assert_eq!(data.len(), decoded.len());
1042        for (orig, dec) in data.iter().zip(decoded.iter()) {
1043            assert!((orig - dec).abs() < 0.001, "orig={}, dec={}", orig, dec);
1044        }
1045    }
1046
1047    #[test]
1048    fn test_encode_decode_high_precision() {
1049        let codec = BuffCodec::new(100000); // 5 decimal places
1050        let data = vec![1.23456, 5.67890, 9.01234];
1051        let encoded = codec.encode(&data).unwrap();
1052        let decoded = codec.decode(&encoded).unwrap();
1053
1054        for (orig, dec) in data.iter().zip(decoded.iter()) {
1055            assert!((orig - dec).abs() < 0.00001, "orig={}, dec={}", orig, dec);
1056        }
1057    }
1058
1059    #[test]
1060    fn test_empty_input() {
1061        let codec = BuffCodec::new(1000);
1062        let result = codec.encode(&[]);
1063        assert!(matches!(result, Err(BuffError::EmptyInput)));
1064    }
1065
1066    #[test]
1067    fn test_single_value() {
1068        let codec = BuffCodec::new(1000);
1069        let data = vec![42.123];
1070        let encoded = codec.encode(&data).unwrap();
1071        let decoded = codec.decode(&encoded).unwrap();
1072
1073        assert_eq!(decoded.len(), 1);
1074        assert!((data[0] - decoded[0]).abs() < 0.001);
1075    }
1076
1077    #[test]
1078    fn test_identical_values() {
1079        let codec = BuffCodec::new(1000);
1080        let data = vec![3.14159; 100];
1081        let encoded = codec.encode(&data).unwrap();
1082        let decoded = codec.decode(&encoded).unwrap();
1083
1084        assert_eq!(decoded.len(), 100);
1085        for &val in &decoded {
1086            assert!((val - 3.14159).abs() < 0.001);
1087        }
1088    }
1089
1090    #[test]
1091    fn test_sum() {
1092        let codec = BuffCodec::new(1000);
1093        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
1094        let encoded = codec.encode(&data).unwrap();
1095        let sum = codec.sum(&encoded).unwrap();
1096
1097        assert!((sum - 15.0).abs() < 0.01);
1098    }
1099
1100    #[test]
1101    fn test_max() {
1102        let codec = BuffCodec::new(1000);
1103        let data = vec![1.5, 9.9, 3.2, 7.1, 2.8];
1104        let encoded = codec.encode(&data).unwrap();
1105        let max = codec.max(&encoded).unwrap();
1106
1107        assert!((max - 9.9).abs() < 0.01);
1108    }
1109
1110    #[test]
1111    fn test_metadata() {
1112        let codec = BuffCodec::new(1000);
1113        let data = vec![1.0, 2.0, 3.0];
1114        let encoded = codec.encode(&data).unwrap();
1115        let metadata = codec.metadata(&encoded).unwrap();
1116
1117        assert_eq!(metadata.count, 3);
1118        assert!(metadata.compression_ratio() < 1.5); // Should be reasonably compressed
1119    }
1120
1121    #[test]
1122    fn test_negative_values() {
1123        let codec = BuffCodec::new(1000);
1124        let data = vec![-10.5, -5.25, 0.0, 5.25, 10.5];
1125        let encoded = codec.encode(&data).unwrap();
1126        let decoded = codec.decode(&encoded).unwrap();
1127
1128        for (orig, dec) in data.iter().zip(decoded.iter()) {
1129            assert!((orig - dec).abs() < 0.001, "orig={}, dec={}", orig, dec);
1130        }
1131    }
1132
1133    #[test]
1134    fn test_large_values() {
1135        let codec = BuffCodec::new(100);
1136        let data = vec![1000000.0, 2000000.0, 3000000.0];
1137        let encoded = codec.encode(&data).unwrap();
1138        let decoded = codec.decode(&encoded).unwrap();
1139
1140        for (orig, dec) in data.iter().zip(decoded.iter()) {
1141            assert!((orig - dec).abs() < 1.0, "orig={}, dec={}", orig, dec);
1142        }
1143    }
1144
1145    #[test]
1146    fn test_special_values_infinity() {
1147        let codec = BuffCodec::new(1000);
1148        let data = vec![1.0, f64::INFINITY, 2.0, f64::NEG_INFINITY, 3.0];
1149        let encoded = codec.encode_with_special(&data).unwrap();
1150
1151        assert!(codec.has_special_values(&encoded));
1152
1153        let decoded = codec.decode(&encoded).unwrap();
1154
1155        assert_eq!(decoded.len(), 5);
1156        assert!((decoded[0] - 1.0).abs() < 0.001);
1157        assert!(decoded[1].is_infinite() && decoded[1].is_sign_positive());
1158        assert!((decoded[2] - 2.0).abs() < 0.001);
1159        assert!(decoded[3].is_infinite() && decoded[3].is_sign_negative());
1160        assert!((decoded[4] - 3.0).abs() < 0.001);
1161    }
1162
1163    #[test]
1164    fn test_special_values_nan() {
1165        let codec = BuffCodec::new(1000);
1166        let data = vec![1.0, f64::NAN, 2.0];
1167        let encoded = codec.encode_with_special(&data).unwrap();
1168
1169        let decoded = codec.decode(&encoded).unwrap();
1170
1171        assert_eq!(decoded.len(), 3);
1172        assert!((decoded[0] - 1.0).abs() < 0.001);
1173        assert!(decoded[1].is_nan());
1174        assert!((decoded[2] - 2.0).abs() < 0.001);
1175    }
1176
1177    #[test]
1178    fn test_all_special_values() {
1179        let codec = BuffCodec::new(1000);
1180        let data = vec![f64::INFINITY, f64::NAN, f64::NEG_INFINITY];
1181        let encoded = codec.encode_with_special(&data).unwrap();
1182        let decoded = codec.decode(&encoded).unwrap();
1183
1184        assert_eq!(decoded.len(), 3);
1185        assert!(decoded[0].is_infinite() && decoded[0].is_sign_positive());
1186        assert!(decoded[1].is_nan());
1187        assert!(decoded[2].is_infinite() && decoded[2].is_sign_negative());
1188    }
1189
1190    #[test]
1191    fn test_no_special_values_uses_v1() {
1192        let codec = BuffCodec::new(1000);
1193        let data = vec![1.0, 2.0, 3.0];
1194
1195        // encode_with_special should use v1 when no special values
1196        let encoded = codec.encode_with_special(&data).unwrap();
1197        assert!(!codec.has_special_values(&encoded));
1198
1199        let decoded = codec.decode(&encoded).unwrap();
1200        assert_eq!(decoded.len(), 3);
1201    }
1202
1203    #[test]
1204    fn test_special_value_kind() {
1205        assert_eq!(SpecialValueKind::PositiveInfinity.to_f64(), f64::INFINITY);
1206        assert_eq!(
1207            SpecialValueKind::NegativeInfinity.to_f64(),
1208            f64::NEG_INFINITY
1209        );
1210        assert!(SpecialValueKind::NaN.to_f64().is_nan());
1211    }
1212
1213    #[test]
1214    fn test_classify_float() {
1215        assert_eq!(classify_float(1.0), None);
1216        assert_eq!(
1217            classify_float(f64::INFINITY),
1218            Some(SpecialValueKind::PositiveInfinity)
1219        );
1220        assert_eq!(
1221            classify_float(f64::NEG_INFINITY),
1222            Some(SpecialValueKind::NegativeInfinity)
1223        );
1224        assert_eq!(classify_float(f64::NAN), Some(SpecialValueKind::NaN));
1225    }
1226
1227    #[test]
1228    fn test_special_value_kind_from_byte() {
1229        assert_eq!(
1230            SpecialValueKind::from_byte(SPECIAL_POS_INF),
1231            Some(SpecialValueKind::PositiveInfinity)
1232        );
1233        assert_eq!(
1234            SpecialValueKind::from_byte(SPECIAL_NEG_INF),
1235            Some(SpecialValueKind::NegativeInfinity)
1236        );
1237        assert_eq!(
1238            SpecialValueKind::from_byte(SPECIAL_NAN),
1239            Some(SpecialValueKind::NaN)
1240        );
1241        assert_eq!(SpecialValueKind::from_byte(0), None);
1242        assert_eq!(SpecialValueKind::from_byte(99), None);
1243    }
1244
1245    #[test]
1246    fn test_special_value_kind_to_byte() {
1247        assert_eq!(
1248            SpecialValueKind::PositiveInfinity.to_byte(),
1249            SPECIAL_POS_INF
1250        );
1251        assert_eq!(
1252            SpecialValueKind::NegativeInfinity.to_byte(),
1253            SPECIAL_NEG_INF
1254        );
1255        assert_eq!(SpecialValueKind::NaN.to_byte(), SPECIAL_NAN);
1256    }
1257
1258    #[test]
1259    fn test_ceil_div() {
1260        // Note: ceil_div(0, y) returns 1 due to saturating_sub implementation
1261        assert_eq!(ceil_div(0, 8), 1);
1262        assert_eq!(ceil_div(1, 8), 1);
1263        assert_eq!(ceil_div(8, 8), 1);
1264        assert_eq!(ceil_div(9, 8), 2);
1265        assert_eq!(ceil_div(16, 8), 2);
1266        assert_eq!(ceil_div(17, 8), 3);
1267    }
1268
1269    #[test]
1270    fn test_count_greater_than() {
1271        let codec = BuffCodec::new(1000);
1272        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
1273        let encoded = codec.encode(&data).unwrap();
1274
1275        assert_eq!(codec.count_greater_than(&encoded, 0.0).unwrap(), 5);
1276        assert_eq!(codec.count_greater_than(&encoded, 2.5).unwrap(), 3);
1277        assert_eq!(codec.count_greater_than(&encoded, 5.0).unwrap(), 0);
1278    }
1279
1280    #[test]
1281    fn test_count_equal() {
1282        let codec = BuffCodec::new(1000);
1283        let data = vec![1.0, 2.0, 2.0, 3.0, 2.0];
1284        let encoded = codec.encode(&data).unwrap();
1285
1286        assert_eq!(codec.count_equal(&encoded, 2.0).unwrap(), 3);
1287        assert_eq!(codec.count_equal(&encoded, 1.0).unwrap(), 1);
1288        assert_eq!(codec.count_equal(&encoded, 99.0).unwrap(), 0);
1289    }
1290
1291    #[test]
1292    fn test_precision_method() {
1293        let codec1 = BuffCodec::new(1000);
1294        assert_eq!(codec1.precision(), 3);
1295
1296        let codec2 = BuffCodec::new(100);
1297        assert_eq!(codec2.precision(), 2);
1298
1299        let codec3 = BuffCodec::new(10);
1300        assert_eq!(codec3.precision(), 1);
1301
1302        let codec0 = BuffCodec::new(0);
1303        assert_eq!(codec0.precision(), 0);
1304    }
1305
1306    #[test]
1307    fn test_has_special_values() {
1308        let codec = BuffCodec::new(1000);
1309
1310        // V1 format (no special values)
1311        let data_v1 = vec![1.0, 2.0, 3.0];
1312        let encoded_v1 = codec.encode(&data_v1).unwrap();
1313        assert!(!codec.has_special_values(&encoded_v1));
1314
1315        // V2 format (with special values)
1316        let data_v2 = vec![1.0, f64::INFINITY, 3.0];
1317        let encoded_v2 = codec.encode_with_special(&data_v2).unwrap();
1318        assert!(codec.has_special_values(&encoded_v2));
1319
1320        // Empty data
1321        assert!(!codec.has_special_values(&[]));
1322    }
1323
1324    #[test]
1325    fn test_decode_invalid_data_v1() {
1326        let codec = BuffCodec::new(1000);
1327
1328        // Too short header
1329        let short_data = vec![0u8; 10];
1330        assert!(codec.decode(&short_data).is_err());
1331    }
1332
1333    #[test]
1334    fn test_decode_invalid_data_v2() {
1335        let codec = BuffCodec::new(1000);
1336
1337        // V2 format marker but too short
1338        let mut short_v2 = vec![FORMAT_V2];
1339        short_v2.extend_from_slice(&[0u8; 10]);
1340        assert!(codec.decode(&short_v2).is_err());
1341    }
1342
1343    #[test]
1344    fn test_encode_with_special_empty() {
1345        let codec = BuffCodec::new(1000);
1346        let result = codec.encode_with_special(&[]);
1347        assert!(matches!(result, Err(BuffError::EmptyInput)));
1348    }
1349
1350    #[test]
1351    fn test_metadata_compression_ratio() {
1352        let codec = BuffCodec::new(1000);
1353        let data: Vec<f64> = (0..100).map(|i| i as f64 / 10.0).collect();
1354        let encoded = codec.encode(&data).unwrap();
1355        let metadata = codec.metadata(&encoded).unwrap();
1356
1357        // Compression ratio should be reasonable (less than 1.0 for good compression)
1358        let ratio = metadata.compression_ratio();
1359        assert!(ratio > 0.0);
1360        assert!(ratio < 2.0); // Should at least not be wildly inefficient
1361    }
1362
1363    #[test]
1364    fn test_special_value_struct() {
1365        let sv = SpecialValue {
1366            index: 42,
1367            kind: SpecialValueKind::NaN,
1368        };
1369        assert_eq!(sv.index, 42);
1370        assert_eq!(sv.kind, SpecialValueKind::NaN);
1371    }
1372
1373    #[test]
1374    fn test_buff_metadata_debug() {
1375        let metadata = BuffMetadata {
1376            base_value: 100,
1377            count: 10,
1378            integer_bits: 8,
1379            decimal_bits: 4,
1380            total_bytes: 50,
1381        };
1382        let debug_str = format!("{:?}", metadata);
1383        assert!(debug_str.contains("BuffMetadata"));
1384    }
1385
1386    #[test]
1387    fn test_encode_decode_wide_range() {
1388        let codec = BuffCodec::new(100);
1389        // Wide range data that requires multiple byte chunks
1390        let data: Vec<f64> = (0..100).map(|i| i as f64 * 1000.0).collect();
1391        let encoded = codec.encode(&data).unwrap();
1392        let decoded = codec.decode(&encoded).unwrap();
1393
1394        assert_eq!(data.len(), decoded.len());
1395        for (orig, dec) in data.iter().zip(decoded.iter()) {
1396            assert!((orig - dec).abs() < 10.0);
1397        }
1398    }
1399
1400    #[test]
1401    fn test_encode_decode_zero_delta() {
1402        let codec = BuffCodec::new(1000);
1403        // All identical values (delta = 0)
1404        let data = vec![42.0; 50];
1405        let encoded = codec.encode(&data).unwrap();
1406        let decoded = codec.decode(&encoded).unwrap();
1407
1408        assert_eq!(decoded.len(), 50);
1409        for val in decoded {
1410            assert!((val - 42.0).abs() < 0.001);
1411        }
1412    }
1413
1414    #[test]
1415    fn test_sum_identical_values() {
1416        let codec = BuffCodec::new(1000);
1417        let data = vec![5.0; 100];
1418        let encoded = codec.encode(&data).unwrap();
1419        let sum = codec.sum(&encoded).unwrap();
1420
1421        assert!((sum - 500.0).abs() < 0.1);
1422    }
1423
1424    #[test]
1425    fn test_max_identical_values() {
1426        let codec = BuffCodec::new(1000);
1427        let data = vec![7.5; 100];
1428        let encoded = codec.encode(&data).unwrap();
1429        let max = codec.max(&encoded).unwrap();
1430
1431        assert!((max - 7.5).abs() < 0.01);
1432    }
1433
1434    #[test]
1435    fn test_encode_decode_three_chunks() {
1436        // Create data that requires 3 byte chunks (17-24 bits)
1437        let codec = BuffCodec::new(10000); // 4 decimal places
1438        let data: Vec<f64> = (0..100).map(|i| i as f64 * 100.0 + 0.1234).collect();
1439        let encoded = codec.encode(&data).unwrap();
1440        let decoded = codec.decode(&encoded).unwrap();
1441
1442        assert_eq!(data.len(), decoded.len());
1443        for (orig, dec) in data.iter().zip(decoded.iter()) {
1444            assert!((orig - dec).abs() < 0.001);
1445        }
1446    }
1447
1448    #[test]
1449    fn test_encode_decode_four_chunks() {
1450        // Create data that requires 4 byte chunks (25-32 bits)
1451        // Using smaller scale and range to stay within 32 bits
1452        let codec = BuffCodec::new(1000); // 3 decimal places
1453        let data: Vec<f64> = (0..100).map(|i| i as f64 * 1000.0).collect();
1454        let encoded = codec.encode(&data).unwrap();
1455        let decoded = codec.decode(&encoded).unwrap();
1456
1457        assert_eq!(data.len(), decoded.len());
1458        for (orig, dec) in data.iter().zip(decoded.iter()) {
1459            assert!((orig - dec).abs() < 0.1);
1460        }
1461    }
1462
1463    #[test]
1464    fn test_sum_three_chunks() {
1465        let codec = BuffCodec::new(10000);
1466        let data: Vec<f64> = (1..=10).map(|i| i as f64 * 100.0).collect();
1467        let encoded = codec.encode(&data).unwrap();
1468        let sum = codec.sum(&encoded).unwrap();
1469
1470        let expected: f64 = (1..=10).map(|i| i as f64 * 100.0).sum();
1471        assert!((sum - expected).abs() < 1.0);
1472    }
1473
1474    #[test]
1475    fn test_sum_four_chunks() {
1476        let codec = BuffCodec::new(1000);
1477        let data: Vec<f64> = (1..=10).map(|i| i as f64 * 1000.0).collect();
1478        let encoded = codec.encode(&data).unwrap();
1479        let sum = codec.sum(&encoded).unwrap();
1480
1481        let expected: f64 = (1..=10).map(|i| i as f64 * 1000.0).sum();
1482        assert!((sum - expected).abs() < 10.0);
1483    }
1484
1485    #[test]
1486    fn test_max_three_chunks() {
1487        let codec = BuffCodec::new(10000);
1488        let data: Vec<f64> = vec![100.0, 200.0, 5000.0, 300.0];
1489        let encoded = codec.encode(&data).unwrap();
1490        let max = codec.max(&encoded).unwrap();
1491
1492        assert!((max - 5000.0).abs() < 1.0);
1493    }
1494
1495    #[test]
1496    fn test_max_four_chunks() {
1497        let codec = BuffCodec::new(1000);
1498        let data: Vec<f64> = vec![100.0, 50000.0, 3000.0, 4000.0];
1499        let encoded = codec.encode(&data).unwrap();
1500        let max = codec.max(&encoded).unwrap();
1501
1502        assert!((max - 50000.0).abs() < 1.0);
1503    }
1504
1505    #[test]
1506    fn test_decode_v2_truncated_special_values() {
1507        let codec = BuffCodec::new(1000);
1508
1509        // Create v2 format with special values count but truncated special values section
1510        let mut data = vec![FORMAT_V2];
1511        data.extend_from_slice(&0i64.to_le_bytes()); // base
1512        data.extend_from_slice(&0u32.to_le_bytes()); // count
1513        data.extend_from_slice(&0u32.to_le_bytes()); // ilen
1514        data.extend_from_slice(&0u32.to_le_bytes()); // dlen
1515        data.extend_from_slice(&5u32.to_le_bytes()); // special count = 5 (but we won't add them)
1516                                                     // No special values data - truncated
1517
1518        let result = codec.decode(&data);
1519        assert!(result.is_err());
1520    }
1521
1522    #[test]
1523    fn test_decode_v2_invalid_special_type() {
1524        let codec = BuffCodec::new(1000);
1525
1526        // Create v2 format with invalid special value type
1527        let mut data = vec![FORMAT_V2];
1528        data.extend_from_slice(&0i64.to_le_bytes()); // base
1529        data.extend_from_slice(&0u32.to_le_bytes()); // count
1530        data.extend_from_slice(&0u32.to_le_bytes()); // ilen
1531        data.extend_from_slice(&0u32.to_le_bytes()); // dlen
1532        data.extend_from_slice(&1u32.to_le_bytes()); // special count = 1
1533        data.extend_from_slice(&0u32.to_le_bytes()); // index
1534        data.push(99); // invalid special type
1535
1536        let result = codec.decode(&data);
1537        assert!(result.is_err());
1538    }
1539
1540    #[test]
1541    fn test_sum_error_handling() {
1542        let codec = BuffCodec::new(1000);
1543        let short_data = vec![0u8; 10]; // Too short for header
1544        let result = codec.sum(&short_data);
1545        assert!(result.is_err());
1546    }
1547
1548    #[test]
1549    fn test_max_error_handling() {
1550        let codec = BuffCodec::new(1000);
1551        let short_data = vec![0u8; 10]; // Too short for header
1552        let result = codec.max(&short_data);
1553        assert!(result.is_err());
1554    }
1555
1556    #[test]
1557    fn test_metadata_error_handling() {
1558        let codec = BuffCodec::new(1000);
1559        let short_data = vec![0u8; 10]; // Too short for header
1560        let result = codec.metadata(&short_data);
1561        assert!(result.is_err());
1562    }
1563
1564    #[test]
1565    fn test_count_greater_than_with_special() {
1566        let codec = BuffCodec::new(1000);
1567        let data = vec![1.0, f64::INFINITY, 3.0, f64::NAN, 5.0];
1568        let encoded = codec.encode_with_special(&data).unwrap();
1569
1570        // Infinity is greater than any finite threshold
1571        let count = codec.count_greater_than(&encoded, 2.0).unwrap();
1572        assert!(count >= 2); // 3.0 and 5.0 are definitely > 2.0
1573    }
1574
1575    #[test]
1576    fn test_encode_decode_two_chunks() {
1577        // Create data that requires 2 byte chunks (9-16 bits)
1578        let codec = BuffCodec::new(100);
1579        let data: Vec<f64> = (0..100).map(|i| i as f64 * 10.0).collect();
1580        let encoded = codec.encode(&data).unwrap();
1581        let decoded = codec.decode(&encoded).unwrap();
1582
1583        assert_eq!(data.len(), decoded.len());
1584        for (orig, dec) in data.iter().zip(decoded.iter()) {
1585            assert!((orig - dec).abs() < 0.1);
1586        }
1587    }
1588
1589    #[test]
1590    fn test_sum_two_chunks() {
1591        let codec = BuffCodec::new(100);
1592        let data: Vec<f64> = (1..=10).map(|i| i as f64 * 10.0).collect();
1593        let encoded = codec.encode(&data).unwrap();
1594        let sum = codec.sum(&encoded).unwrap();
1595
1596        let expected: f64 = (1..=10).map(|i| i as f64 * 10.0).sum();
1597        assert!((sum - expected).abs() < 1.0);
1598    }
1599
1600    #[test]
1601    fn test_max_two_chunks() {
1602        let codec = BuffCodec::new(100);
1603        let data: Vec<f64> = vec![10.0, 50.0, 30.0, 40.0];
1604        let encoded = codec.encode(&data).unwrap();
1605        let max = codec.max(&encoded).unwrap();
1606
1607        assert!((max - 50.0).abs() < 0.1);
1608    }
1609
1610    #[test]
1611    fn test_encode_with_special_mixed_regular_and_special() {
1612        let codec = BuffCodec::new(1000);
1613
1614        // Mix of regular and special values at various positions
1615        let data = vec![
1616            f64::NEG_INFINITY, // start with special
1617            1.0,
1618            2.0,
1619            f64::NAN,
1620            3.0,
1621            f64::INFINITY, // end with special
1622        ];
1623
1624        let encoded = codec.encode_with_special(&data).unwrap();
1625        let decoded = codec.decode(&encoded).unwrap();
1626
1627        assert_eq!(decoded.len(), 6);
1628        assert!(decoded[0].is_infinite() && decoded[0].is_sign_negative());
1629        assert!((decoded[1] - 1.0).abs() < 0.001);
1630        assert!((decoded[2] - 2.0).abs() < 0.001);
1631        assert!(decoded[3].is_nan());
1632        assert!((decoded[4] - 3.0).abs() < 0.001);
1633        assert!(decoded[5].is_infinite() && decoded[5].is_sign_positive());
1634    }
1635
1636    #[test]
1637    fn test_buff_codec_clone() {
1638        let codec1 = BuffCodec::new(1000);
1639        let codec2 = codec1.clone();
1640        assert_eq!(codec1.precision(), codec2.precision());
1641    }
1642
1643    #[test]
1644    fn test_buff_codec_debug() {
1645        let codec = BuffCodec::new(1000);
1646        let debug_str = format!("{:?}", codec);
1647        assert!(debug_str.contains("BuffCodec"));
1648    }
1649
1650    #[test]
1651    fn test_special_value_debug() {
1652        let sv = SpecialValue {
1653            index: 5,
1654            kind: SpecialValueKind::NaN,
1655        };
1656        let debug_str = format!("{:?}", sv);
1657        assert!(debug_str.contains("SpecialValue"));
1658        assert!(debug_str.contains("NaN"));
1659    }
1660
1661    #[test]
1662    fn test_special_value_clone() {
1663        let sv1 = SpecialValue {
1664            index: 5,
1665            kind: SpecialValueKind::PositiveInfinity,
1666        };
1667        let sv2 = sv1;
1668        assert_eq!(sv1, sv2);
1669    }
1670
1671    #[test]
1672    fn test_special_value_kind_eq() {
1673        assert_eq!(SpecialValueKind::NaN, SpecialValueKind::NaN);
1674        assert_eq!(
1675            SpecialValueKind::PositiveInfinity,
1676            SpecialValueKind::PositiveInfinity
1677        );
1678        assert_ne!(SpecialValueKind::NaN, SpecialValueKind::PositiveInfinity);
1679    }
1680
1681    #[test]
1682    fn test_buff_metadata_eq() {
1683        let m1 = BuffMetadata {
1684            base_value: 100,
1685            count: 10,
1686            integer_bits: 8,
1687            decimal_bits: 4,
1688            total_bytes: 50,
1689        };
1690        let m2 = BuffMetadata {
1691            base_value: 100,
1692            count: 10,
1693            integer_bits: 8,
1694            decimal_bits: 4,
1695            total_bytes: 50,
1696        };
1697        assert_eq!(m1, m2);
1698    }
1699
1700    #[test]
1701    fn test_buff_metadata_clone() {
1702        let m1 = BuffMetadata {
1703            base_value: 100,
1704            count: 10,
1705            integer_bits: 8,
1706            decimal_bits: 4,
1707            total_bytes: 50,
1708        };
1709        let m2 = m1.clone();
1710        assert_eq!(m1, m2);
1711    }
1712
1713    #[test]
1714    fn test_encode_decode_small_delta_less_than_byte() {
1715        // Create data where delta fits in less than 8 bits
1716        let codec = BuffCodec::new(10);
1717        let data = vec![1.0, 1.1, 1.2, 1.3]; // Very small delta
1718        let encoded = codec.encode(&data).unwrap();
1719        let decoded = codec.decode(&encoded).unwrap();
1720
1721        assert_eq!(data.len(), decoded.len());
1722        for (orig, dec) in data.iter().zip(decoded.iter()) {
1723            assert!((orig - dec).abs() < 0.2);
1724        }
1725    }
1726
1727    #[test]
1728    fn test_v2_format_with_zero_regular_values_decoding() {
1729        let codec = BuffCodec::new(1000);
1730
1731        // All special values
1732        let data = vec![f64::INFINITY, f64::NAN];
1733        let encoded = codec.encode_with_special(&data).unwrap();
1734        let decoded = codec.decode(&encoded).unwrap();
1735
1736        assert_eq!(decoded.len(), 2);
1737        assert!(decoded[0].is_infinite());
1738        assert!(decoded[1].is_nan());
1739    }
1740
1741    #[test]
1742    fn test_encode_with_special_small_delta_less_than_byte() {
1743        // Test encode_with_special with data that requires < 8 bits (lines 436-439)
1744        let codec = BuffCodec::new(10);
1745        let data = vec![1.0, f64::INFINITY, 1.1, 1.2]; // Small delta with special value
1746        let encoded = codec.encode_with_special(&data).unwrap();
1747        let decoded = codec.decode(&encoded).unwrap();
1748
1749        assert_eq!(decoded.len(), 4);
1750        assert!((decoded[0] - 1.0).abs() < 0.2);
1751        assert!(decoded[1].is_infinite());
1752        assert!((decoded[2] - 1.1).abs() < 0.2);
1753        assert!((decoded[3] - 1.2).abs() < 0.2);
1754    }
1755
1756    #[test]
1757    fn test_encode_with_special_exactly_8_bits() {
1758        // Test encode_with_special where remain becomes exactly 0 after first byte (line 450)
1759        let codec = BuffCodec::new(1);
1760        let data = vec![0.0, f64::NAN, 10.0]; // Range of ~10 with scale 1
1761        let encoded = codec.encode_with_special(&data).unwrap();
1762        let decoded = codec.decode(&encoded).unwrap();
1763
1764        assert_eq!(decoded.len(), 3);
1765        assert!((decoded[0] - 0.0).abs() < 1.0);
1766        assert!(decoded[1].is_nan());
1767        assert!((decoded[2] - 10.0).abs() < 1.0);
1768    }
1769
1770    #[test]
1771    fn test_encode_with_special_multi_byte() {
1772        // Test encode_with_special with multiple byte chunks (lines 456-466)
1773        let codec = BuffCodec::new(100);
1774        let data = vec![0.0, f64::INFINITY, 1000.0, f64::NEG_INFINITY, 500.0];
1775        let encoded = codec.encode_with_special(&data).unwrap();
1776        let decoded = codec.decode(&encoded).unwrap();
1777
1778        assert_eq!(decoded.len(), 5);
1779        assert!((decoded[0] - 0.0).abs() < 1.0);
1780        assert!(decoded[1].is_infinite() && decoded[1].is_sign_positive());
1781        assert!((decoded[2] - 1000.0).abs() < 1.0);
1782        assert!(decoded[3].is_infinite() && decoded[3].is_sign_negative());
1783        assert!((decoded[4] - 500.0).abs() < 1.0);
1784    }
1785
1786    #[test]
1787    fn test_encode_with_special_remain_zero_after_loop() {
1788        // Test where remain becomes exactly 0 in the while loop (lines 462-464)
1789        let codec = BuffCodec::new(10);
1790        let data = vec![0.0, f64::NAN, 100.0, 200.0]; // Range requiring ~16 bits
1791        let encoded = codec.encode_with_special(&data).unwrap();
1792        let decoded = codec.decode(&encoded).unwrap();
1793
1794        assert_eq!(decoded.len(), 4);
1795        assert!(decoded[1].is_nan());
1796    }
1797
1798    #[test]
1799    fn test_encode_with_special_partial_byte_remainder() {
1800        // Test where there's a partial byte remainder (lines 469-474)
1801        let codec = BuffCodec::new(100);
1802        let data = vec![0.0, f64::INFINITY, 50.0, 100.0, 150.0];
1803        let encoded = codec.encode_with_special(&data).unwrap();
1804        let decoded = codec.decode(&encoded).unwrap();
1805
1806        assert_eq!(decoded.len(), 5);
1807        assert!(decoded[1].is_infinite());
1808    }
1809
1810    #[test]
1811    fn test_encode_exactly_8_bits() {
1812        // Test encode (v1) where remain becomes 0 after first byte (lines 265-270)
1813        let codec = BuffCodec::new(1);
1814        let data = vec![0.0, 10.0, 5.0]; // Small range with low scale
1815        let encoded = codec.encode(&data).unwrap();
1816        let decoded = codec.decode(&encoded).unwrap();
1817
1818        assert_eq!(decoded.len(), 3);
1819        for (orig, dec) in data.iter().zip(decoded.iter()) {
1820            assert!((orig - dec).abs() < 1.0);
1821        }
1822    }
1823
1824    #[test]
1825    fn test_decode_v2_two_chunks() {
1826        // Test decode_v2 with 2 byte chunks (lines 682-689)
1827        let codec = BuffCodec::new(100);
1828        let data = vec![0.0, f64::NAN, 500.0];
1829        let encoded = codec.encode_with_special(&data).unwrap();
1830        let decoded = codec.decode(&encoded).unwrap();
1831
1832        assert_eq!(decoded.len(), 3);
1833        assert!(decoded[1].is_nan());
1834    }
1835
1836    #[test]
1837    fn test_decode_v2_three_chunks() {
1838        // Test decode_v2 with 3 byte chunks (lines 691-702)
1839        let codec = BuffCodec::new(1000);
1840        let data = vec![0.0, f64::INFINITY, 50000.0];
1841        let encoded = codec.encode_with_special(&data).unwrap();
1842        let decoded = codec.decode(&encoded).unwrap();
1843
1844        assert_eq!(decoded.len(), 3);
1845        assert!(decoded[1].is_infinite());
1846    }
1847
1848    #[test]
1849    fn test_decode_v2_four_chunks() {
1850        // Test decode_v2 with 4 byte chunks (lines 704-723)
1851        let codec = BuffCodec::new(1000);
1852        let data = vec![0.0, f64::NEG_INFINITY, 100000.0, 50000.0];
1853        let encoded = codec.encode_with_special(&data).unwrap();
1854        let decoded = codec.decode(&encoded).unwrap();
1855
1856        assert_eq!(decoded.len(), 4);
1857        assert!(decoded[1].is_infinite() && decoded[1].is_sign_negative());
1858    }
1859
1860    #[test]
1861    fn test_sum_v2_format() {
1862        // Test sum with v2 format data
1863        let codec = BuffCodec::new(1000);
1864        let data = vec![1.0, 2.0, 3.0]; // No special values but test sum
1865        let encoded = codec.encode(&data).unwrap();
1866        let sum = codec.sum(&encoded).unwrap();
1867        assert!((sum - 6.0).abs() < 0.01);
1868    }
1869
1870    #[test]
1871    fn test_max_v2_format() {
1872        // Test max with v2 format data
1873        let codec = BuffCodec::new(1000);
1874        let data = vec![1.0, 5.0, 3.0];
1875        let encoded = codec.encode(&data).unwrap();
1876        let max = codec.max(&encoded).unwrap();
1877        assert!((max - 5.0).abs() < 0.01);
1878    }
1879
1880    #[test]
1881    fn test_decode_v1_unsupported_bit_length() {
1882        // Test decode_v1 with unsupported bit length (lines 597-602)
1883        let codec = BuffCodec::new(1000);
1884
1885        // Craft invalid data with ilen + dlen > 32
1886        let mut data = Vec::new();
1887        // Base (8 bytes)
1888        data.extend_from_slice(&0u64.to_le_bytes());
1889        // Count (4 bytes)
1890        data.extend_from_slice(&10u32.to_le_bytes());
1891        // ilen (4 bytes) - set to 20
1892        data.extend_from_slice(&20u32.to_le_bytes());
1893        // dlen (4 bytes) - set to 20 (total 40 bits > 32)
1894        data.extend_from_slice(&20u32.to_le_bytes());
1895
1896        let result = codec.decode(&data);
1897        assert!(result.is_err());
1898    }
1899
1900    #[test]
1901    fn test_decode_v2_unsupported_bit_length() {
1902        // Test decode_v2 with unsupported bit length (lines 726-731)
1903        let codec = BuffCodec::new(1000);
1904
1905        // Craft v2 format with unsupported bit length
1906        let mut data = vec![FORMAT_V2];
1907        // Base (8 bytes)
1908        data.extend_from_slice(&0i64.to_le_bytes());
1909        // Regular count (4 bytes)
1910        data.extend_from_slice(&5u32.to_le_bytes());
1911        // ilen (4 bytes) - set to 20
1912        data.extend_from_slice(&20u32.to_le_bytes());
1913        // dlen (4 bytes) - set to 20 (total 40 bits > 32)
1914        data.extend_from_slice(&20u32.to_le_bytes());
1915        // Special count (4 bytes)
1916        data.extend_from_slice(&0u32.to_le_bytes());
1917        // Add some padding data
1918        data.extend_from_slice(&[0u8; 50]);
1919
1920        let result = codec.decode(&data);
1921        assert!(result.is_err());
1922    }
1923
1924    #[test]
1925    fn test_sum_unsupported_bit_length() {
1926        // Test sum with unsupported bit length (lines 840-845)
1927        let codec = BuffCodec::new(1000);
1928
1929        // Craft invalid data with ilen + dlen > 32
1930        let mut data = Vec::new();
1931        data.extend_from_slice(&0u64.to_le_bytes()); // base
1932        data.extend_from_slice(&10u32.to_le_bytes()); // count
1933        data.extend_from_slice(&20u32.to_le_bytes()); // ilen
1934        data.extend_from_slice(&20u32.to_le_bytes()); // dlen (total 40 > 32)
1935
1936        let result = codec.sum(&data);
1937        assert!(result.is_err());
1938    }
1939
1940    #[test]
1941    fn test_max_unsupported_bit_length() {
1942        // Test max with unsupported bit length (lines 946-951)
1943        let codec = BuffCodec::new(1000);
1944
1945        // Craft invalid data with ilen + dlen > 32
1946        let mut data = Vec::new();
1947        data.extend_from_slice(&0u64.to_le_bytes()); // base
1948        data.extend_from_slice(&10u32.to_le_bytes()); // count
1949        data.extend_from_slice(&20u32.to_le_bytes()); // ilen
1950        data.extend_from_slice(&20u32.to_le_bytes()); // dlen (total 40 > 32)
1951
1952        let result = codec.max(&data);
1953        assert!(result.is_err());
1954    }
1955}