packages feed

grapefruit-frp-0.1.0.0: src/Internal/Signal/Incremental/Sequence/Selection.hs

module Internal.Signal.Incremental.Sequence.Selection (

    SeqSel,
    fromSeq,
    splitAt,
    selectionIndex,
    selectionInterval,
    atomicPatch

) where

    -- Prelude
    import Prelude hiding (splitAt)

    -- Data
    import Data.Monoid     as Monoid
    import Data.Foldable   as Foldable
    import Data.FingerTree as FingerTree
    import Data.Sequence   as Seq        (Seq)

    -- Internal
    import           Internal.Signal.Incremental.Sequence.AtomicDiff as AtomicDiff
                                                                        hiding (atomicPatch)
    import qualified Internal.Signal.Incremental.Sequence.AtomicDiff as AtomicDiff

    -- * Pairs of original length and filtered length
    newtype Lengths = Lengths (Sum Int,Sum Int) deriving (Monoid)

    lengths :: Int -> Int -> Lengths
    lengths origLen selLen = Lengths (Sum origLen,Sum selLen)

    originalLength :: Lengths -> Int
    originalLength (Lengths (Sum origLen,_)) = origLen

    selectionLength :: Lengths -> Int
    selectionLength (Lengths (_,Sum selLen)) = selLen

    -- * Blocks of multiple bad elements and one good element
    newtype Block = Block Int

    instance Measured Lengths Block where

        measure (Block badCnt) = lengths (succ badCnt) 1

    -- * Sequence selections
    data SeqSel  = SeqSel (FingerTree Lengths Block) Int

    instance Monoid SeqSel where

        mempty = SeqSel FingerTree.empty 0

        SeqSel blocks1 end1 `mappend` seqSel2 = SeqSel (blocks1 >< others) end' where

            SeqSel others end' = adjustFront end1 seqSel2

    fromSeq :: (el -> Bool) -> Seq el -> SeqSel
    fromSeq prd = fromList . toList where

        fromList list = case break prd list of
                            (bads,[])         -> SeqSel FingerTree.empty (length bads)
                            (bads,ok : list') -> cons (length bads) (fromList list')

    cons :: Int -> SeqSel -> SeqSel
    cons badCnt (SeqSel blocks end) = SeqSel (Block badCnt <| blocks) end

    adjustFront :: Int -> SeqSel -> SeqSel
    adjustFront delta (SeqSel blocks end) = case viewl blocks of
                                                EmptyL          -> SeqSel empty (end + delta)
                                                block :< blocks -> SeqSel (adjBlock block <| blocks)
                                                                          end
                                            where

        adjBlock (Block badCnt) = Block (badCnt + delta)

    splitAt :: Int -> SeqSel -> (SeqSel,SeqSel)
    splitAt idx (SeqSel blocks end) = (SeqSel blocks1 end1,seqSel2) where

        (blocks1,others) = FingerTree.split ((<= idx) . originalLength) blocks

        end1             = idx - originalLength (measure blocks1)

        seqSel2          = adjustFront (negate end1) (SeqSel others end)

    selectionIndex :: SeqSel -> Int -> Int
    selectionIndex (SeqSel blocks _) idx = selectionLength $
                                           measure $
                                           FingerTree.takeUntil ((<= idx) . originalLength) blocks

    selectionInterval :: SeqSel -> Int -> Int -> (Int,Int)
    selectionInterval seqSel idx cnt = (selIdx,selectionIndex seqSel (idx + cnt) - selIdx) where

        selIdx = selectionIndex seqSel idx

    atomicPatch :: (el -> Bool) -> SeqSel -> AtomicDiff el -> SeqSel
    atomicPatch prd = AtomicDiff.atomicPatch (fromSeq prd) splitAt mappend