use crate::constants::*; use crate::cubie::move_cubes; use crate::cubie::{Corner::*, CubieCube, Edge::*}; use crate::error::Error; use crate::{decode_table, write_table}; #[allow(non_camel_case_types)] enum BS { ROT_URF3, ROT_F2, ROT_U4, MIRR_LR2, } /// 120° clockwise rotation around the long diagonal URF-DBL const CC_ROT_URF3: CubieCube = CubieCube { cp: [URF, DFR, DLF, UFL, UBR, DRB, DBL, ULB], co: [1, 2, 1, 2, 2, 1, 2, 1], ep: [UF, FR, DF, FL, UB, BR, DB, BL, UR, DR, DL, UL], eo: [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1], }; /// 180° rotation around the axis through the F and B centers const CC_ROT_F2: CubieCube = CubieCube { cp: [DLF, DFR, DRB, DBL, UFL, URF, UBR, ULB], co: [0, 0, 0, 0, 0, 0, 0, 0], ep: [DL, DF, DR, DB, UL, UF, UR, UB, FL, FR, BR, BL], eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], }; /// 90° clockwise rotation around the axis through the U and D centers const CC_ROT_U4: CubieCube = CubieCube { cp: [UBR, URF, UFL, ULB, DRB, DFR, DLF, DBL], co: [0, 0, 0, 0, 0, 0, 0, 0], ep: [UB, UR, UF, UL, DB, DR, DF, DL, BR, FR, FL, BL], eo: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], }; /// reflection at the plane through the U, D, F, B centers const CC_MIRR_LR2: CubieCube = CubieCube { cp: [UFL, URF, UBR, ULB, DLF, DFR, DRB, DBL], co: [3, 3, 3, 3, 3, 3, 3, 3], ep: [UL, UF, UR, UB, DL, DF, DR, DB, FL, FR, BR, BL], eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], }; /// Tables to store symmetries. #[derive(Debug, Clone)] pub struct SymmetriesTables { pub bsc: [CubieCube; 4], pub sc: [CubieCube; 48], pub inv_idx: [u8; 48], pub mult_sym: Vec, pub conj_move: Vec, pub twist_conj: Vec, pub ud_edges_conj: Vec, pub flipslice_classidx: Vec, pub flipslice_sym: Vec, pub flipslice_rep: Vec, pub corner_classidx: Vec, pub corner_sym: Vec, pub corner_rep: Vec, } impl SymmetriesTables { pub fn new() -> Self { let flipslice = flipslice_syms().unwrap(); let cornersyms = corner_syms().unwrap(); Self { bsc: basicsc(), sc: sc(), inv_idx: inv_idx(), mult_sym: mult_sym(), conj_move: conj_move(), twist_conj: conj_twist().unwrap(), ud_edges_conj: conj_ud_edges().unwrap(), flipslice_classidx: flipslice.classidx, flipslice_sym: flipslice.sym, flipslice_rep: flipslice.rep, corner_classidx: cornersyms.classidx, corner_sym: cornersyms.sym, corner_rep: cornersyms.rep, } } } /// Permutations and orientation changes of the basic symmetries fn basicsc() -> [CubieCube; 4] { let mut bsc = [CubieCube::default(); 4]; bsc[BS::ROT_URF3 as usize] = CC_ROT_URF3.clone(); bsc[BS::ROT_F2 as usize] = CC_ROT_F2.clone(); bsc[BS::ROT_U4 as usize] = CC_ROT_U4.clone(); bsc[BS::MIRR_LR2 as usize] = CC_MIRR_LR2.clone(); bsc } /// Basic symmetries of the cube. All 48 cube symmetries can be generated by sequences of these 4 symmetries. /// /// sc list, 48 CubieCubes will represent the 48 cube symmetries pub fn sc() -> [CubieCube; 48] { let bsc = basicsc(); let mut sc = [CubieCube::default(); 48]; let mut cc = CubieCube::default(); // Identity cube let mut idx = 0; for _urf3 in 0..3 { for _f2 in 0..2 { for _u4 in 0..4 { for _lr2 in 0..2 { sc[idx] = CubieCube { cp: cc.cp, co: cc.co, ep: cc.ep, eo: cc.eo, }; idx += 1; cc.multiply(bsc[BS::MIRR_LR2 as usize]); } cc.multiply(bsc[BS::ROT_U4 as usize]); } cc.multiply(bsc[BS::ROT_F2 as usize]); } cc.multiply(bsc[BS::ROT_URF3 as usize]); } sc } /// Fill the inv_idx array. /// /// Indices for the inverse symmetries: sc[inv_idx[idx]] == sc[idx]^(-1) pub fn inv_idx() -> [u8; 48] { let mut inv_idx_arr: [u8; 48] = [0; N_SYM]; let sc = sc(); for j in 0..N_SYM { for i in 0..N_SYM { let mut cc = CubieCube { cp: sc[j].cp, co: sc[j].co, ep: sc[j].ep, eo: sc[j].eo, }; cc.corner_multiply(sc[i]); if cc.cp[URF as usize] == URF && cc.cp[UFL as usize] == UFL && cc.cp[ULB as usize] == ULB { inv_idx_arr[j] = i as u8; break; } } } inv_idx_arr } /// Generate the group table for the 48 cube symmetries. fn mult_sym() -> Vec { let mut sym_mult = vec![0; N_SYM * N_SYM]; let sc = sc(); for i in 0..N_SYM { for j in 0..N_SYM { let mut cc = CubieCube { cp: sc[i].cp, co: sc[i].co, ep: sc[i].ep, eo: sc[i].eo, }; cc.multiply(sc[j]); for k in 0..N_SYM { if cc == sc[k] { // sc[i]*sc[j] == sc[k]; sym_mult[N_SYM * i + j] = k; break; } } } } sym_mult } /// Generate the table for the conjugation of a move m by a symmetry s. conj_move[N_MOVE*s + m] = s*m*s^-1 fn conj_move() -> Vec { let sc = sc(); let mc = move_cubes(); let inv_idx = inv_idx(); let mut move_conj = vec![0; N_MOVE * N_SYM]; for s in 0..N_SYM { for m in ALL_MOVES { let mut ss = CubieCube { cp: sc[s].cp, co: sc[s].co, ep: sc[s].ep, eo: sc[s].eo, }; // copy cube ss.multiply(mc[m as usize]); // s*m ss.multiply(sc[inv_idx[s] as usize]); // s*m*s^-1 for m2 in ALL_MOVES { if ss == mc[m2 as usize] { move_conj[N_MOVE * s + m as usize] = m2 as usize; } } } } move_conj } /// Generate the phase 1 table for the conjugation of the twist t by a symmetry s. twist_conj[t, s] = s*t*s^-1 #### fn conj_twist() -> Result, Error> { let sc = sc(); let inv_idx = inv_idx(); std::fs::create_dir_all("tables")?; let fname = "tables/conj_twist"; let conj_table = std::fs::read(&fname).unwrap_or("".into()); let mut twist_conj = vec![0; N_TWIST * N_SYM_D4H]; if conj_table.is_empty() { println!("On the first run, several tables will be created. This may take a few minutes."); println!( "All tables are stored in tables/{}.\n\nCreating {} table...\n", fname, fname ); for t in 0..N_TWIST { let mut cc = CubieCube::default(); cc.set_twist(t as u16); for s in 0..N_SYM_D4H { let mut ss = CubieCube { cp: sc[s].cp, co: sc[s].co, ep: sc[s].ep, eo: sc[s].eo, }; // copy cube ss.corner_multiply(cc); // s*t ss.corner_multiply(sc[inv_idx[s] as usize]); // s*t*s^-1 twist_conj[N_SYM_D4H * t + s] = ss.get_twist(); } } write_table(fname, &twist_conj)?; } else { // println!("Loading {} table...", &fname); twist_conj = decode_table(&conj_table)?; } Ok(twist_conj) } /// Generate the phase 2 table for the conjugation of the URtoDB coordinate by a symmetrie. fn conj_ud_edges() -> Result, Error> { let sc = sc(); let inv_idx = inv_idx(); std::fs::create_dir_all("tables")?; let fname = "tables/conj_ud_edges"; let conj_table = std::fs::read(&fname).unwrap_or("".into()); let mut ud_edges_conj = vec![0; N_UD_EDGES * N_SYM_D4H]; if conj_table.is_empty() { println!("Creating {} table...", fname); for t in 0..N_UD_EDGES { if (t + 1) % 400 == 0 { print!(""); } if (t + 1) % 32000 == 0 { println!(); } let mut cc = CubieCube::default(); cc.set_ud_edges(t); for s in 0..N_SYM_D4H { let mut ss = CubieCube { cp: sc[s].cp, co: sc[s].co, ep: sc[s].ep, eo: sc[s].eo, }; // copy cube ss.edge_multiply(cc); // s*t ss.edge_multiply(sc[inv_idx[s] as usize]); // s*t*s^-1 ud_edges_conj[N_SYM_D4H * t + s] = ss.get_ud_edges(); } } println!(); write_table(fname, &ud_edges_conj)?; } else { // println!("Loading {} table...", &fname); ud_edges_conj = decode_table(&conj_table)?; } Ok(ud_edges_conj) } /// The tables to handle the symmetry reduced flip-slice coordinate in phase 1. pub struct FlipSliceSyms { pub classidx: Vec, pub sym: Vec, pub rep: Vec, } /// Generate the tables to handle the symmetry reduced flip-slice coordinate in phase 1. pub fn flipslice_syms() -> Result { let sc = sc(); let inv_idx = inv_idx(); std::fs::create_dir_all("tables")?; let fname1 = "tables/fs_classidx"; let fname2 = "tables/fs_sym"; let fname3 = "tables/fs_rep"; let classidx_table = std::fs::read(&fname1).unwrap_or("".into()); let sym_table = std::fs::read(&fname2).unwrap_or("".into()); let rep_table = std::fs::read(&fname3).unwrap_or("".into()); let mut flipslice_classidx = vec![65535; N_FLIP * N_SLICE]; // idx -> classidx let mut flipslice_sym = vec![0; N_FLIP * N_SLICE]; // idx -> symmetry let mut flipslice_rep = vec![0; N_FLIPSLICE_CLASS]; // classidx -> idx of representant if classidx_table.is_empty() { println!("Creating flipslice sym-tables..."); let mut classidx = 0; let mut cc = CubieCube::default(); for slc in 0..N_SLICE { cc.set_slice(slc as u16); for flip in 0..N_FLIP { cc.set_flip(flip as u16); let idx = N_FLIP * slc + flip; if (idx + 1) % 4000 == 0 { print!("."); } if (idx + 1) % 320000 == 0 { println!(); } if flipslice_classidx[idx] == 65535 { flipslice_classidx[idx] = classidx; flipslice_sym[idx] = 0; flipslice_rep[classidx as usize] = idx as u32; } else { continue; } for s in 0..N_SYM_D4H { // conjugate representant by all 16 symmetries let si = inv_idx[s] as usize; let mut ss = CubieCube { cp: sc[si].cp, co: sc[si].co, ep: sc[si].ep, eo: sc[si].eo, }; // copy cube ss.edge_multiply(cc); ss.edge_multiply(sc[s]); // s^-1*cc*s let idx_new = N_FLIP * ss.get_slice() as usize + ss.get_flip() as usize; if flipslice_classidx[idx_new] == 65535 { flipslice_classidx[idx_new] = classidx; flipslice_sym[idx_new] = s as u8; } } classidx += 1; } } println!(); write_table(fname1, &flipslice_classidx)?; write_table(fname2, &flipslice_sym)?; write_table(fname3, &flipslice_rep)?; } else { // println!("Loading flipslice sym-tables..."); flipslice_classidx = decode_table(&classidx_table)?; flipslice_sym = decode_table(&sym_table)?; flipslice_rep = decode_table(&rep_table)?; } Ok(FlipSliceSyms { classidx: flipslice_classidx, sym: flipslice_sym, rep: flipslice_rep, }) } /// The tables to handle the symmetry reduced corner permutation coordinate in phase 2. pub struct CornerSyms { pub classidx: Vec, pub sym: Vec, pub rep: Vec, } /// Generate the tables to handle the symmetry reduced corner permutation coordinate in phase 2. pub fn corner_syms() -> Result { let sc = sc(); let inv_idx = inv_idx(); std::fs::create_dir_all("tables")?; let fname1 = "tables/co_classidx"; let fname2 = "tables/co_sym"; let fname3 = "tables/co_rep"; let classidx_table = std::fs::read(&fname1).unwrap_or("".into()); let sym_table = std::fs::read(&fname2).unwrap_or("".into()); let rep_table = std::fs::read(&fname3).unwrap_or("".into()); let mut corner_classidx = vec![65535; N_CORNERS]; // idx -> classidx let mut corner_sym = vec![0; N_CORNERS]; // idx -> symmetry let mut corner_rep = vec![0; N_CORNERS_CLASS]; // classidx -> idx of representant if classidx_table.is_empty() { println!("Creating corner sym-tables..."); let mut classidx = 0; let mut cc = CubieCube::default(); for cp in 0..N_CORNERS { cc.set_corners(cp as u16); if (cp + 1) % 8000 == 0 { print!("."); } if corner_classidx[cp] == 65535 { corner_classidx[cp] = classidx; corner_sym[cp] = 0; corner_rep[classidx as usize] = cp as u16; } else { continue; } for s in 0..N_SYM_D4H { let si = inv_idx[s] as usize; // conjugate representant by all 16 symmetries let mut ss = CubieCube { cp: sc[si].cp, co: sc[si].co, ep: sc[si].ep, eo: sc[si].eo, }; // copy cube ss.corner_multiply(cc); ss.corner_multiply(sc[s]); // s^-1*cc*s let cp_new = ss.get_corners(); if corner_classidx[cp_new as usize] == 65535 { corner_classidx[cp_new as usize] = classidx; corner_sym[cp_new as usize] = s as u8; } } classidx += 1; } println!(); write_table(fname1, &corner_classidx)?; write_table(fname2, &corner_sym)?; write_table(fname3, &corner_rep)?; } else { // println!("Loading corner sym-tables..."); corner_classidx = decode_table(&classidx_table)?; corner_sym = decode_table(&sym_table)?; corner_rep = decode_table(&rep_table)?; } Ok(CornerSyms { classidx: corner_classidx, sym: corner_sym, rep: corner_rep, }) } #[cfg(test)] mod test { use crate::symmetries::*; #[test] fn test_symcube() { let bsc = basicsc(); let bsc2 = CubieCube { cp: [UBR, URF, UFL, ULB, DRB, DFR, DLF, DBL], co: [0, 0, 0, 0, 0, 0, 0, 0], ep: [UB, UR, UF, UL, DB, DR, DF, DL, BR, FR, FL, BL], eo: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], }; assert_eq!(bsc[2], bsc2); let sc = sc(); let sc22 = CubieCube { cp: [DFR, DLF, UFL, URF, DRB, DBL, ULB, UBR], co: [2, 1, 2, 1, 1, 2, 1, 2], ep: [FR, DF, FL, UF, BR, DB, BL, UB, DR, DL, UL, UR], eo: [0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0], }; assert_eq!(sc[22], sc22); } #[test] fn test_inv_idx() { let inv_idx = inv_idx(); let inv_idx_py: [u8; 48] = [ 0, 1, 6, 3, 4, 5, 2, 7, 8, 9, 10, 15, 12, 13, 14, 11, 32, 41, 18, 19, 40, 33, 30, 31, 44, 37, 26, 27, 36, 45, 22, 23, 16, 21, 46, 35, 28, 25, 38, 43, 20, 17, 42, 39, 24, 29, 34, 47, ]; assert_eq!(inv_idx, inv_idx_py); } #[test] fn test_mult_sym() { let mult_sym = mult_sym(); assert_eq!(mult_sym[344], 11); } #[test] fn test_conj_move() { let conj_move = conj_move(); assert_eq!(conj_move[863], 15); } #[test] fn test_conj_twist() { let conj_twist_table = conj_twist().unwrap(); assert_eq!(conj_twist_table.len(), 34992); assert_eq!(conj_twist_table[34991], 1174); assert_eq!(conj_twist_table[349], 135); assert_eq!(conj_twist_table[34], 7); assert_eq!(conj_twist_table[3], 0); } #[test] fn test_conj_ud_edges() { let conj_ud_edges = conj_ud_edges().unwrap(); assert_eq!(conj_ud_edges.len(), 645120); assert_eq!(conj_ud_edges[645119], 19857); assert_eq!(conj_ud_edges[64511], 29351); assert_eq!(conj_ud_edges[645], 31); assert_eq!(conj_ud_edges[0], 0); } #[test] fn test_flipslice_syms() { let flipslice_syms = flipslice_syms().unwrap(); assert_eq!(flipslice_syms.classidx.len(), 1013760); assert_eq!(flipslice_syms.sym.len(), 1013760); assert_eq!(flipslice_syms.rep.len(), 64430); assert_eq!(flipslice_syms.classidx[12345], 2315); assert_eq!(flipslice_syms.sym[12345], 6); assert_eq!(flipslice_syms.rep[12345], 42257); } #[test] fn test_corner_syms() { let cornersyms = corner_syms().unwrap(); assert_eq!(cornersyms.classidx.len(), 40320); assert_eq!(cornersyms.sym.len(), 40320); assert_eq!(cornersyms.rep.len(), 2768); assert_eq!(cornersyms.classidx[12345], 1831); assert_eq!(cornersyms.sym[12345], 8); assert_eq!(cornersyms.rep[1234], 2335); } }