From bc84daa92277d80455344a1706bdaf29c23fbe84 Mon Sep 17 00:00:00 2001 From: Shrish Senthilkumar Date: Mon, 28 Jul 2025 12:22:00 -0400 Subject: [PATCH] Updated quit.js with B+ tree capability --- src/js/algorithms/quit.js | 297 +++++++++++++++++++++++++++++++++++--- 1 file changed, 273 insertions(+), 24 deletions(-) diff --git a/src/js/algorithms/quit.js b/src/js/algorithms/quit.js index 1f91f0d..d95d9bb 100644 --- a/src/js/algorithms/quit.js +++ b/src/js/algorithms/quit.js @@ -1,4 +1,3 @@ - /* * One step of the QuIT algorithm */ @@ -6,6 +5,8 @@ function quit() { // Get the page from the data stream let page; + let removedPageIndex = 0; // the page flushed from buffer is always page0 + // If we are at the start state fill the data stream too if (total_data.length == selectedN) { page = total_data[0]; @@ -40,30 +41,36 @@ function quit() { } page = parseInt(page); // convert page to integer - + // Initial phase - building first pole while we have less than leaf_node_size if (inserted_data_quit.length < leaf_node_size - 1) { inserted_data_quit.push(page); pole.push(page); pole.sort((a, b) => a - b); quit_fast_inserts++; + + //Visualize fast insert + const { side, level } = getTargetLevelForPage(page); + traceTreePath(side, "fast", level); + + //animateQuitPage(removedPageIndex, 'normal'); return; } - + // Create pole_prev when we first reach leaf_node_size if (inserted_data_quit.length == leaf_node_size - 1) { inserted_data_quit.push(page); pole.push(page); pole.sort((a, b) => a - b); - + // Get a copy of the pole let pole_copy = [...pole]; // Empty the pole pole = []; - + // Split at 50% (predefined) let mid = Math.floor(leaf_node_size / 2); - + // Split the pole into pole_prev and pole, first half goes to pole_prev for (let i = 0; i < mid; i++) { pole_prev.push(pole_copy[i]); @@ -72,31 +79,32 @@ function quit() { for (let i = mid; i < leaf_node_size; i++) { pole.push(pole_copy[i]); } - + // Pole changes, do animations let random_x = Math.floor(Math.random() * 81) + 10; document.getElementById("pole").style.left = random_x + "%"; quit_pole_resets++; - + + //animateQuitPage(removedPageIndex, 'bulk-load'); return; } - + // Get minimum pages for prediction let q = Math.min(...pole); let p = Math.min(...pole_prev); - + // Check if page belongs to current pole's range if (page >= q && page <= Math.max(...pole)) { console.log("page belongs to current pole range"); - + // Handle pole splitting if needed (leaf node size is reached) if (pole.length == leaf_node_size) { console.log("Pole is full - Initiating split"); - + let pole_copy = [...pole]; pole = []; pole_next = []; - + // Split let mid = Math.floor(leaf_node_size / 2); for (let i = 0; i < mid; i++) { @@ -105,15 +113,15 @@ function quit() { for (let i = mid; i < leaf_node_size; i++) { pole_next.push(pole_copy[i]); } - + // Calculate catching up condition let e = Math.min(...pole_next); let x = q + ((q-p) / pole_prev.length) * pole.length * 1.5; - + console.log("Catching up check:"); console.log("e: ", e); console.log("x ", x); - + // Check if pole_next is catching up (if it is, move pole forward) if (e <= x) { console.log("Catching up - Moving pole forward"); @@ -126,36 +134,45 @@ function quit() { let random_x = Math.floor(Math.random() * 81) + 10; document.getElementById("pole").style.left = random_x + "%"; quit_pole_resets++; + + //animateQuitPage(removedPageIndex, 'bulk-load'); return; } // If pole_next is not catching up, do nothing else { console.log("Not catching up - Maintaining pole_prev, pole, and pole_next"); + //animateQuitPage(removedPageIndex, 'normal'); return; } } - + // Fast insert into current pole inserted_data_quit.push(page); pole.push(page); pole.sort((a, b) => a - b); quit_fast_inserts++; - + console.log("Fast insert complete. New pole:", pole); - + + // Visualize fast insert + const { side, level } = getTargetLevelForPage(page); + traceTreePath(side, "fast", level); + + //animateQuitPage(removedPageIndex, 'normal'); + } // Page is outside pole range else { console.log("page is outside pole range - Handling outlier"); - + // Simulate top_insert by checking if page belongs in pole_next let leaf_for_outlier = null; if (pole_next.length > 0 && page >= Math.min(...pole_next) && page <= Math.max(...pole_next)) { leaf_for_outlier = 'pole_next'; } - + inserted_data_quit.push(page); - + // Update pole structure if outlier belongs in pole_next if (leaf_for_outlier === 'pole_next') { console.log("Outlier belongs in pole_next - Catching up"); @@ -171,12 +188,244 @@ function quit() { let random_x = Math.floor(Math.random() * 81) + 10; document.getElementById("pole").style.left = random_x + "%"; quit_pole_resets++; + + //animateQuitPage(removedPageIndex, 'bulk-load'); } else { console.log("True outlier - Requires top insert"); quit_top_inserts++; + + //Visualize top insert + const { side, level } = getTargetLevelForPage(page); + traceTreePath(side, "top", level); + + //animateQuitPage(removedPageIndex, 'top-insert'); } } - + console.log("Finished processing page", page); +} + +/* + * Trace the tree path down to the given level + */ +function traceTreePath(side, mode = "top", targetLevel = 4) { + const path = []; + if (side === "left") { + path.push("node-1", "node-2-left", "node-3-leftmost", "node-4-leftmost"); + } else { + path.push("node-1", "node-2-right", "node-3-rightmost", "node-4-rightmost"); + } + + if (mode === "top") { + // Highlight path down to targetLevel + for (let i = 0; i < targetLevel; i++) { + const nodeId = path[i]; + const node = document.getElementById(nodeId); + if (node) { + setTimeout(() => { + node.style.backgroundColor = "yellow"; + setTimeout(() => { + node.style.backgroundColor = "#ccc"; + }, 800); + }, i * 200); + } + } + } else if (mode === "fast") { + // Just flash green target node + const nodeId = + side === "left" + ? `node-${targetLevel}-leftmost` + : `node-${targetLevel}-rightmost`; + const node = document.getElementById(nodeId); + if (node) { + node.style.backgroundColor = "green"; + setTimeout(() => { + node.style.backgroundColor = "#ccc"; + }, 800); + } + } +} + +/* + * Determine the correct tree side and level for this page + */ +function getTargetLevelForPage(page) { + let side = page < 50 ? "left" : "right"; // adjust threshold + let level = 4; + + if (pole.includes(page)) { + level = 4; + } else if (pole_next.length > 0 && page >= Math.min(...pole_next) && page <= Math.max(...pole_next)) { + level = 4; + } else if (pole_prev.length > 0 && page >= Math.min(...pole_prev) && page <= Math.max(...pole_prev)) { + level = 3; + } else if (page < Math.min(...pole_prev)) { + level = 2; + } else { + level = 1; + } + + return { side, level }; +} + +class Node { + constructor(t, leaf) { + this.t = t; + this.leaf = leaf; + this.keys = []; + this.children = []; + this.n = 0; + } +} + +class BTree { + constructor(t) { + this.t = t; + this.root = new Node(t, true); + + // QuIT poles + this.currPole = []; + this.prevPole = []; + this.nextPole = []; + + // Zone tracking + this.levelMap = {}; + } + + insertWithLevel(page) { + let level = 1; + + const insertRecursive = (node, key, currentLevel) => { + let i = node.n - 1; + + if (node.leaf) { + while (i >= 0 && node.keys[i] > key) { + node.keys[i + 1] = node.keys[i]; + i--; + } + node.keys[i + 1] = key; + node.n += 1; + + this.trackLevelKey(key, currentLevel); + return currentLevel; + } else { + while (i >= 0 && node.keys[i] > key) i--; + i++; + if (node.children[i].n === 2 * this.t - 1) { + this.splitChild(node, i, node.children[i]); + if (node.keys[i] < key) i++; + } + return insertRecursive(node.children[i], key, currentLevel + 1); + } + }; + + if (this.root.n === 2 * this.t - 1) { + let newRoot = new Node(this.t, false); + newRoot.children[0] = this.root; + this.splitChild(newRoot, 0, this.root); + this.root = newRoot; + } + + return insertRecursive(this.root, page, level); + } + + splitChild(parent, i, child) { + let t = this.t; + let newChild = new Node(t, child.leaf); + newChild.n = t - 1; + + for (let j = 0; j < t - 1; j++) { + newChild.keys[j] = child.keys[j + t]; + } + + if (!child.leaf) { + for (let j = 0; j < t; j++) { + newChild.children[j] = child.children[j + t]; + } + } + + child.n = t - 1; + + for (let j = parent.n; j >= i + 1; j--) { + parent.children[j + 1] = parent.children[j]; + } + parent.children[i + 1] = newChild; + + for (let j = parent.n - 1; j >= i; j--) { + parent.keys[j + 1] = parent.keys[j]; + } + parent.keys[i] = child.keys[t - 1]; + + parent.n += 1; + } + + trackLevelKey(key, level) { + if (!this.levelMap[level]) this.levelMap[level] = []; + this.levelMap[level].push(key); + } + + getZoneForPage(page, level) { + const keysAtLevel = this.levelMap[level]; + if (!keysAtLevel || keysAtLevel.length === 0) return 'unknown'; + + const sorted = [...keysAtLevel].sort((a, b) => a - b); + const index = sorted.indexOf(page); + if (index === -1) return 'not in level'; + + const percent = index / sorted.length; + + if (level === 1) { + return 'zone 1'; + } else if (level === 2) { + return percent < 0.5 ? 'zone 1/2' : 'zone 2/2'; + } else { + if (percent < 0.25) return 'zone 1/4'; + else if (percent < 0.5) return 'zone 2/4'; + else if (percent < 0.75) return 'zone 3/4'; + else return 'zone 4/4'; + } + } + + getPoleForPage(page) { + if (this.currPole.includes(page)) return 'currPole'; + if (this.prevPole.includes(page)) return 'prevPole'; + if (this.nextPole.includes(page)) return 'nextPole'; + return 'none'; + } +} + +// ======================= +// Create B+ Tree instance +// ======================= +const MIN_DEGREE = 6; +const btree = new BTree(MIN_DEGREE); + +// Save original quit() +const original_quit = quit; + +quit = function () { + original_quit.apply(this, arguments); + if (!running) return; + + const page = inserted_data_quit[inserted_data_quit.length - 1]; + const level = btree.insertWithLevel(page); + + btree.currPole = [...pole]; + btree.prevPole = [...pole_prev]; + btree.nextPole = [...pole_next]; + + const poleName = btree.getPoleForPage(page); + const zone = btree.getZoneForPage(page, level); + + console.log(`[B+Tree] Page ${page} inserted into ${poleName} at tree level ${level}`); + console.log(`Zone Mapping: ${zone}`); + logPoleStatus(btree); +}; -} \ No newline at end of file +function logPoleStatus(btree) { + console.log( + `Poles Status:\n` + + `prevPole: [${btree.prevPole.join(", ")}]\n` + + `currPole: [${btree.currPole.join(", ")}]\n` + ); +}