module Main where
import Common
import qualified RowEchelon
import qualified Data.Array.Comfort.Boxed as Array
import qualified Data.Foldable as Fold
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.List.HT as ListHT
import qualified Data.List as List
import qualified Data.Ratio as Ratio
import Data.Array.Comfort.Boxed (Array)
import Data.Set (Set)
import Data.Ratio ((%))
import qualified Options.Applicative as OP
matrixFromReactantSet ::
Set Reactant -> Array (Set String, Set Reactant) Rational
matrixFromReactantSet reactantSet =
let elements = foldMap Map.keysSet reactantSet
in Array.fromAssociations 0 (elements, reactantSet) $ do
r <- Set.toList reactantSet
(e,n) <- Map.toList r
return ((e,r), fromInteger n)
gcdRat :: Rational -> Rational -> Rational
gcdRat x y =
gcd (Ratio.numerator x) (Ratio.numerator y)
%
lcm (Ratio.denominator x) (Ratio.denominator y)
integerFromRational :: Rational -> Integer
integerFromRational r =
let n = round r in
if r == n%1 then n else error "gcdRat failed"
normalizeRationalVector :: (Functor f, Foldable f) => f Rational -> f Integer
normalizeRationalVector v =
let d = Fold.foldl gcdRat 0 v
in fmap (integerFromRational . (/d)) v
main :: IO ()
main = do
(verbosity,format,args) <- OP.execParser $ info parser
(reactantSet,reactantsWithoutDuplicates)
<- preprocessArguments verbosity args
let mat = matrixFromReactantSet reactantSet
let sols =
map (Map.fromList . zip (Set.toAscList reactantSet)) $
List.transpose $ RowEchelon.matrixRows $
RowEchelon.nullspace $
RowEchelon.matrixFromRows $
ListHT.sliceVertical (Set.size reactantSet) $
Array.toList mat
displaySolutions verbosity format reactantsWithoutDuplicates $
map normalizeRationalVector sols