module Algebra.Linear.GaussianElimination (linSolve) where
import Prelude hiding (Num(..),(/))
import Algebra.Classes
import qualified Data.Map.Strict as M
import Data.Maybe
import Algebra.Linear
gaussianElim :: (Ord v,Eq c,Field c) => [Constraint v c] -> [(v,LinFunc v c)]
gaussianElim [] = []
gaussianElim (co@(Func f k):cos) = case M.minViewWithKey f of
Nothing -> if k == 0 then gaussianElim cos
else error "gaussianElim: unsatisfiable"
Just ((_,0),_) -> error "gaussianElim: invariant not respected"
Just ((v,c),_) -> (v,clean $ var v co') : gaussianElim (fmap t cos)
where co' = (1/c) *^ co
t co2@(Func m _) = case (M.lookup v m) of
Nothing -> co2
Just cv -> clean (co2 cv *^ co')
substitution :: (Ord v,Ring c, Eq c) => [(v,LinFunc v c)] -> [(v,LinFunc v c)]
substitution [] = []
substitution ((v,f):vfs) = (v,f) : substitution [(v',subst v f f') | (v',f') <- vfs]
subst :: (Eq scalar, Ord v, Ring scalar) => v -> LinFunc v scalar -> LinFunc v scalar -> LinFunc v scalar
subst v f f'@(Func m _) = clean $ f' coef *^ var v + coef *^ f
where coef = fromMaybe 0 (M.lookup v m)
linSolve :: (Eq c, Ord v, Field c) => [Constraint v c] -> [(v, LinFunc v c)]
linSolve = substitution . reverse . gaussianElim . fmap clean