Skip to main content

tract_linalg/frame/
pack.rs

1use std::alloc::Layout;
2use std::fmt::{Debug, Display};
3use std::marker::PhantomData;
4use std::ops::Range;
5use std::sync::Arc;
6use tract_data::internal::*;
7
8use crate::mmm::{EagerPackedInput, MMMInputFormat, MMMInputValue, PackedOpaqueFact};
9
10use crate::WeightType;
11
12#[derive(Clone, Eq, PartialEq, Hash)]
13pub struct PackedFormat {
14    pub dt: DatumType,
15    pub r: usize,
16    pub alignment_bytes: usize,
17    pub end_padding_record: usize,
18}
19
20impl MMMInputFormat for PackedFormat {
21    fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult<Tensor> {
22        let packed = PackedFormat::pack_tensor(self, t, k_axis, mn_axis)?;
23        Ok(tensor0(Opaque(Arc::new(packed))))
24    }
25
26    fn prepare_one(
27        &self,
28        t: &Tensor,
29        k_axis: usize,
30        mn_axis: usize,
31    ) -> TractResult<Box<dyn MMMInputValue>> {
32        PackedFormat::pack_tensor(self, t, k_axis, mn_axis)
33    }
34
35    fn precursor(&self) -> WeightType {
36        WeightType::Plain(self.dt)
37    }
38
39    fn r(&self) -> usize {
40        self.r
41    }
42
43    fn k_alignment(&self) -> usize {
44        1
45    }
46
47    fn same_as(&self, other: &dyn MMMInputFormat) -> bool {
48        other.downcast_ref::<Self>().is_some_and(|other| self == other)
49    }
50
51    fn mem_size(&self, k: TDim, mn: TDim) -> TDim {
52        self.len(k, mn) * self.dt.size_of()
53    }
54
55    fn extract_at_mn_f16(
56        &self,
57        data: &EagerPackedInput,
58        mn: usize,
59        slice: &mut [f16],
60    ) -> TractResult<()> {
61        ensure!(data.format().same_as(self));
62        ensure!(self.len(data.k(), data.mn()) * self.dt.size_of() == data.packed.len());
63        unsafe {
64            let ptr = data.packed.as_ptr().add(
65                (self.single_panel_len(data.k()) * (mn / self.r) + mn % self.r) * self.dt.size_of(),
66            );
67            for (i, slot) in slice.iter_mut().enumerate() {
68                let ptr = ptr.add(i * self.dt.size_of() * self.r);
69                *slot = if self.dt == f16::datum_type() {
70                    *(ptr as *const f16)
71                } else if self.dt == f32::datum_type() {
72                    f16::from_f32(*(ptr as *const f32))
73                } else {
74                    bail!("Unexpected DT {:?}", self.dt)
75                }
76            }
77        }
78        Ok(())
79    }
80
81    fn extract_at_mn_f32(
82        &self,
83        data: &EagerPackedInput,
84        mn: usize,
85        slice: &mut [f32],
86    ) -> TractResult<()> {
87        ensure!(data.format().same_as(self));
88        ensure!(self.len(data.k(), data.mn()) * self.dt.size_of() == data.packed.len());
89        unsafe {
90            let ptr = data.packed.as_ptr().add(
91                (self.single_panel_len(data.k()) * (mn / self.r) + mn % self.r) * self.dt.size_of(),
92            );
93            for (i, slot) in slice.iter_mut().enumerate() {
94                let ptr = ptr.add(i * self.dt.size_of() * self.r);
95                *slot = if self.dt == f16::datum_type() {
96                    (*(ptr as *const f16)).to_f32()
97                } else if self.dt == f32::datum_type() {
98                    *(ptr as *const f32)
99                } else {
100                    bail!("Unexpected DT {:?}", self.dt)
101                }
102            }
103        }
104        Ok(())
105    }
106}
107
108impl Display for PackedFormat {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        write!(f, "Packed{:?}[{}]", self.dt, self.r)
111    }
112}
113
114impl Debug for PackedFormat {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        write!(
117            f,
118            "Packed{:?}[{}]@{}+{}",
119            self.dt, self.r, self.alignment_bytes, self.end_padding_record
120        )
121    }
122}
123
124impl PackedFormat {
125    pub const fn new(dt: DatumType, nr: usize, alignment_bytes: usize) -> PackedFormat {
126        PackedFormat { dt, r: nr, alignment_bytes, end_padding_record: 1 }
127    }
128
129    pub const fn with_end_padding_record(self, end_padding_record: usize) -> Self {
130        PackedFormat { end_padding_record, ..self }
131    }
132
133    #[inline]
134    pub fn align(self, alignment: usize) -> Self {
135        Self { alignment_bytes: alignment, ..self }
136    }
137
138    #[inline]
139    pub fn alignment(&self) -> usize {
140        self.alignment_bytes
141    }
142
143    #[inline]
144    pub fn panel_width(&self) -> usize {
145        self.r
146    }
147
148    #[inline]
149    pub fn len<D: DimLike>(&self, k: D, n: D) -> D {
150        n.divceil(self.r) * self.single_panel_len(k)
151    }
152
153    #[inline]
154    pub fn single_panel_len<D: DimLike>(&self, k: D) -> D {
155        ((k + self.end_padding_record) * self.r).divceil(self.alignment()) * self.alignment()
156    }
157
158    #[inline]
159    pub fn single_panel_layout(&self, k: usize, item_size: usize) -> Layout {
160        Layout::from_size_align(self.single_panel_len(k) * item_size, self.alignment()).unwrap()
161    }
162
163    pub fn pack_tensor(
164        &self,
165        t: &Tensor,
166        k_axis: usize,
167        mn_axis: usize,
168    ) -> TractResult<Box<dyn MMMInputValue>> {
169        ensure!(t.datum_type().is_copy());
170        ensure!(
171            t.datum_type().unquantized() == self.dt.unquantized(),
172            "Attempting to pack for {self} tensor {t:?}"
173        );
174        let k = t.shape()[k_axis];
175        let mn = t.shape()[mn_axis];
176        let packed_len = self.len(k, mn);
177        let panel_len = self.single_panel_len(k);
178        let panel_bytes = panel_len * t.datum_type().size_of();
179        let strides = t.strides();
180        unsafe {
181            let mut packed = Blob::new_for_size_and_align(
182                t.datum_type().size_of() * packed_len,
183                self.alignment_bytes,
184            );
185            if cfg!(debug_assertions) {
186                packed.as_bytes_mut().fill(0u8);
187            }
188            dispatch_copy!(Self::pack_t(t.datum_type())(
189                self,
190                packed.as_mut_ptr() as _,
191                t.as_ptr_unchecked(),
192                mn,
193                strides[k_axis],
194                strides[mn_axis],
195                0..k,
196                0..mn
197            ));
198            Ok(Box::new(EagerPackedInput {
199                fact: PackedOpaqueFact { format: Box::new(self.clone()), mn: mn.to_dim(), k },
200                packed: packed.into(),
201                panel_bytes,
202                mn,
203            }))
204        }
205    }
206
207    pub fn pack_tensor_view(
208        &self,
209        t: &TensorView,
210        k_axis: usize,
211        mn_axis: usize,
212    ) -> TractResult<Box<dyn MMMInputValue>> {
213        ensure!(
214            t.datum_type().unquantized() == self.dt.unquantized(),
215            "Attempting to pack for {self} tensor view {t:?}"
216        );
217        let k = t.shape()[k_axis];
218        let mn = t.shape()[mn_axis];
219        let packed_len = self.len(k, mn);
220        let panel_len = self.single_panel_len(k);
221        let panel_bytes = panel_len * t.datum_type().size_of();
222        let strides = t.strides();
223        unsafe {
224            let mut packed = Blob::new_for_size_and_align(
225                t.datum_type().size_of() * packed_len,
226                self.alignment_bytes,
227            );
228            if cfg!(debug_assertions) {
229                packed.as_bytes_mut().fill(0u8);
230            }
231            dispatch_copy!(Self::pack_t(t.datum_type())(
232                self,
233                packed.as_mut_ptr() as _,
234                t.as_ptr_unchecked(),
235                mn,
236                strides[k_axis],
237                strides[mn_axis],
238                0..k,
239                0..mn
240            ));
241            Ok(Box::new(EagerPackedInput {
242                fact: PackedOpaqueFact { format: Box::new(self.clone()), mn: mn.to_dim(), k },
243                packed: packed.into(),
244                panel_bytes,
245                mn,
246            }))
247        }
248    }
249
250    pub unsafe fn pack<'a, 'b>(
251        &self,
252        pb: impl std::borrow::BorrowMut<TensorView<'a>>,
253        b: impl std::borrow::Borrow<TensorView<'b>>,
254        k_axis: usize,
255        mn_axis: usize,
256    ) {
257        let k = b.borrow().shape()[k_axis];
258        let mn = b.borrow().shape()[mn_axis];
259        self.pack_segment(pb, b, k_axis, mn_axis, 0..k, 0..mn);
260    }
261
262
263    #[allow(clippy::too_many_arguments)]
264    #[rustfmt::skip]
265    pub unsafe fn pack_t<T: Datum + Copy>(
266        &self,
267        pb: *mut T,
268        b: *const T,
269        mn: usize,
270        k_stride: isize,
271        mn_stride: isize,
272        k_range: Range<usize>,
273        mn_range: Range<usize>,
274        ) {
275        if k_range.len() == 0 || mn_range.len() == 0 {
276            return
277        }
278        if self.r == 1 && k_stride == 1 && mn == 1 {
279            pb.copy_from_nonoverlapping(b.add(k_range.start), k_range.len())
280        } else if mn_stride == 1 {
281            let size_of = T::datum_type().size_of();
282            let rbytes = self.r * size_of;
283            let mn_valid_end = mn_range.end.min(mn);
284            let mn_range_bytes = mn_range.start * size_of..mn_valid_end * size_of;
285            let k_stride_bytes = k_stride * size_of as isize;
286            let bb = b as *const u8;
287            let pbb = pb as *mut u8;
288            let panel_len = self.single_panel_len(k_range.len()) * size_of;
289            match rbytes {
290                16 => pack_mn_major::<[u8; 16]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range),
291                24 => pack_mn_major::<[u8; 24]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range),
292                32 => pack_mn_major::<[u8; 32]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range),
293                48 => pack_mn_major::<[u8; 48]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range),
294                64 => pack_mn_major::<[u8; 64]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range),
295                _ => {
296                    let mut packer = self.write_with_k_outer(pb, k_range.len(), mn_range.len());
297                    for k in k_range {
298                        for x in mn_range.start..mn_valid_end {
299                            packer.write(*b.offset(x as isize + k_stride * k as isize))
300                        }
301                        for _x in mn_valid_end..mn_range.end {
302                            packer.write(T::default())
303                        }
304                    }
305                }
306            }
307        } else if k_stride == 1 {
308            let mut packer = self.write_with_k_inner(pb, k_range.len(), mn);
309            let mn_valid_end = mn_range.end.min(mn);
310            for x in mn_range.start..mn_valid_end {
311                for k in k_range.clone() {
312                    packer.write(*b.offset(x as isize * mn_stride + k as isize))
313                }
314            }
315            // just ignore invalid mn_range
316        } else {
317            let mut packer = self.write_with_k_outer(pb, k_range.len(), mn);
318            let mn_valid_end = mn_range.end.min(mn);
319            for k in k_range {
320                for x in mn_range.start..mn_valid_end {
321                    packer.write(*b.offset(x as isize * mn_stride + k_stride * k as isize))
322                }
323                for _x in mn_valid_end..mn_range.end {
324                    packer.write(T::default())
325                }
326            }
327        }
328    }
329
330    #[inline]
331    pub unsafe fn pack_segment<'a, 'b>(
332        &self,
333        mut pb: impl std::borrow::BorrowMut<TensorView<'a>>,
334        b: impl std::borrow::Borrow<TensorView<'b>>,
335        k_axis: usize,
336        mn_axis: usize,
337        k_range: Range<usize>,
338        mn_range: Range<usize>,
339    ) {
340        debug_assert!(pb.borrow().len() >= self.len(k_range.len(), mn_range.len()));
341        let pb = pb.borrow_mut();
342        let b = b.borrow();
343        let dt = pb.datum_type();
344        dispatch_copy!(Self::pack_t(dt)(
345            self,
346            pb.as_ptr_mut_unchecked(),
347            b.as_ptr_unchecked(),
348            b.shape()[mn_axis],
349            b.strides()[k_axis],
350            b.strides()[mn_axis],
351            k_range,
352            mn_range
353        ));
354    }
355
356    pub fn write_with_k_outer<'p, T: Copy + Debug>(
357        &self,
358        pb: *mut T,
359        k: usize,
360        mn: usize,
361    ) -> KOutWriter<'p, T> {
362        KOutWriter::new(pb, self.r, self.single_panel_len(k), mn, k)
363    }
364
365    pub fn write_single_panel_with_k_outer<'p, T: Copy + Debug>(
366        &self,
367        pb: *mut T,
368    ) -> KOutSinglePanelWriter<'p, T> {
369        KOutSinglePanelWriter::new(pb)
370    }
371
372    pub fn write_with_k_inner<'p, T: Copy + Debug>(
373        &self,
374        pb: *mut T,
375        k: usize,
376        mn: usize,
377    ) -> KInWriter<'p, T> {
378        let panel_len = self.single_panel_len(k);
379        KInWriter::new(pb, panel_len, self.r, mn, k)
380    }
381}
382
383pub trait PackingWriter<T: Copy> {
384    fn write(&mut self, t: T);
385}
386
387#[derive(Debug)]
388pub struct KOutSinglePanelWriter<'p, T>
389where
390    T: Copy + std::fmt::Debug,
391{
392    ptr: *mut T,
393    _phantom: PhantomData<&'p T>,
394}
395
396impl<'p, T> KOutSinglePanelWriter<'p, T>
397where
398    T: Copy + std::fmt::Debug,
399{
400    pub fn new(ptr: *mut T) -> KOutSinglePanelWriter<'p, T> {
401        KOutSinglePanelWriter { ptr, _phantom: PhantomData }
402    }
403}
404
405impl<T> PackingWriter<T> for KOutSinglePanelWriter<'_, T>
406where
407    T: Copy + std::fmt::Debug,
408{
409    #[inline(always)]
410    fn write(&mut self, t: T) {
411        unsafe {
412            *self.ptr = t;
413            self.ptr = self.ptr.offset(1);
414        }
415    }
416}
417
418#[derive(Debug)]
419pub struct KOutWriter<'p, T>
420where
421    T: Copy + std::fmt::Debug,
422{
423    ptr: *mut T,
424    panels: usize,
425    panel_width: usize,
426    last_panel_width: usize,
427    remain: usize,
428    current_panel: usize,
429    next_panel: isize,
430    next_lane: isize,
431    _phantom: PhantomData<&'p T>,
432}
433
434impl<'p, T> KOutWriter<'p, T>
435where
436    T: Copy + std::fmt::Debug,
437{
438    pub fn new(
439        ptr: *mut T,
440        panel_width: usize,
441        panel_len: usize,
442        mn: usize,
443        _k: usize,
444    ) -> KOutWriter<'p, T> {
445        let panels = mn.divceil(panel_width);
446        let last_panel_width = mn - (panels - 1) * panel_width;
447        KOutWriter {
448            ptr,
449            panels,
450            panel_width,
451            last_panel_width,
452            remain: if panels > 1 { panel_width } else { last_panel_width },
453            current_panel: 0,
454            next_panel: (panel_len - panel_width) as isize,
455            next_lane: (panel_width - last_panel_width) as isize
456                - (panel_len * (panels - 1)) as isize,
457            _phantom: PhantomData,
458        }
459    }
460}
461
462impl<T> PackingWriter<T> for KOutWriter<'_, T>
463where
464    T: Copy + std::fmt::Debug,
465{
466    #[inline(always)]
467    fn write(&mut self, t: T) {
468        unsafe {
469            *self.ptr = t;
470            self.remain -= 1;
471            self.ptr = self.ptr.offset(1);
472            if self.remain == 0 {
473                self.current_panel += 1;
474                if self.current_panel == self.panels {
475                    self.ptr = self.ptr.offset(self.next_lane);
476                    self.current_panel = 0;
477                } else {
478                    self.ptr = self.ptr.offset(self.next_panel);
479                }
480                if self.current_panel == self.panels - 1 {
481                    self.remain = self.last_panel_width;
482                } else {
483                    self.remain = self.panel_width;
484                }
485            }
486        }
487    }
488}
489
490#[derive(Debug)]
491pub struct KInWriter<'p, T>
492where
493    T: Copy + Debug,
494{
495    ptr: *mut T,
496    k: usize,
497    panels: usize,
498    panel_width: usize,
499    last_panel_width: usize,
500    remain_on_k: usize,
501    remain_on_mn: usize,
502    current_panel: usize,
503    next_mn_offset: isize,
504    next_panel_offset: isize,
505    _phantom: PhantomData<&'p T>,
506}
507
508impl<'p, T> KInWriter<'p, T>
509where
510    T: Copy + Debug,
511{
512    pub fn new(
513        ptr: *mut T,
514        panel_len: usize,
515        panel_width: usize,
516        mn: usize,
517        k: usize,
518    ) -> KInWriter<'p, T> {
519        let panels = mn.divceil(panel_width);
520        let last_panel_width = mn - (panels - 1) * panel_width;
521        KInWriter {
522            ptr,
523            k,
524            panels,
525            panel_width,
526            last_panel_width,
527            remain_on_k: k,
528            remain_on_mn: if panels == 1 { last_panel_width } else { panel_width },
529            current_panel: 0,
530            next_mn_offset: 1 - (k * panel_width) as isize,
531            next_panel_offset: panel_len as isize - (k * panel_width + panel_width - 1) as isize,
532            //                 ^ next panel     ^    ^ rewind left ^   ^ rewind up   ^
533            _phantom: PhantomData,
534        }
535    }
536}
537
538impl<T> PackingWriter<T> for KInWriter<'_, T>
539where
540    T: Copy + std::fmt::Debug,
541{
542    #[inline(always)]
543    fn write(&mut self, t: T) {
544        unsafe {
545            *self.ptr = t;
546            self.remain_on_k -= 1;
547            self.ptr = self.ptr.add(self.panel_width);
548            if self.remain_on_k == 0 {
549                self.remain_on_k = self.k;
550                self.remain_on_mn -= 1;
551                if self.remain_on_mn > 0 {
552                    self.ptr = self.ptr.offset(self.next_mn_offset);
553                } else {
554                    self.ptr = self.ptr.offset(self.next_panel_offset);
555                    self.current_panel += 1;
556                    if self.current_panel == self.panels - 1 {
557                        self.remain_on_mn = self.last_panel_width;
558                    } else {
559                        self.remain_on_mn = self.panel_width;
560                    }
561                }
562            }
563        }
564    }
565}
566
567#[inline(never)]
568unsafe fn pack_mn_major<Chunk: Copy>(
569    b: *const u8,
570    packed: *mut u8,
571    panel_len: usize,
572    k_stride_bytes: isize,
573    mn_range_bytes: Range<usize>,
574    k_range: Range<usize>,
575) {
576    let mnr = std::mem::size_of::<Chunk>();
577    let full_panes = mn_range_bytes.len() / mnr;
578    let partial_pane = mn_range_bytes.len() % mnr;
579    for k in 0..k_range.len() {
580        let mut p_row = packed.add(k * mnr);
581        let mut b_row =
582            b.offset((k_range.start + k) as isize * k_stride_bytes + mn_range_bytes.start as isize);
583        for _ in 0..full_panes {
584            p_row.copy_from_nonoverlapping(b_row, mnr);
585            p_row = p_row.add(panel_len);
586            b_row = b_row.add(mnr);
587        }
588        if partial_pane > 0 {
589            p_row.copy_from_nonoverlapping(b_row, partial_pane);
590        }
591    }
592}
593
594pub trait Packing {
595    fn packing(r: usize) -> PackedFormat;
596}
597
598impl<D: Datum> Packing for D {
599    fn packing(r: usize) -> PackedFormat {
600        PackedFormat::new(Self::datum_type(), r, vector_size())
601    }
602}
603
604#[cfg(test)]
605mod test {
606    use std::ops::Range;
607
608    use proptest::prelude::*;
609    use tract_data::internal::num_integer::Integer;
610    use tract_data::internal::tract_ndarray::Zip;
611    use tract_data::internal::*;
612    use tract_ndarray::prelude::*;
613
614    #[derive(Debug)]
615    struct PackProblem {
616        k: usize,
617        mn: usize,
618        is_a: bool,
619        r: usize,
620        k_range: Range<usize>,
621        mn_range: Range<usize>,
622        align_panel: usize,
623    }
624
625    impl PackProblem {
626        fn input(&self) -> Array2<u32> {
627            let shape = if self.is_a { (self.mn, self.k) } else { (self.k, self.mn) };
628            let data = (0..(self.k * self.mn) as u32).collect();
629            Array2::from_shape_vec(shape, data).unwrap()
630        }
631
632        fn packer(&self) -> Array2<u32> {
633            let panels = self.mn_range.len().divceil(self.r);
634            let packer = super::PackedFormat::new(u32::datum_type(), self.r, self.align_panel)
635                .with_end_padding_record(0);
636            let input = self.input().into_tensor();
637            let panel_len = packer.single_panel_len(self.k_range.len());
638            let mut output =
639                Tensor::zero::<u32>(&[packer.len(self.k_range.len(), self.mn_range.len())])
640                    .unwrap();
641            unsafe {
642                packer.pack_segment(
643                    output.view_mut(),
644                    input.view(),
645                    self.is_a as usize,
646                    !self.is_a as usize,
647                    self.k_range.clone(),
648                    self.mn_range.clone(),
649                )
650            };
651            output.into_array::<u32>().unwrap().into_shape_with_order((panels, panel_len)).unwrap()
652        }
653
654        fn reference(&self) -> Array2<u32> {
655            let input = self.input();
656            let panels = self.mn_range.len().divceil(self.r);
657            let len = Integer::next_multiple_of(&(self.k_range.len() * self.r), &self.align_panel);
658            Array2::from_shape_fn([panels, len], |(panel, z)| {
659                let k = z / self.r;
660                let x = z % self.r;
661                let mn = panel * self.r + x + self.mn_range.start;
662                let k = k + self.k_range.start;
663                let coords = if self.is_a { (mn, k) } else { (k, mn) };
664                *input.get(coords).unwrap_or(&0)
665            })
666        }
667
668        fn valid(&self) -> Array2<bool> {
669            let panels = self.mn_range.len().divceil(self.r);
670            let len = Integer::next_multiple_of(&(self.k_range.len() * self.r), &self.align_panel);
671            Array2::from_shape_fn([panels, len], |(panel, z)| {
672                let k = z / self.r;
673                let x = z % self.r;
674                let k = k + self.k_range.start;
675                let mn = panel * self.r + x + self.mn_range.start;
676                k < self.k_range.end.min(self.k) && mn < self.mn_range.end.min(self.mn)
677            })
678        }
679
680        fn check(&self) {
681            let mut packer = self.packer();
682            let mut reference = self.reference();
683            let valid = self.valid();
684            Zip::from(&mut packer).and(&valid).for_each(|p, v| *p = if *v { *p } else { -1 as _ });
685            Zip::from(&mut reference)
686                .and(&valid)
687                .for_each(|p, v| *p = if *v { *p } else { -1 as _ });
688            assert_eq!(packer, reference);
689        }
690    }
691
692    impl Arbitrary for PackProblem {
693        type Parameters = ();
694        type Strategy = BoxedStrategy<PackProblem>;
695        fn arbitrary_with(_args: ()) -> Self::Strategy {
696            (any::<bool>(), 1usize..9, 1usize..20, 1usize..20)
697                .prop_flat_map(|(is_a, r, k, mn)| {
698                    (
699                        Just((is_a, r, k, mn)),
700                        sub_range_strat(0..k),
701                        sub_range_strat(0..mn),
702                        1usize..5,
703                    )
704                })
705                .prop_map(|((is_a, r, k, mn), k_range, mn_range, align_panel)| PackProblem {
706                    k,
707                    mn,
708                    is_a,
709                    r,
710                    k_range,
711                    mn_range,
712                    align_panel,
713                })
714                .boxed()
715        }
716    }
717
718    fn sub_range_strat(range: Range<usize>) -> BoxedStrategy<Range<usize>> {
719        (0..range.len())
720            .prop_flat_map(|cropped| (Just(cropped), 0..=cropped))
721            .prop_map(move |(cropped, left)| range.start + left..range.end - (cropped - left))
722            .boxed()
723    }
724
725    proptest::proptest! {
726        #[test]
727        fn prop(pb in any::<PackProblem>()) {
728            pb.check();
729        }
730
731        #[test]
732        fn subrange_prop(_range in sub_range_strat(0..20)) {
733        }
734
735    }
736
737    #[test]
738    fn simple_b_1() {
739        PackProblem {
740            k: 2,
741            mn: 1,
742            is_a: false,
743            r: 1,
744            k_range: 0..2,
745            mn_range: 0..1,
746            align_panel: 1,
747        }
748        .check();
749    }
750
751    #[test]
752    fn simple_b_2() {
753        PackProblem {
754            k: 2,
755            mn: 2,
756            is_a: false,
757            r: 1,
758            k_range: 0..2,
759            mn_range: 0..2,
760            align_panel: 1,
761        }
762        .check()
763    }
764
765    #[test]
766    fn simple_b_3() {
767        PackProblem {
768            k: 2,
769            mn: 1,
770            is_a: false,
771            r: 4,
772            k_range: 0..2,
773            mn_range: 0..1,
774            align_panel: 1,
775        }
776        .check();
777    }
778
779    #[test]
780    fn simple_b_4() {
781        PackProblem {
782            k: 1,
783            mn: 3,
784            is_a: false,
785            r: 2,
786            k_range: 0..1,
787            mn_range: 0..3,
788            align_panel: 1,
789        }
790        .check();
791    }
792
793    #[test]
794    fn simple_a_1() {
795        PackProblem {
796            k: 2,
797            mn: 2,
798            is_a: true,
799            r: 1,
800            k_range: 0..2,
801            mn_range: 0..2,
802            align_panel: 1,
803        }
804        .check();
805    }
806
807    #[test]
808    fn simple_a_2() {
809        PackProblem {
810            k: 2,
811            mn: 3,
812            is_a: true,
813            r: 2,
814            k_range: 0..2,
815            mn_range: 0..3,
816            align_panel: 1,
817        }
818        .check();
819    }
820
821    #[test]
822    fn range_k_0() {
823        PackProblem {
824            k: 2,
825            mn: 1,
826            is_a: false,
827            r: 1,
828            k_range: 1..2,
829            mn_range: 0..1,
830            align_panel: 1,
831        }
832        .check();
833    }
834
835    #[test]
836    fn range_k_1() {
837        PackProblem {
838            k: 2,
839            mn: 2,
840            is_a: false,
841            r: 1,
842            k_range: 0..2,
843            mn_range: 0..1,
844            align_panel: 1,
845        }
846        .check();
847    }
848
849    #[test]
850    fn range_k_2() {
851        PackProblem {
852            k: 2,
853            mn: 1,
854            is_a: false,
855            r: 6,
856            k_range: 1..2,
857            mn_range: 0..1,
858            align_panel: 1,
859        }
860        .check();
861    }
862
863    #[test]
864    fn range_mn_0() {
865        PackProblem {
866            k: 1,
867            mn: 2,
868            is_a: false,
869            r: 2,
870            k_range: 0..1,
871            mn_range: 0..1,
872            align_panel: 1,
873        }
874        .check();
875    }
876
877    #[test]
878    fn range_b_4() {
879        PackProblem {
880            k: 1,
881            mn: 2,
882            is_a: false,
883            r: 6,
884            k_range: 0..1,
885            mn_range: 1..2,
886            align_panel: 1,
887        }
888        .check();
889    }
890
891    #[test]
892    fn range_b_5() {
893        PackProblem {
894            k: 1,
895            mn: 7,
896            is_a: false,
897            r: 6,
898            k_range: 0..1,
899            mn_range: 1..7,
900            align_panel: 1,
901        }
902        .check();
903    }
904
905    #[test]
906    fn align_a_1() {
907        PackProblem {
908            k: 2,
909            mn: 2,
910            is_a: true,
911            r: 1,
912            k_range: 0..1,
913            mn_range: 0..2,
914            align_panel: 2,
915        }
916        .check();
917    }
918
919    #[test]
920    fn align_b_1() {
921        PackProblem {
922            k: 1,
923            mn: 1,
924            is_a: false,
925            r: 1,
926            k_range: 0..1,
927            mn_range: 0..1,
928            align_panel: 2,
929        }
930        .check();
931    }
932
933    #[test]
934    fn align_b_2() {
935        PackProblem {
936            k: 3,
937            mn: 1,
938            is_a: false,
939            r: 1,
940            k_range: 0..3,
941            mn_range: 0..1,
942            align_panel: 2,
943        }
944        .check();
945    }
946
947    #[test]
948    fn align_b_3() {
949        PackProblem {
950            k: 1,
951            mn: 1,
952            is_a: false,
953            r: 3,
954            k_range: 0..1,
955            mn_range: 0..1,
956            align_panel: 2,
957        }
958        .check();
959    }
960
961    #[test]
962    fn align_b_4() {
963        PackProblem {
964            k: 2,
965            mn: 1,
966            is_a: false,
967            r: 1,
968            k_range: 0..1,
969            mn_range: 0..1,
970            align_panel: 2,
971        }
972        .check();
973    }
974
975    #[test]
976    fn align_b_5() {
977        PackProblem {
978            k: 1,
979            mn: 5,
980            is_a: false,
981            r: 4,
982            k_range: 0..1,
983            mn_range: 0..5,
984            align_panel: 3,
985        }
986        .check();
987    }
988}