src/3-tree/implementations/2-Deep.js
import assert from 'assert';
import {Tree} from '../base/index.js';
import {
_app3,
_from_digit,
_from_small_list,
_deepL,
_deepR,
CachedMeasure,
Split,
} from '../../0-core/index.js';
import {One, Two, Four} from '../../1-digit/index.js';
import {delay, Lazy} from '../../4-lazy/index.js';
import {Empty} from './index.js';
export function Deep(M, left, middle, right) {
assert(middle instanceof Lazy || middle.M instanceof CachedMeasure);
this.M = M;
this.left = left;
this.middle = middle;
this.right = right;
this.v = null;
}
Deep.prototype = new Tree();
Deep.prototype.measure = function () {
if (this.v === null) {
const M = this.M;
this.v = M.plus(
this.left.measure(M),
M.plus(this.middle.measure(), this.right.measure(M)),
);
}
return this.v;
};
Deep.prototype.isEmpty = function () {
return false;
};
Deep.prototype.head = function () {
return this.left.head();
};
Deep.prototype.last = function () {
return this.right.last();
};
Deep.prototype.tail = function () {
if (this.left instanceof One) {
if (this.middle.isEmpty()) {
return _from_digit(this.M, this.right);
}
return new Deep(
this.M,
this.middle.head().digit(),
delay(() => this.middle.tail()),
this.right,
);
}
return new Deep(this.M, this.left.tail(), this.middle, this.right);
};
Deep.prototype.init = function () {
if (this.right instanceof One) {
if (this.middle.isEmpty()) {
return _from_digit(this.M, this.left);
}
return new Deep(
this.M,
this.left,
delay(() => this.middle.init()),
this.middle.last().digit(),
);
}
return new Deep(this.M, this.left, this.middle, this.right.init());
};
Deep.prototype.cons = function (value) {
if (this.left instanceof Four) {
return new Deep(
this.M,
new Two(value, this.left.head()),
this.middle.cons(this.left.tail().node(this.M)),
this.right,
);
}
return new Deep(this.M, this.left.cons(value), this.middle, this.right);
};
Deep.prototype.push = function (value) {
if (this.right instanceof Four) {
return new Deep(
this.M,
this.left,
this.middle.push(this.right.init().node(this.M)),
new Two(this.right.last(), value),
);
}
return new Deep(this.M, this.left, this.middle, this.right.push(value));
};
Deep.prototype.concat = function (other) {
return _app3(this, other);
};
Deep.prototype[Symbol.iterator] = function* () {
yield* this.left;
for (const node of this.middle) yield* node;
yield* this.right;
};
Deep.prototype.reversed = function* () {
yield* this.right.reversed();
for (const node of this.middle.reversed()) yield* node.reversed();
yield* this.left.reversed();
};
/**
* It is assumed that p(i+|this|) is true.
*/
Deep.prototype.splitTree = function (p, i) {
assert(p(this.M.plus(i, this.measure())));
const {left, middle, right, M} = this;
// See if the split point is inside the left tree
const leftMeasure = M.plus(i, left.measure(M));
if (p(leftMeasure)) {
const split = left.splitDigit(p, i, M);
return new Split(
_from_small_list(M, split.left),
split.middle,
_deepL(M, split.right, middle, right),
);
}
// See if the split point is inside the middle tree
const midMeasure = M.plus(leftMeasure, middle.measure());
if (p(midMeasure)) {
const midSplit = middle.splitTree(p, leftMeasure);
// Midsplit.middle is a Node since middle is a Tree ( Node a )
const split = midSplit.middle
.digit()
.splitDigit(p, M.plus(leftMeasure, midSplit.left.measure()), M);
return new Split(
_deepR(M, left, midSplit.left, split.left),
split.middle,
_deepL(M, split.right, midSplit.right, right),
);
}
// The split point is in the right tree
const split = right.splitDigit(p, midMeasure, M);
return new Split(
_deepR(M, left, middle, split.left),
split.middle,
_from_small_list(M, split.right),
);
};
Deep.prototype.split = function (p) {
if (p(this.measure())) {
const split = this.splitTree(p, this.M.zero());
return [split.left, split.right.cons(split.middle)];
}
return [this, new Empty(this.M)];
};