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,
}
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],
};
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],
};
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],
};
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],
};
#[derive(Debug, Clone)]
pub struct SymmetriesTables {
pub bsc: [CubieCube; 4],
pub sc: [CubieCube; 48],
pub inv_idx: [u8; 48],
pub mult_sym: Vec<usize>,
pub conj_move: Vec<usize>,
pub twist_conj: Vec<u16>,
pub ud_edges_conj: Vec<u16>,
pub flipslice_classidx: Vec<u16>,
pub flipslice_sym: Vec<u8>,
pub flipslice_rep: Vec<u32>,
pub corner_classidx: Vec<u16>,
pub corner_sym: Vec<u8>,
pub corner_rep: Vec<u16>,
}
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,
}
}
}
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
}
pub fn sc() -> [CubieCube; 48] {
let bsc = basicsc();
let mut sc = [CubieCube::default(); 48];
let mut cc = CubieCube::default(); 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
}
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
}
fn mult_sym() -> Vec<usize> {
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] {
sym_mult[N_SYM * i + j] = k;
break;
}
}
}
}
sym_mult
}
fn conj_move() -> Vec<usize> {
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,
}; ss.multiply(mc[m as usize]); ss.multiply(sc[inv_idx[s] as usize]); for m2 in ALL_MOVES {
if ss == mc[m2 as usize] {
move_conj[N_MOVE * s + m as usize] = m2 as usize;
}
}
}
}
move_conj
}
fn conj_twist() -> Result<Vec<u16>, 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,
}; ss.corner_multiply(cc); ss.corner_multiply(sc[inv_idx[s] as usize]); twist_conj[N_SYM_D4H * t + s] = ss.get_twist();
}
}
write_table(fname, &twist_conj)?;
} else {
twist_conj = decode_table(&conj_table)?;
}
Ok(twist_conj)
}
fn conj_ud_edges() -> Result<Vec<u16>, 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,
}; ss.edge_multiply(cc); ss.edge_multiply(sc[inv_idx[s] as usize]); ud_edges_conj[N_SYM_D4H * t + s] = ss.get_ud_edges();
}
}
println!();
write_table(fname, &ud_edges_conj)?;
} else {
ud_edges_conj = decode_table(&conj_table)?;
}
Ok(ud_edges_conj)
}
pub struct FlipSliceSyms {
pub classidx: Vec<u16>,
pub sym: Vec<u8>,
pub rep: Vec<u32>,
}
pub fn flipslice_syms() -> Result<FlipSliceSyms, Error> {
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]; let mut flipslice_sym = vec![0; N_FLIP * N_SLICE]; let mut flipslice_rep = vec![0; N_FLIPSLICE_CLASS]; 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 {
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,
}; ss.edge_multiply(cc);
ss.edge_multiply(sc[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 {
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,
})
}
pub struct CornerSyms {
pub classidx: Vec<u16>,
pub sym: Vec<u8>,
pub rep: Vec<u16>,
}
pub fn corner_syms() -> Result<CornerSyms, Error> {
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]; let mut corner_sym = vec![0; N_CORNERS]; let mut corner_rep = vec![0; N_CORNERS_CLASS]; 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;
let mut ss = CubieCube {
cp: sc[si].cp,
co: sc[si].co,
ep: sc[si].ep,
eo: sc[si].eo,
}; ss.corner_multiply(cc);
ss.corner_multiply(sc[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 {
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);
}
}