#!/usr/bin/ruby

# Liniar ecuations solver - Cramer's method.

# Example:
# | 2x - 3y +  z =  4
# |  x - 2y - 2z = -6
# | 3x - 4y +  z =  5

var matrix = [
    [2, -3,  1],
    [1, -2, -2],
    [3, -4,  1],
];

var free_terms = [4, -6, 5];

func diag_multiply_sum (matrix, left) {

    var r1    = matrix[0].range;   # range from 0 to len(matrix[0])-1
    var products = [];

    (left ? r1.reverse : r1).each { |i|

        var x = 0;
        var nums = [];
        var r2 = i.to(matrix[0].end+i);   # range from i to len(matrix[0])-1+i

        (left ? r2.reverse : r2).each { |j|
            nums.push(matrix[j][x++]);
        }

        products.push(nums.prod);       # appending the product of 'nums' inside the 'products' array
    }

    "sum %s\n".printf(products.dump);

    return(products.sum);
}

func make_det_matrix (matrix) {
    matrix + [matrix[matrix.end.range...]];
}

func calculate_delta (matrix, free_terms, position) {

    # Make a deep clone of the matrix
    matrix = matrix.dclone;

    free_terms.range.each { |i|
        matrix[i][position] = free_terms[i];    # substituting the 'pos' column with the free terms
    };

    var det_matrix = make_det_matrix(matrix);
    var right      = diag_multiply_sum(det_matrix, false);
    var left       = diag_multiply_sum(det_matrix, true);

    var diff = (right - left);
    "%d - %d == %d\n\n".printf(right, left, diff);

    return(diff);
}

var det_matrix = make_det_matrix(matrix);

var right = diag_multiply_sum(det_matrix, false);
var left  = diag_multiply_sum(det_matrix, true);

var delta = (right - left);
"%d - %d == %d\n\n".printf(right, left, delta);

"Delta == %s\n\n".printf(delta);

var xyz = [];
free_terms.range.each { |i|
    xyz[i] = calculate_delta(
                matrix:     matrix,
                free_terms: free_terms,
                position:   i,
            )
}

var indices = [
    ['X', 0],
    ['Y', 1],
    ['Z', 2],
]

indices.each {
    var i = _[1];
    "Delta %s: %d\n".printf(_[0], xyz[i]);
}

"\n".print;

indices.each {
    var i = _[1];
    "%s == %3d/%-3d == %3s\n".printf(_[0], xyz[i], delta, xyz[i] / delta);
}

assert_eq(xyz, [6,3,9]);
