@skenvy/collatz
Version:
Functions related to the Collatz/Syracuse/3N+1 problem, implemented in JavaScript.
155 lines (154 loc) • 8.11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TreeGraphNode = void 0;
const utilities_js_1 = require("./utilities.js");
const function_js_1 = require("./function.js");
/**
* Nodes that form a "tree graph", structured as a tree, with their own node's value,
* as well as references to either possible child node, where a node can only ever have
* two children, as there are only ever two reverse values. Also records any possible
* "terminal sequence state", whether that be that the "orbit distance" has been reached,
* as an "out of bounds" stop, which is the regularly expected terminal state. Other
* terminal states possible however include the cycle state and cycle length (end) states.
*/
class TreeGraphNode {
/**
* Create an instance of TreeGraphNode which will yield its entire sub-tree of all child nodes.
* This is used internally by itself and the public constructor to pass the cycle checking map,
* recursively determining subsequent child nodes.
* @param nodeValue - The value for which to find the tree graph node reversal.
* @param maxOrbitDistance - The maximum distance/orbit/branch length to travel.
* @param P - Modulus used to devide n, iff n is equivalent to (0 mod P).
* @param a - Factor by which to multiply n.
* @param b - Value to add to the scaled value of n.
* @param cycleCheck - Checks if this node's value already occurred.
* @param createManually - Create an instance of TreeGraphNode by directly passing it the values
* required to instantiate, intended to be used in testing by manually creating trees in reverse,
* by passing expected child nodes to their parents until the entire expected tree is created.
* @param terminalSequenceState - The expected sequence state;
* null, MAX_STOP_OUT_OF_BOUNDS, CYCLE_INIT or CYCLE_LENGTH.
* @param preNDivPNode - The expected "Pre N/P" child node.
* @param preANplusBNode - The expected "Pre aN+b" child node.
* @returns the tree graph node and its subtree, computed for the parameters provided.
* @throws FailedSaneParameterCheck
* Thrown if either P or a are 0.
*/
constructor(nodeValue, maxOrbitDistance, P, a, b, cycleCheck, createManually, terminalSequenceState, preNDivPNode, preANplusBNode) {
/** The terminal state; null if not a terminal node, MAX_STOP_OUT_OF_BOUNDS if the maxOrbitDistance
* has been reached, CYCLE_LENGTH if the node's value is found to have occured previously, or
* CYCLE_INIT, retroactively applied when a CYCLE_LENGTH state node is found. */
// The only variable of TreeGraphNode to not be "final" as it must be possible to update retroactively.
this.terminalSequenceState = null;
this.nodeValue = nodeValue;
this.cycleCheck = cycleCheck;
if (createManually) {
this.terminalSequenceState = terminalSequenceState;
this.preNDivPNode = preNDivPNode;
this.preANplusBNode = preANplusBNode;
}
else {
this.cycleCheck = cycleCheck;
if (this.cycleCheck.has(this.nodeValue)) {
const cycleInitNode = this.cycleCheck.get(this.nodeValue);
if (cycleInitNode != null) {
cycleInitNode.terminalSequenceState = utilities_js_1.SequenceState.CYCLE_INIT;
}
this.terminalSequenceState = utilities_js_1.SequenceState.CYCLE_LENGTH;
this.preNDivPNode = null;
this.preANplusBNode = null;
}
else if (Math.max(0, maxOrbitDistance) === 0) {
this.terminalSequenceState = utilities_js_1.SequenceState.MAX_STOP_OUT_OF_BOUNDS;
this.preNDivPNode = null;
this.preANplusBNode = null;
}
else {
this.cycleCheck.set(this.nodeValue, this);
this.terminalSequenceState = null;
const reverses = (0, function_js_1.reverseFunction)({ n: nodeValue, P: P, a: a, b: b });
this.preNDivPNode = new TreeGraphNode(reverses[0], maxOrbitDistance - 1, P, a, b, this.cycleCheck, false, null, null, null);
if (reverses.length === 2) {
this.preANplusBNode = new TreeGraphNode(reverses[1], maxOrbitDistance - 1, P, a, b, this.cycleCheck, false, null, null, null);
}
else {
this.preANplusBNode = null;
}
}
}
}
/**
* Create an instance of TreeGraphNode which will yield its entire sub-tree of all child nodes.
* @param nodeValue - The value for which to find the tree graph node reversal.
* @param maxOrbitDistance - The maximum distance/orbit/branch length to travel.
* @param P - Modulus used to devide n, iff n is equivalent to (0 mod P).
* @param a - Factor by which to multiply n.
* @param b - Value to add to the scaled value of n.
* @returns the tree graph node and its subtree, computed for the parameters provided.
* @throws FailedSaneParameterCheck
* Thrown if either P or a are 0.
*/
static new(nodeValue, maxOrbitDistance, P, a, b) {
return new TreeGraphNode(nodeValue, maxOrbitDistance, P, a, b, new Map(), false, null, null, null);
}
/**
* This is used internally by itself and the public constructor to pass the cycle checking map,
* recursively determining subsequent child nodes.
* @param nodeValue - The value for which to find the tree graph node reversal.
* @param terminalSequenceState - The expected sequence state;
* null, MAX_STOP_OUT_OF_BOUNDS, CYCLE_INIT or CYCLE_LENGTH.
* @param preNDivPNode - The expected "Pre N/P" child node.
* @param preANplusBNode - The expected "Pre aN+b" child node.
* @returns the tree graph node and its subtree, where the subtrees are passed in.
* @throws FailedSaneParameterCheck
* Thrown if either P or a are 0.
*/
static newTest(nodeValue, terminalSequenceState, preNDivPNode, preANplusBNode) {
return new TreeGraphNode(nodeValue, 0, 0n, 0n, 0n, TreeGraphNode.emptyMap, true, terminalSequenceState, preNDivPNode, preANplusBNode);
}
/**
* This will only confirm an equality if the whole subtree of both nodes, including
* node values, sequence states, and child nodes, checked recursively, are equal.
* It ignores the cycle checking map, which is purely a utility variable.
* @param tgn - The TreeGraphNode with which to compare equality.
* @returns true, if the entire sub-trees are equal.
*/
subTreeEquals(tgn) {
if (tgn === null) {
return false;
}
if (this.nodeValue !== tgn.nodeValue || this.terminalSequenceState !== tgn.terminalSequenceState) {
return false;
}
if (this.preNDivPNode === null && tgn.preNDivPNode !== null) {
return false;
}
if (this.preNDivPNode !== null && !this.preNDivPNode.subTreeEquals(tgn.preNDivPNode)) {
return false;
}
if (this.preANplusBNode == null && tgn.preANplusBNode != null) {
return false;
}
if (this.preANplusBNode != null && !this.preANplusBNode.subTreeEquals(tgn.preANplusBNode)) {
return false;
}
return true;
}
/**
* Traverse a tree and assign the cycle map manually on all nodes.
* @param cycleCheck - The map to retroactively assign to all nodes in a test tree.
*/
copyActualTreesCycleMapIntoTestTree(actualTree) {
this.cycleCheck = actualTree.cycleCheck;
if (this.preNDivPNode !== null) {
this.preNDivPNode.copyActualTreesCycleMapIntoTestTree(actualTree);
}
if (this.preANplusBNode != null) {
this.preANplusBNode.copyActualTreesCycleMapIntoTestTree(actualTree);
}
}
}
exports.TreeGraphNode = TreeGraphNode;
TreeGraphNode.emptyMap = new Map();
exports.default = {
TreeGraphNode,
};