Skip to content

Commit

Permalink
Merge pull request #22 from TheAlgorithms/fr33m0nk/add-dynamic-array
Browse files Browse the repository at this point in the history
Adds Dynamic array implementation
  • Loading branch information
fr33m0nk authored Apr 4, 2024
2 parents fb75d65 + 02b4ee1 commit d4196d4
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 0 deletions.
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* [Sublist of a list by consecutive repetitions](https://github.com/TheAlgorithms/Clojure/blob/main/src/data_structures/lists/pack_consecutive_repetitions_in_sublist.clj)
* [Run length encoding](https://github.com/TheAlgorithms/Clojure/blob/main/src/data_structures/lists/run_length_encoding.clj)

* [Dynamic Array implementation](/src/data_structures/dynamic_array/core.clj)

## Sorting
* [Merge Sort](https://github.com/TheAlgorithms/Clojure/blob/main/src/sorts/merge_sort.clj)
* [Quick Sort](https://github.com/TheAlgorithms/Clojure/blob/main/src/sorts/quick_sort.clj)
Expand Down
85 changes: 85 additions & 0 deletions src/data_structures/dynamic_array/core.clj
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)))
132 changes: 132 additions & 0 deletions test/data_structures/dynamic_array/core_test.clj
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))))))

0 comments on commit d4196d4

Please sign in to comment.