summaryrefslogtreecommitdiff
path: root/rpn.hs
blob: d70c9090179dbc3f63d65406730f25130fb7c8cc (plain)
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env runghc

{-# LANGUAGE ViewPatterns #-}

import Control.Monad (join, sequence, forever)
import Control.Applicative
import System.Environment
import Data.Maybe (listToMaybe, isJust)
import Data.Char (isSpace)
import Data.List (nub)
import qualified Control.Monad.State as S
import qualified Data.Map as M
import System.IO

data Symbol a = Number a a | Err | Add | Sub | Mult | Div | Unc | Exp | Pow deriving (Eq, Ord, Show)
instance Floating a => Num (Symbol a) where
  (Number x del) + (Number y del') = Number (x + y) (abs $ gMean [del, del'])
  _ + _ = Err
  (Number x del) * (Number y del') = Number (x * y) (abs $ (x * y) * (gMean [del / x, del' / y]))
  _ * _ = Err
  negate (Number x del) = Number (negate x) del
  negate _ = Err
  abs (Number x del) = Number (abs x) del
  abs _ = Err
  signum (Number x del) = Number (signum x) 0
  signum _ = Err
  fromInteger i = Number (fromInteger i) 0

instance Floating a => Fractional (Symbol a) where
  recip (Number x del) = Number (recip x) (abs $ (recip x) * (del / x))
  recip _ = Err
  fromRational x = Number (fromRational x) 0

dropUnc :: Floating a => Symbol a -> a
dropUnc (Number x _) = x
dropUnc _ = 0

gMean :: Floating a => [a] -> a
gMean xs = sqrt $ foldl1 (+) $ map (\x -> x * x) xs

fromSymbol :: Floating a => Symbol a -> (a, a)
fromSymbol (Number x d) = (x, d)
fromSymbol _ = (0, 0)


pPrint :: (Show a, Fractional a) => Symbol a -> String
pPrint (Number x d) = x `seq` d `seq` (show x) ++ " ± " ++ (show d) ++ " (relative error: " ++ (show $ d / x) ++ ")"
pPrint s = show s

main :: IO ()
main = do
  hSetBuffering stdin LineBuffering
  hSetBuffering stdout NoBuffering
  forever $ do
    line <- maybe (putStr "Formula: " >> getLine) (<$ unsetEnv "FORMULA") =<< lookupEnv "FORMULA"
    let --symbols = join . join $ map (map words) . map lines $ args
      symbols = words line
      unknownSymbols = nub $ filter (not . isKnownSymbol) symbols
    symbolMap <- sequence $ map (sequencePair . (\x -> (x, clarifySymbol x))) unknownSymbols
    let symbolMap' = M.fromList symbolMap
        symbols' = map (\x -> if x `elem` (M.keys symbolMap') then symbolMap' M.! x else [x]) symbols
        symbols'' = map interpreteSymbol $ join symbols'
    let result = evalExpVec symbols''
    putStrLn . pPrint $ evalExpVec symbols''

clarifySymbol :: String -> IO [String]
clarifySymbol s = maybe clarify' (return . words) =<< lookupEnv s
  where
    clarify' = do
      putStr $ "Define " ++ s ++ ": "
      line <- getLine
      return $ words line

isKnownSymbol :: String -> Bool
isKnownSymbol s = or [
  s `elem` ["+", "-", "*", "/", "±", "+-", "e", "^"],
  isJust $ (maybeRead s :: Maybe Double)
                     ]

sequencePair :: (a, IO b) -> IO (a, b)
sequencePair (a, b) = do
  b' <- b
  return (a, b')

maybeRead :: (Read a) => String -> Maybe a
maybeRead = fmap fst . listToMaybe . filter (null . dropWhile isSpace . snd) . reads

interpreteSymbol :: String -> Symbol Double
interpreteSymbol "+" = Add
interpreteSymbol "-" = Sub
interpreteSymbol "*" = Mult
interpreteSymbol "/" = Div
interpreteSymbol "±" = Unc
interpreteSymbol "+-" = Unc
interpreteSymbol "e" = Exp
interpreteSymbol "^" = Pow
interpreteSymbol x = Number (read x) 0

evalExpVec :: (Floating a, RealFrac a, Eq a) => [Symbol a] -> Symbol a
evalExpVec is = head . snd $ S.runState (evalExpVec' is) []

evalExpVec' :: (Floating a, RealFrac a, Eq a) => [Symbol a] -> S.State [Symbol a] ()
evalExpVec' is = do
  sequence $ map evalExp is
  return ()

evalExp :: (Floating a, RealFrac a, Eq a) => Symbol a -> S.State [Symbol a] ()
evalExp (Number x del) = do
  state <- S.get
  S.put $ ((Number x del):state)
evalExp Unc = do
  state <- S.get
  S.put $ ((Number (dropUnc (state !! 1)) (dropUnc (state !! 0))):drop 2 state)
evalExp Add = do
  state <- S.get
  S.put $ ((foldl1 (+) $ take 2 state):drop 2 state)
evalExp Sub = do
  state <- S.get
  S.put $ ((foldl1 (-) . reverse $ take 2 state):drop 2 state)
evalExp Mult = do
  state <- S.get
  S.put $ ((foldl1 (*) $ take 2 state):drop 2 state)
evalExp Div = do
  state <- S.get
  S.put $ ((foldl1 (/) . reverse $ take 2 state):drop 2 state)
evalExp Exp = do
  (e:b:state) <- S.get
  S.put $ (b * (Number (10 ** (fst $ fromSymbol e)) 0)):state
evalExp Pow = do
  ((Number b'@(round -> b) 0):(Number y dY):(Number a'@(round -> a) 0):(Number x dX):state) <- S.get
  let
    prod = (x^^a) * (y^^b)
    dX' = dX / x
    dY' = dY / y
    err = prod * (sqrt $ (a' * dX')^2 + (b' * dY')^2)
  S.put $ (Number prod err):state