slide-dsl/src/SlideRule.hs
2015-02-24 17:40:25 -08:00

192 lines
4.3 KiB
Haskell

{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DataKinds #-}
module SlideRule where
import Control.Applicative
import Control.Monad.Trans.State
import Data.Fixed (mod')
import Numeric
newtype SlideRule a = SlideRule (State SlideState a)
deriving (Monad, Applicative, Functor)
data SlideState = SlideState
{ slideWheel :: Double
, slideCursor :: Double
}
initialState :: SlideState
initialState = SlideState
{ slideWheel = 0
, slideCursor = 0
}
runSlideRule :: SlideRule a -> a
runSlideRule (SlideRule m) = evalState m initialState
printAnswer :: IsOnWheel pos => SlideRule (Scale pos) -> String
printAnswer s = showFFloat (Just 3) (runSlideRule (readCursor =<< s)) ""
instance Show a => Show (SlideRule a) where
show = show . runSlideRule
data ScalePosition = OnWheel | OffWheel
class IsOnWheel (pos :: ScalePosition) where
scaleWheel :: scale pos -> Bool
instance IsOnWheel OnWheel where scaleWheel _ = True
instance IsOnWheel OffWheel where scaleWheel _ = False
data Scale (w :: ScalePosition) = Scale
{ scaleName :: String
, scaleFun :: Double -> Double
, scaleFun' :: Double -> Double
}
cScale :: Scale OnWheel
cScale = Scale
{ scaleName = "C"
, scaleFun = logBase 10
, scaleFun' = (10**)
}
ciScale :: Scale OnWheel
ciScale = Scale
{ scaleName = "CI"
, scaleFun = \x -> mod' (1-logBase 10 x) 1
, scaleFun' = \x -> 10** mod' (1-x) 1
}
dScale :: Scale OffWheel
dScale = Scale
{ scaleName = "D"
, scaleFun = logBase 10
, scaleFun' = (10**)
}
aScale :: Scale OffWheel
aScale = Scale
{ scaleName = "A"
, scaleFun = logBase 100
, scaleFun' = (100**)
}
bScale :: Scale OnWheel
bScale = Scale
{ scaleName = "B"
, scaleFun = logBase 100
, scaleFun' = (100**)
}
kScale :: Scale OffWheel
kScale = Scale
{ scaleName = "K"
, scaleFun = logBase 1000
, scaleFun' = (1000**)
}
llScale :: Int -> Scale pos
llScale n = Scale
{ scaleName = "LL" ++ show n
, scaleFun = subtract off . logBase 10 . log
, scaleFun' = exp . (10**) . (+off)
}
where
off = fromIntegral n - 3
ll3Scale :: Scale OffWheel
ll3Scale = llScale 3
ll2Scale :: Scale OffWheel
ll2Scale = llScale 2
sScale :: Scale OnWheel
sScale = Scale
{ scaleName = "S"
, scaleFun = (+1) . logBase 10 . sin . degToRad
, scaleFun' = radToDeg . asin . (10**) . subtract 1
}
stScale :: Scale OnWheel
stScale = Scale
{ scaleName = "ST"
, scaleFun = (+2) . logBase 10 . sin . degToRad
, scaleFun' = radToDeg . asin . (10**) . subtract 2
}
t1Scale :: Scale OnWheel
t1Scale = Scale
{ scaleName = "T1"
, scaleFun = (+1) . logBase 10 . tan . degToRad
, scaleFun' = radToDeg . atan . (10**) . subtract 1
}
t2Scale :: Scale OnWheel
t2Scale = Scale
{ scaleName = "T2"
, scaleFun = logBase 10 . tan . degToRad
, scaleFun' = radToDeg . atan . (10**)
}
degToRad :: Double -> Double
degToRad x = x * pi / 180
radToDeg :: Double -> Double
radToDeg x = x * 180 / pi
getCursorPos :: SlideRule Double
getCursorPos = SlideRule (gets slideCursor)
getWheelPos :: SlideRule Double
getWheelPos = SlideRule (gets slideWheel)
setCursorPos :: Double -> SlideRule ()
setCursorPos x = SlideRule (modify (\s -> s { slideCursor = x }))
setWheelPos :: Double -> SlideRule ()
setWheelPos x = SlideRule (modify (\s -> s { slideWheel = x }))
mark :: IsOnWheel pos => Scale pos -> Double -> SlideRule ()
mark scale value
| scaleWheel scale = do w <- getWheelPos
setCursorPos (mod' (w+frac) 1)
| otherwise = do setCursorPos frac
where
frac = scaleFun scale value
readCursor :: IsOnWheel pos => Scale pos -> SlideRule Double
readCursor scale = do
c <- getCursorPos
w <- if scaleWheel scale then getWheelPos else return 0
let c' = mod' (c-w) 1
return (scaleFun' scale c')
move :: Scale OnWheel -> Double -> SlideRule ()
move scale value =
do c <- getCursorPos
let v = scaleFun scale value
setWheelPos (mod' (c - v) 1)
multiply :: Double -> Double -> String
multiply x y = printAnswer $
do mark dScale x
move ciScale y
mark ciScale 1
return dScale
fourthPowCIK :: Double -> String
fourthPowCIK x = printAnswer $
do mark kScale x
move ciScale x
mark ciScale 1
return kScale
fourthPowCCIA :: Double -> String
fourthPowCCIA x = printAnswer $
do move ciScale x
mark cScale x
return aScale