packages feed

ralist-0.4.0.0: src/Data/RAList.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable,DeriveAnyClass,DerivingVia #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ExplicitForAll, RankNTypes #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PatternSynonyms,ViewPatterns #-}
{-# LANGUAGE ScopedTypeVariables, BangPatterns #-}
{-# LANGUAGE DeriveFoldable , DeriveTraversable,DeriveGeneric#-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses#-}
{-# LANGUAGE MonadComprehensions,RoleAnnotations #-}
{-# LANGUAGE Trustworthy#-}

-- |
-- A random-access list implementation based on Chris Okasaki's approach
-- on his book \"Purely Functional Data Structures\", Cambridge University
-- Press, 1998, chapter 9.3.
--
-- 'RAList' is a replacement for ordinary finite lists.
-- 'RAList' provides the same complexity as ordinary for most the list operations.
-- Some operations take /O(log n)/ for 'RAList' where the list operation is /O(n)/,
-- notably indexing, '(!!)'.
--
module Data.RAList
   (
     RAList(Nil,Cons,(:|))

   -- * Basic functions
   --, empty
   , cons
   , uncons
--   , singleton
   , (++)
   , head
   , last
   , tail
   , init
   , null
   , length

   -- * Indexing lists
   -- | These functions treat a list @xs@ as a indexed collection,
   -- with indices ranging from 0 to @'length' xs - 1@.

   , (!!)
   ,lookupWithDefault
   ,lookupM
   ,lookup
   ,lookupCC

   --- * KV indexing
   --- | This function treats a RAList as an association list
   ,lookupL


   -- * List transformations
   , map
   , reverse
{-RA
   , intersperse
   , intercalate
   , transpose

   , subsequences
   , permutations
-}

  -- * indexed operations
  ,imap
  ,itraverse
  ,ifoldMap
  ,ifoldl'
  ,ifoldr



   -- * Reducing lists (folds)

   , foldl
   , foldl'
   , foldl1
   , foldl1'
   , foldr
   , foldr1


   -- ** Special folds

   , concat
   , concatMap
   , and
   , or
   , any
   , all
   , sum
   , product
   , maximum
   , minimum

   -- * Building lists
{-RA
   -- ** Scans
   , scanl
   , scanl1
   , scanr
   , scanr1

   -- ** Accumulating maps
   , mapAccumL
   , mapAccumR
-}
   -- ** Repetition
   , replicate


   -- ** Unfolding
   , unfoldr


   -- * Sublists

   -- ** Extracting sublists
   , take
   , drop
   , simpleDrop
   , splitAt
{-RA

   , takeWhile
   , dropWhile
   , dropWhileEnd
   , span
   , break

   , stripPrefix

   , group

   , inits
   , tails

   -- ** Predicates
   , isPrefixOf
   , isSuffixOf
   , isInfixOf
-}
   -- * Searching lists

   -- ** Searching by equality
   , elem
   , notElem

{-RA
   -- ** Searching with a predicate
   , find
-}
   , filter
   , partition
   , mapMaybe
   , catMaybes
   , wither

{-RA
   , elemIndex
   , elemIndices

   , findIndex
   , findIndices
-}
   -- * Zipping and unzipping lists

   , zip
{-RA
   , zip3
   , zip4, zip5, zip6, zip7
-}
   , zipWith
{-RA
   , zipWith3
   , zipWith4, zipWith5, zipWith6, zipWith7
-}
   , unzip
{-RA
   , unzip3
   , unzip4, unzip5, unzip6, unzip7

   -- * Special lists

   -- ** Functions on strings
   , lines
   , words
   , unlines
   , unwords

   -- ** \"Set\" operations

   , nub

   , delete
   , (\\)

   , union
   , intersect

   -- ** Ordered lists
   , sort
   , insert

   -- * Generalized functions

   -- ** The \"@By@\" operations

   -- *** User-supplied equality (replacing an @Eq@ context)
   -- | The predicate is assumed to define an equivalence.
   , nubBy
   , deleteBy
   , deleteFirstsBy
   , unionBy
   , intersectBy
   , groupBy

   -- *** User-supplied comparison (replacing an @Ord@ context)
   -- | The function is assumed to define a total ordering.
   , sortBy
   , insertBy
   , maximumBy
   , minimumBy
-}
   -- ** The \"@generic@\" operations
   -- | The prefix \`@generic@\' indicates an overloaded function that
   -- is a generalized version of a "Prelude" function.

   , genericLength
   , genericTake
   , genericDrop
   , genericSplitAt
   , genericIndex
   , genericReplicate

   -- * Update
   , update
   , adjust
   -- * List conversion
   , toList
   , fromList
   -- * List style fusion tools
   , build
   , augment

   , wLength
   ) where
import qualified Prelude
import Prelude hiding(
    (++), head, last, tail, init, null, length, map, reverse,
    foldl, foldl1, foldr, foldr1, concat, concatMap,
    and, or, any, all, sum, product, maximum, minimum, take,
    drop, elem, splitAt, notElem, lookup, replicate, (!!), filter,
    zip, zipWith, unzip
    )
import qualified Data.List as List

-- this should be a cabal flag for debugging data structure bugs :)
#define DEBUG 0

#if MIN_VERSION_base(4,11,0)
#else
import Data.Semigroup(Semigroup,(<>))
#endif
import Data.Data(Data,Typeable)
--import Data.Functor.Identity(runIdentity)
import Data.Word

import  Data.Foldable as F hiding (concat, concatMap)
import qualified Control.Monad.Fail as MF

import Control.Monad.Zip
import Numeric.Natural

--import GHC.Exts (oneShot)

import qualified GHC.Exts as GE (IsList(..))

import Data.Foldable.WithIndex
import Data.Functor.WithIndex
import Data.Traversable.WithIndex

import Data.RAList.Internal
import Control.Applicative(Applicative(liftA2))

import GHC.Generics(Generic,Generic1)


import Control.DeepSeq

infixl 9  !!
infixr 5  `cons`, ++
infixr 5 `Cons`
infixr 5 :|

-- | our '[]' by another name
pattern Nil :: forall a. RAList a
pattern Nil = RNil

-- | Constructor notation ':'
pattern Cons :: forall a. a -> RAList a -> RAList a
pattern Cons x xs <-( uncons -> Just(x,xs) )
 where Cons x xs = cons x xs
{-# COMPLETE Nil,Cons #-}


-- | like ':' but for RAList
pattern (:|) :: forall a. a -> RAList a -> RAList a
pattern x :| xs = Cons x xs
{-# COMPLETE (:|), Nil #-}



-- A RAList is stored as a list of trees.  Each tree is a full binary tree.
-- The sizes of the trees are monotonically increasing, except that the two
-- first trees may have the same size.
-- The first few tree sizes:
-- [ [], [1], [1,1], [3], [1,3], [1,1,3], [3,3], [7], [1,7], [1,1,7],
--   [3,7], [1,3,7], [1,1,3,7], [3,3,7], [7,7], [15], ...
-- (I.e., skew binary numbers.)


type role RAList representational

-- Special list type for (Word64, Tree a), i.e., Top a ~= [(Word64, Tree a)]
data RAList a = RNil
                | RCons {-# UNPACK #-}  !Word64 -- total number of elements, aka sum of subtrees
                        {-# UNPACK #-}  !Word64 --  size of this subtree
                                        (Tree a)
                                        (RAList a)
    deriving (Eq
              ,Data
              ,Typeable
              ,Functor
              ,Traversable
#if DEBUG
              , Show
#endif
              , Generic
              , Generic1
              ,NFData
              ,NFData1
              )


#if !DEBUG
instance (Show a) => Show (RAList a) where
    showsPrec p xs = showParen (p >= 10) $ showString "fromList " . showsPrec 10 (toList xs)
#endif

--instance (Read a) => Read (RAList a) where
--    readsPrec p = readParen (p > 10) $ \ r -> [(fromList xs, t) | ("fromList", s) <- lex r, (xs, t) <- reads s]

instance (Ord a) => Ord (RAList a) where
  --- this is kinda naive, but simple for now
    xs <  ys        = toList xs <  toList ys
    xs <= ys        = toList xs <= toList ys
    xs >  ys        = toList xs >  toList ys
    xs >= ys        = toList xs >= toList ys
    xs `compare` ys = toList xs `compare` toList ys

instance Monoid (RAList a) where
    mempty  = Nil


instance Semigroup (RAList a) where
    {-# INLINE (<>) #-}
    (<>) = (++)

--instance Functor RAList where
--    fmap f (RAList s skewlist) = RAList s (fmap f skewlist)

--- lets just use  MonadComprehensions to write out the applictives
instance Applicative RAList where
    {-# INLINE pure #-}
    pure = \x -> Cons x Nil
    {-# INLINE (<*>) #-}
    fs <*> xs = [f x | f <- fs, x <- xs]
    {-# INLINE liftA2 #-}
    liftA2 f xs ys = [f x y | x <- xs, y <- ys]
    {-# INLINE (*>) #-}
    xs *> ys  = [y | _ <- xs, y <- ys]


instance Monad RAList where
    return = pure
    (>>=) = flip concatMap

instance GE.IsList (RAList a) where
  type Item (RAList a) = a
  toList = toList
  fromList = fromList

instance MonadZip RAList where
  mzipWith = zipWith
  munzip = unzip

{-# INLINE unzip #-}
-- adapted from List definition in base
unzip :: RAList (a,b) -> (RAList a,RAList b)
unzip    =  foldr' (\(a,b) (!as,!bs) -> (a:| as,b:|bs)) (Nil,Nil)
--unzip    =  foldr (\(a,b) ~(as,bs) -> (a:| as,b:|bs)) (Nil,Nil)

--instance Traversable RAList where
    --{-# INLINE traverse #-} -- so that traverse can fuse
    -- deriving might be nice too, need to compare later
    --traverse f = foldr cons_f (pure Nil)
      --where cons_f x ys = liftA2 (cons) (f x) ys


instance   TraversableWithIndex Word64 RAList where
  {-# INLINE itraverse #-}
  itraverse = \ f s -> snd $ runIndexing (traverse (\a -> Indexing (\i -> i `seq` (i + 1, f i a))) s) 0
instance   FoldableWithIndex Word64 RAList where
instance   FunctorWithIndex Word64 RAList where

-- TODO: look into ways to make the toList more efficient if needed

instance Foldable RAList  where
  {-# INLINE null#-}
  null = \ x -> case x of Nil -> True ; _ -> False
  {-# INLINE length #-}
  length = genericLength -- :)



    -- This INLINE allows more list functions to fuse. See #9848.
  --{-# INLINE foldMap #-}
  --foldMap f = foldr (mappend . f) mempty
  --foldMap _f RNil = mempty
  --foldMap f (RCons _stot _stre tree rest) = foldMap f tree <> foldMap f rest

  foldMap = \(f:: a -> m) (ra:: RAList a ) ->
    let
        go :: RAList a -> m
        go  ral =    case ral of   RNil -> mempty
                                   (RCons _stot _stre tree rest) -> foldMap f tree <> go  rest
       in go ra

   --not sure if providing my own foldr is a good idea, but lets try for now : )
  --{-# INLINE [0] foldr #-}
  {-
  foldr f z = go
          where
            go Nil     = z
            go (Cons y ys) = y `f` go ys
  -- {-# INLINE toList #-}
  toList = foldr (:) []
  -}

  --{-# INLINE foldl' #-}
{-
 foldl' k z0 xs =
      foldr (\(v::a) (fn::b->b) -> oneShot (\(z::b) -> z `seq` fn (k z v))) (id :: b -> b) xs z0
      -}


--instance Functor Top where
--    fmap _ Nil = Nil
--    fmap f (Cons w t xs) = Cons w (fmap f t) (fmap f xs)

-- Complete binary tree.  The completeness of the trees is an invariant that must
-- be preserved for the implementation to work.

{-# specialize genericLength :: RAList a -> Word64  #-}
{-# specialize genericLength :: RAList a -> Integer  #-}
{-# specialize genericLength :: RAList a -> Int  #-}
{-# specialize genericLength :: RAList a -> Word  #-}
genericLength :: Integral w =>RAList a -> w
genericLength = \ra -> case ra of RNil ->  0 ; (RCons tot _trtot _tree _rest) -> fromIntegral tot

wLength :: RAList a -> Word64
wLength = genericLength

type role Tree representational
data Tree a
     = Leaf a
     | Node a (Tree a) (Tree a)
     deriving
        (Eq
        ,Data
        ,Typeable
        ,Functor
        ,NFData
        ,NFData1
        ,Generic
        ,Generic1
        ,Traversable
#if DEBUG
        , Show
#endif
         )

instance Foldable Tree  where
  -- Tree is a PREORDER sequence layout
  foldMap f (Leaf a) = f a
  foldMap f (Node a l r) =  f a <> foldMap f l <>  foldMap f r


--instance Functor Tree where
--     fmap f (Leaf x)     = Leaf (f x)
--     fmap f (Node x l r) = Node (f x) (fmap f l) (fmap f r)

-- todo audit inline pragmas for `cons`
-- also, i think we can say that cons is whnf strict in its second argument, lazy in the first?
{-# INLINE CONLIKE [0]   cons #-}
-- | Complexity /O(1)/.
cons :: a -> RAList a -> RAList a
cons = \ x ls -> case ls of
    (RCons tots1 tsz1 t1
       (RCons _tots2 tsz2 t2 rest))
              | tsz2 == tsz1
          -> RCons (tots1 + 1) (tsz1 * 2 + 1 ) (Node x t1 t2 ) rest
    rlist -> RCons (1 + wLength rlist ) 1 (Leaf x) rlist
{-
cons x (RCons tots1 tsz1 t1
              (RCons _tots2 tsz2 t2 rest))
           | tsz2 == tsz1 = RCons (tots1 + 1) (tsz1 * 2 + 1 ) (Node x t1 t2 ) rest
cons x rlist  = RCons (1 + wLength rlist ) 1 (Leaf x) rlist
-}

--(++) :: RAList a -> RAList a -> RAList a
--xs  ++ Nil = xs
--Nil ++ ys = ys
--xs  ++ ys = foldr cons ys xs

(++) :: RAList a -> RAList a-> RAList a
--{-# NOINLINE  (++) #-}    -- We want the RULE to fire first.
                             -- It's recursive, so won't inline anyway,
                             -- but saying so is more explicit
(++) Nil     ys = ys
(++) xs    Nil = xs
(++) (Cons x xs) ys = Cons x ( xs ++ ys)

-- {-# RULES
-- "RALIST/++"    [~1] forall xs ys. xs ++ ys = augment (\c n -> foldr c n xs) ys
--  #-}


{-

      (++) :: [a] -> [a] -> [a]
      {-# NOINLINE [1] (++) #-}    -- We want the RULE to fire first.
                                   -- It's recursive, so won't inline anyway,
                                   -- but saying so is more explicit
      (++) []     ys = ys
      (++) (x:xs) ys = x : xs ++ ys

      {-# RULES
      "++"    [~1] forall xs ys. xs ++ ys = augment (\c n -> foldr c n xs) ys
        #-}


-}

uncons :: RAList a -> Maybe (a, RAList a)
uncons (RNil) =  Nothing
uncons (RCons _tot _treetot  (Leaf h)     wts) =  Just (h,wts)
uncons (RCons _tot w (Node x l r) wts) = Just (x, (RCons (restsize + w2 + w2) w2 l (RCons (restsize + w2) w2 r wts)))
      where
        w2 = w `quot` 2
        restsize = wLength wts

-- | Complexity /O(1)/.
head :: RAList a -> Maybe a
head = fmap fst  . uncons

-- | Complexity /O(log n)/.
last :: RAList a -> a
last xs= xs !! (genericLength xs - 1)

half :: Word64 -> Word64
half = \ n ->  n `quot` 2

-- | Complexity /O(log n)/.
(!!) :: RAList a -> Word64 -> a
r !! n | n <  0 = error "Data.RAList.!!: negative index"
                    | n >= genericLength r  = error "Data.RAList.!!: index too large"
                    | otherwise = lookupCC  r n  id error


lookupCC :: forall a r.  RAList a ->  Word64 -> (a -> r) -> (String -> r) -> r
lookupCC  =  \  ralist  index  retval retfail ->
    let
                look RNil _ = retfail "RAList.lookup bad subscript, something is corrupted"
                look (RCons _tots tsz t xs) ix
                    | ix < tsz     = lookTree tsz  ix t
                    | otherwise = look xs (ix - tsz)

                lookTree _  ix (Leaf x)
                    | ix == 0    = retval x
                    | otherwise = retfail "RAList.lookup: not found. somehow we reached a leaf but our index doesnt match, this is bad"
                lookTree jsz ix (Node x l r)
                    | ix > (half jsz)  = lookTree (half jsz) (ix - 1 - (half jsz)) r
                    | ix /= 0        = lookTree (half jsz) (ix - 1) l -- ix between zero and floor of size/2
                    | otherwise     = retval x  -- when ix is zero
      in
        if  index >= (genericLength  ralist)
           then  retfail $   "provide index larger than Ralist max valid coord " <> (show index) <> " " <> (show (length ralist))
           else look ralist index


lookup :: forall a. RAList a ->  Word64 -> Maybe a
lookup  = \ xs i ->    lookupCC xs i Just (const Nothing)


{-# SPECIALIZE genericIndex :: RAList a -> Integer -> a #-}
{-# SPECIALIZE genericIndex :: RAList a -> Word -> a #-}
{-# SPECIALIZE genericIndex :: RAList a -> Word64 -> a #-}
{-# SPECIALIZE genericIndex :: RAList a -> Int -> a #-}
{-# SPECIALIZE genericIndex :: RAList a -> Natural -> a #-}
genericIndex :: Integral n => RAList a -> n -> a
genericIndex ls ix | word64Representable ix =  ls !! (fromIntegral ix)
                   | otherwise = error "argument index for Data.RAList.genericIndex not representable in Word64"


{-# SPECIALIZE lookupM :: forall a . RAList a ->  Word64 -> Maybe a  #-}
{-# SPECIALIZE lookupM :: forall a . RAList a ->  Word64 ->  IO a  #-}
lookupM :: forall a m. MF.MonadFail m => RAList a ->   Word64 -> m  a
lookupM = \ ix lst  -> lookupCC ix lst return fail

lookupWithDefault :: forall t. t -> Word64 ->  RAList t ->   t
lookupWithDefault = \  d tree ix  -> lookupCC  ix tree id (const d)


-- | Complexity /O(1)/.
tail :: RAList a -> Maybe (RAList a)
tail = fmap snd . uncons
-- XXX Is there some clever way to do this?
init :: RAList a -> RAList a
init = fromList . Prelude.init . toList


-- -- | Complexity /O(1)/.
--length :: RAList a -> Word64
--length (RCons s  _treesize _tree  _rest) = s
--length RNil = 0

map :: (a->b) -> RAList a -> RAList b
map = fmap


--- adapted from ghc base
-- | 'reverse' @xs@ returns the elements of @xs@ in reverse order.
-- @xs@ must be finite.
reverse                 :: RAList a -> RAList a
#if defined(USE_REPORT_PRELUDE)
reverse                 =  foldl (flip  cons) Nil
#else
reverse l =  rev l Nil
  where
    rev Nil    a = a
    rev (Cons x xs) a = rev xs (Cons x a)
#endif



foldl1' :: (a -> a -> a) -> RAList a -> a
foldl1' f xs | null xs = errorEmptyList "foldl1'"
             | otherwise = List.foldl1' f (toList xs)

---- XXX This could be deforested.
--foldr :: (a -> b -> b) -> b -> RAList a -> b
--foldr f z xs = Prelude.foldr f z (toList xs)

--foldr1 :: (a -> a -> a) -> RAList a -> a
--foldr1 f xs | null xs = errorEmptyList "foldr1"
--            | otherwise = Prelude.foldr1 f (toList xs)

concat :: RAList (RAList a) -> RAList a
concat = foldr (<>) Nil
{-# INLINE  concat #-}
-- {-# NOINLINE [1] concat #-}

-- {-# RULES
-- "concat" forall xs. concat xs =
--     build (\c n -> foldr (\x y -> foldr c y x) n xs)
-- -- We don't bother to turn non-fusible applications of concat back into concat
-- #-}



concatMap :: (a -> RAList b) -> RAList a -> RAList b
--concatMap f = concat . fmap f
-- TODO: should this and others be foldr' ?
concatMap f             =  foldr ((++) . f) Nil
{-# INLINE concatMap #-}
--{-# NOINLINE [1] concatMap #-}

--{-# RULES
--"concatMap" forall f xs . concatMap f xs =
--    build (\c n -> foldr (\x b -> foldr c b (f x)) n xs)
 --  #-}

--and :: RAList Bool -> Bool
--and = foldr (&&) True

--or :: RAList Bool -> Bool
--or = foldr (||) False

--any :: (a -> Bool) -> RAList a -> Bool
--any p = or . map p

--all :: (a -> Bool) -> RAList a -> Bool
--all p = and . map p

--sum :: (Num a) => RAList a -> a
--sum = foldl (+) 0

--product :: (Num a) => RAList a -> a
--product = foldl (*) 1

--maximum :: (Ord a) => RAList a -> a
--maximum xs | null xs   = errorEmptyList "maximum"
--           | otherwise = foldl1 max xs

--minimum :: (Ord a) => RAList a -> a
--minimum xs | null xs   = errorEmptyList "minimum"
--           | otherwise = foldl1 min xs

replicate :: Word64 -> a -> RAList a
replicate n v = fromList $ Prelude.replicate (fromIntegral n)  v

{-# SPECIALIZE genericReplicate ::  Int  -> a -> RAList a #-}
{-# SPECIALIZE genericReplicate ::  Word -> a -> RAList a #-}
{-# SPECIALIZE genericReplicate ::  Word64 -> a -> RAList a #-}
{-# SPECIALIZE genericReplicate ::  Integer-> a -> RAList a #-}
{-# SPECIALIZE genericReplicate ::  Natural -> a -> RAList a #-}
genericReplicate :: Integral n => n -> a -> RAList a
genericReplicate siz val
  |  word64Representable siz    = replicate (fromIntegral siz) val
  |  siz < 0 = error "negative replicate size arg in Data.RAList.genericReplicate"
  | otherwise = error "too large integral arg to Data.Ralist.genericReplicate"

-- when converting from  a non Word64 integral type to Word64, we want to make sure either
-- that the source integral type is representable / embedded within word64
-- OR that if its a type which can represent a Word64 value exactly, the value does
-- not exceed the size of the largest positive Word64 value. At least with Replicate :)
word64Representable :: Integral a => a -> Bool
word64Representable siz = fromIntegral siz <= (maxBound :: Word64)   || siz <= fromIntegral (maxBound :: Word64)

-- unlike drop, i dont think we can do better than the list take in complexity
take :: Word64 -> RAList a -> RAList a
take n ls | n <  (maxBound :: Word64) = fromList $  Prelude.take (fromIntegral n) $ toList ls
          | otherwise = ls

genericTake :: Integral n => n -> RAList a -> RAList a
genericTake siz ls |  siz <= 0 =  Nil
                   | word64Representable siz =  take (fromIntegral siz) ls
                   | otherwise = error "too large integral arg for Data.RAList.genericTake"

-- | @`drop` i l@ where l has length n has worst case complexity  Complexity /O(log n)/, Average case
-- complexity should be /O(min(log i, log n))/.
drop :: Word64 -> RAList a -> RAList a
drop n rlist   | n <= 0 = rlist
drop n rlist  | n >=( genericLength rlist) = Nil
drop n rlist  = (loop n rlist)
  where loop 0 xs = xs
        loop m (RCons _tot treesize _ xs) | treesize <= m = loop (m-treesize) xs -- drops full trees
        loop m (RCons _tot treesize  tre xs) = splitTree m treesize tre xs -- splits tree
        loop _ _ = error "Data.RAList.drop: impossible"


genericDrop :: Integral n => n -> RAList a -> RAList a
genericDrop siz ls | siz <= 0 = ls
                   | word64Representable siz = drop (fromIntegral siz) ls
                   | otherwise = Nil -- because a list with more than putatively 2**64 elements :)

-- helper function for drop
-- drops the first n elements of the tree and adds them to the front
splitTree :: Word64 -> Word64 -> Tree a -> RAList a -> RAList a
splitTree n treeSize tree@(Node _ l r) xs =
    case (compare n  1, n <= half treeSize) of
      (LT {- n==0 -}, _ )  -> RCons (suffixSize + treeSize)  treeSize tree xs
      (EQ {- n==1 -}, _ )  -> RCons (suffixSize + 2* halfTreeSize) halfTreeSize l
                                (RCons (suffixSize + halfTreeSize) halfTreeSize r xs)
      (_, True )           -> splitTree (n-1) halfTreeSize l (RCons (suffixSize + halfTreeSize) halfTreeSize r xs)
      (_, False)           -> splitTree (n-halfTreeSize-1) halfTreeSize r xs
    where suffixSize = genericLength xs
          halfTreeSize = half treeSize
splitTree n treeSize nd@(Leaf _) xs =
  case compare n 1 of
    EQ {-1-} -> xs
    LT {-0-}-> RCons ((genericLength xs) + treeSize) treeSize nd xs
    GT {- > 1-} -> error "drop invariant violated, must be smaller than current tree"



-- Old version of drop
-- worst case complexity /O(n)/
simpleDrop :: Word64 -> RAList a -> RAList a
simpleDrop n xs  | n <= 0 = xs
                 | n >= (genericLength xs) = Nil
                 | otherwise =  (loop n xs)
    where loop 0 rs = rs
          loop m (RCons _tot w _ rs) | w <= m = loop (m-w) rs
          loop m (RCons _tot w (Node _ l r) rs) = loop (m-1) (RCons ((genericLength xs) + 2 * w2) w2 l (RCons ((genericLength xs) + w2) w2 r rs))
            where w2 = half w
          loop _ _ = error "Data.RAList.drop: impossible"


-- we *could* try to do better here, but this is fine
splitAt :: Word64 -> RAList a -> (RAList a, RAList a)
splitAt n xs = (take n xs, drop n xs)

genericSplitAt :: Integral n => n  -> RAList a -> (RAList a, RAList a)
genericSplitAt siz ls | siz <=0 = (Nil,ls)
                      | word64Representable siz = (take (fromIntegral siz) ls, drop (fromIntegral siz) ls)
                      | otherwise = (ls, Nil)

--elem :: (Eq a) => a -> RAList a -> Bool
--elem x = any (== x)

--notElem :: (Eq a) => a -> RAList a -> Bool
--notElem x = not . elem x -- aka all (/=)

-- naive list based lookup
lookupL :: (Eq a) => a -> RAList (a, b) -> Maybe b
lookupL x xys = Prelude.lookup x (toList xys)

-- catMaybes ls = mapMaybe Just ls
catMaybes :: RAList (Maybe a) -> RAList a
catMaybes = \  ls-> foldr' (\ a bs -> maybe bs (:| bs) a ) Nil  ls

wither :: forall a b f . Applicative f => (a -> f (Maybe b)) -> RAList a -> f (RAList b)
wither f ls =  foldr ((\ a fbs -> liftA2 (maybe id (cons)) (f a) fbs))  (pure Nil ) ls


-- mapMaybe f ls ===  foldr' (\ a bs -> maybe bs (\b -> b :| bs ) $! f a) ls
mapMaybe :: forall a b .  (a -> Maybe b) -> RAList a -> RAList b
mapMaybe = \ fm ls ->
    let
        go :: RAList a -> RAList b
        go Nil = Nil
        go (a:| as) | Just b <- fm a =  b :| go as
                    | otherwise      = go as
        in
        go ls

-- wither f ls == foldr


{-# NOINLINE [1] filter #-}
filter :: forall a . (a -> Bool) -> RAList a -> RAList a
filter = \ f   ls ->
  let go :: RAList a  -> RAList a
      go Nil = Nil
      go (a :| as) = if f a
                       then a :| go as
                       else go as
    in
     go ls


--filter _p Nil    = Nil
--filter p  (Cons x xs)
--  | p x         = x `Cons` filter p xs
--  | otherwise      = filter p xs



{-# INLINE [0] filterFB #-} -- See Note [Inline FB functions] in ghc base
filterFB :: (a -> b -> b) -> (a -> Bool) -> a -> b -> b
filterFB c p x r | p x       = x `c` r
                 | otherwise = r

--- ANY late rule is problematic that uses cons :(

{-# RULES
"RA/filter"     [~1] forall p xs.  filter p xs = build (\c n -> foldr (filterFB c p) n xs)
"RA/filterList" [1]  forall p.     foldr (filterFB (cons) p) RNil = filter p
"RA/filterFB"        forall c p q. filterFB (filterFB c p) q = filterFB c (\x -> q x && p x)
 #-}


partition :: (a->Bool) -> RAList a -> (RAList a, RAList a)
partition p xs = (filter p xs, filter (not . p) xs)



zip :: RAList a -> RAList b -> RAList (a, b)
zip = zipWith (,)

zipWith :: forall a b c .  (a->b->c) -> RAList a -> RAList b -> RAList c
zipWith f  = \ xs1 xs2 ->


                  case compare (wLength xs1) (wLength xs2) of
                      EQ -> zipTop xs1 xs2

                      LT -> zipTop  xs1
                                    (take (wLength xs1) xs2)

                      GT -> zipTop  (take (wLength xs2) xs1)
                                     xs2

    --      | s1 == s2 = RAList s1 (zipTop wts1 wts2)
    --    | otherwise = fromList $ Prelude.zipWith f (toList xs1) (toList xs2)
  where zipTree (Leaf x1) (Leaf x2) = Leaf (f x1 x2)
        zipTree (Node x1 l1 r1) (Node x2 l2 r2) = Node (f x1 x2) (zipTree l1 l2) (zipTree r1 r2)
        zipTree _ _ = error "Data.RAList.zipWith: impossible"
        zipTop :: RAList a -> RAList b -> RAList c
        zipTop RNil RNil = RNil
        zipTop (RCons tot1 w t1 xss1) (RCons _tot2 _ t2 xss2) = RCons tot1 w (zipTree t1 t2) (zipTop xss1 xss2)
        zipTop _ _ = error "Data.RAList.zipWith: impossible"



-- | Change element at the given index.
-- Complexity /O(log n)/.
update :: Word64 -> a -> RAList a -> RAList a
update i x = adjust (const x) i

-- | Apply a function to the value at the given index.
-- Complexity /O(log n)/.
adjust :: forall a . (a->a) -> Word64 -> RAList a -> RAList a
adjust f n s | n <  0 = error "Data.RAList.adjust: negative index"
                          | n >= (genericLength s) = error "Data.RAList.adjust: index too large"
                          | otherwise = (adj n s )
  where adj  :: Word64 -> RAList a -> RAList a
        adj j (RCons tot  w t wts') | j < w     = RCons tot w (adjt j (w `quot` 2) t) wts'
                                    | otherwise = RCons tot w t (adj (j-w) wts')
        adj j _ = error ("Data.RAList.adjust: impossible Nil element: " <> show j)

        adjt :: Word64 -> Word64 -> Tree a -> Tree a
        adjt 0 0 (Leaf x)     = Leaf (f x)
        adjt 0 _ (Node x l r) = Node (f x) l r
        adjt j w (Node x l r) | j <= w    = Node x (adjt (j-1) (w `quot` 2) l) r
                              | otherwise = Node x l (adjt (j-1-w) (w `quot` 2) r)
        adjt _ _ _ = error "Data.RAList.adjust: impossible"



-- | Complexity /O(n)/.
fromList :: [a] -> RAList a
fromList = Prelude.foldr Cons Nil

errorEmptyList :: String -> a
errorEmptyList fun =
  error ("Data.RAList." Prelude.++ fun Prelude.++ ": empty list")


--- copy fusion codes of your own :) perhaps?
--- for now these fusion rules are shamelessly copied from the ghc base library

{-# INLINE [1] build #-}
--- a
build   :: forall a. (forall b. (a -> b -> b) -> b -> b) -> RAList a
build = \ g -> g  cons Nil

unfoldr :: (b -> Maybe (a, b)) -> b -> RAList a
{-# INLINE unfoldr #-} -- See Note [INLINE unfoldr  in ghc base library original source]
unfoldr f b0 = build (\c n ->
  let go b = case f b of
               Just (a, new_b) -> a `c` go new_b
               Nothing         -> n
  in go b0)


augment :: forall a. (forall b. (a->b->b) -> b -> b) -> RAList a -> RAList a
-- {-# INLINE [1] augment #-}
augment g xs = g cons xs



--{-# RULES
--"RALIST/fold/build"    forall k z (g::forall b. (a->b->b) -> b -> b) .
--                foldr k z (build g) = g k z
--
--"RALIST/foldr/augment" forall k z xs (g::forall b. (a->b->b) -> b -> b) .
--                foldr k z (augment g xs) = g k (foldr k z xs)
--
--
--"RALIST/augment/build" forall (g::forall b. (a->b->b) -> b -> b)
--                       (h::forall b. (a->b->b) -> b -> b) .
--                       augment g (build h) = build (\c n -> g c (h c n))
--
----- not sure if these latter rules will be useful for RALIST
--
--"RALIST/foldr/cons/build" forall k z x (g::forall b. (a->b->b) -> b -> b) .
--                           foldr k z (cons x (build g)) = k x (g k z)
--
--
--"RALIST/foldr/single"  forall k z x. foldr k z (cons x RNil) = k x z
--"RALIST/foldr/nil"     forall k z.   foldr k z RNil  = z
--
--
--"RALIST/foldr/cons/build" forall k z x (g::forall b. (a->b->b) -> b -> b) .
--                           foldr k z (cons x (build g)) = k x (g k z)
--
--"RALIST/augment/build" forall (g::forall b. (a->b->b) -> b -> b)
--                       (h::forall b. (a->b->b) -> b -> b) .
--                       augment g (build h) = build (\c n -> g c (h c n))
--"RALIST/augment/nil"   forall (g::forall b. (a->b->b) -> b -> b) .
--                        augment g RNil = build g
--
--"RALIST/foldr/id"                        foldr (cons) RNil = \x  -> x
--"RALIST/foldr/app"     [1] forall ys. foldr (cons) ys = \xs -> xs ++ ys
--        -- Only activate this from phase 1, because that's
--        -- when we disable the rule that expands (++) into foldr
-- #-}

-- {-# RULES
-- "RALIST/++"    [~1] forall xs ys. xs ++ ys = augment (\c n -> foldr c n xs) ys
--   #-}



{-
additional ru

"foldr/id"                        foldr (:) [] = \x  -> x
        -- Only activate this from phase 1, because that's
        -- when we disable the rule that expands (++) into foldr

-- The foldr/cons rule looks nice, but it can give disastrously
-- bloated code when compiling
--      array (a,b) [(1,2), (2,2), (3,2), ...very long list... ]
-- i.e. when there are very very long literal lists
-- So I've disabled it for now. We could have special cases
-- for short lists, I suppose.
-- "foldr/cons" forall k z x xs. foldr k z (x:xs) = k x (foldr k z xs)

"foldr/single"  forall k z x. foldr k z [x] = k x z
"foldr/nil"     forall k z.   foldr k z []  = z

"foldr/cons/build" forall k z x (g::forall b. (a->b->b) -> b -> b) .
                           foldr k z (x:build g) = k x (g k z)

-}