cube_rotations/
luts.rs

1//! A module containing wrapper data-types whose calculations are performed
2//! using **L**ook-**U**p **T**ables.
3//!
4//! # Usage
5//!
6//! ```
7//! use cube_rotations::luts::CspLarge;
8//! use cube_rotations::CubeSurfacePoint::*;
9//! let x = CspLarge(NegThreePosOnePosTwo);
10//! let y = CspLarge(PosTwoPosThreeNegOne);
11//! let z = CspLarge(PosThreePosOneNegTwo);
12//! let x_actual = CspLarge(PosThreePosTwoPosOne);
13//! let rotation = x_actual / x;
14//! let x_actual = x * rotation;
15//! let y_actual = y * rotation;
16//! let z_actual = z * rotation;
17//! ```
18//!
19//! # Contents
20//!
21//! * [`CubeSurfacePoint::<false>`]: As per the basic
22//!     [`CubeSurfacePoint`](super::CubeSurfacePoint). Each LUT used is at most 48 bytes in length,
23//!     but some operations might need multiple look-ups.
24//! * [`CubeSurfacePoint::<true>`]: As per the basic
25//!     [`CubeSurfacePoint`](super::CubeSurfacePoint). Each LUT used is up to 2304 bytes in length,
26//!     and each operation is guaranteed to consist of a single look-up.
27//! * [`ReferenceGroupPoint`]: As per the basic
28//!     [`ReferenceGroupPoint`](super::ReferenceGroupPoint). Each LUT used is up to 576 bytes in
29//!     length, and each operation is guaranteed to consist of a single look-up.
30//! * [`OppositeGroupPoint`]: As per the basic
31//!     [`OppositeGroupPoint`](super::OppositeGroupPoint). Each LUT used is up to 576 bytes in
32//!     length, and each operation is guaranteed to consist of a single look-up.
33//!     
34//!     
35//! # Using LUTs for multiplication and division of rotation data-types
36//!
37//! The rotation data-types do not carry LUT information inside of them.
38//! This means that, if they are multiplied/divided using the corresponding
39//! operands, then their divisions and multiplications always occur iteratively.
40//!
41//! To alleviate this, the
42//! [`multiply_rotations_luts`] and
43//! [`divide_rotations_luts`] have been provided.
44//! They need a boolean const parametre that denotes whether the operations will
45//! take place using big LUTs or not. That said, they only operate on general
46//! [`Rotation`]s; users needing more will need to implement their own functions
47//! as per the source code available.
48//!
49//! With all that said, any down-stream user for whom this restriction is a
50//! legitimate problem is cordially invited to inform the crate maintainer
51//! posthaste.
52//!
53//! ### Small note
54//!
55//! The Geometric-Group-specific data-types contained herein only operate
56//! using big LUTs. This is because the small-LUT versions of multiplication
57//! and division operate, like the non-LUT versions, by conditionally performing
58//! Elementary Reflections on the given point. However, a reflection immediately
59//! switches the Geometric Group to which a point belongs, which means that the
60//! LUTs used therein need to be 48 bytes long. Thus, multiplication and
61//! division have no performance benefit compared to
62//! [the Geometric-Group-agnostic data-type](CubeSurfacePoint);
63//! since those operations are also the ones that need the most computational
64//! resources, the implementation of small-point LUTs for
65//! Geometric-Group-Specific point-types was not deemed worthwhile.
66//!
67//! # How to use one less big LUT
68//! If one big LUT is necessary but two are too many, one can
69//! get rid of the division's LUT. This can be achieved by only using `div_alt`
70//! instead of the division operator; this substitutes division's big LUT for
71//! a small LUT for the point's reciprocal, which is then multiplied by the
72//! point in question using the remaining big LUT. The cost of this is that
73//! `div_alt` takes two look-ups instead of just one.
74//!
75//! It is not yet known whether `div_alt` deserves to also exist for
76//! `OppositeGroupPoint`s.
77use crate::CubeSurfacePoint as CratePt;
78use crate::Direction;
79use crate::Rotation;
80use core::convert::identity as id;
81use core::ops::{Div, Mul, MulAssign};
82
83/// A type synonym for convenience.
84/// ```
85/// use cube_rotations::CubeSurfacePoint::*;
86/// use cube_rotations::luts::*;
87/// let c: CspLarge = CspLarge(PosOnePosTwoPosThree);
88/// // This line was not trivial to make functional, but we did it after all.
89/// ```
90pub type CspLarge = CubeSurfacePoint<true>;
91
92#[doc(hidden)]
93#[allow(non_snake_case)]
94pub const fn CspLarge(x: CratePt) -> CspLarge {
95    CubeSurfacePoint::<true>(x)
96}
97
98/// A type synonym for convenience.
99/// ```
100/// use cube_rotations::CubeSurfacePoint::*;
101/// use cube_rotations::luts::*;
102/// let c: CspSmall = CspSmall(PosThreePosTwoPosOne);
103/// // This line was not trivial to make functional, but we did it after all.
104/// ```
105pub type CspSmall = CubeSurfacePoint<false>;
106
107#[doc(hidden)]
108#[allow(non_snake_case)]
109pub const fn CspSmall(x: CratePt) -> CspSmall {
110    CubeSurfacePoint::<false>(x)
111}
112
113/// A type synonym for convenience.
114/// ```
115/// use cube_rotations::ReferenceGroupPoint::*;
116/// use cube_rotations::luts::*;
117/// let c: RgpLarge = RgpLarge(PosOnePosTwoPosThree);
118/// // This line was not trivial to make functional, but we did it after all.
119/// ```
120pub type RgpLarge = ReferenceGroupPoint;
121
122#[doc(hidden)]
123#[allow(non_snake_case)]
124pub const fn RgpLarge(x: crate::ReferenceGroupPoint) -> RgpLarge {
125    ReferenceGroupPoint(x)
126}
127
128/// A type synonym for convenience.
129/// ```
130/// use cube_rotations::OppositeGroupPoint::*;
131/// use cube_rotations::luts::*;
132/// let c: OgpLarge = OgpLarge(PosThreePosTwoPosOne);
133/// // This line was not trivial to make functional, but we did it after all.
134/// ```
135pub type OgpLarge = OppositeGroupPoint;
136
137#[doc(hidden)]
138#[allow(non_snake_case)]
139pub const fn OgpLarge(x: crate::OppositeGroupPoint) -> OgpLarge {
140    OppositeGroupPoint(x)
141}
142
143macro_rules! with_lut {
144    ($truefalse: expr; $fnam: ident $(; $vis:vis)?) => {
145
146        #[doc = concat!(
147        "Please refer to the [function of the same name](crate::",
148                        stringify!(CubeSurfacePoint),
149                        "::",
150                        stringify!($fnam),
151                        ") in the basic [`",
152                        stringify!(CubeSurfacePoint),
153                        "`](crate::",
154                        stringify!(CubeSurfacePoint),
155                        ")."
156        )]
157        $($vis)? const fn $fnam(self) -> Self {
158            type Csp = CubeSurfacePoint::<{ $truefalse }>;
159
160            const fn from_serial_number(x: u8) -> Csp {
161                CubeSurfacePoint::<{ $truefalse }> (
162                    CratePt::probs_from_u8(x).$fnam()
163                )
164            }
165
166            const LUT: [Csp; 48] = {
167                let mut result = [Csp::REFERENCE_POINT; 48];
168                let mut i = 48;
169                while i > 0 {
170                    i -= 1;
171                    result[i] = from_serial_number(i as u8);
172                }
173                result
174            };
175
176            LUT[self.0 as usize]
177        }
178
179    };
180
181    (true; $name: ident, $($names:ident),+ $(; $vis:vis)?) => {
182        with_lut!(true; $name $(; $vis)?);
183        with_lut!(true; $($names),+ $(; $vis)?);
184    };
185
186    (false; $name: ident, $($names:ident),+ $(; $vis:vis)?) => {
187        with_lut!(false; $name $(; $vis)?);
188        with_lut!(false; $($names),+ $(; $vis)?);
189    };
190}
191
192/// A helper wrapper-type around a [`CubeSurfacePoint`] that operates using
193/// look-up tables.
194///
195/// A `luts::CubeSurfacePoint::<false>` only has LUTs 48
196/// bytes long, but might need more than one look-up for each operation. A
197/// `luts::CubeSurfacePoint::<true>`, in contrast, has LUTs up to 2304 bytes
198/// long, but only ever needs one look-up per operation.
199#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
200pub struct CubeSurfacePoint<const BIG_LUTS: bool>(pub crate::CubeSurfacePoint);
201
202impl<const BIG_LUTS: bool> CubeSurfacePoint<BIG_LUTS> {
203    /// Please refer to the ordinary
204    /// [`REFERENCE_POINT`](CubeSurfacePoint::REFERENCE_POINT).
205    pub const REFERENCE_POINT: Self = Self(CratePt::REFERENCE_POINT);
206
207    #[doc = concat!(
208    "Please refer to the [function of the same name]",
209    "(crate::CubeSurfacePoint::direction", ") in [`CubeSurfacePoint`]."
210    )]
211    pub const fn direction(self) -> Direction {
212        const fn from_serial_number(x: u8) -> Direction {
213            CratePt::probs_from_u8(x).direction()
214        }
215
216        const LUT: [Direction; 48] = {
217            let ref_pt = CratePt::REFERENCE_POINT;
218            let mut result = [ref_pt.direction(); 48];
219            let mut i = 48;
220            while i > 0 {
221                i -= 1;
222                result[i] = from_serial_number(i as u8);
223            }
224            result
225        };
226        LUT[self.0 as usize]
227    }
228
229    const fn probs_from_u8(x: u8) -> Self {
230        Self(CratePt::probs_from_u8(x))
231    }
232}
233
234impl<const BIG_LUTS: bool> From<CratePt> for CubeSurfacePoint<BIG_LUTS> {
235    fn from(x: CratePt) -> Self {
236        Self(x)
237    }
238}
239
240impl<const BIG_LUTS: bool> From<CubeSurfacePoint<BIG_LUTS>> for CratePt {
241    fn from(x: CubeSurfacePoint<BIG_LUTS>) -> Self {
242        x.0
243    }
244}
245
246impl From<Rotation> for CubeSurfacePoint<true> {
247    fn from(x: Rotation) -> Self {
248        Self(x.corresponding_point)
249    }
250}
251
252impl From<Rotation> for CubeSurfacePoint<false> {
253    fn from(x: Rotation) -> Self {
254        Self(x.corresponding_point)
255    }
256}
257
258impl CubeSurfacePoint<false> {
259    with_lut!(false; one_right_angle_cw, one_right_angle_acw, beside,
260                opposite, opposite_then_beside, flip_sign_of_2, flip_2_and_3; pub);
261    with_lut!(false; swap_x_y, swap_y_z, swap_z_x, reciprocal);
262
263    #[doc = concat!(
264    "Please refer to the [function of the same name
265    ](crate::CubeSurfacePoint::",
266                    stringify!(n_right_angles),
267                    ") in [`CubeSurfacePoint`](crate::CubeSurfacePoint)."
268    )]
269    pub const fn n_right_angles<const CLOCKWISE: bool>(self, angle: u8) -> Self {
270        let mut angle = angle & 0b11;
271        let mut result = self;
272        while angle > 0 {
273            result = if CLOCKWISE {
274                result.one_right_angle_cw()
275            } else {
276                result.one_right_angle_acw()
277            };
278            angle -= 1;
279        }
280        result
281    }
282
283    #[doc = concat!(
284    "Please refer to the [function of the same name
285    ](crate::CubeSurfacePoint::",
286                    stringify!(n_right_angles_acw),
287                    ") in [`CubeSurfacePoint`](crate::CubeSurfacePoint)."
288    )]
289    pub const fn n_right_angles_acw(self, angle: u8) -> Self {
290        self.n_right_angles::<false>(angle)
291    }
292
293    #[doc = concat!(
294    "Please refer to the [function of the same name
295    ](crate::CubeSurfacePoint::",
296                    stringify!(n_right_angles_cw),
297                    ") in [`CubeSurfacePoint`](crate::CubeSurfacePoint)."
298    )]
299    pub const fn n_right_angles_cw(self, angle: u8) -> Self {
300        self.n_right_angles::<true>(angle)
301    }
302}
303
304impl CubeSurfacePoint<true> {
305    with_lut!(true; one_right_angle_cw, one_right_angle_acw, beside,
306                opposite, opposite_then_beside, flip_sign_of_2, flip_2_and_3; pub);
307
308    #[doc = concat!(
309    "Please refer to the [function of the same name
310    ](crate::CubeSurfacePoint::",
311                    stringify!(n_right_angles_cw),
312                    ") in [`CubeSurfacePoint`](crate::CubeSurfacePoint)."
313    )]
314    pub const fn n_right_angles_cw(self, angle: u8) -> Self {
315        let angle = angle & 0b11;
316        type Csp = CubeSurfacePoint<true>;
317        const LUT: [[Csp; 48]; 4] = {
318            let mut result = [[Csp::REFERENCE_POINT; 48]; 4];
319            let mut i: usize = 48 * 4;
320            while i > 0 {
321                i -= 1;
322                result[i % 4][i / 4] = CubeSurfacePoint::<true>(
323                    CratePt::probs_from_u8(i as u8 / 4).n_right_angles_cw(i as u8 % 4),
324                );
325            }
326            result
327        };
328
329        LUT[angle as usize][self.0 as usize]
330    }
331
332    #[doc = concat!(
333    "Please refer to the [function of the same name
334    ](crate::CubeSurfacePoint::",
335                    stringify!(n_right_angles_acw),
336                    ") in [`CubeSurfacePoint`](crate::CubeSurfacePoint)."
337    )]
338    pub const fn n_right_angles_acw(self, angle: u8) -> Self {
339        let angle = angle & 0b11;
340        type Csp = CubeSurfacePoint<true>;
341        const LUT: [[Csp; 48]; 4] = {
342            let mut result = [[Csp::REFERENCE_POINT; 48]; 4];
343            let mut i: usize = 48 * 4;
344            while i > 0 {
345                i -= 1;
346                result[i % 4][i / 4] = CubeSurfacePoint::<true>(
347                    CratePt::probs_from_u8(i as u8 / 4).n_right_angles_acw(i as u8 % 4),
348                );
349            }
350            result
351        };
352
353        LUT[angle as usize][self.0 as usize]
354    }
355
356    /// An alternative implementation of division, that uses the same big
357    /// LUT as multiplication does, but performs two look-ups instead of
358    /// one.
359    pub fn div_alt(self, divisor: Self) -> Rotation {
360        const RECIPROCALS: [CubeSurfacePoint<true>; 48] = {
361            let mut i = 48;
362            let ref_pt = CubeSurfacePoint::<true>::REFERENCE_POINT;
363            let mut result = [ref_pt; 48];
364            while i > 0 {
365                i -= 1;
366                let pt = CratePt::probs_from_u8(i as u8);
367                let siiiigh = ref_pt.0.mul(ref_pt.0.div(pt));
368                result[i] = CubeSurfacePoint::<true>(siiiigh);
369            }
370            result
371        };
372
373        Rotation {
374            corresponding_point: (Rotation {
375                corresponding_point: self.0,
376            } * RECIPROCALS[divisor.0 as usize])
377                .0,
378        }
379    }
380}
381
382/// Rotates a copy of `self` according to a `Rotation`.
383impl Mul<Rotation> for CubeSurfacePoint<false> {
384    /// We use the most general data-type possible, so the output does not
385    /// change.
386    type Output = Self;
387
388    /// The rotation happens Elementary-Reflection-by-Elementary-Reflection
389    /// as usual, but each Elementary Reflection is performed with a LUT.
390    fn mul(self, rot: Rotation) -> Self::Output {
391        let rot = rot.corresponding_point as u8;
392        let mut result = self;
393
394        if rot & 0b001_000 != 0 {
395            result = result.swap_z_x();
396        }
397
398        if rot & 0b010_000 != 0 {
399            result = result.swap_y_z();
400        }
401
402        if rot & 0b100_000 != 0 {
403            result = result.swap_x_y();
404        }
405
406        let rot = rot & 7;
407
408        Self::probs_from_u8(result.0 as u8 ^ rot)
409    }
410}
411
412/// Rotates a copy of `self` according to a `Rotation`.
413impl Mul<Rotation> for CubeSurfacePoint<true> {
414    /// We use the most general data-type possible, so the output does not
415    /// change.
416    type Output = Self;
417
418    /// The `Rotation` is not examined bit-by-bit. Instead, a
419    /// look-up on a 2-D LUT produces the result directly.
420    fn mul(self, rot: Rotation) -> Self::Output {
421        type Csp = CubeSurfacePoint<true>;
422        const fn from_serial_number(x: u16) -> Csp {
423            let rot = crate::Rotation {
424                corresponding_point: CratePt::probs_from_u8((x / 48) as u8),
425            };
426            let surface_point = CratePt::probs_from_u8((x % 48) as u8);
427            CubeSurfacePoint::<true>(rot.mul(surface_point))
428        }
429        const LUT: [[Csp; 48]; 48] = {
430            let mut result = [[Csp::REFERENCE_POINT; 48]; 48];
431            let mut i = 48 * 48;
432            while i > 0 {
433                i -= 1;
434                result[i / 48][i % 48] = from_serial_number(i as u16);
435            }
436            result
437        };
438
439        LUT[rot.corresponding_point as usize][self.0 as usize]
440    }
441}
442
443/// Rotates `self` according to a `Rotation`.
444impl MulAssign<Rotation> for CubeSurfacePoint<true> {
445    /// We use the most general data-type possible, so the output does not
446    /// change. Thus, it can be directly assigned.
447    ///
448    /// The `Rotation` is not examined bit-by-bit. Instead, a look-up on a
449    /// 2-D LUT produces the result directly.
450    fn mul_assign(&mut self, rot: Rotation) {
451        *self = *self * rot;
452    }
453}
454
455/// Rotates `self` according to a `Rotation`.
456impl MulAssign<Rotation> for CubeSurfacePoint<false> {
457    /// We use the most general data-type possible, so the output does not
458    /// change. Thus, it can be directly assigned.
459    ///
460    /// The rotation happens Elementary-Reflection-by-Elementary-Reflection
461    /// as usual, but each Elementary Reflection is performed with a LUT.
462    fn mul_assign(&mut self, rot: Rotation) {
463        *self = *self * rot;
464    }
465}
466
467/// Rotates a copy of a given `CubeSurfacePoint` according to `self`.
468impl Mul<CubeSurfacePoint<true>> for Rotation {
469    /// Output same as input.
470    type Output = CubeSurfacePoint<true>;
471
472    /// `self` is not examined bit-by-bit. Instead, a look-up on a 2-D
473    /// LUT produces the result directly.
474    fn mul(self, cub_sur_pt: Self::Output) -> Self::Output {
475        cub_sur_pt * self
476    }
477}
478
479/// Rotates a copy of a given `CubeSurfacePoint` according to `self`.
480impl Mul<CubeSurfacePoint<false>> for Rotation {
481    /// Output same as input.
482    type Output = CubeSurfacePoint<false>;
483
484    /// The rotation happens Elementary-Reflection-by-Elementary-Reflection
485    /// as usual, but each Elementary Reflection is performed with a LUT.
486    fn mul(self, cub_sur_pt: Self::Output) -> Self::Output {
487        cub_sur_pt * self
488    }
489}
490
491/// Extracts the rotation that must occur so that the `divisor` point ends
492/// up coinciding with `self`, ie the dividend.
493impl Div for CubeSurfacePoint<false> {
494    /// This rotation can be either proper or improper.
495    type Output = Rotation;
496
497    /// The operation occurs by finding the reciprocal of the `divisor`
498    /// using a LUT, then rotating it according to `self`.
499    /// The rotation happens Elementary-Reflection-by-Elementary-Reflection
500    /// as usual, but each Elementary Reflection is performed with a LUT.
501    fn div(self, divisor: Self) -> Self::Output {
502        let recip = divisor.reciprocal();
503        let rot = Rotation {
504            corresponding_point: self.0,
505        };
506        Rotation {
507            corresponding_point: (recip.mul(rot)).0,
508        }
509    }
510}
511
512/// Extracts the rotation that must occur so that the `divisor` point ends
513/// up coinciding with `self`, ie the dividend.
514impl Div for CubeSurfacePoint<true> {
515    /// This rotation can be either proper or improper.
516    type Output = Rotation;
517
518    /// A look-up on a 2-D LUT produces the result directly.
519    fn div(self, divisor: Self) -> Self::Output {
520        const fn from_serial_number(x: u16) -> Rotation {
521            let divisor = CratePt::probs_from_u8((x / 48) as u8);
522
523            let point = CratePt::probs_from_u8((x % 48) as u8);
524            point.div(divisor)
525        }
526
527        const LUT: [[Rotation; 48]; 48] = {
528            let temp_rot = Rotation {
529                corresponding_point: CratePt::REFERENCE_POINT,
530            };
531            let mut result = [[temp_rot; 48]; 48];
532            let mut i = 48 * 48;
533            while i > 0 {
534                i -= 1;
535                result[i % 48][i / 48] = from_serial_number(i as u16);
536            }
537            result
538        };
539
540        let divisor = divisor.0 as usize;
541        let dividend = self.0 as usize;
542        LUT[dividend][divisor]
543    }
544}
545
546use crate::{ImproperRotation, ProperRotation};
547
548/// Rotates a copy of `self` according to a `ProperRotation`.
549/// Maintains Geometric Group.
550impl Mul<ProperRotation> for CubeSurfacePoint<false> {
551    /// The Geometric Group doesn't change. Even if it did, this data-type
552    /// is group-agnostic.
553    type Output = Self;
554
555    /// The rotation happens Elementary-Reflection-by-Elementary-Reflection
556    /// as usual, but each Elementary Reflection is performed with a LUT.
557    fn mul(self, x: ProperRotation) -> Self {
558        let rot: Rotation = x.into();
559        rot * self
560    }
561}
562
563/// Rotates a copy of `self` according to a `ProperRotation`.
564/// Maintains Geometric Group.
565impl Mul<ProperRotation> for CubeSurfacePoint<true> {
566    /// The Geometric Group doesn't change. Even if it did, this data-type
567    /// is group-agnostic.
568    type Output = Self;
569
570    /// The `ProperRotation` is not examined bit-by-bit. Instead, a look-up
571    /// on a 2-D LUT produces the result directly.
572    ///
573    /// While this could have been implemented using smaller LUTs than the
574    /// ones used for `Mul<Rotation>`, it was deemed a useless middle
575    /// solution.
576    fn mul(self, x: ProperRotation) -> Self {
577        let rot: Rotation = x.into();
578        rot * self
579    }
580}
581
582/// Rotates a copy of `self` according to an `ImproperRotation`.
583/// Switches Geometric Group.
584impl Mul<ImproperRotation> for CubeSurfacePoint<false> {
585    /// Although the Geometric Group does change, `CubeSurfacePoint` does
586    /// not change its data-type depending on Geometric Group. Thus the
587    /// data-type remains the same.
588    type Output = Self;
589
590    /// The rotation happens Elementary-Reflection-by-Elementary-Reflection
591    /// as usual, but each Elementary Reflection is performed with a LUT.
592    fn mul(self, x: ImproperRotation) -> Self {
593        let rot: Rotation = x.into();
594        rot * self
595    }
596}
597
598/// Rotates a copy of `self` according to an `ImproperRotation`. Switches
599/// Geometric Group.
600impl Mul<ImproperRotation> for CubeSurfacePoint<true> {
601    /// Although the Geometric Group does change, `CubeSurfacePoint` does not
602    /// change its data-type depending on Geometric Group. Thus the data-type
603    /// remains the same.
604    type Output = Self;
605
606    /// The `ImproperRotation` is not examined bit-by-bit. Instead, a
607    /// look-up on a 2-D LUT produces the result directly.
608    ///
609    /// While this could have been implemented using smaller LUTs than the
610    /// ones
611    /// used for `Mul<Rotation>`, it was deemed a useless middle solution.
612    fn mul(self, x: ImproperRotation) -> Self {
613        let rot: Rotation = x.into();
614        rot * self
615    }
616}
617
618/// Rotates `self` according to a `ProperRotation`. Maintains Geometric
619/// Group.
620impl MulAssign<ProperRotation> for CubeSurfacePoint<true> {
621    /// Neither the Geometric Group nor the data-type change, so the result
622    /// can be directly assigned.
623    ///
624    /// The `ProperRotation` is not examined bit-by-bit. Instead, a
625    /// look-up on a 2-D LUT produces the result directly.
626    ///
627    /// While this could have been implemented using smaller LUTs than the
628    /// ones
629    /// used for `Mul<Rotation>`, it was deemed a useless middle solution.
630    fn mul_assign(&mut self, x: ProperRotation) {
631        *self = *self * x;
632    }
633}
634
635/// Rotates `self` according to an `ImproperRotation`. Switches Geometric
636/// Group.
637impl MulAssign<ImproperRotation> for CubeSurfacePoint<true> {
638    /// The data-type remains the same, despite the Geometric Group
639    /// changing. Thus, the result can be directly assigned.
640    ///
641    /// The `ImproperRotation` is not examined bit-by-bit. Instead, a
642    /// look-up on a 2-D LUT produces the result directly.
643    ///
644    /// While this could have been implemented using smaller LUTs than the
645    /// ones
646    /// used for `Mul<Rotation>`, it was deemed a useless middle solution.
647    fn mul_assign(&mut self, x: ImproperRotation) {
648        *self = *self * x;
649    }
650}
651
652fn mul_rots_big_luts(rot_1: Rotation, rot_2: Rotation) -> Rotation {
653    let corr_point: CubeSurfacePoint<true> = rot_2.into();
654    (rot_1 * corr_point).into()
655}
656
657fn div_rots_big_luts(rot_1: Rotation, rot_2: Rotation) -> Rotation {
658    let corr_point_1: CubeSurfacePoint<true> = rot_1.into();
659    let corr_point_2: CubeSurfacePoint<true> = rot_2.into();
660    corr_point_1 / corr_point_2
661}
662
663fn mul_rots_small_luts(rot_1: Rotation, rot_2: Rotation) -> Rotation {
664    let corr_point: CubeSurfacePoint<false> = rot_2.into();
665    (rot_1 * corr_point).into()
666}
667
668fn div_rots_small_luts(rot_1: Rotation, rot_2: Rotation) -> Rotation {
669    let corr_point_1: CubeSurfacePoint<false> = rot_1.into();
670    let corr_point_2: CubeSurfacePoint<false> = rot_2.into();
671    corr_point_1 / corr_point_2
672}
673
674/// Multiplies two `Rotation`s together, using LUTs.
675pub fn multiply_rotations_luts<const BIG: bool>(rot_1: Rotation, rot_2: Rotation) -> Rotation {
676    [mul_rots_small_luts, mul_rots_big_luts][BIG as usize](rot_1, rot_2)
677}
678
679/// Divides two `Rotation`s together, using LUTs.
680pub fn divide_rotations_luts<const BIG: bool>(rot_1: Rotation, rot_2: Rotation) -> Rotation {
681    [div_rots_small_luts, div_rots_big_luts][BIG as usize](rot_1, rot_2)
682}
683
684/// Rotates `self` according to a `ProperRotation`. Maintains Geometric
685/// Group.
686impl MulAssign<ProperRotation> for CubeSurfacePoint<false> {
687    /// Neither the Geometric Group nor the data-type change, so the result
688    /// can be directly assigned.
689    ///
690    /// The `ProperRotation` is not examined bit-by-bit. Instead, a
691    /// look-up on a 2-D LUT produces the result directly.
692    ///
693    /// While this could have been implemented using smaller LUTs than the
694    /// ones
695    /// used for `Mul<Rotation>`, it was deemed a useless middle solution.
696    fn mul_assign(&mut self, x: ProperRotation) {
697        *self = *self * x;
698    }
699}
700
701/// Rotates `self` according to a `ImproperRotation`. Switches Geometric
702/// Group.
703impl MulAssign<ImproperRotation> for CubeSurfacePoint<false> {
704    /// The data-type remains the same, despite the Geometric Group
705    /// changing. Thus, the result can be directly assigned.
706    ///
707    /// The `ImproperRotation` is not examined bit-by-bit. Instead, a
708    /// look-up on a 2-D LUT produces the result directly.
709    ///
710    /// While this could have been implemented using smaller LUTs than the
711    /// ones
712    /// used for `Mul<Rotation>`, it was deemed a useless middle solution.
713    fn mul_assign(&mut self, x: ImproperRotation) {
714        *self = *self * x;
715    }
716}
717
718macro_rules! with_lut {
719    ($us: ty, $out: path, $fnam: ident, $convert: ident) => {
720
721        #[doc = concat!(
722        "Please refer to the [function of the same name
723        ](crate::CubeSurfacePoint::",
724                        stringify!($fnam),
725                        ") in 
726                        [`CubeSurfacePoint`](crate::CubeSurfacePoint)."
727        )]
728        pub const fn $fnam(self) -> $out {
729
730            const fn from_serial_number (i: usize) -> $out {
731                $convert(<$us>::index_to_self(i).0.$fnam())
732            }
733
734            const LUT: [$out; 24] = {
735                let mut result = [from_serial_number(0); 24];
736                let mut i = 24;
737                while i > 0 {
738                    i -= 1;
739                    result[i] = from_serial_number(i);
740                }
741                result
742            };
743
744            LUT[self.0 as usize >> 1]
745        }
746    };
747
748    (angles; $us: path, $fnam: ident) => {
749        #[doc = concat!(
750        "Please refer to the [function of the same name
751        ](crate::CubeSurfacePoint::",
752                        stringify!($fnam),
753                        ") in 
754                        [`CubeSurfacePoint`](crate::CubeSurfacePoint)."
755        )]
756        pub const fn $fnam(self, angle:u8) -> Self {
757            let angle = angle & 0b11;
758
759            const fn from_serial_number (i: usize) -> $us {
760                $us(<$us>::index_to_self(i / 4).0.$fnam((i as u8) % 4))
761            }
762
763            const LUT: [[$us; 24]; 4] = {
764                let mut result = [[from_serial_number(0); 24]; 4];
765                let mut i: usize = 24 * 4;
766                while i > 0 {
767                    i -= 1;
768                    result[i % 4][i / 4] = from_serial_number(i);
769                }
770                result
771            };
772
773            LUT[angle as usize][self.0 as usize >> 1]
774        }
775    };
776
777    (muldiv; $us: ty, $other: ty, $out: ty, $fnam: ident, $out_to_out: ident $(; $const:tt)? $(.$siiiigh: tt)?) => {
778        #[doc = concat!(
779        "Please refer to the [function of the same name](crate::",
780                        stringify!($us),
781                        "::",
782                        stringify!($fnam),
783                        ") in the basic [`",
784                        stringify!($us),
785                        "`](crate::",
786                        stringify!($us),
787                        ")."
788        )]
789        $(pub $const)? fn $fnam(self, other: $other) -> $out {
790            const fn from_serial_number(x: usize) -> $out {
791                let us = <$us>::index_to_self(x / 24).0;
792                let other = <$other>::index_to_self(x % 24) $(. $siiiigh)?;
793                $out_to_out(us.$fnam(other))
794            }
795
796            const LUT: [[$out; 24]; 24] = {
797                let mut result = [[from_serial_number(0); 24]; 24];
798                let mut i = 24 * 24;
799                while i > 0 {
800                    i -= 1;
801                    result[i / 24][i % 24] = from_serial_number(i);
802                }
803                result
804            };
805
806            LUT[self.0 as usize >> 1][other.self_to_index() as usize >> 1]
807        }
808    };
809
810    (everything; $us: ty, $others:ty, $check: ident,
811        $self_to_self:ident, $other_to_other: ident $(,)?) => {
812
813            const fn index_to_self(x: usize) -> $us {
814                let x = (x as u8) * 2;
815                let pt_1 = CratePt::probs_from_u8(x);
816                let pt_2 = CratePt::probs_from_u8(x ^ 1);
817                let result = match (pt_1.$check(), pt_2.$check()) {
818                    (Ok(point), Err(_)) => point,
819                    (Err(_), Ok(point)) => point,
820                    _ => crate::unreachable_semichecked(),
821                };
822                $self_to_self(result)
823            }
824
825            const fn self_to_index(self) -> usize {
826                self.0 as usize
827            }
828
829            with_lut!($us, Direction, direction, id);
830            with_lut!($us, $others, beside, $other_to_other);
831            with_lut!($us, $others, opposite, $other_to_other);
832            with_lut!($us, $others, flip_sign_of_2, $other_to_other);
833            with_lut!($us, $us, opposite_then_beside, $self_to_self);
834            with_lut!($us, $us, flip_2_and_3, $self_to_self);
835            with_lut!($us, $us, one_right_angle_cw, $self_to_self);
836            with_lut!($us, $us, one_right_angle_acw, $self_to_self);
837            with_lut!(angles; $us, n_right_angles_cw);
838            with_lut!(angles; $us, n_right_angles_acw);
839            with_lut!(muldiv; $us, $us, ProperRotation, div_prop, id; const .0);
840            with_lut!(muldiv; $us, $others, ImproperRotation, div_improp, id; const .0);
841            with_lut!(muldiv; $us, ProperRotation, $us, mul_prop, $self_to_self; const);
842            with_lut!(muldiv; $us, ImproperRotation, $others, mul_improp, $other_to_other; const);
843        };
844
845        (impls; $us: ty, $others: ty) => {
846
847            /// Rotates a copy of `self` according to a `ProperRotation`.
848            /// Maintains Geometric Group.
849            impl Mul<ProperRotation> for $us {
850                /// Output belongs to the same Geometric Group.
851                type Output = Self;
852                /// The `ProperRotation` is not examined bit-by-bit. Instead, a
853                /// look-up on a 2-D LUT produces the result directly.
854                fn mul (self, x: ProperRotation) -> Self::Output {
855                    self.mul_prop(x)
856                }
857            }
858
859            /// Rotates `self` according to a `ProperRotation`. Maintains
860            /// Geometric Group.
861            impl MulAssign<ProperRotation> for $us {
862                /// The data-type doesn't change, so the result can be directly
863                /// assigned.
864                ///
865                /// The `ProperRotation` is not examined bit-by-bit. Instead, a
866                /// look-up on a 2-D LUT produces the result directly.
867                fn mul_assign(&mut self, x: ProperRotation) {
868                    *self = *self * x;
869                }
870            }
871
872            /// Rotates a copy of `self` according to an `ImproperRotation`.
873            /// Switches Geometric Group.
874            impl Mul<ImproperRotation> for $us {
875                /// Output belongs to the other Geometric Group.
876                type Output = $others;
877                /// The `ImproperRotation` is not examined bit-by-bit. Instead,
878                /// a look-up on a 2-D LUT produces the result directly.
879                fn mul (self, x: ImproperRotation) -> Self::Output {
880                    self.mul_improp(x)
881                }
882            }
883
884            impl Mul<$us> for ProperRotation {
885                /// Output belongs to the same Geometric Group.
886                type Output = $us;
887                /// The `ProperRotation` is not examined bit-by-bit. Instead,
888                /// a look-up on a 2-D LUT produces the result directly.
889                fn mul (self, x: $us) -> Self::Output {
890                    x * self
891                }
892            }
893
894            impl Mul<$us> for ImproperRotation {
895                /// Output belongs to the other Geometric Group.
896                type Output = $others;
897                /// The `ImproperRotation` is not examined bit-by-bit. Instead,
898                /// a look-up on a 2-D LUT produces the result directly.
899                fn mul (self, x: $us) -> Self::Output {
900                    x * self
901                }
902            }
903
904            /// Extracts the proper rotation that must occur so that the
905            /// divisor` point ends up coinciding with `self`, ie the dividend.
906            impl Div for $us {
907                /// A proper rotation is enough for this operation.
908                type Output = ProperRotation;
909                /// A look-up on a 2-D LUT produces the result directly.
910                fn div (self, x: Self) -> Self::Output {
911                    self.div_prop(x)
912                }
913            }
914
915            /// Extracts the improper rotation that must occur so that the
916            /// divisor` point ends up coinciding with `self`, ie the dividend.
917            impl Div<$others> for $us {
918                /// This operation needs an improper rotation.
919                type Output = ImproperRotation;
920                /// A look-up on a 2-D LUT produces the result directly.
921                fn div (self, x: $others) -> Self::Output {
922                    self.div_improp(x)
923                }
924            }
925        };
926
927        (impls; $us: ty, $others: ty, $check: ident, $self_to_self: ident, $other_to_other: ident) => {
928            /// Discards any knowledge of Geometric Group, producing a general
929            /// `crate::CubeSurfacePoint`.
930            impl From<$us> for CratePt {
931                fn from(x: $us) -> CratePt {
932                    x.0.downcast()
933                }
934            }
935
936            /// Please refer to [`crate::CubeSurfacePoint::determine_group`].
937            impl TryFrom<CratePt> for $us {
938                #[doc = concat!(
939                "If a certain [`CubeSurfacePoint`] does not belong to the [`",
940                stringify!($us),
941                                "`]s, it must by necessity belong to the [`",
942                                stringify!($others),
943                                "`]s."
944                )]
945                type Error = $others;
946
947                fn try_from(x: CratePt) -> Result<Self, Self::Error> {
948                    match x.$check() {
949                        Ok(point) => {
950                            Ok($self_to_self(point))
951                        },
952                        Err(point) => {
953                            Err($other_to_other(point))
954                        },
955                    }
956                }
957            }
958        };
959
960}
961
962/// As per the basic
963/// [`ReferenceGroupPoint`]. Each LUT used is up to 576 bytes in
964/// length, and each operation is guaranteed to consist of a single look-up.
965#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
966pub struct ReferenceGroupPoint(pub crate::ReferenceGroupPoint);
967
968/// As per the basic
969/// [`OppositeGroupPoint`]. Each LUT used is up to 576 bytes in
970/// length, and each operation is guaranteed to consist of a single look-up.
971#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
972pub struct OppositeGroupPoint(pub crate::OppositeGroupPoint);
973
974const fn rgp_to_rgp(x: crate::ReferenceGroupPoint) -> ReferenceGroupPoint {
975    ReferenceGroupPoint(x)
976}
977
978const fn ogp_to_ogp(x: crate::OppositeGroupPoint) -> OppositeGroupPoint {
979    OppositeGroupPoint(x)
980}
981
982impl From<ProperRotation> for ReferenceGroupPoint {
983    fn from(x: ProperRotation) -> Self {
984        Self(x.corresponding_point)
985    }
986}
987
988impl From<ImproperRotation> for OppositeGroupPoint {
989    fn from(x: ImproperRotation) -> Self {
990        Self(x.corresponding_point)
991    }
992}
993
994impl From<CubeSurfacePoint<true>> for Rotation {
995    fn from(corresponding_point: CubeSurfacePoint<true>) -> Self {
996        Self {
997            corresponding_point: corresponding_point.0,
998        }
999    }
1000}
1001
1002impl From<CubeSurfacePoint<false>> for Rotation {
1003    fn from(corresponding_point: CubeSurfacePoint<false>) -> Self {
1004        Self {
1005            corresponding_point: corresponding_point.0,
1006        }
1007    }
1008}
1009
1010impl From<ReferenceGroupPoint> for ProperRotation {
1011    fn from(corresponding_point: ReferenceGroupPoint) -> Self {
1012        Self {
1013            corresponding_point: corresponding_point.0,
1014        }
1015    }
1016}
1017
1018impl From<OppositeGroupPoint> for ImproperRotation {
1019    fn from(corresponding_point: OppositeGroupPoint) -> Self {
1020        Self {
1021            corresponding_point: corresponding_point.0,
1022        }
1023    }
1024}
1025
1026impl ProperRotation {
1027    const fn index_to_self(x: usize) -> Self {
1028        Self {
1029            corresponding_point: ReferenceGroupPoint::index_to_self(x).0,
1030        }
1031    }
1032
1033    const fn self_to_index(self) -> usize {
1034        self.corresponding_point as usize
1035    }
1036}
1037
1038impl ImproperRotation {
1039    const fn index_to_self(x: usize) -> Self {
1040        Self {
1041            corresponding_point: OppositeGroupPoint::index_to_self(x).0,
1042        }
1043    }
1044
1045    const fn self_to_index(self) -> usize {
1046        self.corresponding_point as usize
1047    }
1048}
1049
1050impl ReferenceGroupPoint {
1051    with_lut!(
1052        everything;
1053        ReferenceGroupPoint,
1054        OppositeGroupPoint,
1055        determine_group,
1056        rgp_to_rgp,
1057        ogp_to_ogp,
1058    );
1059
1060    /// An alternative implementation of division, that uses the same big
1061    /// LUT as multiplication does, but performs two look-ups instead of
1062    /// one.
1063    pub fn div_alt(self, x: Self) -> ProperRotation {
1064        const LUT: [ReferenceGroupPoint; 24] = {
1065            let mut result = [ReferenceGroupPoint::index_to_self(0); 24];
1066            let mut i = 24;
1067            while i > 0 {
1068                i -= 1;
1069                result[i] = ReferenceGroupPoint(
1070                    ReferenceGroupPoint::index_to_self(0)
1071                        .div_prop(ReferenceGroupPoint::index_to_self(i))
1072                        .corresponding_point,
1073                );
1074            }
1075            result
1076        };
1077
1078        let corresponding_point = ProperRotation {
1079            corresponding_point: self.0,
1080        } * LUT[x.0 as usize >> 1];
1081        ProperRotation {
1082            corresponding_point: corresponding_point.0,
1083        }
1084    }
1085}
1086
1087impl OppositeGroupPoint {
1088    with_lut!(
1089        everything;
1090        OppositeGroupPoint,
1091        ReferenceGroupPoint,
1092        determine_antigroup,
1093        ogp_to_ogp,
1094        rgp_to_rgp,
1095    );
1096
1097    /// An alternative implementation of division, that uses the same big
1098    /// LUT as multiplication does, but performs two look-ups instead of
1099    /// one.
1100    #[deprecated(note = "It is not yet clear whether this function should \
1101also exist for `OppositeGroupPoint`s.")]
1102    pub fn div_alt(self, x: Self) -> ProperRotation {
1103        const LUT: [OppositeGroupPoint; 24] = {
1104            let mut result = [OppositeGroupPoint::index_to_self(0); 24];
1105            let mut i = 24;
1106            while i > 0 {
1107                i -= 1;
1108                result[i] = OppositeGroupPoint(
1109                    ReferenceGroupPoint::index_to_self(0)
1110                        .div_improp(OppositeGroupPoint::index_to_self(i))
1111                        .corresponding_point,
1112                );
1113            }
1114            result
1115        };
1116
1117        let corresponding_point = ImproperRotation {
1118            corresponding_point: self.0,
1119        } * LUT[x.0 as usize >> 1];
1120        ProperRotation {
1121            corresponding_point: corresponding_point.0,
1122        }
1123    }
1124}
1125
1126with_lut!(impls; ReferenceGroupPoint, OppositeGroupPoint);
1127with_lut!(impls; OppositeGroupPoint, ReferenceGroupPoint);
1128with_lut!(impls;
1129ReferenceGroupPoint,
1130OppositeGroupPoint,
1131determine_group,
1132rgp_to_rgp,
1133ogp_to_ogp
1134);
1135with_lut!(impls;
1136OppositeGroupPoint,
1137ReferenceGroupPoint,
1138determine_antigroup,
1139ogp_to_ogp,
1140rgp_to_rgp
1141);