From 7b6a94a85d391c3d6f6f26cb2f929d143b17d510 Mon Sep 17 00:00:00 2001 From: SouparnaChatterjee Date: Sun, 16 Nov 2025 11:33:23 +0530 Subject: [PATCH 1/3] refactor(simulator): clean up EventQueue implementation and add TS types --- build.js | 24 +++++ package.json | 2 +- src/simulator/src/eventQueue.ts | 183 +++++++++++++++++++------------- 3 files changed, 133 insertions(+), 76 deletions(-) create mode 100644 build.js diff --git a/build.js b/build.js new file mode 100644 index 000000000..ffa4b9183 --- /dev/null +++ b/build.js @@ -0,0 +1,24 @@ +// build.js (Windows + cross-platform replacement for build.sh) + +const { execSync } = require("child_process"); +const fs = require("fs"); + +console.log("Reading version.json..."); + +const versions = JSON.parse(fs.readFileSync("version.json", "utf8")) + .map((v) => v.version); + +for (const version of versions) { + console.log(`\nBuilding for version: ${version}`); + + try { + execSync(`npx vite build --config vite.config.${version}.ts`, { + stdio: "inherit", + }); + } catch (err) { + console.error(`❌ Build failed for version: ${version}`); + process.exit(1); + } +} + +console.log("\n✅ All builds completed successfully!"); diff --git a/package.json b/package.json index 6751f7281..5f09b1e70 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "serve": "vite preview", - "build": "bash build.sh", + "build": "node build.js", "dev": "vite", "tauri": "tauri", "preview": "vite preview", diff --git a/src/simulator/src/eventQueue.ts b/src/simulator/src/eventQueue.ts index 349f137ca..74c519801 100644 --- a/src/simulator/src/eventQueue.ts +++ b/src/simulator/src/eventQueue.ts @@ -1,123 +1,156 @@ /** - * Event Queue is simply a priority Queue, basic implementation O(n^2). + * EventQueue implements a priority queue where events are ordered by time. + * This is a simple O(n²) shifting implementation used by the CircuitVerse simulator. * @category eventQueue */ -interface QueueObject { +export interface QueueObject { queueProperties: { - inQueue: boolean - time: number - index: number - } - propagationDelay: number + inQueue: boolean; + time: number; + index: number; + }; + propagationDelay: number; } export class EventQueue { - size: number - queue: Array - frontIndex: number - time: number + private size: number; + private queue: QueueObject[]; + private frontIndex: number; + time: number; + constructor(size: number) { - this.size = size - this.queue = new Array(size) - this.frontIndex = 0 - this.time = 0 + this.size = size; + this.queue = new Array(size); + this.frontIndex = 0; + this.time = 0; } + /** + * Insert an object with a delay (or the object's default propagation delay). + * This maintains the queue sorted by event time. + */ add(obj: QueueObject, delay: number) { + const eventTime = this.time + (delay ?? obj.propagationDelay); + if (obj.queueProperties.inQueue) { - obj.queueProperties.time = - this.time + (delay || obj.propagationDelay) - let i = obj.queueProperties.index - while ( - i > 0 && - obj.queueProperties.time > - this.queue[i - 1].queueProperties.time - ) { - this.swap(i, i - 1) - i-- - } - i = obj.queueProperties.index - while ( - i < this.frontIndex - 1 && - obj.queueProperties.time < - this.queue[i + 1].queueProperties.time - ) { - this.swap(i, i + 1) - i++ - } - return + // Update the time and re-order + obj.queueProperties.time = eventTime; + this.reorder(obj); + return; + } + + if (this.frontIndex === this.size) { + throw new Error("EventQueue size exceeded"); + } + + // Insert at end + this.queue[this.frontIndex] = obj; + obj.queueProperties.time = eventTime; + obj.queueProperties.index = this.frontIndex; + obj.queueProperties.inQueue = true; + this.frontIndex++; + + this.shiftUp(obj); + } + + /** + * Insert an object at current time without delay. + */ + addImmediate(obj: QueueObject) { + if (this.frontIndex === this.size) { + throw new Error("EventQueue size exceeded"); } - if (this.frontIndex == this.size) throw 'EventQueue size exceeded' - this.queue[this.frontIndex] = obj - obj.queueProperties.time = this.time + (delay || obj.propagationDelay) - obj.queueProperties.index = this.frontIndex - this.frontIndex++ - obj.queueProperties.inQueue = true - let i = obj.queueProperties.index + this.queue[this.frontIndex] = obj; + obj.queueProperties.time = this.time; + obj.queueProperties.index = this.frontIndex; + obj.queueProperties.inQueue = true; + this.frontIndex++; + } + + /** + * Ensure correct ordering when an in-queue object's time changes. + */ + private reorder(obj: QueueObject) { + this.shiftUp(obj); + this.shiftDown(obj); + } + + /** + * Move object earlier in queue if its time increased. + */ + private shiftUp(obj: QueueObject) { + let i = obj.queueProperties.index; while ( i > 0 && obj.queueProperties.time > this.queue[i - 1].queueProperties.time ) { - this.swap(i, i - 1) - i-- + this.swap(i, i - 1); + i--; } } /** - * To add without any delay. - * @param {CircuitElement} obj - the object to be added + * Move object later in queue if its time decreased. */ - addImmediate(obj: QueueObject) { - this.queue[this.frontIndex] = obj - obj.queueProperties.time = this.time - obj.queueProperties.index = this.frontIndex - obj.queueProperties.inQueue = true - this.frontIndex++ + private shiftDown(obj: QueueObject) { + let i = obj.queueProperties.index; + while ( + i < this.frontIndex - 1 && + obj.queueProperties.time < this.queue[i + 1].queueProperties.time + ) { + this.swap(i, i + 1); + i++; + } } /** - * Function to swap two objects in queue. + * Swap two queue positions and update indices. */ - swap(v1: number, v2: number) { - const obj1 = this.queue[v1] - obj1.queueProperties.index = v2 + private swap(i: number, j: number) { + const obj1 = this.queue[i]; + const obj2 = this.queue[j]; - const obj2 = this.queue[v2] - obj2.queueProperties.index = v1 + obj1.queueProperties.index = j; + obj2.queueProperties.index = i; - this.queue[v1] = obj2 - this.queue[v2] = obj1 + this.queue[i] = obj2; + this.queue[j] = obj1; } /** - * function to pop element from queue. + * Pop the next event (highest time). */ pop() { - if (this.isEmpty()) throw 'Queue Empty' + if (this.isEmpty()) { + throw new Error("Queue Empty"); + } - this.frontIndex-- - const obj = this.queue[this.frontIndex] - this.time = obj.queueProperties.time - obj.queueProperties.inQueue = false - return obj + this.frontIndex--; + const obj = this.queue[this.frontIndex]; + + this.time = obj.queueProperties.time; + obj.queueProperties.inQueue = false; + + return obj; } /** - * function to reset queue. + * Reset entire queue. */ reset() { - for (let i = 0; i < this.frontIndex; i++) - this.queue[i].queueProperties.inQueue = false - this.time = 0 - this.frontIndex = 0 + for (let i = 0; i < this.frontIndex; i++) { + this.queue[i].queueProperties.inQueue = false; + } + this.time = 0; + this.frontIndex = 0; } /** - * function to check if empty queue. + * Whether queue contains zero events. */ isEmpty() { - return this.frontIndex == 0 + return this.frontIndex === 0; } } From eec59c6d7e65477fac9cb46f8d62b202c86a46c6 Mon Sep 17 00:00:00 2001 From: SouparnaChatterjee Date: Mon, 17 Nov 2025 18:39:42 +0530 Subject: [PATCH 2/3] =?UTF-8?q?refactor(eventQueue):=20replace=20O(n=C2=B2?= =?UTF-8?q?)=20queue=20with=20min-heap=20priority=20queue=20+=20build=20sc?= =?UTF-8?q?ript=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 33 +++--- event-queue-cleanup.patch | 0 src/simulator/src/eventQueue.ts | 171 ++++++++++++++------------------ 3 files changed, 93 insertions(+), 111 deletions(-) create mode 100644 event-queue-cleanup.patch diff --git a/build.sh b/build.sh index 3ccba04c0..39bed4a5e 100644 --- a/build.sh +++ b/build.sh @@ -1,19 +1,24 @@ -#!/bin/bash +// build.js (Windows + cross-platform equivalent of build.sh) +const { execSync } = require("child_process"); +const fs = require("fs"); -versions=($(jq -r '.[].version' version.json)) +console.log("Reading version.json..."); +const versions = JSON.parse(fs.readFileSync("version.json", "utf8")) + .map((v) => v.version); -for version in "${versions[@]}"; do - echo "Building for version: $version" - - npx vite build --config vite.config."$version".ts - - #Build status - if [ $? -ne 0 ]; then - echo "Build failed for version: $version" - exit 1 - fi -done +for (const version of versions) { + console.log(`\nBuilding for version: ${version}`); -echo "All builds completed successfully" + try { + execSync(`npx vite build --config vite.config.${version}.ts`, { + stdio: "inherit", + }); + } catch (err) { + console.error(`❌ Build failed for version: ${version}`); + process.exit(1); + } +} + +console.log("\n✅ All builds completed successfully!"); diff --git a/event-queue-cleanup.patch b/event-queue-cleanup.patch new file mode 100644 index 000000000..e69de29bb diff --git a/src/simulator/src/eventQueue.ts b/src/simulator/src/eventQueue.ts index 74c519801..d05f76b89 100644 --- a/src/simulator/src/eventQueue.ts +++ b/src/simulator/src/eventQueue.ts @@ -1,6 +1,6 @@ /** * EventQueue implements a priority queue where events are ordered by time. - * This is a simple O(n²) shifting implementation used by the CircuitVerse simulator. + * Uses a binary min-heap for O(log n) insertion and removal. * @category eventQueue */ @@ -8,149 +8,126 @@ export interface QueueObject { queueProperties: { inQueue: boolean; time: number; - index: number; + index: number; // heap index }; propagationDelay: number; } export class EventQueue { - private size: number; - private queue: QueueObject[]; - private frontIndex: number; - time: number; - - constructor(size: number) { - this.size = size; - this.queue = new Array(size); - this.frontIndex = 0; - this.time = 0; - } + private heap: QueueObject[] = []; + time = 0; + + constructor(private size: number) {} /** - * Insert an object with a delay (or the object's default propagation delay). - * This maintains the queue sorted by event time. + * Insert an object with delay. Maintains min-heap by event time. */ add(obj: QueueObject, delay: number) { const eventTime = this.time + (delay ?? obj.propagationDelay); + obj.queueProperties.time = eventTime; if (obj.queueProperties.inQueue) { - // Update the time and re-order - obj.queueProperties.time = eventTime; - this.reorder(obj); + // Update time + reposition + this.heapifyUp(obj.queueProperties.index); + this.heapifyDown(obj.queueProperties.index); return; } - if (this.frontIndex === this.size) { + if (this.heap.length === this.size) { throw new Error("EventQueue size exceeded"); } - // Insert at end - this.queue[this.frontIndex] = obj; - obj.queueProperties.time = eventTime; - obj.queueProperties.index = this.frontIndex; + obj.queueProperties.index = this.heap.length; obj.queueProperties.inQueue = true; - this.frontIndex++; - - this.shiftUp(obj); + this.heap.push(obj); + this.heapifyUp(obj.queueProperties.index); } /** - * Insert an object at current time without delay. + * Insert object at current time. */ addImmediate(obj: QueueObject) { - if (this.frontIndex === this.size) { - throw new Error("EventQueue size exceeded"); - } - - this.queue[this.frontIndex] = obj; - obj.queueProperties.time = this.time; - obj.queueProperties.index = this.frontIndex; - obj.queueProperties.inQueue = true; - this.frontIndex++; + this.add(obj, 0); } /** - * Ensure correct ordering when an in-queue object's time changes. + * Pop next event (minimum event time) */ - private reorder(obj: QueueObject) { - this.shiftUp(obj); - this.shiftDown(obj); - } + pop() { + if (this.isEmpty()) throw new Error("Queue Empty"); - /** - * Move object earlier in queue if its time increased. - */ - private shiftUp(obj: QueueObject) { - let i = obj.queueProperties.index; - while ( - i > 0 && - obj.queueProperties.time > this.queue[i - 1].queueProperties.time - ) { - this.swap(i, i - 1); - i--; + const top = this.heap[0]; + const last = this.heap.pop()!; + + if (this.heap.length > 0) { + this.heap[0] = last; + last.queueProperties.index = 0; + this.heapifyDown(0); } + + top.queueProperties.inQueue = false; + this.time = top.queueProperties.time; + return top; } /** - * Move object later in queue if its time decreased. + * Whether queue contains zero events. */ - private shiftDown(obj: QueueObject) { - let i = obj.queueProperties.index; - while ( - i < this.frontIndex - 1 && - obj.queueProperties.time < this.queue[i + 1].queueProperties.time - ) { - this.swap(i, i + 1); - i++; - } + isEmpty() { + return this.heap.length === 0; } /** - * Swap two queue positions and update indices. + * Reset entire queue. */ - private swap(i: number, j: number) { - const obj1 = this.queue[i]; - const obj2 = this.queue[j]; + reset() { + for (const obj of this.heap) obj.queueProperties.inQueue = false; + this.heap = []; + this.time = 0; + } - obj1.queueProperties.index = j; - obj2.queueProperties.index = i; + // ------------------------------- + // Heap Utility Functions + // ------------------------------- - this.queue[i] = obj2; - this.queue[j] = obj1; + private swap(i: number, j: number) { + const a = this.heap[i]; + const b = this.heap[j]; + this.heap[i] = b; + this.heap[j] = a; + a.queueProperties.index = j; + b.queueProperties.index = i; } - /** - * Pop the next event (highest time). - */ - pop() { - if (this.isEmpty()) { - throw new Error("Queue Empty"); + private heapifyUp(i: number) { + while (i > 0) { + const parent = Math.floor((i - 1) / 2); + if (this.heap[i].queueProperties.time >= this.heap[parent].queueProperties.time) break; + this.swap(i, parent); + i = parent; } + } - this.frontIndex--; - const obj = this.queue[this.frontIndex]; + private heapifyDown(i: number) { + const n = this.heap.length; - this.time = obj.queueProperties.time; - obj.queueProperties.inQueue = false; + while (true) { + let smallest = i; + const left = 2 * i + 1; + const right = 2 * i + 2; - return obj; - } + if (left < n && this.heap[left].queueProperties.time < this.heap[smallest].queueProperties.time) { + smallest = left; + } - /** - * Reset entire queue. - */ - reset() { - for (let i = 0; i < this.frontIndex; i++) { - this.queue[i].queueProperties.inQueue = false; - } - this.time = 0; - this.frontIndex = 0; - } + if (right < n && this.heap[right].queueProperties.time < this.heap[smallest].queueProperties.time) { + smallest = right; + } - /** - * Whether queue contains zero events. - */ - isEmpty() { - return this.frontIndex === 0; + if (smallest === i) break; + + this.swap(i, smallest); + i = smallest; + } } } From 3d42f7feb57b2294db0d4c97052faefd3ad88250 Mon Sep 17 00:00:00 2001 From: SouparnaChatterjee Date: Mon, 17 Nov 2025 18:58:37 +0530 Subject: [PATCH 3/3] fix(build): remove wrongly named build.sh --- build.sh | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 build.sh diff --git a/build.sh b/build.sh deleted file mode 100644 index 39bed4a5e..000000000 --- a/build.sh +++ /dev/null @@ -1,24 +0,0 @@ -// build.js (Windows + cross-platform equivalent of build.sh) - -const { execSync } = require("child_process"); -const fs = require("fs"); - -console.log("Reading version.json..."); - -const versions = JSON.parse(fs.readFileSync("version.json", "utf8")) - .map((v) => v.version); - -for (const version of versions) { - console.log(`\nBuilding for version: ${version}`); - - try { - execSync(`npx vite build --config vite.config.${version}.ts`, { - stdio: "inherit", - }); - } catch (err) { - console.error(`❌ Build failed for version: ${version}`); - process.exit(1); - } -} - -console.log("\n✅ All builds completed successfully!");