packages feed

euler-tour-tree (empty) → 0.1.0.0

raw patch · 6 files changed

+468/−0 lines, 6 filesdep +QuickCheckdep +Uniquedep +basesetup-changed

Dependencies added: QuickCheck, Unique, base, containers, euler-tour-tree, fingertree, hlint, keys, mtl, parser-combinators, sequence, tasty, tasty-hunit, tasty-quickcheck, transformers

Files

+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ euler-tour-tree.cabal view
@@ -0,0 +1,76 @@+name:                euler-tour-tree+version:             0.1.0.0+synopsis:            Euler tour trees+-- description:         Cf README file+homepage:            https://github.com/k0ral/euler-tour-tree+license:             PublicDomain+author:              koral+maintainer:          koral@mailoo.org+category:            Data+build-type:          Simple+-- extra-source-files:  ChangeLog.md+cabal-version:       >=1.10+++source-repository head+  type:     git+  location: git://github.com/k0ral/euler-tour-tree.git+++flag enable-hlint-test+  description: Enable hlint test-suite+  manual: True+  default: False+++library+  exposed-modules:+    Data.EulerTourTree+    -- Data.EulerTourTree.Sized+  build-depends:       base >=4.9 && <4.11, containers, fingertree, mtl, parser-combinators, transformers, Unique+  hs-source-dirs:      src+  default-language:    Haskell2010+++test-suite unit-tests+    type: exitcode-stdio-1.0+    main-is: UnitTests.hs+    build-depends:+        euler-tour-tree -any,+        base >=4.9,+        containers,+        tasty -any,+        tasty-hunit -any+    default-language: Haskell2010+    hs-source-dirs: test+++test-suite property-tests+    type: exitcode-stdio-1.0+    main-is: PropertyTests.hs+    build-depends:+        euler-tour-tree -any,+        base >=4.9,+        containers,+        keys,+        QuickCheck,+        -- quickcheck-instances -any,+        sequence,+        tasty -any,+        tasty-quickcheck -any+    default-language: Haskell2010+    hs-source-dirs: test+++test-suite hlint-tests+    if flag(enable-hlint-test)+      buildable: True+    else+      buildable: False+    type: exitcode-stdio-1.0+    main-is: HLint.hs+    build-depends:+        base >=4.9,+        hlint -any+    default-language: Haskell2010+    hs-source-dirs: test
+ src/Data/EulerTourTree.hs view
@@ -0,0 +1,229 @@+{-# LANGUAGE ConstraintKinds       #-}+{-# LANGUAGE ExplicitForAll        #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE StandaloneDeriving    #-}+-- | Implementation of Euler tour trees.+--+-- > import qualified Data.EulerTourTree as ETTree+-- > import Data.EulerTourTree(EulerTourTree)+--+-- When specified, time complexity refers to the number /n/ of nodes in the input tree.+module Data.EulerTourTree+  ( EulerTourTree+    -- * Construction+  , empty+  , singleton+  , fromTree+    -- * Deconstruction+  , toTree+    -- * Query+  , root+  , member+  , size+    -- * Transformation+  , cutEdge+  , splice+  , reroot+  ) where++-- {{{ Imports+import           Control.Applicative             hiding (empty)+import           Control.Applicative.Combinators+import           Control.Monad+import           Control.Monad.State.Lazy+import           Control.Monad.Trans.Maybe+import           Data.FingerTree                 hiding (empty, null, singleton)+import qualified Data.FingerTree                 as FingerTree+import           Data.Foldable+import           Data.List.Unique+import           Data.Maybe+import           Data.Monoid+import           Data.Set                        (Set)+import qualified Data.Set                        as Set+import           Data.Tree+import           Debug.Trace+-- }}}++searchM :: MonadPlus m => Measured v a => (v -> v -> Bool) -> FingerTree v a -> m (FingerTree v a, a, FingerTree v a)+searchM f tree = case FingerTree.search f tree of+  Position before element after -> return (before, element, after)+  _                             -> mzero++initSafe :: Measured v a => FingerTree v a -> FingerTree v a+initSafe tree = case viewr tree of+  result :> _ -> result+  _           -> tree++tailSafe :: Measured v a => FingerTree v a -> FingerTree v a+tailSafe tree = case viewl tree of+  _ :< result -> result+  _           -> tree++newtype EulerTourNode node = EulerTourNode node++deriving instance (Eq node) => Eq (EulerTourNode node)+deriving instance (Ord node) => Ord (EulerTourNode node)+deriving instance (Show node) => Show (EulerTourNode node)++data EulerTourMonoid node = EulerTourMonoid+  (First node)+  (Set (node, node))+  (Last node)+  (Set node)+  (Sum Int)++deriving instance Show node => Show (EulerTourMonoid node)++instance Ord node => Monoid (EulerTourMonoid node) where+  mempty = EulerTourMonoid mempty mempty mempty mempty mempty+  EulerTourMonoid a b c d e `mappend` EulerTourMonoid a' b' c' d' e' = result where+    result = EulerTourMonoid (a <> a') (b <> bMiddle <> b') (c <> c') (d <> d') (e <> e')+    bMiddle = fromMaybe mempty $ do+      l <- getLast c+      f <- getFirst a'+      return $ Set.singleton (min l f, max l f)++instance Ord node => Measured (EulerTourMonoid node) (EulerTourNode node) where+  measure (EulerTourNode node) = EulerTourMonoid (pure node) mempty (pure node) (Set.singleton node) (pure 1)++firstVertex :: MonadPlus m => Ord node => EulerTourMonoid node -> m node+firstVertex (EulerTourMonoid first _ _ _ _) = maybe mzero return $ getFirst first++allNodes :: Ord node => EulerTourMonoid node -> Set node+allNodes (EulerTourMonoid _ _ _ nodes _) = nodes++vertexMember :: Ord node => node -> EulerTourMonoid node -> Bool+vertexMember node (EulerTourMonoid _ _ _ nodes _) = Set.member node nodes++edgeMember :: Ord node => (node, node) -> EulerTourMonoid node -> Bool+edgeMember (u, v) (EulerTourMonoid _ edges _ _ _) = Set.member (min u v, max u v) edges++tourSize :: EulerTourMonoid node -> Int+tourSize (EulerTourMonoid _ _ _ nodes _) = Set.size nodes+++-- | Euler-tour implementation of a tree structure. It is parameterized by a node type @node@.+--+-- Requirements:+--+-- - @node@ is ordered+-- - node values are unique+data EulerTourTree node where+  EulerTourTree :: Ord node => FingerTree (EulerTourMonoid node) (EulerTourNode node) -> EulerTourTree node++instance Ord node => Measured (Set (node, node), Set node, Sum Int) (EulerTourTree node) where+  measure (EulerTourTree tree) = (edges, nodes, size) where+    EulerTourMonoid _ edges _ nodes size = measure tree++-- | 'member' and 'size' are respectively faster than 'elem' and 'length' (which run in /O(n)/).+instance Foldable EulerTourTree where+  foldMap f etTree@(EulerTourTree _) = maybe mempty (foldMap f) $ toTree etTree++deriving instance Eq node => Eq (EulerTourTree node)+deriving instance Ord node => Ord (EulerTourTree node)+deriving instance Show node => Show (EulerTourTree node)+++-- | /O(1)/ Construct an empty Euler tour tree.+empty :: Ord node => EulerTourTree node+empty = EulerTourTree FingerTree.empty++-- | /O(1)/ Construct an Euler tour tree with a single element.+singleton :: Ord node => node -> EulerTourTree node+singleton node = EulerTourTree $ FingerTree.singleton $ EulerTourNode node++-- | /O(n)/ Construct an Euler tour tree from a 'Data.Tree'.+--+-- Fail if, and only if, node values are not unique.+fromTree :: MonadPlus m => Ord node => Tree node -> m (EulerTourTree node)+fromTree tree = do+  guard $ allUnique $ toList tree+  return $ EulerTourTree $ fromTree' tree+  where fromTree' (Node node forest) = EulerTourNode node <| mconcat (map ((\x -> x |> EulerTourNode node) . fromTree') forest)++type Parser node m = (MonadState (FingerTree (EulerTourMonoid node) (EulerTourNode node)) m, MonadPlus m)++-- | /O(n)/ Deconstruct an Euler tour tree into a 'Data.Tree'.+toTree :: MonadPlus m => Ord node => EulerTourTree node -> m (Tree node)+toTree (EulerTourTree fingerTree) = evalStateT parser fingerTree where+  parser = do+    EulerTourNode node <- anyToken+    forest <- parser `endBy` try (token $ EulerTourNode node)+    return $ Node node forest+  anyToken :: Ord node => Parser node m => m (EulerTourNode node)+  anyToken = do+    tree <- get+    case viewl tree of+      node :< tree' -> put tree' >> return node+      _             -> mzero+  token x = do+    t <- anyToken+    guard (t == x)+    return t+  try f = do+    a <- get+    f <|> (put a >> mzero)++-- | /O(1)/ Return the root of the tree. Fail if it is empty.+root :: MonadPlus m => Ord node => EulerTourTree node -> m node+root (EulerTourTree tree) = firstVertex $ measure tree++-- | /O(log n)/ Return 'True' if node is an element of the tree.+member :: Ord node => node -> EulerTourTree node -> Bool+member node (EulerTourTree tree) = vertexMember node $ measure tree++-- | /O(1)/ Return the number of elements in the tree.+size :: Ord node => EulerTourTree node -> Int+size (EulerTourTree fingerTree) = tourSize $ measure fingerTree++-- | /O(log n)/ Return 2 subtrees of @tree@ where @a@ is the subtree of nodes __a__bove @edge@, and @b@ is the subtree of nodes __b__elow @edge@.+--+-- Fail if @edge@ isn't found in @tree@+cutEdge :: MonadPlus m+        => Ord node+        => EulerTourTree node  -- ^ Denoted by @tree@+        -> (node, node)        -- ^ Denoted by @edge@+        -> m (EulerTourTree node, EulerTourTree node)  -- ^ Denoted by @(a, b)@+cutEdge (EulerTourTree tree) e@(a, b) = do+  (left, node, tree') <- searchM p1 tree+  (middle, node', right) <- searchM p2 $ node <| tree'+  let inside = node <| middle+      outside = left >< tailSafe right+  return (EulerTourTree inside, EulerTourTree outside)+  where p1 before after = edgeMember e before+        p2 before after = not (edgeMember e after)++-- | /O(log n)/ Attach @tree1@ as a child of @node@ in @tree2@.+--+-- Fail if @node@ isn't found in @tree2@, or if @tree1@ and @tree2@ have nodes in common.+splice :: MonadPlus m+       => Ord node+       => EulerTourTree node           -- ^ Denoted by @tree1@+       -> node                         -- ^ Denoted by @node@+       -> EulerTourTree node           -- ^ Denoted by @tree2@+       -> m (EulerTourTree node)+splice (EulerTourTree inviteeTree) node (EulerTourTree hostTree) = do+  guard $ null $ Set.intersection (allNodes $ measure inviteeTree) (allNodes $ measure hostTree)+  (left, _, right) <- searchM p hostTree+  let inviteeTree' = if FingerTree.null inviteeTree then inviteeTree else EulerTourNode node <| inviteeTree+  return $ EulerTourTree $ left >< inviteeTree' >< (EulerTourNode node <| right)+  where p before after = vertexMember node before && not (vertexMember node after)++-- | /O(log n)/ Rotate @tree@ such that @node@ is the new root.+--+-- Return 'Nothing' if @node@ isn't found in @tree@.+reroot :: MonadPlus m+       => Ord node+       => node                 -- ^ Denoted by @node@+       -> EulerTourTree node   -- ^ Denoted by @tree@+       -> m (EulerTourTree node)+reroot node (EulerTourTree tree) = do+  (left, _, tree') <- searchM p1 tree+  (middle, _, right) <- searchM p2 (etNode <| tree')+  return $ EulerTourTree $ middle >< (etNode <| initSafe right) >< (left |> etNode)+  where p1 before after = vertexMember node before+        p2 before after = not (vertexMember node after)+        etNode = EulerTourNode node
+ test/HLint.hs view
@@ -0,0 +1,8 @@+import qualified Language.Haskell.HLint as HLint (hlint)+import           System.Exit+++main :: IO ()+main = do+  result <- HLint.hlint [ "test/", "src/" ]+  if null result then exitSuccess else exitFailure
+ test/PropertyTests.hs view
@@ -0,0 +1,51 @@+import           Data.EulerTourTree++import           Data.Foldable+import           Data.Key+import           Data.Monoid+import           Data.Sequence         hiding (empty)+import           Data.Tree+import           Test.QuickCheck.Gen+import           Test.Tasty+import           Test.Tasty.QuickCheck+++-- Unsafe version of fromTree+fromTree' :: Ord node => Tree node -> EulerTourTree node+fromTree' tree = let Just a = fromTree tree in a+++main :: IO ()+main = defaultMain $ testGroup "Property tests"+  [ fromToTree+  , spliceEmpty+  ]++fromToTree :: TestTree+fromToTree = testProperty "toTree . fromTree == id" $ do+  tree <- genTree+  let intermediate = fromTree tree+      result = toTree =<< intermediate+  return $ counterexample (show tree <> "\n" <> show intermediate <> "\n" <> show result) $+    Just tree == result++spliceEmpty :: TestTree+spliceEmpty = testProperty "splice empty == id" $ do+  tree <- fromTree' <$> genTree+  node <- genElement tree+  let result = splice empty node tree+  return $ counterexample (show tree <> "\n" <> show node <> "\n" <> show result) $ Just tree == result+++genElement :: Foldable f => f a -> Gen a+genElement = elements . toList++genTree :: Gen (Tree (Seq Int))+genTree = genTreeDepth =<< elements [1..1]++genTreeDepth :: Int -> Gen (Tree (Seq Int))+genTreeDepth n = mapWithKey (\key _ -> key) <$> genTreeDepth' n++genTreeDepth' :: Int -> Gen (Tree ())+genTreeDepth' 0 = return $ Node () []+genTreeDepth' n = Node () <$> vectorOf 5 (genTreeDepth' $ n-1)
+ test/UnitTests.hs view
@@ -0,0 +1,102 @@+import           Data.EulerTourTree++import           Data.Tree+import           Test.Tasty+import           Test.Tasty.HUnit+++-- Unsafe version of fromTree+fromTree' :: Ord node => Tree node -> EulerTourTree node+fromTree' tree = let Just a = fromTree tree in a++main :: IO ()+main = defaultMain $ testGroup "Unit tests"+  [ cutEdgeTest+  , memberTest+  , rerootTest+  , rootTest+  , sizeTest+  , spliceTest+  ]++testTree1 :: EulerTourTree Int+testTree1 = fromTree' $ Node 0+  [ Node 10+    [ Node 11 []+    , Node 12 [] ]+  , Node 20+    [ Node 21 []+    , Node 22 [] ] ]++testTree2 :: EulerTourTree Int+testTree2 = fromTree' $ Node 100+  [ Node 110+    [ Node 111 []+    , Node 112 []+    ]+  , Node 120+    [ Node 121 []+    , Node 122 []+    ]+  ]+++cutEdgeTest :: TestTree+cutEdgeTest = testCase "cutEdge" $ do+  cutEdge empty (0, 1) @?= Nothing+  cutEdge testTree1 (10, 11) @?= Just (fromTree' $ Node 11 [], fromTree' $ Node 0+   [ Node 10+     [ Node 12 [] ]+   , Node 20+     [ Node 21 []+     , Node 22 [] ] ])++memberTest :: TestTree+memberTest = testCase "member" $ do+  member 0 testTree1  @?= True+  member 1 testTree1  @?= False+  member 12 testTree1 @?= True+  member 21 testTree1 @?= True+  member 23 testTree1 @?= False++rerootTest :: TestTree+rerootTest = testCase "reroot" $ do+  reroot 0 empty @?= Nothing+  reroot 12 testTree1 @?= Just (fromTree' $ Node 12+    [ Node 10+      [ Node 0+        [ Node 20+          [ Node 21 []+          , Node 22 [] ] ]+        , Node 11 [] ] ] )++rootTest :: TestTree+rootTest = testCase "root" $ do+  root (empty :: EulerTourTree Int) @?= Nothing+  root (singleton 1) @?= Just 1+  root testTree1 @?= Just 0++sizeTest :: TestTree+sizeTest = testCase "size" $ do+  size (empty :: EulerTourTree Int) @?= 0+  size (singleton 1) @?= 1+  size testTree1 @?= 7+  size testTree2 @?= 7++spliceTest :: TestTree+spliceTest = testCase "splice" $ do+  splice testTree2 12 empty @?= Nothing+  splice testTree2 12 testTree1 @?= Just (fromTree' $ Node 0+    [ Node 10+      [ Node 11 []+      , Node 12+          [ Node 100+            [ Node 110+              [ Node 111 []+              , Node 112 [] ]+            , Node 120+              [ Node 121 []+              , Node 122 [] ] ] ] ]+    , Node 20+      [ Node 21 []+      , Node 22 [] ] ] )