{-
Copyright 2011 Mario Blazevic
This file is part of the Streaming Component Combinators (SCC) project.
The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
version.
SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with SCC. If not, see
<http://www.gnu.org/licenses/>.
-}
-- | This module defines the 'Monoid' => 'CancellativeMonoid' => 'GCDMonoid' class hierarchy.
--
module Data.Monoid.Cancellative (
-- * Classes
CancellativeMonoid, GCDMonoid,
LeftCancellativeMonoid(..), RightCancellativeMonoid(..),
LeftGCDMonoid(..), RightGCDMonoid(..)
) where
import Data.Monoid (Monoid (mappend))
import qualified Data.List as List
import qualified Data.ByteString as ByteString
import qualified Data.Text as Text
import Data.ByteString (ByteString)
import Data.Text (Text)
-- | Class of monoids with a left inverse of 'mappend', satisfying the following law:
--
-- > mstripPrefix a (a `mappend` b) == Just b
-- > maybe b (a `mappend`) (mstripPrefix a b) == b
class Monoid m => LeftCancellativeMonoid m where
mstripPrefix :: m -> m -> Maybe m
-- | Class of monoids with a right inverse of 'mappend', satisfying the following law:
--
-- > mstripSuffix b (a `mappend` b) == Just a
-- > maybe b (`mappend` a) (mstripSuffix a b) == b
class Monoid m => RightCancellativeMonoid m where
mstripSuffix :: m -> m -> Maybe m
class LeftCancellativeMonoid m => LeftGCDMonoid m where
commonPrefix :: m -> m -> m
class RightCancellativeMonoid m => RightGCDMonoid m where
commonSuffix :: m -> m -> m
-- | Class of monoids for which the 'mappend' operation can be reverted while satisfying the following laws:
--
-- > mstripPrefix a (a `mappend` b) == Just b
-- > mstripSuffix b (a `mappend` b) == Just a
-- > maybe b (a `mappend`) (mstripPrefix a b) == b
-- > maybe b (`mappend` a) (mstripSuffix a b) == b
class (LeftCancellativeMonoid m, RightCancellativeMonoid m) => CancellativeMonoid m
-- | Class of monoids that allow the greatest common denominator to be found for any two given values. The operations
-- must satisfy the following laws:
--
-- > commonPrefix (a `mappend` b) (a `mappend` c) == a `mappend` commonPrefix b c
-- > commonSuffix (a `mappend` c) (b `mappend` c) == commonSuffix a b `mappend` c
class (CancellativeMonoid m, LeftGCDMonoid m, RightGCDMonoid m) => GCDMonoid m
-- List instances
instance Eq x => LeftCancellativeMonoid [x] where
mstripPrefix = List.stripPrefix
instance Eq x => LeftGCDMonoid [x] where
commonPrefix (x:xs) (y:ys) | x == y = x : commonPrefix xs ys
commonPrefix _ _ = []
instance Eq x => RightCancellativeMonoid [x] where
mstripSuffix s l = fmap List.reverse (mstripPrefix (List.reverse s) (List.reverse l))
instance Eq x => RightGCDMonoid [x] where
commonSuffix xs ys = List.reverse (commonPrefix (List.reverse xs) (List.reverse ys))
instance Eq x => CancellativeMonoid [x]
instance Eq x => GCDMonoid [x]
-- ByteString instances
instance LeftCancellativeMonoid ByteString where
mstripPrefix p l = if ByteString.isPrefixOf p l
then Just (ByteString.drop (ByteString.length p) l)
else Nothing
instance RightCancellativeMonoid ByteString where
mstripSuffix s l = if ByteString.isSuffixOf s l
then Just (ByteString.take (ByteString.length l - ByteString.length s) l)
else Nothing
instance CancellativeMonoid ByteString
instance LeftGCDMonoid ByteString where
commonPrefix x y = ByteString.take maxPrefixLength x
where maxPrefixLength = prefixLength 0
prefixLength n | ByteString.index x 0 == ByteString.index y 0 = prefixLength (succ n)
prefixLength n = n
instance RightGCDMonoid ByteString where
commonSuffix x y = ByteString.drop minNonSuffixLength x
where minNonSuffixLength = nonSuffixLength (ByteString.length x - 1) (ByteString.length y - 1)
nonSuffixLength m n | ByteString.index x m == ByteString.index y n = nonSuffixLength (pred m) (pred n)
nonSuffixLength m n = m + 1
instance GCDMonoid ByteString
-- Text instances
instance LeftCancellativeMonoid Text where
mstripPrefix p t = Text.stripPrefix p t
instance RightCancellativeMonoid Text where
mstripSuffix s t = Text.stripSuffix s t
instance CancellativeMonoid Text
instance LeftGCDMonoid Text where
commonPrefix x y = maybe Text.empty (\(p, _, _)-> p) (Text.commonPrefixes x y)
instance RightGCDMonoid Text where
commonSuffix x y = Text.reverse $ commonPrefix (Text.reverse x) (Text.reverse y)
instance GCDMonoid Text