1
0

refactor day 19 part 2

This commit is contained in:
Rokas Puzonas 2022-09-26 22:42:31 +00:00
parent 13d65c6a86
commit 00d1661672

View File

@ -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<Point> 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<Vec<Point>> {
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<Point> {
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<Point>]) -> 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<Rotation> {
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::<HashSet<_>>();
let normalized2 = points2.iter().map(|p| rot.mul(*p - base2)).collect::<HashSet<_>>();
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::<HashSet<_>>();
// let b = points2.iter().map(|p| rot.mul(*p) + offset).collect::<HashSet<_>>();
// 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<Point>]) -> Vec<(Point, Rotation)> {
let mut canonical = vec![];
fn calc_transforms(scanners: &[Vec<Point>]) -> 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::<HashSet<_>>())
.collect::<Vec<_>>();
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<Point>]) -> 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<Point>]) -> 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() {