From 178af73fc9cbc43bc56755bdd5080c98f0b6cca2 Mon Sep 17 00:00:00 2001 From: Nocturn9x Date: Mon, 14 Mar 2022 11:07:46 +0100 Subject: [PATCH] Restructured project --- src/nimdeque.nim | 170 +--------------------------- src/private/queues/linked.nim | 192 ++++++++++++++++++++++++++++++++ example.nim => tests/linked.nim | 20 +++- 3 files changed, 213 insertions(+), 169 deletions(-) create mode 100644 src/private/queues/linked.nim rename example.nim => tests/linked.nim (54%) diff --git a/src/nimdeque.nim b/src/nimdeque.nim index e66830d..ceafac2 100644 --- a/src/nimdeque.nim +++ b/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 \ No newline at end of file diff --git a/src/private/queues/linked.nim b/src/private/queues/linked.nim new file mode 100644 index 0000000..116afca --- /dev/null +++ b/src/private/queues/linked.nim @@ -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 &= "])" \ No newline at end of file diff --git a/example.nim b/tests/linked.nim similarity index 54% rename from example.nim rename to tests/linked.nim index da921ed..9d1ad88 100644 --- a/example.nim +++ b/tests/linked.nim @@ -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"