#!/usr/bin/ruby

var a = [
          [1, 2],
          [3, 4],
          [5, 6],
          [7, 8]
        ]

var b = [
          [1, 2, 3],
          [4, 5, 6]
        ]

assert_eq(a `mmul` b,
    [
        [ 9,  12,  15],
        [19,  26,  33],
        [29,  40,  51],
        [39,  54,  69],
    ]
)

var A =
    [
         [-75, -49,  56, -62],
         [ 36,  86, -20,  88],
         [-56,  20, -97, -25],
         [ 75, -91, -41, -38],
    ]

assert_eq(A.det, A.det_bareiss)
assert_eq(A.invert.invert, A)

assert_eq([A,A,A].combine {|*a| a.sum }, Matrix(A...) * 3)
assert_eq([A,A,A,A].combine {|*a| a.sum }, Matrix(A...) * 4)

do {
    var a = [[6, 6], [4, 4]]
    var b = [[1, 2], [3, 4]]
    var c = [[9, 5], [7, 2]]

    assert_eq([a,b,c].combine{|x,y,z|
        x + y + z
    }, [[16, 13], [14, 10]])
}

var B = [
    [2, -1,  5,  1],
    [3,  2,  2, -6],
    [1,  3,  3, -1],
    [5, -2, -3,  3],
]

assert_eq(B.inv, [
    [  4/171,   11/171,  10/171,   8/57],
    [-55/342,  -23/342, 119/342,   2/57],
    [107/684,   -5/684,  11/684, -7/114],
    [  7/684, -109/684, 103/684,  7/114],
])

assert_eq(B.inv.inv, B)
assert_eq(B `msolve` [-3, -32, -47, 49], [2, -12, -4, 1])
assert_eq(B.det, 684)
assert_eq(B.inv.det, 1/684)

assert_eq([].det, 1)
assert_eq([[]].det, 1)

assert_eq([[]].inv, [[]])
assert_eq([].inv, [])

assert_eq(b `smul` 2, [[1*2, 2*2, 3*2], [4*2, 5*2, 6*2]])
assert_eq(b `ssub` 3, [[1-3, 2-3, 3-3], [4-3, 5-3, 6-3]])
assert_eq(b `sadd` 7, [[1+7, 2+7, 3+7], [4+7, 5+7, 6+7]])
assert_eq(b `sdiv` 2, [[1/2, 2/2, 3/2], [4/2, 5/2, 6/2]])

assert_eq(b `scalar_op` ( '*',  2), [[1*2, 2*2, 3*2], [4*2, 5*2, 6*2]])
assert_eq(b `scalar_op` ('mul', 2), [[1*2, 2*2, 3*2], [4*2, 5*2, 6*2]])

assert_eq(b `scalar_op` ( '/',  2), [[1/2, 2/2, 3/2], [4/2, 5/2, 6/2]])
assert_eq(b `scalar_op` ('div', 2), [[1/2, 2/2, 3/2], [4/2, 5/2, 6/2]])

assert_eq([1,2,3,4] `smul` 4, [1*4, 2*4, 3*4, 4*4])
assert_eq([[[1,2,[3,4]],[9,10]], [1,3], 4] `smul` 5, [[[1*5,2*5,[3*5,4*5]],[9*5,10*5]], [1*5,3*5], 4*5])

assert_eq(a `madd` (a `smul` 3), [
        [1 + (1*3), 2 + (2*3)],
        [3 + (3*3), 4 + (4*3)],
        [5 + (5*3), 6 + (6*3)],
        [7 + (7*3), 8 + (8*3)],
    ])

assert_eq(a `wise_op` ('+', a `smul` 3), [
        [1 + (1*3), 2 + (2*3)],
        [3 + (3*3), 4 + (4*3)],
        [5 + (5*3), 6 + (6*3)],
        [7 + (7*3), 8 + (8*3)],
    ])

assert_eq(a `msub` (a `smul` 3), [
        [1 - (1*3), 2 - (2*3)],
        [3 - (3*3), 4 - (4*3)],
        [5 - (5*3), 6 - (6*3)],
        [7 - (7*3), 8 - (8*3)],
    ])

assert_eq(a ~W- (a ~S* 3), [
        [1 - (1*3), 2 - (2*3)],
        [3 - (3*3), 4 - (4*3)],
        [5 - (5*3), 6 - (6*3)],
        [7 - (7*3), 8 - (8*3)],
    ])

assert_eq(b `madd` (b `smul` 3), [
        [1 + (1*3), 2 + (2*3), 3 + (3*3)],
        [4 + (4*3), 5 + (5*3), 6 + (6*3)],
    ])

assert_eq(b ~W+ (b ~S* 3), [
        [1 + (1*3), 2 + (2*3), 3 + (3*3)],
        [4 + (4*3), 5 + (5*3), 6 + (6*3)],
    ])

assert_eq(b ~W+ (b ~RS* 3), [
        [1 + (1*3), 2 + (2*3), 3 + (3*3)],
        [4 + (4*3), 5 + (5*3), 6 + (6*3)],
    ])

assert_eq(a ~RS/ 1, [
        [1/1, 1/2],
        [1/3, 1/4],
        [1/5, 1/6],
        [1/7, 1/8]
    ])

assert_eq(wise_op([1,2,[3]], '+', [4,5,[6]]), [5, 7, [9]])
assert_eq(wise_op([1,2,3], '', [4,5,6]), [[1, 4], [2, 5], [3, 6]])

assert_eq([1,2,3] ~W [4,5,6], [[1, 4], [2, 5], [3, 6]])
assert_eq([1,2,3] ~S 5, [[1,5], [2,5], [3,5]])

assert_eq([1,2,3] ~S+ 5, [1+5, 2+5, 3+5])
assert_eq([1,[[2],3]] ~S* 5, [1*5, [[2*5], 3*5]])
assert_eq([1,[[2,[3]]]] ~S* 5, [1*5, [[2*5, [3*5]]]])
assert_eq([1,[[[2],[3]]]] ~S* 5, [1*5, [[[2*5], [3*5]]]])
assert_eq([1,[[[2],[3]]]] ~RS* 5, [1*5, [[[2*5], [3*5]]]])

assert_eq([1,[[2,[3]]]] ~W- [1,[[2,[3]]]], [0,[[0,[0]]]])
assert_eq([1,[[[2],[3]]]] ~W+ [1,[[[2],[3]]]], [2,[[[4],[6]]]])

do {
    var a = [1,2,3]
    a[1] = a

    var b = [41, 42, 43]
    b[1] = b

    var c = [99, [5, 6], 101]

    var z  = (a ~W+ b)
    var z2 = [a,b].combine {|x,y| x +y }

    assert_eq(z[0], 42)
    assert_eq(z[1], z)
    assert_eq(z[2], 46)

    assert_eq(z2[0], 42)
    assert_eq(z2[1], z2)
    assert_eq(z2[2], 46)

    assert_eq(z, z)
    assert_eq(z2, z2)

    assert_eq([z...], [z...])
    assert_eq([z2...], [z2...])

    var z2 = (a ~S* 3)
    assert_eq(z2[0], 3)
    assert_eq(z2[1], z2)
    assert_eq(z2[2], 9)
    assert_eq(z2, z2)

    var z3 = (b ~RS/ 1)
    assert_eq(z3[0], 1/41)
    assert_eq(z3[1], z3)
    assert_eq(z3[2], 1/43)

    assert_ne(z3, z2)
    assert_ne(z3, z)
    assert_ne(z, z2)

    var z4 = (a ~W+ c)
    var z5 = (c ~W+ a)

    assert_eq(z4[0], 100)
    assert_eq(z4[2], 104)

    assert_eq(z5[0], 100)
    assert_eq(z5[2], 104)

    assert_ne(z5[1], z4[1])
    assert_ne(z4, z5)

    assert_eq(z4[1], z4)
    assert_eq(z5[1], c[1].map_kv{|k,v| v+a[k] })

    assert_ne(z4[1], z5)
    assert_ne(z5[1], z4)
}

say "** Test passed!"