Restructured project
This commit is contained in:
parent
f81f344d45
commit
178af73fc9
170
src/nimdeque.nim
170
src/nimdeque.nim
|
@ -11,173 +11,9 @@
|
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import strformat
|
||||
import private/queues/linked
|
||||
|
||||
|
||||
type
|
||||
DequeNode*[T] = ref object
|
||||
next: DequeNode[T]
|
||||
prev: DequeNode[T]
|
||||
val: T
|
||||
LinkedDeque*[T] = ref object
|
||||
## A collection type
|
||||
## optimized for ~O(1)
|
||||
## access at both ends
|
||||
## based on a doubly
|
||||
## linked list
|
||||
head: DequeNode[T]
|
||||
tail: DequeNode[T]
|
||||
size: int
|
||||
|
||||
|
||||
proc newDequeNode[T](val: T): DequeNode[T] =
|
||||
## Internal proc to
|
||||
## initialize an element
|
||||
## for the LinkedQueue
|
||||
## object
|
||||
new(result)
|
||||
result.val = val
|
||||
|
||||
|
||||
proc newLinkedDeque*[T]: LinkedDeque[T] =
|
||||
## Initializes a new, empty
|
||||
## LinkedDeque object
|
||||
new(result)
|
||||
result.size = 0
|
||||
|
||||
|
||||
proc add*[T](self: LinkedDeque[T], val: T) =
|
||||
## Appends an element at the end
|
||||
## of the queue
|
||||
var newNode = newDequeNode(val)
|
||||
if self.head == nil:
|
||||
self.head = newNode
|
||||
else:
|
||||
newNode.prev = self.tail
|
||||
self.tail.next = newNode
|
||||
self.tail = newNode
|
||||
inc(self.size)
|
||||
|
||||
|
||||
proc addLeft*[T](self: LinkedDeque[T], val: T) =
|
||||
## Behaves like add(), but inserts a
|
||||
## value at the beginning instead
|
||||
## of at the end. This operation
|
||||
## takes constant time
|
||||
var node = newDequeNode(val)
|
||||
var head = self.head
|
||||
self.head = node
|
||||
self.head.next = head
|
||||
head.prev = node
|
||||
inc(self.size)
|
||||
|
||||
|
||||
proc len*[T](self: LinkedDeque[T]): int =
|
||||
## Returns the length of the deque.
|
||||
## This operation takes constant time
|
||||
result = self.size
|
||||
|
||||
|
||||
proc high*[T](self: LinkedDeque[T]): int =
|
||||
## Returns the index of the last
|
||||
## element in the deque and -1 if
|
||||
## the deque is empty
|
||||
result = self.len() - 1
|
||||
|
||||
|
||||
proc getNode[T](self: LinkedDeque[T], i: int): DequeNode[T] =
|
||||
## Low level method for indexing and getting
|
||||
## a node object back
|
||||
if self.high() == -1:
|
||||
raise newException(IndexDefect, "LinkedDeque is empty")
|
||||
elif i > self.high():
|
||||
raise newException(IndexDefect, &"{i} notin 0..{self.high()}")
|
||||
var pos = 0
|
||||
if i < self.high() div 2:
|
||||
# If we're taking an element
|
||||
# in the first half of the
|
||||
# queue, we start from the
|
||||
# beginning
|
||||
result = self.head
|
||||
while pos < i:
|
||||
result = result.next
|
||||
inc(pos)
|
||||
else:
|
||||
# Otherwise, we start from
|
||||
# the end and go backwards
|
||||
# at the chosen position
|
||||
result = self.tail
|
||||
pos = self.high()
|
||||
while pos > i:
|
||||
result = result.prev
|
||||
dec(pos)
|
||||
|
||||
|
||||
proc `[]`*[T](self: LinkedDeque[T], i: int): T =
|
||||
## Implements indexing into the queue
|
||||
result = self.getNode(i).val
|
||||
|
||||
|
||||
proc `[]=`*[T](self: LinkedDeque[T], i: int, val: T) =
|
||||
## Sets element at position i
|
||||
## to the given value
|
||||
self[i].val = val
|
||||
|
||||
|
||||
proc pop*[T](self: LinkedDeque[T], pos: int = 0): T =
|
||||
## Pops an element off the queue
|
||||
## at the given index (default 0).
|
||||
## The queue is optimized for popping
|
||||
## and appending in roughly constant time
|
||||
## at both ends, so this is quite fast
|
||||
## if the operation is nears the ends of
|
||||
## the iterable. The popped item is returned
|
||||
var node = self.getNode(pos)
|
||||
if pos == 0:
|
||||
self.head = self.head.next
|
||||
elif pos == self.high():
|
||||
self.tail = self.tail.prev
|
||||
else:
|
||||
node.prev.next = node.next
|
||||
result = node.val
|
||||
dec(self.size)
|
||||
|
||||
|
||||
iterator items*[T](self: LinkedDeque[T]): T =
|
||||
## Implements the iteration protocol
|
||||
## for the queue
|
||||
var node = self.head
|
||||
while node != nil:
|
||||
yield node.val
|
||||
node = node.next
|
||||
|
||||
|
||||
iterator pairs*[T](self: LinkedDeque[T]): auto =
|
||||
## Implements pairwise iteration with
|
||||
## the index and the element at that
|
||||
## index
|
||||
var i = 0
|
||||
for element in self:
|
||||
yield (i, element)
|
||||
inc(i)
|
||||
|
||||
|
||||
iterator reversed*[T](self: LinkedDeque[T]): T =
|
||||
## Same as self.items(), but starts at
|
||||
## the end and yields the contents of
|
||||
## the queue backwards
|
||||
var node = self.tail
|
||||
while node != nil:
|
||||
yield node
|
||||
node = node.prev
|
||||
|
||||
|
||||
proc `$`*[T](self: LinkedDeque[T]): string =
|
||||
## Returns a string representation
|
||||
## of the deque
|
||||
result = "deque(["
|
||||
for i, item in self:
|
||||
result &= $item
|
||||
if i < self.high():
|
||||
result &= ", "
|
||||
result &= "])"
|
||||
export newLinkedDeque
|
||||
export LinkedDeque
|
|
@ -0,0 +1,192 @@
|
|||
# Copyright 2022 Mattia Giambirtone
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import strformat
|
||||
|
||||
|
||||
type
|
||||
DequeNode*[T] = ref object
|
||||
next: DequeNode[T]
|
||||
prev: DequeNode[T]
|
||||
val: T
|
||||
LinkedDeque*[T] = ref object
|
||||
## A collection type
|
||||
## optimized for ~O(1)
|
||||
## access at both ends
|
||||
## based on a doubly
|
||||
## linked list
|
||||
head: DequeNode[T]
|
||||
tail: DequeNode[T]
|
||||
size: int
|
||||
|
||||
|
||||
proc newDequeNode[T](val: T): DequeNode[T] =
|
||||
## Internal proc to
|
||||
## initialize an element
|
||||
## for the LinkedQueue
|
||||
## object
|
||||
new(result)
|
||||
result.val = val
|
||||
|
||||
|
||||
proc newLinkedDeque*[T]: LinkedDeque[T] =
|
||||
## Initializes a new, empty
|
||||
## LinkedDeque object
|
||||
new(result)
|
||||
result.size = 0
|
||||
|
||||
|
||||
proc add*[T](self: LinkedDeque[T], val: T) =
|
||||
## Appends an element at the end
|
||||
## of the queue
|
||||
var newNode = newDequeNode(val)
|
||||
if self.head == nil:
|
||||
self.head = newNode
|
||||
else:
|
||||
newNode.prev = self.tail
|
||||
self.tail.next = newNode
|
||||
self.tail = newNode
|
||||
inc(self.size)
|
||||
|
||||
|
||||
proc addLeft*[T](self: LinkedDeque[T], val: T) =
|
||||
## Behaves like add(), but inserts a
|
||||
## value at the beginning instead
|
||||
## of at the end. This operation
|
||||
## takes constant time
|
||||
var node = newDequeNode(val)
|
||||
var head = self.head
|
||||
self.head = node
|
||||
self.head.next = head
|
||||
head.prev = node
|
||||
inc(self.size)
|
||||
|
||||
|
||||
proc len*[T](self: LinkedDeque[T]): int =
|
||||
## Returns the length of the deque.
|
||||
## This operation takes constant time
|
||||
result = self.size
|
||||
|
||||
|
||||
proc high*[T](self: LinkedDeque[T]): int =
|
||||
## Returns the index of the last
|
||||
## element in the deque and -1 if
|
||||
## the deque is empty
|
||||
result = self.len() - 1
|
||||
|
||||
|
||||
proc getNode[T](self: LinkedDeque[T], i: int): DequeNode[T] =
|
||||
## Low level method for indexing and getting
|
||||
## a node object back
|
||||
if self.high() == -1:
|
||||
raise newException(IndexDefect, "LinkedDeque is empty")
|
||||
elif i > self.high():
|
||||
raise newException(IndexDefect, &"{i} notin 0..{self.high()}")
|
||||
var pos = 0
|
||||
if i < self.high() div 2:
|
||||
# If we're taking an element
|
||||
# in the first half of the
|
||||
# queue, we start from the
|
||||
# beginning
|
||||
result = self.head
|
||||
while pos < i:
|
||||
result = result.next
|
||||
inc(pos)
|
||||
else:
|
||||
# Otherwise, we start from
|
||||
# the end and go backwards
|
||||
# at the chosen position
|
||||
result = self.tail
|
||||
pos = self.high()
|
||||
while pos > i:
|
||||
result = result.prev
|
||||
dec(pos)
|
||||
|
||||
|
||||
proc `[]`*[T](self: LinkedDeque[T], i: int): T =
|
||||
## Implements indexing into the queue
|
||||
result = self.getNode(i).val
|
||||
|
||||
|
||||
proc `[]=`*[T](self: LinkedDeque[T], i: int, val: T) =
|
||||
## Sets element at position i
|
||||
## to the given value
|
||||
self[i].val = val
|
||||
|
||||
|
||||
proc pop*[T](self: LinkedDeque[T], pos: int = 0): T =
|
||||
## Pops an element off the queue
|
||||
## at the given index (default 0).
|
||||
## The queue is optimized for popping
|
||||
## and appending in roughly constant time
|
||||
## at both ends, so this is quite fast
|
||||
## if the operation is nears the ends of
|
||||
## the iterable. The popped item is returned
|
||||
var node = self.getNode(pos)
|
||||
if pos == 0:
|
||||
self.head = self.head.next
|
||||
elif pos == self.high():
|
||||
self.tail = self.tail.prev
|
||||
else:
|
||||
node.prev.next = node.next
|
||||
result = node.val
|
||||
dec(self.size)
|
||||
|
||||
|
||||
iterator items*[T](self: LinkedDeque[T]): T =
|
||||
## Implements the iteration protocol
|
||||
## for the queue
|
||||
var node = self.head
|
||||
while node != nil:
|
||||
yield node.val
|
||||
node = node.next
|
||||
|
||||
|
||||
iterator pairs*[T](self: LinkedDeque[T]): auto =
|
||||
## Implements pairwise iteration with
|
||||
## the index and the element at that
|
||||
## index
|
||||
var i = 0
|
||||
for element in self:
|
||||
yield (i, element)
|
||||
inc(i)
|
||||
|
||||
|
||||
iterator reversed*[T](self: LinkedDeque[T]): T =
|
||||
## Same as self.items(), but starts at
|
||||
## the end and yields the contents of
|
||||
## the queue backwards
|
||||
var node = self.tail
|
||||
while node != nil:
|
||||
yield node
|
||||
node = node.prev
|
||||
|
||||
|
||||
proc contains*[T](self: LinkedDeque[T], val: T): bool =
|
||||
## Returns if the given element is in
|
||||
## the deque
|
||||
for element in self:
|
||||
if element == val:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
proc `$`*[T](self: LinkedDeque[T]): string =
|
||||
## Returns a string representation
|
||||
## of the deque
|
||||
result = "deque(["
|
||||
for i, item in self:
|
||||
result &= $item
|
||||
if i < self.high():
|
||||
result &= ", "
|
||||
result &= "])"
|
|
@ -1,5 +1,18 @@
|
|||
# Copyright 2022 Mattia Giambirtone
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import strformat
|
||||
import src/nimdeque
|
||||
import ../src/nimdeque
|
||||
|
||||
|
||||
when isMainModule:
|
||||
|
@ -8,9 +21,12 @@ when isMainModule:
|
|||
echo &"Generating {size} values"
|
||||
for i in countup(0, size, 1):
|
||||
deque.add(i)
|
||||
echo "Checking values"
|
||||
echo "Checking iteration"
|
||||
for i in countup(0, size, 1):
|
||||
doAssert deque[i] == i
|
||||
echo "Checking contains"
|
||||
for i in countup(0, size, 1):
|
||||
doAssert i in deque
|
||||
echo "Popping off the head"
|
||||
doAssert deque.pop() == 0
|
||||
echo "Popping off the tail"
|
Loading…
Reference in New Issue