{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}

module Arithmetic.Numbers
(
  -- * Types
  Tropical(..)
) where

import Data.Function
import Numeric.Algebra hiding (negate, fromInteger)
import Numeric.Algebra.Class as AC




--The set includes all the real numbers and the infinity
data Tropical a = Tropical {value :: a} | Inf
  deriving (Eq)


instance (Show a) => Show (Tropical a) where
  show Inf = "Inf"
  show (Tropical a) = show a



(.+.) :: (Ord a) => Tropical a -> Tropical a -> Tropical a
Inf .+. t = t
t .+. Inf = t
t1 .+. t2 = Tropical $ (min `on` value) t1 t2

(.*.) :: (Num a) => Tropical a -> Tropical a -> Tropical a
t .*. Inf = Inf
Inf .*. t = Inf
t1 .*. t2 = Tropical $ ((Prelude.+) `on` value) t1 t2


instance (Num a) => Unital (Tropical a) where
  one = Tropical 0


instance (Num a, Ord a, Rig a) => LeftModule Natural (Tropical a) where
  a .* t1 = Tropical (fromNatural a) AC.* t1

instance (Num a, Ord a, Rig a) => RightModule Natural (Tropical a) where
  (*.) = flip (.*)


instance (Num a, Ord a, Rig a) => Monoidal (Tropical a) where
  zero = Inf

instance (Num a) => Multiplicative (Tropical a) where
  (*) = (.*.)

instance (Num a) => Commutative (Tropical a)

instance (Rig a, Num a, Ord a) => Rig (Tropical a) where
  fromNatural a = Tropical $ fromNatural a

instance (Ord a) => Additive (Tropical a) where
  (+) = (.+.)
instance (Ord a) => Abelian (Tropical a)
instance (Ord a, Num a) => Semiring (Tropical a)

instance (Ord a, Num a) => Num (Tropical a) where
  (+) = (.+.)
  (*) = (.*.)
  fromInteger a = Tropical $ Prelude.fromInteger a
  negate = fmap negate

-- | Not neccesary to implement since it is already defined  
instance (Num a, Ord a, Rig a) => DecidableZero (Tropical a)
instance Functor Tropical where
  fmap f (Tropical a) = Tropical (f a)


instance Integral (Tropical Integer) where
  toInteger (Tropical a) = a
  toInteger Inf = 0

instance Enum (Tropical Integer) where
  toEnum a = Tropical (toInteger a)
  fromEnum (Tropical a) = fromInteger a
  fromEnum Inf = 0

instance Real (Tropical Integer) where
  toRational (Tropical a) = toRational a
  toRational Inf = 0

instance Ord (Tropical Integer) where
  compare Inf a = LT
  compare a Inf = GT
  compare (Tropical a) (Tropical b) = compare a b