{-
   Haskell Dynamics Engine, Copyright (C) 2007 Ruben Henner Zilibowitz
   All rights reserved. Email: rubenz@cse.unsw.edu.au Web: www.cse.unsw.edu.au/~rubenz
   
   This is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License that is include with this
   package in the file LICENSE-GPL.TXT.
-}

-------------------------------------------------------------------------------
Maths Specification

Gaussian Elimination

------------------------------
Abstract

Input: Matrix A, vector b
Output: vector x st. Ax = b

------------------------------
Method

Input: Matrix A, vector b

1. Convert to upper triangular
   For j = 1 to n-1
     For i = j+1 to n
       row i -> (-aij/ajj)*row j + row i  (row of both a and b)

2. Do back substitution:
   For i = n to 1 
     xi = bi - (ai(i+1)*x(i+1) + ... + ain*xn)

Output: vector x
------------------------------
More concrete version

Input: Matrix A = (aij), vector b = (bi)

1. For j = 1 to n-1
     For i = j+1 to n
       For k = 1 to n (equivalently, j to n)
         aik = (-aij/ajj)*ajk + aik
       bi = (-aij/ajj)*bj + bi

2. For i = n to 1 
     xi = bi - (ai(i+1)*x(i+1) + ... + ain*xn)

Output: vector x

-------------------------------------------------------------------------------
Haskell Specification

> module Gausselim(gauss,Vector,Matrix) where

Types:
Lists for vectors and lists of lists for matrices. For now, we just
use lists - can bother with finite sequences (FinSeq's) later.

> type Vector a = [a]
> type Matrix a = [[a]]  -- list of rows

The following function is needed for a for loop in which the ith value
depends on the jth(j>i). The version written in terms of scan is chosen
here because it's easier to transform to 

> accum_scanr1 :: (a->[b]->b) -> [a] -> [b]
> accum_scanr1 f zs = map head (scanr' g [] zs)
>   where -- g :: a -> [b] -> [b]
>         g x ys = (f x ys):ys
>         -- scanr' is a form of scanr which returns the same number
>         -- of results as the size of the initial list - it misses
>         -- out the initial value - important for parallel things
>         scanr' f a xs = init(scanr f a xs)

The tidied up Haskell specificaion:

> gauss :: RealFloat a => Matrix a -> Vector a -> Vector a
> gauss a b = 
>   let m = map2 join a b         -- combine a and b into one matrix
>         where join xs y = xs ++ [y]
>       n = length b
>       m2 = foldl deal_with_jth_row m [1..n-1]  -- gaussian elimination
>                     -- for each row j=1..n-1, deal with the jth row
>       x = accum_scanr1 solve_ith_eqn m2  -- back substitution
>                     -- for each eqn i=n,..1, solve that eqn for xi
>   in x

> deal_with_jth_row :: RealFloat a => Matrix a -> Int -> Matrix a
> deal_with_jth_row m j = first_j_rows ++ 
>                              (map put_0_in_jth_pos other_rows)
> -- makes rows below row j have 0 in cols i<=j
>   where first_j_rows = take j m
>         other_rows = drop j m
>         put_0_in_jth_pos row = map2 join row (m!!(j-1))
>           where join x y = multiplier * y + x
>                 multiplier = -((row!!(j-1)) `safeDivide` (m!!(j-1)!!(j-1)))

> solve_ith_eqn :: RealFloat a => Vector a -> Vector a -> a
> -- calculates the value of xi, using the ith equation.
> -- row is the ith eqn, and zs are previously calculated x values
> solve_ith_eqn row zs =
>    (foldl (+) (row!!n) (map2 f (drop i row)zs)) `safeDivide` (row!!(i-1))
>   where n = (length row) - 1
>         i = n - length zs
>         f x y = -(x*y)

> map2 = zipWith

> isBadNum x = (isNaN x) || (isInfinite x)
> safeDivide x y | isBadNum z = error "error solving linear system"
>                | otherwise = z
>    where z = x / y
