use cryptocol::number::SmallUInt;
use cryptocol::random::{ Random, RandGen };
#[allow(non_camel_case_types)]
pub type PlaneSudoku_4X4 = PlaneSudoku<u8, 2>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_9X9 = PlaneSudoku<u8, 3>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_16X16 = PlaneSudoku<u8, 4>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_25X25 = PlaneSudoku<u8, 5>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_36X36 = PlaneSudoku<u8, 6>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_49X49 = PlaneSudoku<u8, 7>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_64X64 = PlaneSudoku<u8, 8>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_81X81 = PlaneSudoku<u8, 9>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_100X100 = PlaneSudoku<u8, 10>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_121X121 = PlaneSudoku<u8, 11>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_144X144 = PlaneSudoku<u8, 12>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_169X169 = PlaneSudoku<u8, 13>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_196X196 = PlaneSudoku<u8, 14>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_225X225 = PlaneSudoku<u8, 15>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_256X256 = PlaneSudoku<u16, 16>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_289X289 = PlaneSudoku<u16, 17>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_324X324 = PlaneSudoku<u16, 18>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_361X361 = PlaneSudoku<u16, 19>;
#[allow(non_camel_case_types)]
pub type PlaneSudoku_400X400 = PlaneSudoku<u16, 20>;
pub struct PlaneSudoku<T: SmallUInt = u8, const N: usize = 3>
{
sudoku: [[[[T; N]; N]; N]; N],
candidate: [[[[T; N]; N]; N]; N],
random: RandGen,
}
impl<T: SmallUInt, const N: usize> PlaneSudoku<T, N>
{
pub fn new() -> Option<Self>
{
if !Self::assert()
{
None
}
else
{
let mut me = Self
{
sudoku: [[[[T::MIN; N]; N]; N]; N],
candidate: [[[[T::MIN; N]; N]; N]; N],
random: Random::new(),
};
me.create_candidates();
Some(me)
}
}
pub fn new_with(problem: [[[[T; N]; N]; N]; N]) -> Option<Self>
{
if !Self::assert()
{
None
}
else
{
let mut me = Self
{
sudoku: problem,
candidate: [[[[T::MIN; N]; N]; N]; N],
random: Random::new(),
};
me.create_candidates();
Some(me)
}
}
pub fn new_with_2d<const M: usize>(problem: [[T; M]; M]) -> Option<Self>
{
if N * N != M || !Self::assert()
{ return None; }
let mut me = Self
{
sudoku: [[[[T::MIN; N]; N]; N]; N],
candidate: [[[[T::MIN; N]; N]; N]; N],
random: Random::new(),
};
for row in 0..N
{
for col in 0..N
{
for ro in 0..N
{
for co in 0..N
{
let (r, c) = Self::index_into_2d_view(row, col, ro, co);
me.sudoku[row][col][ro][co] = problem[r][c];
}
}
}
}
me.create_candidates();
Some(me)
}
#[inline]
pub fn get_from_2d_view(&self, row: usize, col: usize) -> T
{
self.sudoku[row / N][col / N][row % N][col % N]
}
pub fn generate(&mut self, n_holes: usize)
{
self.sudoku = [[[[T::MIN; N]; N]; N]; N];
self.solve();
let mut mask = [[[[T::ONE; N]; N]; N]; N];
let (mut row, mut col, mut ro, mut co) = (0_usize, 0_usize, 0_usize, 0_usize);
for _ in 0..(if n_holes <= N * N * N * N {n_holes} else {N * N * N * N})
{
mask[row][col][ro][co] = T::MIN;
(row, col, ro, co) = Self::advance_big(row, col, ro, co);
}
self.shuffle_big(&mut mask);
for row in 0..N
{
for col in 0..N
{
for ro in 0..N
{
for co in 0..N
{
if mask[row][col][ro][co] == T::MIN
{ self.sudoku[row][col][ro][co] = T::MIN; }
}
}
}
}
}
pub fn solve(&mut self) -> bool
{
self.solve_step_by_step( 0, 0, 0, 0)
}
fn solve_step_by_step(&mut self, mut row: usize, mut col: usize, mut ro: usize, mut co: usize) -> bool
{
while self.sudoku[row][col][ro][co] != T::MIN
{
(row, col, ro, co) = Self::advance_big(row, col, ro, co);
if row == N
{ return true; }
}
for r in 0..N
{
for c in 0..N
{
let point = self.candidate[row][col][r][c];
if self.check(row, col, ro, co, point)
{
self.sudoku[row][col][ro][co] = point;
(row, col, ro, co) = Self::advance_big(row, col, ro, co);
if row == N || self.solve_step_by_step(row, col, ro, co)
{ return true; }
(row, col, ro, co) = Self::retreat_big(row, col, ro, co);
self.sudoku[row][col][ro][co] = T::MIN;
}
}
}
false
}
fn check(&self, 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[row][col][r][c]
{ return false; }
}
}
for rrcc in 0..N
{
if rrcc == row || rrcc == col
{ continue; }
for rc in 0..N
{
if point == self.sudoku[rrcc][col][rc][co]
|| point == self.sudoku[row][rrcc][ro][rc]
{ return false; }
}
}
true
}
fn create_candidates(&mut self)
{
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[row][col][ro][co] = candidate;
}
}
self.shuffle_candidates(row, col);
}
}
}
fn shuffle_candidates(&mut self, 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[row][col][ro][co];
self.candidate[row][col][ro][co] = self.candidate[row][col][r][c];
self.candidate[row][col][r][c] = tmp;
(ro, co) = Self::retreat_small(ro, co);
}
}
fn shuffle_big(&mut self, elem: &mut [[[[T; N]; N]; N]; N])
{
let (mut row, mut col, mut ro, mut co) = (N - 1, N - 1, N - 1, N - 1);
while (row, col, ro, co) != (0, 0, 0, 0)
{
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[row][col][ro][co];
elem[row][col][ro][co] = elem[rr][cc][r][c];
elem[rr][cc][r][c] = tmp;
(row, col, ro, co) = Self::retreat_big(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)
}
fn advance_big(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 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)
}
fn retreat_big(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)
}
#[inline]
fn index_into_2d_view(row: usize, col: usize, ro: usize, co: usize) -> (usize, usize)
{
(row * N + ro, col * N + co)
}
#[allow(unused)]
#[inline]
fn index_into_4d_view(row: usize, col: usize) -> (usize, usize, usize, usize)
{
(row / N, col / N, row % N, col % N)
}
#[inline]
fn assert() -> bool
{
N > 1 && T::MAX.into_u128() >= (N as u128 * N as u128)
}
}