Home Manual Reference Source

src/seq.js

export function seq(empty, size) {
	const Seq = function (tree) {
		this.tree = tree;
	};

	Seq.prototype.isEmpty = function () {
		return this.tree.isEmpty();
	};

	Seq.prototype.measure = Seq.prototype.len = function () {
		return this.tree.measure();
	};

	Seq.prototype.head = function () {
		return this.tree.head();
	};

	Seq.prototype.tail = function () {
		return new Seq(this.tree.tail());
	};

	Seq.prototype.last = function () {
		return this.tree.last();
	};

	Seq.prototype.init = function () {
		return new Seq(this.tree.init());
	};

	Seq.prototype.cons = function (value) {
		return new Seq(this.tree.cons(value));
	};

	Seq.prototype.push = function (value) {
		return new Seq(this.tree.push(value));
	};

	Seq.prototype.append = function (iterable) {
		return new Seq(this.tree.append(iterable));
	};

	Seq.prototype.prepend = function (iterable) {
		return new Seq(this.tree.prepend(iterable));
	};

	Seq.prototype.concat = function (other) {
		return new Seq(this.tree.concat(other.tree));
	};

	Seq.prototype.takeUntil = function (predicate) {
		return new Seq(this.tree.takeUntil(predicate));
	};

	Seq.prototype.dropUntil = function (predicate) {
		return new Seq(this.tree.dropUntil(predicate));
	};

	Seq.prototype.set = function (index, value) {
		if (index < 0 || index >= this.len())
			throw new Error(`wrong index '${index}'`);

		const split = this.tree.splitTree((m) => m > index, size.zero());

		return new Seq(split.left.push(value).concat(split.right));
	};

	Seq.prototype.get = function (index) {
		if (index < 0 || index >= this.len())
			throw new Error(`wrong index '${index}'`);

		return this.tree.splitTree((m) => m > index, size.zero()).middle;
	};

	Seq.prototype.insert = function (index, value) {
		if (index < 0 || index >= this.len())
			throw new Error(`wrong index '${index}'`);

		const [prefix, rest] = this.tree.split((m) => m > index);
		return new Seq(prefix.push(value).concat(rest));
	};

	Seq.prototype.remove = function (index) {
		if (index < 0 || index >= this.len())
			throw new Error(`wrong index '${index}'`);

		const {left, right} = this.tree.splitTree((m) => m > index, size.zero());
		return new Seq(left.concat(right));
	};

	Seq.prototype.slice = function (start = 0, end = this.len()) {
		if (start < -this.len() || start > this.len())
			throw new Error(`wrong start '${start}'`);
		if (end < -this.len() || end > this.len())
			throw new Error(`wrong end '${end}'`);

		if (start < 0) start += this.len();
		if (end < 0) end += this.len();

		const [, rest] = this.tree.split((m) => m > start);
		const [slice] = rest.split((m) => m > end - start);
		return new Seq(slice);
	};

	Seq.prototype.splice = function (start, deleteCount, ...items) {
		const length = this.len();

		if (start < -length || start >= length)
			throw new Error(`wrong start '${start}'`);

		if (start < 0) start += length;
		else if (start === length) {
			return [new Seq(this.tree.append(items)), new Seq(empty(size))];
		}

		const split = this.tree.splitTree((m) => m > start, size.zero());

		if (deleteCount === 1) {
			if (items.length === 0) {
				return [
					new Seq(split.left.concat(split.right)),
					new Seq(empty(size).push(split.middle)),
				];
			}

			return [
				new Seq(split.left.append(items).concat(split.right)),
				new Seq(empty(size).push(split.middle)),
			];
		}

		const prefix = split.left;
		const rest = split.right.cons(split.middle);

		if (deleteCount === 0) {
			return [new Seq(prefix.append(items).concat(rest)), new Seq(empty(size))];
		}

		if (deleteCount === undefined) {
			return [new Seq(prefix.append(items)), new Seq(rest)];
		}

		const [deleted, suffix] = rest.split((m) => m > deleteCount);
		return [new Seq(prefix.append(items).concat(suffix)), new Seq(deleted)];
	};

	Seq.prototype[Symbol.iterator] = function () {
		return this.tree[Symbol.iterator]();
	};

	Seq.prototype.split = function (predicate) {
		const [left, right] = this.tree.split(predicate);
		return [new Seq(left), new Seq(right)];
	};

	Seq.prototype.splitAt = function (index) {
		return this.split((m) => m > index);
	};

	return {
		empty: () => new Seq(empty(size)),
		from: (iterable) => new Seq(empty(size)).append(iterable),
	};
}