{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-} -- | -- My module for streams module Data.Rivers.Streams where import Prelude hiding (zip,unzip) import Data.Rivers.Idiom import Data.Rivers.NumExt import Math.OEIS (SequenceData,lookupSequence, getSequenceByID) import Control.Monad (liftM2) import Test.QuickCheck (Arbitrary, CoArbitrary, arbitrary, coarbitrary) import Test.LazySmallCheck (Serial, series, cons2) -- * Streams -- -- | -- Your standard /Streams/, renamed to @S@ because @S@ looks like a meandering -- stream. -- data S v = Cons v (S v) deriving (Eq, Ord, Show, Read) infixr 5 <<| infixr 5 <|| infixr 5 |~| infixr 5 |!| -- ** Cons 1, n: (<||) :: a -> S a -> S a (<<|) :: [a] -> S a -> S a -- ^ prepend, Hinze UFP p.3 (|~|) :: S a -> S a -> S a --(|%|) :: (Fractional a) => S a -> S a -> S a --(|*|) :: (Num a) => S a -> S a -> S a ago :: Integer -> S a -> a anyA :: S a z0 :: (Num a) => S a asum :: (Num a) => S a -> S a bsum :: (Num a) => S a -> S a csum :: (Num a) => S a -> S a diff :: (Num a) => S a -> S a inv :: (Num a) => S a -> S a sconst :: (Num a) => a -> S a times :: (Num a) => a -> S a -> S a plus :: (Num a) => S a -> S a -> S a interleave :: S a -> S a -> S a interleave' :: S a -> S a -> S a alternate :: S a -> S a -> S a combStreams :: [[a]] -> [[a]] drop0L :: S a -> S a dropIp1L :: S a -> S a dup :: S a -> S a -- ** (Improperly) using @Ord@ (|!|) :: (Ord a) => S a -> S a -> S a merge :: (Ord a) => S a -> S a -> S a union :: (Ord a) => S a -> S a -> S a -- ** (Improperly) using @Eq@ allEqual :: (Eq a) => [a] -> Bool group :: (Eq a) => S a -> S [a] fix :: (a -> a) -> a inits :: S a -> S [a] interleave3 :: S a -> S a -> S a intersperse :: a -> S a -> S a map1 :: (a -> b) -> S a -> S b mapAdjacent :: (a -> a -> b) -> [a] -> [b] --power :: (Fractional a, Integral b) => S a -> b -> S a turn :: (Integral a) => a -> [a] --srecip :: (Fractional a) => S a -> S a -- ** @G@enerating Functions -- | -- -- A "generating function" for Streams. type G v o = [v] -> o -- ** Generating Functions, etc fromFG :: G a a -> S a revFix :: G a a -> S a rgen :: G a b -> S a -> S b fwdFix :: G a a -> S a grow :: G a b -> S a -> S b hOfFG :: G a b -> b tOfFG :: G a b -> a -> G a b rep :: (S a -> S b) -> G a b rgen' :: G a b -> [a] -> S a -> S b hOfRG :: (G a b, [a]) -> b tOfRG :: (G a b, [a]) -> a -> (G a b, [a]) fromRG :: (G a a, [a]) -> S a -- ** Gen to Tree: toT :: G a b -> Tree a b -- ** Tree to Gen: toG :: Tree a b -> G a b -- * Infinite @Tree@s -- | -- An infinite Tree. Used to represent /Streams/ data Tree a o = Node o (a -> Tree a o) -- ** Trees branches :: Tree a b -> a -> Tree a b fromT :: Tree a a -> S a label :: Tree a b -> b -- * Coalgebras -- | -- Your standard Co-Algebra (dual to Algebra). type Coalg c a b = (c -> b, c -> a -> c) -- ** Coalgebraic unfold :: Coalg c a b -> c -> Tree a b cfix :: Coalg c a a -> c -> S a groW :: Coalg c a b -> c -> S a -> S b sMap :: (a -> b) -> S a -> S b sMap2 :: (a -> b -> c) -> S a -> S b -> S c sMap3 :: (a -> b -> c -> d) -> S a -> S b -> S c -> S d sMap4 :: (a -> b -> c -> d -> e) -> S a -> S b -> S c -> S d -> S e sEven :: S a -> S a seven :: S a -> S a sOdd :: S a -> S a sodd :: S a -> S a -- ** Using @Bool@ Predicates sbreak :: (a -> Bool) -> S a -> ([a], S a) sdropWhile :: (a -> Bool) -> S a -> S a stakeWhile :: (a -> Bool) -> S a -> [a] sfilter :: (a -> Bool) -> S a -> S a spartition :: (a -> Bool) -> S a -> (S a, S a) sspan :: (a -> Bool) -> S a -> ([a], S a) scan :: (a -> b -> a) -> a -> S b -> S a scan' :: (a -> b -> a) -> a -> S b -> S a scan1 :: (a -> a -> a) -> S a -> S a scan1' :: (a -> a -> a) -> S a -> S a scycle :: [a] -> S a -- ** Drivers siterate :: (a -> a) -> a -> S a -- ** Heads and Tails shead :: S a -> a stail :: S a -> S a tail2 :: S a -> S a tails :: S a -> S (S a) -- ** Indexed stake :: Integer -> S a -> [a] sdrop :: Int -> S a -> S a ssplitAt :: Int -> S a -> ([a], S a) smerge :: S a -> S a -> S a -- ** Zips and Unzips sunzip :: S (a, b) -> (S a, S b) szipWith :: (a -> b -> c) -> S a -> S b -> S c transpose :: S (S a) -> S (S a) -- ** Utility Functions fromJust :: Maybe a -> a fromOEIS :: String -> [Integer] instance Functor S where fmap f ~(Cons h t) = f h <|| fmap f t instance Monad S where return = srepeat xs >>= f = join (fmap f xs) where join ~(Cons zs xss) = Cons (shead zs) (join (smap stail xss)) instance Arbitrary a => Arbitrary (S a) where arbitrary = liftM2 (<||) arbitrary arbitrary instance CoArbitrary a => CoArbitrary (S a) where coarbitrary xs gen = do n <- arbitrary coarbitrary (stake (abs n) xs) gen instance Serial a => Serial (S a) where series = cons2 Cons instance Idiom S where pure a = s where s = a <|| s s <> t = shead s (shead t) <|| stail s <> stail t srepeat a = s where s = a <|| s smap f s = f (shead s) <|| smap f (stail s) zip g s t = g (shead s) (shead t) <|| zip g (stail s) (stail t) instance (Num a) => Num (S a) where (+) = zip (+) (-) = zip (-) (*) = zip (*) negate = sMap negate abs = sMap abs signum = sMap signum fromInteger = srepeat . fromInteger instance (Enum a) => Enum (S a) where toEnum i = srepeat (toEnum i) fromEnum = error "fromEnum: not defined for streams" instance (Real a) => Real (S a) where toRational = error "toRational: not defined for streams" instance (Integral a) => Integral (S a) where div = zip div mod = zip mod quotRem s t = sunzip (zip quotRem s t) toInteger = error "toInteger: currently not defined for streams" instance (Fractional a) => Fractional (S a) where s / t = zip (Prelude./) s t recip = smap recip fromRational r = srepeat (fromRational r) -- | unzip, specialized to Stream tuples sunzip s = (a <|| as , b <|| bs) where (a , b ) = shead s ( as , bs) = sunzip (stail s) -- | 'filter' @p@ @xs@, removes any elements from @xs@ that do not satisfy @p@. -- -- /Beware/: this function may diverge if there is no element of -- @xs@ that satisfies @p@, e.g. @filter odd (repeat 0)@ will loop. sfilter p ~(Cons x xs) | p x = Cons x (sfilter p xs) | otherwise = sfilter p xs -- | 'takeWhile' @p@ @xs@ returns the longest prefix of the stream -- @xs@ for which the predicate @p@ holds. stakeWhile p (Cons x xs) | p x = x : stakeWhile p xs | otherwise = [] -- | 'dropWhile' @p@ @xs@ returns the suffix remaining after -- 'takeWhile' @p@ @xs@. -- -- /Beware/: this function may diverge if every element of @xs@ -- satisfies @p@, e.g. @dropWhile even (repeat 0)@ will loop. sdropWhile p ~(Cons x xs) | p x = sdropWhile p xs | otherwise = Cons x xs -- | 'sspan' @p@ @xs@ returns the longest prefix of @xs@ that satisfies -- @p@, together with the remainder of the stream. sspan p (Cons x xs) | p x = let (trues, falses) = sspan p xs in (x : trues, falses) | otherwise = ([], Cons x xs) -- | The 'break' @p@ function is equivalent to 'span' @not . p@. sbreak p = sspan (not . p) -- | The 'splitAt' function takes an integer @n@ and a stream @xs@ -- and returns a pair consisting of the prefix of @xs@ of length -- @n@ and the remaining stream immediately following this prefix. -- -- /Beware/: passing a negative integer as the first argument will -- cause an error. ssplitAt n xs | n == 0 = ([],xs) | n > 0 = let (prefix,rest) = ssplitAt (n-1) (stail xs) in (shead xs : prefix, rest) | otherwise = error "S.splitAt negative argument." -- | The 'partition' function takes a predicate @p@ and a stream -- @xs@, and returns a pair of streams. The first stream corresponds -- to the elements of @xs@ for which @p@ holds; the second stream -- corresponds to the elements of @xs@ for which @p@ does not hold. -- -- /Beware/: One of the elements of the tuple may be undefined. For -- example, @fst (partition even (repeat 0)) == repeat 0@; on the -- other hand @snd (partition even (repeat 0))@ is undefined. spartition p ~(Cons x xs) = let (trues,falses) = spartition p xs in if p x then (Cons x trues, falses) else (trues, Cons x falses) -- | The 'group' function takes a stream and returns a stream of -- lists such that flattening the resulting stream is equal to the -- argument. Moreover, each sublist in the resulting stream -- contains only equal elements. For example, -- group ~(Cons x ys) = let (xs, zs) = sspan (\y -> x == y) ys in (x : xs) <|| group zs -- | 'drop' @n@ @xs@ drops the first @n@ elements off the front of -- the sequence @xs@. -- -- /Beware/: passing a negative integer as the first argument will -- cause an error. sdrop n xs | n == 0 = xs | n > 0 = sdrop (n - 1) (stail xs) | otherwise = error "Stream.drop: negative argument." inits xs = Cons [] (fmap (shead xs :) (inits (stail xs))) -- | The 'stails' function takes a stream @xs@ and returns all the -- suffixes of @xs@. tails xs = Cons xs (tails (stail xs)) (<||) = Cons shead (Cons h _) = h stail (Cons _ t) = t anyA = anyA stake 0 ___________ = [] stake (n) (Cons x xs) = x : stake (n-1) xs ago 0 (Cons x __) = x ago (n) (Cons _ xs) = (n-1) `ago` xs -- | merge, version 2 [Hinze UFP p.35] -- | map, version 1 -- | map, version 2 -- | map2, really zip? s |~| t = shead s <|| t |~| stail s s |!| t = s `union` t map1 f s = f (shead s) <|| map1 f (stail s) sMap f (Cons x xs) = f x <|| sMap f xs merge s@(Cons m s') t@(Cons n t') = if m <= n then m <|| merge s' t else n <|| merge s t' -- ^ from Unique Fixed Point p.35 -- | union for streams union s@(Cons m s') t@(Cons n t') = case compare m n of LT -> m <|| union s' t EQ -> m <|| union s' t' GT -> n <|| union s t' --zip f s t = f (shead s) (shead t) <|| zip f (stail s) (stail t) sMap2 f (Cons x xs) (Cons y ys) = f x y <|| sMap2 f xs ys sMap3 f (Cons x xs) (Cons y ys) (Cons z zs) = f x y z <|| sMap3 f xs ys zs sMap4 f (Cons t ts) (Cons x xs) (Cons y ys) (Cons z zs) = f t x y z <|| sMap4 f ts xs ys zs smerge (Cons m s) t = m <|| smerge t s plus (Cons m s) (Cons n t) = m + n <|| plus s t alternate (Cons m s) (Cons _ t) = m <|| alternate t s interleave (Cons m s) (Cons n t) = m <|| interleave (n <|| t) s interleave' (Cons m s) t = m <|| interleave' t s szipWith f ~(Cons x xs) ~(Cons y ys) = Cons (f x y) (szipWith f xs ys) -- | Interleave two Streams @xs@ and @ys@, alternating elements -- from each list. -- -- > [x1,x2,...] `interleave` [y1,y2,...] == [x1,y1,x2,y2,...] interleave3 ~(Cons x xs) ys = Cons x (interleave3 ys xs) -- | 'intersperse' @y@ @xs@ creates an alternating stream of -- elements from @xs@ and @y@. intersperse y ~(Cons x xs) = Cons x (Cons y (intersperse y xs)) -- | infix prepend [ ] <<| s = s (a:as) <<| s = a <|| (as <<| s) -- | turn something turn n | n == 0 = [] turn n | n > 0 = turn (n-1) ++ [n-1] ++ turn (n-1) turn n | n < 0 = error "turn: negative argument" turn _ = error "blah!" -- | 'cycle' @xs@ returns the infinite repetition of @xs@: -- -- > cycle [1,2,3] = Cons 1 (Cons 2 (Cons 3 (Cons 1 (Cons 2 ... scycle xs = foldr Cons (scycle xs) xs -- | Arithmatic, Jumping, ... -- -- -- -- | multiplication -- | stream inversion -- | finite (forward) difference -- | duplicate the head of the stream -- | even (indexed) elements -- | odd (indexed) elements -- | even (indexed) elements, v2 -- | odd (indexed) elements, v2 -- | drop function, results in (4*n - 1) -- | drop function, results in (2*n) -- | an alternative tail function -- | a kind of sum function -- | right inverse of diff -- Finite (or forward) difference diff (Cons m s@(Cons n _)) = n - m <|| diff s -- ^ from Hinze UFP p.45 tail2 (Cons _ (Cons n s)) = n <|| s -- ^ from Hinze UFP p.49 times n (Cons m s) = n * m <|| times n s asum s = 0 <|| s + asum s -- ^ from Hinze UFP p.4 bsum s = t where t = 0 <|| t + s csum s = 0 <|| srepeat (shead s) + csum (stail s) -- | iterate (inductively) over a stream -- -- this can't be stopped? siterate f a = a <|| siterate f (f a) -- from Hinze UFP p.4 -- as patterns are co-pointed functors: -- data C x = As x (B x) dup s@(Cons m _) = m <|| s -- ^ from Hinze UFP p.39 inv (Cons m s) = (1 - m) <|| inv s -- ^ from Hinze UFP p.41 -- | 2D operator? -- seven (Cons m (Cons _ s)) = m <|| seven s -- ^ from Hinze UFP p.45 sodd (Cons _ (Cons n s)) = n <|| sodd s -- ^ from Hinze UFP p.45 -- | mutually recursive sEven s = shead s <|| sOdd (stail s) -- ^ from Hinze UFP p.45 sOdd s = sEven (stail s) -- ^ from Hinze UFP p.45 -- from Hinze UFP p.45 -- --seven <=> drop1of2 --sodd <=> drop0of2 -- | -- -- > scan f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...] scan f z ~(Cons x xs) = z <|| scan f (f z x) xs -- | @scan'@ is a strict scan. -- scan' f z xs = z <|| (scan' f $! f z (shead xs)) (stail xs) -- | 'scan1' is a variant of 'scan' that has no starting value argument: -- -- > scan1 f [x1, x2, ...] == [x1, x1 `f` x2, ...] scan1 f ~(Cons x xs) = scan f x xs -- | @scan1'@ is a strict scan that has no starting value. scan1' f ~(Cons x xs) = scan' f x xs -- | 'transpose' computes the transposition of a stream of streams. transpose ~(Cons (Cons x xs) yss) = (x <|| smap shead yss) <|| transpose (xs <|| smap stail yss) -- from Hinze UFP p.45 {-- shead (dropIIofL s) = shead s stail (dropIIofL s) = dropIofL (stail s) shead (drop0ofL s) = shead (stail s) stail (drop0ofL s) = dropLppofL (stail (stail s)) -} dropIp1L (Cons m s ) = m <|| dropIp1L (stail s ) -- ^ from Hinze UFP p.45 drop0L (Cons _ (Cons n s)) = n <|| drop0L (stail (stail s)) -- ^ from Hinze UFP p.45 -- | standard fix-point function -- | standard fix-point function, specialized to Streams (forward ordering) -- | standard fix-point function, specialized to Streams (reverse ordering) -- | transform a generator to a Stream operator -- | transform a generator to a Stream operator - v2? -- | transform a Stream operator to a generator -- | transform a generator, along with a reversed list, into a Stream operator ---------------------------------------------------------------------------------------------------- fix f = let x = f x in x fwdFix g = fix (grow g) revFix g = fix (rgen g) grow g ~(Cons x xs) = g [] <|| grow (g . (x:)) xs rgen' g ys ~(Cons x xs) = g ys <|| rgen' g (x:ys) xs rgen g = rgen' g [] rep f [] = shead (f anyA) rep f (x:xs) = rep (stail . f . (x <||) ) xs ------------------------------------------------------------ -- -- -- from dons's --onesv = 0 <|| onesv + 1 - carry -- -- Tree Representation -- -- | smart constructor for Tree labels -- | smart constructor for Tree branches -- | translate a Tree to a Generator -- | translate a Generator to a Tree -- | translate a Tree element to a Stream element -- | translate a Generator element to a Stream element -- | fromFG helper function (head) -- | fromFG helper function (tail) -- | fromRG: translate a Generator (and a reversed list) to a Stream element -- | fromRG helper function (head) -- | fromRG helper function (tail) -- | unfold operator, specialized to Co-Algebras -- | standard fix-point function, specialized to Co-Algebras -- | generate a Stream operator, given a Co-Algebra unfold (h,t) z = Node (h z) (\x -> unfold (h,t) (t z x) ) cfix (h,t) z = fix (groW (h,t) z ) groW (h,t) z ~(Cons x xs) = h z <|| groW (h,t) (t z x) xs --generate (h,t) z = gen (gen' (unfold (h,t) z )) label (Node y _) = y branches (Node _ f) = f toG (Node y _) [] = y toG (Node _ f) (x:xs) = toG (f x) xs toT g = Node (g []) (\x -> toT (g . (x:))) fromT = cfix (label , branches) fromFG = cfix (hOfFG , tOfFG) fromRG = cfix (hOfRG , tOfRG) hOfFG g = g [] tOfFG g x = g . (x:) hOfRG (g,xs) = g xs tOfRG (g, xs) x = (g, x:xs) -- | utility function to lookup sequence in OEIS -- | utility function to check of all elements of a list are equal -- | utility function to unwrap a (known good) Maybe -- | utility function to map over adjacent elements in a list combStreams = foldr (zipWith (:)) (Prelude.repeat []) allEqual = and . mapAdjacent (==) mapAdjacent f xs = zipWith f xs (tail xs) fromOEIS str = fromJust $ getSequenceByID str fromJust Nothing = error "My programmer promised he knew what he was doing! He's a liar!" fromJust (Just x) = x -- | Power Series "Glasses" -- sconst n = n <|| srepeat 0 z0 = 0 <|| 1 <|| srepeat 0 -- | Horner's Rule on Streams -- -- s = sconst (shead t) + (z |*| stail s) -- -- implies -- -- z |*| s = 0 <|| s -- {- infixl 7 |*| infixl 7 |%| (|*|) s t = shead s * shead t <|| srepeat (shead s) * (stail t) + (stail s) |*| t srecip s = t where a = srecip (shead s) t = a <|| srepeat (- a) * (stail s |*| t) (|%|) s t = s |*| srecip t power s n | n >= 0 = pow s n | otherwise = srecip (pow s (- n)) where pow _t 0 = sconst 1 pow t (k) = t ** pow t (k - 1) pow _ _ = error "power: impossible" -- diverges -- -} main :: IO () main = putStrLn "mine.hs"