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
1376pub 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
1416pub fn random_cube() -> String {
1418 let mut cc = Cubie::new();
1419 cc.random_reset();
1420 cc.to_facelet()
1421}
1422
1423pub fn from_moves(cube_moves: &String) -> Option<String> {
1431 apply_moves(&Cubie::new().to_facelet(), cube_moves)
1432}
1433
1434pub 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
1492pub 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}