gdpr audit implemented, email log, vollmachten, pdf delete cancel data privacy and vollmachten, removed message no id card in engergy car, and other contracts that are not telecom contracts, added insert counter for engery
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2016 by Marijn Haverbeke <marijn@haverbeke.berlin>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# rope-sequence
|
||||
|
||||
This module implements a single data type, `RopeSequence`, which is a
|
||||
persistent sequence type implemented as a loosely-balanced
|
||||
[rope](https://www.cs.rit.edu/usr/local/pub/jeh/courses/QUARTERS/FP/Labs/CedarRope/rope-paper.pdf).
|
||||
It supports appending, prepending, and slicing without doing a full
|
||||
copy. Random access is somewhat more expensive than in an array
|
||||
(logarithmic, with some overhead), but should still be relatively
|
||||
fast.
|
||||
|
||||
Licensed under the MIT license.
|
||||
|
||||
## class `RopeSequence<T>`
|
||||
|
||||
`static `**`from`**`(?union<[T], RopeSequence<T>>) → RopeSequence<T>`
|
||||
|
||||
Create a rope representing the given array, or return the rope itself
|
||||
if a rope was given.
|
||||
|
||||
`static `**`empty`**`: RopeSequence<T>`
|
||||
|
||||
The empty rope.
|
||||
|
||||
**`length`**`: number`
|
||||
|
||||
The length of the rope.
|
||||
|
||||
**`append`**`(union<[T], RopeSequence<T>>) → RopeSequence<T>`
|
||||
|
||||
Append an array or other rope to this one, returning a new rope.
|
||||
|
||||
**`prepend`**`(union<[T], RopeSequence<T>>) → RopeSequence<T>`
|
||||
|
||||
Prepend an array or other rope to this one, returning a new rope.
|
||||
|
||||
**`slice`**`(from: ?number = 0, to: ?number = this.length) → RopeSequence<T>`
|
||||
|
||||
Create a rope repesenting a sub-sequence of this rope.
|
||||
|
||||
**`get`**`(index: number) → T`
|
||||
|
||||
Retrieve the element at the given position from this rope.
|
||||
|
||||
**`forEach`**`(f: fn(element: T, index: number) → ?bool, from: ?number, to: ?number)`
|
||||
|
||||
Call the given function for each element between the given indices.
|
||||
This tends to be more efficient than looping over the indices and
|
||||
calling `get`, because it doesn't have to descend the tree for every
|
||||
element.
|
||||
|
||||
`to` may be less then `from`, in which case the iteration will happen
|
||||
in reverse (starting at index `from - 1`, down to index `to`.
|
||||
|
||||
The iteration function may return `false` to abort iteration early.
|
||||
|
||||
**`map`**`(f: fn(element: T, index: number) → U, from: ?number, to: ?number) → [U]`
|
||||
|
||||
Map the given functions over the elements of the rope, producing a
|
||||
flat array.
|
||||
|
||||
**`flatten`**`() → [T]`
|
||||
|
||||
Return the content of this rope as an array.
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
'use strict';
|
||||
|
||||
var GOOD_LEAF_SIZE = 200;
|
||||
|
||||
// :: class<T> A rope sequence is a persistent sequence data structure
|
||||
// that supports appending, prepending, and slicing without doing a
|
||||
// full copy. It is represented as a mostly-balanced tree.
|
||||
var RopeSequence = function RopeSequence () {};
|
||||
|
||||
RopeSequence.prototype.append = function append (other) {
|
||||
if (!other.length) { return this }
|
||||
other = RopeSequence.from(other);
|
||||
|
||||
return (!this.length && other) ||
|
||||
(other.length < GOOD_LEAF_SIZE && this.leafAppend(other)) ||
|
||||
(this.length < GOOD_LEAF_SIZE && other.leafPrepend(this)) ||
|
||||
this.appendInner(other)
|
||||
};
|
||||
|
||||
// :: (union<[T], RopeSequence<T>>) → RopeSequence<T>
|
||||
// Prepend an array or other rope to this one, returning a new rope.
|
||||
RopeSequence.prototype.prepend = function prepend (other) {
|
||||
if (!other.length) { return this }
|
||||
return RopeSequence.from(other).append(this)
|
||||
};
|
||||
|
||||
RopeSequence.prototype.appendInner = function appendInner (other) {
|
||||
return new Append(this, other)
|
||||
};
|
||||
|
||||
// :: (?number, ?number) → RopeSequence<T>
|
||||
// Create a rope repesenting a sub-sequence of this rope.
|
||||
RopeSequence.prototype.slice = function slice (from, to) {
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = this.length;
|
||||
|
||||
if (from >= to) { return RopeSequence.empty }
|
||||
return this.sliceInner(Math.max(0, from), Math.min(this.length, to))
|
||||
};
|
||||
|
||||
// :: (number) → T
|
||||
// Retrieve the element at the given position from this rope.
|
||||
RopeSequence.prototype.get = function get (i) {
|
||||
if (i < 0 || i >= this.length) { return undefined }
|
||||
return this.getInner(i)
|
||||
};
|
||||
|
||||
// :: ((element: T, index: number) → ?bool, ?number, ?number)
|
||||
// Call the given function for each element between the given
|
||||
// indices. This tends to be more efficient than looping over the
|
||||
// indices and calling `get`, because it doesn't have to descend the
|
||||
// tree for every element.
|
||||
RopeSequence.prototype.forEach = function forEach (f, from, to) {
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = this.length;
|
||||
|
||||
if (from <= to)
|
||||
{ this.forEachInner(f, from, to, 0); }
|
||||
else
|
||||
{ this.forEachInvertedInner(f, from, to, 0); }
|
||||
};
|
||||
|
||||
// :: ((element: T, index: number) → U, ?number, ?number) → [U]
|
||||
// Map the given functions over the elements of the rope, producing
|
||||
// a flat array.
|
||||
RopeSequence.prototype.map = function map (f, from, to) {
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = this.length;
|
||||
|
||||
var result = [];
|
||||
this.forEach(function (elt, i) { return result.push(f(elt, i)); }, from, to);
|
||||
return result
|
||||
};
|
||||
|
||||
// :: (?union<[T], RopeSequence<T>>) → RopeSequence<T>
|
||||
// Create a rope representing the given array, or return the rope
|
||||
// itself if a rope was given.
|
||||
RopeSequence.from = function from (values) {
|
||||
if (values instanceof RopeSequence) { return values }
|
||||
return values && values.length ? new Leaf(values) : RopeSequence.empty
|
||||
};
|
||||
|
||||
var Leaf = /*@__PURE__*/(function (RopeSequence) {
|
||||
function Leaf(values) {
|
||||
RopeSequence.call(this);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
if ( RopeSequence ) Leaf.__proto__ = RopeSequence;
|
||||
Leaf.prototype = Object.create( RopeSequence && RopeSequence.prototype );
|
||||
Leaf.prototype.constructor = Leaf;
|
||||
|
||||
var prototypeAccessors = { length: { configurable: true },depth: { configurable: true } };
|
||||
|
||||
Leaf.prototype.flatten = function flatten () {
|
||||
return this.values
|
||||
};
|
||||
|
||||
Leaf.prototype.sliceInner = function sliceInner (from, to) {
|
||||
if (from == 0 && to == this.length) { return this }
|
||||
return new Leaf(this.values.slice(from, to))
|
||||
};
|
||||
|
||||
Leaf.prototype.getInner = function getInner (i) {
|
||||
return this.values[i]
|
||||
};
|
||||
|
||||
Leaf.prototype.forEachInner = function forEachInner (f, from, to, start) {
|
||||
for (var i = from; i < to; i++)
|
||||
{ if (f(this.values[i], start + i) === false) { return false } }
|
||||
};
|
||||
|
||||
Leaf.prototype.forEachInvertedInner = function forEachInvertedInner (f, from, to, start) {
|
||||
for (var i = from - 1; i >= to; i--)
|
||||
{ if (f(this.values[i], start + i) === false) { return false } }
|
||||
};
|
||||
|
||||
Leaf.prototype.leafAppend = function leafAppend (other) {
|
||||
if (this.length + other.length <= GOOD_LEAF_SIZE)
|
||||
{ return new Leaf(this.values.concat(other.flatten())) }
|
||||
};
|
||||
|
||||
Leaf.prototype.leafPrepend = function leafPrepend (other) {
|
||||
if (this.length + other.length <= GOOD_LEAF_SIZE)
|
||||
{ return new Leaf(other.flatten().concat(this.values)) }
|
||||
};
|
||||
|
||||
prototypeAccessors.length.get = function () { return this.values.length };
|
||||
|
||||
prototypeAccessors.depth.get = function () { return 0 };
|
||||
|
||||
Object.defineProperties( Leaf.prototype, prototypeAccessors );
|
||||
|
||||
return Leaf;
|
||||
}(RopeSequence));
|
||||
|
||||
// :: RopeSequence
|
||||
// The empty rope sequence.
|
||||
RopeSequence.empty = new Leaf([]);
|
||||
|
||||
var Append = /*@__PURE__*/(function (RopeSequence) {
|
||||
function Append(left, right) {
|
||||
RopeSequence.call(this);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.length = left.length + right.length;
|
||||
this.depth = Math.max(left.depth, right.depth) + 1;
|
||||
}
|
||||
|
||||
if ( RopeSequence ) Append.__proto__ = RopeSequence;
|
||||
Append.prototype = Object.create( RopeSequence && RopeSequence.prototype );
|
||||
Append.prototype.constructor = Append;
|
||||
|
||||
Append.prototype.flatten = function flatten () {
|
||||
return this.left.flatten().concat(this.right.flatten())
|
||||
};
|
||||
|
||||
Append.prototype.getInner = function getInner (i) {
|
||||
return i < this.left.length ? this.left.get(i) : this.right.get(i - this.left.length)
|
||||
};
|
||||
|
||||
Append.prototype.forEachInner = function forEachInner (f, from, to, start) {
|
||||
var leftLen = this.left.length;
|
||||
if (from < leftLen &&
|
||||
this.left.forEachInner(f, from, Math.min(to, leftLen), start) === false)
|
||||
{ return false }
|
||||
if (to > leftLen &&
|
||||
this.right.forEachInner(f, Math.max(from - leftLen, 0), Math.min(this.length, to) - leftLen, start + leftLen) === false)
|
||||
{ return false }
|
||||
};
|
||||
|
||||
Append.prototype.forEachInvertedInner = function forEachInvertedInner (f, from, to, start) {
|
||||
var leftLen = this.left.length;
|
||||
if (from > leftLen &&
|
||||
this.right.forEachInvertedInner(f, from - leftLen, Math.max(to, leftLen) - leftLen, start + leftLen) === false)
|
||||
{ return false }
|
||||
if (to < leftLen &&
|
||||
this.left.forEachInvertedInner(f, Math.min(from, leftLen), to, start) === false)
|
||||
{ return false }
|
||||
};
|
||||
|
||||
Append.prototype.sliceInner = function sliceInner (from, to) {
|
||||
if (from == 0 && to == this.length) { return this }
|
||||
var leftLen = this.left.length;
|
||||
if (to <= leftLen) { return this.left.slice(from, to) }
|
||||
if (from >= leftLen) { return this.right.slice(from - leftLen, to - leftLen) }
|
||||
return this.left.slice(from, leftLen).append(this.right.slice(0, to - leftLen))
|
||||
};
|
||||
|
||||
Append.prototype.leafAppend = function leafAppend (other) {
|
||||
var inner = this.right.leafAppend(other);
|
||||
if (inner) { return new Append(this.left, inner) }
|
||||
};
|
||||
|
||||
Append.prototype.leafPrepend = function leafPrepend (other) {
|
||||
var inner = this.left.leafPrepend(other);
|
||||
if (inner) { return new Append(inner, this.right) }
|
||||
};
|
||||
|
||||
Append.prototype.appendInner = function appendInner (other) {
|
||||
if (this.left.depth >= Math.max(this.right.depth, other.depth) + 1)
|
||||
{ return new Append(this.left, new Append(this.right, other)) }
|
||||
return new Append(this, other)
|
||||
};
|
||||
|
||||
return Append;
|
||||
}(RopeSequence));
|
||||
|
||||
module.exports = RopeSequence;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
export default class RopeSequence<T> {
|
||||
length: number
|
||||
prepend(other: RopeSequence<T> | readonly T[]): RopeSequence<T>
|
||||
append(other: RopeSequence<T> | readonly T[]): RopeSequence<T>
|
||||
slice(from: number, to?: number): RopeSequence<T>
|
||||
get(i: number): T
|
||||
forEach(f: (elt: T, index: number) => boolean | void, from?: number, to?: number): void
|
||||
map<U>(f: (elt: T, index: number) => U, from?: number, to?: number): U[]
|
||||
static from<T>(value: readonly T[] | RopeSequence<T>): RopeSequence<T>
|
||||
static empty: RopeSequence<any>
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
export default class RopeSequence<T> {
|
||||
length: number
|
||||
prepend(other: RopeSequence<T> | readonly T[]): RopeSequence<T>
|
||||
append(other: RopeSequence<T> | readonly T[]): RopeSequence<T>
|
||||
slice(from: number, to?: number): RopeSequence<T>
|
||||
get(i: number): T
|
||||
forEach(f: (elt: T, index: number) => boolean | void, from?: number, to?: number): void
|
||||
map<U>(f: (elt: T, index: number) => U, from?: number, to?: number): U[]
|
||||
static from<T>(value: readonly T[] | RopeSequence<T>): RopeSequence<T>
|
||||
static empty: RopeSequence<any>
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
var GOOD_LEAF_SIZE = 200;
|
||||
|
||||
// :: class<T> A rope sequence is a persistent sequence data structure
|
||||
// that supports appending, prepending, and slicing without doing a
|
||||
// full copy. It is represented as a mostly-balanced tree.
|
||||
var RopeSequence = function RopeSequence () {};
|
||||
|
||||
RopeSequence.prototype.append = function append (other) {
|
||||
if (!other.length) { return this }
|
||||
other = RopeSequence.from(other);
|
||||
|
||||
return (!this.length && other) ||
|
||||
(other.length < GOOD_LEAF_SIZE && this.leafAppend(other)) ||
|
||||
(this.length < GOOD_LEAF_SIZE && other.leafPrepend(this)) ||
|
||||
this.appendInner(other)
|
||||
};
|
||||
|
||||
// :: (union<[T], RopeSequence<T>>) → RopeSequence<T>
|
||||
// Prepend an array or other rope to this one, returning a new rope.
|
||||
RopeSequence.prototype.prepend = function prepend (other) {
|
||||
if (!other.length) { return this }
|
||||
return RopeSequence.from(other).append(this)
|
||||
};
|
||||
|
||||
RopeSequence.prototype.appendInner = function appendInner (other) {
|
||||
return new Append(this, other)
|
||||
};
|
||||
|
||||
// :: (?number, ?number) → RopeSequence<T>
|
||||
// Create a rope repesenting a sub-sequence of this rope.
|
||||
RopeSequence.prototype.slice = function slice (from, to) {
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = this.length;
|
||||
|
||||
if (from >= to) { return RopeSequence.empty }
|
||||
return this.sliceInner(Math.max(0, from), Math.min(this.length, to))
|
||||
};
|
||||
|
||||
// :: (number) → T
|
||||
// Retrieve the element at the given position from this rope.
|
||||
RopeSequence.prototype.get = function get (i) {
|
||||
if (i < 0 || i >= this.length) { return undefined }
|
||||
return this.getInner(i)
|
||||
};
|
||||
|
||||
// :: ((element: T, index: number) → ?bool, ?number, ?number)
|
||||
// Call the given function for each element between the given
|
||||
// indices. This tends to be more efficient than looping over the
|
||||
// indices and calling `get`, because it doesn't have to descend the
|
||||
// tree for every element.
|
||||
RopeSequence.prototype.forEach = function forEach (f, from, to) {
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = this.length;
|
||||
|
||||
if (from <= to)
|
||||
{ this.forEachInner(f, from, to, 0); }
|
||||
else
|
||||
{ this.forEachInvertedInner(f, from, to, 0); }
|
||||
};
|
||||
|
||||
// :: ((element: T, index: number) → U, ?number, ?number) → [U]
|
||||
// Map the given functions over the elements of the rope, producing
|
||||
// a flat array.
|
||||
RopeSequence.prototype.map = function map (f, from, to) {
|
||||
if ( from === void 0 ) from = 0;
|
||||
if ( to === void 0 ) to = this.length;
|
||||
|
||||
var result = [];
|
||||
this.forEach(function (elt, i) { return result.push(f(elt, i)); }, from, to);
|
||||
return result
|
||||
};
|
||||
|
||||
// :: (?union<[T], RopeSequence<T>>) → RopeSequence<T>
|
||||
// Create a rope representing the given array, or return the rope
|
||||
// itself if a rope was given.
|
||||
RopeSequence.from = function from (values) {
|
||||
if (values instanceof RopeSequence) { return values }
|
||||
return values && values.length ? new Leaf(values) : RopeSequence.empty
|
||||
};
|
||||
|
||||
var Leaf = /*@__PURE__*/(function (RopeSequence) {
|
||||
function Leaf(values) {
|
||||
RopeSequence.call(this);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
if ( RopeSequence ) Leaf.__proto__ = RopeSequence;
|
||||
Leaf.prototype = Object.create( RopeSequence && RopeSequence.prototype );
|
||||
Leaf.prototype.constructor = Leaf;
|
||||
|
||||
var prototypeAccessors = { length: { configurable: true },depth: { configurable: true } };
|
||||
|
||||
Leaf.prototype.flatten = function flatten () {
|
||||
return this.values
|
||||
};
|
||||
|
||||
Leaf.prototype.sliceInner = function sliceInner (from, to) {
|
||||
if (from == 0 && to == this.length) { return this }
|
||||
return new Leaf(this.values.slice(from, to))
|
||||
};
|
||||
|
||||
Leaf.prototype.getInner = function getInner (i) {
|
||||
return this.values[i]
|
||||
};
|
||||
|
||||
Leaf.prototype.forEachInner = function forEachInner (f, from, to, start) {
|
||||
for (var i = from; i < to; i++)
|
||||
{ if (f(this.values[i], start + i) === false) { return false } }
|
||||
};
|
||||
|
||||
Leaf.prototype.forEachInvertedInner = function forEachInvertedInner (f, from, to, start) {
|
||||
for (var i = from - 1; i >= to; i--)
|
||||
{ if (f(this.values[i], start + i) === false) { return false } }
|
||||
};
|
||||
|
||||
Leaf.prototype.leafAppend = function leafAppend (other) {
|
||||
if (this.length + other.length <= GOOD_LEAF_SIZE)
|
||||
{ return new Leaf(this.values.concat(other.flatten())) }
|
||||
};
|
||||
|
||||
Leaf.prototype.leafPrepend = function leafPrepend (other) {
|
||||
if (this.length + other.length <= GOOD_LEAF_SIZE)
|
||||
{ return new Leaf(other.flatten().concat(this.values)) }
|
||||
};
|
||||
|
||||
prototypeAccessors.length.get = function () { return this.values.length };
|
||||
|
||||
prototypeAccessors.depth.get = function () { return 0 };
|
||||
|
||||
Object.defineProperties( Leaf.prototype, prototypeAccessors );
|
||||
|
||||
return Leaf;
|
||||
}(RopeSequence));
|
||||
|
||||
// :: RopeSequence
|
||||
// The empty rope sequence.
|
||||
RopeSequence.empty = new Leaf([]);
|
||||
|
||||
var Append = /*@__PURE__*/(function (RopeSequence) {
|
||||
function Append(left, right) {
|
||||
RopeSequence.call(this);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.length = left.length + right.length;
|
||||
this.depth = Math.max(left.depth, right.depth) + 1;
|
||||
}
|
||||
|
||||
if ( RopeSequence ) Append.__proto__ = RopeSequence;
|
||||
Append.prototype = Object.create( RopeSequence && RopeSequence.prototype );
|
||||
Append.prototype.constructor = Append;
|
||||
|
||||
Append.prototype.flatten = function flatten () {
|
||||
return this.left.flatten().concat(this.right.flatten())
|
||||
};
|
||||
|
||||
Append.prototype.getInner = function getInner (i) {
|
||||
return i < this.left.length ? this.left.get(i) : this.right.get(i - this.left.length)
|
||||
};
|
||||
|
||||
Append.prototype.forEachInner = function forEachInner (f, from, to, start) {
|
||||
var leftLen = this.left.length;
|
||||
if (from < leftLen &&
|
||||
this.left.forEachInner(f, from, Math.min(to, leftLen), start) === false)
|
||||
{ return false }
|
||||
if (to > leftLen &&
|
||||
this.right.forEachInner(f, Math.max(from - leftLen, 0), Math.min(this.length, to) - leftLen, start + leftLen) === false)
|
||||
{ return false }
|
||||
};
|
||||
|
||||
Append.prototype.forEachInvertedInner = function forEachInvertedInner (f, from, to, start) {
|
||||
var leftLen = this.left.length;
|
||||
if (from > leftLen &&
|
||||
this.right.forEachInvertedInner(f, from - leftLen, Math.max(to, leftLen) - leftLen, start + leftLen) === false)
|
||||
{ return false }
|
||||
if (to < leftLen &&
|
||||
this.left.forEachInvertedInner(f, Math.min(from, leftLen), to, start) === false)
|
||||
{ return false }
|
||||
};
|
||||
|
||||
Append.prototype.sliceInner = function sliceInner (from, to) {
|
||||
if (from == 0 && to == this.length) { return this }
|
||||
var leftLen = this.left.length;
|
||||
if (to <= leftLen) { return this.left.slice(from, to) }
|
||||
if (from >= leftLen) { return this.right.slice(from - leftLen, to - leftLen) }
|
||||
return this.left.slice(from, leftLen).append(this.right.slice(0, to - leftLen))
|
||||
};
|
||||
|
||||
Append.prototype.leafAppend = function leafAppend (other) {
|
||||
var inner = this.right.leafAppend(other);
|
||||
if (inner) { return new Append(this.left, inner) }
|
||||
};
|
||||
|
||||
Append.prototype.leafPrepend = function leafPrepend (other) {
|
||||
var inner = this.left.leafPrepend(other);
|
||||
if (inner) { return new Append(inner, this.right) }
|
||||
};
|
||||
|
||||
Append.prototype.appendInner = function appendInner (other) {
|
||||
if (this.left.depth >= Math.max(this.right.depth, other.depth) + 1)
|
||||
{ return new Append(this.left, new Append(this.right, other)) }
|
||||
return new Append(this, other)
|
||||
};
|
||||
|
||||
return Append;
|
||||
}(RopeSequence));
|
||||
|
||||
export default RopeSequence;
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
const GOOD_LEAF_SIZE = 200
|
||||
|
||||
// :: class<T> A rope sequence is a persistent sequence data structure
|
||||
// that supports appending, prepending, and slicing without doing a
|
||||
// full copy. It is represented as a mostly-balanced tree.
|
||||
class RopeSequence {
|
||||
// length:: number
|
||||
// The length of the rope.
|
||||
|
||||
// :: (union<[T], RopeSequence<T>>) → RopeSequence<T>
|
||||
// Append an array or other rope to this one, returning a new rope.
|
||||
append(other) {
|
||||
if (!other.length) return this
|
||||
other = RopeSequence.from(other)
|
||||
|
||||
return (!this.length && other) ||
|
||||
(other.length < GOOD_LEAF_SIZE && this.leafAppend(other)) ||
|
||||
(this.length < GOOD_LEAF_SIZE && other.leafPrepend(this)) ||
|
||||
this.appendInner(other)
|
||||
}
|
||||
|
||||
// :: (union<[T], RopeSequence<T>>) → RopeSequence<T>
|
||||
// Prepend an array or other rope to this one, returning a new rope.
|
||||
prepend(other) {
|
||||
if (!other.length) return this
|
||||
return RopeSequence.from(other).append(this)
|
||||
}
|
||||
|
||||
appendInner(other) {
|
||||
return new Append(this, other)
|
||||
}
|
||||
|
||||
// :: (?number, ?number) → RopeSequence<T>
|
||||
// Create a rope repesenting a sub-sequence of this rope.
|
||||
slice(from = 0, to = this.length) {
|
||||
if (from >= to) return RopeSequence.empty
|
||||
return this.sliceInner(Math.max(0, from), Math.min(this.length, to))
|
||||
}
|
||||
|
||||
// :: (number) → T
|
||||
// Retrieve the element at the given position from this rope.
|
||||
get(i) {
|
||||
if (i < 0 || i >= this.length) return undefined
|
||||
return this.getInner(i)
|
||||
}
|
||||
|
||||
// :: ((element: T, index: number) → ?bool, ?number, ?number)
|
||||
// Call the given function for each element between the given
|
||||
// indices. This tends to be more efficient than looping over the
|
||||
// indices and calling `get`, because it doesn't have to descend the
|
||||
// tree for every element.
|
||||
forEach(f, from = 0, to = this.length) {
|
||||
if (from <= to)
|
||||
this.forEachInner(f, from, to, 0)
|
||||
else
|
||||
this.forEachInvertedInner(f, from, to, 0)
|
||||
}
|
||||
|
||||
// :: ((element: T, index: number) → U, ?number, ?number) → [U]
|
||||
// Map the given functions over the elements of the rope, producing
|
||||
// a flat array.
|
||||
map(f, from = 0, to = this.length) {
|
||||
let result = []
|
||||
this.forEach((elt, i) => result.push(f(elt, i)), from, to)
|
||||
return result
|
||||
}
|
||||
|
||||
// :: (?union<[T], RopeSequence<T>>) → RopeSequence<T>
|
||||
// Create a rope representing the given array, or return the rope
|
||||
// itself if a rope was given.
|
||||
static from(values) {
|
||||
if (values instanceof RopeSequence) return values
|
||||
return values && values.length ? new Leaf(values) : RopeSequence.empty
|
||||
}
|
||||
|
||||
// flatten:: () → [T]
|
||||
// Return the content of this rope as an array.
|
||||
}
|
||||
|
||||
class Leaf extends RopeSequence {
|
||||
constructor(values) {
|
||||
super()
|
||||
this.values = values
|
||||
}
|
||||
|
||||
flatten() {
|
||||
return this.values
|
||||
}
|
||||
|
||||
sliceInner(from, to) {
|
||||
if (from == 0 && to == this.length) return this
|
||||
return new Leaf(this.values.slice(from, to))
|
||||
}
|
||||
|
||||
getInner(i) {
|
||||
return this.values[i]
|
||||
}
|
||||
|
||||
forEachInner(f, from, to, start) {
|
||||
for (let i = from; i < to; i++)
|
||||
if (f(this.values[i], start + i) === false) return false
|
||||
}
|
||||
|
||||
forEachInvertedInner(f, from, to, start) {
|
||||
for (let i = from - 1; i >= to; i--)
|
||||
if (f(this.values[i], start + i) === false) return false
|
||||
}
|
||||
|
||||
leafAppend(other) {
|
||||
if (this.length + other.length <= GOOD_LEAF_SIZE)
|
||||
return new Leaf(this.values.concat(other.flatten()))
|
||||
}
|
||||
|
||||
leafPrepend(other) {
|
||||
if (this.length + other.length <= GOOD_LEAF_SIZE)
|
||||
return new Leaf(other.flatten().concat(this.values))
|
||||
}
|
||||
|
||||
get length() { return this.values.length }
|
||||
|
||||
get depth() { return 0 }
|
||||
}
|
||||
|
||||
// :: RopeSequence
|
||||
// The empty rope sequence.
|
||||
RopeSequence.empty = new Leaf([])
|
||||
|
||||
class Append extends RopeSequence {
|
||||
constructor(left, right) {
|
||||
super()
|
||||
this.left = left
|
||||
this.right = right
|
||||
this.length = left.length + right.length
|
||||
this.depth = Math.max(left.depth, right.depth) + 1
|
||||
}
|
||||
|
||||
flatten() {
|
||||
return this.left.flatten().concat(this.right.flatten())
|
||||
}
|
||||
|
||||
getInner(i) {
|
||||
return i < this.left.length ? this.left.get(i) : this.right.get(i - this.left.length)
|
||||
}
|
||||
|
||||
forEachInner(f, from, to, start) {
|
||||
let leftLen = this.left.length
|
||||
if (from < leftLen &&
|
||||
this.left.forEachInner(f, from, Math.min(to, leftLen), start) === false)
|
||||
return false
|
||||
if (to > leftLen &&
|
||||
this.right.forEachInner(f, Math.max(from - leftLen, 0), Math.min(this.length, to) - leftLen, start + leftLen) === false)
|
||||
return false
|
||||
}
|
||||
|
||||
forEachInvertedInner(f, from, to, start) {
|
||||
let leftLen = this.left.length
|
||||
if (from > leftLen &&
|
||||
this.right.forEachInvertedInner(f, from - leftLen, Math.max(to, leftLen) - leftLen, start + leftLen) === false)
|
||||
return false
|
||||
if (to < leftLen &&
|
||||
this.left.forEachInvertedInner(f, Math.min(from, leftLen), to, start) === false)
|
||||
return false
|
||||
}
|
||||
|
||||
sliceInner(from, to) {
|
||||
if (from == 0 && to == this.length) return this
|
||||
let leftLen = this.left.length
|
||||
if (to <= leftLen) return this.left.slice(from, to)
|
||||
if (from >= leftLen) return this.right.slice(from - leftLen, to - leftLen)
|
||||
return this.left.slice(from, leftLen).append(this.right.slice(0, to - leftLen))
|
||||
}
|
||||
|
||||
leafAppend(other) {
|
||||
let inner = this.right.leafAppend(other)
|
||||
if (inner) return new Append(this.left, inner)
|
||||
}
|
||||
|
||||
leafPrepend(other) {
|
||||
let inner = this.left.leafPrepend(other)
|
||||
if (inner) return new Append(inner, this.right)
|
||||
}
|
||||
|
||||
appendInner(other) {
|
||||
if (this.left.depth >= Math.max(this.right.depth, other.depth) + 1)
|
||||
return new Append(this.left, new Append(this.right, other))
|
||||
return new Append(this, other)
|
||||
}
|
||||
}
|
||||
|
||||
export default RopeSequence
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "rope-sequence",
|
||||
"version": "1.3.4",
|
||||
"description": "Rope-based persistent sequence type",
|
||||
"main": "dist/index.cjs",
|
||||
"type": "module",
|
||||
"module": "dist/index.js",
|
||||
"exports": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"prepare": "npm run build",
|
||||
"test": "node test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/marijnh/rope-sequence.git"
|
||||
},
|
||||
"keywords": [
|
||||
"persistent",
|
||||
"data",
|
||||
"structure",
|
||||
"rope",
|
||||
"sequence"
|
||||
],
|
||||
"author": "Marijn Haverbeke <marijn@haverbeke.berlin>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-buble": "^0.20.0",
|
||||
"rollup": "^1.26.3"
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import RopeSequence from "./index.js"
|
||||
import assert from "assert"
|
||||
|
||||
function describe(rope) {
|
||||
if (rope.left) return "(" + describe(rope.left) + ", " + describe(rope.right) + ")"
|
||||
else return rope.length
|
||||
}
|
||||
|
||||
function appendBuild(n) {
|
||||
var rope = RopeSequence.empty
|
||||
for (var i = 0; i < n; i++)
|
||||
rope = rope.append([i])
|
||||
return rope
|
||||
}
|
||||
|
||||
function dequeBuild(n) {
|
||||
var mid = n >> 1, rope = RopeSequence.empty
|
||||
for (var from = mid - 1, to = mid; to < n; from--, to++) {
|
||||
rope = rope.append([to])
|
||||
if (from >= 0) rope = RopeSequence.from([from]).append(rope)
|
||||
}
|
||||
return rope
|
||||
}
|
||||
|
||||
function flatBuild(n) {
|
||||
var arr = []
|
||||
for (var i = 0; i < n; i++) arr.push(i)
|
||||
return RopeSequence.from(arr)
|
||||
}
|
||||
|
||||
var SIZE = 10000
|
||||
|
||||
function checkForEach(rope, name, start, end, offset) {
|
||||
var cur = start
|
||||
rope.forEach(function(elt, i) {
|
||||
assert.equal(elt, cur + offset, "Proper element at " + cur + " in " + name)
|
||||
assert.equal(cur, i, "Accurate index passed")
|
||||
cur++
|
||||
}, start, end)
|
||||
assert.equal(cur, end, "Enough elements iterated in " + name)
|
||||
rope.forEach(function(elt, i) {
|
||||
cur--
|
||||
assert.equal(elt, cur + offset, "Proper element during reverse iter at " + cur + " in " + name)
|
||||
assert.equal(cur, i, "Accurate index passed by reverse iter")
|
||||
}, end, start)
|
||||
assert.equal(cur, start, "Enough elements reverse-iterated in " + name + " -- " + cur + " " + start)
|
||||
}
|
||||
|
||||
function check(rope, size, name, offset) {
|
||||
if (!offset) offset = 0
|
||||
assert.equal(rope.length, size, "Size of " + name)
|
||||
for (var i = 0; i < rope.length; i++)
|
||||
assert.equal(rope.get(i), offset + i, "Field at " + i + " in " + name)
|
||||
checkForEach(rope, name, 0, rope.length, offset)
|
||||
for (var i = 0, e = Math.min(10, Math.floor(size / 100)); i < e; i++) {
|
||||
var start = Math.floor(Math.random() * size), end = start + Math.ceil(Math.random() * (size - start))
|
||||
checkForEach(rope, name + "-" + start + "-" + end, start, end, offset)
|
||||
check(rope.slice(start, end), end - start, name + "-sliced-" + start + "-" + end, offset + start)
|
||||
}
|
||||
}
|
||||
|
||||
check(appendBuild(SIZE), SIZE, "appended")
|
||||
check(dequeBuild(SIZE), SIZE, "dequed")
|
||||
check(flatBuild(SIZE), SIZE, "flat")
|
||||
|
||||
var small = RopeSequence.from([1, 2, 4]), empty = RopeSequence.empty
|
||||
assert.equal(small.append(empty), small, "ID append")
|
||||
assert.equal(small.prepend(empty), small, "ID prepend")
|
||||
assert.equal(empty.append(empty), empty, "empty append")
|
||||
assert.equal(small.slice(0, 0), empty, "empty slice")
|
||||
|
||||
var sum = 0
|
||||
small.forEach(function(v) { if (v == 2) return false; sum += v })
|
||||
assert.equal(sum, 1, "abort iteration")
|
||||
|
||||
assert.deepEqual(small.map(function(x) { return x + 1 }), [2, 3, 5], "mapping")
|
||||
|
||||
console.log("All passed")
|
||||
Reference in New Issue
Block a user