test/matrix-test.ss
#|  matrix-test.ss: Test suite for the matrix library.
    Copyright (C) 2008 Will M. Farr <farr@mit.edu>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
|#

(module matrix-test "../matrix-lang.ss"
  (require (planet schematics/schemeunit:2/test)
           (planet schematics/schemeunit:2/text-ui))
  
  (define (random-matrix m n)
    (for/matrix m n
      ((i (in-range (* m n))))
      (random (* 2 m n))))
  
  (define (random-vector n)
    (build-vector n (lambda (i) (random (* n 2)))))
  
  (define (matrix-norm m)
  (for/fold ((norm -inf.0))
    ((x (in-matrix m)))
    (max norm (abs x))))
  
  (define tests
    (test-suite
     "simple-matrix.plt test suite"
     (test-case
      "matrix-ref and matrix-set!"
      (let ((m (make-matrix 2 3 '#(1 2 3 4 5 6))))
        (check-equal? (matrix-ref m 1 1) 5))
      (for ((i (in-range 100)))
        (let ((m (random-matrix 5 10)))
          (let ((i (random 5))
                (j (random 10))
                (x (random)))
            (matrix-set! m i j x)
            (check-equal? (matrix-ref m i j) x)))))
     (test-case
      "matrix-transpose"
      (for ((i (in-range 100)))
        (let ((m (random-matrix (add1 (random 10)) (add1 (random 10)))))
          (check-equal? m (matrix-transpose (matrix-transpose m))))))
     (test-case
      "algebraic operations on matrices"
      (for ((i (in-range 100)))
        (let ((i (add1 (random 10)))
              (j (add1 (random 10))))
          (let ((m1 (random-matrix i j))
                (m2 (random-matrix i j)))
            (check-equal? (+ m1 m2 (- m2)) m1)
            (check-equal? (+ m1 m1) (* m1 2))
            (check-equal? (+ m1 m1) (* 2 m1))
            (check-equal? (+ (- m1 m2) m2) m1)
            (check-equal? (- m1 m2) (* -1 (- m2 m1)))
            (check-equal? (- m1 m2) (/ (- m2 m1) -1))))))
     (test-case
      "algebraic operations on vectors (and dot product)"
      (check-equal? (* '#(15 21) '#(1 2)) 57)
      (for ((i (in-range 100)))
        (let ((n (random 10)))
          (let ((v1 (random-vector n))
                (v2 (random-vector n)))
            (check-equal? (+ v1 v1) (* v1 2))
            (check-equal? (+ v1 v1) (* 2 v1))
            (check-equal? (+ v1 v2 (- v2)) v1)
            (check-equal? (- v1 v2) (* -1 (- v2 v1)))
            (check-equal? (/ v1 -1) (- v1))
            (check-equal? (* v1 v2) (* v2 v1))))))
     (test-case
      "matrix-vector operations"
      (for ((i (in-range 100)))
        (let ((ni (random 10))
              (nj (random 10)))
          (let ((m (random-matrix ni nj))
                (vi (random-vector ni))
                (vj (random-vector nj)))
            (check-equal? (* m vj) (* vj (matrix-transpose m)))
            (check-equal? (* vi m) (* (matrix-transpose m) vi))
            (check-equal? (* vi (matrix-identity ni) vi) (* vi vi))
            (check-equal? (* vi m vj) (* vj (matrix-transpose m) vi))))))
     (test-case
      "matrix*"
      (for ((i (in-range 100)))
        (let ((v (random-vector 4)))
          (check-equal? (make-matrix 2 2 v) (apply matrix* 2 2 (vector->list v))))))
     (test-case
      "non-numeric matrices transpose"
      (check-equal? (matrix-transpose (matrix* 2 2 'a 'b 'c 'd))
                    (matrix* 2 2 'a 'c 'b 'd)))))
  
  (test/text-ui tests 'verbose))