From 8c51ab5523bd265b8e7b638ea325942102bd0c4f Mon Sep 17 00:00:00 2001 From: Lucas Garron Date: Thu, 24 Aug 2023 23:54:14 -0700 Subject: [PATCH] Implement `Debug` and `PartialEq` for `PackedKTransformation`. --- src/rs/cli/commands/canonical_algs.rs | 4 +- src/rs/packed/orientation_packer.rs | 1 + src/rs/packed/packed_kpuzzle.rs | 17 ++- src/rs/packed/packed_kstate.rs | 34 +++++- src/rs/packed/packed_ktransformation.rs | 137 +++++++++++++++++++++++- 5 files changed, 181 insertions(+), 12 deletions(-) diff --git a/src/rs/cli/commands/canonical_algs.rs b/src/rs/cli/commands/canonical_algs.rs index 45345ae7..107f1dc8 100644 --- a/src/rs/cli/commands/canonical_algs.rs +++ b/src/rs/cli/commands/canonical_algs.rs @@ -13,7 +13,7 @@ fn read_to_json Deserialize<'a>>(input_file: &Path) -> Result bool { - t1.unpack().apply_transformation(&t2.unpack()) == t2.unpack().apply_transformation(&t1.unpack()) + t1.apply_transformation(t2) == t2.apply_transformation(t1) } pub fn canonical_algs(args: &CanonicalAlgsArgs) -> Result<(), String> { @@ -22,7 +22,7 @@ pub fn canonical_algs(args: &CanonicalAlgsArgs) -> Result<(), String> { let kpuzzle = KPuzzle::try_new(def).unwrap(); let packed_kpuzzle = PackedKPuzzle::try_from(kpuzzle).unwrap(); - println!("{:?}", packed_kpuzzle.start_state().byte_slice()); + println!("{:?}", packed_kpuzzle.start_state()); println!("{:?}", packed_kpuzzle.start_state().unpack().state_data); let t1 = packed_kpuzzle diff --git a/src/rs/packed/orientation_packer.rs b/src/rs/packed/orientation_packer.rs index 7d38e6e2..232a9fa3 100644 --- a/src/rs/packed/orientation_packer.rs +++ b/src/rs/packed/orientation_packer.rs @@ -142,6 +142,7 @@ mod tests { use crate::PackedKPuzzle; + // TODO: Return a `Result`. #[test] fn test_orientation_mod() { let def = KPuzzleDefinition { diff --git a/src/rs/packed/packed_kpuzzle.rs b/src/rs/packed/packed_kpuzzle.rs index ccff66e8..7847af55 100644 --- a/src/rs/packed/packed_kpuzzle.rs +++ b/src/rs/packed/packed_kpuzzle.rs @@ -2,7 +2,9 @@ use std::{alloc::Layout, sync::Arc}; use cubing::{ alg::Move, - kpuzzle::{InvalidAlgError, InvalidDefinitionError, KPuzzle, KPuzzleOrbitName}, + kpuzzle::{ + InvalidAlgError, InvalidDefinitionError, KPuzzle, KPuzzleOrbitName, KTransformation, + }, }; use super::{byte_conversions::usize_to_u8, PackedKState, PackedKTransformation}; @@ -207,13 +209,24 @@ impl PackedKPuzzle { new_state } + pub fn identity_transformation(&self) -> Result { + let unpacked_ktransformation = self.data.kpuzzle.identity_transformation(); + self.pack_transformation(&unpacked_ktransformation) + } + // TODO: implement this as a `TryFrom`? pub fn transformation_from_move( - &self, // TODO: Any issues with not using `&self`? + &self, key_move: &Move, ) -> Result { let unpacked_ktransformation = self.data.kpuzzle.transformation_from_move(key_move)?; + self.pack_transformation(&unpacked_ktransformation) + } + fn pack_transformation( + &self, + unpacked_ktransformation: &KTransformation, + ) -> Result { let new_transformation = PackedKTransformation::new(self.clone()); for orbit_info in &self.data.orbit_iteration_info { let unpacked_orbit_data = unpacked_ktransformation diff --git a/src/rs/packed/packed_kstate.rs b/src/rs/packed/packed_kstate.rs index e5b00472..84eee531 100644 --- a/src/rs/packed/packed_kstate.rs +++ b/src/rs/packed/packed_kstate.rs @@ -1,4 +1,7 @@ -use std::alloc::{alloc, dealloc}; +use std::{ + alloc::{alloc, dealloc}, + fmt::Debug, +}; use super::{ byte_conversions::{u8_to_usize, PackedOrientationWithMod}, @@ -6,6 +9,7 @@ use super::{ PackedKPuzzle, PackedKTransformation, }; +use cubing::kpuzzle::KPuzzle; #[cfg(not(feature = "no_orientation_mod"))] use cubing::kpuzzle::{KState, KStateData}; #[cfg(not(feature = "no_orientation_mod"))] @@ -98,9 +102,9 @@ impl PackedKState { for i in 0..orbit_info.num_pieces { let transformation_idx = transformation.get_piece_or_permutation(orbit_info, i); - let new_piece_permutation = + let new_piece_value = self.get_piece_or_permutation(orbit_info, u8_to_usize(transformation_idx)); - into_state.set_piece_or_permutation(orbit_info, i, new_piece_permutation); + into_state.set_piece_or_permutation(orbit_info, i, new_piece_value); let previous_packed_orientation = self.get_packed_orientation(orbit_info, u8_to_usize(transformation_idx)); @@ -205,3 +209,27 @@ impl PackedKState { } } } + +struct KPuzzleDebug { + kpuzzle: KPuzzle, +} + +impl Debug for KPuzzleDebug { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ … name: \"{}\" … }}", &self.kpuzzle.definition().name) + } +} + +impl Debug for PackedKState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PackedKState") + .field( + "packed_kpuzzle", + &KPuzzleDebug { + kpuzzle: self.packed_kpuzzle.data.kpuzzle.clone(), + }, + ) + .field("bytes", &self.byte_slice()) + .finish() + } +} diff --git a/src/rs/packed/packed_ktransformation.rs b/src/rs/packed/packed_ktransformation.rs index 47234b3d..2c195f80 100644 --- a/src/rs/packed/packed_ktransformation.rs +++ b/src/rs/packed/packed_ktransformation.rs @@ -1,11 +1,14 @@ -use std::alloc::{alloc, dealloc}; +use std::{ + alloc::{alloc, dealloc}, + fmt::Debug, +}; -use super::{packed_kpuzzle::PackedKPuzzleOrbitInfo, PackedKPuzzle}; +use super::{byte_conversions::u8_to_usize, packed_kpuzzle::PackedKPuzzleOrbitInfo, PackedKPuzzle}; pub struct PackedKTransformation { pub packed_kpuzzle: PackedKPuzzle, pub bytes: *mut u8, } -use cubing::kpuzzle::{KTransformation, KTransformationOrbitData}; +use cubing::kpuzzle::{KPuzzle, KTransformation, KTransformationOrbitData}; impl Drop for PackedKTransformation { fn drop(&mut self) { @@ -58,14 +61,62 @@ impl PackedKTransformation { } } + // Adapted from https://github.com/cubing/cubing.rs/blob/b737c6a36528e9984b45b29f9449a9a330c272fb/src/kpuzzle/transformation.rs#L32-L61 + // TODO: dedup the implementation (but avoid runtime overhead for the shared abstraction). + pub fn apply_transformation( + &self, + transformation: &PackedKTransformation, + ) -> PackedKTransformation { + let mut new_state = PackedKTransformation::new(self.packed_kpuzzle.clone()); + self.apply_transformation_into(transformation, &mut new_state); + new_state + } + + // Adapted from https://github.com/cubing/cubing.rs/blob/b737c6a36528e9984b45b29f9449a9a330c272fb/src/kpuzzle/transformation.rs#L32-L61 + // TODO: dedup the implementation (but avoid runtime overhead for the shared abstraction). + // TODO: assign to self from another value, not into another + pub fn apply_transformation_into( + &self, + transformation: &PackedKTransformation, + into_state: &mut PackedKTransformation, + ) { + for orbit_info in &self.packed_kpuzzle.data.orbit_iteration_info { + // TODO: optimization when either value is the identity. + for i in 0..orbit_info.num_pieces { + let transformation_idx = transformation.get_piece_or_permutation(orbit_info, i); + + let new_piece_permutation = + self.get_piece_or_permutation(orbit_info, u8_to_usize(transformation_idx)); + into_state.set_piece_or_permutation(orbit_info, i, new_piece_permutation); + + let previous_packed_orientation = + self.get_orientation(orbit_info, u8_to_usize(transformation_idx)); + + // TODO: lookup table? + let new_orientation = (previous_packed_orientation + + transformation.get_orientation(orbit_info, i)) + % orbit_info.num_orientations; + into_state.set_orientation(orbit_info, i, new_orientation); + } + } + } + + pub fn byte_slice(&self) -> &[u8] { + // yiss ☺️ + // https://stackoverflow.com/a/27150865 + unsafe { std::slice::from_raw_parts(self.bytes, self.packed_kpuzzle.data.num_bytes) } + } + + pub fn hash(&self) -> u64 { + cityhash::city_hash_64(self.byte_slice()) + } + #[cfg(not(feature = "no_orientation_mod"))] pub fn unpack(&self) -> KTransformation { use std::sync::Arc; use cubing::kpuzzle::KTransformationData; - use crate::packed::byte_conversions::u8_to_usize; - let mut state_data = KTransformationData::new(); for orbit_info in &self.packed_kpuzzle.data.orbit_iteration_info { let mut permutation = Vec::::new(); @@ -86,3 +137,79 @@ impl PackedKTransformation { } } } + +struct KPuzzleDebug { + kpuzzle: KPuzzle, +} + +impl Debug for KPuzzleDebug { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{{ … name: \"{}\" … }}", &self.kpuzzle.definition().name) + } +} + +impl Debug for PackedKTransformation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PackedKTransformation") + .field( + "packed_kpuzzle", + &KPuzzleDebug { + kpuzzle: self.packed_kpuzzle.data.kpuzzle.clone(), + }, + ) + .field("bytes", &self.byte_slice()) + .finish() + } +} + +impl PartialEq for PackedKTransformation { + fn eq(&self, other: &Self) -> bool { + self.byte_slice() == other.byte_slice() + } +} + +#[cfg(test)] +mod tests { + use cubing::alg::AlgParseError; + use cubing::parse_move; + use cubing::puzzles::cube3x3x3_kpuzzle; + + use crate::packed::packed_kpuzzle::ConversionError; + use crate::{PackedKPuzzle, PackedKTransformation}; + + #[test] + fn test_orientation_mod() -> Result<(), String> { + let kpuzzle = cube3x3x3_kpuzzle(); + let packed_kpuzzle = PackedKPuzzle::try_from(kpuzzle).map_err(|e| e.description)?; + + let from_move = |move_str: &str| -> Result { + let r#move = parse_move!(move_str).map_err(|e: AlgParseError| e.description)?; + packed_kpuzzle + .transformation_from_move(&r#move) + .map_err(|e: ConversionError| e.to_string()) + }; + + let id = packed_kpuzzle + .identity_transformation() + .map_err(|e| e.to_string())?; + let t1 = from_move("R")?; + let t2 = from_move("R2")?; + let t2prime = from_move("R2'")?; + let t4 = from_move("R4")?; + let t5 = from_move("R5")?; + + assert_eq!(id, t4); + assert_eq!(t1, t5); + assert_eq!(t2, t2prime); + + assert_ne!(id, t1); + assert_ne!(id, t2); + assert_ne!(t1, t2); + + assert_eq!(id.apply_transformation(&t1), t1); + assert_eq!(t1.apply_transformation(&t1), t2); + assert_eq!(t2.apply_transformation(&t1).apply_transformation(&t2), t1); + + Ok(()) + } +}