const expect = require('chai').expect;
const ArgumentError = require('../src/js/ArgumentError.js');
const Complex = require('../src/js/Complex.js');
const Matrix = require('../src/js/Matrix.js');
const Monomial = require('../src/js/Monomial.js');
const Term = require('../src/js/Term.js');
const Polynomial = require('../src/js/Polynomial.js');

describe('Matrix', function() {
    describe('det', function() {
        it('computes the determinant of a 2x2 matrix', function() {
            let m = new Matrix([
                [Complex.real(1), Complex.real(2)],
                [Complex.real(3), Complex.real(4)]
            ]);
            let det = m.det();
            expect(det).to.deep.equal(new Complex(-2, -0));
        });
        it('computes the determinant of a 3x3 identity matrix', function() {
            let m = new Matrix([
                [Complex.real(1), Complex.real(0), Complex.real(0)],
                [Complex.real(0), Complex.real(1), Complex.real(0)],
                [Complex.real(0), Complex.real(0), Complex.real(1)]
            ]);
            let det = m.det();
            expect(det).to.deep.equal(Complex.real(1));
        });

        it('computes the determinant of a 3x3 integer diagonal matrix', function() {
            let m = new Matrix([
                [Complex.real(2), Complex.real(0), Complex.real(0)],
                [Complex.real(0), Complex.real(3), Complex.real(0)],
                [Complex.real(0), Complex.real(0), Complex.real(4)]
            ]);
            let det = m.det();
            expect(det).to.deep.equal(Complex.real(24));
        });

        it('computes the determinant of a 3x3 integer matrix', function() {
            let m = new Matrix([
                [Complex.real(1), Complex.real(2), Complex.real(4)],
                [Complex.real(3), Complex.real(5), Complex.real(6)],
                [Complex.real(7), Complex.real(8), Complex.real(9)]
            ]);
            let det = m.det();
            expect(det).to.deep.equal(Complex.real(-17));
        });

        it('computes the determinant of a 3x3 integer Polynomial matrix', function() {
            function polyX(n) {
                return new Polynomial([new Term(Complex.real(n), new Monomial({
                    'x': 1
                }))]);
            }

            function polyX3(n) {
                return new Polynomial([new Term(Complex.real(n), new Monomial({
                    'x': 3
                }))]);
            }
            let m = new Matrix([
                [polyX(1), polyX(2), polyX(4)],
                [polyX(3), polyX(5), polyX(6)],
                [polyX(7), polyX(8), polyX(9)]
            ]);
            let det = m.det();
            expect(det.coefficientList_()).to.deep.equal(polyX3(-17).coefficientList_());
        });
        it('throws an ArgumentError if Matrix is empty', function() {
            let m = new Matrix([
                []
            ]);
            expect(function() {
                m.det();
            }).to.throw(ArgumentError, 'empty');
        });
        it('throws an ArgumentError if Matrix is non-square', function() {
            let m = new Matrix([
                [Complex.real(1), Complex.real(2), Complex.real(3)],
                [Complex.real(4), Complex.real(5), Complex.real(6)]
            ]);
            expect(function() {
                m.det();
            }).to.throw(ArgumentError, 'square');
        });
    });
    describe('Matrix.mul', function() {
        it('computes the product of two matrices', function() {
            let m1 = new Matrix([
                [Complex.real(1), Complex.real(2)],
                [Complex.real(3), Complex.real(4)]
            ]);
            let m2 = new Matrix([
                [Complex.real(5), Complex.real(6)],
                [Complex.real(7), Complex.real(8)]
            ]);
            let product = new Matrix([
                [Complex.real(19), Complex.real(22)],
                [Complex.real(43), Complex.real(50)]
            ]);
            expect(Matrix.mul(m1, m2)).to.deep.equal(product);
        });
        it('throws an ArgumentError if Matrix dimensions do not match', function() {
            let m = new Matrix([
                [1, 2, 3],
                [4, 5, 6]
            ]);
            expect(function() {
                Matrix.mul(m, m);
            }).to.throw(ArgumentError, 'dimensions');
        });
    });
});

@@ -709,10 +709,21 @@
 lex(
--2
+1 * 4 - 2 * 3
 , -0
@@ -1593,16 +1593,23 @@
 x.real(2
+ * 3 * 
 4));%0A   
@@ -2031,19 +2031,79 @@
 ex.real(
--17
+1 * (5 * 9 - 6 * 8) - 3 * (2 * 9 - 4 * 8) + 7 * (2 * 6 - 4 * 5)
 ));%0A    
@@ -3535,23 +3535,16 @@
 scribe('
-Matrix.
 mul', fu
@@ -3693,32 +3693,49 @@
  Complex.real(2)
+, Complex.real(3)
 %5D,%0A             
@@ -3743,33 +3743,50 @@
   %5BComplex.real(
-3
+4), Complex.real(5
 ), Complex.real(
@@ -3777,33 +3777,33 @@
 ), Complex.real(
-4
+6
 )%5D%0A            %5D
@@ -3869,34 +3869,87 @@
 ex.real(
-5), Complex.real(6
+7), Complex.real(8)%5D,%0A                %5BComplex.real(9), Complex.real(10
 )%5D,%0A    
@@ -3966,33 +3966,34 @@
   %5BComplex.real(
-7
+11
 ), Complex.real(
@@ -3992,17 +3992,18 @@
 ex.real(
-8
+12
 )%5D%0A     
@@ -4087,81 +4087,163 @@
 al(1
-9), Complex.real(22)%5D,%0A                %5BComplex.real(43), Complex.real(50
+ * 7 + 2 * 9 + 3 * 11), Complex.real(1 * 8 + 2 * 10 + 3 * 12)%5D,%0A                %5BComplex.real(4 * 7 + 5 * 9 + 6 * 11), Complex.real(4 * 8 + 5 * 10 + 6 * 12
 )%5D%0A 
@@ -4471,42 +4471,126 @@
    %5B
-1, 2, 3%5D,%0A                %5B4, 5, 6
+Complex.real(1), Complex.real(2), Complex.real(3)%5D,%0A                %5BComplex.real(4), Complex.real(5), Complex.real(6)
 %5D%0A  

Improve Matrix tests