;; timing test for generalized ref/set!

(define invert-matrix
  (let ((+documentation+ "(invert-matrix matrix b (zero 1.0e-7)) inverts 'matrix'"))
    (lambda* (matrix b (zero 1.0e-7))
      ;; translated from Numerical Recipes (gaussj)
      (call-with-exit
       (lambda (return)
	 (let ((n (vector-dimension matrix 0)))
	   (let ((cols (make-int-vector n 0))
		 (rows (make-int-vector n 0))
		 (pivots (make-int-vector n 0)))
	     (do ((i 0 (+ i 1))
		  (col 0 0)
		  (row 0 0))
		 ((= i n))
	       (do ((biggest 0.0)
		    (j 0 (+ j 1)))
		   ((= j n)
		    (if (< biggest zero) 
			(return #f)))
		 (if (not (= (pivots j) 1))
		     (do ((k 0 (+ k 1)))
			 ((= k n))
		       (if (= (pivots k) 0)
			   (let ((val (abs (matrix j k))))
			     (when (> val biggest)
			       (set! col k)
			       (set! row j)
			       (set! biggest val)))
			   (if (> (pivots k) 1)
			       (return #f))))))
	       (set! (pivots col) (+ (pivots col) 1))
	       (if (not (= row col))
		   (let ((temp (if (sequence? b) (b row) 0.0)))
		     (when (sequence? b)
		       (set! (b row) (b col))
		       (set! (b col) temp))
		     (do ((k 0 (+ k 1)))
			 ((= k n))
		       (set! temp (matrix row k))
		       (set! (matrix row k) (matrix col k))
		       (set! (matrix col k) temp))))
	       (set! (cols i) col)
	       (set! (rows i) row)
	       ;; round-off troubles here
	       (if (< (abs (matrix col col)) zero)
		   (return #f))
	       (let ((inverse-pivot (/ 1.0 (matrix col col))))
		 (set! (matrix col col) 1.0)
		 (do ((k 0 (+ k 1)))
		     ((= k n))
		   (set! (matrix col k) (* inverse-pivot (matrix col k))))
		 (if b (set! (b col) (* inverse-pivot (b col)))))
	       (do ((k 0 (+ k 1)))
		   ((= k n))
		 (if (not (= k col))
		     (let ((scl (matrix k col)))
		       (set! (matrix k col) 0.0)
		       (do ((m 0 (+ 1 m)))
			   ((= m n))
			 (set! (matrix k m) (- (matrix k m) (* scl (matrix col m)))))
		       (if b (set! (b k) (- (b k) (* scl (b col)))))))))
	     (do ((i (- n 1) (- i 1)))
		 ((< i 0))
	       (if (not (= (rows i) (cols i)))
		   (do ((k 0 (+ k 1)))
		       ((= k n))
		     (let ((temp (matrix k (rows i))))
		       (set! (matrix k (rows i)) (matrix k (cols i)))
		       (set! (matrix k (cols i)) temp)))))
	     (list matrix b))))))))


(define (matrix-multiply A B)
  (let ((size (vector-dimension A 0)))
    (do ((C (make-float-vector (list size size) 0))
         (i 0 (+ i 1)))
	((= i size) C)
      (do ((j 0 (+ j 1)))
	  ((= j size))
	(do ((sum 0.0)
             (k 0 (+ k 1)))
	    ((= k size)
             (set! (C i j) sum))
	  (set! sum (+ sum (* (A i k) (B k j)))))))))


;;; Rosetta scheme code + changes
(define (square-matrix mat)
  (matrix-multiply mat mat))

(define (matrix-exp mat pow)
  (cond ((= pow 0)
	 (let ((m (copy mat)))
	   (fill! m 0.0)
	   (let ((size (car (vector-dimensions mat))))
	     (do ((i 0 (+ i 1)))
		 ((= i size) m)
	       (set! (m i i) 1.0)))))
	  
	  ((= pow 1) 
	   mat)

	  ((negative? pow)
	   (let ((im (invert-matrix (matrix-exp mat (abs pow)))))
	     (and (pair? im)
		  (car im))))

	  ((even? pow)
	   (square-matrix (matrix-exp mat (ash pow -1))))
	  
	  (else
	   (matrix-multiply mat (matrix-exp mat (- pow 1))))))

(define (testm)
  (do ((i 0 (+ i 1)))
      ((= i 10000))
    (let ((v (make-float-vector (list 4 4)))
	  (pow (+ 1 (random 10))))
      (do ((k 0 (+ k 1)))
	  ((= k 4))
	(do ((n 0 (+ n 1)))
	    ((= n 4))
	  (set! (v k n) (- (random 50.0) 100.0))))
      (let ((mn (matrix-exp (copy v) (- pow))))
	(if mn
	    (let* ((mp (matrix-exp (copy v) pow))
		   (m1 (matrix-multiply mn mp))
		   (mx1 (m1 0 0))
		   (mx0 0.0))
	      (do ((k 1 (+ k 1)))
		  ((= k 4))
		(if (> (abs (- (m1 k k) 1.0)) (abs (- mx1 1.0)))
		    (set! mx1 (m1 k k))))
	      (do ((k 0 (+ k 1)))
		  ((= k 4))
		(do ((n 0 (+ n 1)))
		    ((= n 4))
		  (unless (= k n)
		    (set! mx0 (max mx0 (abs (m1 k n)))))))))))))
  
(testm)

;;; --------------------------------------------------------------------------------

(define flarray #r2d((0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) 
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)))

(define tfl #r2d((0 0 0 0 0 0 0) (1 1 1 1 1 1 1) (2 2 2 2 2 2 2) (3 3 3 3 3 3 3) 
		 (4 4 4 4 4 4 4) (5 5 5 5 5 5 5) (6 6 6 6 6 6 6) (7 7 7 7 7 7 7) 
		 (8 8 8 8 8 8 8) (9 9 9 9 9 9 9) (10 10 10 10 10 10 10) (11 11 11 11 11 11 11)
		 (12 12 12 12 12 12 12) (13 13 13 13 13 13 13) (14 14 14 14 14 14 14) (15 15 15 15 15 15 15)
		 (16 16 16 16 16 16 16) (17 17 17 17 17 17 17) (18 18 18 18 18 18 18) (19 19 19 19 19 19 19) (20 20 20 20 20 20 20)))

(define narray #2d((0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) 
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
		     (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)))

(define tna #2d((0 0 0 0 0 0 0) (1 1 1 1 1 1 1) (2 2 2 2 2 2 2) (3 3 3 3 3 3 3) 
		 (4 4 4 4 4 4 4) (5 5 5 5 5 5 5) (6 6 6 6 6 6 6) (7 7 7 7 7 7 7) 
		 (8 8 8 8 8 8 8) (9 9 9 9 9 9 9) (10 10 10 10 10 10 10) (11 11 11 11 11 11 11)
		 (12 12 12 12 12 12 12) (13 13 13 13 13 13 13) (14 14 14 14 14 14 14) (15 15 15 15 15 15 15)
		 (16 16 16 16 16 16 16) (17 17 17 17 17 17 17) (18 18 18 18 18 18 18) (19 19 19 19 19 19 19) (20 20 20 20 20 20 20)))

(define tries 10000)
(set! (*s7* 'print-length) 123123)

(define (matrix-transpose mat)
  (let* ((dims (vector-dimensions mat))
	 (new-mat (make-vector (reverse dims)))
	 (end1 (car dims))
	 (end2 (cadr dims)))
    (do ((i 0 (+ i 1)))
	((= i end1) new-mat)
      (do ((j 0 (+ j 1)))
	  ((= j end2))
	(set! (new-mat j i) (mat i j))))))

(unless (equal? (matrix-transpose narray) tna)
  (format *stderr* "tna: ~S~%" (matrix-transpose narray)))

(define (test-transpose)
  (do ((i 0 (+ i 1)))
      ((= i  tries))
    (matrix-transpose narray)))

(test-transpose)


(define (matrix-transpose1 mat)
  (let* ((dims (vector-dimensions mat))
	 (new-mat (make-vector (reverse dims)))
	 (end1 (car dims))
	 (end2 (cadr dims)))
    (do ((i 0 (+ i 1)))
	((= i end1) new-mat)
      (do ((j 0 (+ j 1)))
	  ((= j end2))
	(vector-set! new-mat j i (vector-ref mat i j))))))

(unless (equal? (matrix-transpose1 narray) tna)
  (format *stderr* "tna1: ~S~%" (matrix-transpose1 narray)))

(define (test-transpose1)
  (do ((i 0 (+ i 1)))
      ((= i tries))
    (matrix-transpose1 narray)))

(test-transpose1)


(define (float-matrix-transpose mat)
  (let* ((dims (vector-dimensions mat))
	 (new-mat (make-float-vector (reverse dims)))
	 (end1 (car dims))
	 (end2 (cadr dims)))
    (do ((i 0 (+ i 1)))
	((= i end1) new-mat)
      (do ((j 0 (+ j 1)))
	  ((= j end2))
	(set! (new-mat j i) (mat i j))))))

(unless (equal? (float-matrix-transpose flarray) tfl)
  (format *stderr* "tfl: ~S~%" (float-matrix-transpose flarray)))

(define (test-float-transpose)
  (do ((i 0 (+ i 1)))
      ((= i tries))
    (float-matrix-transpose flarray)))

(test-float-transpose)


(define (float-matrix-transpose1 mat)
  (let* ((dims (vector-dimensions mat))
	 (new-mat (make-float-vector (reverse dims)))
	 (end1 (car dims))
	 (end2 (cadr dims)))
    (do ((i 0 (+ i 1)))
	((= i end1) new-mat)
      (do ((j 0 (+ j 1)))
	  ((= j end2))
	(float-vector-set! new-mat j i (float-vector-ref mat i j))))))

(unless (equal? (float-matrix-transpose1 flarray) tfl)
  (format *stderr* "tfl1: ~S~%" (float-matrix-transpose1 flarray)))

(define (test-float-transpose1)
  (do ((i 0 (+ i 1)))
      ((= i tries))
    (float-matrix-transpose1 flarray)))

(test-float-transpose1)



(when (> (*s7* 'profile) 0)
  (show-profile 200))
(exit)
