{-# LANGUAGE CPP #-}
module Numeric.Product.Commutative
  ( CommutativeProduct (..)
  )
  where

import Control.Applicative (Const)
import Data.Complex (Complex)
import Data.Fixed
import Data.Functor.Identity
import Data.Int
import Data.Ratio (Ratio)
import Data.Word
import Numeric.Natural

#if MIN_VERSION_base(4, 9, 0)
import Data.Semigroup (Max, Min, Product, Sum)
#endif

#if MIN_VERSION_base(4, 11, 0)
import Data.Ord (Down)
#endif

#if MIN_VERSION_base(4, 12, 0)
import Data.Functor.Contravariant (Op)
import Data.Monoid (Alt)
#endif

-- | Subclass of 'Num' where '(*)' is commutative.
--
-- 'Num' doesn't demand commutative '(*)', and there are reasonable
-- "real-world" instances with non-commutative multiplication. There
-- is also no canonical subclass in @base@ that would suffice, as both
-- 'Integral' and 'Floating' imply commutative '(*)' for different
-- reasons.
--
-- Two examples of non-commutative '(*)':
--
-- * @Linear.Quaternion.Quaterion@ from the @linear@ package has a
--   'Num' instance, and quaternion multiplication is
--   noncommutative.
--
-- * @Data.Matrix.Matrix@ from the @matrix@ package uses '(*)' for
--   matrix multiplication, which is also non-commutative (on square
--   matrices, which is the only time the question makes sense).
--
-- @since 0.1.0
class Num a => CommutativeProduct a

instance CommutativeProduct Int8
instance CommutativeProduct Int16
instance CommutativeProduct Int32
instance CommutativeProduct Int64
instance CommutativeProduct Int
instance CommutativeProduct Integer

instance CommutativeProduct Word8
instance CommutativeProduct Word16
instance CommutativeProduct Word32
instance CommutativeProduct Word64
instance CommutativeProduct Word
instance CommutativeProduct Natural

instance CommutativeProduct Float
instance CommutativeProduct Double

instance (RealFloat a, CommutativeProduct a) => CommutativeProduct (Complex a)

instance CommutativeProduct a => CommutativeProduct (Identity a)

-- @since: base-4.7.0.0
instance CommutativeProduct a => CommutativeProduct (Sum a)

-- @since: base-4.7.0.0
instance (Integral a, CommutativeProduct a) => CommutativeProduct (Ratio a)

instance (HasResolution a, CommutativeProduct a) => CommutativeProduct (Fixed a)

#if MIN_VERSION_base(4, 9, 0)
-- @since: base-4.9.0.0
instance CommutativeProduct a => CommutativeProduct (Min a)

-- @since: base-4.9.0.0
instance CommutativeProduct a => CommutativeProduct (Max a)

-- @since: base-4.9.0.0
instance CommutativeProduct a => CommutativeProduct (Product a)

-- @since: base-4.9.0.0
instance CommutativeProduct a => CommutativeProduct (Const a b)
#endif

#if MIN_VERSION_base(4, 11, 0)
-- @since: base-4.11.0.0
instance CommutativeProduct a => CommutativeProduct (Down a)
#endif

#if MIN_VERSION_base(4, 12, 0)
-- @since: base-4.12.0.0
instance CommutativeProduct a => CommutativeProduct (Op a b)

-- @since: base-4.12.0.0
instance CommutativeProduct (f a) => CommutativeProduct (Alt f a)
#endif