getTrees = {
const TREE_CACHE = {
get(nodeCount, leafCount) {
return this[[nodeCount, leafCount]];
},
set(nodeCount, leafCount, trees) {
this[[nodeCount, leafCount]] = trees;
}
};
function *allTrees(nodeCount, leafCount) {
if (nodeCount === 1) {
if (leafCount === 1) {
yield { tree: 1, index: 0 };
}
} else {
let i = 0;
for (const children of remainingChildren(nodeCount - 1, leafCount)) {
yield { tree: children, index: i++ };
}
}
}
function *getTrees(nodeCount, leafCount) {
const cached = TREE_CACHE.get(nodeCount, leafCount);
if (cached) {
for (let index = 0; index < cached.length; index++) {
yield { tree: cached[index], index };
}
} else {
const trees = [];
TREE_CACHE.set(nodeCount, leafCount, trees);
for (const { tree, index } of allTrees(nodeCount, leafCount)) {
trees[index] = tree;
yield { tree, index };
}
}
}
// Read trees from the cache for (nodeCount, leafCount) up to maxIndex inclusive.
// The full list of trees does not have to be generated yet, as long as it goes up
// to at least maxIndex.
function *getTreesPartial(nodeCount, leafCount, maxIndex) {
const cached = TREE_CACHE.get(nodeCount, leafCount);
if (!(cached && maxIndex < cached.length)) {
throw new Error(`${nodeCount} ${leafCount}`);
}
for (let index = 0; index <= maxIndex; index++) {
yield { tree: cached[index], index };
}
}
function *remainingChildren(nodesLeft, leavesLeft, [prevNodeCount, prevLeafCount, prevIndex] = [0, 0, 0]) {
if (nodesLeft === 0 && leavesLeft === 0) {
yield [];
return;
}
if (nodesLeft < leavesLeft) {
return;
}
// only generate further children with:
// - non-decreasing node count;
// - same node count and non-decreasing leaf count; or
// - same node and leaf count but non-increasing tree index
// note: the third case only becomes important when leavesLeft >= 4
const minNodeCount = prevNodeCount || 1;
for (let nodeCount = minNodeCount; nodeCount <= nodesLeft; nodeCount++) {
const minLeafCount = nodeCount === prevNodeCount ? prevLeafCount : 1;
for (let leafCount = minLeafCount; leafCount <= leavesLeft; leafCount++) {
const trees = (nodeCount === prevNodeCount && leafCount === prevLeafCount) ?
getTreesPartial(nodeCount, leafCount, prevIndex) : getTrees(nodeCount, leafCount);
for (const { tree, index } of trees) {
for (const rest of remainingChildren(
nodesLeft - nodeCount,
leavesLeft - leafCount,
[nodeCount, leafCount, index])) {
yield [tree, ...rest];
}
}
}
}
}
return getTrees;
}