use cryptocol::number::SmallUInt;
use cryptocol::random::{ Random, RandGen };
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_4X4X4 = MultiLayerSudoku<u8, 2>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_9X9X9 = MultiLayerSudoku<u8, 3>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_16X16X16 = MultiLayerSudoku<u8, 4>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_25X25X25 = MultiLayerSudoku<u8, 5>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_36X36X36 = MultiLayerSudoku<u8, 6>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_49X49X49 = MultiLayerSudoku<u8, 7>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_64X64X64 = MultiLayerSudoku<u8, 8>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_81X81X81 = MultiLayerSudoku<u8, 9>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_100X100X100 = MultiLayerSudoku<u8, 10>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_121X121X121 = MultiLayerSudoku<u8, 11>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_144X144X144 = MultiLayerSudoku<u8, 12>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_169X169X169 = MultiLayerSudoku<u8, 13>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_196X196X196 = MultiLayerSudoku<u8, 14>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_225X225X225 = MultiLayerSudoku<u8, 15>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_256X256X256 = MultiLayerSudoku<u16, 16>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_289X289X289 = MultiLayerSudoku<u16, 17>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_324X324X324 = MultiLayerSudoku<u16, 18>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_361X361X361 = MultiLayerSudoku<u16, 19>;
#[allow(non_camel_case_types)]
pub type MultiLayerSudoku_400X400X400 = MultiLayerSudoku<u16, 20>;
pub struct MultiLayerSudoku<T: SmallUInt = u8, const N: usize = 2>
{
sudoku: [[[[[[T; N]; N]; N]; N]; N]; N],
candidate: [[[[[[T; N]; N]; N]; N]; N]; N],
random: RandGen,
}
impl<T: SmallUInt, const N: usize> MultiLayerSudoku<T, N>
{
pub fn new() -> Option<Self>
{
if !Self::assert()
{
None
}
else
{
let mut me = Self
{
sudoku: [[[[[[T::MIN; N]; N]; N]; N]; N]; N],
candidate: [[[[[[T::MIN; N]; N]; N]; N]; N]; N],
random: Random::new(),
};
me.create_candidates();
Some(me)
}
}
pub fn new_with(problem: [[[[[[T; N]; N]; N]; N]; N]; N]) -> Option<Self>
{
if !Self::assert()
{
None
}
else
{
let mut me = Self
{
sudoku: problem,
candidate: [[[[[[T::MIN; N]; N]; N]; N]; N]; N],
random: Random::new(),
};
me.create_candidates();
Some(me)
}
}
pub fn new_with_3d<const M: usize>(problem: [[[T; M]; M]; M]) -> Option<Self>
{
if N * N != M || !Self::assert()
{ return None; }
let mut me = Self
{
sudoku: [[[[[[T::MIN; N]; N]; N]; N]; N]; N],
candidate: [[[[[[T::MIN; N]; N]; N]; N]; N]; N],
random: Random::new(),
};
for lay in 0..N
{
for la in 0..N
{
for row in 0..N
{
for col in 0..N
{
for ro in 0..N
{
for co in 0..N
{
let (ll, rr, cc) = Self::index_into_3d_view(lay, la, row, col, ro, co);
me.sudoku[lay][la][row][col][ro][co] = problem[ll][rr][cc];
}
}
}
}
}
}
me.create_candidates();
Some(me)
}
#[inline]
pub fn get_from_3d_view(&self, lay: usize, row: usize, col: usize) -> T
{
self.sudoku[lay / N][lay % N][row / N][col / N][row % N][col % N]
}
pub fn generate(&mut self, n_holes: usize)
{
self.sudoku = [[[[[[T::MIN; N]; N]; N]; N]; N]; N];
self.solve();
let mut mask = [[[[[[T::ONE; N]; N]; N]; N]; N]; N];
let (mut lay, mut la, mut row, mut col, mut ro, mut co) = (0_usize, 0_usize, 0_usize, 0_usize, 0_usize, 0_usize);
for _ in 0..(if n_holes <= N * N * N * N * N * N {n_holes} else {N * N * N * N * N * N})
{
mask[lay][la][row][col][ro][co] = T::MIN;
(lay, la, row, col, ro, co) = Self::advance_big(lay, la, row, col, ro, co);
}
self.shuffle_big(&mut mask);
for lay in 0..N
{
for la in 0..N
{
for row in 0..N
{
for col in 0..N
{
for ro in 0..N
{
for co in 0..N
{
if mask[lay][la][row][col][ro][co] == T::MIN
{ self.sudoku[lay][la][row][col][ro][co] = T::MIN; }
}
}
}
}
}
}
}
pub fn solve(&mut self) -> bool
{
self.solve_step_by_step( 0, 0, 0, 0, 0, 0)
}
fn solve_step_by_step(&mut self, mut lay: usize, mut la: usize, mut row: usize, mut col: usize, mut ro: usize, mut co: usize) -> bool
{
while self.sudoku[lay][la][row][col][ro][co] != T::MIN
{
(lay, la, row, col, ro, co) = Self::advance_big(lay, la, row, col, ro, co);
if lay == N
{ return true; }
}
for r in 0..N
{
for c in 0..N
{
let point = self.candidate[lay][la][row][col][r][c];
if self.check(lay, la, row, col, ro, co, point)
{
self.sudoku[lay][la][row][col][ro][co] = point;
(lay, la, row, col, ro, co) = Self::advance_big(lay, la, row, col, ro, co);
if lay == N || self.solve_step_by_step(lay, la, row, col, ro, co)
{ return true; }
(lay, la, row, col, ro, co) = Self::retreat_big(lay, la, row, col, ro, co);
self.sudoku[lay][la][row][col][ro][co] = T::MIN;
}
}
}
false
}
fn check(&self, lay: usize, la: usize, row: usize, col: usize, ro: usize, co: usize, point: T) -> bool
{
for r in 0..N
{
for c in 0..N
{
if ro == r && co == c
{ continue; }
if point == self.sudoku[lay][la][row][col][r][c]
{ return false; }
}
}
for l in 0..N
{
if l == la
{ continue; }
for rc in 0..N
{
if point == self.sudoku[lay][l][row][col][ro][rc]
|| point == self.sudoku[lay][l][row][col][rc][co]
{ return false; }
}
}
for rrcc in 0..N
{
if rrcc == row || rrcc == col
{ continue; }
for lrc in 0..N
{
if point == self.sudoku[lay][la][rrcc][col][lrc][co]
|| point == self.sudoku[lay][la][row][rrcc][ro][lrc]
{ return false; }
}
}
for ll in 0..N
{
for l in 0..N
{
if ll == lay && l == la
{ continue; }
if point == self.sudoku[ll][l][row][col][ro][co]
{ return false; }
}
}
true
}
fn create_candidates(&mut self)
{
for lay in 0..N
{
for la in 0..N
{
for row in 0..N
{
for col in 0..N
{
let mut candidate = T::MIN;
for ro in 0..N
{
for co in 0..N
{
candidate += T::ONE;
self.candidate[lay][la][row][col][ro][co] = candidate;
}
}
self.shuffle_candidates(lay, la, row, col);
}
}
}
}
}
fn shuffle_candidates(&mut self, lay: usize, la: usize, row: usize, col: usize)
{
let (mut ro, mut co) = (N - 1, N - 1);
while (ro, co) != (0, 0)
{
let r = self.random.random_under_uint_(ro + 1);
let c = self.random.random_under_uint_(co + 1);
let tmp = self.candidate[lay][la][row][col][ro][co];
self.candidate[lay][la][row][col][ro][co] = self.candidate[lay][la][row][col][r][c];
self.candidate[lay][la][row][col][r][c] = tmp;
(ro, co) = Self::retreat_small(ro, co);
}
}
fn shuffle_big(&mut self, elem: &mut [[[[[[T; N]; N]; N]; N]; N]; N])
{
let (mut lay, mut la, mut row, mut col, mut ro, mut co) = (N - 1, N - 1, N - 1, N - 1, N - 1, N - 1);
while (lay, la, row, col, ro, co) != (0, 0, 0, 0, 0, 0)
{
let ll = self.random.random_under_uint_(lay + 1);
let l = self.random.random_under_uint_(la + 1);
let rr = self.random.random_under_uint_(row + 1);
let cc = self.random.random_under_uint_(col + 1);
let r = self.random.random_under_uint_(ro + 1);
let c = self.random.random_under_uint_(co + 1);
let tmp = elem[lay][la][row][col][ro][co];
elem[lay][la][row][col][ro][co] = elem[ll][l][rr][cc][r][c];
elem[ll][l][rr][cc][r][c] = tmp;
(lay, la, row, col, ro, co) = Self::retreat_big(lay, la, row, col, ro, co);
}
}
fn carry(mut high: usize, mut low: usize) -> (usize, usize)
{
if low == N
{
low = 0;
high += 1;
}
(high, low)
}
#[allow(unused)]
fn advance_small(ro: usize, co: usize) -> (usize, usize)
{
Self::carry(ro % N, (co % N) + 1)
}
#[allow(unused)]
fn advance_middle(mut row: usize, mut col: usize, mut ro: usize, mut co: usize) -> (usize, usize, usize, usize)
{
(ro, co) = Self::advance_small(ro % N, co % N);
(col, ro) = Self::carry(col % N, ro);
(row, col) = Self::carry(row % N, col);
(row, col, ro, co)
}
fn advance_big(mut lay: usize, mut la: usize, mut row: usize, mut col: usize, mut ro: usize, mut co: usize) -> (usize, usize, usize, usize, usize, usize)
{
(ro, co) = Self::advance_small(ro % N, co % N);
(col, ro) = Self::carry(col % N, ro);
(row, col) = Self::carry(row % N, col);
(la, row) = Self::carry(la % N, row);
(lay, la) = Self::carry(lay % N, la);
(lay, la, row, col, ro, co)
}
fn borrow(mut high: usize, mut low: usize) -> (usize, usize)
{
if low == N
{
low = N - 1;
if high == 0
{ high = N; }
else
{ high -= 1; }
}
(high, low)
}
fn retreat_small(ro: usize, mut co: usize) -> (usize, usize)
{
co = co % N;
if co != 0
{ co -= 1; }
else
{ co = N; }
Self::borrow(ro % N, co)
}
#[allow(unused)]
fn retreat_middle(mut row: usize, mut col: usize, mut ro: usize, mut co: usize) -> (usize, usize, usize, usize)
{
co = co % N;
if co != 0
{ co -= 1; }
else
{ co = N; }
(ro, co) = Self::borrow(ro % N, co);
(col, ro) = Self::borrow(col % N, ro);
(row, col) = Self::borrow(row % N, col);
(row, col, ro, co)
}
fn retreat_big(mut lay: usize, mut la: usize, mut row: usize, mut col: usize, mut ro: usize, mut co: usize) -> (usize, usize, usize, usize, usize, usize)
{
co = co % N;
if co != 0
{ co -= 1; }
else
{ co = N; }
(ro, co) = Self::borrow(ro % N, co);
(col, ro) = Self::borrow(col % N, ro);
(row, col) = Self::borrow(row % N, col);
(la, row) = Self::borrow(la % N, row);
(lay, la) = Self::borrow(lay % N, la);
(lay, la, row, col, ro, co)
}
#[inline]
fn index_into_3d_view(lay: usize, la: usize, row: usize, col: usize, ro: usize, co: usize) -> (usize, usize, usize)
{
(lay * N + la, row * N + ro, col * N + co)
}
#[allow(unused)]
#[inline]
fn index_into_6d_view(lay: usize, row: usize, col: usize) -> (usize, usize, usize, usize, usize, usize)
{
(lay / N, lay % N, row / N, col / N, row % N, col % N)
}
#[inline]
fn assert() -> bool
{
N > 1 && T::MAX.into_u128() >= (N as u128 * N as u128)
}
}