Skip to content

Commit d4196d4

Browse files
authored
Merge pull request #22 from TheAlgorithms/fr33m0nk/add-dynamic-array
Adds Dynamic array implementation
2 parents fb75d65 + 02b4ee1 commit d4196d4

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

DIRECTORY.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
* [Sublist of a list by consecutive repetitions](https://github.com/TheAlgorithms/Clojure/blob/main/src/data_structures/lists/pack_consecutive_repetitions_in_sublist.clj)
1717
* [Run length encoding](https://github.com/TheAlgorithms/Clojure/blob/main/src/data_structures/lists/run_length_encoding.clj)
1818

19+
* [Dynamic Array implementation](/src/data_structures/dynamic_array/core.clj)
20+
1921
## Sorting
2022
* [Merge Sort](https://github.com/TheAlgorithms/Clojure/blob/main/src/sorts/merge_sort.clj)
2123
* [Quick Sort](https://github.com/TheAlgorithms/Clojure/blob/main/src/sorts/quick_sort.clj)
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
(ns data-structures.dynamic-array.core
2+
(:refer-clojure :exclude [get set empty? remove pop]))
3+
4+
;; See tests for the documentation on implementation
5+
(defprotocol IDynamicArray
6+
(get [this idx])
7+
(set [this idx value])
8+
(remove [this idx])
9+
(empty? [this])
10+
(length [this])
11+
(next-idx [this])
12+
(append [this value])
13+
(pop [this]))
14+
15+
(deftype DynamicArray [^:unsynchronized-mutable length
16+
^:unsynchronized-mutable next-idx
17+
^:unsynchronized-mutable array]
18+
IDynamicArray
19+
(get [_ idx]
20+
(assert (< -1 idx next-idx) "Invalid index value supplied")
21+
(aget array idx))
22+
(set [_ idx value]
23+
(assert (<= 0 idx) "Invalid index value supplied")
24+
(when (or (= next-idx length) (> idx length))
25+
(let [next-length (* idx 2)
26+
next-array (make-array Integer/TYPE next-length)]
27+
(doseq [i (range length)]
28+
(->> (aget array i)
29+
(aset next-array i)))
30+
(set! array next-array)
31+
(set! length next-length)))
32+
(aset array next-idx value)
33+
(set! next-idx (inc idx))
34+
idx)
35+
(remove [_ idx]
36+
(assert (< -1 idx next-idx) "Invalid index value supplied")
37+
(let [next-next-idx (dec next-idx)
38+
popped-element (aget array idx)]
39+
(doseq [dest-idx (range idx next-idx)
40+
:let [source-idx (inc dest-idx)]
41+
:when (not= source-idx length)]
42+
(aset array dest-idx (aget array source-idx)))
43+
(set! next-idx next-next-idx)
44+
popped-element))
45+
(empty? [_]
46+
(zero? next-idx))
47+
(length [_]
48+
(alength array))
49+
(next-idx [this]
50+
next-idx)
51+
(append [_ value]
52+
(when (= next-idx length)
53+
(let [next-length (* length 2)
54+
next-array (make-array Integer/TYPE next-length)]
55+
(doseq [i (range length)]
56+
(->> (aget array i)
57+
(aset next-array i)))
58+
(set! array next-array)
59+
(set! length next-length)))
60+
(let [old-capacity next-idx]
61+
(aset array next-idx value)
62+
(set! next-idx (inc next-idx))
63+
old-capacity))
64+
(pop [_]
65+
(assert (> next-idx 0) "Nothing to pop")
66+
(let [next-next-idx (dec next-idx)
67+
popped-element (aget array next-next-idx)]
68+
(aset array next-next-idx 0)
69+
(set! next-idx next-next-idx)
70+
popped-element))
71+
Object
72+
(toString [_]
73+
(let [^StringBuilder sb (StringBuilder.)]
74+
(.append sb "[ ")
75+
(doseq [i (range next-idx)]
76+
(.append sb (aget array i))
77+
(.append sb " "))
78+
(.append sb "]")
79+
(.toString sb))))
80+
81+
(defn ->DynamicArray
82+
[initial-capacity]
83+
(when (>= 0 initial-capacity)
84+
(throw (IllegalArgumentException. "Invalid initial capacity")))
85+
(DynamicArray. initial-capacity 0 (make-array Integer/TYPE initial-capacity)))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
(ns data-structures.dynamic-array.core-test
2+
(:require [clojure.test :refer [deftest testing is]]
3+
[data-structures.dynamic-array.core :as da]))
4+
5+
(deftest ->DynamicArray-test
6+
(testing "throws when invalid initial capacity is provided"
7+
(is (thrown? IllegalArgumentException (da/->DynamicArray 0)))
8+
(is (thrown? IllegalArgumentException (da/->DynamicArray -1)))))
9+
(deftest empty?-test
10+
(testing "return true if dynamic array is empty"
11+
(let [dynamic-array (da/->DynamicArray 3)]
12+
(is (true? (da/empty? dynamic-array)))))
13+
14+
(testing "return false if dynamic array is empty"
15+
(let [dynamic-array (da/->DynamicArray 3)]
16+
(da/set dynamic-array (da/next-idx dynamic-array) 20)
17+
(da/set dynamic-array (da/next-idx dynamic-array) 30)
18+
(is (false? (da/empty? dynamic-array))))))
19+
20+
(deftest length-test
21+
(testing "returns initial capacity till dynamic array is full"
22+
(let [dynamic-array (da/->DynamicArray 3)]
23+
(is (= 3 (da/length dynamic-array)))))
24+
25+
(testing "returns expanded capacity once storage demands exceeds initial capacity"
26+
(let [dynamic-array (da/->DynamicArray 3)]
27+
(da/set dynamic-array (da/next-idx dynamic-array) 10)
28+
(da/set dynamic-array (da/next-idx dynamic-array) 20)
29+
(da/set dynamic-array (da/next-idx dynamic-array) 30)
30+
(da/set dynamic-array (da/next-idx dynamic-array) 40)
31+
(is (= 6 (da/length dynamic-array))))))
32+
33+
(deftest next-idx-test
34+
(testing "returns initial index as the starting point when dynamic array is empty"
35+
(let [dynamic-array (da/->DynamicArray 3)]
36+
(is (= 0 (da/next-idx dynamic-array)))))
37+
38+
(testing "returns current filled index as the starting point when dynamic array is empty"
39+
(let [dynamic-array (da/->DynamicArray 3)]
40+
(da/set dynamic-array (da/next-idx dynamic-array) 10)
41+
(da/set dynamic-array (da/next-idx dynamic-array) 20)
42+
(da/set dynamic-array (da/next-idx dynamic-array) 30)
43+
(is (= 3 (da/next-idx dynamic-array))))))
44+
45+
(deftest set-test
46+
(let [dynamic-array (da/->DynamicArray 3)]
47+
(testing "throws Assertion error when index is less than 0"
48+
(is (thrown? AssertionError (da/set dynamic-array -1 10))))
49+
50+
(testing "stores the element within the dynamic array when a valid index is provided"
51+
(da/set dynamic-array (da/next-idx dynamic-array) 10)
52+
(is (false? (da/empty? dynamic-array)))
53+
(is (= 1 (da/next-idx dynamic-array))))
54+
55+
(testing "expands dynamic array to incorporate more elements"
56+
(da/set dynamic-array (da/next-idx dynamic-array) 20)
57+
(da/set dynamic-array (da/next-idx dynamic-array) 30)
58+
(da/set dynamic-array (da/next-idx dynamic-array) 40)
59+
(is (= 4 (da/next-idx dynamic-array)))
60+
(is (= 6 (da/length dynamic-array))))
61+
62+
(testing "expands dynamic array to incorporate for an arbitrary large index and sets next-idx accordingly"
63+
(da/set dynamic-array 60 40)
64+
(is (= 61 (da/next-idx dynamic-array)) "This behaviour causes fragmentation in the dynamic array")
65+
(is (= 120 (da/length dynamic-array))))))
66+
67+
(deftest get-test
68+
(let [dynamic-array (da/->DynamicArray 3)]
69+
(testing "throws Assertion error when index is less than 0"
70+
(is (thrown? AssertionError (da/get dynamic-array -1))))
71+
72+
(testing "throws Assertion error when index is greater than next-index"
73+
(is (thrown? AssertionError (da/get dynamic-array (inc (da/next-idx dynamic-array))))))
74+
75+
(testing "fetches the content of the dynamic array stored at valid index"
76+
(da/set dynamic-array (da/next-idx dynamic-array) 40)
77+
(is (= 40 (da/get dynamic-array (dec (da/next-idx dynamic-array))))))))
78+
79+
(deftest remove-test
80+
(let [dynamic-array (da/->DynamicArray 3)]
81+
(testing "throws Assertion error when index is less than 0"
82+
(is (thrown? AssertionError (da/remove dynamic-array -1))))
83+
84+
(testing "throws Assertion error when index is greater than next index"
85+
(is (thrown? AssertionError (da/remove dynamic-array (inc (da/next-idx dynamic-array))))))
86+
87+
(testing "removes the element from the dynamic array and returns it if the index is valid"
88+
(da/set dynamic-array (da/next-idx dynamic-array) 10)
89+
(da/set dynamic-array (da/next-idx dynamic-array) 20)
90+
(da/set dynamic-array (da/next-idx dynamic-array) 30)
91+
(da/set dynamic-array (da/next-idx dynamic-array) 40)
92+
(is (= 4 (da/next-idx dynamic-array)) "Sanity check to ensure that next-index is correctly calculated")
93+
(is (= 30 (da/remove dynamic-array 2)))
94+
(is (= 3 (da/next-idx dynamic-array)))
95+
(is (= "[ 10 20 40 ]" (.toString dynamic-array))))))
96+
97+
(deftest append-test
98+
(let [dynamic-array (da/->DynamicArray 3)]
99+
(testing "appends values to dynamic array"
100+
(da/set dynamic-array (da/next-idx dynamic-array) 10)
101+
(da/set dynamic-array (da/next-idx dynamic-array) 20)
102+
(is (= 2 (da/next-idx dynamic-array)))
103+
(is (= 3 (da/length dynamic-array))))
104+
105+
(testing "expands dynamic array to allocate more elements"
106+
(da/set dynamic-array (da/next-idx dynamic-array) 30)
107+
(da/set dynamic-array (da/next-idx dynamic-array) 40)
108+
(is (= 4 (da/next-idx dynamic-array)))
109+
(is (= 6 (da/length dynamic-array))))))
110+
111+
(deftest pop-test
112+
(let [dynamic-array (da/->DynamicArray 3)]
113+
(testing "throws when the dynamic array is empty"
114+
(is (thrown? AssertionError (da/pop dynamic-array))))
115+
116+
(testing "pops the element from dynamic array and returns it"
117+
(da/set dynamic-array (da/next-idx dynamic-array) 10)
118+
(da/set dynamic-array (da/next-idx dynamic-array) 20)
119+
(da/set dynamic-array (da/next-idx dynamic-array) 30)
120+
(is (= 30 (da/pop dynamic-array)))
121+
(is (= 2 (da/next-idx dynamic-array))))))
122+
123+
(deftest toString-test
124+
(testing "provides string representation of an empty array"
125+
(let [dynamic-array (da/->DynamicArray 3)]
126+
(is (= "[ ]" (.toString dynamic-array)))))
127+
128+
(testing "provides string representation of a non empty array"
129+
(let [dynamic-array (da/->DynamicArray 10)]
130+
(doseq [i (range 10 15)]
131+
(da/append dynamic-array i))
132+
(is (= "[ 10 11 12 13 14 ]" (.toString dynamic-array))))))

0 commit comments

Comments
 (0)