Skip to main content

min2phase/
lib.rs

1#[macro_use(lazy_static)]
2extern crate lazy_static;
3
4use rand::Rng;
5
6#[derive(Clone, Copy)]
7struct Cubie {
8	ca: [u8; 8],
9	ea: [u8; 12],
10}
11
12#[derive(Clone, Copy)]
13struct Coord {
14	twst: u16,
15	tsym: u16,
16	flip: u16,
17	fsym: u16,
18	slice: u16,
19	prun: i8,
20}
21
22#[derive(Clone, Copy)]
23struct Coord2 {
24	edge: u16,
25	esym: u16,
26	corn: u16,
27	csym: u16,
28	mid: u16
29}
30
31#[repr(C)]
32struct Solution {
33	depth1: i8,
34	verbose: u8,
35	urf_idx: u8,
36	premv_len: i8,
37	length: i8,
38	moves: [u8; 31],
39}
40
41const INVERSE_SOLUTION: u8 = 0x01;
42const USE_SEPARATOR: u8 = 0x02;
43const APPEND_LENGTH: u8 = 0x04;
44const MAX_PREMV_LEN: i8 = 20;
45const MIN_P1PRE_LEN: i8 = 7;
46
47const N_FLIP     : usize =  2048;
48const N_FLIP_SYM : usize =   336;
49const N_TWST     : usize =  2187;
50const N_TWST_SYM : usize =   324;
51const N_SLICE    : usize =   495;
52const N_PERM     : usize = 40320;
53const N_PERM_SYM : usize =  2768;
54const N_MPERM    : usize =    24;
55const N_CCOMB    : usize =    70;
56
57const N_MOVES_P1 : usize =    18;
58const N_MOVES_P2 : usize =    10;
59const MAX_DEPTH2 : usize =    13;
60
61static MOVE2STR: [&str; 18] = ["U ", "U2", "U'", "R ", "R2", "R'", "F ", "F2", "F'", "D ", "D2", "D'", "L ", "L2", "L'", "B ", "B2", "B'"];
62static URF_MOVE: [[u8; 18]; 6] = [
63	[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
64	[6, 7, 8, 0, 1, 2, 3, 4, 5, 15, 16, 17, 9, 10, 11, 12, 13, 14],
65	[3, 4, 5, 6, 7, 8, 0, 1, 2, 12, 13, 14, 15, 16, 17, 9, 10, 11],
66	[2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12, 17, 16, 15],
67	[8, 7, 6, 2, 1, 0, 5, 4, 3, 17, 16, 15, 11, 10, 9, 14, 13, 12],
68	[5, 4, 3, 8, 7, 6, 2, 1, 0, 14, 13, 12, 17, 16, 15, 11, 10, 9]
69];
70
71const U: u8 = 0;
72const R: u8 = 9;
73const F: u8 = 18;
74const D: u8 = 27;
75const L: u8 = 36;
76const B: u8 = 45;
77
78static CORNER_FACELET: [[u8; 3]; 8] = [
79	[ U + 8, R + 0, F + 2 ], [ U + 6, F + 0, L + 2 ], [ U + 0, L + 0, B + 2 ], [ U + 2, B + 0, R + 2 ],
80	[ D + 2, F + 8, R + 6 ], [ D + 0, L + 8, F + 6 ], [ D + 6, B + 8, L + 6 ], [ D + 8, R + 8, B + 6 ]
81];
82
83static EDGE_FACELET: [[u8; 2]; 12] = [
84	[ U + 5, R + 1 ], [ U + 7, F + 1 ], [ U + 3, L + 1 ], [ U + 1, B + 1 ], [ D + 5, R + 7 ], [ D + 1, F + 7 ],
85	[ D + 3, L + 7 ], [ D + 7, B + 7 ], [ F + 5, R + 3 ], [ F + 3, L + 5 ], [ B + 5, L + 3 ], [ B + 3, R + 5 ]
86];
87
88
89static P2MOVES: [u8; 18] = [0, 1, 2, 4, 7, 9, 10, 11, 13, 16, 3, 5, 6, 8, 12, 14, 15, 17];
90
91impl Solution {
92	fn append_move(&mut self, cur_move: u8) {
93		if self.length == 0 {
94			self.moves[self.length as usize] = cur_move;
95			self.length += 1;
96			return;
97		}
98
99		let cur_axis = cur_move / 3;
100		let last_axis = self.moves[(self.length - 1) as usize] / 3;
101
102		if cur_axis == last_axis {
103			let pow = (cur_move % 3 + self.moves[(self.length - 1) as usize] % 3 + 1) % 4;
104			if pow == 3 {
105				self.length -= 1;
106			} else {
107				self.moves[(self.length - 1) as usize] = cur_axis * 3 + pow;
108			}
109			return;
110		}
111
112		if self.length > 1
113			&& cur_axis % 3 == last_axis % 3
114			&& cur_axis == self.moves[(self.length - 2) as usize] / 3
115		{
116			let pow = (cur_move % 3 + self.moves[(self.length - 2) as usize] % 3 + 1) % 4;
117			if pow == 3 {
118				self.moves[(self.length - 2) as usize] = self.moves[(self.length - 1) as usize];
119				self.length -= 1;
120			} else {
121				self.moves[(self.length - 2) as usize] = cur_axis * 3 + pow;
122			}
123			return;
124		}
125
126		self.moves[self.length as usize] = cur_move;
127		self.length += 1;
128	}
129
130	fn to_string(&self) -> String {
131		let mut buf = String::new();
132		let urf = if self.verbose & INVERSE_SOLUTION != 0 {
133			(self.urf_idx + 3) % 6
134		} else {
135			self.urf_idx
136		};
137
138		if urf < 3 {
139			for (s, &mv) in self.moves.iter().enumerate().take(self.length as usize) {
140				if self.verbose & USE_SEPARATOR != 0 && s == self.depth1 as usize {
141					buf.push_str(".  ");
142				}
143				buf.push_str(MOVE2STR[URF_MOVE[urf as usize][mv as usize] as usize]);
144				buf.push_str(" ");
145			}
146		} else {
147			for (s, &mv) in self.moves.iter().enumerate().take(self.length as usize).rev() {
148				buf.push_str(MOVE2STR[URF_MOVE[urf as usize][mv as usize] as usize]);
149				buf.push_str(" ");
150				if self.verbose & USE_SEPARATOR != 0 && s == self.depth1 as usize {
151					buf.push_str(".  ");
152				}
153			}
154		}
155
156		if self.verbose & APPEND_LENGTH != 0 {
157			buf.push_str(&format!("({}f)", self.length));
158		}
159
160		buf
161	}
162}
163
164#[repr(C)]
165struct IdaContext {
166	mv: [u8; 30],
167	allow_shorter: bool,
168	depth1: i8,
169	length1: i8,
170	valid1: i8,
171	urf_idx: u8,
172	p1_cubies: [Cubie; 20],
173	urf_cubies: [Cubie; 6],
174	premv: [u8; 15],
175	premv_len: i8,
176	max_depth2: i8,
177	target_length: i8,
178	probes: u64,
179	min_probes: u64,
180	solution: Solution,
181}
182
183impl Cubie {
184	fn new() -> Self {
185		let mut cc = Cubie {ca: [0; 8], ea: [0; 12]};
186		cc.reset();
187		cc
188	}
189
190	fn reset(&mut self) {
191		for i in 0..8 {
192			self.ca[i] = i as u8;
193		}
194		for i in 0..12 {
195			self.ea[i] = i as u8 * 2;
196		}
197	}
198
199	fn cmp(&self, other: &Cubie) -> i8 {
200		for i in 0..8 {
201			if self.ca[i] != other.ca[i] {
202				return (self.ca[i] as i8) - (other.ca[i] as i8);
203			}
204		}
205		for i in 0..12 {
206			if self.ea[i] != other.ea[i] {
207				return (self.ea[i] as i8) - (other.ea[i] as i8);
208			}
209		}
210		0
211	}
212
213	fn corn_mult(a: &Cubie, b: &Cubie, prod: &mut Cubie) {
214		for cn in 0..8 {
215			let ori_a = (a.ca[(b.ca[cn] & 0x7) as usize] >> 3) as u8;
216			let ori_b = (b.ca[cn] >> 3) as u8;
217			let mut ori = ori_a + if ori_a < 3 { ori_b } else { 6 - ori_b };
218			ori = ori % 3 + if (ori_a < 3) == (ori_b < 3) { 0 } else { 3 };
219			prod.ca[cn] = (a.ca[(b.ca[cn] & 0x7) as usize] & 0x7) | (ori << 3);
220		}
221	}
222
223	fn edge_mult(a: &Cubie, b: &Cubie, prod: &mut Cubie) {
224		for ed in 0..12 {
225			prod.ea[ed] = a.ea[(b.ea[ed] >> 1) as usize] ^ (b.ea[ed] & 1);
226		}
227	}
228
229	fn inv(src: &Cubie, inv: &mut Cubie) {
230		for ed in 0..12 {
231			inv.ea[(src.ea[ed] >> 1) as usize] = (ed as u8 * 2) | (src.ea[ed] & 0x1);
232		}
233		for cn in 0..8 {
234			inv.ca[(src.ca[cn] & 0x7) as usize] = cn as u8 | (((0x20 >> (src.ca[cn] >> 3)) & 0x18) as u8);
235		}
236	}
237}
238
239fn get_nparity(mut idx: i32, n: i32) -> i32 {
240	let mut p = 0;
241	let mut i = n - 2;
242	while i >= 0 {
243		p ^= idx % (n - i);
244		idx /= n - i;
245		i -= 1;
246	}
247	p & 1
248}
249
250fn get_nperm(arr: &[u8], n: i32) -> i32 {
251	let mut idx = 0;
252	let mut val = 0x76543210;
253	for i in 0..(n - 1) {
254		let v = arr[i as usize] << 2;
255		idx = (n - i) * idx + ((val >> v) & 0xf) as i32;
256		val -= 0x11111110 << v;
257	}
258	idx
259}
260
261fn set_nperm(arr: &mut [u8], mut idx: u16, n: u16) {
262	let mut extract = 0;
263	let mut val = 0x76543210;
264	for i in 2..=n {
265		extract = (extract << 4) | (idx % i) as u32;
266		idx /= i;
267	}
268	for i in 0..(n - 1) {
269		let v = (extract & 0xf) << 2;
270		extract >>= 4;
271		arr[i as usize] = ((val >> v) & 0xf) as u8;
272		let m = (1 << v) - 1;
273		val = (val & m) | ((val >> 4) & !m);
274	}
275	arr[(n - 1) as usize] = (val & 0xf) as u8;
276}
277
278fn get_comb(arr: &[u8], n: i32, mask: i32) -> i32 {
279	let mut idx_c = 0;
280	let mut r = 4;
281	let mut cnk = if n == 12 { 330 } else { 35 };
282	for i in (0..n).rev() {
283		if (arr[i as usize] & 0xc) == mask as u8 {
284			idx_c += cnk;
285			cnk = cnk * r / std::cmp::max(1, i - r + 1);
286			r -= 1;
287		}
288		cnk = cnk * (i - r) / std::cmp::max(1, i);
289	}
290	idx_c
291}
292
293fn set_comb(arr: &mut [u8], mut idx_c: i32, n: i32, mask: i32) {
294	let mut r = 4;
295	let mut fill = n - 1;
296	let mut cnk = if n == 12 { 330 } else { 35 };
297	for i in (0..n).rev() {
298		if idx_c >= cnk {
299			idx_c -= cnk;
300			cnk = cnk * r / std::cmp::max(1, i - r + 1);
301			r -= 1;
302			arr[i as usize] = (r | mask) as u8;
303		} else {
304			if (fill & 0xc) == mask {
305				fill -= 4;
306			}
307			arr[i as usize] = fill as u8;
308			fill -= 1;
309		}
310		cnk = cnk * (i - r) / std::cmp::max(1, i);
311	}
312}
313
314impl Cubie {
315	fn get_flip(&self) -> i32 {
316		let mut idx = 0;
317		for i in 0..11 {
318			idx = (idx << 1) | (self.ea[i] & 1) as i32;
319		}
320		idx
321	}
322
323	fn set_flip(&mut self, mut idx: u16) {
324		let mut parity = 0;
325		for i in (0..11).rev() {
326			let val = idx & 1;
327			idx >>= 1;
328			parity ^= val;
329			self.ea[i] = (self.ea[i] & !1) | (val as u8);
330		}
331		self.ea[11] = (self.ea[11] & !1) | (parity as u8);
332	}
333
334	fn get_twst(&self) -> i32 {
335		let mut idx = 0;
336		for i in 0..7 {
337			idx += (idx << 1) + (self.ca[i] >> 3) as i32;
338		}
339		idx
340	}
341
342	fn set_twst(&mut self, mut idx: u16) {
343		let mut twst = 15;
344		for i in (0..7).rev() {
345			let val = idx % 3;
346			idx /= 3;
347			twst -= val;
348			self.ca[i] = (self.ca[i] & 0x7) | (val << 3) as u8;
349		}
350		self.ca[7] = (self.ca[7] & 0x7) | ((twst % 3) << 3) as u8;
351	}
352
353	fn get_slice(&self) -> u16 {
354		let mut arr = [0u8; 12];
355		for i in 0..12 {
356			arr[i] = self.ea[i] >> 1;
357		}
358		494 - get_comb(&arr, 12, 8) as u16
359	}
360
361	fn set_slice(&mut self, idx: i32) {
362		let mut arr = [0u8; 12];
363		set_comb(&mut arr, 494 - idx, 12, 8);
364		for i in 0..12 {
365			self.ea[i] = (self.ea[i] & 1) | (arr[i] << 1);
366		}
367	}
368
369	fn get_cperm(&self) -> i32 {
370		let mut arr = [0u8; 8];
371		for i in 0..8 {
372			arr[i] = self.ca[i] & 0x7;
373		}
374		get_nperm(&arr, 8)
375	}
376
377	fn set_cperm(&mut self, idx: u16) {
378		let mut arr = [0u8; 8];
379		set_nperm(&mut arr, idx, 8);
380		for i in 0..8 {
381			self.ca[i] = (self.ca[i] & !0x7) | arr[i];
382		}
383	}
384
385	fn get_eperm(&self) -> i32 {
386		let mut arr = [0u8; 8];
387		for i in 0..8 {
388			arr[i] = self.ea[i] >> 1;
389		}
390		get_nperm(&arr, 8)
391	}
392
393	fn set_eperm(&mut self, idx: u16) {
394		let mut arr = [0u8; 8];
395		set_nperm(&mut arr, idx, 8);
396		for i in 0..8 {
397			self.ea[i] = (self.ea[i] & 1) | (arr[i] << 1);
398		}
399	}
400
401	fn get_mperm(&self) -> i32 {
402		let mut arr = [0u8; 4];
403		for i in 8..12 {
404			arr[i - 8] = (self.ea[i] >> 1) & 0x3;
405		}
406		get_nperm(&arr, 4)
407	}
408
409	fn set_mperm(&mut self, idx: u16) {
410		let mut arr = [0u8; 4];
411		set_nperm(&mut arr, idx, 4);
412		for i in 8..12 {
413			self.ea[i] = (self.ea[i] & 1) | ((arr[i - 8] + 8) << 1);
414		}
415	}
416
417	fn get_ccomb(&self) -> i32 {
418		let mut arr = [0u8; 8];
419		for i in 0..8 {
420			arr[i] = self.ca[i] & 0x7;
421		}
422		get_comb(&arr, 8, 0)
423	}
424
425	fn set_ccomb(&mut self, idx: i32) {
426		let mut arr = [0u8; 8];
427		set_comb(&mut arr, idx, 8, 0);
428		for i in 0..8 {
429			self.ca[i] = (self.ca[i] & !0x7) | arr[i];
430		}
431	}
432}
433
434fn esym2csym(esym: u16) -> u16 {
435	esym ^ (0x00dddd00u32 >> ((esym & 0xf) << 1) & 3) as u16
436}
437
438struct StaticContext {
439	movecube: [Cubie; 18],
440	symcube: [Cubie; 16],
441	symmult: [[u8; 16]; 16],
442	symmuli: [[u8; 16]; 16],
443	symmove: [[u8; 8]; N_MOVES_P1],
444	symmove2: [[u8; 16]; N_MOVES_P1],
445	canon_masks2: [u16; 11],
446	symurf: Cubie,
447	symurfi: Cubie,
448}
449
450impl StaticContext {
451	fn box_new() -> Box<Self> {
452		let mut sctx = Box::new(StaticContext {
453			movecube: [Cubie::new(); 18],
454			symcube: [Cubie::new(); 16],
455			symmult: [[0; 16]; 16],
456			symmuli: [[0; 16]; 16],
457			symmove: [[0; 8]; 18],
458			symmove2: [[0; 16]; 18],
459			canon_masks2: [0; 11],
460			symurf: Cubie::new(),
461			symurfi: Cubie::new(),
462		});
463		sctx.init();
464		sctx
465	}
466
467	fn init(&mut self) {
468		let movebase: [Cubie; 6] = [
469			Cubie {ca: [3, 0, 1, 2, 4, 5, 6, 7], ea: [6, 0, 2, 4, 8, 10, 12, 14, 16, 18, 20, 22]},
470			Cubie {ca: [20, 1, 2, 8, 15, 5, 6, 19], ea: [16, 2, 4, 6, 22, 10, 12, 14, 8, 18, 20, 0]},
471			Cubie {ca: [9, 21, 2, 3, 16, 12, 6, 7], ea: [0, 19, 4, 6, 8, 17, 12, 14, 3, 11, 20, 22]},
472			Cubie {ca: [0, 1, 2, 3, 5, 6, 7, 4], ea: [0, 2, 4, 6, 10, 12, 14, 8, 16, 18, 20, 22]},
473			Cubie {ca: [0, 10, 22, 3, 4, 17, 13, 7], ea: [0, 2, 20, 6, 8, 10, 18, 14, 16, 4, 12, 22]},
474			Cubie {ca: [0, 1, 11, 23, 4, 5, 18, 14], ea: [0, 2, 4, 23, 8, 10, 12, 21, 16, 18, 7, 15]}
475		];
476		for i in 0..18 {
477			if i % 3 == 0 {
478				self.movecube[i] = movebase[i / 3];
479			} else {
480				let mut cc = Cubie::new();
481				Cubie::corn_mult(&self.movecube[i - 1], &movebase[i / 3], &mut cc);
482				Cubie::edge_mult(&self.movecube[i - 1], &movebase[i / 3], &mut cc);
483				self.movecube[i] = cc;
484			}
485		}
486
487		let u4 = Cubie {
488			ca: [3, 0, 1, 2, 7, 4, 5, 6],
489			ea: [6, 0, 2, 4, 14, 8, 10, 12, 23, 17, 19, 21]
490		};
491		let lr2 = Cubie {
492			ca: [25, 24, 27, 26, 29, 28, 31, 30],
493			ea: [4, 2, 0, 6, 12, 10, 8, 14, 18, 16, 22, 20]
494		};
495		let f2 = Cubie {
496			ca: [5, 4, 7, 6, 1, 0, 3, 2],
497			ea: [12, 10, 8, 14, 4, 2, 0, 6, 18, 16, 22, 20]
498		};
499
500		let mut cc = Cubie::new();
501		let mut cd = Cubie::new();
502
503		for i in 0..16 {
504			self.symcube[i] = cc;
505			Cubie::corn_mult(&cc, &u4, &mut cd);
506			Cubie::edge_mult(&cc, &u4, &mut cd);
507			cc = cd;
508			if i % 4 == 3 {
509				Cubie::corn_mult(&cc, &lr2, &mut cd);
510				Cubie::edge_mult(&cc, &lr2, &mut cd);
511				cc = cd;
512			}
513			if i % 8 == 7 {
514				Cubie::corn_mult(&cc, &f2, &mut cd);
515				Cubie::edge_mult(&cc, &f2, &mut cd);
516				cc = cd;
517			}
518		}
519
520		self.symurf = Cubie{ca: [8, 20, 13, 17, 19, 15, 22, 10], ea: [3, 16, 11, 18, 7, 22, 15, 20, 1, 9, 13, 5]};
521		Cubie::corn_mult(&self.symurf, &self.symurf, &mut self.symurfi);
522		Cubie::edge_mult(&self.symurf, &self.symurf, &mut self.symurfi);
523
524		for i in 0..16 {
525			for j in 0..16 {
526				Cubie::corn_mult(&self.symcube[i], &self.symcube[j], &mut cc);
527				Cubie::edge_mult(&self.symcube[i], &self.symcube[j], &mut cc);
528				for k in 0..16 {
529					if Cubie::cmp(&cc, &self.symcube[k]) == 0 {
530						self.symmult[i][j] = k as u8;
531						self.symmuli[k][j] = i as u8;
532					}
533				}
534			}
535		}
536
537		let mut p2moves_imap = [0; 18];
538		for i in 0..18 {
539			p2moves_imap[P2MOVES[i] as usize] = i;
540		}
541
542		for i in 0..18 {
543			for j in 0..16 {
544				Cubie::corn_mult(&self.symcube[j], &self.movecube[i], &mut cc);
545				Cubie::corn_mult(&cc, &self.symcube[self.symmuli[0][j] as usize], &mut cd);
546				Cubie::edge_mult(&self.symcube[j], &self.movecube[i], &mut cc);
547				Cubie::edge_mult(&cc, &self.symcube[self.symmuli[0][j] as usize], &mut cd);
548				for k in 0..18 {
549					if Cubie::cmp(&self.movecube[k], &cd) == 0 {
550						self.symmove2[p2moves_imap[i] as usize][j] = p2moves_imap[k] as u8;
551						if j % 2 == 0 {
552							self.symmove[i][j / 2] = k as u8;
553						}
554						break;
555					}
556				}
557			}
558		}
559
560		for i in 0..10 {
561			let ix = P2MOVES[i] as usize / 3;
562			self.canon_masks2[i] = 0;
563			for j in 0..10 {
564				let jx = P2MOVES[j] as usize / 3;
565				self.canon_masks2[i] |= if (ix == jx) || ((ix % 3 == jx % 3) && (ix >= jx)) { 1 } else { 0 } << j;
566			}
567		}
568		self.canon_masks2[10] = 0;
569	}
570}
571
572fn init_sym2raw(
573	sctx: &StaticContext, n_raw: usize, coord: usize,
574	sym2raw: &mut [u16], raw2sym: &mut [u16], selfsym: &mut [u16],
575) -> usize {
576	let mut c = Cubie::new();
577	let mut e = Cubie::new();
578	let mut d = Cubie::new();
579	let sym_inc = if coord >= 2 { 1 } else { 2 };
580	let sym_shift = if coord >= 2 { 0 } else { 1 };
581	for i in 0..n_raw {
582		raw2sym[i] = 0;
583	}
584	let mut count = 0;
585	for i in 0..n_raw {
586		if raw2sym[i] != 0 {
587			continue;
588		}
589		match coord {
590			0 => c.set_flip(i as u16),
591			1 => c.set_twst(i as u16),
592			2 => c.set_eperm(i as u16),
593			_ => unreachable!(),
594		}
595		for s in (0..16).step_by(sym_inc) {
596			if coord == 1 {
597				Cubie::corn_mult(&sctx.symcube[sctx.symmuli[0][s as usize] as usize], &c, &mut e);
598				Cubie::corn_mult(&e, &sctx.symcube[s as usize], &mut d);
599			} else {
600				Cubie::edge_mult(&sctx.symcube[sctx.symmuli[0][s as usize] as usize], &c, &mut e);
601				Cubie::edge_mult(&e, &sctx.symcube[s as usize], &mut d);
602			}
603			let idx = match coord {
604				0 => d.get_flip(),
605				1 => d.get_twst(),
606				2 => d.get_eperm(),
607				_ => unreachable!(),
608			};
609			if idx == i as i32 {
610				selfsym[count] |= 1 << (s >> sym_shift);
611			}
612			raw2sym[idx as usize] = ((count << 4 | s) >> sym_shift) as u16;
613		}
614		sym2raw[count] = i as u16;
615		count += 1;
616	}
617	#[cfg(debug_assertions)]
618	println!("init sym2raw coord={} count={}", coord, count);
619	count
620}
621
622struct StaticTables {
623	perm_sym_inv : [u16; N_PERM_SYM],
624	cperm2comb   : [u8; N_PERM_SYM],
625	flip_sym2raw : [u16; N_FLIP_SYM],
626	flip_raw2sym : [u16; N_FLIP],
627	flip_selfsym : [u16; N_FLIP_SYM],
628	twst_sym2raw : [u16; N_TWST_SYM],
629	twst_raw2sym : [u16; N_TWST],
630	twst_selfsym : [u16; N_TWST_SYM],
631	eperm_sym2raw: [u16; N_PERM_SYM],
632	eperm_raw2sym: [u16; N_PERM],
633	eperm_selfsym: [u16; N_PERM_SYM],
634	flip_move    : [u16; N_FLIP_SYM * N_MOVES_P1],
635	twst_move    : [u16; N_TWST_SYM * N_MOVES_P1],
636	slice_move   : [u16; N_SLICE * N_MOVES_P1],
637	slice_conj   : [u16; N_SLICE * 8],
638	cperm_move   : [u16; N_PERM_SYM * N_MOVES_P2],
639	eperm_move   : [u16; N_PERM_SYM * N_MOVES_P2],
640	mperm_move   : [u16; N_MPERM * N_MOVES_P2],
641	mperm_conj   : [u16; N_MPERM * 16],
642	ccomb_move   : [u16; N_CCOMB * N_MOVES_P2],
643	ccomb_conj   : [u16; N_CCOMB * 16],
644	slice_flip_prun : [u32; N_SLICE * N_FLIP_SYM / 8 + 1],
645	slice_twst_prun : [u32; N_SLICE * N_TWST_SYM / 8 + 1],
646	ccomb_eperm_prun: [u32; N_CCOMB * N_PERM_SYM / 8 + 1],
647	mperm_cperm_prun: [u32; N_MPERM * N_PERM_SYM / 8 + 1],
648}
649
650fn init_move_tables(sctx: &StaticContext, stbl: &mut StaticTables) {
651	let mut c = Cubie::new();
652	c.reset();
653
654	for i in 0..N_FLIP_SYM {
655		c.set_flip(stbl.flip_sym2raw[i]);
656		for j in 0..N_MOVES_P1 {
657			let mut d = Cubie::new();
658			Cubie::edge_mult(&c, &sctx.movecube[j], &mut d);
659			stbl.flip_move[i * N_MOVES_P1 + j] = stbl.flip_raw2sym[d.get_flip() as usize];
660		}
661	}
662
663	for i in 0..N_TWST_SYM {
664		c.set_twst(stbl.twst_sym2raw[i]);
665		for j in 0..N_MOVES_P1 {
666			let mut d = Cubie::new();
667			Cubie::corn_mult(&c, &sctx.movecube[j], &mut d);
668			stbl.twst_move[i * N_MOVES_P1 + j] = stbl.twst_raw2sym[d.get_twst() as usize];
669		}
670	}
671
672	for i in 0..N_SLICE {
673		c.set_slice(i as i32);
674		for j in 0..N_MOVES_P1 {
675			let mut d = Cubie::new();
676			Cubie::edge_mult(&c, &sctx.movecube[j], &mut d);
677			stbl.slice_move[i * N_MOVES_P1 + j] = d.get_slice();
678		}
679		for j in 0..8 {
680			let mut e = Cubie::new();
681			let mut d = Cubie::new();
682			Cubie::edge_mult(&sctx.symcube[j << 1], &c, &mut e);
683			Cubie::edge_mult(&e, &sctx.symcube[j << 1], &mut d);
684			stbl.slice_conj[i * 8 + j] = d.get_slice();
685		}
686	}
687
688	c.reset();
689	for i in 0..N_PERM_SYM {
690		c.set_cperm(stbl.eperm_sym2raw[i]);
691		c.set_eperm(stbl.eperm_sym2raw[i]);
692		for j in 0..N_MOVES_P2 {
693			let mut d = Cubie::new();
694			Cubie::corn_mult(&c, &sctx.movecube[P2MOVES[j] as usize], &mut d);
695			Cubie::edge_mult(&c, &sctx.movecube[P2MOVES[j] as usize], &mut d);
696			stbl.cperm_move[i * N_MOVES_P2 + j] = esym2csym(stbl.eperm_raw2sym[d.get_cperm() as usize]);
697			stbl.eperm_move[i * N_MOVES_P2 + j] = stbl.eperm_raw2sym[d.get_eperm() as usize];
698		}
699		let mut d = Cubie::new();
700		Cubie::inv(&c, &mut d);
701		stbl.perm_sym_inv[i] = stbl.eperm_raw2sym[d.get_eperm() as usize];
702		stbl.cperm2comb[i] = c.get_ccomb() as u8;
703	}
704
705	for i in 0..N_MPERM {
706		c.set_mperm(i as u16);
707		for j in 0..N_MOVES_P2 {
708			let mut d = Cubie::new();
709			Cubie::edge_mult(&c, &sctx.movecube[P2MOVES[j] as usize], &mut d);
710			stbl.mperm_move[i * N_MOVES_P2 + j] = d.get_mperm() as u16;
711		}
712		for j in 0..16 {
713			let mut e = Cubie::new();
714			let mut d = Cubie::new();
715			Cubie::edge_mult(&sctx.symcube[j], &c, &mut e);
716			Cubie::edge_mult(&e, &sctx.symcube[sctx.symmuli[0][j] as usize], &mut d);
717			stbl.mperm_conj[i * 16 + j] = d.get_mperm() as u16;
718		}
719	}
720
721	for i in 0..N_CCOMB {
722		c.set_ccomb(i as i32);
723		for j in 0..N_MOVES_P2 {
724			let mut d = Cubie::new();
725			Cubie::corn_mult(&c, &sctx.movecube[P2MOVES[j] as usize], &mut d);
726			stbl.ccomb_move[i * N_MOVES_P2 + j] = d.get_ccomb() as u16;
727		}
728		for j in 0..16 {
729			let mut e = Cubie::new();
730			let mut d = Cubie::new();
731			Cubie::corn_mult(&sctx.symcube[j], &c, &mut e);
732			Cubie::corn_mult(&e, &sctx.symcube[sctx.symmuli[0][j] as usize], &mut d);
733			stbl.ccomb_conj[i * 16 + j] = d.get_ccomb() as u16;
734		}
735	}
736}
737
738fn set_pruning(table: &mut [u32], index: usize, value: u32) {
739	table[index >> 3] ^= value << ((index & 7) << 2);
740}
741
742fn get_pruning(table: &[u32], index: usize) -> u32 {
743	(table[index >> 3] >> ((index & 7) << 2)) & 0xf
744}
745
746fn init_raw_sym_prun(
747	prun_table: &mut [u32],
748	raw_move: &[u16],
749	raw_conj: &[u16],
750	sym_move: &[u16],
751	sym_selfsym: &[u16],
752	n_raw: usize,
753	n_sym: usize,
754	prun_flag: usize,
755) {
756	let sym_shift: usize = prun_flag & 0xf;
757	let sym_e2c_magic: usize = if (prun_flag >> 4) & 1 == 1 { 0x00DDDD00 } else { 0 };
758	let is_phase2: usize = if (prun_flag >> 5) & 1 == 1 { 1 } else { 0 };
759	let inv_depth: usize = (prun_flag >> 8) & 0xf;
760	let max_depth: usize = (prun_flag >> 12) & 0xf;
761
762	let sym_mask: usize = (1 << sym_shift) - 1;
763	let n_entries: usize = n_raw * n_sym;
764	let n_moves: usize = if is_phase2 != 0 { N_MOVES_P2 } else { N_MOVES_P1 };
765
766	let mut depth: usize = 0;
767	let mut _done: usize = 1;
768
769	for i in 0..(n_entries / 8 + 1) {
770		prun_table[i] = 0xffffffff;
771	}
772	set_pruning(prun_table, 0, 0xf);
773
774	while depth < max_depth as usize {
775		let inv = depth > inv_depth;
776		let select = (if inv { 0xf } else { depth }) as u32;
777		let check = (if inv { depth } else { 0xf }) as u32;
778		depth += 1;
779		let xor_val = (depth ^ 0xf) as u32;
780		let mut val: u32 = 0;
781		let mut i = 0;
782		while i < n_entries {
783			if (i & 7) == 0 {
784				val = prun_table[i >> 3];
785				if !inv && val == 0xffffffff {
786					i += 8;
787					continue;
788				}
789			}
790
791			if (val & 0xf) != select {
792				i += 1;
793				val >>= 4;
794				continue;
795			}
796
797			let raw = i % n_raw;
798			let sym = i / n_raw;
799
800			for m in 0..n_moves {
801				let symx = sym_move[sym * n_moves + m] as usize;
802				let rawx = raw_conj[(raw_move[raw * n_moves + m] as usize) << sym_shift | (symx & sym_mask)] as usize;
803				let symx = symx >> sym_shift;
804				let idx = symx * n_raw + rawx;
805				let prun = get_pruning(prun_table, idx);
806
807				if prun != check {
808					continue;
809				}
810
811				_done += 1;
812				if inv {
813					set_pruning(prun_table, i, xor_val);
814					break;
815				}
816
817				set_pruning(prun_table, idx, xor_val);
818				let idx = idx - rawx;
819
820				for j in 1..=15 {
821					let ssmask = sym_selfsym[symx as usize];
822					if (ssmask >> j) & 1 == 0 {
823						continue;
824					}
825					let idxx = idx + raw_conj[((rawx << sym_shift) | (j ^ (sym_e2c_magic >> (j << 1) & 3))) as usize] as usize;
826					if get_pruning(prun_table, idxx) == check {
827						set_pruning(prun_table, idxx, xor_val);
828						_done += 1;
829					}
830				}
831			}
832			i += 1;
833			val >>= 4;
834		}
835		#[cfg(debug_assertions)]
836		println!("depth={:2} entry_cnt={:10}", depth, _done);
837	}
838}
839
840impl StaticTables {
841	fn box_new(sctx: &StaticContext) -> Box<Self> {
842		let mut stbl = Box::new(StaticTables {
843			perm_sym_inv : [0u16; N_PERM_SYM],
844			cperm2comb   : [0u8; N_PERM_SYM],
845			flip_sym2raw : [0u16; N_FLIP_SYM],
846			flip_raw2sym : [0u16; N_FLIP],
847			flip_selfsym : [0u16; N_FLIP_SYM],
848			twst_sym2raw : [0u16; N_TWST_SYM],
849			twst_raw2sym : [0u16; N_TWST],
850			twst_selfsym : [0u16; N_TWST_SYM],
851			eperm_sym2raw: [0u16; N_PERM_SYM],
852			eperm_raw2sym: [0u16; N_PERM],
853			eperm_selfsym: [0u16; N_PERM_SYM],
854			flip_move    : [0u16; N_FLIP_SYM * N_MOVES_P1],
855			twst_move    : [0u16; N_TWST_SYM * N_MOVES_P1],
856			slice_move   : [0u16; N_SLICE * N_MOVES_P1],
857			slice_conj   : [0u16; N_SLICE * 8],
858			cperm_move   : [0u16; N_PERM_SYM * N_MOVES_P2],
859			eperm_move   : [0u16; N_PERM_SYM * N_MOVES_P2],
860			mperm_move   : [0u16; N_MPERM * N_MOVES_P2],
861			mperm_conj   : [0u16; N_MPERM * 16],
862			ccomb_move   : [0u16; N_CCOMB * N_MOVES_P2],
863			ccomb_conj   : [0u16; N_CCOMB * 16],
864			slice_flip_prun : [0u32; N_SLICE * N_FLIP_SYM / 8 + 1],
865			slice_twst_prun : [0u32; N_SLICE * N_TWST_SYM / 8 + 1],
866			ccomb_eperm_prun: [0u32; N_CCOMB * N_PERM_SYM / 8 + 1],
867			mperm_cperm_prun: [0u32; N_MPERM * N_PERM_SYM / 8 + 1],
868		});
869		stbl.init(&sctx);
870		stbl
871	}
872
873	fn init(&mut self, sctx: &StaticContext) {
874		init_sym2raw(sctx, N_FLIP, 0, &mut self.flip_sym2raw, &mut self.flip_raw2sym, &mut self.flip_selfsym);
875		init_sym2raw(sctx, N_TWST, 1, &mut self.twst_sym2raw, &mut self.twst_raw2sym, &mut self.twst_selfsym);
876		init_sym2raw(sctx, N_PERM, 2, &mut self.eperm_sym2raw, &mut self.eperm_raw2sym, &mut self.eperm_selfsym);
877		init_move_tables(sctx, self);
878		init_raw_sym_prun(&mut self.slice_twst_prun, &self.slice_move, &self.slice_conj, &self.twst_move, &self.twst_selfsym, N_SLICE, N_TWST_SYM, 0x69603);
879		init_raw_sym_prun(&mut self.slice_flip_prun, &self.slice_move, &self.slice_conj, &self.flip_move, &self.flip_selfsym, N_SLICE, N_FLIP_SYM, 0x69603);
880		init_raw_sym_prun(&mut self.ccomb_eperm_prun, &self.ccomb_move, &self.ccomb_conj, &self.eperm_move, &self.eperm_selfsym, N_CCOMB, N_PERM_SYM, 0x7c824);
881		init_raw_sym_prun(&mut self.mperm_cperm_prun, &self.mperm_move, &self.mperm_conj, &self.cperm_move, &self.eperm_selfsym, N_MPERM, N_PERM_SYM, 0x8ea34);
882	}
883}
884
885fn get_perm_sym_inv(sctx: &StaticContext, stbl: &StaticTables, idx: u16, sym: u16, is_corner: i32) -> u16 {
886	let idxi = stbl.perm_sym_inv[idx as usize];
887	let mut result = if is_corner != 0 {
888		esym2csym(idxi)
889	} else {
890		idxi
891	};
892	result = (result & 0xfff0) | (sctx.symmult[result as usize & 0xf][sym as usize] as u16);
893	result
894}
895
896impl Coord {
897	fn new() -> Self {
898		Coord {
899			twst: 0u16,
900			tsym: 0u16,
901			flip: 0u16,
902			fsym: 0u16,
903			slice: 0u16,
904			prun: 0i8
905		}
906	}
907
908	fn from_cubie(&mut self, stbl: &StaticTables, src: &Cubie) -> i8 {
909		self.slice = src.get_slice();
910		self.flip = stbl.flip_raw2sym[src.get_flip() as usize];
911		self.fsym = self.flip & 7;
912		self.flip >>= 3;
913		self.twst = stbl.twst_raw2sym[src.get_twst() as usize];
914		self.tsym = self.twst & 7;
915		self.twst >>= 3;
916		self.prun = std::cmp::max(
917			get_pruning(&stbl.slice_twst_prun, self.twst as usize * N_SLICE + stbl.slice_conj[(self.slice * 8 + self.tsym) as usize] as usize),
918			get_pruning(&stbl.slice_flip_prun, self.flip as usize * N_SLICE + stbl.slice_conj[(self.slice * 8 + self.fsym) as usize] as usize)
919		) as i8;
920		self.prun
921	}
922
923	fn move_prun(&mut self, sctx: &StaticContext, stbl: &StaticTables, src: &Coord, mv: usize) -> i8 {
924		self.slice = stbl.slice_move[src.slice as usize * N_MOVES_P1 + mv];
925		self.flip = stbl.flip_move[src.flip as usize * N_MOVES_P1 + sctx.symmove[mv][src.fsym as usize] as usize];
926		self.fsym = (self.flip & 7) ^ src.fsym;
927		self.flip >>= 3;
928		self.twst = stbl.twst_move[src.twst as usize * N_MOVES_P1 + sctx.symmove[mv][src.tsym as usize] as usize];
929		self.tsym = (self.twst & 7) ^ src.tsym;
930		self.twst >>= 3;
931		self.prun = std::cmp::max(
932			get_pruning(&stbl.slice_twst_prun, self.twst as usize * N_SLICE + stbl.slice_conj[(self.slice * 8 + self.tsym) as usize] as usize),
933			get_pruning(&stbl.slice_flip_prun, self.flip as usize * N_SLICE + stbl.slice_conj[(self.slice * 8 + self.fsym) as usize] as usize)
934		) as i8;
935		self.prun
936	}
937}
938
939impl Coord2 {
940	fn new() -> Self {
941		Coord2 {
942			edge: 0u16,
943			esym: 0u16,
944			corn: 0u16,
945			csym: 0u16,
946			mid: 0u16
947		}
948	}
949
950	fn from_cubie(&mut self, sctx: &StaticContext, stbl: &StaticTables, src: &Cubie) -> i8 {
951		self.corn = esym2csym(stbl.eperm_raw2sym[src.get_cperm() as usize]) as u16;
952		self.csym = self.corn & 0xf;
953		self.corn = self.corn >> 4;
954		self.edge = stbl.eperm_raw2sym[src.get_eperm() as usize] as u16;
955		self.esym = self.edge & 0xf;
956		self.edge = self.edge >> 4;
957		self.mid = src.get_mperm() as u16;
958		let edgei = get_perm_sym_inv(sctx, stbl, self.edge, self.esym, 0);
959		let corni = get_perm_sym_inv(sctx, stbl, self.corn, self.csym, 1);
960		std::cmp::max(
961			get_pruning(&stbl.ccomb_eperm_prun, (edgei >> 4) as usize * N_CCOMB + stbl.ccomb_conj[stbl.cperm2comb[corni as usize >> 4] as usize * 16 + sctx.symmuli[edgei as usize & 0xf][corni as usize & 0xf] as usize] as usize),
962			std::cmp::max(
963				get_pruning(&stbl.ccomb_eperm_prun, self.edge as usize * N_CCOMB + stbl.ccomb_conj[stbl.cperm2comb[self.corn as usize] as usize * 16 + sctx.symmuli[self.esym as usize][self.csym as usize] as usize] as usize),
964				get_pruning(&stbl.mperm_cperm_prun, self.corn as usize * N_MPERM + stbl.mperm_conj[self.mid as usize * 16 + self.csym as usize] as usize)
965			)
966		) as i8
967	}
968}
969
970impl IdaContext {
971	pub fn new() -> Self {
972		IdaContext {
973			mv: [0; 30],
974			allow_shorter: false,
975			depth1: 0,
976			length1: 0,
977			valid1: 0,
978			urf_idx: 0,
979			p1_cubies: [Cubie::new(); 20],
980			urf_cubies: [Cubie::new(); 6],
981			premv: [0; 15],
982			premv_len: 0,
983			max_depth2: 0,
984			target_length: 0,
985			probes: 0,
986			min_probes: 0,
987			solution: Solution {
988				depth1: 0,
989				verbose: 0,
990				urf_idx: 0,
991				premv_len: 0,
992				length: 0,
993				moves: [0; 31],
994			},
995		}
996	}
997
998	pub fn solve_cubie(&mut self, sctx: &StaticContext, stbl: &StaticTables, cc: &Cubie, target_length: i8) -> String {
999		let mut cc1 = *cc;
1000		let mut cc2 = Cubie::new();
1001		self.target_length = target_length + 1;
1002		self.probes = 0;
1003		for i in 0..6 {
1004			self.urf_cubies[i] = cc1;
1005			Cubie::corn_mult(&sctx.symurfi, &cc1, &mut cc2);
1006			Cubie::edge_mult(&sctx.symurfi, &cc1, &mut cc2);
1007			Cubie::corn_mult(&cc2, &sctx.symurf, &mut cc1);
1008			Cubie::edge_mult(&cc2, &sctx.symurf, &mut cc1);
1009			if i == 2 {
1010				Cubie::inv(&cc1, &mut cc2);
1011				cc1 = cc2;
1012			}
1013		}
1014		for length1 in 0..21 {
1015			self.length1 = length1;
1016			self.max_depth2 = std::cmp::min(MAX_DEPTH2 as i8, self.target_length as i8 - self.length1 as i8 - 1);
1017			self.depth1 = self.length1 - self.premv_len;
1018			self.allow_shorter = false;
1019			for urf_idx in 0..6 {
1020				self.urf_idx = urf_idx;
1021				let cc = self.urf_cubies[self.urf_idx as usize];
1022				let ret = self.phase1_pre_moves(sctx, stbl, MAX_PREMV_LEN as i8, -30, &cc, 0);
1023				if ret == 0 {
1024					let solbuf = self.solution.to_string();
1025					#[cfg(debug_assertions)]
1026					println!("solution found in {:2}+{:2} moves urf={} premv={} probe={:5}: {}",
1027						self.length1, self.solution.length - self.length1,
1028						self.solution.urf_idx, self.solution.premv_len, self.probes, solbuf);
1029					return solbuf;
1030				}
1031			}
1032		}
1033		String::from("Error 8")
1034	}
1035
1036	fn phase1_pre_moves(&mut self, sctx: &StaticContext, stbl: &StaticTables,
1037			maxl: i8, lm: i8, cc: &Cubie, _ssym: i32) -> i32 {
1038		self.premv_len = MAX_PREMV_LEN - maxl;
1039		if self.premv_len == 0 || ((0o667667 >> lm) & 1) == 0 {
1040			self.depth1 = self.length1 - self.premv_len;
1041			self.allow_shorter = self.depth1 == MIN_P1PRE_LEN && self.premv_len != 0;
1042			self.p1_cubies[0] = *cc;
1043			let mut node = Coord::new();
1044			if node.from_cubie(stbl, &self.p1_cubies[0]) <= self.depth1 {
1045				let ret = self.phase1(sctx, stbl, &node, 0, self.depth1, -1);
1046				if ret == 0 {
1047					return 0;
1048				}
1049			}
1050		}
1051		if maxl == 0 || self.premv_len + MIN_P1PRE_LEN >= self.length1 {
1052			return 1;
1053		}
1054		let skip_moves = if maxl == 1 || self.premv_len + 1 + MIN_P1PRE_LEN >= self.length1 {
1055			0o227227
1056		} else {
1057			0
1058		};
1059		let mut cd = Cubie::new();
1060		let lm = lm / 3;
1061		for m in 0..18 {
1062			if m / 3 == lm || m / 3 == lm - 3 || m / 3 == lm + 3 {
1063				continue;
1064			}
1065			if (skip_moves & (1 << m)) != 0 {
1066				continue;
1067			}
1068			Cubie::corn_mult(&sctx.movecube[m as usize], cc, &mut cd);
1069			Cubie::edge_mult(&sctx.movecube[m as usize], cc, &mut cd);
1070			self.premv[MAX_PREMV_LEN as usize - maxl as usize] = m as u8;
1071			let ret = self.phase1_pre_moves(sctx, stbl, maxl - 1, m, &cd, 0);
1072			if ret == 0 {
1073				return 0;
1074			}
1075		}
1076		1
1077	}
1078
1079	fn phase1(&mut self, sctx: &StaticContext, stbl: &StaticTables,
1080			node: &Coord, _ssym: i32, maxl: i8, lm: i8) -> i8 {
1081		let mut next_node: Coord = Coord::new();
1082		if node.prun == 0 && maxl < 5 {
1083			if self.allow_shorter || maxl == 0 {
1084				self.depth1 -= maxl;
1085				let ret = self.init_phase2(sctx, stbl);
1086				self.depth1 += maxl;
1087				return ret;
1088			} else {
1089				return 1;
1090			}
1091		}
1092		for axis in (0..N_MOVES_P1 as i8).step_by(3) {
1093			if axis == lm || axis == lm - 9 {
1094				continue;
1095			}
1096			for power in 0..3 {
1097				let m = axis + power;
1098				let prun = next_node.move_prun(sctx, stbl, node, m as usize);
1099				if prun > maxl {
1100					break;
1101				} else if prun == maxl {
1102					continue;
1103				}
1104				self.mv[self.depth1 as usize - maxl as usize] = m as u8;
1105				self.valid1 = self.valid1.min(self.depth1 - maxl);
1106				let ret = self.phase1(sctx, stbl, &next_node, 0, maxl - 1, axis);
1107				if ret == 0 {
1108					return 0;
1109				} else if ret >= 2 {
1110					break;
1111				}
1112			}
1113		}
1114		1
1115	}
1116
1117	fn init_phase2(&mut self, sctx: &StaticContext, stbl: &StaticTables) -> i8 {
1118		self.probes += 1;
1119		let mut cc = if self.depth1 == 0 {
1120			self.p1_cubies[0]
1121		} else {
1122			Cubie::new()
1123		};
1124		for i in self.valid1 as usize..self.depth1 as usize {
1125			Cubie::corn_mult(&self.p1_cubies[i], &sctx.movecube[self.mv[i] as usize], &mut cc);
1126			Cubie::edge_mult(&self.p1_cubies[i], &sctx.movecube[self.mv[i] as usize], &mut cc);
1127			self.p1_cubies[i + 1] = cc;
1128		}
1129		self.valid1 = self.depth1;
1130		let mut node1 = Coord2::new();
1131		let mut prun = node1.from_cubie(sctx, stbl, &cc);
1132		let mut node2 = Coord2::new();
1133		if self.premv_len > 0 {
1134			let m = self.premv[self.premv_len as usize - 1] as usize / 3 * 3 + 1;
1135			let mut cd = Cubie::new();
1136			Cubie::corn_mult(&sctx.movecube[m], &cc, &mut cd);
1137			Cubie::edge_mult(&sctx.movecube[m], &cc, &mut cd);
1138			prun = prun.min(node2.from_cubie(sctx, stbl, &cd));
1139		}
1140		if prun > self.max_depth2 {
1141			return prun - self.max_depth2;
1142		}
1143		let mut depth2 = self.max_depth2;
1144		while depth2 >= prun {
1145			let mut sol_src = 0;
1146			let mut ret = self.phase2(sctx, stbl, &node1, depth2, self.depth1, 10);
1147			if ret < 0 && self.premv_len > 0 {
1148				sol_src = 1;
1149				ret = self.phase2(sctx, stbl, &node2, depth2, self.depth1, 10);
1150			}
1151			if ret < 0 {
1152				break;
1153			}
1154			depth2 -= ret;
1155			self.target_length = 0;
1156			self.solution.length = 0;
1157			self.solution.urf_idx = self.urf_idx;
1158			self.solution.depth1 = self.depth1;
1159			self.solution.premv_len = self.premv_len;
1160			for i in 0..self.depth1 + depth2 {
1161				self.solution.append_move(self.mv[i as usize]);
1162			}
1163			if sol_src == 1 {
1164				self.solution.append_move(self.premv[self.premv_len as usize - 1] / 3 * 3 + 1);
1165			}
1166			for i in (0..self.premv_len).rev() {
1167				self.solution.append_move(self.premv[i as usize]);
1168			}
1169			self.target_length = self.solution.length;
1170			depth2 -= 1;
1171		}
1172
1173		if depth2 != self.max_depth2 {
1174			self.max_depth2 = std::cmp::min(MAX_DEPTH2 as i8, self.target_length as i8 - self.length1 - 1);
1175			return if self.probes >= self.min_probes { 0 } else { 1 };
1176		}
1177		1
1178	}
1179
1180	fn phase2(&mut self, sctx: &StaticContext, stbl: &StaticTables,
1181			node: &Coord2, maxl: i8, depth: i8, lm: i8) -> i8 {
1182		if node.edge == 0 && node.corn == 0 && node.mid == 0 {
1183			return maxl;
1184		}
1185		let move_mask = sctx.canon_masks2[lm as usize];
1186		let mut nodex = Coord2::new();
1187		for m in 0..N_MOVES_P2 {
1188			if (move_mask >> m & 1) != 0 {
1189				continue;
1190			}
1191			nodex.mid = stbl.mperm_move[node.mid as usize * N_MOVES_P2 + m];
1192			nodex.corn = stbl.cperm_move[node.corn as usize * N_MOVES_P2 + sctx.symmove2[m][node.csym as usize] as usize];
1193			nodex.csym = sctx.symmult[nodex.corn as usize & 0xf][node.csym as usize] as u16;
1194			nodex.corn = nodex.corn >> 4;
1195			nodex.edge = stbl.eperm_move[node.edge as usize * N_MOVES_P2 + sctx.symmove2[m][node.esym as usize] as usize];
1196			nodex.esym = sctx.symmult[nodex.edge as usize & 0xf][node.esym as usize] as u16;
1197			nodex.edge = nodex.edge >> 4;
1198			let edgei = get_perm_sym_inv(sctx, stbl, nodex.edge, nodex.esym, 0) as usize;
1199			let corni = get_perm_sym_inv(sctx, stbl, nodex.corn, nodex.csym, 1) as usize;
1200			let prun = get_pruning(&stbl.ccomb_eperm_prun,
1201				(edgei >> 4) as usize * N_CCOMB +
1202				stbl.ccomb_conj[stbl.cperm2comb[corni as usize >> 4] as usize * 16 + sctx.symmuli[edgei as usize & 0xf][corni as usize & 0xf] as usize] as usize) as i8;
1203			if prun > maxl + 1 {
1204				return maxl - prun + 1;
1205			} else if prun >= maxl {
1206				continue;
1207			}
1208			let prun = std::cmp::max(
1209				get_pruning(&stbl.mperm_cperm_prun, nodex.corn as usize * N_MPERM + stbl.mperm_conj[nodex.mid as usize * 16 + nodex.csym as usize] as usize),
1210				get_pruning(&stbl.ccomb_eperm_prun, nodex.edge as usize * N_CCOMB + stbl.ccomb_conj[stbl.cperm2comb[nodex.corn as usize] as usize * 16 + sctx.symmuli[nodex.esym as usize][nodex.csym as usize] as usize] as usize)
1211			) as i8;
1212			if prun >= maxl {
1213				continue;
1214			}
1215			let ret = self.phase2(sctx, stbl, &nodex, maxl - 1, depth + 1, m as i8);
1216			if ret >= 0 {
1217				self.mv[depth as usize] = P2MOVES[m];
1218				return ret;
1219			} else if ret < -2 {
1220				break;
1221			}
1222		}
1223		-1
1224	}
1225}
1226
1227impl Cubie {
1228	pub fn verify(&self) -> i32 {
1229		let mut sum = 0;
1230		let mut edge_mask = 0;
1231		for e in 0..12 {
1232			edge_mask |= 1 << (self.ea[e] >> 1);
1233			sum ^= self.ea[e] & 1;
1234		}
1235		if edge_mask != 0xfff {
1236			return -2;
1237		} else if sum != 0 {
1238			return -3;
1239		}
1240		let mut corn_mask = 0;
1241		for c in 0..8 {
1242			corn_mask |= 1 << (self.ca[c] & 7);
1243			sum += self.ca[c] >> 3;
1244		}
1245		if corn_mask != 0xff {
1246			return -4;
1247		} else if sum % 3 != 0 {
1248			return -5;
1249		}
1250		let mut parity = get_nparity(self.get_cperm(), 8);
1251		let mut ea: [u8; 12] = self.ea;
1252		for i in 0..12 {
1253			while ((ea[i] as usize) >> 1) != i {
1254				let j = (ea[i] as usize) >> 1;
1255				ea.swap(i, j);
1256				parity ^= 1;
1257			}
1258		}
1259		if parity != 0 {
1260			return -6;
1261		}
1262		0
1263	}
1264
1265	fn random_reset(&mut self) {
1266		let mut rng = rand::thread_rng();
1267		let cperm = rng.gen_range(0..N_PERM) as u16;
1268		let mut parity = get_nparity(cperm as i32, 8);
1269		self.reset();
1270		self.set_cperm(cperm);
1271		self.set_twst(rng.gen_range(0..N_TWST) as u16);
1272		self.set_flip(rng.gen_range(0..N_FLIP) as u16);
1273		for i in 0..10 {
1274			let j = i + rng.gen_range(0..12 - i);
1275			if i != j {
1276				self.ea.swap(i, j);
1277				parity ^= 1;
1278			}
1279		}
1280		if parity != 0 {
1281			self.ea.swap(10, 11);
1282		}
1283	}
1284
1285	fn from_facelet(&mut self, facelet: &String) -> i32 {
1286		if facelet.len() < 54 {
1287			return -1;
1288		}
1289		let fstr: &[u8] = facelet.as_bytes();
1290		let mut f: [u8; 54] = [0; 54];
1291		let colors: [u8; 6] = [fstr[4], fstr[13], fstr[22], fstr[31], fstr[40], fstr[49]];
1292		let mut count: i32 = 0;
1293		for i in 0..54 {
1294			if let Some(fidx) = colors.iter().position(|&s| s == fstr[i]) {
1295				f[i] = fidx as u8;
1296				count += 1 << (fidx * 4);
1297			} else {
1298				return -1;
1299			}
1300		}
1301		if count != 0x999999 {
1302			return -1;
1303		}
1304		self.reset();
1305		let mut ori: usize;
1306		for i in 0..8 {
1307			ori = 0;
1308			while ori < 3 {
1309				if f[CORNER_FACELET[i][ori] as usize] == 0 || f[CORNER_FACELET[i][ori] as usize] == 3 {
1310					break;
1311				}
1312				ori += 1
1313			}
1314			let col1 = f[CORNER_FACELET[i][(ori + 1) % 3] as usize];
1315			let col2 = f[CORNER_FACELET[i][(ori + 2) % 3] as usize];
1316			for j in 0..8 {
1317				if col1 == CORNER_FACELET[j][1] / 9 && col2 == CORNER_FACELET[j][2] / 9 {
1318					self.ca[i] = (ori as u8 % 3) << 3 | j as u8;
1319					break;
1320				}
1321			}
1322		}
1323		for i in 0..12 {
1324			for j in 0..12 {
1325				if f[EDGE_FACELET[i][0] as usize] == EDGE_FACELET[j][0] / 9
1326					&& f[EDGE_FACELET[i][1] as usize] == EDGE_FACELET[j][1] / 9
1327				{
1328					self.ea[i] = (j as u8) << 1;
1329					break;
1330				}
1331				if f[EDGE_FACELET[i][0] as usize] == EDGE_FACELET[j][1] / 9
1332					&& f[EDGE_FACELET[i][1] as usize] == EDGE_FACELET[j][0] / 9
1333				{
1334					self.ea[i] = ((j as u8) << 1) | 1;
1335					break;
1336				}
1337			}
1338		}
1339		0
1340	}
1341
1342	fn to_facelet(&self) -> String {
1343		let colors: [char; 6] = ['U', 'R', 'F', 'D', 'L', 'B'];
1344		let mut f: [u8; 54] = [0; 54];
1345		for i in 0..54 {
1346			f[i] = (i as u8) / 9;
1347		}
1348		for c in 0..8 {
1349			let j = (self.ca[c] & 0x7) as usize;
1350			let ori = (self.ca[c] >> 3) as usize;
1351			for n in 0..3 {
1352				f[CORNER_FACELET[c][(n + ori) % 3] as usize] = CORNER_FACELET[j][n] / 9;
1353			}
1354		}
1355		for e in 0..12 {
1356			let j = (self.ea[e] >> 1) as usize;
1357			let ori = (self.ea[e] & 1) as usize;
1358			for n in 0..2 {
1359				f[EDGE_FACELET[e][(n + ori) % 2] as usize] = EDGE_FACELET[j][n] / 9;
1360			}
1361		}
1362		let mut buf = String::new();
1363		for i in 0..54 {
1364			buf.push(colors[f[i] as usize]);
1365		}
1366		buf
1367	}
1368}
1369
1370lazy_static! {
1371	static ref global_sctx: Box<StaticContext> = StaticContext::box_new();
1372	static ref global_stbl: Box<StaticTables> = StaticTables::box_new(&global_sctx);
1373}
1374
1375
1376/// Solve a Rubik's cube represented in facelet
1377///
1378/// # Arguments
1379///
1380/// * `facelet` - the Rubik's cube to be solved, represented in facelet
1381/// * `maxl` - number of moves to solve the cube, included. 21 or 20 is recommended.
1382///
1383/// Facelet for the rubik's cube:
1384/// ```text
1385///          +--------+
1386///          |U1 U2 U3|
1387///          |U4 U5 U6|
1388///          |U7 U8 U9|
1389/// +--------+--------+--------+--------+
1390/// |L1 L2 L3|F1 F2 F3|R1 R2 R3|B1 B2 B3|
1391/// |L4 L5 L6|F4 F5 F6|R4 R5 R6|B4 B5 B6|
1392/// |L7 L8 L9|F7 F8 F9|R7 R8 R9|B7 B8 B9|
1393/// +--------+--------+--------+--------+
1394///          |D1 D2 D3|
1395///          |D4 D5 D6|
1396///          |D7 D8 D9|
1397///          +--------+
1398/// ```
1399/// should be: U1U2...U9R1R2...R9F1..F9D1..D9L1..L9B1..B9
1400/// Example, facelet of solved cube is UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB
1401///
1402/// Return solution moves on success, return "Error " + error_code on failure
1403pub fn solve(facelet: &String, maxl: u8) -> String {
1404	let mut cc = Cubie::new();
1405	if cc.from_facelet(facelet) < 0 {
1406		return String::from("Error 1");
1407	}
1408	let verify = cc.verify();
1409	if verify < 0 {
1410		return String::from("Error ") + &(-verify).to_string();
1411	}
1412	let mut ctx = IdaContext::new();
1413	return ctx.solve_cubie(&global_sctx, &global_stbl, &cc, std::cmp::min(25, maxl) as i8)
1414}
1415
1416/// Generate a random cube represented in facelet
1417pub fn random_cube() -> String {
1418	let mut cc = Cubie::new();
1419	cc.random_reset();
1420	cc.to_facelet()
1421}
1422
1423/// Apply moves to a solved Rubik's cube
1424///
1425/// # Arguments
1426///
1427/// * `cube_moves` - should match ```([URFDLB][123'] ?)*```
1428///
1429/// Return ```facelet``` on success
1430pub fn from_moves(cube_moves: &String) -> Option<String> {
1431	apply_moves(&Cubie::new().to_facelet(), cube_moves)
1432}
1433
1434/// Apply moves to a Rubik's cube represented by facelet
1435///
1436/// # Arguments
1437///
1438/// * `facelet` - the Rubik's cube to be moved, must be a solvable Rubik's cube
1439/// * `cube_moves` - should match ```([URFDLB][123'] ?)*```
1440///
1441/// Return ```facelet``` of the moved cube on success
1442pub fn apply_moves(facelet: &String, cube_moves: &String) -> Option<String> {
1443	let mut cc = Cubie::new();
1444	if cc.from_facelet(facelet) < 0 {
1445		return None;
1446	}
1447	let verify = cc.verify();
1448	if verify < 0 {
1449		return None;
1450	}
1451	let mut s = cube_moves.trim().chars().peekable();
1452	let mut axis = 0;
1453	let mut pow = 0;
1454	let mut cd = Cubie::new();
1455	while let Some(c) = s.next() {
1456		match c {
1457			'U' | 'R' | 'F' | 'D' | 'L' | 'B' => {
1458				if pow != 0 {
1459					Cubie::corn_mult(&cc, &global_sctx.movecube[axis * 3 + pow - 1], &mut cd);
1460					Cubie::edge_mult(&cc, &global_sctx.movecube[axis * 3 + pow - 1], &mut cd);
1461					cc = cd;
1462				}
1463				pow = 1;
1464				match c {
1465					'U' => { axis = 0; },
1466					'R' => { axis = 1; },
1467					'F' => { axis = 2; },
1468					'D' => { axis = 3; },
1469					'L' => { axis = 4; },
1470					'B' => { axis = 5; },
1471					_ => (),
1472				}
1473			},
1474			'\'' | '-' => pow = (4 - pow) % 4,
1475			'3' => pow = pow * 3 % 4,
1476			'2' => pow = pow * 2 % 4,
1477			'+' | '1' | ' ' | '\t' => (),
1478			_ => {
1479				return None;
1480			}
1481		}
1482	}
1483
1484	if pow != 0 {
1485		Cubie::corn_mult(&cc, &global_sctx.movecube[axis * 3 + pow - 1], &mut cd);
1486		Cubie::edge_mult(&cc, &global_sctx.movecube[axis * 3 + pow - 1], &mut cd);
1487		cc = cd;
1488	}
1489	Some(cc.to_facelet())
1490}
1491
1492/// Generate a random move sequence in specific number of moves
1493///
1494/// # Arguments
1495///
1496/// * `n_moves` - number of moves
1497///
1498/// Return moves, ensure no redaudant moves exists, e.g. "R R", "R L R", etc.
1499///
1500/// Call ```from_moves(cube_moves)``` to obtain the scrambled cube
1501pub fn random_moves(n_moves: u16) -> String {
1502	let mut rng = rand::thread_rng();
1503	let mut last_axis = 18;
1504	let mut scramble = String::new();
1505	let mut i = 0;
1506	while i < n_moves {
1507		let mv = rng.gen_range(0..18);
1508		let axis = mv / 3;
1509		if axis == last_axis || (axis % 3 == last_axis % 3 && axis > last_axis) {
1510			continue;
1511		}
1512		last_axis = axis;
1513		scramble.push_str(MOVE2STR[mv]);
1514		scramble.push_str(" ");
1515		i += 1
1516	}
1517	scramble
1518}