#!/usr/bin/ruby

#
## https://rosettacode.org/wiki/Reduced_row_echelon_form
#

func rref (Array m) {
    m.is_empty && return();
    var (lead, rows, cols) = (0, m.len, m[0].len);

    rows.range.each { |r|
        lead >= cols && return m;
        var i = r;

        while (!m[i][lead]) {
            ++i == rows || next;
            i = r;
            ++lead == cols && return m;
        }

        m[i, r] = m[r, i];
        var lv = m[r][lead];
        m[r] = (m[r] »/» lv);

        rows.range.each { |n|
            n == r && next;
            m[n] = (m[n] »-« (m[r] «*« m[n][lead]))
        }
        ++lead;
    }
    return m
}

func say_it (message, array) {
    say "\n#{message}";
    array.each { |row|
        say row.map { |n| " %5s" % n.as_rat }.join
    }
}

var M = [
    [ # base test case
      [  1,  2,  -1,  -4 ],
      [  2,  3,  -1, -11 ],
      [ -2,  0,  -3,  22 ],
    ],
    [ # mix of number styles
      [  3,   0,  -3,    1 ],
      [ .5, 3/2,  -3,   -2 ],
      [ .2, 4/5,  -1.6, .3 ],
    ],
    [ # degenerate case
      [ 1,  2,  3,  4,  3,  1],
      [ 2,  4,  6,  2,  6,  2],
      [ 3,  6, 18,  9,  9, -6],
      [ 4,  8, 12, 10, 12,  4],
      [ 5, 10, 24, 11, 15, -4],
    ],
];

M.each { |matrix|
    say_it('Original Matrix', matrix);
    say_it('Reduced Row Echelon Form Matrix', rref(matrix));
    say '';
}
