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 Shell.Utility.Verbosity as Verbosity
import System.Environment (getArgs)
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
let verbosity = Verbosity.normal
(reactantSet,reactantsWithoutDuplicates)
<- preprocessArguments verbosity =<< getArgs
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 reactantsWithoutDuplicates $
map normalizeRationalVector sols