#![cfg_attr(not(test), no_std)]
#![cfg_attr(debug_assertions, forbid(unsafe_code))]
#![allow(clippy::zero_prefixed_literal)]
#![allow(clippy::unusual_byte_groupings)]
#![deny(clippy::missing_const_for_fn)]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
pub mod luts;
use core::ops::{Div, Mul, MulAssign};
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
#[repr(u8)]
pub enum CubeSurfacePoint {
PosOnePosTwoPosThree = 00,
PosOnePosTwoNegThree = 01,
PosOneNegTwoPosThree = 02,
PosOneNegTwoNegThree = 03,
NegOnePosTwoPosThree = 04,
NegOnePosTwoNegThree = 05,
NegOneNegTwoPosThree = 06,
NegOneNegTwoNegThree = 07,
PosThreePosTwoPosOne = 08,
PosThreePosTwoNegOne = 09,
PosThreeNegTwoPosOne = 10,
PosThreeNegTwoNegOne = 11,
NegThreePosTwoPosOne = 12,
NegThreePosTwoNegOne = 13,
NegThreeNegTwoPosOne = 14,
NegThreeNegTwoNegOne = 15,
PosOnePosThreePosTwo = 16,
PosOnePosThreeNegTwo = 17,
PosOneNegThreePosTwo = 18,
PosOneNegThreeNegTwo = 19,
NegOnePosThreePosTwo = 20,
NegOnePosThreeNegTwo = 21,
NegOneNegThreePosTwo = 22,
NegOneNegThreeNegTwo = 23,
PosThreePosOnePosTwo = 24,
PosThreePosOneNegTwo = 25,
PosThreeNegOnePosTwo = 26,
PosThreeNegOneNegTwo = 27,
NegThreePosOnePosTwo = 28,
NegThreePosOneNegTwo = 29,
NegThreeNegOnePosTwo = 30,
NegThreeNegOneNegTwo = 31,
PosTwoPosOnePosThree = 32,
PosTwoPosOneNegThree = 33,
PosTwoNegOnePosThree = 34,
PosTwoNegOneNegThree = 35,
NegTwoPosOnePosThree = 36,
NegTwoPosOneNegThree = 37,
NegTwoNegOnePosThree = 38,
NegTwoNegOneNegThree = 39,
PosTwoPosThreePosOne = 40,
PosTwoPosThreeNegOne = 41,
PosTwoNegThreePosOne = 42,
PosTwoNegThreeNegOne = 43,
NegTwoPosThreePosOne = 44,
NegTwoPosThreeNegOne = 45,
NegTwoNegThreePosOne = 46,
NegTwoNegThreeNegOne = 47,
}
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Rotation {
corresponding_point: CubeSurfacePoint,
}
impl Div for CubeSurfacePoint {
type Output = Rotation;
fn div(self, divisor: Self) -> Self::Output {
self.div(divisor)
}
}
impl Mul<Rotation> for CubeSurfacePoint {
type Output = Self;
fn mul(self, rot: Rotation) -> Self::Output {
self.mul(rot)
}
}
impl MulAssign<Rotation> for CubeSurfacePoint {
fn mul_assign(&mut self, rot: Rotation) {
*self = *self * rot;
}
}
impl core::convert::TryFrom<u8> for CubeSurfacePoint {
type Error = u8;
fn try_from(input: u8) -> Result<Self, Self::Error> {
Self::try_from_u8(input).ok_or(input)
}
}
impl Rotation {
pub const fn mul(self, x: CubeSurfacePoint) -> CubeSurfacePoint {
x.mul(self)
}
}
impl Mul<CubeSurfacePoint> for Rotation {
type Output = CubeSurfacePoint;
fn mul(self, cub_sur_pt: CubeSurfacePoint) -> Self::Output {
cub_sur_pt * self
}
}
#[cfg(debug_assertions)]
#[cold]
const fn unreachable_semichecked<T>() -> T {
debug_assert!(false, "Dead code was called!");
panic!()
}
#[cfg(not(debug_assertions))]
#[cold]
const fn unreachable_semichecked<T>() -> T {
debug_assert!(false, "Dead code was called!");
unsafe { core::hint::unreachable_unchecked() }
}
impl CubeSurfacePoint {
pub const REFERENCE_POINT: Self = Self::probs_from_u8(0);
pub const fn odd_ones(self) -> bool {
(self as u8).count_ones() & 1 != 0
}
pub const fn even_ones(self) -> bool {
!self.odd_ones()
}
const fn xor(self, bit_mask: u8) -> Self {
Self::probs_from_u8(self as u8 ^ bit_mask)
}
#[inline(always)]
pub const fn determine_group(
self: CubeSurfacePoint,
) -> Result<ReferenceGroupPoint, OppositeGroupPoint> {
use CubeSurfacePoint as Csp;
macro_rules! test {
($type:ty; $($names:ident),+ $(,)?) => {
match self {
$( _ if (self as u8 == <$type>::$names as u8) &&
(self as u8 == Csp::$names as u8) => {
<$type>::$names
}, )+
_ => unreachable_semichecked(),
}
};
}
if self.even_ones() {
Ok(test!(ReferenceGroupPoint;
PosOnePosTwoPosThree, PosOneNegTwoNegThree,
NegOnePosTwoNegThree, NegOneNegTwoPosThree,
PosThreePosTwoNegOne, PosThreeNegTwoPosOne,
NegThreePosTwoPosOne, NegThreeNegTwoNegOne,
PosOnePosThreeNegTwo, PosOneNegThreePosTwo,
NegOnePosThreePosTwo, NegOneNegThreeNegTwo,
PosThreePosOnePosTwo, PosThreeNegOneNegTwo,
NegThreePosOneNegTwo, NegThreeNegOnePosTwo,
PosTwoPosOneNegThree, PosTwoNegOnePosThree,
NegTwoPosOnePosThree, NegTwoNegOneNegThree,
PosTwoPosThreePosOne, PosTwoNegThreeNegOne,
NegTwoPosThreeNegOne, NegTwoNegThreePosOne,
))
} else {
Err(test!(OppositeGroupPoint;
PosOnePosTwoNegThree, PosOneNegTwoPosThree,
NegOnePosTwoPosThree, NegOneNegTwoNegThree,
PosThreePosTwoPosOne, PosThreeNegTwoNegOne,
NegThreePosTwoNegOne, NegThreeNegTwoPosOne,
PosOnePosThreePosTwo, PosOneNegThreeNegTwo,
NegOnePosThreeNegTwo, NegOneNegThreePosTwo,
PosThreePosOneNegTwo, PosThreeNegOnePosTwo,
NegThreePosOnePosTwo, NegThreeNegOneNegTwo,
PosTwoPosOnePosThree, PosTwoNegOneNegThree,
NegTwoPosOneNegThree, NegTwoNegOnePosThree,
PosTwoPosThreeNegOne, PosTwoNegThreePosOne,
NegTwoPosThreePosOne, NegTwoNegThreeNegOne,
))
}
}
#[inline(always)]
pub const fn determine_antigroup(
self: CubeSurfacePoint,
) -> Result<OppositeGroupPoint, ReferenceGroupPoint> {
match self.determine_group() {
Ok(a) => Err(a),
Err(a) => Ok(a),
}
}
const fn reciprocal(self) -> Self {
let mut result = Self::probs_from_u8(self as u8 & 7);
if self as u8 & 0b100_000 != 0 {
result = result.swap_x_y();
}
if self as u8 & 0b010_000 != 0 {
result = result.swap_y_z();
}
if self as u8 & 0b001_000 != 0 {
result = result.swap_z_x();
}
result
}
const fn swap_x_y(&self) -> Self {
let mut x = *self as u8;
if x & 0b010_000 != 0 {
x ^= 0b001_000;
} else {
x ^= 0b100_000;
}
if (x + 2) & 7 > 3 {
x ^= 0b000_110;
}
Self::probs_from_u8(x)
}
const fn swap_y_z(&self) -> Self {
let mut x = *self as u8;
if x >= 0b100_000 {
x ^= 0b001_000;
} else {
x ^= 0b010_000;
}
if (x + 1) & 3 > 1 {
x ^= 0b000_011;
}
Self::probs_from_u8(x)
}
const fn swap_z_x(&self) -> Self {
let mut x = *self as u8;
if x < 0b010_000 {
x ^= 0b001_000;
} else {
x ^= 0b111_000;
}
let x_ = (x & 5).wrapping_sub(1);
if x_ & 7 < 4 {
x ^= 0b000_101;
}
Self::probs_from_u8(x)
}
pub const fn opposite(self) -> Self {
if (self as u8) & 0b011_000 == 0 {
self.xor(0b001)
} else if (self as u8) & 0b101_000 == 0b001_000 {
self.xor(0b100)
} else {
self.xor(0b010)
}
}
pub const fn beside(self) -> Self {
if (self as u8) >= 24 && (self as u8) < 40 {
self.xor(0b010)
} else if (self as u8) & 0b001_000 != 0 {
self.xor(0b001)
} else {
self.xor(0b100)
}
}
pub const fn opposite_then_beside(self) -> Self {
if (self as u8) >= 32 {
self.xor(0b011)
} else if (self as u8) < 16 {
self.xor(0b101)
} else {
self.xor(0b110)
}
}
pub const fn beside_then_opposite(self) -> Self {
self.opposite_then_beside()
}
pub const fn flip_sign_of_1(self) -> Self {
self.beside()
}
pub const fn flip_sign_of_2(self) -> Self {
if (self as u8) >= 32 {
self.xor(0b100)
} else if (self as u8) < 16 {
self.xor(0b010)
} else {
self.xor(0b001)
}
}
pub const fn flip_sign_of_3(self) -> Self {
self.opposite()
}
pub const fn flip_1_and_3(self) -> Self {
self.opposite_then_beside()
}
pub const fn flip_2_and_3(self) -> Self {
if (self as u8) >= 24 && (self as u8) < 40 {
self.xor(0b101)
} else if (self as u8) & 0b001_000 != 0 {
self.xor(0b110)
} else {
self.xor(0b011)
}
}
pub const fn flip_1_and_2(self) -> Self {
if (self as u8) & 0b011_000 == 0 {
self.xor(0b110)
} else if (self as u8) & 0b101_000 == 0b001_000 {
self.xor(0b011)
} else {
self.xor(0b101)
}
}
const fn const_unwrap_semichecked(x: Option<Self>) -> Self {
debug_assert!(
x.is_some(),
"This value does not correspond to any cube surface point!"
);
if let Some(y) = x {
y
} else {
unreachable_semichecked()
}
}
const fn probs_from_u8(x: u8) -> Self {
Self::const_unwrap_semichecked(Self::try_from_u8(x))
}
#[cfg(not(debug_assertions))]
const fn _try_from_u8_readable(x: u8) -> Option<Self> {
if x < 48 {
unsafe { core::mem::transmute(x) }
} else {
None
}
}
#[rustfmt::skip]
#[inline(always)]
const fn try_from_u8_unreadable(x: u8) -> Option<Self> {
macro_rules! test {
($($names:ident $(= $_whatever:literal)?),+ $(,)?) => {
match x {
$(
_ if x == CubeSurfacePoint::$names as u8 => {
Some(CubeSurfacePoint::$names)
},
)*
_ => None,
}
};
}
test!(
PosOnePosTwoPosThree, PosOnePosTwoNegThree, PosOneNegTwoPosThree,
PosOneNegTwoNegThree, NegOnePosTwoPosThree, NegOnePosTwoNegThree,
NegOneNegTwoPosThree, NegOneNegTwoNegThree, PosThreePosTwoPosOne,
PosThreePosTwoNegOne, PosThreeNegTwoPosOne, PosThreeNegTwoNegOne,
NegThreePosTwoPosOne, NegThreePosTwoNegOne, NegThreeNegTwoPosOne,
NegThreeNegTwoNegOne, PosOnePosThreePosTwo, PosOnePosThreeNegTwo,
PosOneNegThreePosTwo, PosOneNegThreeNegTwo, NegOnePosThreePosTwo,
NegOnePosThreeNegTwo, NegOneNegThreePosTwo, NegOneNegThreeNegTwo,
PosThreePosOnePosTwo, PosThreePosOneNegTwo, PosThreeNegOnePosTwo,
PosThreeNegOneNegTwo, NegThreePosOnePosTwo, NegThreePosOneNegTwo,
NegThreeNegOnePosTwo, NegThreeNegOneNegTwo, PosTwoPosOnePosThree,
PosTwoPosOneNegThree, PosTwoNegOnePosThree, PosTwoNegOneNegThree,
NegTwoPosOnePosThree, NegTwoPosOneNegThree, NegTwoNegOnePosThree,
NegTwoNegOneNegThree, PosTwoPosThreePosOne, PosTwoPosThreeNegOne,
PosTwoNegThreePosOne, PosTwoNegThreeNegOne, NegTwoPosThreePosOne,
NegTwoPosThreeNegOne, NegTwoNegThreePosOne, NegTwoNegThreeNegOne,
)
}
#[inline(always)]
pub const fn try_from_u8(x: u8) -> Option<Self> {
Self::try_from_u8_unreadable(x)
}
pub const fn mul(self, rot: Rotation) -> Self {
let rot = rot.corresponding_point as u8;
let mut result = self;
if rot & 0b001_000 != 0 {
result = result.swap_z_x();
}
if rot & 0b010_000 != 0 {
result = result.swap_y_z();
}
if rot & 0b100_000 != 0 {
result = result.swap_x_y();
}
let rot = rot & 7;
Self::probs_from_u8(result as u8 ^ rot)
}
pub const fn div(self, divisor: Self) -> Rotation {
let reciprocal = divisor.reciprocal();
let corresponding_point = Rotation {
corresponding_point: self,
}
.mul(reciprocal);
Rotation {
corresponding_point,
}
}
pub const fn one_right_angle<const CLOCKWISE: bool>(self) -> Self {
let mut result = self;
let x = self as u8;
let mask = if x & 0b011_000 == 0 {
result = result.swap_x_y();
if (x & 0b000_001 == 0) == CLOCKWISE {
0b000_010
} else {
0b000_100
}
} else if x & 0b101_000 == 0b001_000 {
result = result.swap_y_z();
if (x & 0b000_100 == 0) == CLOCKWISE {
0b000_001
} else {
0b000_010
}
} else {
result = result.swap_z_x();
if (x & 0b000_010 == 0) == CLOCKWISE {
0b000_100
} else {
0b000_001
}
};
result.xor(mask)
}
pub const fn one_right_angle_cw(self) -> Self {
self.one_right_angle::<true>()
}
pub const fn one_right_angle_acw(self) -> Self {
self.one_right_angle::<false>()
}
pub const fn n_right_angles<const CLOCKWISE: bool>(self, angle: u8) -> Self {
let angle = angle & 0b11;
let one_or_three = if CLOCKWISE { 1 } else { 3 };
let three_or_one = if CLOCKWISE { 3 } else { 1 };
match angle {
0 => self,
_ if angle == one_or_three => self.one_right_angle_cw(),
_ if angle == three_or_one => self.one_right_angle_acw(),
2 => self.flip_1_and_2(),
_ => {
debug_assert!(false, "A 2-bit number cannot be larger than 3!");
unreachable_semichecked()
}
}
}
pub const fn n_right_angles_cw(self, angle: u8) -> CubeSurfacePoint {
self.n_right_angles::<true>(angle)
}
pub const fn n_right_angles_acw(self, angle: u8) -> CubeSurfacePoint {
self.n_right_angles::<false>(angle)
}
pub const fn direction(self) -> Direction {
use Direction::*;
let x = self as u8;
if x & 0b011_000 == 0 {
[Down, Up][(x & 0b000_001 == 0) as usize]
} else if x & 0b101_000 == 0b001_000 {
[Left, Right][(x & 0b000_100 == 0) as usize]
} else {
[Back, Front][(x & 0b000_010 == 0) as usize]
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
pub enum Direction {
Left,
Right,
Back,
Front,
Down,
Up,
}
pub trait OctahedrallySymmetricPoint:
Into<CubeSurfacePoint>
+ TryFrom<CubeSurfacePoint>
+ Div<Self>
+ Mul<ProperRotation, Output = Self>
+ Mul<ImproperRotation, Output = Self::OtherGroup>
+ MulAssign<ProperRotation>
{
type OtherGroup;
fn beside(self) -> Self::OtherGroup;
fn opposite(self) -> Self::OtherGroup;
fn opposite_then_beside(self) -> Self;
fn one_right_angle_cw(self) -> Self;
fn one_right_angle_acw(self) -> Self;
fn n_right_angles_cw(self, angle: u8) -> Self;
fn n_right_angles_acw(self, angle: u8) -> Self;
fn flip_sign_of_2(self) -> Self::OtherGroup;
fn flip_2_and_3(self) -> Self;
fn direction(self) -> Direction {
Into::<CubeSurfacePoint>::into(self).direction()
}
fn odd_ones(self) -> bool {
Into::<CubeSurfacePoint>::into(self).odd_ones()
}
fn even_ones(self) -> bool {
!self.odd_ones()
}
fn n_right_angles<const CLOCKWISE: bool>(self, angle: u8) -> Self {
if CLOCKWISE {
self.n_right_angles_cw(angle)
} else {
self.n_right_angles_acw(angle)
}
}
fn beside_then_opposite(self) -> Self {
self.opposite_then_beside()
}
fn flip_1_and_3(self) -> Self {
self.opposite_then_beside()
}
fn flip_sign_of_1(self) -> Self::OtherGroup {
self.beside()
}
fn flip_sign_of_3(self) -> Self::OtherGroup {
self.opposite()
}
fn flip_1_and_2(self) -> Self {
self.n_right_angles_cw(2)
}
}
impl Mul<ProperRotation> for CubeSurfacePoint {
type Output = CubeSurfacePoint;
fn mul(self, rotation: ProperRotation) -> Self {
self * Into::<Rotation>::into(rotation)
}
}
impl MulAssign<ProperRotation> for CubeSurfacePoint {
fn mul_assign(&mut self, x: ProperRotation) {
*self = *self * x;
}
}
impl Mul<ImproperRotation> for CubeSurfacePoint {
type Output = CubeSurfacePoint;
fn mul(self, rotation: ImproperRotation) -> Self {
self * Into::<Rotation>::into(rotation)
}
}
impl MulAssign<ImproperRotation> for CubeSurfacePoint {
fn mul_assign(&mut self, x: ImproperRotation) {
*self = *self * x;
}
}
macro_rules! implement_trivial {
($fn: ident, $out: ty $(, $angle: ident)?) => {
fn $fn(self $(, $angle:u8)?) -> $out {
self.$fn($($angle)?)
}
};
(everything; $us: ty, $others: ty) => {
impl OctahedrallySymmetricPoint for $us {
type OtherGroup = $others;
implement_trivial!(beside, Self::OtherGroup);
implement_trivial!(opposite, Self::OtherGroup);
implement_trivial!(opposite_then_beside, Self);
implement_trivial!(one_right_angle_cw, Self);
implement_trivial!(one_right_angle_acw, Self);
implement_trivial!(flip_sign_of_2, Self::OtherGroup);
implement_trivial!(flip_2_and_3, Self);
implement_trivial!(direction, Direction);
implement_trivial!(n_right_angles_cw, Self, angle);
implement_trivial!(n_right_angles_acw, Self, angle);
}
}
}
implement_trivial!(everything; CubeSurfacePoint, CubeSurfacePoint);
implement_trivial!(
everything;
luts::CubeSurfacePoint<true>,
luts::CubeSurfacePoint<true>
);
implement_trivial!(
everything;
luts::CubeSurfacePoint<false>,
luts::CubeSurfacePoint<false>
);
implement_trivial!(
everything;
luts::ReferenceGroupPoint,
luts::OppositeGroupPoint
);
implement_trivial!(
everything;
luts::OppositeGroupPoint,
luts::ReferenceGroupPoint
);
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
#[repr(u8)]
pub enum ReferenceGroupPoint {
PosOnePosTwoPosThree = 00,
PosOneNegTwoNegThree = 03,
NegOnePosTwoNegThree = 05,
NegOneNegTwoPosThree = 06,
PosThreePosTwoNegOne = 09,
PosThreeNegTwoPosOne = 10,
NegThreePosTwoPosOne = 12,
NegThreeNegTwoNegOne = 15,
PosOnePosThreeNegTwo = 17,
PosOneNegThreePosTwo = 18,
NegOnePosThreePosTwo = 20,
NegOneNegThreeNegTwo = 23,
PosThreePosOnePosTwo = 24,
PosThreeNegOneNegTwo = 27,
NegThreePosOneNegTwo = 29,
NegThreeNegOnePosTwo = 30,
PosTwoPosOneNegThree = 33,
PosTwoNegOnePosThree = 34,
NegTwoPosOnePosThree = 36,
NegTwoNegOneNegThree = 39,
PosTwoPosThreePosOne = 40,
PosTwoNegThreeNegOne = 43,
NegTwoPosThreeNegOne = 45,
NegTwoNegThreePosOne = 46,
}
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
#[repr(u8)]
pub enum OppositeGroupPoint {
PosOnePosTwoNegThree = 01,
PosOneNegTwoPosThree = 02,
NegOnePosTwoPosThree = 04,
NegOneNegTwoNegThree = 07,
PosThreePosTwoPosOne = 08,
PosThreeNegTwoNegOne = 11,
NegThreePosTwoNegOne = 13,
NegThreeNegTwoPosOne = 14,
PosOnePosThreePosTwo = 16,
PosOneNegThreeNegTwo = 19,
NegOnePosThreeNegTwo = 21,
NegOneNegThreePosTwo = 22,
PosThreePosOneNegTwo = 25,
PosThreeNegOnePosTwo = 26,
NegThreePosOnePosTwo = 28,
NegThreeNegOneNegTwo = 31,
PosTwoPosOnePosThree = 32,
PosTwoNegOneNegThree = 35,
NegTwoPosOneNegThree = 37,
NegTwoNegOnePosThree = 38,
PosTwoPosThreeNegOne = 41,
PosTwoNegThreePosOne = 42,
NegTwoPosThreePosOne = 44,
NegTwoNegThreeNegOne = 47,
}
impl ReferenceGroupPoint {
const fn to_u8(self) -> u8 {
self as u8
}
}
impl OppositeGroupPoint {
const fn to_u8(self) -> u8 {
self as u8
}
}
macro_rules! implement {
($fnam: ident, $out: ty, $check: ident $(, $angle: ident)? $(; $const:tt)?) => {
#[doc = concat!(
"Please refer to the [function of the same name
](CubeSurfacePoint::",
stringify!($fnam),
") in [`CubeSurfacePoint`]."
)]
$(pub $const)? fn $fnam(self $(, $angle:u8)?) -> $out {
let Ok(result) = self.downcast().$fnam($($angle)?).$check() else {
unreachable_semichecked()
};
result
}
};
(mul; $rot: ty, $us: ty, $out: ty, $check: ident, $fnam:ident $(; $const:tt)?) => {
#[doc = concat!(
"Multiplies one [`",
stringify!($us),
"`] with one [`",
stringify!($rot),
"`], producing one [`",
stringify!($out),
"`] as a result. "
)]
$(pub $const)? fn $fnam(self, other: $rot) -> $out {
let a: CubeSurfacePoint = self.downcast();
let b: Rotation = Rotation {
corresponding_point: other.corresponding_point.downcast()
};
let Ok(result) = a.mul(b).$check() else {
unreachable_semichecked()
};
result
}
};
(div; $us: ty, $other: ty, $out: ty, $check: ident, $fnam: ident $(; $const:tt)?) =>
{
#[doc = concat!(
"Divides one [`",
stringify!($us),
"`] by one [`",
stringify!($other),
"`], producing one [`",
stringify!($out),
"`] as a result. "
)]
$(pub $const)? fn $fnam(self, other: $other) -> $out {
let a: CubeSurfacePoint = self.downcast();
let b: CubeSurfacePoint = other.downcast();
let Ok(result) = a.div(b).$check() else {
unreachable_semichecked()
};
result
}
};
(everything; $us: ty, $others: ty, $check: ident, $anticheck: ident) => {
impl $us {
#[doc = concat!("Down-casts one [`", stringify!($us), "`] to a [`CubeSurfacePoint`].")]
pub const fn downcast(self) -> CubeSurfacePoint {
CubeSurfacePoint::probs_from_u8(self.to_u8())
}
implement!(beside, $others, $anticheck; const);
implement!(opposite, $others, $anticheck; const);
implement!(opposite_then_beside, Self, $check; const);
implement!(one_right_angle_cw, Self, $check; const);
implement!(one_right_angle_acw, Self, $check; const);
implement!(flip_sign_of_2, $others, $anticheck; const);
implement!(flip_2_and_3, Self, $check; const);
implement!(n_right_angles_cw, Self, $check, angle; const);
implement!(n_right_angles_acw, Self, $check, angle; const);
implement!(mul; ProperRotation, $us, $us, $check, mul_prop; const);
implement!(mul; ImproperRotation, $us, $others, $anticheck, mul_improp; const);
implement!(div; $us, $us, ProperRotation, determine_group, div_prop; const);
implement!(div; $us, $others, ImproperRotation, determine_antigroup, div_improp; const);
pub const fn direction(self) -> Direction {
self.downcast().direction()
}
}
impl Mul<ProperRotation> for $us {
type Output = Self;
implement!(mul; ProperRotation, $us, $us, $check, mul);
}
impl MulAssign<ProperRotation> for $us {
fn mul_assign (&mut self, x: ProperRotation) {
*self = *self * x;
}
}
impl Mul<$us> for ProperRotation {
type Output = $us;
fn mul(self, x: $us) -> Self::Output {
x * self
}
}
impl Mul<ImproperRotation> for $us {
type Output = $others;
implement!(mul; ImproperRotation, $us, $others, $anticheck, mul);
}
impl Mul<$us> for ImproperRotation {
type Output = $others;
fn mul(self, x: $us) -> Self::Output {
x * self
}
}
impl Div for $us {
type Output = ProperRotation;
implement!(div; Self, Self, ProperRotation, determine_group, div);
}
impl Div<$others> for $us {
type Output = ImproperRotation;
implement!(div; Self, $others, ImproperRotation, determine_antigroup, div);
}
impl From<$us> for CubeSurfacePoint {
fn from(x: $us) -> Self {
x.downcast()
}
}
impl TryFrom<CubeSurfacePoint> for $us {
#[doc = concat!(
"If a certain [`CubeSurfacePoint`] does not belong to the [`",
stringify!($us),
"`]s, it must by necessity belong to the [`",
stringify!($others),
"`]s."
)]
type Error = $others;
fn try_from(x: CubeSurfacePoint) -> Result<Self, Self::Error> {
x.$check()
}
}
impl OctahedrallySymmetricPoint for $us {
type OtherGroup = $others;
implement!(beside, Self::OtherGroup, $anticheck);
implement!(opposite, Self::OtherGroup, $anticheck);
implement!(flip_sign_of_2, Self::OtherGroup, $anticheck);
implement!(opposite_then_beside, Self, $check);
implement!(flip_2_and_3, Self, $check);
implement!(one_right_angle_cw, Self, $check);
implement!(one_right_angle_acw, Self, $check);
implement!(n_right_angles_cw, Self, $check, angle);
implement!(n_right_angles_acw, Self, $check, angle);
}
};
}
implement!(everything; ReferenceGroupPoint, OppositeGroupPoint, determine_group, determine_antigroup);
implement!(everything; OppositeGroupPoint, ReferenceGroupPoint, determine_antigroup, determine_group);
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ProperRotation {
corresponding_point: ReferenceGroupPoint,
}
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ImproperRotation {
corresponding_point: OppositeGroupPoint,
}
impl Rotation {
const fn determine_group(self) -> Result<ProperRotation, ImproperRotation> {
let argh = self.corresponding_point.determine_group();
match argh {
Ok(corresponding_point) => Ok(ProperRotation {
corresponding_point,
}),
Err(corresponding_point) => Err(ImproperRotation {
corresponding_point,
}),
}
}
const fn determine_antigroup(self) -> Result<ImproperRotation, ProperRotation> {
match self.determine_group() {
Ok(a) => Err(a),
Err(a) => Ok(a),
}
}
}
macro_rules! mul_div_rots {
($us: ty, $other: ty, $output: path) => {
impl Mul<$other> for $us {
type Output = $output;
fn mul(self, other: $other) -> Self::Output {
let corresponding_point = self * other.corresponding_point;
$output {
corresponding_point,
}
}
}
impl Div<$other> for $us {
type Output = $output;
fn div(self, other: $other) -> Self::Output {
self.corresponding_point / other.corresponding_point
}
}
};
}
mul_div_rots!(Rotation, Rotation, Rotation);
mul_div_rots!(ProperRotation, ProperRotation, ProperRotation);
mul_div_rots!(ImproperRotation, ImproperRotation, ProperRotation);
mul_div_rots!(ProperRotation, ImproperRotation, ImproperRotation);
mul_div_rots!(ImproperRotation, ProperRotation, ImproperRotation);
impl From<ProperRotation> for Rotation {
fn from(x: ProperRotation) -> Self {
let c_p = x.corresponding_point as u8;
Self {
corresponding_point: CubeSurfacePoint::probs_from_u8(c_p),
}
}
}
impl From<ImproperRotation> for Rotation {
fn from(x: ImproperRotation) -> Self {
let c_p = x.corresponding_point as u8;
Self {
corresponding_point: CubeSurfacePoint::probs_from_u8(c_p),
}
}
}
impl TryFrom<Rotation> for ImproperRotation {
type Error = ProperRotation;
fn try_from(x: Rotation) -> Result<Self, Self::Error> {
x.determine_antigroup()
}
}
impl TryFrom<Rotation> for ProperRotation {
type Error = ImproperRotation;
fn try_from(x: Rotation) -> Result<Self, Self::Error> {
x.determine_group()
}
}
impl From<Rotation> for CubeSurfacePoint {
fn from(x: Rotation) -> Self {
x.corresponding_point
}
}
impl From<ProperRotation> for ReferenceGroupPoint {
fn from(x: ProperRotation) -> Self {
x.corresponding_point
}
}
impl From<ImproperRotation> for OppositeGroupPoint {
fn from(x: ImproperRotation) -> Self {
x.corresponding_point
}
}
impl From<CubeSurfacePoint> for Rotation {
fn from(corresponding_point: CubeSurfacePoint) -> Self {
Self {
corresponding_point,
}
}
}
impl From<ReferenceGroupPoint> for ProperRotation {
fn from(corresponding_point: ReferenceGroupPoint) -> Self {
Self {
corresponding_point,
}
}
}
impl From<OppositeGroupPoint> for ImproperRotation {
fn from(corresponding_point: OppositeGroupPoint) -> Self {
Self {
corresponding_point,
}
}
}
#[cfg(test)]
mod tests;