-
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #22 from TheAlgorithms/fr33m0nk/add-dynamic-array
Adds Dynamic array implementation
- Loading branch information
Showing
3 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
(ns data-structures.dynamic-array.core | ||
(:refer-clojure :exclude [get set empty? remove pop])) | ||
|
||
;; See tests for the documentation on implementation | ||
(defprotocol IDynamicArray | ||
(get [this idx]) | ||
(set [this idx value]) | ||
(remove [this idx]) | ||
(empty? [this]) | ||
(length [this]) | ||
(next-idx [this]) | ||
(append [this value]) | ||
(pop [this])) | ||
|
||
(deftype DynamicArray [^:unsynchronized-mutable length | ||
^:unsynchronized-mutable next-idx | ||
^:unsynchronized-mutable array] | ||
IDynamicArray | ||
(get [_ idx] | ||
(assert (< -1 idx next-idx) "Invalid index value supplied") | ||
(aget array idx)) | ||
(set [_ idx value] | ||
(assert (<= 0 idx) "Invalid index value supplied") | ||
(when (or (= next-idx length) (> idx length)) | ||
(let [next-length (* idx 2) | ||
next-array (make-array Integer/TYPE next-length)] | ||
(doseq [i (range length)] | ||
(->> (aget array i) | ||
(aset next-array i))) | ||
(set! array next-array) | ||
(set! length next-length))) | ||
(aset array next-idx value) | ||
(set! next-idx (inc idx)) | ||
idx) | ||
(remove [_ idx] | ||
(assert (< -1 idx next-idx) "Invalid index value supplied") | ||
(let [next-next-idx (dec next-idx) | ||
popped-element (aget array idx)] | ||
(doseq [dest-idx (range idx next-idx) | ||
:let [source-idx (inc dest-idx)] | ||
:when (not= source-idx length)] | ||
(aset array dest-idx (aget array source-idx))) | ||
(set! next-idx next-next-idx) | ||
popped-element)) | ||
(empty? [_] | ||
(zero? next-idx)) | ||
(length [_] | ||
(alength array)) | ||
(next-idx [this] | ||
next-idx) | ||
(append [_ value] | ||
(when (= next-idx length) | ||
(let [next-length (* length 2) | ||
next-array (make-array Integer/TYPE next-length)] | ||
(doseq [i (range length)] | ||
(->> (aget array i) | ||
(aset next-array i))) | ||
(set! array next-array) | ||
(set! length next-length))) | ||
(let [old-capacity next-idx] | ||
(aset array next-idx value) | ||
(set! next-idx (inc next-idx)) | ||
old-capacity)) | ||
(pop [_] | ||
(assert (> next-idx 0) "Nothing to pop") | ||
(let [next-next-idx (dec next-idx) | ||
popped-element (aget array next-next-idx)] | ||
(aset array next-next-idx 0) | ||
(set! next-idx next-next-idx) | ||
popped-element)) | ||
Object | ||
(toString [_] | ||
(let [^StringBuilder sb (StringBuilder.)] | ||
(.append sb "[ ") | ||
(doseq [i (range next-idx)] | ||
(.append sb (aget array i)) | ||
(.append sb " ")) | ||
(.append sb "]") | ||
(.toString sb)))) | ||
|
||
(defn ->DynamicArray | ||
[initial-capacity] | ||
(when (>= 0 initial-capacity) | ||
(throw (IllegalArgumentException. "Invalid initial capacity"))) | ||
(DynamicArray. initial-capacity 0 (make-array Integer/TYPE initial-capacity))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
(ns data-structures.dynamic-array.core-test | ||
(:require [clojure.test :refer [deftest testing is]] | ||
[data-structures.dynamic-array.core :as da])) | ||
|
||
(deftest ->DynamicArray-test | ||
(testing "throws when invalid initial capacity is provided" | ||
(is (thrown? IllegalArgumentException (da/->DynamicArray 0))) | ||
(is (thrown? IllegalArgumentException (da/->DynamicArray -1))))) | ||
(deftest empty?-test | ||
(testing "return true if dynamic array is empty" | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(is (true? (da/empty? dynamic-array))))) | ||
|
||
(testing "return false if dynamic array is empty" | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(da/set dynamic-array (da/next-idx dynamic-array) 20) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 30) | ||
(is (false? (da/empty? dynamic-array)))))) | ||
|
||
(deftest length-test | ||
(testing "returns initial capacity till dynamic array is full" | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(is (= 3 (da/length dynamic-array))))) | ||
|
||
(testing "returns expanded capacity once storage demands exceeds initial capacity" | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(da/set dynamic-array (da/next-idx dynamic-array) 10) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 20) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 30) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 40) | ||
(is (= 6 (da/length dynamic-array)))))) | ||
|
||
(deftest next-idx-test | ||
(testing "returns initial index as the starting point when dynamic array is empty" | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(is (= 0 (da/next-idx dynamic-array))))) | ||
|
||
(testing "returns current filled index as the starting point when dynamic array is empty" | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(da/set dynamic-array (da/next-idx dynamic-array) 10) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 20) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 30) | ||
(is (= 3 (da/next-idx dynamic-array)))))) | ||
|
||
(deftest set-test | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(testing "throws Assertion error when index is less than 0" | ||
(is (thrown? AssertionError (da/set dynamic-array -1 10)))) | ||
|
||
(testing "stores the element within the dynamic array when a valid index is provided" | ||
(da/set dynamic-array (da/next-idx dynamic-array) 10) | ||
(is (false? (da/empty? dynamic-array))) | ||
(is (= 1 (da/next-idx dynamic-array)))) | ||
|
||
(testing "expands dynamic array to incorporate more elements" | ||
(da/set dynamic-array (da/next-idx dynamic-array) 20) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 30) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 40) | ||
(is (= 4 (da/next-idx dynamic-array))) | ||
(is (= 6 (da/length dynamic-array)))) | ||
|
||
(testing "expands dynamic array to incorporate for an arbitrary large index and sets next-idx accordingly" | ||
(da/set dynamic-array 60 40) | ||
(is (= 61 (da/next-idx dynamic-array)) "This behaviour causes fragmentation in the dynamic array") | ||
(is (= 120 (da/length dynamic-array)))))) | ||
|
||
(deftest get-test | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(testing "throws Assertion error when index is less than 0" | ||
(is (thrown? AssertionError (da/get dynamic-array -1)))) | ||
|
||
(testing "throws Assertion error when index is greater than next-index" | ||
(is (thrown? AssertionError (da/get dynamic-array (inc (da/next-idx dynamic-array)))))) | ||
|
||
(testing "fetches the content of the dynamic array stored at valid index" | ||
(da/set dynamic-array (da/next-idx dynamic-array) 40) | ||
(is (= 40 (da/get dynamic-array (dec (da/next-idx dynamic-array)))))))) | ||
|
||
(deftest remove-test | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(testing "throws Assertion error when index is less than 0" | ||
(is (thrown? AssertionError (da/remove dynamic-array -1)))) | ||
|
||
(testing "throws Assertion error when index is greater than next index" | ||
(is (thrown? AssertionError (da/remove dynamic-array (inc (da/next-idx dynamic-array)))))) | ||
|
||
(testing "removes the element from the dynamic array and returns it if the index is valid" | ||
(da/set dynamic-array (da/next-idx dynamic-array) 10) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 20) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 30) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 40) | ||
(is (= 4 (da/next-idx dynamic-array)) "Sanity check to ensure that next-index is correctly calculated") | ||
(is (= 30 (da/remove dynamic-array 2))) | ||
(is (= 3 (da/next-idx dynamic-array))) | ||
(is (= "[ 10 20 40 ]" (.toString dynamic-array)))))) | ||
|
||
(deftest append-test | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(testing "appends values to dynamic array" | ||
(da/set dynamic-array (da/next-idx dynamic-array) 10) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 20) | ||
(is (= 2 (da/next-idx dynamic-array))) | ||
(is (= 3 (da/length dynamic-array)))) | ||
|
||
(testing "expands dynamic array to allocate more elements" | ||
(da/set dynamic-array (da/next-idx dynamic-array) 30) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 40) | ||
(is (= 4 (da/next-idx dynamic-array))) | ||
(is (= 6 (da/length dynamic-array)))))) | ||
|
||
(deftest pop-test | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(testing "throws when the dynamic array is empty" | ||
(is (thrown? AssertionError (da/pop dynamic-array)))) | ||
|
||
(testing "pops the element from dynamic array and returns it" | ||
(da/set dynamic-array (da/next-idx dynamic-array) 10) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 20) | ||
(da/set dynamic-array (da/next-idx dynamic-array) 30) | ||
(is (= 30 (da/pop dynamic-array))) | ||
(is (= 2 (da/next-idx dynamic-array)))))) | ||
|
||
(deftest toString-test | ||
(testing "provides string representation of an empty array" | ||
(let [dynamic-array (da/->DynamicArray 3)] | ||
(is (= "[ ]" (.toString dynamic-array))))) | ||
|
||
(testing "provides string representation of a non empty array" | ||
(let [dynamic-array (da/->DynamicArray 10)] | ||
(doseq [i (range 10 15)] | ||
(da/append dynamic-array i)) | ||
(is (= "[ 10 11 12 13 14 ]" (.toString dynamic-array)))))) |