{-# LANGUAGE RankNTypes #-} {- - Copyright 2018 Raymond Hogenson - - This file is part of HSC. - HSC is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - HSC is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with HSC. If not, see . -} module RealCalc (calculate) where import qualified Expr import qualified Data.Ratio data Result = RInt Integer | RFloat Double | RRatio Rational data ResultPair = RInts Integer Integer | RFloats Double Double | RRatios Rational Rational instance Show Result where show (RInt i) = show i show (RFloat d) = show d show (RRatio r) = show (Data.Ratio.numerator r) ++ "/" ++ show (Data.Ratio.denominator r) rToDouble :: Result -> Double rToDouble (RInt i) = fromIntegral i rToDouble (RFloat d) = d rToDouble (RRatio r) = fromIntegral (Data.Ratio.numerator r) / fromIntegral (Data.Ratio.denominator r) castSame :: Result -> Result -> ResultPair castSame a (RFloat b) = RFloats (rToDouble a) b castSame (RFloat a) b = RFloats a (rToDouble b) castSame (RRatio a) (RRatio b) = RRatios a b castSame (RInt a) (RRatio b) = RRatios (fromIntegral a) b castSame (RInt a) (RInt b) = RInts a b castSame (RRatio a) (RInt b) = RRatios a (fromIntegral b) genericCombine :: (forall a. Num a => a -> a -> a) -> Result -> Result -> Result genericCombine f i j = case castSame i j of RInts a b -> RInt (f a b) RFloats a b -> RFloat (f a b) RRatios a b -> RRatio (f a b) genericInner :: (forall a. Num a => a -> a) -> Result -> Result genericInner f (RInt i) = RInt (f i) genericInner f (RFloat d) = RFloat (f d) genericInner f (RRatio r) = RRatio (f r) simplify :: Expr.Expr -> Result simplify (Expr.Plus e1 e2) = genericCombine (+) (simplify e1) (simplify e2) simplify (Expr.Negate e) = genericInner negate (simplify e) simplify (Expr.Minus e1 e2) = genericCombine (-) (simplify e1) (simplify e2) simplify (Expr.Times e1 e2) = genericCombine (*) (simplify e1) (simplify e2) simplify (Expr.Div e1 e2) = case castSame (simplify e1) (simplify e2) of RInts a b -> RRatio (a Data.Ratio.% b) RRatios a b -> RRatio (a / b) RFloats a b -> RFloat (a / b) simplify (Expr.Pow e1 e2) = case (simplify e1, simplify e2) of (RInt a, RInt b) | b >= 0 -> RInt (a ^ b) (RRatio a, RInt b) -> RRatio (a ^^ b) (RFloat a, RInt b) -> RFloat (a ^^ b) (a, b) -> RFloat (rToDouble a ** rToDouble b) simplify (Expr.EInt i) = RInt i simplify (Expr.EFloat d) = RFloat d simplify Expr.E = RFloat (exp 1) simplify Expr.Pi = RFloat pi simplify (Expr.Log e) = RFloat . log . rToDouble $ simplify e simplify (Expr.Sin e) = RFloat . sin . rToDouble $ simplify e simplify (Expr.Cos e) = RFloat . cos . rToDouble $ simplify e simplify (Expr.Sqrt e) = RFloat . sqrt . rToDouble $ simplify e calculate :: String -> Maybe String calculate s = case Expr.parseE s of Left _ -> Nothing Right e -> Just . show $ simplify e