-- |
-- Module : Data.Edison.Seq
-- Copyright : Copyright (c) 1998-1999 Chris Okasaki
-- License : MIT; see COPYRIGHT file for terms and conditions
--
-- Maintainer : robdockins AT fastmail DOT fm
-- Stability : stable
-- Portability : GHC, Hugs (MPTC and FD)
--
-- The sequence abstraction is usually viewed as a hierarchy of ADTs
-- including lists, queues, deques, catenable lists, etc. However, such
-- a hierarchy is based on efficiency rather than functionality. For example,
-- a list supports all the operations that a deque supports, even though
-- some of the operations may be inefficient. Hence, in Edison, all sequence
-- data structures are defined as instances of the single Sequence class:
--
-- @ class (Functor s, MonadPlus s) => Sequence s@
--
-- All sequences are also instances of 'Functor', 'Monad', and 'MonadPlus'.
-- In addition, all sequences are expected to be instances of @Eq@, @Show@,
-- and @Read@, although this is not enforced.
--
-- We follow the naming convention that every module implementing sequences
-- defines a type constructor named @Seq@.
--
-- For each method the \"default\" complexity is listed. Individual
-- implementations may differ for some methods. The documentation for
-- each implementation will list those methods for which the running time
-- differs from these.
--
-- A description of each Sequence function appears below. In most cases
-- psudeocode is also provided. Obviously, the psudeocode is illustrative only.
--
-- Sequences are represented in psudecode between angle brackets:
--
-- >
--
-- Such that @x0@ is at the left (front) of the sequence and
-- @xn-1@ is at the right (rear) of the sequence.
module Data.Edison.Seq (
-- * Superclass aliases
-- ** Functor aliases
map
-- ** Monad aliases
, singleton
, concatMap
-- ** MonadPlus aliases
, empty
, append
-- * The Sequence class
, Sequence (..)
) where
import Prelude hiding (concat,reverse,map,concatMap,foldr,foldl,foldr1,foldl1,
filter,takeWhile,dropWhile,lookup,take,drop,splitAt,
zip,zip3,zipWith,zipWith3,unzip,unzip3,null)
import Control.Monad
import Data.Monoid
import Data.Edison.Prelude
-- | Return the result of applying a function to
-- every element of a sequence. Identical
-- to @fmap@ from @Functor@.
--
-- > map f =
--
-- /Axioms:/
--
-- * @map f empty = empty@
--
-- * @map f (lcons x xs) = lcons (f x) (map f xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
map :: Sequence s => (a -> b) -> s a -> s b
map = fmap
-- | Create a singleton sequence. Identical to @return@
-- from @Monad@.
--
-- > singleton x =
--
-- /Axioms:/
--
-- * @singleton x = lcons x empty = rcons x empty@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
singleton :: Sequence s => a -> s a
singleton = return
-- | Apply a sequence-producing function to every element
-- of a sequence and flatten the result. 'concatMap'
-- is the bind @(>>=)@ operation of from @Monad@ with the
-- arguments in the reverse order.
--
-- > concatMap f xs = concat (map f xs)
--
-- /Axioms:/
--
-- * @concatMap f xs = concat (map f xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n + m )@
-- where @n@ is the length of the input sequence, @m@ is the
-- length of the output sequence, and @t@ is the running time of @f@
concatMap :: Sequence s => (a -> s b) -> s a -> s b
concatMap = flip (>>=)
-- | The empty sequence. Identical to @mzero@
-- from @MonadPlus@.
--
-- > empty = <>
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
empty :: Sequence s => s a
empty = mzero
-- | Append two sequence, with the first argument on the left
-- and the second argument on the right. Identical to @mplus@
-- from @MonadPlus@.
--
-- > append =
--
-- /Axioms:/
--
-- * @append xs ys = foldr lcons ys xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n1 )@
append :: Sequence s => s a -> s a -> s a
append = mplus
-- | The 'Sequence' class defines an interface for datatypes which
-- implement sequences. A description for each function is
-- given below.
class (Functor s, MonadPlus s) => Sequence s where
-- | Add a new element to the front\/left of a sequence
--
-- > lcons x =
--
-- /Axioms:/
--
-- * @lcons x xs = append (singleton x) xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
lcons :: a -> s a -> s a
-- | Add a new element to the right\/rear of a sequence
--
-- > rcons x =
--
-- /Axioms:/
--
-- * @rcons x xs = append xs (singleton x)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
rcons :: a -> s a -> s a
-- | Convert a list into a sequence
--
-- > fromList [x0,...,xn-1] =
--
-- /Axioms:/
--
-- * @fromList xs = foldr lcons empty xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
fromList :: [a] -> s a
-- | Create a sequence containing @n@ copies of the given element.
-- Return 'empty' if @n\<0@.
--
-- @copy n x = \@
--
-- /Axioms:/
--
-- * @n > 0 ==> copy n x = cons x (copy (n-1) x)@
--
-- * @n \<= 0 ==> copy n x = empty@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
copy :: Int -> a -> s a
-- | Separate a sequence into its first (leftmost) element and the
-- remaining sequence. Calls 'fail' if the sequence is empty.
--
-- /Axioms:/
--
-- * @lview empty = fail@
--
-- * @lview (lcons x xs) = return (x,xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
lview :: (Monad m) => s a -> m (a, s a)
-- | Return the first element of a sequence.
-- Signals an error if the sequence is empty.
--
-- /Axioms:/
--
-- * @lhead empty = undefined@
--
-- * @lhead (lcons x xs) = x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
lhead :: s a -> a
-- | Returns the first element of a sequence.
-- Calls 'fail' if the sequence is empty.
--
-- /Axioms:/
--
-- * @lheadM empty = fail@
--
-- * @lheadM (lcons x xs) = return x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
lheadM :: (Monad m) => s a -> m a
-- | Delete the first element of the sequence.
-- Signals error if sequence is empty.
--
-- /Axioms:/
--
-- * @ltail empty = undefined@
--
-- * @ltail (lcons x xs) = xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
ltail :: s a -> s a
-- | Delete the first element of the sequence.
-- Calls 'fail' if the sequence is empty.
--
-- /Axioms:/
--
-- * @ltailM empty = fail@
--
-- * @ltailM (lcons x xs) = return xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
ltailM :: (Monad m) => s a -> m (s a)
-- | Separate a sequence into its last (rightmost) element and the
-- remaining sequence. Calls 'fail' if the sequence is empty.
--
-- /Axioms:/
--
-- * @rview empty = fail@
--
-- * @rview (rcons x xs) = return (x,xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
rview :: (Monad m) => s a -> m (a, s a)
-- | Return the last (rightmost) element of the sequence.
-- Signals error if sequence is empty.
--
-- /Axioms:/
--
-- * @rhead empty = undefined@
--
-- * @rhead (rcons x xs) = x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
rhead :: s a -> a
-- | Returns the last element of the sequence.
-- Calls 'fail' if the sequence is empty.
--
-- /Axioms:/
--
-- * @rheadM empty = fail@
--
-- * @rheadM (rcons x xs) = return x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
rheadM :: (Monad m) => s a -> m a
-- | Delete the last (rightmost) element of the sequence.
-- Signals an error if the sequence is empty.
--
-- /Axioms:/
--
-- * @rtail empty = undefined@
--
-- * @rtail (rcons x xs) = xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
rtail :: s a -> s a
-- | Delete the last (rightmost) element of the sequence.
-- Calls 'fail' of the sequence is empty
--
-- /Axioms:/
--
-- * @rtailM empty = fail@
--
-- * @rtailM (rcons x xs) = return xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
rtailM :: (Monad m) => s a -> m (s a)
-- | Returns 'True' if the sequence is empty and 'False' otherwise.
--
-- > null = (n==0)
--
-- /Axioms:/
--
-- * @null xs = (size xs == 0)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( 1 )@
null :: s a -> Bool
-- | Returns the length of a sequence.
--
-- > size = n
--
-- /Axioms:/
--
-- * @size empty = 0@
--
-- * @size (lcons x xs) = 1 + size xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
size :: s a -> Int
-- | Convert a sequence to a list.
--
-- > toList = [x0,...,xn-1]
--
-- /Axioms:/
--
-- * @toList empty = []@
--
-- * @toList (lcons x xs) = x : toList xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
toList :: s a -> [a]
-- | Flatten a sequence of sequences into a simple sequence.
--
-- > concat xss = foldr append empty xss
--
-- /Axioms:/
--
-- * @concat xss = foldr append empty xss@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n + m )@
-- where @n@ is the length of the input sequence and @m@ is
-- length of the output sequence.
concat :: s (s a) -> s a
-- | Reverse the order of a sequence
--
-- > reverse =
--
-- /Axioms:/
--
-- * @reverse empty = empty@
--
-- * @reverse (lcons x xs) = rcons x (reverse xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
reverse :: s a -> s a
-- | Reverse a sequence onto the front of another sequence.
--
-- > reverseOnto =
--
-- /Axioms:/
--
-- * @reverseOnto xs ys = append (reverse xs) ys@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n1 )@
reverseOnto :: s a -> s a -> s a
-- | Combine all the elements of a sequence into a single value,
-- given a combining function and an initial value. The order
-- in which the elements are applied to the combining function
-- is unspecified. @fold@ is one of the few ambiguous sequence
-- functions.
--
-- /Axioms:/
--
-- * @fold f c empty = c@
--
-- * @f is fold-commutative ==> fold f = foldr f = foldl f@
--
-- @fold f@ is /unambiguous/ iff @f@ is fold-commutative.
--
-- Default running type: @O( t * n )@
-- where @t@ is the running tome of @f@.
fold :: (a -> b -> b) -> b -> s a -> b
-- | A strict variant of 'fold'. @fold'@ is one of the few ambiguous
-- sequence functions.
--
-- /Axioms:/
--
-- * @forall a. f a _|_ = _|_ ==> fold f x xs = fold' f x xs@
--
-- @fold f@ is /unambiguous/ iff @f@ is fold-commutative.
--
-- Default running type: @O( t * n )@
-- where @t@ is the running tome of @f@.
fold' :: (a -> b -> b) -> b -> s a -> b
-- | Combine all the elements of a non-empty sequence into a
-- single value, given a combining function. Signals an error
-- if the sequence is empty.
--
-- /Axioms:/
--
-- * @f is fold-commutative ==> fold1 f = foldr1 f = foldl1 f@
--
-- @fold1 f@ is /unambiguous/ iff @f@ is fold-commutative.
--
-- Default running type: @O( t * n )@
-- where @t@ is the running tome of @f@.
fold1 :: (a -> a -> a) -> s a -> a
-- | A strict variant of 'fold1'.
--
-- /Axioms:/
--
-- * @forall a. f a _|_ = _|_ ==> fold1' f xs = fold1 f xs@
--
-- @fold1' f@ is /unambiguous/ iff @f@ is fold-commutative.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
fold1' :: (a -> a -> a) -> s a -> a
-- | Combine all the elements of a sequence into a single value,
-- given a combining function and an initial value. The function
-- is applied with right nesting.
--
-- > foldr (%) c = x0 % (x1 % ( ... % (xn-1 % c)))
--
-- /Axioms:/
--
-- * @foldr f c empty = c@
--
-- * @foldr f c (lcons x xs) = f x (foldr f c xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldr :: (a -> b -> b) -> b -> s a -> b
-- | Strict variant of 'foldr'.
--
-- /Axioms:/
--
-- * @forall a. f a _|_ = _|_ ==> foldr f x xs = foldr' f x xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldr' :: (a -> b -> b) -> b -> s a -> b
-- | Combine all the elements of a sequence into a single value,
-- given a combining function and an initial value. The function
-- is applied with left nesting.
--
-- > foldl (%) c = (((c % x0) % x1) % ... ) % xn-1
--
-- /Axioms:/
--
-- * @foldl f c empty = c@
--
-- * @foldl f c (lcons x xs) = foldl f (f c x) xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldl :: (b -> a -> b) -> b -> s a -> b
-- | Strict variant of 'foldl'.
--
-- /Axioms:/
--
-- * forall a. f _|_ a = _|_ ==> foldl f z xs = foldl' f z xs
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldl' :: (b -> a -> b) -> b -> s a -> b
-- | Combine all the elements of a non-empty sequence into a
-- single value, given a combining function. The function
-- is applied with right nesting. Signals an error if the
-- sequence is empty.
--
-- > foldr1 (+)
-- > | n==0 = error "ModuleName.foldr1: empty sequence"
-- > | n>0 = x0 + (x1 + ... + xn-1)
--
-- /Axioms:/
--
-- * @foldr1 f empty = undefined@
--
-- * @foldr1 f (rcons x xs) = foldr f x xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldr1 :: (a -> a -> a) -> s a -> a
-- | Strict variant of 'foldr1'.
--
-- /Axioms:/
--
-- * forall a. f a _|_ = _|_ ==> foldr1 f xs = foldr1' f xs
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldr1' :: (a -> a -> a) -> s a -> a
-- | Combine all the elements of a non-empty sequence into
-- a single value, given a combining function. The function
-- is applied with left nesting. Signals an error if the
-- sequence is empty.
--
-- > foldl1 (+)
-- > | n==0 = error "ModuleName.foldl1: empty sequence"
-- > | n>0 = (x0 + x1) + ... + xn-1
--
-- /Axioms:/
--
-- * @foldl1 f empty = undefined@
--
-- * @foldl1 f (lcons x xs) = foldl f x xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldl1 :: (a -> a -> a) -> s a -> a
-- | Strict variant of 'foldl1'.
--
-- /Axioms:/
--
-- * forall a. f _|_ a = _|_ ==> foldl1 f xs = foldl1' f xs
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldl1' :: (a -> a -> a) -> s a -> a
-- | See 'reduce1' for additional notes.
--
-- > reducer f x xs = reduce1 f (cons x xs)
--
-- /Axioms:/
--
-- * @reducer f c xs = foldr f c xs@ for associative @f@
--
-- @reducer f@ is unambiguous iff @f@ is an associative function.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
reducer :: (a -> a -> a) -> a -> s a -> a
-- | Strict variant of 'reducer'.
--
-- See 'reduce1' for additional notes.
--
-- /Axioms:/
--
-- * @forall a. f a _|_ = _|_ && forall a. f _|_ a = _|_ ==>
-- reducer f x xs = reducer' f x xs@
--
-- @reducer' f@ is unambiguous iff @f@ is an associative function.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
reducer' :: (a -> a -> a) -> a -> s a -> a
-- | See 'reduce1' for additional notes.
--
-- > reducel f x xs = reduce1 f (rcons x xs)
--
-- /Axioms:/
--
-- * @reducel f c xs = foldl f c xs@ for associative @f@
--
-- @reducel f@ is unambiguous iff @f@ is an associative function.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
reducel :: (a -> a -> a) -> a -> s a -> a
-- | Strict variant of 'reducel'.
--
-- See 'reduce1' for additional notes.
--
-- /Axioms:/
--
-- * @forall a. f a _|_ = _|_ && forall a. f _|_ a = _|_ ==>
-- reducel f x xs = reducel' f x xs@
--
-- @reducel' f@ is unambiguous iff @f@ is an associative function.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
reducel' :: (a -> a -> a) -> a -> s a -> a
-- | A reduce is similar to a fold, but combines elements in a balanced fashion.
-- The combining function should usually be associative. If the combining
-- function is associative, the various reduce functions yield the same
-- results as the corresponding folds.
--
-- What is meant by \"in a balanced fashion\"? We mean that
-- @reduce1 (%) \@ equals some complete parenthesization of
-- @x0 % x1 % ... % xn-1@ such that the nesting depth of parentheses
-- is @O( log n )@. The precise shape of this parenthesization is
-- unspecified.
--
-- > reduce1 f = x
-- > reduce1 f =
-- > f (reduce1 f ) (reduce1 f )
--
-- for some @i@ such that @ 0 \<= i && i \< n-1 @
--
-- Although the exact value of i is unspecified it tends toward @n\/2@
-- so that the depth of calls to @f@ is at most logarithmic.
--
-- Note that @reduce@* are some of the only sequence operations for which
-- different implementations are permitted to yield different answers. Also
-- note that a single implementation may choose different parenthisizations
-- for different sequences, even if they are the same length. This will
-- typically happen when the sequences were constructed differently.
--
-- The canonical applications of the reduce functions are algorithms like
-- merge sort where:
--
-- > mergesort xs = reducer merge empty (map singleton xs)
--
--
-- /Axioms:/
--
-- * @reduce1 f empty = undefined@
--
-- * @reduce1 f xs = foldr1 f xs = foldl1 f xs@ for associative @f@
--
-- @reduce1 f@ is unambiguous iff @f@ is an associative function.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
reduce1 :: (a -> a -> a) -> s a -> a
-- | Strict variant of 'reduce1'.
--
-- /Axioms:/
--
-- * @forall a. f a _|_ = _|_ && forall a. f _|_ a = _|_ ==>
-- reduce1 f xs = reduce1' f xs@
--
-- @reduce1' f@ is unambiguous iff @f@ is an associative function.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
reduce1' :: (a -> a -> a) -> s a -> a
-- | Extract a prefix of length @i@ from the sequence. Return
-- 'empty' if @i@ is negative, or the entire sequence if @i@
-- is too large.
--
-- > take i xs = fst (splitAt i xs)
--
-- /Axioms:/
--
-- * @i \< 0 ==> take i xs = empty@
--
-- * @i > size xs ==> take i xs = xs@
--
-- * @size xs == i ==> take i (append xs ys) = xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
take :: Int -> s a -> s a
-- | Delete a prefix of length @i@ from a sequence. Return
-- the entire sequence if @i@ is negative, or 'empty' if
-- @i@ is too large.
--
-- > drop i xs = snd (splitAt i xs)
--
-- /Axioms:/
--
-- * @i \< 0 ==> drop i xs = xs@
--
-- * @i > size xs ==> drop i xs = empty@
--
-- * @size xs == i ==> drop i (append xs ys) = ys@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
drop :: Int -> s a -> s a
-- | Split a sequence into a prefix of length @i@
-- and the remaining sequence. Behaves the same
-- as the corresponding calls to 'take' and 'drop'
-- if @i@ is negative or too large.
--
-- > splitAt i xs
-- > | i < 0 = (<> , )
-- > | i < n = (, )
-- > | i >= n = (, <> )
--
-- /Axioms:/
--
-- * @splitAt i xs = (take i xs,drop i xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
splitAt :: Int -> s a -> (s a, s a)
-- | Extract a subsequence from a sequence. The integer
-- arguments are \"start index\" and \"length\" NOT
-- \"start index\" and \"end index\". Behaves the same
-- as the corresponding calls to 'take' and 'drop' if the
-- start index or length are negative or too large.
--
-- > subseq i len xs = take len (drop i xs)
--
-- /Axioms:/
--
-- * @subseq i len xs = take len (drop i xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i + len )@
subseq :: Int -> Int -> s a -> s a
-- | Extract the elements of a sequence that satisfy the
-- given predicate, retaining the relative ordering of
-- elements from the original sequence.
--
-- > filter p xs = foldr pcons empty xs
-- > where pcons x xs = if p x then cons x xs else xs
--
-- /Axioms:/
--
-- * @filter p empty = empty@
--
-- * @filter p (lcons x xs) = if p x
-- then lcons x (filter p xs)
-- else filter p xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @p@
filter :: (a -> Bool) -> s a -> s a
-- | Separate the elements of a sequence into those that
-- satisfy the given predicate and those that do not,
-- retaining the relative ordering of elements from the
-- original sequence.
--
-- > partition p xs = (filter p xs, filter (not . p) xs)
--
-- /Axioms:/
--
-- * @partition p xs = (filter p xs, filter (not . p) xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @p@
partition :: (a -> Bool) -> s a -> (s a, s a)
-- | Extract the maximal prefix of elements satisfying the
-- given predicate.
--
-- > takeWhile p xs = fst (splitWhile p xs)
--
-- /Axioms:/
--
-- * @takeWhile p empty = empty@
--
-- * @takeWhile p (lcons x xs) = if p x
-- then lcons x (takeWhile p xs)
-- else empty@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @p@
takeWhile :: (a -> Bool) -> s a -> s a
-- | Delete the maximal prefix of elements satisfying the
-- given predicate.
--
-- > dropWhile p xs = snd (splitWhile p xs)
--
-- /Axioms:/
--
-- * @dropWhile p empty = empty@
--
-- * @dropWhile p (lcons x xs) = if p x
-- then dropWhile p xs
-- else lcons x xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @p@
dropWhile :: (a -> Bool) -> s a -> s a
-- | Split a sequence into the maximal prefix of elements
-- satisfying the given predicate, and the remaining sequence.
--
-- > splitWhile p = (, )
-- > where i = min j such that p xj (or n if no such j)
--
-- /Axioms:/
--
-- * @splitWhile p xs = (takeWhile p xs,dropWhile p xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @p@
splitWhile :: (a -> Bool) -> s a -> (s a, s a)
-- | Test whether an index is valid for the given sequence. All indexes
-- are 0 based.
--
-- > inBounds i = (0 <= i && i < n)
--
-- /Axioms:/
--
-- * @inBounds i xs = (0 \<= i && i \< size xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
inBounds :: Int -> s a -> Bool
-- | Return the element at the given index. All indexes are 0 based.
-- Signals error if the index out of bounds.
--
-- > lookup i xs@
-- > | inBounds i xs = xi
-- > | otherwise = error "ModuleName.lookup: index out of bounds"
--
-- /Axioms:/
--
-- * @not (inBounds i xs) ==> lookup i xs = undefined@
--
-- * @size xs == i ==> lookup i (append xs (lcons x ys)) = x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
lookup :: Int -> s a -> a
-- | Return the element at the given index. All indexes are 0 based.
-- Calls 'fail' if the index is out of bounds.
--
-- > lookupM i xs@
-- > | inBounds i xs = Just xi
-- > | otherwise = Nothing
--
-- /Axioms:/
--
-- * @not (inBounds i xs) ==> lookupM i xs = fail@
--
-- * @size xs == i ==> lookupM i (append xs (lcons x ys)) = return x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
lookupM :: (Monad m) => Int -> s a -> m a
-- | Return the element at the given index, or the
-- default argument if the index is out of bounds. All indexes are
-- 0 based.
--
-- > lookupWithDefault d i xs@
-- > | inBounds i xs = xi
-- > | otherwise = d
--
-- /Axioms:/
--
-- * @not (inBounds i xs) ==> lookupWithDefault d i xs = d@
--
-- * @size xs == i ==> lookupWithDefault d i (append xs (lcons x ys)) = x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
lookupWithDefault :: a -> Int -> s a -> a
-- | Replace the element at the given index, or return
-- the original sequence if the index is out of bounds.
-- All indexes are 0 based.
--
-- > update i y xs@
-- > | inBounds i xs =
-- > | otherwise = xs
--
-- /Axioms:/
--
-- * @not (inBounds i xs) ==> update i y xs = xs@
--
-- * @size xs == i ==> update i y (append xs (lcons x ys)) =
-- append xs (lcons y ys)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i )@
update :: Int -> a -> s a -> s a
-- | Apply a function to the element at the given index, or
-- return the original sequence if the index is out of bounds.
-- All indexes are 0 based.
--
-- > adjust f i xs@
-- > | inBounds i xs =
-- > | otherwise = xs
--
-- /Axioms:/
--
-- * @not (inBounds i xs) ==> adjust f i xs = xs@
--
-- * @size xs == i ==> adjust f i (append xs (lcons x ys)) =
-- append xs (cons (f x) ys)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( i + t )@
-- where @t@ is the running time of @f@
adjust :: (a -> a) -> Int -> s a -> s a -- map a single element
-- | Like 'map', but include the index with each element.
-- All indexes are 0 based.
--
-- > mapWithIndex f =
--
-- /Axioms:/
--
-- * @mapWithIndex f empty = empty@
--
-- * @mapWithIndex f (rcons x xs) = rcons (f (size xs) x) (mapWithIndex f xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
mapWithIndex :: (Int -> a -> b) -> s a -> s b
-- | Like 'foldr', but include the index with each element.
-- All indexes are 0 based.
--
-- > foldrWithIndex f c =
-- > f 0 x0 (f 1 x1 (... (f (n-1) xn-1 c)))
--
-- /Axioms:/
--
-- * @foldrWithIndex f c empty = c@
--
-- * @foldrWithIndex f c (rcons x xs) =
-- foldrWithIndex f (f (size xs) x c) xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldrWithIndex :: (Int -> a -> b -> b) -> b -> s a -> b
-- | Strict variant of 'foldrWithIndex'.
--
-- /Axioms:/
--
-- * @forall i a. f i a _|_ = _|_ ==> foldrWithIndex f x xs =
-- foldrWithIndex' f x xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldrWithIndex' :: (Int -> a -> b -> b) -> b -> s a -> b
-- | Like 'foldl', but include the index with each element.
-- All indexes are 0 based.
--
-- > foldlWithIndex f c =
-- > f (...(f (f c 0 x0) 1 x1)...) (n-1) xn-1)
--
-- /Axioms:/
--
-- * @foldlWithIndex f c empty = c@
--
-- * @foldlWithIndex f c (rcons x xs) =
-- f (foldlWithIndex f c xs) (size xs) x@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldlWithIndex :: (b -> Int -> a -> b) -> b -> s a -> b
-- | Strict variant of 'foldlWithIndex'.
--
-- /Axioms:/
--
-- * @forall i a. f _|_ i a = _|_ ==> foldlWithIndex f x xs =
-- foldlWithIndex' f x xs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the running time of @f@
foldlWithIndex' :: (b -> Int -> a -> b) -> b -> s a -> b
-- | Combine two sequences into a sequence of pairs. If the
-- sequences are different lengths, the excess elements of the
-- longer sequence is discarded.
--
-- > zip = <(x0,y0),...,(xj-1,yj-1)>
-- > where j = min {n,m}
--
-- /Axioms:/
--
-- * @zip xs ys = zipWith (,) xs ys@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( min( n1, n2 ) )@
zip :: s a -> s b -> s (a,b)
-- | Like 'zip', but combines three sequences into triples.
--
-- > zip3 =
-- > <(x0,y0,z0),...,(xj-1,yj-1,zj-1)>
-- > where j = min {n,m,k}
--
-- /Axioms:/
--
-- * @zip3 xs ys zs = zipWith3 (,,) xs ys zs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( min( n1, n2, n3 ) )@
zip3 :: s a -> s b -> s c -> s (a,b,c)
-- | Combine two sequences into a single sequence by mapping
-- a combining function across corresponding elements. If
-- the sequences are of different lengths, the excess elements
-- of the longer sequence are discarded.
--
-- > zipWith f xs ys = map (uncurry f) (zip xs ys)
--
-- /Axioms:/
--
-- * @zipWith f (lcons x xs) (lcons y ys) =
-- lcons (f x y) (zipWith f xs ys)@
--
-- * @(null xs || null ys) ==> zipWith xs ys = empty@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * min( n1, n2 ) )@
-- where @t@ is the running time of @f@
zipWith :: (a -> b -> c) -> s a -> s b -> s c
-- | Like 'zipWith' but for a three-place function and three
-- sequences.
--
-- > zipWith3 f xs ys zs = map (uncurry f) (zip3 xs ys zs)
--
-- /Axioms:/
--
-- * @zipWith3 (lcons x xs) (lcons y ys) (lcons z zs) =
-- lcons (f x y z) (zipWith3 f xs ys zs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * min( n1, n2, n3 ) )@
-- where @t@ is the running time of @f@
zipWith3 :: (a -> b -> c -> d) -> s a -> s b -> s c -> s d
-- | Transpose a sequence of pairs into a pair of sequences.
--
-- > unzip xs = (map fst xs, map snd xs)
--
-- /Axioms:/
--
-- * @unzip xys = unzipWith fst snd xys@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
unzip :: s (a,b) -> (s a, s b)
-- | Transpose a sequence of triples into a triple of sequences
--
-- > unzip3 xs = (map fst3 xs, map snd3 xs, map thd3 xs)
-- > where fst3 (x,y,z) = x
-- > snd3 (x,y,z) = y
-- > thd3 (x,y,z) = z
--
-- /Axioms:/
--
-- * @unzip3 xyzs = unzipWith3 fst3 snd3 thd3 xyzs@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
unzip3 :: s (a,b,c) -> (s a, s b, s c)
-- | Map two functions across every element of a sequence,
-- yielding a pair of sequences
--
-- > unzipWith f g xs = (map f xs, map g xs)
--
-- /Axioms:/
--
-- * @unzipWith f g xs = (map f xs, map g xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the maximum running time
-- of @f@ and @g@
unzipWith :: (a -> b) -> (a -> c) -> s a -> (s b, s c)
-- | Map three functions across every element of a sequence,
-- yielding a triple of sequences.
--
-- > unzipWith3 f g h xs = (map f xs, map g xs, map h xs)
--
-- /Axioms:/
--
-- * @unzipWith3 f g h xs = (map f xs,map g xs,map h xs)@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( t * n )@
-- where @t@ is the maximum running time
-- of @f@, @g@, and @h@
unzipWith3 :: (a -> b) -> (a -> c) -> (a -> d) -> s a -> (s b, s c, s d)
-- | Semanticly, this function is a partial identity function. If the
-- datastructure is infinite in size or contains exceptions or non-termination
-- in the structure itself, then @strict@ will result in bottom. Operationally,
-- this function walks the datastructure forcing any closures. Elements contained
-- in the sequence are /not/ forced.
--
-- /Axioms:/
--
-- * @strict xs = xs@ OR @strict xs = _|_@
--
-- This function is always /unambiguous/.
--
-- Default running time: @O( n )@
strict :: s a -> s a
-- | Similar to 'strict', this function walks the datastructure forcing closures.
-- However, @strictWith@ will additionally apply the given function to the
-- sequence elements, force the result using @seq@, and then ignore it.
-- This function can be used to perform various levels of forcing on the
-- sequence elements. In particular:
--
-- > strictWith id xs
--
-- will force the spine of the datastructure and reduce each element to WHNF.
--
-- /Axioms:/
--
-- * forall @f :: a -> b@, @strictWith f xs = xs@ OR @strictWith f xs = _|_@
--
-- This function is always /unambiguous/.
--
-- Default running time: unbounded (forcing element closures can take arbitrairly long)
strictWith :: (a -> b) -> s a -> s a
-- | A method to facilitate unit testing. Returns 'True' if the structural
-- invariants of the implementation hold for the given sequence. If
-- this function returns 'False', it represents a bug in the implementation.
structuralInvariant :: s a -> Bool
-- | The name of the module implementing s.
instanceName :: s a -> String
----------------------------------------------------------------------
-- Other possible operations not currently included
{-
insertAt :: Int -> a -> s a -> s a
-- adds to front or rear if index out of bounds
--
-- insertAt i y xs@
-- | i < 0 = cons y xs
-- | i >= n = snoc xs y
-- | otherwise =
deleteAt :: Int -> s a -> s a
-- returns original sequence if index out of bounds
--
-- deleteAt i xs@
-- | i < 0 = xs
-- | i >= n = xs
-- | otherwise =
insertAt i x s = append before (cons x after)
where (before, after) = splitAt i s
deleteAt i s = if i < 0 then s else append before (ltail after)
where (before, after) = splitAt i s
-}