feat: Test running overhaul, switch to Prettier & reformat everything (#1407)

* chore: Switch to Node 20 + Vitest

* chore: migrate to vitest mock functions

* chore: code style (switch to prettier)

* test: re-enable long-running test

Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime!

see #1193

* chore: code style

* chore: fix failing tests

* Updated Documentation in README.md

* Update contribution guidelines to state usage of Prettier

* fix: set prettier printWidth back to 80

* chore: apply updated code style automatically

* fix: set prettier line endings to lf again

* chore: apply updated code style automatically

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
This commit is contained in:
Roland Hummel
2023-10-03 23:08:19 +02:00
committed by GitHub
parent 0ca18c2b2c
commit 86d333ee94
392 changed files with 5849 additions and 16622 deletions

View File

@@ -19,12 +19,12 @@
*/
// Priority Queue Helper functions
const getParentPosition = position => Math.floor((position - 1) / 2)
const getChildrenPositions = position => [2 * position + 1, 2 * position + 2]
const getParentPosition = (position) => Math.floor((position - 1) / 2)
const getChildrenPositions = (position) => [2 * position + 1, 2 * position + 2]
class KeyPriorityQueue {
// Priority Queue class using Minimum Binary Heap
constructor () {
constructor() {
this._heap = []
this.priorities = new Map()
}
@@ -33,7 +33,7 @@ class KeyPriorityQueue {
* Checks if the heap is empty
* @returns boolean
*/
isEmpty () {
isEmpty() {
return this._heap.length === 0
}
@@ -42,7 +42,7 @@ class KeyPriorityQueue {
* @param {*} key
* @param {number} priority
*/
push (key, priority) {
push(key, priority) {
this._heap.push(key)
this.priorities.set(key, priority)
this._shiftUp(this._heap.length - 1)
@@ -52,7 +52,7 @@ class KeyPriorityQueue {
* Removes the element with least priority
* @returns the key of the element with least priority
*/
pop () {
pop() {
this._swap(0, this._heap.length - 1)
const key = this._heap.pop()
this.priorities.delete(key)
@@ -65,7 +65,7 @@ class KeyPriorityQueue {
* @param {*} key
* @returns boolean
*/
contains (key) {
contains(key) {
return this.priorities.has(key)
}
@@ -75,7 +75,7 @@ class KeyPriorityQueue {
* @param {*} key the element to change
* @param {number} priority new priority of the element
*/
update (key, priority) {
update(key, priority) {
const currPos = this._heap.indexOf(key)
// if the key does not exist yet, add it
if (currPos === -1) return this.push(key, priority)
@@ -95,13 +95,14 @@ class KeyPriorityQueue {
}
}
_getPriorityOrInfinite (position) {
_getPriorityOrInfinite(position) {
// Helper function, returns priority of the node, or Infinite if no node corresponds to this position
if (position >= 0 && position < this._heap.length) return this.priorities.get(this._heap[position])
if (position >= 0 && position < this._heap.length)
return this.priorities.get(this._heap[position])
else return Infinity
}
_shiftUp (position) {
_shiftUp(position) {
// Helper function to shift up a node to proper position (equivalent to bubbleUp)
let currPos = position
let parentPos = getParentPosition(currPos)
@@ -117,7 +118,7 @@ class KeyPriorityQueue {
}
}
_shiftDown (position) {
_shiftDown(position) {
// Helper function to shift down a node to proper position (equivalent to bubbleDown)
let currPos = position
let [child1Pos, child2Pos] = getChildrenPositions(currPos)
@@ -137,16 +138,19 @@ class KeyPriorityQueue {
this._swap(child2Pos, currPos)
currPos = child2Pos
}
[child1Pos, child2Pos] = getChildrenPositions(currPos)
;[child1Pos, child2Pos] = getChildrenPositions(currPos)
child1Priority = this._getPriorityOrInfinite(child1Pos)
child2Priority = this._getPriorityOrInfinite(child2Pos)
currPriority = this._getPriorityOrInfinite(currPos)
}
}
_swap (position1, position2) {
_swap(position1, position2) {
// Helper function to swap 2 nodes
[this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]]
;[this._heap[position1], this._heap[position2]] = [
this._heap[position2],
this._heap[position1]
]
}
}

View File

@@ -4,25 +4,25 @@
*/
class BinaryHeap {
constructor () {
constructor() {
this.heap = []
}
insert (value) {
insert(value) {
this.heap.push(value)
this.heapify()
}
size () {
size() {
return this.heap.length
}
empty () {
empty() {
return this.size() === 0
}
// using iterative approach to reorder the heap after insertion
heapify () {
heapify() {
let index = this.size() - 1
while (index > 0) {
@@ -38,7 +38,7 @@ class BinaryHeap {
}
// Extracting the maximum element from the Heap
extractMax () {
extractMax() {
const max = this.heap[0]
const tmp = this.heap.pop()
if (!this.empty()) {
@@ -49,7 +49,7 @@ class BinaryHeap {
}
// To restore the balance of the heap after extraction.
sinkDown (index) {
sinkDown(index) {
const left = 2 * index + 1
const right = 2 * index + 2
let largest = index

View File

@@ -19,15 +19,15 @@
*/
class MinHeap {
constructor (array) {
constructor(array) {
this.heap = this.initializeHeap(array)
}
/**
* startingParent represents the parent of the last index (=== array.length-1)
* and iterates towards 0 with all index values below sorted to meet heap conditions
*/
initializeHeap (array) {
*/
initializeHeap(array) {
const startingParent = Math.floor((array.length - 2) / 2)
for (let currIdx = startingParent; currIdx >= 0; currIdx--) {
@@ -52,15 +52,16 @@ class MinHeap {
* update currIdx and recalculate the new childOneIdx to check heap conditions again.
*
* if there is no swap, it means the children indices and the parent index satisfy heap conditions and can exit the function.
*/
sinkDown (currIdx, endIdx, heap) {
*/
sinkDown(currIdx, endIdx, heap) {
let childOneIdx = currIdx * 2 + 1
while (childOneIdx <= endIdx) {
const childTwoIdx = childOneIdx + 1 <= endIdx ? childOneIdx + 1 : -1
const swapIdx = childTwoIdx !== -1 && heap[childTwoIdx] < heap[childOneIdx]
? childTwoIdx
: childOneIdx
const swapIdx =
childTwoIdx !== -1 && heap[childTwoIdx] < heap[childOneIdx]
? childTwoIdx
: childOneIdx
if (heap[swapIdx] < heap[currIdx]) {
this.swap(currIdx, swapIdx, heap)
@@ -79,8 +80,8 @@ class MinHeap {
* update currIdx and recalculate the new parentIdx to check heap condition again.
*
* iteration does not end while a valid currIdx has a value smaller than its parentIdx's value
*/
bubbleUp (currIdx) {
*/
bubbleUp(currIdx) {
let parentIdx = Math.floor((currIdx - 1) / 2)
while (currIdx > 0 && this.heap[currIdx] < this.heap[parentIdx]) {
@@ -90,7 +91,7 @@ class MinHeap {
}
}
peek () {
peek() {
return this.heap[0]
}
@@ -101,8 +102,8 @@ class MinHeap {
* the resulting min heap value now resides at heap[heap.length-1] which is popped and later returned.
*
* the remaining values in the heap are re-sorted
*/
extractMin () {
*/
extractMin() {
this.swap(0, this.heap.length - 1, this.heap)
const min = this.heap.pop()
this.sinkDown(0, this.heap.length - 1, this.heap)
@@ -110,13 +111,13 @@ class MinHeap {
}
// a new value is pushed to the end of the heap and sorted up
insert (value) {
insert(value) {
this.heap.push(value)
this.bubbleUp(this.heap.length - 1)
}
// index-swapping helper method
swap (idx1, idx2, heap) {
swap(idx1, idx2, heap) {
const temp = heap[idx1]
heap[idx1] = heap[idx2]
heap[idx2] = temp

View File

@@ -1,18 +1,18 @@
/* Minimum Priority Queue
* It is a part of heap data structure
* A heap is a specific tree based data structure
* in which all the nodes of tree are in a specific order.
* that is the children are arranged in some
* respect of their parents, can either be greater
* or less than the parent. This makes it a min priority queue
* or max priority queue.
*/
* It is a part of heap data structure
* A heap is a specific tree based data structure
* in which all the nodes of tree are in a specific order.
* that is the children are arranged in some
* respect of their parents, can either be greater
* or less than the parent. This makes it a min priority queue
* or max priority queue.
*/
// Functions: insert, delete, peek, isEmpty, print, heapSort, sink
class MinPriorityQueue {
// calls the constructor and initializes the capacity
constructor (c) {
constructor(c) {
this.heap = []
this.capacity = c
this.size = 0
@@ -20,7 +20,7 @@ class MinPriorityQueue {
// inserts the key at the end and rearranges it
// so that the binary heap is in appropriate order
insert (key) {
insert(key) {
if (this.isFull()) return
this.heap[this.size + 1] = key
let k = this.size + 1
@@ -36,33 +36,36 @@ class MinPriorityQueue {
}
// returns the highest priority value
peek () {
peek() {
return this.heap[1]
}
// returns boolean value whether the heap is empty or not
isEmpty () {
isEmpty() {
return this.size === 0
}
// returns boolean value whether the heap is full or not
isFull () {
isFull() {
return this.size === this.capacity
}
// prints the heap
print (output = value => console.log(value)) {
print(output = (value) => console.log(value)) {
output(this.heap.slice(1))
}
// heap reverse can be done by performing swapping the first
// element with the last, removing the last element to
// new array and calling sink function.
heapReverse () {
heapReverse() {
const heapSort = []
while (this.size > 0) {
// swap first element with last element
[this.heap[1], this.heap[this.size]] = [this.heap[this.size], this.heap[1]]
;[this.heap[1], this.heap[this.size]] = [
this.heap[this.size],
this.heap[1]
]
heapSort.push(this.heap.pop())
this.size--
this.sink()
@@ -74,7 +77,7 @@ class MinPriorityQueue {
}
// this function reorders the heap after every delete function
sink () {
sink() {
let k = 1
while (2 * k <= this.size || 2 * k + 1 <= this.size) {
let minIndex
@@ -92,8 +95,7 @@ class MinPriorityQueue {
this.heap[k] > this.heap[2 * k] ||
this.heap[k] > this.heap[2 * k + 1]
) {
minIndex =
this.heap[2 * k] < this.heap[2 * k + 1] ? 2 * k : 2 * k + 1
minIndex = this.heap[2 * k] < this.heap[2 * k + 1] ? 2 * k : 2 * k + 1
} else {
minIndex = k
}
@@ -107,7 +109,7 @@ class MinPriorityQueue {
// deletes the highest priority value from the heap. The last
// element goes to ahead to first position and reorder heap
delete () {
delete() {
// checks empty and one element array conditions
if (this.isEmpty()) return
if (this.size === 1) {

View File

@@ -9,7 +9,9 @@ describe('MinHeap', () => {
})
it('should initialize a heap from an input array', () => {
expect(heap).toEqual({ 'heap': [1, 4, 2, 7, 16, 10, 39, 23, 9, 43, 85, 42, 51] }) // eslint-disable-line
expect(heap).toEqual({
heap: [1, 4, 2, 7, 16, 10, 39, 23, 9, 43, 85, 42, 51]
}) // eslint-disable-line
})
it('should show the top value in the heap', () => {
@@ -22,12 +24,14 @@ describe('MinHeap', () => {
const minValue = heap.extractMin()
expect(minValue).toEqual(1)
expect(heap).toEqual({ 'heap': [2, 4, 10, 7, 16, 42, 39, 23, 9, 43, 85, 51] }) // eslint-disable-line
expect(heap).toEqual({ heap: [2, 4, 10, 7, 16, 42, 39, 23, 9, 43, 85, 51] }) // eslint-disable-line
})
it('should insert a new value and sort until it meets heap conditions', () => {
heap.insert(15)
expect(heap).toEqual({ 'heap': [2, 4, 10, 7, 16, 15, 39, 23, 9, 43, 85, 51, 42] }) // eslint-disable-line
expect(heap).toEqual({
heap: [2, 4, 10, 7, 16, 15, 39, 23, 9, 43, 85, 51, 42]
}) // eslint-disable-line
})
})

View File

@@ -7,11 +7,11 @@ describe('MinPriorityQueue', () => {
beforeEach(() => {
queue = new MinPriorityQueue(capacity)
values.forEach(v => queue.insert(v))
values.forEach((v) => queue.insert(v))
})
it('Check heap ordering', () => {
const mockFn = jest.fn()
const mockFn = vi.fn()
queue.print(mockFn)
expect(mockFn.mock.calls.length).toBe(1) // Expect one call
@@ -24,7 +24,7 @@ describe('MinPriorityQueue', () => {
it('heapSort() expected to reverse the heap ordering', () => {
queue.heapReverse()
const mockFn = jest.fn()
const mockFn = vi.fn()
queue.print(mockFn)
expect(mockFn.mock.calls.length).toBe(1)