packages feed

chemical-equation-0.0.1: src/Main.hs

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