Skip to main content

oximedia_codec/reconstruct/
residual.rs

1//! Residual buffer management for transform coefficients.
2//!
3//! This module provides buffers for storing and manipulating transform
4//! coefficients (residuals) during video decoding. Residuals are the
5//! difference between the prediction and the actual pixel values.
6
7#![forbid(unsafe_code)]
8#![allow(clippy::unreadable_literal)]
9#![allow(clippy::items_after_statements)]
10#![allow(clippy::unnecessary_wraps)]
11#![allow(clippy::struct_excessive_bools)]
12#![allow(clippy::identity_op)]
13#![allow(clippy::range_plus_one)]
14#![allow(clippy::needless_range_loop)]
15#![allow(clippy::useless_conversion)]
16#![allow(clippy::redundant_closure_for_method_calls)]
17#![allow(clippy::single_match_else)]
18#![allow(dead_code)]
19#![allow(clippy::doc_markdown)]
20#![allow(clippy::unused_self)]
21#![allow(clippy::trivially_copy_pass_by_ref)]
22#![allow(clippy::cast_possible_truncation)]
23#![allow(clippy::cast_sign_loss)]
24#![allow(clippy::missing_errors_doc)]
25#![allow(clippy::similar_names)]
26#![allow(clippy::cast_precision_loss)]
27#![allow(clippy::cast_lossless)]
28#![allow(clippy::manual_div_ceil)]
29
30use super::{
31    ChromaSubsampling, FrameBuffer, PlaneBuffer, PlaneType, ReconstructResult, ReconstructionError,
32};
33
34// =============================================================================
35// Constants
36// =============================================================================
37
38/// Maximum transform size.
39pub const MAX_TX_SIZE: usize = 64;
40
41/// Minimum transform size.
42pub const MIN_TX_SIZE: usize = 4;
43
44/// Maximum coefficient value (for 12-bit).
45pub const MAX_COEFF_VALUE: i32 = 32767;
46
47/// Minimum coefficient value.
48pub const MIN_COEFF_VALUE: i32 = -32768;
49
50// =============================================================================
51// Transform Block
52// =============================================================================
53
54/// Transform block containing coefficients.
55#[derive(Clone, Debug)]
56pub struct TransformBlock {
57    /// Coefficient data in scan order.
58    coeffs: Vec<i32>,
59    /// Block width.
60    width: usize,
61    /// Block height.
62    height: usize,
63    /// End of block position (index of last non-zero coefficient + 1).
64    eob: usize,
65    /// Transform type.
66    tx_type: TransformType,
67}
68
69impl TransformBlock {
70    /// Create a new transform block.
71    #[must_use]
72    pub fn new(width: usize, height: usize) -> Self {
73        let size = width * height;
74        Self {
75            coeffs: vec![0; size],
76            width,
77            height,
78            eob: 0,
79            tx_type: TransformType::Dct,
80        }
81    }
82
83    /// Get the block width.
84    #[must_use]
85    pub const fn width(&self) -> usize {
86        self.width
87    }
88
89    /// Get the block height.
90    #[must_use]
91    pub const fn height(&self) -> usize {
92        self.height
93    }
94
95    /// Get the end of block position.
96    #[must_use]
97    pub const fn eob(&self) -> usize {
98        self.eob
99    }
100
101    /// Set the end of block position.
102    pub fn set_eob(&mut self, eob: usize) {
103        self.eob = eob;
104    }
105
106    /// Get the transform type.
107    #[must_use]
108    pub const fn tx_type(&self) -> TransformType {
109        self.tx_type
110    }
111
112    /// Set the transform type.
113    pub fn set_tx_type(&mut self, tx_type: TransformType) {
114        self.tx_type = tx_type;
115    }
116
117    /// Get coefficient at position.
118    #[must_use]
119    pub fn get(&self, row: usize, col: usize) -> i32 {
120        if row < self.height && col < self.width {
121            self.coeffs[row * self.width + col]
122        } else {
123            0
124        }
125    }
126
127    /// Set coefficient at position.
128    pub fn set(&mut self, row: usize, col: usize, value: i32) {
129        if row < self.height && col < self.width {
130            self.coeffs[row * self.width + col] = value;
131        }
132    }
133
134    /// Set coefficient with clamping.
135    pub fn set_clamped(&mut self, row: usize, col: usize, value: i32) {
136        let clamped = value.clamp(MIN_COEFF_VALUE, MAX_COEFF_VALUE);
137        self.set(row, col, clamped);
138    }
139
140    /// Get coefficients as slice.
141    #[must_use]
142    pub fn coeffs(&self) -> &[i32] {
143        &self.coeffs
144    }
145
146    /// Get coefficients as mutable slice.
147    pub fn coeffs_mut(&mut self) -> &mut [i32] {
148        &mut self.coeffs
149    }
150
151    /// Clear all coefficients.
152    pub fn clear(&mut self) {
153        self.coeffs.fill(0);
154        self.eob = 0;
155    }
156
157    /// Check if the block is all zeros.
158    #[must_use]
159    pub const fn is_zero(&self) -> bool {
160        self.eob == 0
161    }
162
163    /// Count non-zero coefficients.
164    #[must_use]
165    pub fn count_nonzero(&self) -> usize {
166        self.coeffs.iter().filter(|&&c| c != 0).count()
167    }
168
169    /// Get coefficient at scan position.
170    #[must_use]
171    pub fn get_scan(&self, scan_pos: usize) -> i32 {
172        if scan_pos < self.coeffs.len() {
173            self.coeffs[scan_pos]
174        } else {
175            0
176        }
177    }
178
179    /// Set coefficient at scan position.
180    pub fn set_scan(&mut self, scan_pos: usize, value: i32) {
181        if scan_pos < self.coeffs.len() {
182            self.coeffs[scan_pos] = value;
183        }
184    }
185}
186
187/// Transform type for AV1/VP9.
188#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
189pub enum TransformType {
190    /// Discrete Cosine Transform.
191    #[default]
192    Dct,
193    /// Asymmetric Discrete Sine Transform.
194    Adst,
195    /// Identity transform.
196    Identity,
197    /// Flip DCT (reversed order).
198    FlipDct,
199    /// Flip ADST.
200    FlipAdst,
201}
202
203impl TransformType {
204    /// Get the horizontal transform type for a 2D transform.
205    #[must_use]
206    pub const fn horizontal(self) -> Self {
207        self
208    }
209
210    /// Get the vertical transform type for a 2D transform.
211    #[must_use]
212    pub const fn vertical(self) -> Self {
213        self
214    }
215
216    /// Check if this is an identity transform.
217    #[must_use]
218    pub const fn is_identity(self) -> bool {
219        matches!(self, Self::Identity)
220    }
221}
222
223// =============================================================================
224// Residual Plane
225// =============================================================================
226
227/// Buffer for residuals of a single plane.
228#[derive(Clone, Debug)]
229pub struct ResidualPlane {
230    /// Residual data.
231    data: Vec<i32>,
232    /// Plane width.
233    width: u32,
234    /// Plane height.
235    height: u32,
236    /// Row stride.
237    stride: usize,
238    /// Plane type.
239    plane_type: PlaneType,
240}
241
242impl ResidualPlane {
243    /// Create a new residual plane.
244    #[must_use]
245    pub fn new(width: u32, height: u32, plane_type: PlaneType) -> Self {
246        // Align stride to 16 for SIMD
247        let stride = ((width as usize + 15) / 16) * 16;
248        let size = stride * height as usize;
249
250        Self {
251            data: vec![0; size],
252            width,
253            height,
254            stride,
255            plane_type,
256        }
257    }
258
259    /// Get the plane width.
260    #[must_use]
261    pub const fn width(&self) -> u32 {
262        self.width
263    }
264
265    /// Get the plane height.
266    #[must_use]
267    pub const fn height(&self) -> u32 {
268        self.height
269    }
270
271    /// Get the row stride.
272    #[must_use]
273    pub const fn stride(&self) -> usize {
274        self.stride
275    }
276
277    /// Get the plane type.
278    #[must_use]
279    pub const fn plane_type(&self) -> PlaneType {
280        self.plane_type
281    }
282
283    /// Get a residual value at (x, y).
284    #[must_use]
285    pub fn get(&self, x: u32, y: u32) -> i32 {
286        if x >= self.width || y >= self.height {
287            return 0;
288        }
289        let idx = y as usize * self.stride + x as usize;
290        self.data.get(idx).copied().unwrap_or(0)
291    }
292
293    /// Set a residual value at (x, y).
294    pub fn set(&mut self, x: u32, y: u32, value: i32) {
295        if x < self.width && y < self.height {
296            let idx = y as usize * self.stride + x as usize;
297            if idx < self.data.len() {
298                self.data[idx] = value;
299            }
300        }
301    }
302
303    /// Get a row of residuals.
304    #[must_use]
305    pub fn row(&self, y: u32) -> &[i32] {
306        if y >= self.height {
307            return &[];
308        }
309        let start = y as usize * self.stride;
310        let end = start + self.width as usize;
311        if end <= self.data.len() {
312            &self.data[start..end]
313        } else {
314            &[]
315        }
316    }
317
318    /// Get a mutable row of residuals.
319    pub fn row_mut(&mut self, y: u32) -> &mut [i32] {
320        if y >= self.height {
321            return &mut [];
322        }
323        let start = y as usize * self.stride;
324        let end = start + self.width as usize;
325        if end <= self.data.len() {
326            &mut self.data[start..end]
327        } else {
328            &mut []
329        }
330    }
331
332    /// Get raw data slice.
333    #[must_use]
334    pub fn data(&self) -> &[i32] {
335        &self.data
336    }
337
338    /// Get raw data as mutable slice.
339    pub fn data_mut(&mut self) -> &mut [i32] {
340        &mut self.data
341    }
342
343    /// Clear all residuals to zero.
344    pub fn clear(&mut self) {
345        self.data.fill(0);
346    }
347
348    /// Clear a block region.
349    pub fn clear_block(&mut self, x: u32, y: u32, width: u32, height: u32) {
350        for dy in 0..height {
351            for dx in 0..width {
352                self.set(x + dx, y + dy, 0);
353            }
354        }
355    }
356
357    /// Write a transform block to the residual plane.
358    pub fn write_block(&mut self, x: u32, y: u32, block: &TransformBlock) {
359        for row in 0..block.height() {
360            for col in 0..block.width() {
361                let value = block.get(row, col);
362                self.set(x + col as u32, y + row as u32, value);
363            }
364        }
365    }
366
367    /// Read a transform block from the residual plane.
368    #[must_use]
369    pub fn read_block(&self, x: u32, y: u32, width: usize, height: usize) -> TransformBlock {
370        let mut block = TransformBlock::new(width, height);
371        for row in 0..height {
372            for col in 0..width {
373                let value = self.get(x + col as u32, y + row as u32);
374                block.set(row, col, value);
375            }
376        }
377        block
378    }
379}
380
381// =============================================================================
382// Residual Buffer
383// =============================================================================
384
385/// Buffer for all residual planes in a frame.
386#[derive(Clone, Debug)]
387pub struct ResidualBuffer {
388    /// Y plane residuals.
389    y_plane: ResidualPlane,
390    /// U plane residuals.
391    u_plane: Option<ResidualPlane>,
392    /// V plane residuals.
393    v_plane: Option<ResidualPlane>,
394    /// Frame width.
395    width: u32,
396    /// Frame height.
397    height: u32,
398    /// Chroma subsampling.
399    subsampling: ChromaSubsampling,
400}
401
402impl ResidualBuffer {
403    /// Create a new residual buffer.
404    #[must_use]
405    pub fn new(width: u32, height: u32, subsampling: ChromaSubsampling) -> Self {
406        let y_plane = ResidualPlane::new(width, height, PlaneType::Y);
407
408        let (u_plane, v_plane) = match subsampling {
409            ChromaSubsampling::Mono => (None, None),
410            _ => {
411                let (cw, ch) = subsampling.chroma_size(width, height);
412                (
413                    Some(ResidualPlane::new(cw, ch, PlaneType::U)),
414                    Some(ResidualPlane::new(cw, ch, PlaneType::V)),
415                )
416            }
417        };
418
419        Self {
420            y_plane,
421            u_plane,
422            v_plane,
423            width,
424            height,
425            subsampling,
426        }
427    }
428
429    /// Get the frame width.
430    #[must_use]
431    pub const fn width(&self) -> u32 {
432        self.width
433    }
434
435    /// Get the frame height.
436    #[must_use]
437    pub const fn height(&self) -> u32 {
438        self.height
439    }
440
441    /// Get the Y plane.
442    #[must_use]
443    pub fn y_plane(&self) -> &ResidualPlane {
444        &self.y_plane
445    }
446
447    /// Get the Y plane mutably.
448    pub fn y_plane_mut(&mut self) -> &mut ResidualPlane {
449        &mut self.y_plane
450    }
451
452    /// Get the U plane.
453    #[must_use]
454    pub fn u_plane(&self) -> Option<&ResidualPlane> {
455        self.u_plane.as_ref()
456    }
457
458    /// Get the U plane mutably.
459    pub fn u_plane_mut(&mut self) -> Option<&mut ResidualPlane> {
460        self.u_plane.as_mut()
461    }
462
463    /// Get the V plane.
464    #[must_use]
465    pub fn v_plane(&self) -> Option<&ResidualPlane> {
466        self.v_plane.as_ref()
467    }
468
469    /// Get the V plane mutably.
470    pub fn v_plane_mut(&mut self) -> Option<&mut ResidualPlane> {
471        self.v_plane.as_mut()
472    }
473
474    /// Get a plane by type.
475    #[must_use]
476    pub fn plane(&self, plane_type: PlaneType) -> Option<&ResidualPlane> {
477        match plane_type {
478            PlaneType::Y => Some(&self.y_plane),
479            PlaneType::U => self.u_plane.as_ref(),
480            PlaneType::V => self.v_plane.as_ref(),
481        }
482    }
483
484    /// Get a plane mutably by type.
485    pub fn plane_mut(&mut self, plane_type: PlaneType) -> Option<&mut ResidualPlane> {
486        match plane_type {
487            PlaneType::Y => Some(&mut self.y_plane),
488            PlaneType::U => self.u_plane.as_mut(),
489            PlaneType::V => self.v_plane.as_mut(),
490        }
491    }
492
493    /// Clear all residual planes.
494    pub fn clear(&mut self) {
495        self.y_plane.clear();
496        if let Some(ref mut u) = self.u_plane {
497            u.clear();
498        }
499        if let Some(ref mut v) = self.v_plane {
500            v.clear();
501        }
502    }
503
504    /// Add residuals to a frame buffer.
505    ///
506    /// # Errors
507    ///
508    /// Returns error if dimensions don't match.
509    pub fn add_to_frame(&self, frame: &mut FrameBuffer) -> ReconstructResult<()> {
510        if frame.width() != self.width || frame.height() != self.height {
511            return Err(ReconstructionError::InvalidDimensions {
512                width: self.width,
513                height: self.height,
514            });
515        }
516
517        // Add Y plane residuals
518        add_residual_plane(&self.y_plane, frame.y_plane_mut());
519
520        // Add U plane residuals
521        if let (Some(ref resid), Some(ref mut plane)) = (&self.u_plane, frame.u_plane_mut()) {
522            add_residual_plane(resid, plane);
523        }
524
525        // Add V plane residuals
526        if let (Some(ref resid), Some(ref mut plane)) = (&self.v_plane, frame.v_plane_mut()) {
527            add_residual_plane(resid, plane);
528        }
529
530        Ok(())
531    }
532
533    /// Add residuals to a frame buffer for a specific block.
534    pub fn add_block_to_frame(
535        &self,
536        frame: &mut FrameBuffer,
537        plane: PlaneType,
538        x: u32,
539        y: u32,
540        width: u32,
541        height: u32,
542    ) -> ReconstructResult<()> {
543        let resid_plane = self
544            .plane(plane)
545            .ok_or_else(|| ReconstructionError::InvalidInput("Plane not available".to_string()))?;
546        let frame_plane = frame
547            .plane_mut(plane)
548            .ok_or_else(|| ReconstructionError::InvalidInput("Plane not available".to_string()))?;
549
550        let max_val = frame_plane.max_value();
551
552        for dy in 0..height {
553            for dx in 0..width {
554                let px = x + dx;
555                let py = y + dy;
556
557                let pixel = i32::from(frame_plane.get(px, py));
558                let residual = resid_plane.get(px, py);
559                let result = (pixel + residual).clamp(0, i32::from(max_val));
560
561                frame_plane.set(px, py, result as i16);
562            }
563        }
564
565        Ok(())
566    }
567}
568
569/// Add residuals from a residual plane to a pixel plane.
570fn add_residual_plane(residual: &ResidualPlane, pixels: &mut PlaneBuffer) {
571    let max_val = pixels.max_value();
572    let width = residual.width().min(pixels.width());
573    let height = residual.height().min(pixels.height());
574
575    for y in 0..height {
576        let resid_row = residual.row(y);
577        let pixel_row = pixels.row_mut(y);
578
579        for x in 0..width as usize {
580            let pixel = i32::from(pixel_row[x]);
581            let resid = resid_row[x];
582            let result = (pixel + resid).clamp(0, i32::from(max_val));
583            pixel_row[x] = result as i16;
584        }
585    }
586}
587
588// =============================================================================
589// Residual Operations
590// =============================================================================
591
592/// Add a residual block to prediction.
593///
594/// # Arguments
595///
596/// * `prediction` - The prediction buffer (modified in place).
597/// * `residual` - The residual values.
598/// * `width` - Block width.
599/// * `height` - Block height.
600/// * `bit_depth` - Bit depth for clamping.
601pub fn add_residual(
602    prediction: &mut [i16],
603    residual: &[i32],
604    width: usize,
605    height: usize,
606    bit_depth: u8,
607) {
608    let max_val = (1i32 << bit_depth) - 1;
609    let size = width * height;
610
611    for i in 0..size.min(prediction.len()).min(residual.len()) {
612        let pred = i32::from(prediction[i]);
613        let resid = residual[i];
614        let result = (pred + resid).clamp(0, max_val);
615        prediction[i] = result as i16;
616    }
617}
618
619/// Add residual with stride.
620pub fn add_residual_stride(
621    prediction: &mut [i16],
622    pred_stride: usize,
623    residual: &[i32],
624    resid_stride: usize,
625    width: usize,
626    height: usize,
627    bit_depth: u8,
628) {
629    let max_val = (1i32 << bit_depth) - 1;
630
631    for y in 0..height {
632        let pred_row = &mut prediction[y * pred_stride..];
633        let resid_row = &residual[y * resid_stride..];
634
635        for x in 0..width {
636            let pred = i32::from(pred_row[x]);
637            let resid = resid_row[x];
638            let result = (pred + resid).clamp(0, max_val);
639            pred_row[x] = result as i16;
640        }
641    }
642}
643
644/// Clip residual values to valid range.
645pub fn clip_residuals(residuals: &mut [i32], bit_depth: u8) {
646    // Residuals can be in range [-(1<<bd), (1<<bd)-1]
647    let max = (1i32 << bit_depth) - 1;
648    let min = -(1i32 << bit_depth);
649
650    for r in residuals.iter_mut() {
651        *r = (*r).clamp(min, max);
652    }
653}
654
655// =============================================================================
656// Tests
657// =============================================================================
658
659#[cfg(test)]
660mod tests {
661    use super::*;
662
663    #[test]
664    fn test_transform_block_new() {
665        let block = TransformBlock::new(8, 8);
666        assert_eq!(block.width(), 8);
667        assert_eq!(block.height(), 8);
668        assert_eq!(block.eob(), 0);
669        assert!(block.is_zero());
670    }
671
672    #[test]
673    fn test_transform_block_get_set() {
674        let mut block = TransformBlock::new(8, 8);
675        block.set(2, 3, 100);
676        block.set_eob(25);
677
678        assert_eq!(block.get(2, 3), 100);
679        assert_eq!(block.eob(), 25);
680        assert!(!block.is_zero());
681    }
682
683    #[test]
684    fn test_transform_block_count_nonzero() {
685        let mut block = TransformBlock::new(4, 4);
686        block.set(0, 0, 10);
687        block.set(1, 1, 20);
688        block.set(2, 2, 30);
689
690        assert_eq!(block.count_nonzero(), 3);
691    }
692
693    #[test]
694    fn test_transform_type() {
695        assert!(!TransformType::Dct.is_identity());
696        assert!(TransformType::Identity.is_identity());
697    }
698
699    #[test]
700    fn test_residual_plane_new() {
701        let plane = ResidualPlane::new(64, 48, PlaneType::Y);
702        assert_eq!(plane.width(), 64);
703        assert_eq!(plane.height(), 48);
704        assert_eq!(plane.plane_type(), PlaneType::Y);
705    }
706
707    #[test]
708    fn test_residual_plane_get_set() {
709        let mut plane = ResidualPlane::new(64, 48, PlaneType::Y);
710        plane.set(10, 20, 500);
711        assert_eq!(plane.get(10, 20), 500);
712        assert_eq!(plane.get(0, 0), 0);
713    }
714
715    #[test]
716    fn test_residual_plane_write_read_block() {
717        let mut plane = ResidualPlane::new(64, 48, PlaneType::Y);
718
719        let mut block = TransformBlock::new(4, 4);
720        block.set(0, 0, 100);
721        block.set(1, 1, 200);
722        block.set(3, 3, 300);
723
724        plane.write_block(8, 8, &block);
725
726        let read_block = plane.read_block(8, 8, 4, 4);
727        assert_eq!(read_block.get(0, 0), 100);
728        assert_eq!(read_block.get(1, 1), 200);
729        assert_eq!(read_block.get(3, 3), 300);
730    }
731
732    #[test]
733    fn test_residual_buffer_new() {
734        let buffer = ResidualBuffer::new(1920, 1080, ChromaSubsampling::Cs420);
735        assert_eq!(buffer.width(), 1920);
736        assert_eq!(buffer.height(), 1080);
737
738        assert!(buffer.u_plane().is_some());
739        assert!(buffer.v_plane().is_some());
740    }
741
742    #[test]
743    fn test_residual_buffer_mono() {
744        let buffer = ResidualBuffer::new(1920, 1080, ChromaSubsampling::Mono);
745        assert!(buffer.u_plane().is_none());
746        assert!(buffer.v_plane().is_none());
747    }
748
749    #[test]
750    fn test_residual_buffer_clear() {
751        let mut buffer = ResidualBuffer::new(64, 48, ChromaSubsampling::Cs420);
752        buffer.y_plane_mut().set(10, 10, 1000);
753
754        buffer.clear();
755        assert_eq!(buffer.y_plane().get(10, 10), 0);
756    }
757
758    #[test]
759    fn test_add_residual_to_frame() {
760        let mut frame = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
761        let mut residual = ResidualBuffer::new(64, 48, ChromaSubsampling::Cs420);
762
763        // Set prediction value
764        frame.y_plane_mut().set(10, 10, 100);
765
766        // Set residual
767        residual.y_plane_mut().set(10, 10, 50);
768
769        // Add residual
770        residual.add_to_frame(&mut frame).expect("should succeed");
771
772        // Check result
773        assert_eq!(frame.y_plane().get(10, 10), 150);
774    }
775
776    #[test]
777    fn test_add_residual_clamping() {
778        let mut frame = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
779        let mut residual = ResidualBuffer::new(64, 48, ChromaSubsampling::Cs420);
780
781        // Set prediction near max
782        frame.y_plane_mut().set(10, 10, 250);
783
784        // Set large positive residual
785        residual.y_plane_mut().set(10, 10, 100);
786
787        // Add residual (should clamp to 255)
788        residual.add_to_frame(&mut frame).expect("should succeed");
789
790        assert_eq!(frame.y_plane().get(10, 10), 255);
791    }
792
793    #[test]
794    fn test_add_residual_function() {
795        let mut prediction = vec![100i16, 150, 200, 50];
796        let residual = vec![20i32, -30, 100, -100];
797
798        add_residual(&mut prediction, &residual, 2, 2, 8);
799
800        assert_eq!(prediction[0], 120); // 100 + 20
801        assert_eq!(prediction[1], 120); // 150 - 30
802        assert_eq!(prediction[2], 255); // 200 + 100, clamped
803        assert_eq!(prediction[3], 0); // 50 - 100, clamped
804    }
805
806    #[test]
807    fn test_clip_residuals() {
808        let mut residuals = vec![100, -100, 500, -500];
809        clip_residuals(&mut residuals, 8);
810
811        assert_eq!(residuals[0], 100);
812        assert_eq!(residuals[1], -100);
813        assert_eq!(residuals[2], 255); // Clamped to max
814        assert_eq!(residuals[3], -256); // Clamped to min
815    }
816
817    #[test]
818    fn test_constants() {
819        assert_eq!(MAX_TX_SIZE, 64);
820        assert_eq!(MIN_TX_SIZE, 4);
821        assert_eq!(MAX_COEFF_VALUE, 32767);
822        assert_eq!(MIN_COEFF_VALUE, -32768);
823    }
824}