{- 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 . -} -- | 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