// Sudoku of format

// | a11 | a12 || b11 | b12 |
// --------------------------
// | a21 | a22 || b21 | b22 |
// ==========================
// | c11 | c12 || d11 | d12 |
// --------------------------
// | c21 | c22 || d21 | d22 |

type Grid<N> = field[N][N];
const field[4] PRIMES = [2, 3, 5, 7];

// We encode values in the following way:
// 1 -> 2
// 2 -> 3
// 3 -> 5
// 4 -> 7

// returns true if there are no duplicates
// assumption: `a, b, c, d` are all in `{ 2, 3, 5, 7 }`
def checkNoDuplicates(field a, field b, field c, field d) -> bool {
    // as `{ 2, 3, 5, 7 }` are primes, the set `{ a, b, c, d }` is equal to the set `{ 2, 3, 5, 7}` if and only if the products match
    return a * b * c * d == PRIMES[0] * PRIMES[1] * PRIMES[2] * PRIMES[3];
}

// returns true if and only if `x` is one of the `4` primes
def validateInput(field x) -> bool {
    field mut res = 1;

    for u32 i in 0..4 {
        res = res * (x - PRIMES[i]);
    }

    return res == 0;
}

// variables naming: box'row''column'
def main(field a21, field b11, field b22, field c11, field c22, field d21, private field a11, private field a12, private field a22, private field b12, private field b21, private field c12, private field c21, private field d11, private field d12, private field d22) -> bool {

    Grid<4> a = [[a11, a12, b11, b12], [a21, a22, b21, b22], [c11, c12, d11, d12], [c21, c22, d21, d22]];
    bool mut res = true;

    // go through the whole grid and check that all elements are valid
    for u32 i in 0..4 {
        for u32 j in 0..4 {
            res = res && validateInput(a[i][j]);
        }
    }

    // go through the 4 2x2 boxes and check that they do not contain duplicates
    for u32 i in 0..1 {
        for u32 j in 0..1 {
            res = res && checkNoDuplicates(a[2*i][2*i], a[2*i][2*i + 1], a[2*i + 1][2*i], a[2*i + 1][2*i + 1]);
        }
    }

    // go through the 4 rows and check that they do not contain duplicates
    for u32 i in 0..4 {
        res = res && checkNoDuplicates(a[i][0], a[i][1], a[i][2], a[i][3]);
    }

    // go through the 4 columns and check that they do not contain duplicates
    for u32 j in 0..4 {
        res = res && checkNoDuplicates(a[0][j], a[1][j], a[2][j], a[3][j]);
    }

    return res;
}
