aboutsummaryrefslogtreecommitdiffstats
path: root/RealCalc.hs
blob: d54cf84475ed600cac36295f6cc01f43051b8b8b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
{-# 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 <http://www.gnu.org/licenses/>.
 -}

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