module Numeric.Partial.Group
  ( PartialGroup(..)
  ) where

import Control.Applicative
import Data.Int
import Data.Word
import Numeric.Partial.Semigroup
import Numeric.Partial.Monoid
import Numeric.Natural

class PartialMonoid a => PartialGroup a where
  pnegate :: a -> Maybe a
  pnegate = pminus pzero

  pminus :: a -> a -> Maybe a
  pminus a b = padd a =<< pnegate b 

  psubtract :: a -> a -> Maybe a
  psubtract a b = pnegate a >>= (`padd` b)

instance PartialGroup Int where
  pnegate = Just . negate

instance PartialGroup Integer where
  pnegate = Just . negate

instance PartialGroup Int8 where
  pnegate = Just . negate

instance PartialGroup Int16 where
  pnegate = Just . negate

instance PartialGroup Int32 where
  pnegate = Just . negate

instance PartialGroup Int64 where
  pnegate = Just . negate

instance PartialGroup Word where
  pnegate = Just . negate

instance PartialGroup Word8 where
  pnegate = Just . negate

instance PartialGroup Word16 where
  pnegate = Just . negate

instance PartialGroup Word32 where
  pnegate = Just . negate

instance PartialGroup Word64 where
  pnegate = Just . negate

instance PartialGroup Natural where
  pnegate 0 = Just 0
  pnegate _ = Nothing
  pminus a b 
    | a < b = Nothing
    | otherwise = Just (a - b)
  psubtract a b 
    | a > b = Nothing
    | otherwise = Just (b - a)

instance PartialGroup () where
  pnegate _ = Just () 
  pminus _ _ = Just ()
  psubtract _ _ = Just ()

instance (PartialGroup a, PartialGroup b) => PartialGroup (a, b) where
  pnegate (a, b) = (,) <$> pnegate a <*> pnegate b
  pminus (a, b) (i, j) = (,) <$> pminus a i <*> pminus b j
  psubtract (a, b) (i, j) = (,) <$> psubtract a i <*> psubtract b j

instance (PartialGroup a, PartialGroup b, PartialGroup c) => PartialGroup (a, b, c) where
  pnegate (a, b, c) = (,,) <$> pnegate a <*> pnegate b <*> pnegate c
  pminus (a, b, c) (i, j, k) = (,,) <$> pminus a i <*> pminus b j <*> pminus c k
  psubtract (a, b, c) (i, j, k) = (,,) <$> psubtract a i <*> psubtract b j <*> psubtract c k

instance (PartialGroup a, PartialGroup b, PartialGroup c, PartialGroup d) => PartialGroup (a, b, c, d) where
  pnegate (a, b, c, d) = (,,,) <$> pnegate a <*> pnegate b <*> pnegate c <*> pnegate d
  pminus (a, b, c, d) (i, j, k, l) = (,,,) <$> pminus a i <*> pminus b j <*> pminus c k <*> pminus d l
  psubtract (a, b, c, d) (i, j, k, l) = (,,,) <$> psubtract a i <*> psubtract b j <*> psubtract c k <*> psubtract d l

instance (PartialGroup a, PartialGroup b, PartialGroup c, PartialGroup d, PartialGroup e) => PartialGroup (a, b, c, d, e) where
  pnegate (a, b, c, d, e) = (,,,,) <$> pnegate a <*> pnegate b <*> pnegate c <*> pnegate d <*> pnegate e
  pminus (a, b, c, d, e) (i, j, k, l, m) = (,,,,) <$> pminus a i <*> pminus b j <*> pminus c k <*> pminus d l <*> pminus e m
  psubtract (a, b, c, d, e) (i, j, k, l, m) = (,,,,) <$> psubtract a i <*> psubtract b j <*> psubtract c k <*> psubtract d l <*> psubtract e m