From d7a9df805b6184349015cc72cf50b38a47576ee7 Mon Sep 17 00:00:00 2001
From: Svornost <11434-svornost@users.noreply.gitgud.io>
Date: Tue, 20 Oct 2020 14:14:32 -0700
Subject: [PATCH] Restructure create_node_rec to skip *any* previously-created
 nodes, including for the current slave or those in its own descendant lines. 
 This makes it a bit faster and totally tolerant of inheritance cycles (which
 will throw later on anyway, with a better error message).

---
 src/js/ibcJS.js | 72 +++++++++++++++++++++++++------------------------
 1 file changed, 37 insertions(+), 35 deletions(-)

diff --git a/src/js/ibcJS.js b/src/js/ibcJS.js
index 58e02d5dbcc..654765c6b8e 100644
--- a/src/js/ibcJS.js
+++ b/src/js/ibcJS.js
@@ -1,7 +1,7 @@
 /** @typedef IBCRelative
  * An very simple object that represents a entity in a family tree.
  * Represents a group of common properties shared by SlaveState, InfantState, and PlayerState,
- * as well as fetus and genepool objects.
+ * as well as genepool objects.
  * @type {object}
  * @property {number} ID
  * @property {number} mother
@@ -292,45 +292,47 @@ globalThis.ibc = (() => {
 		 * @param {IBCRelative} s
 		 */
 		let create_node_rec = s => {
-			// Certain parents (e.g. 0, societal elite) are not considered to be related, despite
-			// having the same ID; convert them to null
-			let m = or_null(s.mother);
-			let f = or_null(s.father);
-
-			// Ensure that parent nodes are created
-			[m, f].forEach(p => {
-				if (p !== null && !(p in nodes)) { // Not created, we have to do something
-					if (p === -1) {
-						create_node_rec(V.PC);
-					} else {
-						// Search for a slave state, genePool entry, or missingTable entry
-						let gp = find_gp(p);
-						if (gp !== null) {
-							// If we find one, we might have ancestry information: recurse
-							create_node_rec(gp);
+			if (!(s.ID in nodes)) {
+				nodes[s.ID] = new IBCNode(s.ID);
+
+				// Certain parents (e.g. 0, societal elite) are not considered to be related, despite
+				// having the same ID; convert them to null
+				const m = or_null(s.mother);
+				const f = or_null(s.father);
+
+				// Ensure that parent nodes are created
+				[m, f].forEach(p => {
+					if (p !== null && !(p in nodes)) { // Not created, we have to do something
+						if (p === -1) {
+							create_node_rec(V.PC);
 						} else {
-							// Otherwise, just create a plain node
-							nodes[p] = new IBCNode(p);
+							// Search for a slave state, genePool entry, or missingTable entry
+							let gp = find_gp(p);
+							if (gp !== null) {
+								// If we find one, we might have ancestry information: recurse
+								create_node_rec(gp);
+							} else {
+								// Otherwise, just create a plain node
+								nodes[p] = new IBCNode(p);
+							}
 						}
 					}
-				}
-			});
+				});
 
-			if (!(s.ID in nodes)) // Create this node if necessary
-				nodes[s.ID] = new IBCNode(s.ID);
-			// We created its parents earlier, so set them to the actual nodes
-			nodes[s.ID].mother = (m === null) ? m : nodes[m];
-			nodes[s.ID].father = (f === null) ? f : nodes[f];
-
-			// Try to use a cached CoI for performance
-			let sg = find_gp(s.ID);
-			if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
-				nodes[s.ID]._coeff = sg.inbreedingCoeff;
+				// Set parents to the actual nodes
+				nodes[s.ID].mother = (m === null) ? m : nodes[m];
+				nodes[s.ID].father = (f === null) ? f : nodes[f];
+
+				// Try to use a cached CoI for performance
+				let sg = find_gp(s.ID);
+				if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
+					nodes[s.ID]._coeff = sg.inbreedingCoeff;
+				}
 			}
 		};
 
 		// Populate the nodes
-		slaves.forEach(s=> create_node_rec(s));
+		slaves.forEach(s => create_node_rec(s));
 
 		// Populate NodeCodes
 		make_nc(nodes);
@@ -442,13 +444,13 @@ globalThis.ibc = (() => {
 		all_slave_like.push(oldMaster);
 
 		// Gather the genetics of all current fetuses
-		/** @type {IBCRelative[]} */
+		/** @type {FC.FetusGenetics[]} */
 		let all_fetuses = V.slaves.filter(s => s.preg > 0).map(s => s.womb.map(i => i.genetics)).reduce((res, cur) => res.concat(cur), []);
 
 		/** Recursively find all of the given ID's children, born and unborn
 		 * @param {number} id
 		 * @param {Set<number>} cur_slaves
-		 * @param {Set<IBCRelative>} cur_fetuses
+		 * @param {Set<FC.FetusGenetics>} cur_fetuses
 		 * @param {Set<number>} cur_fetus_parents
 		 */
 		let find_children_rec = (id, cur_slaves, cur_fetuses, cur_fetus_parents) => {
@@ -476,7 +478,7 @@ globalThis.ibc = (() => {
 		/** @type {Set<number>} */
 		let needed_slave_ids = new Set();
 		// Since each fetus has a unique record, a set still suffices
-		/** @type {Set<IBCRelative>} */
+		/** @type {Set<FC.FetusGenetics>} */
 		let needed_fetuses = new Set();
 		/** @type {Set<number>} */
 		let needed_parent_ids = new Set();
-- 
GitLab