module Data.Semigroup.Abelian
( AbelianSemigroup
, FreeAbelianSemigroup
, toNonEmpty
, fromNonEmpty
) where
import Data.Constraint (Dict (..))
import Data.IntSet (IntSet)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NE
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Set (Set)
import Data.Semigroup
( Semigroup
, All
, Any
, Dual
, Max
, Min
, Option
, Product
, Sum
)
import Data.Void (Void)
import Data.Algebra.Free
( AlgebraType
, AlgebraType0
, FreeAlgebra (..)
, Proof (..)
)
class Semigroup m => AbelianSemigroup m
instance AbelianSemigroup Void
instance AbelianSemigroup ()
instance AbelianSemigroup All
instance AbelianSemigroup Any
instance AbelianSemigroup a => AbelianSemigroup (Dual a)
instance Ord a => AbelianSemigroup (Max a)
instance Ord a => AbelianSemigroup (Min a)
instance AbelianSemigroup a => AbelianSemigroup (Option a)
instance Num a => AbelianSemigroup (Product a)
instance Num a => AbelianSemigroup (Sum a)
instance Ord a => AbelianSemigroup (Set a)
instance AbelianSemigroup IntSet
newtype FreeAbelianSemigroup a = FreeAbelianSemigroup { runFreeAbelianSemigroup :: Map a Integer }
deriving (Ord, Eq, Show)
toNonEmpty :: FreeAbelianSemigroup a -> NonEmpty (a, Integer)
toNonEmpty (FreeAbelianSemigroup as) = NE.fromList . Map.toList $ as
fromNonEmpty :: Ord a => NonEmpty (a, Integer) -> Maybe (FreeAbelianSemigroup a)
fromNonEmpty = fmap (FreeAbelianSemigroup . Map.fromList) . go . NE.toList
where
go [] = Just []
go ((a, n) : as) | n < 0 = Nothing
| otherwise = ((a, n) :) <$> go as
instance Ord a => Semigroup (FreeAbelianSemigroup a) where
(FreeAbelianSemigroup a) <> (FreeAbelianSemigroup b) = FreeAbelianSemigroup $ Map.unionWith (+) a b
instance Ord a => AbelianSemigroup (FreeAbelianSemigroup a)
type instance AlgebraType0 FreeAbelianSemigroup a = Ord a
type instance AlgebraType FreeAbelianSemigroup a = (Ord a, AbelianSemigroup a)
instance FreeAlgebra FreeAbelianSemigroup where
returnFree a = FreeAbelianSemigroup $ Map.singleton a 1
foldMapFree f (FreeAbelianSemigroup as) = foldMapFree f (toNonEmpty_ as)
where
replicate_ :: a -> Integer -> [a]
replicate_ _ n | n <= 0 = error "foldMapFree @FreeAbelianSemigroup: impossible"
replicate_ a 1 = [a]
replicate_ a n = a : replicate_ a (n - 1)
toNonEmpty_ :: Map a Integer -> NonEmpty a
toNonEmpty_ = NE.fromList . concat . map (uncurry replicate_) . Map.toList
proof = Proof Dict
forget = Proof Dict