{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module Numeric.Rng.Zero
  ( ZeroRng(..)
  ) where

import Numeric.Algebra
import Data.Foldable (toList)
import Prelude hiding ((+),(-),negate,subtract,replicate)

-- *** The Zero Rng for an Abelian Group, adding the trivial product
--
-- > _ * _ = zero 
--
-- which distributes over (+)

-- ZeroRng/runZeroRng witness an additive Abelian group isomorphism to the zero rng.
newtype ZeroRng r = ZeroRng { runZeroRng :: r } deriving (Eq,Ord,Show,Read)

instance Additive r => Additive (ZeroRng r) where
  ZeroRng a + ZeroRng b = ZeroRng (a + b)
  sumWith1 f = ZeroRng . sumWith1 (runZeroRng . f)

instance Idempotent r => Idempotent (ZeroRng r)

instance Abelian r => Abelian (ZeroRng r)

instance Monoidal r => Monoidal (ZeroRng r) where
  zero = ZeroRng zero
  sumWith f = ZeroRng . sumWith (runZeroRng . f)
  sinnum n (ZeroRng a) = ZeroRng (sinnum n a)
  
instance Group r => Group (ZeroRng r) where
  ZeroRng a - ZeroRng b = ZeroRng (a - b)
  negate (ZeroRng a) = ZeroRng (negate a)
  subtract (ZeroRng a) (ZeroRng b) = ZeroRng (subtract a b)
  times n (ZeroRng a) = ZeroRng (times n a)

instance Monoidal r => Multiplicative (ZeroRng r) where
  _ * _ = zero
  productWith1 f as = case toList as of
    [] -> error "productWith1: empty Foldable1"
    [a] -> f a
    _   -> zero

instance (Monoidal r, Abelian r) => Semiring (ZeroRng r)
instance Monoidal r => Commutative (ZeroRng r)
instance (Group r, Abelian r) => Rng (ZeroRng r)
instance Monoidal r => LeftModule Natural (ZeroRng r) where
  (.*) = sinnum
instance Monoidal r => RightModule Natural (ZeroRng r) where
  m *. n = sinnum n m
instance Group r => LeftModule Integer (ZeroRng r) where
  (.*) = times
instance Group r => RightModule Integer (ZeroRng r) where
  m *. n = times n m