//! returns: 0
//! Matrix multiplication.
//! Multiply two 4x4 integer matrices and verify the result against
//! a known expected output. Classic benchmark kernel.

const N: u32 = 4;

/// A 4x4 integer matrix (row-major).
record Mat4 {
    rows: [[i32; 4]; 4],
}

/// Perform matrix multiplication: c = a * b.
fn matmul(c: *mut Mat4, a: *Mat4, b: *Mat4) {
    let mut i: u32 = 0;
    while i < N {
        let mut j: u32 = 0;
        while j < N {
            let mut sum: i32 = 0;
            let mut k: u32 = 0;
            while k < N {
                sum += a.rows[i][k] * b.rows[k][j];
                k += 1;
            }
            c.rows[i][j] = sum;
            j += 1;
        }
        i += 1;
    }
}

/// Verify c matches expected.
fn verifyResult(c: *Mat4, expected: *Mat4) -> i32 {
    let mut i: u32 = 0;
    while i < N {
        let mut j: u32 = 0;
        while j < N {
            if c.rows[i][j] != expected.rows[i][j] {
                return (i * N + j) as i32 + 1;
            }
            j += 1;
        }
        i += 1;
    }
    return 0;
}

/// Compute the trace (sum of diagonal) of a matrix.
fn trace(m: *Mat4) -> i32 {
    let mut sum: i32 = 0;
    let mut i: u32 = 0;
    while i < N {
        sum += m.rows[i][i];
        i += 1;
    }
    return sum;
}

/// Zero out a matrix.
fn zero(m: *mut Mat4) {
    let mut i: u32 = 0;
    while i < N {
        let mut j: u32 = 0;
        while j < N {
            m.rows[i][j] = 0;
            j += 1;
        }
        i += 1;
    }
}

/// Multiply matrices multiple times to test repeated computation.
fn testRepeatedMultiply(c: *mut Mat4, a: *Mat4, b: *Mat4) -> i32 {
    // First pass already done, trace = 13+43+43+85 = 184.
    assert trace(c) == 184;

    // Zero out c and re-multiply to ensure idempotent.
    zero(c);
    matmul(c, a, b);
    assert trace(c) == 184;
    return 0;
}

@default fn main() -> i32 {
    let a = Mat4 { rows: [
        [ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12],
        [13, 14, 15, 16]
    ] };

    let b = Mat4 { rows: [
        [ 2,  0,  1,  3],
        [ 1,  2,  0,  1],
        [ 3,  1,  2,  0],
        [ 0,  3,  1,  2]
    ] };

    // Expected result of a * b:
    //   [0] = [1*2+2*1+3*3+4*0, 1*0+2*2+3*1+4*3, 1*1+2*0+3*2+4*1, 1*3+2*1+3*0+4*2] = [13, 19, 11, 13]
    //   [1] = [5*2+6*1+7*3+8*0, 5*0+6*2+7*1+8*3, 5*1+6*0+7*2+8*1, 5*3+6*1+7*0+8*2] = [37, 43, 27, 37]
    //   [2] = [9*2+10*1+11*3+12*0, ...] = [61, 67, 43, 61]
    //   [3] = [13*2+14*1+15*3+16*0, ...] = [85, 91, 59, 85]
    let expected = Mat4 { rows: [
        [13, 19, 11, 13],
        [37, 43, 27, 37],
        [61, 67, 43, 61],
        [85, 91, 59, 85]
    ] };

    let mut c = Mat4 { rows: [[0; 4]; 4] };
    matmul(&mut c, &a, &b);

    let r1 = verifyResult(&c, &expected);
    if r1 != 0 {
        return 10 + r1;
    }

    let r2 = testRepeatedMultiply(&mut c, &a, &b);
    if r2 != 0 {
        return 30 + r2;
    }
    return 0;
}
