diff --git a/bench/amortized-deque/Main.hs b/bench/amortized-deque/Main.hs deleted file mode 100644 index 6fb008f..0000000 --- a/bench/amortized-deque/Main.hs +++ /dev/null @@ -1,29 +0,0 @@ -{-# OPTIONS_GHC -Wno-incomplete-patterns #-} - -module Main (main) where - -import Deque (Deque) -import Deque qualified -import Test.Tasty.Bench (bench, defaultMain, whnf) - -main :: IO () -main = do - let n = 100000 :: Int - defaultMain [bench "Deque" (whnf theBenchmark n)] - -theBenchmark :: Int -> Int -theBenchmark num = - loop1 0 0 (Deque.enqueue 0 Deque.empty) - where - loop1 :: Int -> Int -> Deque Int -> Int - loop1 !n !acc queue - | n == num = loop2 acc queue - | otherwise = - case Deque.dequeue queue of - Just (m, queue') -> loop1 (n + 1) (acc + m) (Deque.enqueue n (Deque.enqueue (n + 1) queue')) - - loop2 :: Int -> Deque Int -> Int - loop2 !n queue = - case Deque.dequeue queue of - Nothing -> n - Just (m, queue') -> loop2 (n + m) queue' diff --git a/bench/real-time-deque/Main.hs b/bench/real-time-deque/Main.hs deleted file mode 100644 index 645f2f8..0000000 --- a/bench/real-time-deque/Main.hs +++ /dev/null @@ -1,29 +0,0 @@ -{-# OPTIONS_GHC -Wno-incomplete-patterns #-} - -module Main (main) where - -import Deque.RealTime (RealTimeDeque) -import Deque.RealTime qualified -import Test.Tasty.Bench (bench, defaultMain, whnf) - -main :: IO () -main = do - let n = 100000 :: Int - defaultMain [bench "RealTimeDeque" (whnf theBenchmark n)] - -theBenchmark :: Int -> Int -theBenchmark num = - loop1 0 0 (Deque.RealTime.enqueue 0 Deque.RealTime.empty) - where - loop1 :: Int -> Int -> RealTimeDeque Int -> Int - loop1 !n !acc queue - | n == num = loop2 acc queue - | otherwise = - case Deque.RealTime.dequeue queue of - Just (m, queue') -> loop1 (n + 1) (acc + m) (Deque.RealTime.enqueue n (Deque.RealTime.enqueue (n + 1) queue')) - - loop2 :: Int -> RealTimeDeque Int -> Int - loop2 !n queue = - case Deque.RealTime.dequeue queue of - Nothing -> n - Just (m, queue') -> loop2 (n + m) queue' diff --git a/queues.cabal b/queues.cabal index 5fe9214..342242d 100644 --- a/queues.cabal +++ b/queues.cabal @@ -10,17 +10,17 @@ license-file: LICENSE maintainer: Mitchell Rosen , Travis Staton name: queues stability: experimental -synopsis: Queue and deque data structures. +synopsis: Queue data structures. tested-with: GHC == 9.4.7, GHC == 9.6.3, GHC == 9.8.1 version: 0.1.0 description: - Queue and deque data structures, as described in + Queue data structures, as described in . * Okasaki, Chris. "Simple and efficient purely functional queues and deques." /Journal of functional programming/ 5.4 (1995): 583-592. * Okasaki, Chris. /Purely Functional Data Structures/. Diss. Princeton University, 1996. . - This package provides two queue variants and one deque, whose salient API differences are summarized in the following table: + This package provides two queue variants, whose salient API differences are summarized in the following table: . +-----------------+----------------------+--------------------+ | | @"EphemeralQueue"@ | @"Queue"@ | @@ -29,13 +29,8 @@ description: +-----------------+----------------------+--------------------+ | @dequeue@ | \(\mathcal{O}(1)^⧧\) | \(\mathcal{O}(1)\) | +-----------------+----------------------+--------------------+ - | @length@ | \(\mathcal{O}(n)\) | \(\mathcal{O}(n)\) | - +-----------------+----------------------+--------------------+ - | @fromList@ | \(\mathcal{O}(1)\) | \(\mathcal{O}(1)\) | - +-----------------+----------------------+--------------------+ . - * \(^*\) Amortized. - * \(^⧧\) Amortized under ephemeral usage only. + * \(^*\) Amortized, under ephemeral usage only. . To see a rough performance comparison between the data structures, click into an individual module. Always benchmark your own code for accurate numbers. @@ -93,8 +88,6 @@ library build-depends: base ^>= 4.17 || ^>= 4.18 || ^>= 4.19, exposed-modules: - Deque - Deque.RealTime Queue Queue.Ephemeral hs-source-dirs: src @@ -111,17 +104,6 @@ test-suite test main-is: Main.hs type: exitcode-stdio-1.0 -benchmark bench-amortized-deque - import: component - build-depends: - base, - queues, - tasty-bench ^>= 0.3.5, - ghc-options: -fproc-alignment=64 -rtsopts -threaded - hs-source-dirs: bench/amortized-deque - main-is: Main.hs - type: exitcode-stdio-1.0 - benchmark bench-ephemeral-queue import: component build-depends: @@ -133,17 +115,6 @@ benchmark bench-ephemeral-queue main-is: Main.hs type: exitcode-stdio-1.0 -benchmark bench-real-time-deque - import: component - build-depends: - base, - queues, - tasty-bench ^>= 0.3.5, - ghc-options: -fproc-alignment=64 -rtsopts -threaded - hs-source-dirs: bench/real-time-deque - main-is: Main.hs - type: exitcode-stdio-1.0 - benchmark bench-real-time-queue import: component build-depends: diff --git a/scripts/run-benchmarks.hs b/scripts/run-benchmarks.hs index 33006b1..68dcddc 100755 --- a/scripts/run-benchmarks.hs +++ b/scripts/run-benchmarks.hs @@ -20,9 +20,7 @@ import System.Process qualified as Process import Text.Printf (printf) data Results = Results - { amortizedDeque :: !Result, - ephemeralQueue :: !Result, - realTimeDeque :: !Result, + { ephemeralQueue :: !Result, realTimeQueue :: !Result, sequenceQueue :: !Result } @@ -40,9 +38,7 @@ main :: IO () main = do Process.callCommand "cabal build --enable-benchmarks all" - amortizedDequeBenchmark <- List.init <$> Process.readProcess "cabal" ["list-bin", "bench-amortized-deque"] "" ephemeralQueueBenchmark <- List.init <$> Process.readProcess "cabal" ["list-bin", "bench-ephemeral-queue"] "" - realTimeDequeBenchmark <- List.init <$> Process.readProcess "cabal" ["list-bin", "bench-real-time-deque"] "" realTimeQueueBenchmark <- List.init <$> Process.readProcess "cabal" ["list-bin", "bench-real-time-queue"] "" sequenceQueueBenchmark <- List.init <$> Process.readProcess "cabal" ["list-bin", "bench-sequence-queue"] "" @@ -64,21 +60,11 @@ main = do ] readCsv "results.csv" - let runAmortizedDequeBenchmark :: Results -> IO Results - runAmortizedDequeBenchmark results = do - result <- runBenchmark amortizedDequeBenchmark - pure $! results {amortizedDeque = amortizedDeque results <> result} - let runEphemeralQueueBenchmark :: Results -> IO Results runEphemeralQueueBenchmark results = do result <- runBenchmark ephemeralQueueBenchmark pure $! results {ephemeralQueue = ephemeralQueue results <> result} - let runRealTimeDequeBenchmark :: Results -> IO Results - runRealTimeDequeBenchmark results = do - result <- runBenchmark realTimeDequeBenchmark - pure $! results {realTimeDeque = realTimeDeque results <> result} - let runRealTimeQueueBenchmark :: Results -> IO Results runRealTimeQueueBenchmark results = do result <- runBenchmark realTimeQueueBenchmark @@ -92,9 +78,7 @@ main = do let runBenchmarks :: [Results -> IO Results] runBenchmarks = cycle - [ runAmortizedDequeBenchmark, - runEphemeralQueueBenchmark, - runRealTimeDequeBenchmark, + [ runEphemeralQueueBenchmark, runRealTimeQueueBenchmark, runSequenceQueueBenchmark ] @@ -104,21 +88,17 @@ main = do renderResults results1 go results1 benches - go (Results mempty mempty mempty mempty mempty) runBenchmarks + go (Results mempty mempty mempty) runBenchmarks renderResults :: Results -> IO () renderResults Results - { amortizedDeque = Result amortizedDequeRuns amortizedDequeTime0 amortizedDequeMem0, - ephemeralQueue = Result ephemeralQueueRuns ephemeralQueueTime0 ephemeralQueueMem0, - realTimeDeque = Result realTimeDequeRuns realTimeDequeTime0 realTimeDequeMem0, + { ephemeralQueue = Result ephemeralQueueRuns ephemeralQueueTime0 ephemeralQueueMem0, realTimeQueue = Result realTimeQueueRuns realTimeQueueTime0 realTimeQueueMem0, sequenceQueue = Result sequenceQueueRuns sequenceQueueTime0 sequenceQueueMem0 } = do - let amortizedDequeInfo, ephemeralQueueInfo, realTimeDequeInfo, realTimeQueueInfo, sequenceQueueInfo :: (Text.Builder, Double, Double, Double) - amortizedDequeInfo = ("Deque", amortizedDequeRuns, amortizedDequeTime0 / amortizedDequeRuns, amortizedDequeMem0 / amortizedDequeRuns) + let ephemeralQueueInfo, realTimeQueueInfo, sequenceQueueInfo :: (Text.Builder, Double, Double, Double) ephemeralQueueInfo = ("Queue.Ephemeral", ephemeralQueueRuns, ephemeralQueueTime0 / ephemeralQueueRuns, ephemeralQueueMem0 / ephemeralQueueRuns) - realTimeDequeInfo = ("Deque.RealTime", realTimeDequeRuns, realTimeDequeTime0 / realTimeDequeRuns, realTimeDequeMem0 / realTimeDequeRuns) realTimeQueueInfo = ("Queue", realTimeQueueRuns, realTimeQueueTime0 / realTimeQueueRuns, realTimeQueueMem0 / realTimeQueueRuns) sequenceQueueInfo = ("Seq", sequenceQueueRuns, sequenceQueueTime0 / sequenceQueueRuns, sequenceQueueMem0 / sequenceQueueRuns) @@ -138,27 +118,11 @@ renderResults (ByteString.putStr . Text.Builder.runBuilderBS) $ Text.Builder.fromText (Text.replicate 80 "=") <> newline - <> renderComparison ephemeralQueueInfo amortizedDequeInfo - <> renderComparison realTimeDequeInfo amortizedDequeInfo - <> renderComparison realTimeQueueInfo amortizedDequeInfo - <> renderComparison sequenceQueueInfo amortizedDequeInfo - <> Text.Builder.fromText (Text.replicate 40 "-") - <> newline - <> renderComparison amortizedDequeInfo ephemeralQueueInfo - <> renderComparison realTimeDequeInfo ephemeralQueueInfo <> renderComparison realTimeQueueInfo ephemeralQueueInfo <> renderComparison sequenceQueueInfo ephemeralQueueInfo <> Text.Builder.fromText (Text.replicate 40 "-") <> newline - <> renderComparison amortizedDequeInfo realTimeDequeInfo - <> renderComparison ephemeralQueueInfo realTimeDequeInfo - <> renderComparison realTimeQueueInfo realTimeDequeInfo - <> renderComparison sequenceQueueInfo realTimeDequeInfo - <> Text.Builder.fromText (Text.replicate 40 "-") - <> newline - <> renderComparison amortizedDequeInfo realTimeQueueInfo <> renderComparison ephemeralQueueInfo realTimeQueueInfo - <> renderComparison realTimeDequeInfo realTimeQueueInfo <> renderComparison sequenceQueueInfo realTimeQueueInfo where newline = Text.Builder.fromChar '\n' diff --git a/src/Deque.hs b/src/Deque.hs deleted file mode 100644 index 08a7350..0000000 --- a/src/Deque.hs +++ /dev/null @@ -1,216 +0,0 @@ --- | A double-ended queue data structure with \(\mathcal{O}(1)^*\) (amortized) operations, as described in --- --- * Okasaki, Chris. \"Simple and efficient purely functional queues and deques.\" /Journal of functional programming/ 5.4 (1995): 583-592. --- * Okasaki, Chris. /Purely Functional Data Structures/. Diss. Princeton University, 1996. -module Deque - ( -- * Deque - Deque (Empty, Front, Back), - - -- ** Initialization - empty, - - -- * Basic interface - enqueue, - enqueueFront, - dequeue, - dequeueBack, - - -- * Queries - isEmpty, - length, - - -- * Transformations - map, - traverse, - reverse, - - -- * List conversions - fromList, - toList, - ) -where - -import Data.Bits (unsafeShiftR) -import Data.Foldable qualified as Foldable -import Data.List qualified as List -import Data.Traversable qualified as Traversable -import Prelude hiding (drop, foldMap, length, map, reverse, span, take, traverse) - --- | A double-ended queue data structure with \(\mathcal{O}(1)^*\) (amortized) operations. -data Deque a - = Q - [a] - {-# UNPACK #-} !Int - [a] - {-# UNPACK #-} !Int - deriving stock (Functor) - -instance (Eq a) => Eq (Deque a) where - (==) :: Deque a -> Deque a -> Bool - xs == ys = - Deque.length xs == Deque.length ys && Deque.toList xs == Deque.toList ys - -instance Foldable Deque where - foldMap :: (Monoid m) => (a -> m) -> Deque a -> m - foldMap f = - go - where - go = \case - Empty -> mempty - Front x xs -> f x <> go xs - - elem :: (Eq a) => a -> Deque a -> Bool - elem x (Q xs _ ys _) = - List.elem x xs || List.elem x ys - - length :: Deque a -> Int - length = - Deque.length - - null :: Deque a -> Bool - null = - isEmpty - - toList :: Deque a -> [a] - toList = - Deque.toList - -instance Monoid (Deque a) where - mempty :: Deque a - mempty = - empty - --- | \(\mathcal{O}(n)\), where \(n\) is the size of the smaller argument. -instance Semigroup (Deque a) where - (<>) :: Deque a -> Deque a -> Deque a - xs <> ys - -- Either enqueue xs at the front of ys, or ys onto the back of xs, depending on which one would be fewer enqueues. - | Deque.length xs < Deque.length ys = prepend xs ys - | otherwise = append xs ys - -instance (Show a) => Show (Deque a) where - show :: Deque a -> String - show = - show . Deque.toList - -instance Traversable Deque where - traverse :: (Applicative f) => (a -> f b) -> Deque a -> f (Deque b) - traverse = - Deque.traverse - --- | An empty double-ended queue. -pattern Empty :: Deque a -pattern Empty <- - (dequeue -> Nothing) - --- | The front of a double-ended queue, and the rest of it. -pattern Front :: a -> Deque a -> Deque a -pattern Front x xs <- - (dequeue -> Just (x, xs)) - --- | The back of a double-ended queue, and the rest of it. -pattern Back :: Deque a -> a -> Deque a -pattern Back xs x <- - (dequeueBack -> Just (xs, x)) - -{-# COMPLETE Empty, Front #-} - -{-# COMPLETE Empty, Back #-} - --- Deque smart constructor, to use when it is possible the front list is too long. -makeDeque1 :: [a] -> Int -> [a] -> Int -> Deque a -makeDeque1 xs xlen ys ylen - | xlen > (3 * ylen + 1) = Q (List.take xlen1 xs) xlen1 (ys ++ List.reverse (List.drop xlen1 xs)) (xlen + ylen - xlen1) - | otherwise = Q xs xlen ys ylen - where - xlen1 = (xlen + ylen) `unsafeShiftR` 1 - --- Deque smart constructor, to use when it is possible the back list is too long. -makeDeque2 :: [a] -> Int -> [a] -> Int -> Deque a -makeDeque2 xs xlen ys ylen - | ylen > (3 * xlen + 1) = Q (xs ++ List.reverse (List.drop ylen1 ys)) xlen1 (List.take ylen1 ys) ylen1 - | otherwise = Q xs xlen ys ylen - where - xlen1 = (xlen + ylen) `unsafeShiftR` 1 - ylen1 = xlen + ylen - xlen1 - --- | An empty double-ended queue. -empty :: Deque a -empty = - Q [] 0 [] 0 - --- | \(\mathcal{O}(1)^*\). Enqueue an element at the back of a double-ended queue. -enqueue :: a -> Deque a -> Deque a -enqueue y (Q xs xlen ys ylen) = - makeDeque2 xs xlen (y : ys) (ylen + 1) - --- | \(\mathcal{O}(1)^*\). Enqueue an element at the front of a double-ended queue. -enqueueFront :: a -> Deque a -> Deque a -enqueueFront x (Q xs xlen ys ylen) = - makeDeque1 (x : xs) (xlen + 1) ys ylen - --- | \(\mathcal{O}(1)\) front, \(\mathcal{O}(1)^*\) rest. Dequeue an element from the front of a double-ended queue. -dequeue :: Deque a -> Maybe (a, Deque a) -dequeue = \case - Q [] _ [] _ -> Nothing - Q [] _ (y : _) _ -> Just (y, empty) - Q (x : xs) xlen ys ylen -> Just (x, makeDeque2 xs (xlen - 1) ys ylen) - --- | \(\mathcal{O}(1)\) back, \(\mathcal{O}(1)^*\) rest. Dequeue an element from of the back of a double-ended queue. -dequeueBack :: Deque a -> Maybe (Deque a, a) -dequeueBack = \case - Q [] _ [] _ -> Nothing - Q (x : _) _ [] _ -> Just (empty, x) - Q xs xlen (y : ys) ylen -> Just (makeDeque1 xs xlen ys (ylen - 1), y) - --- | \(\mathcal{O}(1)\). Is a double-ended queue empty? -isEmpty :: Deque a -> Bool -isEmpty (Q _ xlen _ ylen) = - xlen == 0 && ylen == 0 - --- | \(\mathcal{O}(1)\). How many elements are in a double-ended queue? -length :: Deque a -> Int -length (Q _ xlen _ ylen) = - xlen + ylen - --- | \(\mathcal{O}(1)\). Reverse a double-ended queue. -reverse :: Deque a -> Deque a -reverse (Q xs xlen ys ylen) = - Q ys ylen xs xlen - -append :: Deque a -> Deque a -> Deque a -append xs Empty = xs -append xs (Front y ys) = append (enqueue y xs) ys - -prepend :: Deque a -> Deque a -> Deque a -prepend Empty ys = ys -prepend (Back xs x) ys = prepend xs (enqueueFront x ys) - --- | \(\mathcal{O}(n)\). Apply a function to every element in a double-ended queue. -map :: (a -> b) -> Deque a -> Deque b -map = - fmap - --- | \(\mathcal{O}(n)\). Apply a function to every element in a double-ended queue. -traverse :: (Applicative f) => (a -> f b) -> Deque a -> f (Deque b) -traverse f (Q xs xlen ys ylen) = - (\xs1 ys1 -> Q xs1 xlen ys1 ylen) <$> Traversable.traverse f xs <*> backwards ys - where - backwards = - go - where - go = \case - [] -> pure [] - z : zs -> flip (:) <$> go zs <*> f z - --- | \(\mathcal{O}(n)\). Construct a double-ended queue from a list. The head of the list corresponds to the front of --- the double-ended queue. -fromList :: [a] -> Deque a -fromList = - foldr enqueueFront empty - --- | \(\mathcal{O}(n)\). Construct a list from a double-ended queue. The head of the list corresponds to the front of --- the double-ended queue. -toList :: Deque a -> [a] -toList (Q xs _ ys _) = - xs ++ List.reverse ys diff --git a/src/Deque/RealTime.hs b/src/Deque/RealTime.hs deleted file mode 100644 index 1346df0..0000000 --- a/src/Deque/RealTime.hs +++ /dev/null @@ -1,258 +0,0 @@ --- It seems this is only needed on GHC <= 9.4 -{-# LANGUAGE UndecidableInstances #-} - --- | A double-ended queue data structure with \(\mathcal{O}(1)\) (worst-case) operations, as described in --- --- * Okasaki, Chris. \"Simple and efficient purely functional queues and deques.\" /Journal of functional programming/ 5.4 (1995): 583-592. --- * Okasaki, Chris. /Purely Functional Data Structures/. Diss. Princeton University, 1996. -module Deque.RealTime - ( -- * Real-time deque - RealTimeDeque (Empty, Front, Back), - - -- ** Initialization - empty, - - -- * Basic interface - enqueue, - enqueueFront, - dequeue, - dequeueBack, - - -- * Queries - isEmpty, - length, - - -- * Transformations - map, - traverse, - reverse, - - -- * List conversions - fromList, - toList, - ) -where - -import Data.Bits (unsafeShiftR) -import Data.Foldable qualified as Foldable -import Data.List qualified as List -import Data.Traversable qualified as Traversable -import GHC.Exts (Any) -import Unsafe.Coerce (unsafeCoerce) -import Prelude hiding (drop, foldMap, length, map, reverse, take, traverse) - --- | A double-ended queue data structure with \(\mathcal{O}(1)\) (worst-case) operations. -data RealTimeDeque a - = Q - [a] - {-# UNPACK #-} !Int - Schedule - [a] - {-# UNPACK #-} !Int - Schedule - -instance (Eq a) => Eq (RealTimeDeque a) where - (==) :: RealTimeDeque a -> RealTimeDeque a -> Bool - xs == ys = - Deque.RealTime.length xs == Deque.RealTime.length ys - && Deque.RealTime.toList xs == Deque.RealTime.toList ys - -instance Foldable RealTimeDeque where - foldMap :: (Monoid m) => (a -> m) -> RealTimeDeque a -> m - foldMap f = - go - where - go = \case - Empty -> mempty - Front x xs -> f x <> go xs - - elem :: (Eq a) => a -> RealTimeDeque a -> Bool - elem x (Q xs _ _ ys _ _) = - List.elem x xs || List.elem x ys - - length :: RealTimeDeque a -> Int - length = - Deque.RealTime.length - - null :: RealTimeDeque a -> Bool - null = - isEmpty - - toList :: RealTimeDeque a -> [a] - toList = - Deque.RealTime.toList - -instance Functor RealTimeDeque where - fmap :: (a -> b) -> RealTimeDeque a -> RealTimeDeque b - fmap = - map - -instance Monoid (RealTimeDeque a) where - mempty :: RealTimeDeque a - mempty = - empty - --- | \(\mathcal{O}(n)\), where \(n\) is the size of the smaller argument. -instance Semigroup (RealTimeDeque a) where - (<>) :: RealTimeDeque a -> RealTimeDeque a -> RealTimeDeque a - xs <> ys - -- Either enqueue xs at the front of ys, or ys onto the back of xs, depending on which one would be fewer enqueues. - | Deque.RealTime.length xs < Deque.RealTime.length ys = prepend xs ys - | otherwise = append xs ys - -instance (Show a) => Show (RealTimeDeque a) where - show :: RealTimeDeque a -> String - show = - show . Deque.RealTime.toList - -instance Traversable RealTimeDeque where - traverse :: (Applicative f) => (a -> f b) -> RealTimeDeque a -> f (RealTimeDeque b) - traverse = - Deque.RealTime.traverse - --- | An empty double-ended queue. -pattern Empty :: RealTimeDeque a -pattern Empty <- - (dequeue -> Nothing) - --- | The front of a double-ended queue, and the rest of it. -pattern Front :: a -> RealTimeDeque a -> RealTimeDeque a -pattern Front x xs <- - (dequeue -> Just (x, xs)) - --- | The back of a double-ended queue, and the rest of it. -pattern Back :: RealTimeDeque a -> a -> RealTimeDeque a -pattern Back xs x <- - (dequeueBack -> Just (xs, x)) - -{-# COMPLETE Empty, Front #-} - -{-# COMPLETE Empty, Back #-} - --- Deque smart constructor, to use when it is possible the front list is too long. -makeDeque1 :: [a] -> Int -> [Any] -> [a] -> Int -> [Any] -> RealTimeDeque a -makeDeque1 xs xlen xc ys ylen yc - | xlen > (3 * ylen + 1) = - let xs1 = List.take xlen1 xs - ys1 = rotate1 xlen1 ys xs - in Q xs1 xlen1 (schedule xs1) ys1 (xlen + ylen - xlen1) (schedule ys1) - | otherwise = Q xs xlen xc ys ylen yc - where - xlen1 = (xlen + ylen) `unsafeShiftR` 1 - --- Deque smart constructor, to use when it is possible the back list is too long. -makeDeque2 :: [a] -> Int -> [Any] -> [a] -> Int -> [Any] -> RealTimeDeque a -makeDeque2 xs xlen xc ys ylen yc - | ylen > (3 * xlen + 1) = - let xs1 = rotate1 ylen1 xs ys - ys1 = List.take ylen1 ys - in Q xs1 xlen1 (schedule xs1) ys1 ylen1 (schedule ys1) - | otherwise = Q xs xlen xc ys ylen yc - where - xlen1 = (xlen + ylen) `unsafeShiftR` 1 - ylen1 = xlen + ylen - xlen1 - -rotate1 :: Int -> [a] -> [a] -> [a] -rotate1 i (x : xs) ys | i >= 3 = x : rotate1 (i - 3) xs (List.drop 3 ys) -rotate1 i xs ys = rotate2 (List.drop i ys) [] xs - -rotate2 :: [a] -> [a] -> [a] -> [a] -rotate2 ys zs = \case - [] -> List.reverse ys ++ zs - x : xs -> x : rotate2 (List.drop 3 ys) (List.reverse (List.take 3 ys) ++ zs) xs - --- | An empty double-ended queue. -empty :: RealTimeDeque a -empty = - Q [] 0 [] [] 0 [] - --- | \(\mathcal{O}(1)\). Enqueue an element at the back of a double-ended queue. -enqueue :: a -> RealTimeDeque a -> RealTimeDeque a -enqueue y (Q xs xlen xc ys ylen yc) = - makeDeque2 xs xlen (execute1 xc) (y : ys) (ylen + 1) (execute1 yc) - --- | \(\mathcal{O}(1)\). Enqueue an element at the front of a double-ended queue. -enqueueFront :: a -> RealTimeDeque a -> RealTimeDeque a -enqueueFront x (Q xs xlen xc ys ylen yc) = - makeDeque1 (x : xs) (xlen + 1) (execute1 xc) ys ylen (execute1 yc) - --- | \(\mathcal{O}(1)\) front, \(\mathcal{O}(1)\) rest. Dequeue an element from the front of a double-ended queue. -dequeue :: RealTimeDeque a -> Maybe (a, RealTimeDeque a) -dequeue = \case - Q [] _ _ [] _ _ -> Nothing - Q [] _ _ (y : _) _ _ -> Just (y, empty) - Q (x : xs) xlen xc ys ylen yc -> Just (x, makeDeque2 xs (xlen - 1) (execute2 xc) ys ylen (execute2 yc)) - --- | \(\mathcal{O}(1)\) back, \(\mathcal{O}(1)\) rest. Dequeue an element from of the back of a double-ended queue. -dequeueBack :: RealTimeDeque a -> Maybe (RealTimeDeque a, a) -dequeueBack = \case - Q [] _ _ [] _ _ -> Nothing - Q (x : _) _ _ [] _ _ -> Just (empty, x) - Q xs xlen xc (y : ys) ylen yc -> Just (makeDeque1 xs xlen (execute2 xc) ys (ylen - 1) (execute2 yc), y) - --- | \(\mathcal{O}(1)\). Is a double-ended queue empty? -isEmpty :: RealTimeDeque a -> Bool -isEmpty (Q _ xlen _ _ ylen _) = - xlen == 0 && ylen == 0 - --- | \(\mathcal{O}(1)\). How many elements are in a double-ended queue? -length :: RealTimeDeque a -> Int -length (Q _ xlen _ _ ylen _) = - xlen + ylen - --- | \(\mathcal{O}(1)\). Reverse a double-ended queue. -reverse :: RealTimeDeque a -> RealTimeDeque a -reverse (Q xs xlen xc ys ylen yc) = - Q ys ylen yc xs xlen xc - --- O(ys). @append xs ys@ enqueues @ys@ onto the back of @ys@. -append :: RealTimeDeque a -> RealTimeDeque a -> RealTimeDeque a -append xs Empty = xs -append xs (Front y ys) = append (enqueue y xs) ys - --- O(xs). @prepend xs ys@ enqueues @xs@ onto the front of @ys@. -prepend :: RealTimeDeque a -> RealTimeDeque a -> RealTimeDeque a -prepend Empty ys = ys -prepend (Back xs x) ys = prepend xs (enqueueFront x ys) - --- | \(\mathcal{O}(n)\). Apply a function to every element in a double-ended queue. -map :: (a -> b) -> RealTimeDeque a -> RealTimeDeque b -map f = - fromList . List.map f . toList - --- | \(\mathcal{O}(n)\). Apply a function to every element in a double-ended queue. -traverse :: (Applicative f) => (a -> f b) -> RealTimeDeque a -> f (RealTimeDeque b) -traverse f = - fmap fromList . Traversable.traverse f . toList - --- | \(\mathcal{O}(n)\). Construct a double-ended queue from a list. The head of the list corresponds to the front of --- the double-ended queue. -fromList :: [a] -> RealTimeDeque a -fromList = - foldr enqueueFront empty - --- | \(\mathcal{O}(n)\). Construct a list from a double-ended queue. The head of the list corresponds to the front of --- the double-ended queue. -toList :: RealTimeDeque a -> [a] -toList = - List.unfoldr dequeue - ------------------------------------------------------------------------------------------------------------------------- --- Schedule utils - -type Schedule = - [Any] - -schedule :: [a] -> Schedule -schedule = - unsafeCoerce - -execute1 :: Schedule -> Schedule -execute1 = \case - [] -> [] - _ : xs -> xs - -execute2 :: Schedule -> Schedule -execute2 = \case - _ : _ : xs -> xs - _ -> [] diff --git a/test/Main.hs b/test/Main.hs index ab6021d..1827c6f 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -7,8 +7,6 @@ import Data.List qualified as List import Data.Sequence (Seq) import Data.Sequence qualified as Seq import Data.Word (Word8) -import Deque qualified -import Queue.Ephemeral qualified import Hedgehog ( Gen, Group (Group), @@ -24,7 +22,7 @@ import Hedgehog.Gen qualified as Gen import Hedgehog.Main qualified as Hedgehog (defaultMain) import Hedgehog.Range qualified as Range import Queue qualified -import Deque.RealTime qualified +import Queue.Ephemeral qualified main :: IO () main = do @@ -44,7 +42,6 @@ tests = list <- forAll generateList test realTimeQueueIface list test ephemeralQueueIface list - test realTimeDequeIface list ), ( "enqueue/enqueueFront/dequeue state machine tests", do @@ -52,13 +49,6 @@ tests = let expected = applyQueueActions seqIface actions applyQueueActions ephemeralQueueIface actions === expected applyQueueActions realTimeQueueIface actions === expected - ), - ( "`enqueue/enqueueFront/dequeue/dequeueBack state machine tests`", - do - actions <- forAll (generateDequeActions 1000) - let expected = applyDequeActions seqIface actions - applyDequeActions amortizedDequeIface actions === expected - applyDequeActions realTimeDequeIface actions === expected ) ] @@ -71,7 +61,6 @@ data Iface a = forall queue. enqueue :: a -> queue a -> queue a, dequeue :: queue a -> Maybe (a, queue a), enqueueFront :: a -> queue a -> queue a, - dequeueBack :: queue a -> Maybe (queue a, a), toList :: queue a -> [a], fromList :: [a] -> queue a } @@ -83,7 +72,6 @@ realTimeQueueIface = Queue.enqueue Queue.dequeue Queue.enqueueFront - (error "Queue has no dequeueBack") Queue.toList Queue.fromList @@ -94,28 +82,12 @@ ephemeralQueueIface = Queue.Ephemeral.enqueue Queue.Ephemeral.dequeue Queue.Ephemeral.enqueueFront - (error "EphemeralQueue has no dequeueBack") Queue.Ephemeral.toList Queue.Ephemeral.fromList -amortizedDequeIface :: Iface a -amortizedDequeIface = - Iface Deque.empty Deque.enqueue Deque.dequeue Deque.enqueueFront Deque.dequeueBack Deque.toList Deque.fromList - -realTimeDequeIface :: Iface a -realTimeDequeIface = - Iface - Deque.RealTime.empty - Deque.RealTime.enqueue - Deque.RealTime.dequeue - Deque.RealTime.enqueueFront - Deque.RealTime.dequeueBack - Deque.RealTime.toList - Deque.RealTime.fromList - seqIface :: Iface a seqIface = - Iface Seq.empty seqEnqueue seqDequeue seqEnqueueFront seqDequeueBack Foldable.toList Seq.fromList + Iface Seq.empty seqEnqueue seqDequeue seqEnqueueFront Foldable.toList Seq.fromList where seqEnqueue :: a -> Seq a -> Seq a seqEnqueue = @@ -130,11 +102,6 @@ seqIface = seqEnqueueFront = (Seq.<|) - seqDequeueBack :: Seq a -> Maybe (Seq a, a) - seqDequeueBack = \case - Seq.Empty -> Nothing - xs Seq.:|> x -> Just (xs, x) - ------------------------------------------------------------------------------------------------------------------------ -- Generators @@ -171,41 +138,6 @@ applyQueueActions Iface {empty, enqueue, dequeue, enqueueFront, toList} = Nothing -> (Nothing : dequeues, queue) Just (x, queue1) -> (Just x : dequeues, queue1) -data DequeAction - = DequeActionEnqueue !Word8 - | DequeActionEnqueueFront !Word8 - | DequeActionDequeue - | DequeActionDequeueBack - deriving stock (Show) - -generateDequeActions :: Int -> Gen [DequeAction] -generateDequeActions n = - Gen.list - (Range.linear 0 n) - ( Gen.frequency - [ (10, DequeActionEnqueue <$> generateWord8), - (10, DequeActionEnqueueFront <$> generateWord8), - (2, pure DequeActionDequeue), - (2, pure DequeActionDequeueBack) - ] - ) - -applyDequeActions :: Iface Word8 -> [DequeAction] -> ([Maybe Word8], [Word8]) -applyDequeActions Iface {empty, enqueue, dequeue, enqueueFront, dequeueBack, toList} = - second toList . List.foldl' apply ([], empty) - where - apply (dequeues, queue) = \case - DequeActionEnqueue x -> (dequeues, enqueue x queue) - DequeActionEnqueueFront x -> (dequeues, enqueueFront x queue) - DequeActionDequeue -> - case dequeue queue of - Nothing -> (Nothing : dequeues, queue) - Just (x, queue1) -> (Just x : dequeues, queue1) - DequeActionDequeueBack -> - case dequeueBack queue of - Nothing -> (Nothing : dequeues, queue) - Just (queue1, x) -> (Just x : dequeues, queue1) - generateWord8 :: Gen Word8 generateWord8 = Gen.word8 Range.constantBounded