;; author: ismdeep
;; blog: https://ismdeep.com
;; blog entry: https://ismdeep.com/238
;; doolittle algorithm to decompose a square matrix

(defun mmul (A B)
  (let* ((m (car (array-dimensions A)))
         (n (cadr (array-dimensions A)))
         (l (cadr (array-dimensions B)))
         (C (make-array `(,m ,l) :initial-element 0)))
    (loop for i from 0 to (- m 1) do
              (loop for k from 0 to (- l 1) do
                    (setf (aref C i k)
                          (loop for j from 0 to (- n 1)
                                sum (* (aref A i j)
                                       (aref B j k))))))
    C))

;; Using doolittle algorithm to decomposes a square matrix A for LU = A and LDV = A
(defun doolittle-lu (A)
  (let* ((n (car (array-dimensions A))) (tmp 0)
         (L (make-array `(,n ,n) :initial-element 0))
         (U (make-array `(,n ,n) :initial-element 0))
         (D (make-array `(,n ,n) :initial-element 0))
         (V (make-array `(,n ,n) :initial-element 0))
         (lu-mmul 0)
         (ldv-mmul 0))

    (loop for j from 0 to (- n 1) do (setf (aref U 0 j) (aref A 0 j)))
    (loop for i from 0 to (- n 1) do (setf (aref L i i) 1))
    (loop for i from 1 to (- n 1) do 
      ; generate L
      (loop for j from 0 to (- i 1) do
        (setf tmp (aref A i j))
        (loop for k from 0 to (- j 1) do (setf tmp (- tmp (* (aref L i k) (aref U k j)))))
        (setf (aref L i j) (/ tmp (aref U j j)))
        )
      ; generate U
      (loop for j from i to (- n 1) do
        (setf tmp (aref A i j ))
        (loop for k from 0 to (- i 1) do (setf tmp (- tmp (* (aref L i k) (aref U k j)))))
        (setf (aref U i j) tmp)
        )
      )

    ; output A
    (format t "---------- A ------------------~%")
    (loop for i from 0 to (- n 1) do
      (loop for j from 0 to (- n 1) do 
        (format t "~4A " (aref A i j)))
      (format t "~%"))    

    ; output L
    (format t "~%~%---------- L ------------------~%")
    (loop for i from 0 to (- n 1) do
      (loop for j from 0 to (- n 1) do 
        (format t "~4A " (aref L i j)))
      (format t "~%"))

    ; output U
    (format t "~%~%---------- U ------------------~%")
    (loop for i from 0 to (- n 1) do
      (loop for j from 0 to (- n 1) do 
        (format t "~4A " (aref U i j)))
      (format t "~%"))

    ; output L * U
    (setf lu-mmul (mmul L U))
    (format t "~%~%---------- L * U --------------~%")
    (loop for i from 0 to (- n 1) do
      (loop for j from 0 to (- n 1) do 
        (format t "~4A " (aref lu-mmul i j)))
      (format t "~%"))

    ; generate D
    (loop for i from 0 to (- n 1) do
      (setf (aref D i i) (aref U i i)))

    ; generate V
    (loop for i from 0 to (- n 1) do
      (loop for j from i to (- n 1) do
        (setf (aref V i j) (/ (aref U i j) (aref U i i)))))

    ; output D
    (format t "~%~%---------- D ------------------~%")
    (loop for i from 0 to (- n 1) do
      (loop for j from 0 to (- n 1) do 
        (format t "~4A " (aref D i j)))
      (format t "~%"))

    ; output V
    (format t "~%~%---------- V ------------------~%")
    (loop for i from 0 to (- n 1) do
      (loop for j from 0 to (- n 1) do 
        (format t "~4A " (aref V i j)))
      (format t "~%"))


    ; output L * D * V
    (setf ldv-mmul (mmul (mmul L D) V))
    (format t "~%~%---------- L * D * V ----------~%")
    (loop for i from 0 to (- n 1) do
      (loop for j from 0 to (- n 1) do 
        (format t "~4A " (aref ldv-mmul i j)))
      (format t "~%"))


  ;; Return L and U
  (values L U)))


(let ((g 0))
  (setf g (make-array '(4 4) :initial-contents '((2 1 -5 1) (1 -3 0 -6) (0 2 -1 2) (1 4 -7 6)))) 
  (doolittle-lu g))