UNPKG

binary-decision-diagram

Version:

A library to create, minimize and optimize binary decision diagrams

141 lines (117 loc) 4.52 kB
import { RootNode } from './root-node.js'; import { AbstractNode } from './abstract-node.js'; import type { NonRootNode, NonLeafNode } from './types.js'; import { InternalNode } from './internal-node.js'; /** * run some tests on the bdd * to ensure everything is correct */ export function ensureCorrectBdd(bdd: RootNode) { const jsonString = JSON.stringify(bdd.toJSON(true)); let allNodes: AbstractNode[] = []; const nodesById: Map<string, AbstractNode> = new Map(); bdd.getLevels().forEach(level => { const levelNodes = bdd.getNodesOfLevel(level); levelNodes.forEach(node => { nodesById.set(node.id, node); }); allNodes = allNodes.concat(levelNodes); }); const recursiveNodes = getNodesRecursive(bdd); if (allNodes.length !== recursiveNodes.size) { const allNodesIds = allNodes.map(n => n.id).sort(); const recursiveNodesIds = Array.from(recursiveNodes).map(n => n.id).sort(); const nodesOnlyInRecursive: string[] = recursiveNodesIds.filter(id => !allNodesIds.includes(id)); // console.log(JSON.stringify(allNodes.map(n => n.id).sort(), null, 2)); // console.log(JSON.stringify(Array.from(recursiveNodes).map(n => n.id).sort(), null, 2)); if (recursiveNodes.size > allNodes.length) { const firstId = nodesOnlyInRecursive[0]; const referenceToFirst = allNodes.find(n => { if (n.isInternalNode()) { return (n as InternalNode).branches.hasNodeIdAsBranch(firstId); } return false; }); console.log('referenceToFirst:'); referenceToFirst?.log(); } throw new Error( 'ensureCorrectBdd() ' + 'nodes in list not equal size to recursive nodes ' + 'allNodes: ' + allNodes.length + ' ' + 'recursiveNodes: ' + recursiveNodes.size + ' ' + 'nodesOnlyInRecursive: ' + nodesOnlyInRecursive.join(', ') + ' ' ); } allNodes.forEach(node => { if (node.isRootNode()) { return; } const useNode = node as NonRootNode; if (node.deleted) { throw new Error( 'ensureCorrectBdd() ' + 'bdd includes a deleted node' ); } // each node should have a parent if (useNode.parents.size === 0) { throw new Error( 'ensureCorrectBdd() ' + 'node has no parent ' + useNode.id ); } if (useNode.isInternalNode()) { const internalNode: InternalNode = useNode as any; const bothBranches = internalNode.branches.getBothBranches(); // a node should not have 2 equal branches if (internalNode.branches.areBranchesStrictEqual()) { throw new Error( 'ensureCorrectBdd() ' + 'node has two equal branches: ' + bothBranches.map(n => n.id).join(', ') ); } // each branch should have the node as parent bothBranches.forEach(branch => { if (!branch.parents.has(internalNode)) { throw new Error( 'ensureCorrectBdd() ' + 'branch must have the node as parent' ); } }); } // each parent should have the child as branch useNode.parents.getAll().forEach(parent => { if (!parent.branches.hasBranchAsNode(useNode)) { throw new Error( 'ensureCorrectBdd() ' + 'parent node does not have child as branch' ); } }); }); if (jsonString.includes('"deleted":true')) { throw new Error( 'ensureCorrectBdd() ' + 'bdd includes a deleted node' ); } } export function getNodesRecursive( node: AbstractNode, set: Set<AbstractNode> = new Set() ): Set<AbstractNode> { set.add(node); if (!node.isLeafNode()) { const useNode = node as NonLeafNode; const branch1 = useNode.branches.getBranch('0'); set.add(branch1); getNodesRecursive(branch1, set); const branch2 = useNode.branches.getBranch('1'); set.add(branch2); getNodesRecursive(branch2, set); } return set; }