Skip to content

Commit 122b46b

Browse files
committed
Adding Deques
1 parent ca6aeee commit 122b46b

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

linear-base.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ library
4040
Data.Bifunctor.Linear.Internal.Bifunctor
4141
Data.Bifunctor.Linear.Internal.SymmetricMonoidal
4242
Data.Bool.Linear
43+
Data.Deque.Mutable.Linear
4344
Data.Either.Linear
4445
Data.Functor.Linear
4546
Data.Functor.Linear.Internal.Functor

src/Data/Deque/Mutable/Linear.hs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE LinearTypes #-}
3+
{-# LANGUAGE LambdaCase #-}
4+
{-# LANGUAGE GADTs #-}
5+
{-# LANGUAGE NoImplicitPrelude #-}
6+
7+
-- | Mutable Linear Deque
8+
--
9+
-- This module provides a pure interface to a mutable deque.
10+
--
11+
-- It is designed to be imported qualfied:
12+
--
13+
-- > import qualfied Data.Deque.Mutable.Linear as Deque
14+
module Data.Deque.Mutable.Linear
15+
(
16+
-- * Allocation
17+
alloc
18+
, fromList
19+
-- * Querying
20+
, size
21+
, length
22+
, isFull
23+
, peekRight
24+
, peekLeft
25+
-- * Modification
26+
, addLeft
27+
, addRight
28+
, popLeft
29+
, popRight
30+
, map
31+
-- * Consumption
32+
, toList
33+
)
34+
where
35+
36+
import qualified Data.Array.Mutable.Linear as Array
37+
import Data.Unrestricted.Linear
38+
import Prelude.Linear hiding (length, map)
39+
import qualified Prelude
40+
import GHC.Stack
41+
42+
43+
-- # Types
44+
-------------------------------------------------------------------------------
45+
46+
data Deque a where
47+
Deque :: Int -> Maybe (Int,Int) -> !(Array.Array a) %1-> Deque a
48+
-- This is: Deque size (Maybe (left ptr,right ptr)) array
49+
--
50+
-- The deque is represented as a slice from the left pointer
51+
-- moving forwards (and wrapping the end) to the right pointer.
52+
-- We are full when the right pointer is one less than the left pointer
53+
-- modulo the array size. An empty deque has no left or right pointers
54+
-- and we start a singleton deque with both pointers at 0.
55+
--
56+
-- Invariants:
57+
-- > If empty, we store no pointers
58+
-- > The first int is the size of the array
59+
-- > If we store pointers l,r then [arr[l], ... arr[r]] is the deque
60+
61+
data Dir = L | R -- A left or right direction
62+
63+
-- # Internal Helpers
64+
-------------------------------------------------------------------------------
65+
66+
-- Given a size, get the next index
67+
nextIx :: Int -> Int -> Int
68+
nextIx sz ix = (ix + 1) `mod` sz
69+
70+
-- Given a size, get the previous index
71+
prevIx :: Int -> Int -> Int
72+
prevIx sz ix = (ix - 1 + sz) `mod` sz
73+
74+
-- # Allocation
75+
-------------------------------------------------------------------------------
76+
77+
-- | Run a computation of an empty Deque with a given size
78+
alloc :: HasCallStack => Int -> (Deque a %1-> Ur b) %1-> Ur b
79+
alloc k f = Array.alloc k err $ \arr -> f (Deque k Nothing arr) where
80+
err = Prelude.error "Accessing error element of a collection!"
81+
82+
-- | Run a computation on a Deque that is deterimined by the given the list
83+
-- where we treat the start and end of the list as the left and right pointers,
84+
-- with the total capacity as the length of the list.
85+
fromList :: [a] -> (Deque a %1-> Ur b) %1-> Ur b
86+
fromList xs f | l <- Prelude.length xs = Array.fromList xs $
87+
\arr -> f (Deque l (Just (0,l-1)) arr)
88+
89+
90+
-- # Querying
91+
-------------------------------------------------------------------------------
92+
93+
-- | The total capacity of the Deque
94+
size :: Deque a %1-> (Ur Int, Deque a)
95+
size (Deque sz ptrs arr) = (Ur sz, Deque sz ptrs arr)
96+
97+
-- | The number of elements currently stored
98+
length :: Deque a %1-> (Ur Int, Deque a)
99+
length (Deque sz Nothing arr) = (Ur 0, Deque sz Nothing arr)
100+
length (Deque sz p@(Just (l,r)) arr)
101+
| l <= r = (Ur (1 + r - l), Deque sz p arr)
102+
| otherwise = (Ur (sz + 1 - l + r), Deque sz p arr)
103+
104+
-- | We are full if the length equals the size
105+
isFull :: Deque a %1-> (Ur Bool, Deque a)
106+
isFull d =
107+
length d & \(Ur len, Deque sz p arr) -> (Ur (len == sz), Deque sz p arr)
108+
109+
peek :: HasCallStack => Dir -> Deque a %1-> (Ur a, Deque a)
110+
peek _ (Deque _ Nothing arr) = error "Peeking in empty deque." $ arr
111+
peek dir (Deque sz p@(Just (l,r)) arr) = case dir of
112+
L -> Array.read arr l & \(x, arr') -> (x, Deque sz p arr')
113+
R -> Array.read arr r & \(x, arr') -> (x, Deque sz p arr')
114+
115+
-- | View the top of the left queue
116+
peekLeft :: HasCallStack => Deque a %1-> (Ur a, Deque a)
117+
peekLeft = peek L
118+
119+
-- | View the top of the right queue
120+
peekRight :: HasCallStack => Deque a %1-> (Ur a, Deque a)
121+
peekRight = peek R
122+
123+
124+
-- # Modification
125+
-------------------------------------------------------------------------------
126+
127+
add :: HasCallStack => Dir -> Deque a %1-> a -> Deque a
128+
add dir deq x = isFull deq & \case
129+
(Ur True, deq') -> error "Adding to a full deque" $ deq'
130+
(Ur False, Deque sz Nothing arr) ->
131+
Deque sz (Just (0,0)) (Array.write arr 0 x)
132+
(Ur False, Deque sz (Just (l,r)) arr) -> case dir of
133+
L -> Array.write arr (prevIx sz l) x &
134+
\arr' -> Deque sz (Just (prevIx sz l, r)) arr'
135+
R -> Array.write arr (nextIx sz r) x &
136+
\arr' -> Deque sz (Just (l,nextIx sz r)) arr'
137+
138+
-- | Add to the left queue
139+
addLeft :: HasCallStack => Deque a %1-> a -> Deque a
140+
addLeft = add L
141+
142+
-- | Add to the right queue
143+
addRight :: HasCallStack => Deque a %1-> a -> Deque a
144+
addRight = add R
145+
146+
pop :: HasCallStack => Dir -> Deque a %1-> Deque a
147+
pop _ (Deque _ Nothing arr) = error "Popping from empty deque." $ arr
148+
pop dir (Deque sz (Just (l,r)) arr)
149+
| l==r = Deque sz Nothing arr
150+
| otherwise = case dir of
151+
L -> Deque sz (Just (nextIx sz l, r)) arr
152+
R -> Deque sz (Just (l, prevIx sz r)) arr
153+
154+
-- | Remove the last added element from the left queue
155+
popLeft :: HasCallStack => Deque a %1-> Deque a
156+
popLeft = pop L
157+
158+
-- | Remove the last added element from the right queue
159+
popRight :: HasCallStack => Deque a %1-> Deque a
160+
popRight = pop R
161+
162+
-- Note: We can't use a Prelude.Functor nor a Data.Functor
163+
-- because the mapped function need not be linear but we must
164+
-- consume the Deque linearly. The types don't align.
165+
map :: (a -> b) -> Deque a %1-> Deque b
166+
map f (Deque sz p arr) = Deque sz p (Array.map f arr)
167+
168+
169+
-- # Consumption
170+
-------------------------------------------------------------------------------
171+
172+
-- | Convert the Deque to a list where the first element is the left
173+
-- top and the last element is the right top
174+
toList :: Deque a %1-> Ur [a]
175+
toList (Deque _ Nothing arr) = lseq arr (Ur [])
176+
toList (Deque sz (Just (l0,r0)) arr) = loop l0 r0 [] arr where
177+
loop :: Int -> Int -> [a] -> Array.Array a %1-> Ur [a]
178+
loop l r xs arr0
179+
| l==r = Array.read arr0 r & \(Ur a, arr1) -> lseq arr1 (Ur (a:xs))
180+
| otherwise = Array.read arr0 r & \(Ur a, arr1) ->
181+
loop l (prevIx sz r) (a:xs) arr1
182+

src/Data/Unrestricted/Internal/Instances.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ instance Dupable (Ur a) where
196196
instance Movable (Ur a) where
197197
move (Ur a) = Ur (Ur a)
198198

199+
deriving instance Prelude.Show a => Prelude.Show (Ur a)
200+
199201
instance Prelude.Functor Ur where
200202
fmap f (Ur a) = Ur (f a)
201203

0 commit comments

Comments
 (0)