diff --git a/src/day19.rs b/src/day19.rs index 7865f94..c9233a2 100644 --- a/src/day19.rs +++ b/src/day19.rs @@ -1,43 +1,84 @@ -use std::{collections::{HashSet, HashMap}, ops::{Add, Sub, Neg, Mul}}; +use std::{ops::{Sub, Mul, Add, Neg}, collections::{HashMap, HashSet}, vec, fmt}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Point(i32, i32, i32); +// https://i.imgur.com/Ff1vGT9.png +const ROTATIONS: [Rotation; 24] = [ + Rotation( 1, 0, 0, 0, 1, 0, 0, 0, 1), // (x ,y ,z) + Rotation( 1, 0, 0, 0, 0, 1, 0,-1, 0), // (x ,z ,-y) + Rotation( 1, 0, 0, 0,-1, 0, 0, 0,-1), // (x ,-y,-z) + Rotation( 1, 0, 0, 0, 0,-1, 0, 1, 0), // (x ,-z,y ) + Rotation(-1, 0, 0, 0,-1, 0, 0, 0, 1), // (-x,-y,z) + Rotation(-1, 0, 0, 0, 0, 1, 0, 1, 0), // (-x,z ,y ) + Rotation(-1, 0, 0, 0, 1, 0, 0, 0,-1), // (-x,y ,-z) + Rotation(-1, 0, 0, 0, 0,-1, 0,-1, 0), // (-x,-z,-y ) + Rotation( 0, 1, 0, 0, 0, 1, 1, 0, 0), // (y ,z ,x) + Rotation( 0, 1, 0, 1, 0, 0, 0, 0,-1), // (y ,x ,-z) + Rotation( 0, 1, 0, 0, 0,-1,-1, 0, 0), // (y ,-z,-x) + Rotation( 0, 1, 0,-1, 0, 0, 0, 0, 1), // (y ,-x,z ) + Rotation( 0,-1, 0, 0, 0,-1, 1, 0, 0), // (-y,-z,x) + Rotation( 0,-1, 0, 1, 0, 0, 0, 0, 1), // (-y,x ,z ) + Rotation( 0,-1, 0, 0, 0, 1,-1, 0, 0), // (-y,z ,-x) + Rotation( 0,-1, 0,-1, 0, 0, 0, 0,-1), // (-y,-x,-z ) + Rotation( 0, 0, 1, 1, 0, 0, 0, 1, 0), // (z ,x ,y) + Rotation( 0, 0, 1, 0, 1, 0,-1, 0, 0), // (z ,y ,-x) + Rotation( 0, 0, 1,-1, 0, 0, 0,-1, 0), // (z ,-x,-y) + Rotation( 0, 0, 1, 0,-1, 0, 1, 0, 0), // (z ,-y,x ) + Rotation( 0, 0,-1,-1, 0, 0, 0, 1, 0), // (-z,-x,y) + Rotation( 0, 0,-1, 0, 1, 0, 1, 0, 0), // (-z,y ,x ) + Rotation( 0, 0,-1, 1, 0, 0, 0,-1, 0), // (-z,x ,-y) + Rotation( 0, 0,-1, 0,-1, 0,-1, 0, 0), // (-z,-y,-x) +]; -impl Add for Point { - type Output = Self; +#[derive(Clone, Copy, PartialEq, Eq)] +struct Rotation(i32, i32, i32, i32, i32, i32, i32, i32, i32); - fn add(self, rhs: Self) -> Self::Output { - Point( - self.0 + rhs.0, - self.1 + rhs.1, - self.2 + rhs.2 - ) - } +impl Rotation { + fn identity() -> Rotation { + Rotation(1, 0, 0, 0, 1, 0, 0, 0, 1) + } + + fn transpose(self) -> Rotation { + Rotation( + self.0, + self.3, + self.6, + + self.1, + self.4, + self.7, + + self.2, + self.5, + self.8, + ) + } } -impl Sub for Point { - type Output = Self; +impl fmt::Debug for Rotation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn write_param(f: &mut fmt::Formatter<'_>, x: i32, y: i32, z: i32) -> fmt::Result { + if x != 0 { + write!(f, "{}", if x < 0 {"-x"} else {"x"})?; + } else if y != 0 { + write!(f, "{}", if y < 0 {"-y"} else {"y"})?; + } else if z != 0 { + write!(f, "{}", if z < 0 {"-z"} else {"z"})?; + } else { + write!(f, "0")?; + } + Ok(()) + } - fn sub(self, rhs: Self) -> Self::Output { - Point( - self.0 - rhs.0, - self.1 - rhs.1, - self.2 - rhs.2 - ) - } -} - -impl Neg for Point { - type Output = Point; - - fn neg(self) -> Self::Output { - Point(-self.0, -self.1, -self.2) + write!(f, "Rotation(")?; + write_param(f, self.0, self.1, self.2)?; + write!(f, ",")?; + write_param(f, self.3, self.4, self.5)?; + write!(f, ",")?; + write_param(f, self.6, self.7, self.8)?; + write!(f, ")")?; + Ok(()) } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct Rotation(i32, i32, i32, i32, i32, i32, i32, i32, i32); - impl Mul for Rotation { type Output = Point; @@ -69,55 +110,57 @@ impl Mul for Rotation { } } -impl Rotation { - fn identity() -> Rotation { - Rotation(1, 0, 0, 0, 1, 0, 0, 0, 1) +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Point(i32, i32, i32); + +impl Neg for Point { + type Output = Point; + + fn neg(self) -> Self::Output { + Point(-self.0, -self.1, -self.2) + } +} + +impl fmt::Debug for Point { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Point({},{},{})", self.0, self.1, self.2)?; + Ok(()) + } +} + +impl Point { + fn mag_sqrd(&self) -> i32 { + self.0*self.0 + self.1*self.1 + self.2*self.2 } - fn transpose(self) -> Rotation { - Rotation( - self.0, - self.3, - self.6, - - self.1, - self.4, - self.7, - - self.2, - self.5, - self.8, - ) + fn manhattan(&self, other: &Point) -> i32 { + (self.0-other.0).abs() + (self.1-other.1).abs() + (self.2-other.2).abs() } } -// https://i.imgur.com/Ff1vGT9.png -const ROTATIONS: [Rotation; 24] = [ - Rotation( 1, 0, 0, 0, 1, 0, 0, 0, 1), // (x ,y ,z) - Rotation( 1, 0, 0, 0, 0, 1, 0,-1, 0), // (x ,z ,-y) - Rotation( 1, 0, 0, 0,-1, 0, 0, 0,-1), // (x ,-y,-z) - Rotation( 1, 0, 0, 0, 0,-1, 0, 1, 0), // (x ,-z,y ) - Rotation(-1, 0, 0, 0,-1, 0, 0, 0, 1), // (-x,-y,z) - Rotation(-1, 0, 0, 0, 0, 1, 0, 1, 0), // (-x,z ,y ) - Rotation(-1, 0, 0, 0, 1, 0, 0, 0,-1), // (-x,y ,-z) - Rotation(-1, 0, 0, 0, 0,-1, 0,-1, 0), // (-x,-z,-y ) - Rotation( 0, 1, 0, 0, 0, 1, 1, 0, 0), // (y ,z ,x) - Rotation( 0, 1, 0, 1, 0, 0, 0, 0,-1), // (y ,x ,-z) - Rotation( 0, 1, 0, 0, 0,-1,-1, 0, 0), // (y ,-z,-x) - Rotation( 0, 1, 0,-1, 0, 0, 0, 0, 1), // (y ,-x,z ) - Rotation( 0,-1, 0, 0, 0,-1, 1, 0, 0), // (-y,-z,x) - Rotation( 0,-1, 0, 1, 0, 0, 0, 0, 1), // (-y,x ,z ) - Rotation( 0,-1, 0, 0, 0, 1,-1, 0, 0), // (-y,z ,-x) - Rotation( 0,-1, 0,-1, 0, 0, 0, 0,-1), // (-y,-x,-z ) - Rotation( 0, 0, 1, 1, 0, 0, 0, 1, 0), // (z ,x ,y) - Rotation( 0, 0, 1, 0, 1, 0,-1, 0, 0), // (z ,y ,-x) - Rotation( 0, 0, 1,-1, 0, 0, 0,-1, 0), // (z ,-x,-y) - Rotation( 0, 0, 1, 0,-1, 0, 1, 0, 0), // (z ,-y,x ) - Rotation( 0, 0,-1,-1, 0, 0, 0, 1, 0), // (-z,-x,y) - Rotation( 0, 0,-1, 0, 1, 0, 1, 0, 0), // (-z,y ,x ) - Rotation( 0, 0,-1, 1, 0, 0, 0,-1, 0), // (-z,x ,-y) - Rotation( 0, 0,-1, 0,-1, 0,-1, 0, 0), // (-z,-y,-x) -]; +impl Sub for Point { + type Output = Point; + + fn sub(self, rhs: Self) -> Self::Output { + Point ( + self.0 - rhs.0, + self.1 - rhs.1, + self.2 - rhs.2 + ) + } +} + +impl Add for Point { + type Output = Point; + + fn add(self, rhs: Self) -> Self::Output { + Point ( + self.0 + rhs.0, + self.1 + rhs.1, + self.2 + rhs.2 + ) + } +} fn parse_point(line: &str) -> Point { let nums: Vec<&str> = line.splitn(3, ',').collect(); @@ -142,94 +185,39 @@ pub fn parse_input(input: &str) -> Vec> { scanners } -fn calc_diff(a: &Point, b: &Point) -> Point { - let x = (a.0 - b.0).abs(); - let y = (a.1 - b.1).abs(); - let z = (a.2 - b.2).abs(); - - let lowest = x.min(y).min(z); - let highest = x.max(y).max(z); - let middle = x + y + z - lowest - highest; - - Point(lowest, middle, highest) -} - -fn get_point_pairs(points: &[Point]) -> Vec<(Point, Point)> { - points.iter() - .enumerate() - .flat_map(|(i, p1)| points.iter().skip(i + 1).map(move |p2| (*p1, *p2))) - .collect() -} - -fn get_point_diffs(a: &[Point]) -> HashSet { - get_point_pairs(a).iter() - .map(|(p1, p2)| calc_diff(p1, p2)) - .collect() -} - -fn do_scanners_possibly_overlap(a: &[Point], b: &[Point]) -> bool { - let a_diffs = get_point_diffs(a); - let b_diffs = get_point_diffs(b); - - a_diffs.intersection(&b_diffs).count() >= 66 -} - -fn find_possibly_overlapping_scanners(scanners: &[Vec]) -> Vec<(usize, usize)> { - let mut pairs = vec![]; - - for (i, scanner1) in scanners.iter().enumerate() { - for (j, scanner2) in scanners.iter().enumerate().skip(i + 1) { - if do_scanners_possibly_overlap(scanner1, scanner2) { - pairs.push((i, j)); - } +fn calc_beacon_pairs(beacons: &[Point]) -> HashMap<(usize, usize), i32> { + let mut diffs = HashMap::new(); + for (i, p1) in beacons.iter().enumerate() { + for (j, p2) in beacons.iter().enumerate().skip(i+1) { + let diff = (*p1 - *p2).mag_sqrd(); + diffs.insert((i, j), diff); } } - - pairs + diffs } -fn find_possible_rotations(dir1: Point, dir2: Point) -> Vec { - let mut possible_rotations = vec![]; +fn find_correct_transform( + points1: &[Point], diffs1: &HashMap<(usize, usize), i32>, + points2: &[Point], diffs2: &HashMap<(usize, usize), i32> + ) -> Option<(Rotation, Point)> { for rot in ROTATIONS { - if rot.mul(dir2) == dir1 { - possible_rotations.push(rot); - break; - } - } + for (pair1, diff1) in diffs1 { + for (pair2, diff2) in diffs2 { + if diff1 == diff2 { + let base1 = points1[pair1.0]; + let base2 = points2[pair2.0]; + let normalized1 = points1.iter().map(|p| *p - base1).collect::>(); + let normalized2 = points2.iter().map(|p| rot.mul(*p - base2)).collect::>(); - for rot in ROTATIONS { - if rot.mul(dir2) == -dir1 { - possible_rotations.push(rot); - break; - } - } - - possible_rotations -} - -fn find_transformation(scanner1: &[Point], scanner2: &[Point]) -> Option<(Point, Rotation)> { - let point_pairs1 = get_point_pairs(scanner1); - let point_pairs2 = get_point_pairs(scanner2); - - let scanner1_set: HashSet<_> = scanner1.iter().copied().collect(); - - for pair1 in &point_pairs1 { - for pair2 in &point_pairs2 { - if calc_diff(&pair1.0, &pair1.1) != calc_diff(&pair2.0, &pair2.1) { - continue; - } - let dir1 = pair1.0 - pair1.1; - let dir2 = pair2.0 - pair2.1; - - for rotation in find_possible_rotations(dir1, dir2) { - let translation = pair1.0 - rotation.mul(pair2.0); - let transformed_scanner2: HashSet<_> = scanner2.iter() - .map(|p| rotation.mul(*p) + translation) - .collect(); - - if scanner1_set.intersection(&transformed_scanner2).count() >= 12 { - return Some((translation, rotation)); + if normalized1.intersection(&normalized2).count() >= 12 { + let offset = base1 - rot.mul(base2); + // dbg!(normalized1.intersection(&normalized2).count()); + // let a = points1.iter().copied().collect::>(); + // let b = points2.iter().map(|p| rot.mul(*p) + offset).collect::>(); + // dbg!(a.intersection(&b).count()); + return Some((rot, offset)) + } } } } @@ -238,74 +226,91 @@ fn find_transformation(scanner1: &[Point], scanner2: &[Point]) -> Option<(Point, None } -fn find_canonical_transformations(scanners: &[Vec]) -> Vec<(Point, Rotation)> { - let mut canonical = vec![]; +fn calc_transforms(scanners: &[Vec]) -> Vec<(Rotation, Point)> { + let mut diffs = vec![]; + for scanner in scanners { + diffs.push(calc_beacon_pairs(scanner)) + } + + let mut relative_transforms = HashMap::new(); + { + let dist_diffs = diffs.iter() + .map(|m| m.values().collect::>()) + .collect::>(); + + for (i, pairs1) in dist_diffs.iter().enumerate() { + for (j, pairs2) in dist_diffs.iter().enumerate().skip(i+1) { + if pairs1.intersection(pairs2).count() >= 66 { + let trans = find_correct_transform( + &scanners[i], &diffs[i], + &scanners[j], &diffs[j] + ); + if let Some((rot, offset)) = trans { + relative_transforms.entry(i) + .or_insert(vec![]) + .push((j, rot, offset)); + + let inv_rot = rot.transpose(); + + relative_transforms.entry(j) + .or_insert(vec![]) + .push((i, inv_rot, inv_rot.mul(-offset))); + } + } + } + } + } + + let mut transforms = vec![]; for _ in 0..scanners.len() { - canonical.push((Point(0, 0, 0), Rotation::identity())) + transforms.push((Rotation::identity(), Point(0, 0, 0))); } - let mut transforms = HashMap::new(); - for (scanner1, scanner2) in find_possibly_overlapping_scanners(scanners) { - let (translation, rotation) = find_transformation(&scanners[scanner1], &scanners[scanner2]).unwrap(); - let inv_rotation = rotation.transpose(); - - transforms.entry(scanner1) - .or_insert_with(Vec::new) - .push((scanner2, translation, rotation)); - - transforms.entry(scanner2) - .or_insert_with(Vec::new) - .push((scanner1, inv_rotation.mul(-translation), inv_rotation)); - } - - let mut stack = vec![(0, Point(0, 0, 0), Rotation::identity())]; + let mut stack = vec![(0, Rotation::identity(), Point(0, 0, 0))]; let mut visited = HashSet::new(); while !stack.is_empty() { - let (id, translation, rotation) = stack.pop().unwrap(); + let (id, rot, offset) = stack.pop().unwrap(); if visited.contains(&id) { continue; } visited.insert(id); - for nbr in transforms.get(&id).unwrap() { + for nbr in relative_transforms.get(&id).unwrap() { if visited.contains(&nbr.0) { continue; } - let new_translation = rotation.mul(nbr.1) + translation; - let new_rotation = rotation.mul(nbr.2); - stack.push((nbr.0, new_translation, new_rotation)); - canonical[nbr.0] = (new_translation, new_rotation); + let new_offset = rot.mul(nbr.2) + offset; + let new_rot = rot.mul(nbr.1); + stack.push((nbr.0, new_rot, new_offset)); + transforms[nbr.0] = (new_rot, new_offset); } } - canonical + transforms } pub fn part1(scanners: &[Vec]) -> u32 { - let transforms = find_canonical_transformations(scanners); - let mut beacons = HashSet::new(); + let transforms = calc_transforms(scanners); - for (i, points) in scanners.iter().enumerate() { - let (translation, rotation) = transforms[i]; - for point in points.iter().map(|p| rotation.mul(*p) + translation) { - beacons.insert(point); + let mut all_beacons = HashSet::new(); + for (i, (rot, offset)) in transforms.iter().enumerate() { + for scanner in &scanners[i] { + all_beacons.insert(rot.mul(*scanner) + *offset); } } - beacons.len() as u32 + all_beacons.len() as u32 } pub fn part2(scanners: &[Vec]) -> i32 { - let transforms = find_canonical_transformations(scanners); + let transforms = calc_transforms(scanners); - let mut max_distance = 0; - for (i, transform1) in transforms.iter().enumerate() { - for transform2 in transforms.iter().skip(i+1) { - let diff = transform1.0 - transform2.0; - let distance = diff.0 + diff.1 + diff.2; - max_distance = max_distance.max(distance); + let mut result = 0; + for (i, (_, offset1)) in transforms.iter().enumerate() { + for (_, offset2) in transforms.iter().skip(i+1) { + result = result.max(offset1.manhattan(offset2)) } } - max_distance + result } #[cfg(test)] @@ -340,38 +345,6 @@ mod tests { assert_eq!(result, 3621); } - #[test] - #[rustfmt::skip] - fn part1_overlap() { - let scanner1 = vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)]; - let scanner2 = vec![Point(686, 422, 578), Point(605, 423, 415), Point(515, 917, -361), Point(-336, 658, 858), Point(95, 138, 22), Point(-476, 619, 847), Point(-340, -569, -846), Point(567, -361, 727), Point(-460, 603, -452), Point(669, -402, 600), Point(729, 430, 532), Point(-500, -761, 534), Point(-322, 571, 750), Point(-466, -666, -811), Point(-429, -592, 574), Point(-355, 545, -477), Point(703, -491, -529), Point(-328, -685, 520), Point(413, 935, -424), Point(-391, 539, -444), Point(586, -435, 557), Point(-364, -763, -893), Point(807, -499, -711), Point(755, -354, -619), Point(553, 889, -390)]; - assert!(do_scanners_possibly_overlap(&scanner1, &scanner2)); - } - - #[test] - #[rustfmt::skip] - fn part1_overlapping_scanners() { - let scanners = vec![ - vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)], - vec![Point(686, 422, 578), Point(605, 423, 415), Point(515, 917, -361), Point(-336, 658, 858), Point(95, 138, 22), Point(-476, 619, 847), Point(-340, -569, -846), Point(567, -361, 727), Point(-460, 603, -452), Point(669, -402, 600), Point(729, 430, 532), Point(-500, -761, 534), Point(-322, 571, 750), Point(-466, -666, -811), Point(-429, -592, 574), Point(-355, 545, -477), Point(703, -491, -529), Point(-328, -685, 520), Point(413, 935, -424), Point(-391, 539, -444), Point(586, -435, 557), Point(-364, -763, -893), Point(807, -499, -711), Point(755, -354, -619), Point(553, 889, -390)], - vec![Point(649, 640, 665), Point(682, -795, 504), Point(-784, 533, -524), Point(-644, 584, -595), Point(-588, -843, 648), Point(-30, 6, 44), Point(-674, 560, 763), Point(500, 723, -460), Point(609, 671, -379), Point(-555, -800, 653), Point(-675, -892, -343), Point(697, -426, -610), Point(578, 704, 681), Point(493, 664, -388), Point(-671, -858, 530), Point(-667, 343, 800), Point(571, -461, -707), Point(-138, -166, 112), Point(-889, 563, -600), Point(646, -828, 498), Point(640, 759, 510), Point(-630, 509, 768), Point(-681, -892, -333), Point(673, -379, -804), Point(-742, -814, -386), Point(577, -820, 562)], - vec![Point(-589, 542, 597), Point(605, -692, 669), Point(-500, 565, -823), Point(-660, 373, 557), Point(-458, -679, -417), Point(-488, 449, 543), Point(-626, 468, -788), Point(338, -750, -386), Point(528, -832, -391), Point(562, -778, 733), Point(-938, -730, 414), Point(543, 643, -506), Point(-524, 371, -870), Point(407, 773, 750), Point(-104, 29, 83), Point(378, -903, -323), Point(-778, -728, 485), Point(426, 699, 580), Point(-438, -605, -362), Point(-469, -447, -387), Point(509, 732, 623), Point(647, 635, -688), Point(-868, -804, 481), Point(614, -800, 639), Point(595, 780, -596)], - vec![Point(727, 592, 562), Point(-293, -554, 779), Point(441, 611, -461), Point(-714, 465, -776), Point(-743, 427, -804), Point(-660, -479, -426), Point(832, -632, 460), Point(927, -485, -438), Point(408, 393, -506), Point(466, 436, -512), Point(110, 16, 151), Point(-258, -428, 682), Point(-393, 719, 612), Point(-211, -452, 876), Point(808, -476, -593), Point(-575, 615, 604), Point(-485, 667, 467), Point(-680, 325, -822), Point(-627, -443, -432), Point(872, -547, -609), Point(833, 512, 582), Point(807, 604, 487), Point(839, -516, 451), Point(891, -625, 532), Point(-652, -548, -490), Point(30, -46, -14)], - ]; - let overlapping = find_possibly_overlapping_scanners(&scanners); - assert_eq!(overlapping, [(0, 1), (1, 3), (1, 4), (2, 4)]); - } - - #[test] - #[rustfmt::skip] - fn part1_find_transformation() { - let scanner1 = vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)]; - let scanner2 = vec![Point(686, 422, 578), Point(605, 423, 415), Point(515, 917, -361), Point(-336, 658, 858), Point(95, 138, 22), Point(-476, 619, 847), Point(-340, -569, -846), Point(567, -361, 727), Point(-460, 603, -452), Point(669, -402, 600), Point(729, 430, 532), Point(-500, -761, 534), Point(-322, 571, 750), Point(-466, -666, -811), Point(-429, -592, 574), Point(-355, 545, -477), Point(703, -491, -529), Point(-328, -685, 520), Point(413, 935, -424), Point(-391, 539, -444), Point(586, -435, 557), Point(-364, -763, -893), Point(807, -499, -711), Point(755, -354, -619), Point(553, 889, -390)]; - let (translation, rotation) = find_transformation(&scanner1, &scanner2).unwrap(); - assert_eq!(translation, Point(68, -1246, -43)); - assert_eq!(rotation, ROTATIONS[6]); - } - #[test] #[rustfmt::skip] fn rotation_matrices() {