Skip to main content

rcuber/
cubie.rs

1use rand::random;
2use static_init::dynamic;
3use std::fmt;
4use std::ops::Mul;
5
6use self::{Corner::*, Edge::*, Move::*};
7use crate::constants::*;
8use crate::error::Error;
9use crate::{facelet::*, moves::*};
10
11/// Represents the 8 corners on the cube, described by the layer they are on.
12/// 
13/// Example: `ULB` (Up, Left, Bottom).
14#[rustfmt::skip]
15#[allow(clippy::upper_case_acronyms)]
16#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
17pub enum Corner {
18    URF, UFL, ULB, UBR, DFR, DLF, DBL, DRB,
19}
20
21impl fmt::Display for Corner {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        write!(f, "{:?}", self)
24    }
25}
26
27impl TryFrom<u8> for Corner {
28    type Error = Error;
29
30    fn try_from(value: u8) -> Result<Self, Self::Error> {
31        match value {
32            0 => Ok(URF),
33            1 => Ok(UFL),
34            2 => Ok(ULB),
35            3 => Ok(UBR),
36            4 => Ok(DFR),
37            5 => Ok(DLF),
38            6 => Ok(DBL),
39            7 => Ok(DRB),
40            _ => Err(Error::InvalidCorner),
41        }
42    }
43}
44
45impl TryFrom<&str> for Corner {
46    type Error = Error;
47
48    fn try_from(value: &str) -> Result<Self, Self::Error> {
49        match value {
50            "URF" => Ok(URF),
51            "UFL" => Ok(UFL),
52            "ULB" => Ok(ULB),
53            "UBR" => Ok(UBR),
54            "DFR" => Ok(DFR),
55            "DLF" => Ok(DLF),
56            "DBL" => Ok(DBL),
57            "DRB" => Ok(DRB),
58            _ => Err(Error::InvalidCorner),
59        }
60    }
61}
62/// Represents the 12 edges on the cube, described by the layer they are on.
63/// 
64/// Example: `BL` (Bottom, Left).
65#[rustfmt::skip]
66#[allow(clippy::upper_case_acronyms)]
67#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Eq, Hash)]
68pub enum Edge {
69    UR, UF, UL, UB, DR, DF, DL, DB, FR, FL, BL, BR,
70}
71
72impl fmt::Display for Edge {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(f, "{:?}", self)
75    }
76}
77
78impl TryFrom<u8> for Edge {
79    type Error = Error;
80
81    fn try_from(value: u8) -> Result<Self, Self::Error> {
82        match value {
83            0 => Ok(UR),
84            1 => Ok(UF),
85            2 => Ok(UL),
86            3 => Ok(UB),
87            4 => Ok(DR),
88            5 => Ok(DF),
89            6 => Ok(DL),
90            7 => Ok(DB),
91            8 => Ok(FR),
92            9 => Ok(FL),
93            10 => Ok(BL),
94            11 => Ok(BR),
95            _ => Err(Error::InvalidEdge),
96        }
97    }
98}
99
100impl TryFrom<&str> for Edge {
101    type Error = Error;
102
103    fn try_from(value: &str) -> Result<Self, Self::Error> {
104        match value {
105            "UR" => Ok(UR),
106            "UF" => Ok(UF),
107            "UL" => Ok(UL),
108            "UB" => Ok(UB),
109            "DR" => Ok(DR),
110            "DF" => Ok(DF),
111            "DL" => Ok(DL),
112            "DB" => Ok(DB),
113            "FR" => Ok(FR),
114            "FL" => Ok(FL),
115            "BL" => Ok(BL),
116            "BR" => Ok(BR),
117            _ => Err(Error::InvalidEdge),
118        }
119    }
120}
121
122/// Cube on the cubie level.
123#[derive(Debug, PartialEq, Clone, Copy)]
124pub struct CubieCube {
125    /// Center permutation, relative to SOLVED_STATE.
126    pub center: [Color; 6],
127    /// Corner permutation, relative to SOLVED_STATE.
128    pub cp: [Corner; 8],
129    /// Corner orientation, 3 possible values: 0 (correctly oriented), 1 (twisted clockwise), 2 (twisted counter-clockwise).
130    pub co: [u8; 8],
131    /// Edge permutation, relative to SOLVED_STATE.
132    pub ep: [Edge; 12],
133    /// Edge orientation, 2 possible values: 0 (correctly oriented), 1 (flipped).
134    pub eo: [u8; 12],
135}
136
137/// Solved cube on the Cubie level.
138pub const SOLVED_CUBIE_CUBE: CubieCube = CubieCube {
139    center: [Color::U, Color::R, Color::F, Color::D, Color::L, Color::B],
140    cp: [URF, UFL, ULB, UBR, DFR, DLF, DBL, DRB],
141    co: [0, 0, 0, 0, 0, 0, 0, 0],
142    ep: [UR, UF, UL, UB, DR, DF, DL, DB, FR, FL, BL, BR],
143    eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
144};
145
146impl Default for CubieCube {
147    fn default() -> Self {
148        SOLVED_CUBIE_CUBE
149    }
150}
151
152impl Mul for CubieCube {
153    type Output = Self;
154
155    fn mul(self, rhs: CubieCube) -> Self::Output {
156        let mut res = CubieCube::default();
157        // (A * B).c = A(B(x).c).c
158        // (A * B).o = A(B(x).c).o + B(x).o
159
160        for i in 0..8 {
161            res.cp[i] = self.cp[rhs.cp[i] as usize];
162            res.co[i] = (self.co[rhs.cp[i] as usize] + rhs.co[i]) % 3;
163        }
164
165        for i in 0..12 {
166            res.ep[i] = self.ep[rhs.ep[i] as usize];
167            res.eo[i] = (self.eo[rhs.ep[i] as usize] + rhs.eo[i]) % 2;
168        }
169
170        for i in 0..6 {
171            res.center[i] = self.center[rhs.center[i] as usize];
172        }
173        res
174    }
175}
176
177impl fmt::Display for CubieCube {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        // Print string for a cubie cube.
180        let mut s = String::new();
181        for i in 0..8 {
182            let cs: String = format!("({},{})", self.cp[i], self.co[i]);
183            s.push_str(&cs);
184        }
185        for i in 0..12 {
186            let es: String = format!("({},{})", self.ep[i], self.eo[i]);
187            s.push_str(&es);
188        }
189        write!(f, "{s}")
190    }
191}
192
193impl From<&Vec<Move>> for CubieCube {
194    fn from(moves: &Vec<Move>) -> Self {
195        CubieCube::default().apply_moves(moves)
196    }
197}
198
199/// Gives cubie representation of a face cube (facelet).
200impl TryFrom<&FaceCube> for CubieCube {
201    type Error = Error;
202    fn try_from(face_cube: &FaceCube) -> Result<Self, Self::Error> {
203        let mut state = CubieCube::default();
204        let mut ori: usize = 0;
205        let mut col1;
206        let mut col2;
207
208        for i in 0..8 {
209            let i = Corner::try_from(i)?;
210            // get the colors of the cubie at corner i, starting with U/D
211            for index in 0..3 {
212                ori = index;
213                if face_cube.f[CORNER_FACELET[i as usize][ori] as usize] == Color::U
214                    || face_cube.f[CORNER_FACELET[i as usize][ori] as usize] == Color::D
215                {
216                    break;
217                }
218            }
219
220            col1 = face_cube.f[CORNER_FACELET[i as usize][(ori + 1) % 3] as usize];
221            col2 = face_cube.f[CORNER_FACELET[i as usize][(ori + 2) % 3] as usize];
222
223            for j in 0..8 {
224                let j = Corner::try_from(j)?;
225                if col1 == CORNER_COLOR[j as usize][1] && col2 == CORNER_COLOR[j as usize][2] {
226                    // in cornerposition i we have cornercubie j
227                    state.cp[i as usize] = j;
228                    state.co[i as usize] = ori as u8 % 3;
229                    break;
230                }
231            }
232        }
233
234        for i in 0..12 {
235            let i = Edge::try_from(i)?;
236            for j in 0..12 {
237                let j = Edge::try_from(j)?;
238                if face_cube.f[EDGE_FACELET[i as usize][0] as usize] == EDGE_COLOR[j as usize][0]
239                    && face_cube.f[EDGE_FACELET[i as usize][1] as usize]
240                        == EDGE_COLOR[j as usize][1]
241                {
242                    state.ep[i as usize] = j;
243                    state.eo[i as usize] = 0;
244                    break;
245                }
246                if face_cube.f[EDGE_FACELET[i as usize][0] as usize] == EDGE_COLOR[j as usize][1]
247                    && face_cube.f[EDGE_FACELET[i as usize][1] as usize]
248                        == EDGE_COLOR[j as usize][0]
249                {
250                    state.ep[i as usize] = j;
251                    state.eo[i as usize] = 1;
252                    break;
253                }
254            }
255        }
256
257        if !state.is_solvable() {
258            Err(Error::InvalidFaceletValue)
259        } else {
260            Ok(state)
261        }
262    }
263}
264
265impl CubieCube {
266    /// Applies a move to the current state.
267    pub fn apply_move(self, move_name: Move) -> Self {
268        let move_state = match move_name {
269            N => N_MOVE,
270            U => U_MOVE,
271            U2 => U_MOVE * U_MOVE,
272            U3 => U_MOVE * U_MOVE * U_MOVE,
273            D => D_MOVE,
274            D2 => D_MOVE * D_MOVE,
275            D3 => D_MOVE * D_MOVE * D_MOVE,
276            R => R_MOVE,
277            R2 => R_MOVE * R_MOVE,
278            R3 => R_MOVE * R_MOVE * R_MOVE,
279            L => L_MOVE,
280            L2 => L_MOVE * L_MOVE,
281            L3 => L_MOVE * L_MOVE * L_MOVE,
282            F => F_MOVE,
283            F2 => F_MOVE * F_MOVE,
284            F3 => F_MOVE * F_MOVE * F_MOVE,
285            B => B_MOVE,
286            B2 => B_MOVE * B_MOVE,
287            B3 => B_MOVE * B_MOVE * B_MOVE,
288            M => M_MOVE,
289            M2 => M_MOVE * M_MOVE,
290            M3 => M_MOVE * M_MOVE * M_MOVE,
291            E => E_MOVE,
292            E2 => E_MOVE * E_MOVE,
293            E3 => E_MOVE * E_MOVE * E_MOVE,
294            S => S_MOVE,
295            S2 => S_MOVE * S_MOVE,
296            S3 => S_MOVE * S_MOVE * S_MOVE,
297            Uw => U_MOVE * E_MOVE * E_MOVE * E_MOVE,
298            Uw2 => U_MOVE * U_MOVE * E_MOVE * E_MOVE,
299            Uw3 => U_MOVE * U_MOVE * U_MOVE * E_MOVE,
300            Dw => D_MOVE * E_MOVE,
301            Dw2 => D_MOVE * D_MOVE * E_MOVE * E_MOVE,
302            Dw3 => D_MOVE * D_MOVE * D_MOVE * E_MOVE * E_MOVE * E_MOVE,
303            Rw => R_MOVE * M_MOVE * M_MOVE * M_MOVE,
304            Rw2 => R_MOVE * R_MOVE * M_MOVE * M_MOVE,
305            Rw3 => R_MOVE * R_MOVE * R_MOVE * M_MOVE,
306            Lw => L_MOVE * M_MOVE,
307            Lw2 => L_MOVE * L_MOVE * M_MOVE * M_MOVE,
308            Lw3 => L_MOVE * L_MOVE * L_MOVE * M_MOVE * M_MOVE * M_MOVE,
309            Fw => F_MOVE * S_MOVE,
310            Fw2 => F_MOVE * F_MOVE * S_MOVE * S_MOVE,
311            Fw3 => F_MOVE * F_MOVE * F_MOVE * S_MOVE * S_MOVE * S_MOVE,
312            Bw => B_MOVE * S_MOVE * S_MOVE * S_MOVE,
313            Bw2 => B_MOVE * B_MOVE * S_MOVE * S_MOVE,
314            Bw3 => B_MOVE * B_MOVE * B_MOVE * S_MOVE,
315            x => R_MOVE * M_MOVE * M_MOVE * M_MOVE * L_MOVE * L_MOVE * L_MOVE,
316            x2 => R_MOVE * R_MOVE * M_MOVE * M_MOVE * L_MOVE * L_MOVE,
317            x3 => R_MOVE * R_MOVE * R_MOVE * M_MOVE * L_MOVE,
318            y => U_MOVE * E_MOVE * E_MOVE * E_MOVE * D_MOVE * D_MOVE * D_MOVE,
319            y2 => U_MOVE * U_MOVE * E_MOVE * E_MOVE * D_MOVE * D_MOVE,
320            y3 => U_MOVE * U_MOVE * U_MOVE * E_MOVE * D_MOVE,
321            z => F_MOVE * S_MOVE * B_MOVE * B_MOVE * B_MOVE,
322            z2 => F_MOVE * F_MOVE * S_MOVE * S_MOVE * B_MOVE * B_MOVE,
323            z3 => F_MOVE * F_MOVE * F_MOVE * S_MOVE * S_MOVE * S_MOVE * B_MOVE,
324        };
325
326        self * move_state
327    }
328
329    /// Applies the sequence of moves to the current state.
330    pub fn apply_moves(&self, moves: &[Move]) -> Self {
331        moves.iter().fold(*self, |acc, &m| acc.apply_move(m))
332    }
333
334    /// Applies the sequence of moves to the current state.
335    pub fn apply_formula(&self, formula: &Formula) -> Self {
336        formula
337            .moves
338            .iter()
339            .fold(*self, |acc, &m| acc.apply_move(m))
340    }
341
342    /// Multiply this cubie cube with another cubie cube b, restricted to the corners.
343    pub fn corner_multiply(&mut self, b: CubieCube) {
344        let mut c_perm = [URF; 8];
345        let mut c_ori = [0; 8];
346        let mut ori = 0;
347        for ci in ALL_CORNERS {
348            let c = ci as usize;
349            c_perm[c] = self.cp[b.cp[c] as usize];
350            let ori_a = self.co[b.cp[c] as usize];
351            let ori_b = b.co[c];
352            if ori_a < 3 && ori_b < 3 {
353                // two regular cubes
354                ori = ori_a + ori_b;
355                if ori >= 3 {
356                    ori -= 3;
357                }
358            } else if ori_a < 3 && 3 <= ori_b {
359                // cube b is in a mirrored state
360                ori = ori_a + ori_b;
361                if ori >= 6 {
362                    ori -= 3; // the composition also is in a mirrored state
363                }
364            } else if ori_a >= 3 && 3 > ori_b {
365                // cube a is in a mirrored state
366                ori = ori_a - ori_b;
367                if ori < 3 {
368                    ori += 3; // the composition is a mirrored cube
369                }
370            } else if ori_a >= 3 && ori_b >= 3 {
371                // if both cubes are in mirrored states
372                if ori_a >= ori_b {
373                    ori = ori_a - ori_b;
374                } else {
375                    ori = ori_b - ori_a;
376                    ori = 3 - ori; // the composition is a regular cube
377                }
378            }
379            c_ori[c] = ori;
380        }
381        for c in ALL_CORNERS {
382            let ci = c as usize;
383            self.cp[ci] = c_perm[ci];
384            self.co[ci] = c_ori[ci];
385        }
386    }
387
388    /// Multiply this cubie cube with another cubie cube b, restricted to the edges.
389    pub fn edge_multiply(&mut self, b: CubieCube) {
390        let mut e_perm: [Edge; 12] = [UR; 12];
391        let mut e_ori = [0; 12];
392        for ei in ALL_EDGES {
393            let e = ei as usize;
394            e_perm[e] = self.ep[b.ep[e] as usize];
395            e_ori[e] = (b.eo[e] + self.eo[b.ep[e] as usize]) % 2;
396        }
397        for ei in ALL_EDGES {
398            let e = ei as usize;
399            self.ep[e] = e_perm[e];
400            self.eo[e] = e_ori[e];
401        }
402    }
403
404    /// Multiply this cubie cube with another cubie cube b.
405    pub fn multiply(&mut self, b: CubieCube) {
406        self.corner_multiply(b);
407        self.edge_multiply(b);
408    }
409
410    /// Apply single move to this cubie cube.
411    pub fn multiply_move(&mut self, m: Move) {
412        self.multiply(AMCT.amc[m as usize]);
413    }
414
415    /// Apply some moves to this cubie cube.
416    pub fn multiply_moves(&mut self, moves: &Vec<Move>) {
417        moves
418            .iter()
419            .for_each(|&m| self.multiply(AMCT.amc[m as usize]));
420    }
421
422    /// Set the twist of the 8 corners. 0 <= twist < 2187 in phase 1, twist = 0 in phase 2.
423    pub fn set_twist(&mut self, twist: u16) {
424        let mut twistparity = 0;
425        let mut twist = twist;
426        for i in ((URF as usize)..(DRB as usize)).rev() {
427            self.co[i] = (twist % 3) as u8;
428            twistparity += self.co[i];
429            twist /= 3;
430        }
431        self.co[DRB as usize] = (3 - twistparity % 3) % 3;
432    }
433
434    /// Set the flip of the 12 edges. 0 <= flip < 2048 in phase 1, flip = 0 in phase 2.
435    pub fn set_flip(&mut self, flip: u16) {
436        let mut flipparity = 0;
437        let mut flip = flip;
438        for i in ((UR as usize)..(BR as usize)).rev() {
439            self.eo[i] = (flip % 2) as u8;
440            flipparity += self.eo[i];
441            flip /= 2;
442        }
443        self.eo[BR as usize] = (2 - flipparity % 2) % 2;
444    }
445
446    /// Get the location of the UD-slice edges FR,FL,BL and BR ignoring their permutation.
447    ///
448    /// 0<= slice < 495 in phase 1, slice = 0 in phase 2.
449    pub fn get_slice(&self) -> u16 {
450        let mut a = 0;
451        let mut _x = 0;
452        // Compute the index a < (12 choose 4)
453        for j in ((UR as usize)..=(BR as usize)).rev() {
454            if FR <= self.ep[j] && self.ep[j] <= BR {
455                a += c_nk((11 - j) as u32, _x + 1);
456                _x += 1;
457            }
458        }
459        a as u16
460    }
461
462    /// Set the location of the UD-slice edges FR,FL,BL and BR ignoring their permutation.
463    ///
464    /// 0<= slice < 495 in phase 1, slice = 0 in phase 2.
465    pub fn set_slice(&mut self, idx: u16) {
466        let slice_edge = [FR, FL, BL, BR];
467        let other_edge = [UR, UF, UL, UB, DR, DF, DL, DB];
468        let mut a = idx; // Location
469        let mut ep = [-1; 12];
470
471        let mut _x: i32 = 4; // set slice edges
472        for j in ALL_EDGES {
473            if a >= c_nk((11 - j as u32) as u32, _x as u32) as u16 {
474                self.ep[j as usize] = slice_edge[(4 - _x) as usize];
475                ep[j as usize] = slice_edge[(4 - _x) as usize] as i32;
476                a -= c_nk(11 - j as u32, _x as u32) as u16;
477                _x -= 1;
478            }
479        }
480        let mut _x = 0; // set the remaining edges UR..DB
481        for j in ALL_EDGES {
482            if ep[j as usize] == -1 {
483                self.ep[j as usize] = other_edge[_x];
484                _x += 1;
485            }
486        }
487    }
488
489    /// Set the permutation of the 8 corners.
490    ///
491    /// 0 <= corners < 40320 defined but unused in phase 1, 0 <= corners < 40320 in phase 2,
492    ///
493    /// corners = 0 for solved cube
494    pub fn set_corners(&mut self, idx: u16) {
495        self.cp = ALL_CORNERS.clone();
496        let mut idx = idx;
497        for j in ALL_CORNERS {
498            let mut k = idx % (j as u16 + 1);
499            idx /= j as u16 + 1;
500            while k > 0 {
501                rotate_right(&mut self.cp, 0, j as usize);
502                k -= 1;
503            }
504        }
505    }
506
507    /// Generate a random cube. The probability is the same for all possible states.
508    pub fn randomize(&mut self) {
509        // The permutation of the 12 edges. 0 <= idx < 12!."""
510        let mut idx = random::<usize>() % 479001600; // 12!
511        self.cp = ALL_CORNERS.clone();
512        for j in ALL_EDGES {
513            let mut k = idx % (j as usize + 1);
514            idx /= j as usize + 1;
515            while k > 0 {
516                rotate_right(&mut self.ep, 0, j as usize);
517                k -= 1;
518            }
519        }
520        let p = self.edge_parity();
521        loop {
522            self.set_corners(random::<u16>() % 40320); // 8!
523            if p == self.corner_parity() {
524                // parities of edge and corner permutations must be the same
525                break;
526            }
527        }
528        self.set_flip(random::<u16>() % 2048); // 2^11
529        self.set_twist(random::<u16>() % 2187); // 3^7
530    }
531
532    /// Returns the number of corner twist needed to orient the corners.
533    pub fn count_corner_twist(&self) -> u8 {
534        self.co.iter().fold(0, |acc, co| acc + ((3 - co) % 3))
535    }
536
537    /// Returns the number of edge twist needed to orient the edges.
538    pub fn count_edge_twist(&self) -> u8 {
539        self.eo.iter().sum()
540    }
541
542    /// Returns the number of corner permutations needed to solve the corners.
543    pub fn count_corner_perm(&self) -> u8 {
544        let mut count = 0;
545        let mut cp = self.cp;
546
547        for i in 0..8 {
548            if cp[i] as usize != i {
549                if let Some(j) = (i + 1..8).find(|&j| cp[j] as usize == i) {
550                    cp.swap(i, j);
551                    count += 1;
552                }
553            }
554        }
555
556        count
557    }
558
559    /// Returns the number of edge permutations needed to solve the edges.
560    pub fn count_edge_perm(&self) -> u8 {
561        let mut count = 0;
562        let mut ep = self.ep;
563
564        for i in 0..12 {
565            if ep[i] as usize != i {
566                if let Some(j) = (i + 1..12).find(|&j| ep[j] as usize == i) {
567                    ep.swap(i, j);
568                    count += 1;
569                }
570            }
571        }
572
573        count
574    }
575
576    /// Checks if CubieCube is a valid cubie representation.
577    pub fn is_solvable(&self) -> bool {
578        let c_perm = self.count_corner_perm();
579        let e_perm = self.count_edge_perm();
580        let c_twist = self.count_corner_twist();
581        let e_twist = self.count_edge_twist();
582        let has_even_permutation = c_perm % 2 == e_perm % 2;
583        let has_valid_twist = c_twist % 3 == 0 && e_twist % 2 == 0;
584
585        has_even_permutation && has_valid_twist
586    }
587
588    /// Return the inverse of this cubiecube.
589    pub fn inverse_cubie_cube(&self) -> Self {
590        let mut d = CubieCube::default();
591        for ei in ALL_EDGES {
592            let e: usize = ei as usize;
593            d.ep[self.ep[e] as usize] = ei;
594        }
595        for ei in ALL_EDGES {
596            let e: usize = ei as usize;
597            d.eo[e] = self.eo[d.ep[e] as usize];
598        }
599
600        for ci in ALL_CORNERS {
601            let c = ci as usize;
602            d.cp[self.cp[c] as usize] = ci;
603        }
604        for ci in ALL_CORNERS {
605            let c = ci as usize;
606            let ori = self.co[d.cp[c] as usize];
607            if ori >= 3 {
608                d.co[c] = ori;
609            } else {
610                d.co[c] = 3 - ori;
611                if d.co[c] == 3 {
612                    d.co[c] = 0;
613                }
614            }
615        }
616        d
617    }
618
619    /// Give the parity of the corner permutation.
620    pub fn corner_parity(&self) -> bool {
621        let mut s = 0;
622        for i in ((URF as usize + 1)..=(DRB as usize)).rev() {
623            for j in ((URF as usize)..=(i - 1)).rev() {
624                if self.cp[j] > self.cp[i] {
625                    s += 1
626                }
627            }
628        }
629        (s % 2) == 0
630    }
631
632    /// Give the parity of the edge permutation. A solvable cube has the same corner and edge parity.
633    pub fn edge_parity(&self) -> bool {
634        let mut s = 0;
635        for i in ((UR as usize + 1)..=(BR as usize)).rev() {
636            for j in ((UR as usize)..=(i - 1)).rev() {
637                if self.ep[j] > self.ep[i] {
638                    s += 1;
639                }
640            }
641        }
642        (s % 2) == 0
643    }
644
645    /// Check if cubiecube is valid.
646    pub fn verify(&self) -> Result<bool, Error> {
647        let mut edge_count = [0; 12];
648        for i in ALL_EDGES {
649            edge_count[self.ep[i as usize] as usize] += 1;
650        }
651        for i in ALL_EDGES {
652            if edge_count[i as usize] != 1 {
653                return Err(Error::InvalidEdge);
654            }
655        }
656        let mut s = 0;
657        for i in ALL_EDGES {
658            s += self.eo[i as usize];
659        }
660        if s % 2 != 0 {
661            return Err(Error::InvalidEdge);
662        }
663
664        let mut corner_count = [0; 8];
665        for i in ALL_CORNERS {
666            corner_count[self.cp[i as usize] as usize] += 1;
667        }
668        for i in ALL_CORNERS {
669            if corner_count[i as usize] != 1 {
670                return Err(Error::InvalidCorner);
671            }
672        }
673        let mut s = 0;
674        for i in ALL_CORNERS {
675            s += self.co[i as usize];
676        }
677        if s % 3 != 0 {
678            return Err(Error::InvalidCorner);
679        }
680
681        if self.edge_parity() != self.corner_parity() {
682            return Err(Error::InvalidCubieValue);
683        }
684        Ok(true)
685    }
686
687    pub fn get_edges_d(&self) -> Vec<(Edge, u8, u8)> {
688        let mut i: u8 = 0;
689        let mut result = Vec::new();
690        for e in self.ep {
691            match e {
692                DR | DF | DL | DB => result.push((e, i, self.eo[i as usize])),
693                _ => {}
694            }
695            i += 1;
696        }
697        result
698    }
699}
700
701// struct BasicMoveCubeTables {
702//     bsc: [CubieCube; 18],
703// }
704// impl BasicMoveCubeTables {
705//     pub fn new() -> Self {
706//         let mut bsc = [CubieCube::default(); 18];
707//         for i in 0..18 {
708//             bsc[i] = bsc[i].apply_move(ALL_MOVES[i]);
709//         }
710//         Self { bsc }
711//     }
712// }
713
714// /// 18 basic move cube tables.
715// /// [U, U2, U3, R, R2, R3, F, F2, F3, D, D2, D3, L, L2, L3, B, B2, B3]
716// #[dynamic]
717// static BSCT: BasicMoveCubeTables = BasicMoveCubeTables::new();
718
719struct AllMoveCubeTables {
720    amc: [CubieCube; 55],
721}
722impl AllMoveCubeTables {
723    pub fn new() -> Self {
724        let mut amc = [CubieCube::default(); 55];
725        for i in 0..55 {
726            amc[i] = amc[i].apply_move(ALL_MOVES_FULL[i]);
727        }
728        Self { amc }
729    }
730}
731
732/// All move cube tables.
733/// [U, U2, U3, R, R2, R3, F, F2, F3, D, D2, D3, L, L2, L3, B, B2, B3, M, M2, M3, E, E2, E3, S, S2, S3, Uw, Uw2, Uw3, Rw, Rw2, Rw3, Fw, Fw2, Fw3, Dw, Dw2, Dw3, Lw, Lw2, Lw3, Bw, Bw2, Bw3, x, x2, x3, y, y2, y3, z, z2, z3, N]
734#[dynamic]
735static AMCT: AllMoveCubeTables = AllMoveCubeTables::new();
736
737/// Rotate array arr right between left and right. right is included.
738pub fn rotate_right<T: Copy>(arr: &mut [T], left: usize, right: usize) {
739    let temp = arr[right];
740    for i in (left + 1..=right).rev() {
741        arr[i] = arr[i - 1];
742    }
743    arr[left] = temp;
744}
745
746/// Rotate array arr left between left and right. right is included.
747pub fn rotate_left<T: Copy>(arr: &mut [T], left: usize, right: usize) {
748    let temp = arr[left];
749    for i in left..right {
750        arr[i] = arr[i + 1];
751    }
752    arr[right] = temp;
753}
754
755/// Binomial coefficient [n choose k].
756pub fn c_nk(n: u32, k: u32) -> u32 {
757    let mut k = k;
758    if n < k {
759        return 0;
760    }
761    if k > (n / 2) {
762        k = n - k;
763    }
764    let mut s = 1;
765    let mut i = n;
766    let mut j = 1;
767    while i != n - k {
768        s *= i;
769        s /= j;
770        i -= 1;
771        j += 1;
772    }
773    s
774}
775
776#[cfg(test)]
777mod tests {
778    use crate::cubie::*;
779    #[cfg(feature = "term")]
780    use crate::printer::print_facelet;
781
782    #[test]
783    fn test_eq() {
784        let state = CubieCube::default();
785        let state2 = CubieCube::default();
786        assert_eq!(state, state2);
787    }
788
789    #[test]
790    fn test_inverse() {
791        let state = CubieCube {
792            center: [Color::U, Color::R, Color::F, Color::D, Color::L, Color::B],
793            cp: [DLF, ULB, DBL, DRB, UBR, UFL, DFR, URF],
794            co: [2, 1, 2, 1, 2, 2, 0, 2],
795            ep: [BR, BL, UB, UR, DR, FR, FL, UF, DF, DL, DB, UL],
796            eo: [1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1],
797        };
798        let ic = state.inverse_cubie_cube();
799        let d = CubieCube {
800            center: [Color::U, Color::R, Color::F, Color::D, Color::L, Color::B],
801            cp: [DRB, DLF, UFL, DFR, DBL, URF, ULB, UBR],
802            co: [1, 1, 2, 1, 0, 1, 1, 2],
803            ep: [UB, DB, BR, UL, DR, FR, FL, BL, DF, DL, UF, UR],
804            eo: [0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1],
805        };
806        assert_eq!(ic, d);
807        let d2 = ic.inverse_cubie_cube();
808        assert_eq!(state, d2);
809    }
810
811    #[test]
812    fn test_parity() {
813        let state = CubieCube::default();
814
815        assert_eq!(state.corner_parity(), true);
816        assert_eq!(state.edge_parity(), true);
817
818        let state = CubieCube::from(&vec![R, U, R3, U3, R3, F, R, F3]);
819
820        assert_eq!(state.corner_parity(), true);
821        assert_eq!(state.edge_parity(), true);
822    }
823
824    #[test]
825    fn test_mult() {
826        let state = CubieCube::default().apply_move(R);
827        assert_eq!(state, R_MOVE);
828
829        let r2_state = CubieCube::default().apply_move(R).apply_move(R);
830        assert_eq!(r2_state, R_MOVE * R_MOVE);
831
832        let r3_state = r2_state.apply_move(R);
833        assert_eq!(r3_state, r2_state * R_MOVE);
834
835        let fr_state = CubieCube {
836            center: [Color::U, Color::R, Color::F, Color::D, Color::L, Color::B],
837            //URF, UFL, ULB, UBR, DFR, DLF, DBL, DRB,
838            cp: [URF, DLF, ULB, UFL, DRB, DFR, DBL, UBR],
839            co: [1, 2, 0, 2, 1, 1, 0, 2],
840            ep: [UF, FL, UL, UB, BR, FR, DL, DB, DR, DF, BL, UR],
841            eo: [1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
842        };
843
844        assert_eq!(F_MOVE * R_MOVE, fr_state);
845    }
846
847    #[test]
848    fn test_move_mes() {
849        let moves: Vec<Move> = vec![M2, E2, S2];
850        let state = CubieCube::default().apply_moves(&moves);
851        #[cfg(feature = "term")]
852        let fc = FaceCube::try_from(&state).unwrap();
853        #[cfg(feature = "term")]
854        let _ = print_facelet(&fc);
855        assert_eq!(state, M_MOVE * M_MOVE * E_MOVE * E_MOVE * S_MOVE * S_MOVE);
856    }
857
858    #[test]
859    fn test_moves() {
860        let empty_move = Vec::new();
861        let cc = CubieCube::default();
862        let cc = cc.apply_moves(&empty_move);
863        assert_eq!(cc, CubieCube::default());
864        let moves = vec![R, U, R3, U3, M, S, E, M, x, R, U, R3, U3, L, F, R, y, z];
865        let _state = CubieCube::default().apply_moves(&moves);
866        #[cfg(feature = "term")]
867        let _fc = FaceCube::try_from(&_state).unwrap();
868        #[cfg(feature = "term")]
869        let _ = print_facelet(&_fc);
870    }
871
872    #[test]
873    fn test_move_n() {
874        let cc = CubieCube::default();
875        let cc = cc.apply_move(N);
876        assert_eq!(cc, CubieCube::default());
877        let moves = vec![R, U, R3, U3];
878        let cc = cc.apply_moves(&moves);
879        let nc = cc.apply_move(N);
880        assert_eq!(nc, cc);
881        let moves = vec![R, U, R3, U3, N, R, U, R3];
882        let cc = CubieCube::default();
883        let ncc = cc.apply_moves(&moves);
884        let moves = vec![R, U, R3, U3, R, U, R3];
885        let cc = cc.apply_moves(&moves);
886        assert_eq!(ncc, cc);
887    }
888
889    #[test]
890    fn test_move_sequence() {
891        // (R U R' U') * 6
892        let moves = vec![
893            R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, R, U, R3, U3, R, U, R3, U3,
894        ];
895        let state = CubieCube::default().apply_moves(&moves);
896
897        assert_eq!(state, SOLVED_CUBIE_CUBE);
898    }
899
900    #[test]
901    fn test_scramble() {
902        // U F' D' F2 D B2 D' R2 U' F2 R2 D2 R2 U' L B L R F' D B'
903        let scramble = vec![
904            U, F3, D3, F2, D, B2, D3, R2, U3, F2, R2, D2, R2, U3, L, B, L, R, F3, D, B3,
905        ];
906        let state = CubieCube::default().apply_moves(&scramble);
907
908        let expected = CubieCube {
909            center: [Color::U, Color::R, Color::F, Color::D, Color::L, Color::B],
910            cp: [DFR, UBR, DLF, ULB, DRB, UFL, URF, DBL],
911            co: [2, 0, 1, 2, 0, 0, 2, 2],
912            ep: [DF, UB, FL, BL, BR, UL, DR, FR, DL, DB, UF, UR],
913            eo: [1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1],
914        };
915
916        assert_eq!(state, expected);
917    }
918
919    #[test]
920    fn test_get_edges_d() {
921        let cc = CubieCube::default();
922        let cc = cc.apply_moves(&vec![R, U, R3, U3, F, L]);
923        let d_edges = cc.get_edges_d();
924        println!("{:?}", d_edges);
925    }
926
927    #[test]
928    fn test_perm_count() {
929        let state = CubieCube::default();
930
931        assert_eq!(state.count_corner_perm(), 0);
932        assert_eq!(state.count_edge_perm(), 0);
933
934        let state = CubieCube::from(&vec![R, U, R3, U3]);
935
936        assert_eq!(state.count_corner_perm(), 2);
937        assert_eq!(state.count_edge_perm(), 2);
938
939        let state = CubieCube::from(&vec![
940            R, U3, R3, U3, R, U, R, D, R3, U3, R, D3, R3, U2, R3, U3,
941        ]);
942
943        assert_eq!(state.count_corner_perm(), 1);
944        assert_eq!(state.count_edge_perm(), 1);
945    }
946
947    #[test]
948    fn test_twist_count() {
949        let state = CubieCube::default();
950
951        assert_eq!(state.count_corner_twist(), 0);
952        assert_eq!(state.count_edge_twist(), 0);
953
954        let state = CubieCube::from(&vec![R, U, R3, U3, R3, F, R, F3]);
955
956        assert_eq!(state.count_corner_twist(), 3);
957        assert_eq!(state.count_edge_twist(), 2);
958    }
959}