-- |
-- The module contains arithmetic calculations according the IEEE 754 standard
-- for plus, minus, unary minus, multiplication, modulo and division.
--


module Text.XML.HXT.XPath.XPathArithmetic
    ( xPathMulti
    , xPathMod
    , xPathDiv
    , xPathAdd
    , xPathUnary
    )
where

import Text.XML.HXT.XPath.XPathDataTypes


-- |
-- Unary minus: the value 'NaN' is not calculatable and returned unchanged,
-- all other values can be denied.
--

xPathUnary :: XPathFilter
xPathUnary (XPVNumber (Float f)) =  XPVNumber (Float (-f))
xPathUnary (XPVError e)          = XPVError e
xPathUnary (XPVNumber NaN)       = XPVNumber NaN
xPathUnary (XPVNumber Pos0)      = XPVNumber Neg0
xPathUnary (XPVNumber Neg0)      = XPVNumber Pos0
xPathUnary (XPVNumber PosInf)    = XPVNumber NegInf
xPathUnary (XPVNumber NegInf)    = XPVNumber PosInf
xPathUnary _                     = XPVError "Call to unaryEval without a number"


-- |
-- Multiplication
--

xPathMulti :: Op -> XPathValue -> XPathFilter
xPathMulti _ (XPVNumber (Float a)) (XPVNumber (Float b))
    = XPVNumber (Float (a * b))
xPathMulti _ (XPVNumber NegInf) (XPVNumber (Float a))
    | a < 0     = XPVNumber PosInf
    | otherwise = XPVNumber NegInf
xPathMulti _ (XPVNumber PosInf) (XPVNumber (Float a))
    | a < 0     = XPVNumber NegInf
    | otherwise = XPVNumber PosInf
xPathMulti _ (XPVNumber (Float a)) (XPVNumber NegInf)
    | a < 0     = XPVNumber PosInf
    | otherwise = XPVNumber NegInf
xPathMulti _ (XPVNumber (Float a)) (XPVNumber PosInf)
    | a < 0     = XPVNumber NegInf
    | otherwise = XPVNumber PosInf
xPathMulti _ (XPVNumber Pos0) (XPVNumber (Float a))
    | a < 0     = XPVNumber Neg0
    | otherwise = XPVNumber Pos0
xPathMulti _ (XPVNumber Neg0) (XPVNumber (Float a))
    | a < 0     = XPVNumber Pos0
    | otherwise = XPVNumber Neg0
xPathMulti _ (XPVNumber (Float a)) (XPVNumber Pos0)
    | a < 0     = XPVNumber Neg0
    | otherwise = XPVNumber Pos0
xPathMulti _ (XPVNumber (Float a)) (XPVNumber Neg0)
    | a < 0     = XPVNumber Pos0
    | otherwise = XPVNumber Neg0
xPathMulti a b c = xPathSpez a b c


-- |
-- Modulo
--

xPathMod :: Op -> XPathValue -> XPathFilter
xPathMod _ (XPVNumber (Float a)) (XPVNumber (Float b))
    | floatMod a b == 0 = XPVNumber Pos0
    | otherwise = XPVNumber (Float (floatMod a b))
      where
      floatMod x y
        | x/y >= 0 = x - y * fromInteger(floor (x / y))
        | otherwise =x - y * fromInteger(ceiling (x / y))

xPathMod _ (XPVNumber (Float a)) (XPVNumber NegInf)
    = XPVNumber (Float a)
xPathMod _ (XPVNumber (Float a)) (XPVNumber PosInf)
    = XPVNumber (Float a)
xPathMod _ (XPVNumber Neg0) (XPVNumber Pos0)
    = XPVNumber Neg0
xPathMod a b c = xPathSpez a b c


-- |
-- Division: the divison-operator is not according the IEEE 754 standard,
-- it calculates the same as the % operator in Java and ECMAScript
--

xPathDiv :: Op -> XPathValue -> XPathFilter
xPathDiv _ (XPVNumber (Float a)) (XPVNumber (Float b))
    = XPVNumber (Float (a / b))
xPathDiv _ (XPVNumber NegInf) (XPVNumber (Float a))
    | a < 0     = XPVNumber PosInf
    | otherwise = XPVNumber NegInf
xPathDiv _ (XPVNumber PosInf) (XPVNumber (Float a))
    | a < 0     = XPVNumber NegInf
    | otherwise = XPVNumber PosInf
xPathDiv _ (XPVNumber (Float a)) (XPVNumber NegInf)
    | a < 0     = XPVNumber Pos0
    | otherwise = XPVNumber Neg0
xPathDiv _ (XPVNumber (Float a)) (XPVNumber PosInf)
    | a < 0     = XPVNumber Neg0
    | otherwise = XPVNumber Pos0
xPathDiv _ (XPVNumber Neg0) (XPVNumber (Float a))
    | a < 0     = XPVNumber Pos0
    | otherwise = XPVNumber Neg0
xPathDiv _ (XPVNumber (Float a)) (XPVNumber Neg0)
    | a < 0     = XPVNumber PosInf
    | otherwise = XPVNumber NegInf
xPathDiv _ (XPVNumber (Float a)) (XPVNumber Pos0)
    | a < 0     = XPVNumber NegInf
    | otherwise = XPVNumber PosInf
xPathDiv a b c  = xPathSpez a b c


-- |
-- Plus and minus
--
--    1.parameter op :  plus or minus operation
--

xPathAdd :: Op -> XPathValue -> XPathFilter
xPathAdd Plus (XPVNumber (Float a)) (XPVNumber (Float b))
    = if a + b == 0
        then XPVNumber Pos0
        else XPVNumber (Float (a+b))
xPathAdd Minus (XPVNumber (Float a)) (XPVNumber (Float b))
    = if a - b == 0
        then XPVNumber Pos0
        else XPVNumber (Float (a-b))
xPathAdd _ (XPVNumber PosInf) (XPVNumber NegInf)  = XPVNumber NaN
xPathAdd _ (XPVNumber NegInf) (XPVNumber PosInf)  = XPVNumber NaN
xPathAdd _ (XPVNumber PosInf) _                   = XPVNumber PosInf
xPathAdd _ (XPVNumber NegInf) _                   = XPVNumber NegInf
xPathAdd _ _ (XPVNumber PosInf)                   = XPVNumber PosInf
xPathAdd _ _ (XPVNumber NegInf)                   = XPVNumber NegInf
xPathAdd _ (XPVNumber (Float a)) (XPVNumber Pos0) = XPVNumber (Float a)
xPathAdd op (XPVNumber Pos0) (XPVNumber (Float a))
    | op == Minus = XPVNumber (Float (-a))
    | otherwise   = XPVNumber (Float a)
xPathAdd op (XPVNumber Neg0) (XPVNumber (Float a))
    | op == Minus = XPVNumber (Float (-a))
    | otherwise   = XPVNumber (Float a)
xPathAdd _ (XPVNumber (Float a)) (XPVNumber Neg0) = XPVNumber (Float a)
xPathAdd _ (XPVNumber Neg0) (XPVNumber Pos0)      = XPVNumber Neg0
xPathAdd _ (XPVNumber Pos0) (XPVNumber Neg0)      = XPVNumber Neg0
xPathAdd _ (XPVNumber Neg0) (XPVNumber Neg0)      = XPVNumber Neg0
xPathAdd _ (XPVNumber Pos0) (XPVNumber Pos0)      = XPVNumber Pos0
xPathAdd a b c = xPathSpez a b c


-- |
-- Identically results of the operators are combined to get
-- as few as possible combinations of the special IEEE values
--
xPathSpez :: Op -> XPathValue -> XPathFilter
xPathSpez _ (XPVError e) _ = XPVError e
xPathSpez _ _ (XPVError e) = XPVError e
xPathSpez _ _ _            = XPVNumber NaN