From a50034bc489abb0ee7c40b71a9211074ba1b3e38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20P=C3=B3=C5=82grabia?=
 <tomasz.polgrabia@protonmail.com>
Date: Sun, 6 Mar 2022 00:26:28 +0100
Subject: [PATCH] Adding prim implementation.

---
 2022/03/algorithms/prim.js     | 243 +++++++++++++++++++++++++++++++++
 2022/03/algorithms/prim_01.txt |  11 ++
 2 files changed, 254 insertions(+)
 create mode 100644 2022/03/algorithms/prim.js
 create mode 100644 2022/03/algorithms/prim_01.txt

diff --git a/2022/03/algorithms/prim.js b/2022/03/algorithms/prim.js
new file mode 100644
index 0000000..36e9718
--- /dev/null
+++ b/2022/03/algorithms/prim.js
@@ -0,0 +1,243 @@
+"use strict";
+
+const fs = require('fs');
+const {EOL} = require('os');
+
+function findPrimMST(graph) {
+	let heap = []; 
+	// we need to make a heap and remove it
+	// one by one. Approach bottom up as it's more
+	// efficient when we create heap from scratch
+	// not just adding single element
+
+	function testHeap() {
+		console.log('Test', heap);
+		for (let i = 0; i < heap.length; i++) {
+			// console.log(`Testing ${i}`);
+			if (2*i+1 < heap.length && heap[2*i+1].w < heap[i].w) {
+				console.log(`Bad ${i} because ${2*i+1} has smaller value ${heap[i].w} > ${heap[2*i+1].w}`);
+				return;
+			}
+
+			if (2*i+2 < heap.length && heap[2*i+2].w < heap[i].w) {
+				console.log(`Bad ${i} because ${2*i+2} has smaller value ${heap[i].w} > ${heap[2*i+2].w}`);
+				return;
+			}
+		}
+	}
+
+	function heapInsert(el) {
+		heap.push(el);
+		let idx = heap.length - 1;
+		let t;
+		while (idx >= 1) {
+			let parent = (idx - 1) >> 1;
+			if (heap[parent].w > heap[idx].w) {
+				t = heap[idx];
+				heap[idx] = heap[parent];
+				heap[parent] = t;
+				idx = parent;
+			} else {
+				break;
+			}
+		}
+	}
+
+	function popSmallest() {
+		let val = heap[0];
+		heap[0] = heap[heap.length - 1];
+		heap.pop();
+		// console.log('Popping first', val);
+
+		// we need to fix from top to bottom
+		// we need to take smallest from descendents
+
+		let idx = 0;
+		let t;
+		while (idx < heap.length) {
+			// console.log(`Checking idx ${idx}`);
+			let smallest = heap[idx].w;
+			if (2*idx + 1 < heap.length) {
+				smallest = Math.min(smallest, heap[2*idx+1].w);
+			}
+
+			if (2*idx + 2 < heap.length) {
+				smallest = Math.min(smallest, heap[2*idx+2].w);
+			}
+
+			// console.log(`Smallest ${smallest}, current: ${heap[idx].w}`);
+
+			if (smallest >= heap[idx].w) {
+				return val; // we're good
+			}
+
+			// we need to have something smaller we try first
+
+			if (smallest === heap[2*idx+1].w) {
+				t = heap[2*idx+1];
+				heap[2*idx+1] = heap[idx];
+				heap[idx] = t;
+				// console.log(`Smallest ${2*idx+1}`);
+				idx = 2*idx+1;
+
+			} else {
+				t = heap[2*idx+2];
+				heap[2*idx+2] = heap[idx];
+				heap[idx] = t;
+				// console.log(`Smallest ${2*idx+2}`);
+				idx = 2*idx+2;
+			}
+		}
+
+		return null; // it should never go here except empty
+	}
+
+	// * TODO troubleshoot it adding bottom up
+	// for (let u in graph.edges) {
+//		for (let {v, w} of graph.edges[u]) {
+			// heap.push({u, v, w}); // bottom up
+			// heapInsert({u,v, w}); // top to bottom
+		// }
+	 //}
+
+	//    0
+	//  1   2 // left desc 2*i + 1, right 2*i + 2
+	// 3 4 5 6
+
+	// parent (i-1) >> 1;
+
+	// console.log('Heap', heap);
+
+	/**
+	let t;
+	for (let i = heap.length - 1; i >= 1; i--) {
+		let idx = (i - 1) >> 1; // divided by 2
+		console.log(`i: ${i}, idx: ${idx}`);
+		console.log(`Parent: ${heap[idx].w}, item: ${heap[i].w}`);
+		if (heap[idx].w > heap[i].w) {
+			console.log(`Replaced`);
+			t = heap[idx];
+			heap[idx] = heap[i];
+			heap[i] = t;
+		}
+	}
+
+	for (let i = heap.length - 1; i >= 1; i--) {
+		let idx = (i - 1) >> 1; // divided by 2
+		console.log(`i: ${i}, idx: ${idx}`);
+		console.log(`Parent: ${heap[idx].w}, item: ${heap[i].w}`);
+		if (heap[idx].w > heap[i].w) {
+			console.log(`Bad...`);
+		}
+	}
+	**/
+	// while (heap.length) {
+	//	console.log(popSmallest());
+	// }
+
+	let remaining = new Set(graph.nodes);
+
+	console.log('Nodes', remaining);
+	let mst = {};
+	let sets = 1;
+
+	function addEdges(u) {
+		// console.log(`Adding edges for ${u}`);
+		for (let el of graph.edges[u]) {
+			// console.log(`Edge`, el);
+			if (typeof(mst[el.v]) === 'number') {
+				continue;
+			}
+			heapInsert({u: u, v: el.v, w: el.w});
+		}	
+	}
+
+	let sum = 0;
+	while (remaining.size) {
+		let u = null;
+		for (let el of remaining) {
+			u = el;
+			break;
+		}
+		remaining.delete(u);
+		mst[u] = sets;
+		addEdges(u);
+		while (true) {
+			let r = popSmallest();
+			console.log('Edge', r);
+			if (!r) {
+				break;
+			}
+
+			let {u,v,w} = r;
+			if (typeof(mst[v]) === 'number') {
+				console.log('Already added v...');
+				continue;
+			}
+
+			mst[v] = sets;
+			remaining.delete(v);
+			sum += w;
+			addEdges(v);
+		}
+	}
+
+	console.log(`Weight ${sum}`);
+	console.log('MST', mst);
+	console.log('Remaining', remaining);
+	
+
+}
+
+function processLine(graph, line) {
+	// console.log(`Processing line ${line}`);
+	let matches = 
+		line.match(/([a-zA-Z]+) - ([a-zA-Z]+): ([0-9.]+)/);
+	let u = matches[1];
+	let v = matches[2];
+	let w = Number.parseFloat(matches[3]);
+	graph.nodes.add(u);
+	graph.nodes.add(v);
+	// bidirectional graph
+	let outEdges = graph.edges[u] || [];
+	outEdges.push({v, w});
+	graph.edges[u] = outEdges;
+
+	outEdges = graph.edges[v] || [];
+	outEdges.push({v: u, w});
+	graph.edges[v] = outEdges;
+}
+
+function main() {
+	let buffer = "";
+	let graph = {
+		nodes: new Set(), // mapping name to idx
+		edges: {}
+	};
+	let inputPath = process.argv.length > 2 ? process.argv[2] : null;
+	console.log(`Input path ${inputPath}...`);
+	let is = inputPath ? fs.createReadStream(inputPath) : process.stdin;
+
+	is.on('data', (chunk) => {
+		// console.log(`Reading chunk ${chunk}`);
+		buffer += chunk;
+	});
+
+	is.on('end', () => {
+		// console.log('Got end of data...');
+		while (buffer.length > 0) {
+			let idx = buffer.indexOf(EOL);
+			if (idx < 0) {
+				idx = buffer.length;
+			}
+			let line = buffer.slice(0, idx);
+			processLine(graph, line);
+			buffer = buffer.slice(idx + EOL.length);
+		}
+
+		is.close();
+		findPrimMST(graph);
+	});
+}
+
+main();
\ No newline at end of file
diff --git a/2022/03/algorithms/prim_01.txt b/2022/03/algorithms/prim_01.txt
new file mode 100644
index 0000000..49409dd
--- /dev/null
+++ b/2022/03/algorithms/prim_01.txt
@@ -0,0 +1,11 @@
+A - B: 7
+A - D: 5
+B - C: 8
+B - E: 7
+B - D: 9
+C - E: 5
+D - E: 15
+D - F: 6
+E - F: 8
+E - G: 9
+F - G: 11
\ No newline at end of file