{-# LANGUAGE DataKinds, DerivingVia, FlexibleInstances, StandaloneDeriving #-} import Prelude hiding ((^)) import Data.Monoid (Sum(Sum)) import Data.Number.CReal (CReal) import Data.Semiring (Semiring, (^), plus, times) import Data.Semiring.Tropical (Tropical(..), Extrema(Maxima)) -- Create our max-plus semiring over the constructive reals (CReal), using the -- Tropical type from the semirings package. (We'll put all the boilerplate -- code after the main function.) -- -- 'Maxima indicates that our semiring is a max-plus semiring, where the plus -- function is maximum, the times function is addition, and the Infinity -- constructor is treated as -∞. newtype MaxPlus = MaxPlus (Tropical 'Maxima CReal) -- Symbolic aliases to satisfy the problem requirements. (⊕), (⊗) :: MaxPlus -> MaxPlus -> MaxPlus (⊕) = plus (⊗) = times infixl 6 ⊕ infixl 7 ⊗ (↑) :: Integral a => MaxPlus -> a -> MaxPlus (↑) = (^) infixr 8 ↑ main :: IO () main = do -- Description Equation Expected Value test "2 ⊗ (-2) == 0" (2 ⊗ (-2)) 0 test "-0.001 ⊕ -Inf == -0.001" (-0.001 ⊕ MaxPlus Infinity) (-0.001) test "0 ⊗ -Inf == -Inf" (0 ⊗ MaxPlus Infinity) (MaxPlus Infinity) test "1.5 ⊕ -1 == 1.5" (1.5 ⊕ (-1)) 1.5 test "-0.5 ⊗ 0 == -0.5" ((-0.5) ⊗ 0) (-0.5) test "5 ↑ 7 == 35" (5 ↑ 7) 35 test "5 ⊗ (8 ⊕ 7) == 13" (5 ⊗ (8 ⊕ 7)) 13 test "5 ⊗ 8 ⊕ 5 ⊗ 7 == 13" (5 ⊗ 8 ⊕ 5 ⊗ 7) 13 -------------------------------------------------------------------------------- -- Boilerplate, utility functions, etc. -- Bootstrap our way to having MaxPlus be a Semiring instance. Also, derive -- Eq and Ord instances. deriving via (Sum CReal) instance Semigroup CReal deriving via (Sum CReal) instance Monoid CReal deriving via Tropical 'Maxima CReal instance Semiring MaxPlus deriving via Tropical 'Maxima CReal instance Eq MaxPlus deriving via Tropical 'Maxima CReal instance Ord MaxPlus -- Create a Num instance for MaxPlus mostly so that we can use fromInteger and -- negate. This lets us treat the numeric literal -2, for example, as a value -- in our semiring. instance Num MaxPlus where (+) = plus (*) = times abs = opError "absolute value" signum (MaxPlus Infinity) = -1 signum x = wrap . signum . unwrap $ x fromInteger = wrap . fromInteger negate (MaxPlus Infinity) = opError "negation of -Inf" negate x = wrap . negate . unwrap $ x -- Similar to Num, this will let us treat numeric literals, like 0.001, as -- MaxPlus values. instance Fractional MaxPlus where fromRational = wrap . fromRational recip _ = opError "reciprocal" instance Show MaxPlus where show (MaxPlus Infinity) = "-Inf" show x = show . unwrap $ x -- Test two expressions for equality. test :: String -> MaxPlus -> MaxPlus -> IO () test s actual expected = do putStr $ "Expecting " ++ s ++ ". Got " ++ show actual ++ " " putStrLn $ if actual == expected then "✔" else "✘" -- Utility functions. wrap :: CReal -> MaxPlus wrap = MaxPlus . Tropical unwrap :: MaxPlus -> CReal unwrap (MaxPlus (Tropical x)) = x unwrap (MaxPlus Infinity) = error "can't convert -Inf to a CReal" opError :: String -> a opError op = error $ op ++ " is not defined on a max-plus semiring"