```
-- | Signs

{-# LANGUAGE CPP, BangPatterns #-}
module Math.Combinat.Sign where

--------------------------------------------------------------------------------

import Data.Monoid

-- Semigroup became a superclass of Monoid
#if MIN_VERSION_base(4,11,0)
import Data.Foldable
import Data.Semigroup
#endif

import System.Random

--------------------------------------------------------------------------------

data Sign
= Plus                            -- hmm, this way @Plus < Minus@, not sure about that
| Minus

--------------------------------------------------------------------------------

-- Semigroup became a superclass of Monoid
#if MIN_VERSION_base(4,11,0)

instance Semigroup Sign where
(<>)    = mulSign
sconcat = foldl1 mulSign

instance Monoid Sign where
mempty  = Plus
mconcat = productOfSigns

#else

instance Monoid Sign where
mempty  = Plus
mappend = mulSign
mconcat = productOfSigns

#endif

--------------------------------------------------------------------------------

instance Random Sign where
random        g = let (b,g') = random g in (if b    then Plus else Minus, g')
randomR (u,v) g = let (y,g') = random g in (if u==v then u    else y    , g')

isPlus, isMinus :: Sign -> Bool
isPlus  s = case s of { Plus  -> True ; _ -> False }
isMinus s = case s of { Minus -> True ; _ -> False }

{-# SPECIALIZE signValue :: Sign -> Int     #-}
{-# SPECIALIZE signValue :: Sign -> Integer #-}

-- | @+1@ or @-1@
signValue :: Num a => Sign -> a
signValue s = case s of
Plus  ->  1
Minus -> -1

{-# SPECIALIZE signed :: Sign -> Int     -> Int     #-}
{-# SPECIALIZE signed :: Sign -> Integer -> Integer #-}

-- | Negate the second argument if the first is 'Minus'
signed :: Num a => Sign -> a -> a
signed s y = case s of
Plus  -> y
Minus -> negate y

{-# SPECIALIZE paritySign :: Int     -> Sign #-}
{-# SPECIALIZE paritySign :: Integer -> Sign #-}

-- | 'Plus' if even, 'Minus' if odd
paritySign :: Integral a => a -> Sign
paritySign x = if even x then Plus else Minus

{-# SPECIALIZE paritySignValue :: Int     -> Integer #-}
{-# SPECIALIZE paritySignValue :: Integer -> Integer #-}

-- | @(-1)^k@
paritySignValue :: Integral a => a -> Integer
paritySignValue k = if odd k then (-1) else 1

{-# SPECIALIZE negateIfOdd :: Int     -> Int     -> Int     #-}
{-# SPECIALIZE negateIfOdd :: Int     -> Integer -> Integer #-}

-- | Negate the second argument if the first is odd
negateIfOdd :: (Integral a, Num b) => a -> b -> b
negateIfOdd k y = if even k then y else negate y

oppositeSign :: Sign -> Sign
oppositeSign s = case s of
Plus  -> Minus
Minus -> Plus

mulSign :: Sign -> Sign -> Sign
mulSign s1 s2 = case s1 of
Plus  -> s2
Minus -> oppositeSign s2

productOfSigns :: [Sign] -> Sign
productOfSigns = go Plus where
go !acc []     = acc
go !acc (x:xs) = case x of
Plus  -> go acc xs
Minus -> go (oppositeSign acc) xs

--------------------------------------------------------------------------------
```