1#![allow(
2 clippy::module_name_repetitions,
3 reason = "false positive; TODO: remove after Rust 1.84 is released"
4)]
5
6use alloc::boxed::Box;
7use alloc::sync::Arc;
8use core::fmt;
9use core::ops::{Deref, DerefMut};
10
11#[cfg(doc)]
12use alloc::vec::Vec;
13
14use euclid::{vec3, Point3D};
15use manyfmt::Refmt as _;
16
17use crate::math::{Axis, Cube, GridAab, GridCoordinate, GridIter, GridPoint, GridVector};
18
19#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
28#[expect(clippy::exhaustive_structs)]
29pub struct ZMaj;
30
31#[derive(Clone, Copy, Eq, Hash, PartialEq)]
47pub struct Vol<C, O = ZMaj> {
48 bounds: GridAab,
50 ordering: O,
51 contents: C,
53}
54
55impl<O> Vol<(), O> {
56 pub(crate) fn new_dataless(bounds: GridAab, ordering: O) -> Result<Self, VolLengthError> {
58 if bounds.volume().is_none() {
59 Err(VolLengthError {
60 input_length: None,
61 bounds,
62 })
63 } else {
64 Ok(Self {
65 bounds,
66 ordering,
67 contents: (),
68 })
69 }
70 }
71
72 #[allow(clippy::missing_inline_in_public_items, reason = "is generic already")]
77 pub fn with_elements<C, V>(self, elements: C) -> Result<Vol<C, O>, VolLengthError>
78 where
79 C: Deref<Target = [V]>,
80 {
81 if elements.len() == self.volume() {
82 Ok(Vol {
83 bounds: self.bounds,
84 ordering: self.ordering,
85 contents: elements,
86 })
87 } else {
88 Err(VolLengthError {
89 input_length: Some(elements.len()),
90 bounds: self.bounds(),
91 })
92 }
93 }
94}
95
96impl Vol<(), ZMaj> {
97 #[allow(clippy::missing_inline_in_public_items)]
106 pub fn subdivide(self) -> Result<[Self; 2], Self> {
107 match find_zmaj_subdivision(self) {
108 Some((lower_half, upper_half, _)) => Ok([lower_half, upper_half]),
109 _ => Err(self),
110 }
111 }
112}
113
114impl<C, O: Default, V> Vol<C, O>
116where
117 C: Deref<Target = [V]>,
118{
119 #[allow(clippy::missing_inline_in_public_items, reason = "is generic already")]
127 pub fn from_elements(bounds: GridAab, elements: impl Into<C>) -> Result<Self, VolLengthError> {
128 let elements = elements.into();
129 if Some(elements.len()) == bounds.volume() {
130 Ok(Vol {
131 bounds,
132 ordering: O::default(),
133 contents: elements,
134 })
135 } else {
136 Err(VolLengthError {
137 input_length: Some(elements.len()),
138 bounds,
139 })
140 }
141 }
142}
143
144#[allow(clippy::missing_inline_in_public_items, reason = "is generic already")]
148impl<C, V> Vol<C, ZMaj>
149where
150 C: Deref<Target = [V]> + FromIterator<V>,
152{
153 #[inline]
159 pub fn from_fn<F>(bounds: GridAab, f: F) -> Self
160 where
161 F: FnMut(Cube) -> V,
162 {
163 let bounds = bounds.to_vol::<ZMaj>().unwrap();
164 bounds
165 .with_elements(bounds.iter_cubes().map(f).collect())
166 .unwrap()
167 }
168
169 #[inline]
171 pub fn repeat(bounds: GridAab, value: V) -> Self
172 where
173 V: Clone,
174 {
175 Self::from_fn(bounds, |_| value.clone())
176 }
177
178 #[inline]
184 pub fn from_element(value: V) -> Self {
185 Self::from_elements(GridAab::ORIGIN_CUBE, core::iter::once(value).collect::<C>()).unwrap()
186 }
187
188 #[doc(hidden)] #[expect(clippy::needless_pass_by_value)]
198 pub fn from_y_flipped_array<const DX: usize, const DY: usize, const DZ: usize>(
199 array: [[[V; DX]; DY]; DZ],
200 ) -> Self
201 where
202 V: Clone,
203 {
204 Self::from_fn(
205 GridAab::from_lower_size([0, 0, 0], vec3(DX, DY, DZ).to_u32()),
206 |p| array[p.z as usize][(DY - 1) - (p.y as usize)][p.x as usize].clone(),
207 )
208 }
209}
210
211impl<C, O> Vol<C, O> {
212 #[inline]
214 pub fn bounds(&self) -> GridAab {
215 self.bounds
216 }
217
218 #[inline]
220 pub fn volume(&self) -> usize {
221 let size = self.bounds.size();
226 size.width as usize * size.height as usize * size.depth as usize
228 }
229
230 #[inline]
232 pub fn into_elements(self) -> C {
233 self.contents
234 }
235
236 #[inline]
240 pub fn without_elements(&self) -> Vol<(), O>
241 where
242 O: Clone,
243 {
244 Vol {
245 bounds: self.bounds,
246 ordering: self.ordering.clone(),
247 contents: (),
248 }
249 }
250
251 #[must_use]
257 #[track_caller]
258 #[inline]
259 pub fn translate(self, offset: impl Into<GridVector>) -> Self {
260 self.translate_impl(offset.into())
261 }
262
263 #[track_caller]
264 #[inline]
265 fn translate_impl(mut self, offset: GridVector) -> Self {
266 let new_bounds = self.bounds.translate(offset);
267 if new_bounds.size() != self.bounds.size() {
268 panic!("Vol::translate() offset caused numeric overflow");
271 }
272 self.bounds = new_bounds;
273 self
274 }
275
276 #[doc(hidden)] #[inline]
279 pub fn map_container<C2, V2, F>(self, f: F) -> Vol<C2, O>
280 where
281 F: FnOnce(C) -> C2,
282 C2: Deref<Target = [V2]>,
283 {
284 let bounds = self.bounds;
285 let volume = self.volume();
286 let contents = f(self.contents);
287 if contents.len() != volume {
288 panic!(
289 "{}",
290 VolLengthError {
291 input_length: Some(contents.len()),
292 bounds: self.bounds,
293 }
294 )
295 }
296 Vol {
297 bounds,
298 ordering: self.ordering,
299 contents,
300 }
301 }
302}
303
304impl<C> Vol<C, ZMaj> {
305 #[inline]
308 pub fn iter_cubes(&self) -> GridIter {
309 GridIter::new(self.bounds)
310 }
311
312 #[inline(always)] pub fn index(&self, cube: Cube) -> Option<usize> {
333 let sizes = self.bounds.size();
334
335 let deoffsetted: GridPoint = GridPoint::from(cube)
340 .zip(self.bounds.lower_bounds(), GridCoordinate::wrapping_sub)
341 .to_point();
342
343 if (deoffsetted.x as u32 >= sizes.width)
345 | (deoffsetted.y as u32 >= sizes.height)
346 | (deoffsetted.z as u32 >= sizes.depth)
347 {
348 return None;
349 }
350
351 let ixvec: Point3D<usize, _> = deoffsetted.map(|s| s as usize);
359
360 Some(
364 (ixvec
365 .x
366 .wrapping_mul(sizes.height as usize)
367 .wrapping_add(ixvec.y))
368 .wrapping_mul(sizes.depth as usize)
369 .wrapping_add(ixvec.z),
370 )
371 }
372}
373
374#[allow(clippy::missing_inline_in_public_items, reason = "is generic already")]
376impl<C, O, V> Vol<C, O>
377where
378 C: Deref<Target = [V]>,
379 O: Copy,
380{
381 pub fn as_ref(&self) -> Vol<&[V], O> {
383 Vol {
384 bounds: self.bounds,
385 ordering: self.ordering,
386 contents: self.as_linear(),
387 }
388 }
389
390 pub fn as_mut(&mut self) -> Vol<&mut [V], O>
392 where
393 C: DerefMut,
394 {
395 Vol {
396 bounds: self.bounds,
397 ordering: self.ordering,
398 contents: self.as_linear_mut(),
399 }
400 }
401
402 pub fn as_linear(&self) -> &[V] {
404 let s = &*self.contents;
405 debug_assert_eq!(s.len(), self.volume());
406 s
407 }
408
409 pub fn as_linear_mut(&mut self) -> &mut [V]
411 where
412 C: DerefMut,
413 {
414 let s = &mut *self.contents;
415 debug_assert_eq!(s.len(), self.bounds.volume().unwrap());
416 s
417 }
418}
419
420impl<'a, V> Vol<&'a [V], ZMaj> {
421 #[inline]
427 pub fn get_ref(&self, position: impl Into<Cube>) -> Option<&'a V> {
428 let index = self.index(position.into())?;
429 Some(&self.contents[index])
430 }
431
432 #[allow(clippy::missing_inline_in_public_items)]
441 pub fn subdivide(self) -> Result<[Self; 2], Self> {
442 match find_zmaj_subdivision(self.without_elements()) {
443 Some((lower_half, upper_half, lower_half_len)) => {
444 let (lower_contents, upper_contents) = self.contents.split_at(lower_half_len);
445
446 Ok([
447 lower_half
448 .with_elements(lower_contents)
449 .unwrap_or_else(unreachable_wrong_size),
450 upper_half
451 .with_elements(upper_contents)
452 .unwrap_or_else(unreachable_wrong_size),
453 ])
454 }
455 _ => Err(self),
456 }
457 }
458}
459
460impl<V> Vol<&mut [V], ZMaj> {
461 #[allow(clippy::missing_inline_in_public_items)]
478 pub fn subdivide(self, filter: impl FnOnce([Vol<()>; 2]) -> bool) -> Result<[Self; 2], Self> {
479 match find_zmaj_subdivision(self.without_elements()) {
480 Some((lower_half, upper_half, lower_half_len)) if filter([lower_half, upper_half]) => {
481 let (lower_contents, upper_contents) = self.contents.split_at_mut(lower_half_len);
482
483 Ok([
484 lower_half
485 .with_elements(lower_contents)
486 .unwrap_or_else(unreachable_wrong_size),
487 upper_half
488 .with_elements(upper_contents)
489 .unwrap_or_else(unreachable_wrong_size),
490 ])
491 }
492 _ => Err(self),
493 }
494 }
495}
496
497impl<V: Clone, O> Vol<Arc<[V]>, O> {
498 #[doc(hidden)] #[allow(clippy::missing_inline_in_public_items)]
501 pub fn make_linear_mut(&mut self) -> &mut [V] {
502 let slice: &mut [V] = Arc::make_mut(&mut self.contents);
503 debug_assert_eq!(slice.len(), self.bounds.volume().unwrap());
504 slice
505 }
506}
507
508#[allow(clippy::missing_inline_in_public_items, reason = "is generic already")]
510impl<C, V> Vol<C, ZMaj>
511where
512 C: Deref<Target = [V]>,
513{
514 #[inline]
517 pub fn get(&self, position: impl Into<Cube>) -> Option<&V> {
518 let index = self.index(position.into())?;
519 Some(&self.as_linear()[index])
520 }
521
522 #[inline]
525 pub fn get_mut(&mut self, position: impl Into<Cube>) -> Option<&mut V>
526 where
527 C: DerefMut,
528 {
529 let index = self.index(position.into())?;
530 Some(&mut self.as_linear_mut()[index])
531 }
532
533 pub fn iter<'s>(&'s self) -> impl Iterator<Item = (Cube, &'s V)> + Clone
536 where
537 V: 's,
538 {
539 self.iter_cubes().zip(self.as_linear().iter())
540 }
541
542 pub fn iter_mut<'s>(&'s mut self) -> impl Iterator<Item = (Cube, &'s mut V)>
545 where
546 C: DerefMut,
547 V: 's,
548 {
549 self.iter_cubes().zip(self.as_linear_mut().iter_mut())
550 }
551}
552
553#[allow(clippy::missing_inline_in_public_items, reason = "is generic already")]
554impl<V, O> Vol<Box<[V]>, O> {
555 pub fn map<T, F>(self, f: F) -> Vol<Box<[T]>, O>
557 where
558 F: FnMut(V) -> T,
559 {
560 Vol {
561 bounds: self.bounds,
562 ordering: self.ordering,
563 contents: <Box<[V]> as IntoIterator>::into_iter(self.contents)
565 .map(f)
566 .collect(),
567 }
568 }
569}
570
571#[allow(clippy::missing_inline_in_public_items, reason = "is generic already")]
572impl<C: fmt::Debug, O: fmt::Debug> fmt::Debug for Vol<C, O> {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 let Self {
579 bounds,
580 ordering,
581 contents,
582 } = self;
583
584 let mut ds = f.debug_struct(&format!(
585 "Vol<{contents_type}, {ordering:?}>",
586 contents_type = core::any::type_name::<C>(),
587 ));
588 ds.field("bounds", &bounds);
589 let volume = self.volume();
590 if core::any::type_name::<C>() == core::any::type_name::<()>() {
591 } else if volume > 32 {
593 ds.field(
594 "contents",
595 &format!("[...{volume} elements]").refmt(&manyfmt::formats::Unquote),
596 );
597 } else {
598 ds.field("contents", &contents);
599 }
600 ds.finish()
601 }
602}
603
604impl<P, C, V> core::ops::Index<P> for Vol<C, ZMaj>
605where
606 P: Into<Cube>,
607 C: Deref<Target = [V]>,
608{
609 type Output = V;
610
611 #[inline(always)] fn index(&self, position: P) -> &Self::Output {
617 let position: Cube = position.into();
618 if let Some(index) = self.index(position) {
619 &self.contents[index]
620 } else {
621 panic!(
622 "position {:?} out of Vol bounds {:?}",
623 position, self.bounds
624 )
625 }
626 }
627}
628impl<P, C, V> core::ops::IndexMut<P> for Vol<C, ZMaj>
629where
630 P: Into<Cube>,
631 C: DerefMut<Target = [V]>,
632{
633 #[inline(always)]
636 fn index_mut(&mut self, position: P) -> &mut Self::Output {
637 let position: Cube = position.into();
638 if let Some(index) = self.index(position) {
639 &mut self.contents[index]
640 } else {
641 panic!(
642 "position {:?} out of Vol bounds {:?}",
643 position, self.bounds
644 )
645 }
646 }
647}
648
649mod aab_compat {
650 use super::*;
651
652 impl<O> PartialEq<GridAab> for Vol<(), O> {
653 #[inline]
654 fn eq(&self, other: &GridAab) -> bool {
655 self.bounds() == *other
656 }
657 }
658
659 impl<O> PartialEq<Vol<(), O>> for GridAab {
660 #[inline]
661 fn eq(&self, other: &Vol<(), O>) -> bool {
662 *self == other.bounds()
663 }
664 }
665}
666
667#[cfg(feature = "arbitrary")]
668#[allow(clippy::missing_inline_in_public_items)]
669pub(crate) mod vol_arb {
670 use super::*;
671 use arbitrary::Arbitrary;
672
673 const MAX_VOLUME: usize = 2_usize.pow(16);
676
677 pub(crate) const ARBITRARY_BOUNDS_SIZE_HINT: (usize, Option<usize>) = {
679 let gc = size_of::<GridCoordinate>();
684 (3 + 1, Some(gc * 6 + 1))
685 };
686
687 impl<O: Default> Vol<(), O> {
688 #[cfg(feature = "arbitrary")]
689 #[doc(hidden)]
690 pub fn arbitrary_with_max_volume(
691 u: &mut arbitrary::Unstructured<'_>,
692 volume: usize,
693 ) -> arbitrary::Result<Self> {
694 let mut limit: u32 = volume.try_into().unwrap_or(u32::MAX);
696 let size_1 = u.int_in_range(0..=limit)?;
697 limit /= size_1.max(1);
698 let size_2 = u.int_in_range(0..=limit)?;
699 limit /= size_2.max(1);
700 let size_3 = u.int_in_range(0..=limit)?;
701
702 let sizes = *u.choose(&[
704 vec3(size_1, size_2, size_3),
705 vec3(size_1, size_3, size_2),
706 vec3(size_2, size_1, size_3),
707 vec3(size_2, size_3, size_1),
708 vec3(size_3, size_1, size_2),
709 vec3(size_3, size_2, size_1),
710 ])?;
711
712 let possible_lower_bounds = sizes.map(|coord| {
714 GridCoordinate::MIN..=GridCoordinate::MAX.saturating_sub_unsigned(coord)
715 });
716 let lower_bounds = GridPoint::new(
717 u.int_in_range(possible_lower_bounds.x)?,
718 u.int_in_range(possible_lower_bounds.y)?,
719 u.int_in_range(possible_lower_bounds.z)?,
720 );
721
722 Ok(GridAab::from_lower_size(lower_bounds, sizes)
723 .to_vol()
724 .expect("GridAab as Arbitrary failed to compute valid bounds"))
725 }
726 }
727
728 impl<'a, V: Arbitrary<'a>, C> Arbitrary<'a> for Vol<C, ZMaj>
729 where
730 C: FromIterator<V> + Deref<Target = [V]>,
731 {
732 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
733 let bounds = Vol::<()>::arbitrary_with_max_volume(u, MAX_VOLUME)?;
734 let contents: C = u
735 .arbitrary_iter()?
736 .take(bounds.volume())
737 .collect::<Result<C, _>>()?;
738 bounds
739 .with_elements(contents)
740 .map_err(|_| arbitrary::Error::NotEnoughData)
741 }
742
743 fn size_hint(depth: usize) -> (usize, Option<usize>) {
744 Self::try_size_hint(depth).unwrap_or_default()
746 }
747
748 fn try_size_hint(
749 depth: usize,
750 ) -> Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
751 arbitrary::size_hint::try_recursion_guard(depth, |depth| {
752 let (lower, upper) = V::try_size_hint(depth)?;
753 Ok(arbitrary::size_hint::and(
754 ARBITRARY_BOUNDS_SIZE_HINT,
755 (
756 lower.saturating_mul(MAX_VOLUME),
757 upper.map(|u| u.saturating_mul(MAX_VOLUME)),
758 ),
759 ))
760 })
761 }
762 }
763}
764
765#[derive(Clone, Copy, Debug, Eq, PartialEq)]
768pub struct VolLengthError {
769 input_length: Option<usize>,
771 bounds: GridAab,
773}
774
775impl core::error::Error for VolLengthError {}
776
777impl fmt::Display for VolLengthError {
778 #[allow(clippy::missing_inline_in_public_items)]
779 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
780 let Self {
781 input_length,
782 bounds,
783 } = self;
784 match (input_length, bounds.volume()) {
785 (Some(input_length), Some(volume)) => write!(
786 f,
787 "data of length {input_length} cannot fill volume {volume} of {bounds:?}",
788 ),
789
790 (Some(input_length), None) => write!(
791 f,
792 "data of length {input_length} cannot fill {bounds:?}, \
793 which is too large to be linearized at all",
794 ),
795
796 (None, None) => write!(
797 f,
798 "{bounds:?} has a volume of {volume_u128}, \
799 which is too large to be linearized",
800 volume_u128 = bounds.size().cast::<u128>().volume(),
802 ),
803
804 (None, Some(_)) => write!(f, "<malformed error {self:?}>"),
805 }
806 }
807}
808
809fn find_zmaj_subdivision(vol: Vol<()>) -> Option<(Vol<()>, Vol<()>, usize)> {
813 let bounds = vol.bounds();
816 for axis in [Axis::X, Axis::Y, Axis::Z] {
817 let axis_range = bounds.axis_range(axis);
818 let size: u32 = bounds.size()[axis];
819 if size >= 2 {
820 #[expect(clippy::cast_possible_wrap, reason = "known to fit")]
821 let split_coordinate = axis_range.start + (size / 2) as i32;
822
823 let mut lower_half_ub = bounds.upper_bounds();
824 lower_half_ub[axis] = split_coordinate;
825 let lower_half = GridAab::from_lower_upper(bounds.lower_bounds(), lower_half_ub)
826 .to_vol()
827 .unwrap_or_else(unreachable_wrong_size);
828
829 let mut upper_half_lb = bounds.lower_bounds();
830 upper_half_lb[axis] = split_coordinate;
831 let upper_half = GridAab::from_lower_upper(upper_half_lb, bounds.upper_bounds())
832 .to_vol()
833 .unwrap_or_else(unreachable_wrong_size);
834
835 let lower_half_volume = lower_half.volume();
836 debug_assert_eq!(lower_half_volume + upper_half.volume(), vol.volume());
837 return Some((lower_half, upper_half, lower_half_volume));
838 }
839 }
840 None
841}
842
843#[cold]
847fn unreachable_wrong_size<T>(error: VolLengthError) -> T {
848 panic!("impossible size mismatch: {error}")
849}
850
851#[cfg(test)]
852mod tests {
853 use super::*;
854 use alloc::string::String;
855 use pretty_assertions::assert_eq;
856
857 type VolBox<T> = Vol<Box<[T]>>;
858
859 fn cube(x: GridCoordinate, y: GridCoordinate, z: GridCoordinate) -> Cube {
860 Cube::new(x, y, z)
861 }
862
863 #[test]
864 fn debug_no_elements() {
865 let vol = GridAab::from_lower_size([10, 0, 0], [4, 1, 1])
866 .to_vol::<ZMaj>()
867 .unwrap();
868 assert_eq!(
869 format!("{vol:#?}"),
870 indoc::indoc! {"
871 Vol<(), ZMaj> {
872 bounds: GridAab(
873 10..14 (4),
874 0..1 (1),
875 0..1 (1),
876 ),
877 }\
878 "}
879 )
880 }
881
882 #[test]
883 fn debug_with_contents() {
884 let vol = VolBox::from_fn(GridAab::from_lower_size([10, 0, 0], [4, 1, 1]), |p| p.x);
885 assert_eq!(
886 format!("{vol:#?}"),
887 indoc::indoc! {"
888 Vol<alloc::boxed::Box<[i32]>, ZMaj> {
889 bounds: GridAab(
890 10..14 (4),
891 0..1 (1),
892 0..1 (1),
893 ),
894 contents: [
895 10,
896 11,
897 12,
898 13,
899 ],
900 }\
901 "}
902 )
903 }
904
905 #[test]
906 fn debug_without_contents() {
907 let vol = VolBox::from_fn(GridAab::from_lower_size([0, 0, 0], [64, 1, 1]), |p| p.x);
908 assert_eq!(
909 format!("{vol:#?}"),
910 indoc::indoc! {"
911 Vol<alloc::boxed::Box<[i32]>, ZMaj> {
912 bounds: GridAab(
913 0..64 (64),
914 0..1 (1),
915 0..1 (1),
916 ),
917 contents: [...64 elements],
918 }\
919 "}
920 )
921 }
922
923 #[test]
924 fn from_elements() {
925 let bounds = GridAab::from_lower_size([10, 0, 0], [4, 1, 1]);
926 assert_eq!(
927 VolBox::from_fn(bounds, |p| p.x),
928 VolBox::from_elements(bounds, vec![10i32, 11, 12, 13]).unwrap(),
929 );
930 }
931
932 #[test]
933 fn from_elements_error() {
934 let bounds = GridAab::from_lower_size([10, 0, 0], [4, 1, 1]);
935 assert_eq!(
936 VolBox::from_elements(bounds, vec![10i32, 11, 12]),
937 Err(VolLengthError {
938 input_length: Some(3),
939 bounds,
940 })
941 );
942 }
943
944 #[test]
945 fn repeat() {
946 let bounds = GridAab::from_lower_size([10, 0, 0], [2, 2, 1]);
947 assert_eq!(
948 VolBox::repeat(bounds, 9),
949 VolBox::from_elements(bounds, vec![9, 9, 9, 9]).unwrap(),
950 );
951 }
952
953 #[test]
954 fn from_element() {
955 let element = String::from("x");
956 assert_eq!(
957 VolBox::from_element(element.clone()),
958 VolBox::from_elements(GridAab::ORIGIN_CUBE, [element]).unwrap(),
959 );
960 }
961
962 #[test]
963 fn from_y_flipped() {
964 let array = VolBox::from_y_flipped_array([
965 [*b"abcd", *b"efgh", *b"ijkl"],
966 [*b"mnop", *b"qrst", *b"uvwx"],
967 ]);
968 assert_eq!(
969 array,
970 Vol::from_elements(
971 GridAab::from_lower_size([0, 0, 0], [4, 3, 2]),
972 *b"iueqamjvfrbnkwgscolxhtdp"
973 )
974 .unwrap()
975 );
976 }
977
978 #[test]
979 fn index_overflow_low() {
980 let low = GridAab::from_lower_size([GridCoordinate::MAX - 1, 0, 0], [1, 1, 1])
983 .to_vol::<ZMaj>()
984 .unwrap();
985 assert_eq!(low.index(cube(0, 0, 0)), None);
986 assert_eq!(low.index(cube(-1, 0, 0)), None);
987 assert_eq!(low.index(cube(-2, 0, 0)), None);
988 assert_eq!(low.index(cube(GridCoordinate::MIN, 0, 0)), None);
989 assert_eq!(low.index(cube(GridCoordinate::MAX - 1, 0, 0)), Some(0));
991 }
992
993 #[test]
994 fn index_overflow_high() {
995 let high = GridAab::from_lower_size([GridCoordinate::MAX - 1, 0, 0], [1, 1, 1])
996 .to_vol::<ZMaj>()
997 .unwrap();
998 assert_eq!(high.index(cube(0, 0, 0)), None);
999 assert_eq!(high.index(cube(1, 0, 0)), None);
1000 assert_eq!(high.index(cube(2, 0, 0)), None);
1001 assert_eq!(high.index(cube(GridCoordinate::MAX - 1, 0, 0)), Some(0));
1002 }
1003
1004 #[test]
1005 fn index_not_overflow_large_volume() {
1006 let vol = GridAab::from_lower_size([0, 0, 0], [2000, 2000, 2000])
1007 .to_vol::<ZMaj>()
1008 .unwrap();
1009 assert_eq!(
1012 vol.index(cube(1500, 1500, 1500)),
1013 Some(((1500 * 2000) + 1500) * 2000 + 1500)
1014 );
1015 }
1016
1017 #[inline(never)]
1019 fn check_subdivide_case(vol: Vol<&mut [Cube]>) {
1020 fn filter(_: [Vol<()>; 2]) -> bool {
1021 true
1022 }
1023
1024 eprintln!("Checking {:?}", vol.bounds());
1025
1026 for (cube, &value) in vol.iter() {
1028 assert_eq!(cube, value);
1029 }
1030
1031 if vol.volume() < 2 {
1032 assert!(vol.without_elements().subdivide().is_err()); assert!(vol.as_ref().subdivide().is_err()); assert!(vol.subdivide(filter).is_err()); } else {
1037 let Ok([a, b]) = vol.without_elements().subdivide() else {
1038 panic!("{vol:?} failed to subdivide");
1039 };
1040 assert_ne!(a.volume(), 0);
1041 assert_ne!(b.volume(), 0);
1042
1043 let [aref, bref] = vol.as_ref().subdivide().unwrap();
1045 assert_eq!((a, b), (aref.without_elements(), bref.without_elements()));
1046
1047 let [amut, bmut] = vol.subdivide(filter).unwrap();
1049 assert_eq!((a, b), (amut.without_elements(), bmut.without_elements()));
1050
1051 check_subdivide_case(amut);
1053 check_subdivide_case(bmut);
1054 }
1055 }
1056 fn check_subdivide(bounds: GridAab) {
1057 check_subdivide_case(Vol::<Box<[Cube]>>::from_fn(bounds, std::convert::identity).as_mut());
1058 }
1059
1060 #[test]
1061 fn subdivide_test() {
1062 check_subdivide(GridAab::ORIGIN_CUBE);
1063 check_subdivide(GridAab::ORIGIN_EMPTY);
1064 check_subdivide(GridAab::from_lower_upper([0, 0, 0], [2, 4, 5]));
1065 }
1066
1067 #[cfg(feature = "arbitrary")]
1068 #[test]
1069 fn arbitrary_bounds_size_hint() {
1070 use arbitrary::{Arbitrary, Unstructured};
1071 let hint = vol_arb::ARBITRARY_BOUNDS_SIZE_HINT;
1072 let most_bytes_used = (0..=255)
1073 .map(|byte| {
1074 let data = [byte; 1000];
1076 let mut u = Unstructured::new(&data);
1077 GridAab::arbitrary(&mut u).unwrap();
1078 let bytes_used = 1000 - u.len();
1079 assert!(
1080 bytes_used >= hint.0,
1081 "used {}, less than {}",
1082 bytes_used,
1083 hint.0
1084 );
1085 bytes_used
1086 })
1087 .max();
1088 assert_eq!(most_bytes_used, hint.1);
1089
1090 }
1092
1093 #[cfg(feature = "arbitrary")]
1094 #[test]
1095 fn arbitrary_bounds_volume() {
1096 use arbitrary::Unstructured;
1097 use itertools::Itertools as _;
1098 let max_volume = 100;
1099 let minmax = (0..=255)
1100 .map(|byte| {
1101 let data = [byte; 25];
1103 let mut u = Unstructured::new(&data);
1104 Vol::<()>::arbitrary_with_max_volume(&mut u, max_volume)
1105 .unwrap()
1106 .volume()
1107 })
1108 .minmax()
1109 .into_option();
1110 assert_eq!(minmax, Some((0, max_volume)));
1111 }
1112}