{-# LANGUAGE QuasiQuotes #-}
module Atomo.Kernel.Numeric (load) where

import Data.Ratio

import Atomo


load :: VM ()
load = do
    mapM_ eval
        [ [e|operator right 8 ^|]
        , [e|operator 7 % * /|]
        , [e|operator 6 + -|]
        ]

    [p|(i: Integer) sqrt|] =: do
        Integer i <- here "i" >>= findInteger
        return (Double (sqrt (fromIntegral i)))

    [p|(d: Double) sqrt|] =: do
        Double d <- here "d" >>= findDouble
        return (Double (sqrt d))

    [p|(d: Double) ceiling|] =: do
        Double d <- here "d" >>= findDouble
        return (Integer (ceiling d))

    [p|(d: Double) round|] =: do
        Double d <- here "d" >>= findDouble
        return (Integer (round d))

    [p|(d: Double) floor|] =: do
        Double d <- here "d" >>= findDouble
        return (Integer (floor d))

    [p|(i: Integer) reciprocal|] =: do
        Integer i <- here "i" >>= findInteger
        return (Double (recip (fromIntegral i)))

    [p|(d: Double) reciprocal|] =: do
        Double d <- here "d" >>= findDouble
        return (Double (recip d))

    [p|(r: Rational) reciprocal|] =: do
        Rational r <- here "r" >>= findRational
        return (Rational (recip r))

    [p|(r: Rational) numerator|] =: do
        Rational r <- here "r" >>= findRational
        return (Integer (numerator r))

    [p|(r: Rational) denominator|] =: do
        Rational r <- here "r" >>= findRational
        return (Integer (denominator r))

    [p|(d: Double) as: Integer|] =::: [e|d floor|]
    [p|(d: Double) as: Rational|] =::: [e|d rationalize|]
    [p|(i: Integer) as: Double|] =: do
        Integer i <- here "i" >>= findInteger
        return (Double (fromIntegral i))
    [p|(i: Integer) as: Rational|] =: do
        Integer i <- here "i" >>= findInteger
        return (Rational (i % 1))
    [p|(r: Rational) as: Double|] =::: [e|r approximate|]
    [p|(r: Rational) as: Integer|] =::: [e|r approximate floor|]

    [p|(i: Integer) rationalize|] =::: [e|(i as: Double) rationalize|]
    [p|(d: Double) rationalize|] =::: [e|d rationalize: 0.001|]
    [p|(d: Double) rationalize: (e: Double)|] =: do
        Double d <- here "d" >>= findDouble
        Double e' <- here "e" >>= findDouble
        return (Rational (approxRational d e'))

    [p|(r: Rational) approximate|] =: do
        Rational r <- here "r" >>= findRational
        return (Double (fromRational r))

    [p|(a: Integer) + (b: Integer)|] =: primII (+)
    [p|(a: Rational) + (b: Rational)|] =: primRR (+)
    [p|(a: Double) + (b: Double)|] =: primDD (+)
    [p|(a: Integer) + (b: Double)|] =: primID (+)
    [p|(a: Integer) + (b: Rational)|] =: primIR (+)
    [p|(a: Double) + (b: Integer)|] =: primDI (+)
    [p|(a: Double) + (b: Rational)|] =: primDR (+)
    [p|(a: Rational) + (b: Integer)|] =: primRI (+)
    [p|(a: Rational) + (b: Double)|] =: primRD (+)

    [p|(a: Integer) - (b: Integer)|] =: primII (-)
    [p|(a: Rational) - (b: Rational)|] =: primRR (-)
    [p|(a: Double) - (b: Double)|] =: primDD (-)
    [p|(a: Integer) - (b: Double)|] =: primID (-)
    [p|(a: Integer) - (b: Rational)|] =: primIR (-)
    [p|(a: Double) - (b: Integer)|] =: primDI (-)
    [p|(a: Double) - (b: Rational)|] =: primDR (-)
    [p|(a: Rational) - (b: Integer)|] =: primRI (-)
    [p|(a: Rational) - (b: Double)|] =: primRD (-)

    [p|(a: Integer) * (b: Integer)|] =: primII (*)
    [p|(a: Rational) * (b: Rational)|] =: primRR (*)
    [p|(a: Double) * (b: Double)|] =: primDD (*)
    [p|(a: Integer) * (b: Double)|] =: primID (*)
    [p|(a: Integer) * (b: Rational)|] =: primIR (*)
    [p|(a: Double) * (b: Integer)|] =: primDI (*)
    [p|(a: Double) * (b: Rational)|] =: primDR (*)
    [p|(a: Rational) * (b: Integer)|] =: primRI (*)
    [p|(a: Rational) * (b: Double)|] =: primRD (*)

    [p|(a: Integer) / (b: Integer)|] =: primII div
    [p|(a: Rational) / (b: Rational)|] =: primRR (/)
    [p|(a: Double) / (b: Double)|] =: primDD (/)
    [p|(a: Integer) / (b: Double)|] =: primID (/)
    [p|(a: Integer) / (b: Rational)|] =: primIR (/)
    [p|(a: Double) / (b: Integer)|] =: primDI (/)
    [p|(a: Double) / (b: Rational)|] =: primDR (/)
    [p|(a: Rational) / (b: Integer)|] =: primRI (/)
    [p|(a: Rational) / (b: Double)|] =: primRD (/)

    [p|(a: Integer) ^ (b: Integer)|] =: primII (^)
    [p|(a: Double) ^ (b: Double)|] =: primDD (**)
    [p|(a: Integer) ^ (b: Double)|] =: primID (**)
    [p|(a: Double) ^ (b: Integer)|] =: primDI (**)
    [p|(a: Rational) ^ (b: Integer)|] =: do
        Rational a <- here "a" >>= findRational
        Integer b <- here "b" >>= findInteger
        return (Rational (a ^ b))

    [p|(a: Integer) % (b: Integer)|] =: primII mod
    [p|(a: Integer) quotient: (b: Integer)|] =: primII quot
    [p|(a: Integer) remainder: (b: Integer)|] =: primII rem
  where
    primII f = do
        Integer a <- here "a" >>= findInteger
        Integer b <- here "b" >>= findInteger
        return (Integer (f a b))

    primDD f = do
        Double a <- here "a" >>= findDouble
        Double b <- here "b" >>= findDouble
        return (Double (f a b))

    primRR f = do
        Rational a <- here "a" >>= findRational
        Rational b <- here "b" >>= findRational
        return (Rational (f a b))

    primID f = do
        Integer a <- here "a" >>= findInteger
        Double b <- here "b" >>= findDouble
        return (Double (f (fromIntegral a) b))

    primIR f = do
        Integer a <- here "a" >>= findInteger
        Rational b <- here "b" >>= findRational
        return (Rational (f (toRational a) b))

    primDI f = do
        Double a <- here "a" >>= findDouble
        Integer b <- here "b" >>= findInteger
        return (Double (f a (fromIntegral b)))

    primDR f = do
        Double a <- here "a" >>= findDouble
        Rational b <- here "b" >>= findRational
        return (Rational (f (toRational a) b))

    primRD f = do
        Rational a <- here "a" >>= findRational
        Double b <- here "b" >>= findDouble
        return (Rational (f a (toRational b)))

    primRI f = do
        Rational a <- here "a" >>= findRational
        Integer b <- here "b" >>= findInteger
        return (Rational (f a (toRational b)))
