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 } 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 _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}