packages feed

ecta-1.0.0.0: src/Data/ECTA/Internal/Paths/Zipper.hs

-- | These were used in an earlier version of the enumeration algorithm, but no longer.
--
--   They are being kept around just in case.


module Data.ECTA.Internal.Paths.Zipper (
    unionPathTrie

  , InvertedPathTrie(..)

  , PathTrieZipper(..)
  , emptyPathTrieZipper
  , pathTrieToZipper
  , zipperCurPathTrie
  , pathTrieZipperDescend
  , pathTrieZipperAscend
  , unionPathTrieZipper
  ) where

import qualified Data.Vector as Vector
import qualified Data.Vector.Mutable as Vector ( unsafeWrite )

import GHC.Exts ( inline )

import Data.ECTA.Internal.Paths

-----------------------------------------------------------------------

---------------------
------- Path trie union
------- (7/9/21: only used as utility for unionPathTrieZipper)
---------------------

unionPathTrie :: PathTrie -> PathTrie -> Maybe PathTrie
unionPathTrie EmptyPathTrie                pt                           = Just pt
unionPathTrie pt                           EmptyPathTrie                = Just pt
unionPathTrie TerminalPathTrie             TerminalPathTrie             = Just TerminalPathTrie
unionPathTrie TerminalPathTrie             _                            = Nothing
unionPathTrie _                            TerminalPathTrie             = Nothing
unionPathTrie (PathTrieSingleChild i1 pt1) (PathTrieSingleChild i2 pt2) =
    if i1 == i2 then
      PathTrieSingleChild i1 <$> unionPathTrie pt1 pt2
    else
      Just $ PathTrie $ Vector.generate (1 + max i1 i2) $ \j -> if j == i1 then
                                                                  pt1
                                                                else if j == i2 then
                                                                  pt2
                                                                else
                                                                  EmptyPathTrie
unionPathTrie (PathTrieSingleChild i pt)   (PathTrie vec) =
  if Vector.length vec > i then
    do updated <- unionPathTrie pt (vec `Vector.unsafeIndex` i)
       Just $ PathTrie $ Vector.modify (\v -> Vector.unsafeWrite v i updated) vec
  else
    Just $ PathTrie $ Vector.generate (i+1) $ \j -> if j < Vector.length vec then
                                                      vec `Vector.unsafeIndex` j
                                                    else if j == i then
                                                      pt
                                                    else
                                                      EmptyPathTrie


unionPathTrie pt1@(PathTrie _)             pt2@(PathTrieSingleChild _ _) = inline unionPathTrie pt2 pt1 -- TODO: Check whether this inlining is effective
unionPathTrie (PathTrie vec1)              (PathTrie vec2)               =
  let newLength = max (Vector.length vec1) (Vector.length vec2)
      smallerLength = min (Vector.length vec1) (Vector.length vec2)
      bigVec   = if Vector.length vec1 > Vector.length vec2 then vec1 else vec2
      smallVec = if Vector.length vec1 > Vector.length vec2 then vec2 else vec1
  in fmap PathTrie $ Vector.generateM newLength $ \i -> if i >= smallerLength then
                                                          return (bigVec `Vector.unsafeIndex` i)
                                                        else
                                                          unionPathTrie (bigVec `Vector.unsafeIndex` i) (smallVec `Vector.unsafeIndex` i)



---------------------
------- Zippers
---------------------

data InvertedPathTrie = PathZipperRoot
                      | PathTrieAt {-# UNPACK #-} !Int !PathTrie !InvertedPathTrie
  deriving ( Eq, Ord, Show )

data PathTrieZipper = PathTrieZipper !PathTrie !InvertedPathTrie
  deriving ( Eq, Ord, Show )

emptyPathTrieZipper :: PathTrieZipper
emptyPathTrieZipper = PathTrieZipper EmptyPathTrie PathZipperRoot

pathTrieToZipper :: PathTrie -> PathTrieZipper
pathTrieToZipper pt = PathTrieZipper pt PathZipperRoot

zipperCurPathTrie :: PathTrieZipper -> PathTrie
zipperCurPathTrie (PathTrieZipper pt _) = pt

unionInvertedPathTrie :: InvertedPathTrie -> InvertedPathTrie -> Maybe InvertedPathTrie
unionInvertedPathTrie PathZipperRoot           ipt                      = Just ipt
unionInvertedPathTrie ipt                      PathZipperRoot           = Just ipt
unionInvertedPathTrie (PathTrieAt i1 pt1 ipt1) (PathTrieAt i2 pt2 ipt2) =
  if i1 /= i2 then
    Nothing
  else
    PathTrieAt i1 <$> unionPathTrie pt1 pt2 <*> unionInvertedPathTrie ipt1 ipt2


unionPathTrieZipper :: PathTrieZipper -> PathTrieZipper -> Maybe PathTrieZipper
unionPathTrieZipper (PathTrieZipper pt1 ipt1) (PathTrieZipper pt2 ipt2) =
  PathTrieZipper <$> unionPathTrie pt1 pt2 <*> unionInvertedPathTrie ipt1 ipt2

pathTrieZipperDescend :: PathTrieZipper -> Int -> PathTrieZipper
pathTrieZipperDescend (PathTrieZipper pt z) i = PathTrieZipper (pathTrieDescend pt i) (PathTrieAt i pt z)

-- | The semantics of this may not be what you expect: Path trie zippers do not support editing currently, only traversing.
--   The value at the cursor (as well as the index) is ignored except when traversing above the root, where it uses those
--   values to extend the path trie upwards.
pathTrieZipperAscend :: PathTrieZipper -> Int -> PathTrieZipper
pathTrieZipperAscend (PathTrieZipper pt PathZipperRoot)         i = PathTrieZipper (PathTrieSingleChild i pt) PathZipperRoot
pathTrieZipperAscend (PathTrieZipper _  (PathTrieAt _ pt' ipt)) _ = PathTrieZipper pt'                        ipt