packages feed

group-theory-0.1.0.0: src/Data/Group/Cyclic.hs

{-# language BangPatterns #-}
{-# language FlexibleInstances #-}
{-# language Safe #-}
-- |
-- Module       : Data.Group.Cyclic
-- Copyright    : (c) 2020 Emily Pillmore
-- License      : BSD-style
--
-- Maintainer   : Emily Pillmore <emilypi@cohomolo.gy>,
--                Reed Mullanix <reedmullanix@gmail.com>
-- Stability    : stable
-- Portability  : non-portable
--
-- This module contains definitions for 'CyclicGroup'
-- along with the relevant combinators.
--
module Data.Group.Cyclic
( -- * Cyclic groups
  CyclicGroup(..)
  -- ** Combinators
, generate
, classify
) where

import Data.Functor.Const
import Data.Functor.Identity
import Data.Group
import Data.Int
import Data.List
import Data.Monoid
import Data.Ord
import Data.Proxy
import Data.Word

-- $setup
--
-- >>> import qualified Prelude
-- >>> import Data.Group
-- >>> import Data.Monoid
-- >>> import Data.Semigroup
-- >>> :set -XTypeApplications

-- -------------------------------------------------------------------- --
-- Cyclic groups

-- | A 'CyclicGroup' is a 'Group' that is generated by a single element.
-- This element is called a /generator/ of the group. There can be many
-- generators for a group, e.g., any representative of an equivalence
-- class of prime numbers of the integers modulo @n@, but to make things
-- easy, we ask for only one generator.
--
class Group g => CyclicGroup g where
  generator :: g
  {-# minimal generator #-}

instance CyclicGroup () where
  generator = ()
  {-# inline generator #-}

-- instance CyclicGroup b => CyclicGroup (a -> b) where
--   generator = const generator
--   {-# inlinable generator #-}

instance CyclicGroup a => CyclicGroup (Dual a) where
  generator = Dual (invert generator)
  {-# inlinable generator #-}

instance CyclicGroup (Sum Integer) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Rational) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Int) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Int8) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Int16) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Int32) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Int64) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Word) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Word8) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Word16) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Word32) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup (Sum Word64) where
  generator = 1
  {-# inline generator #-}

instance CyclicGroup a => CyclicGroup (Const a b) where
  generator = Const generator
  {-# inlinable generator #-}

instance CyclicGroup a => CyclicGroup (Identity a) where
  generator = Identity generator
  {-# inlinable generator #-}

instance CyclicGroup a => CyclicGroup (Proxy a) where
  generator = Proxy
  {-# inlinable generator #-}

instance (CyclicGroup a, CyclicGroup b) => CyclicGroup (a,b) where
  generator = (generator, generator)
  {-# inlinable generator #-}

instance (CyclicGroup a, CyclicGroup b, CyclicGroup c) => CyclicGroup (a,b,c) where
  generator = (generator, generator, generator)
  {-# inlinable generator #-}

instance (CyclicGroup a, CyclicGroup b, CyclicGroup c, CyclicGroup d) => CyclicGroup (a,b,c,d)  where
  generator = (generator, generator, generator, generator)
  {-# inlinable generator #-}

instance (CyclicGroup a, CyclicGroup b, CyclicGroup c, CyclicGroup d, CyclicGroup e) => CyclicGroup (a,b,c,d,e) where
  generator = (generator, generator, generator, generator, generator)
  {-# inlinable generator #-}

instance CyclicGroup a => CyclicGroup (Down a) where
  generator = Down generator
  {-# inline generator #-}

instance CyclicGroup a => CyclicGroup (Endo a) where
  generator = Endo $ const generator
  {-# inline generator #-}

-- -------------------------------------------------------------------- --
-- Cyclic group combinators

-- | Lazily generate all elements of a 'CyclicGroup' from its generator.
--
-- /Note/: fuses.
--
generate :: (Eq a, CyclicGroup a) => [a]
generate = unfoldr go (generator, 0 :: Integer)
  where
    go (a, !n)
      | a == mempty, n > 0 = Nothing
      | otherwise = Just (a, (a <> generator, succ n))
{-# noinline generate #-}

-- | Classify elements of a 'CyclicGroup'.
--
-- Apply a classifying function @a -> Bool@ to the elements
-- of a 'CyclicGroup' as generated by its designated generator.
--
-- === __Examples__:
--
-- >>> classify (< (3 :: Sum Word8))
-- [Sum {getSum = 1},Sum {getSum = 2}]
--
classify :: (Eq a, CyclicGroup a) => (a -> Bool) -> [a]
classify p = filter p generate
{-# inline classify #-}