export class Yallist {
    tail;
    head;
    length = 0;
    static create(list = []) {
        return new Yallist(list);
    }
    constructor(list = []) {
        for (const item of list) {
            this.push(item);
        }
    }
    *[Symbol.iterator]() {
        for (let walker = this.head; walker; walker = walker.next) {
            yield walker.value;
        }
    }
    removeNode(node) {
        if (node.list !== this) {
            throw new Error('removing node which does not belong to this list');
        }
        const next = node.next;
        const prev = node.prev;
        if (next) {
            next.prev = prev;
        }
        if (prev) {
            prev.next = next;
        }
        if (node === this.head) {
            this.head = next;
        }
        if (node === this.tail) {
            this.tail = prev;
        }
        this.length--;
        node.next = undefined;
        node.prev = undefined;
        node.list = undefined;
        return next;
    }
    unshiftNode(node) {
        if (node === this.head) {
            return;
        }
        if (node.list) {
            node.list.removeNode(node);
        }
        const head = this.head;
        node.list = this;
        node.next = head;
        if (head) {
            head.prev = node;
        }
        this.head = node;
        if (!this.tail) {
            this.tail = node;
        }
        this.length++;
    }
    pushNode(node) {
        if (node === this.tail) {
            return;
        }
        if (node.list) {
            node.list.removeNode(node);
        }
        const tail = this.tail;
        node.list = this;
        node.prev = tail;
        if (tail) {
            tail.next = node;
        }
        this.tail = node;
        if (!this.head) {
            this.head = node;
        }
        this.length++;
    }
    push(...args) {
        for (let i = 0, l = args.length; i < l; i++) {
            push(this, args[i]);
        }
        return this.length;
    }
    unshift(...args) {
        for (var i = 0, l = args.length; i < l; i++) {
            unshift(this, args[i]);
        }
        return this.length;
    }
    pop() {
        if (!this.tail) {
            return undefined;
        }
        const res = this.tail.value;
        const t = this.tail;
        this.tail = this.tail.prev;
        if (this.tail) {
            this.tail.next = undefined;
        }
        else {
            this.head = undefined;
        }
        t.list = undefined;
        this.length--;
        return res;
    }
    shift() {
        if (!this.head) {
            return undefined;
        }
        const res = this.head.value;
        const h = this.head;
        this.head = this.head.next;
        if (this.head) {
            this.head.prev = undefined;
        }
        else {
            this.tail = undefined;
        }
        h.list = undefined;
        this.length--;
        return res;
    }
    forEach(fn, thisp) {
        thisp = thisp || this;
        for (let walker = this.head, i = 0; !!walker; i++) {
            fn.call(thisp, walker.value, i, this);
            walker = walker.next;
        }
    }
    forEachReverse(fn, thisp) {
        thisp = thisp || this;
        for (let walker = this.tail, i = this.length - 1; !!walker; i--) {
            fn.call(thisp, walker.value, i, this);
            walker = walker.prev;
        }
    }
    get(n) {
        let i = 0;
        let walker = this.head;
        for (; !!walker && i < n; i++) {
            walker = walker.next;
        }
        if (i === n && !!walker) {
            return walker.value;
        }
    }
    getReverse(n) {
        let i = 0;
        let walker = this.tail;
        for (; !!walker && i < n; i++) {
            // abort out of the list early if we hit a cycle
            walker = walker.prev;
        }
        if (i === n && !!walker) {
            return walker.value;
        }
    }
    map(fn, thisp) {
        thisp = thisp || this;
        const res = new Yallist();
        for (let walker = this.head; !!walker;) {
            res.push(fn.call(thisp, walker.value, this));
            walker = walker.next;
        }
        return res;
    }
    mapReverse(fn, thisp) {
        thisp = thisp || this;
        var res = new Yallist();
        for (let walker = this.tail; !!walker;) {
            res.push(fn.call(thisp, walker.value, this));
            walker = walker.prev;
        }
        return res;
    }
    reduce(fn, initial) {
        let acc;
        let walker = this.head;
        if (arguments.length > 1) {
            acc = initial;
        }
        else if (this.head) {
            walker = this.head.next;
            acc = this.head.value;
        }
        else {
            throw new TypeError('Reduce of empty list with no initial value');
        }
        for (var i = 0; !!walker; i++) {
            acc = fn(acc, walker.value, i);
            walker = walker.next;
        }
        return acc;
    }
    reduceReverse(fn, initial) {
        let acc;
        let walker = this.tail;
        if (arguments.length > 1) {
            acc = initial;
        }
        else if (this.tail) {
            walker = this.tail.prev;
            acc = this.tail.value;
        }
        else {
            throw new TypeError('Reduce of empty list with no initial value');
        }
        for (let i = this.length - 1; !!walker; i--) {
            acc = fn(acc, walker.value, i);
            walker = walker.prev;
        }
        return acc;
    }
    toArray() {
        const arr = new Array(this.length);
        for (let i = 0, walker = this.head; !!walker; i++) {
            arr[i] = walker.value;
            walker = walker.next;
        }
        return arr;
    }
    toArrayReverse() {
        const arr = new Array(this.length);
        for (let i = 0, walker = this.tail; !!walker; i++) {
            arr[i] = walker.value;
            walker = walker.prev;
        }
        return arr;
    }
    slice(from = 0, to = this.length) {
        if (to < 0) {
            to += this.length;
        }
        if (from < 0) {
            from += this.length;
        }
        const ret = new Yallist();
        if (to < from || to < 0) {
            return ret;
        }
        if (from < 0) {
            from = 0;
        }
        if (to > this.length) {
            to = this.length;
        }
        let walker = this.head;
        let i = 0;
        for (i = 0; !!walker && i < from; i++) {
            walker = walker.next;
        }
        for (; !!walker && i < to; i++, walker = walker.next) {
            ret.push(walker.value);
        }
        return ret;
    }
    sliceReverse(from = 0, to = this.length) {
        if (to < 0) {
            to += this.length;
        }
        if (from < 0) {
            from += this.length;
        }
        const ret = new Yallist();
        if (to < from || to < 0) {
            return ret;
        }
        if (from < 0) {
            from = 0;
        }
        if (to > this.length) {
            to = this.length;
        }
        let i = this.length;
        let walker = this.tail;
        for (; !!walker && i > to; i--) {
            walker = walker.prev;
        }
        for (; !!walker && i > from; i--, walker = walker.prev) {
            ret.push(walker.value);
        }
        return ret;
    }
    splice(start, deleteCount = 0, ...nodes) {
        if (start > this.length) {
            start = this.length - 1;
        }
        if (start < 0) {
            start = this.length + start;
        }
        let walker = this.head;
        for (let i = 0; !!walker && i < start; i++) {
            walker = walker.next;
        }
        const ret = [];
        for (let i = 0; !!walker && i < deleteCount; i++) {
            ret.push(walker.value);
            walker = this.removeNode(walker);
        }
        if (!walker) {
            walker = this.tail;
        }
        else if (walker !== this.tail) {
            walker = walker.prev;
        }
        for (const v of nodes) {
            walker = insertAfter(this, walker, v);
        }
        return ret;
    }
    reverse() {
        const head = this.head;
        const tail = this.tail;
        for (let walker = head; !!walker; walker = walker.prev) {
            const p = walker.prev;
            walker.prev = walker.next;
            walker.next = p;
        }
        this.head = tail;
        this.tail = head;
        return this;
    }
}
// insertAfter undefined means "make the node the new head of list"
function insertAfter(self, node, value) {
    const prev = node;
    const next = node ? node.next : self.head;
    const inserted = new Node(value, prev, next, self);
    if (inserted.next === undefined) {
        self.tail = inserted;
    }
    if (inserted.prev === undefined) {
        self.head = inserted;
    }
    self.length++;
    return inserted;
}
function push(self, item) {
    self.tail = new Node(item, self.tail, undefined, self);
    if (!self.head) {
        self.head = self.tail;
    }
    self.length++;
}
function unshift(self, item) {
    self.head = new Node(item, undefined, self.head, self);
    if (!self.tail) {
        self.tail = self.head;
    }
    self.length++;
}
export class Node {
    list;
    next;
    prev;
    value;
    constructor(value, prev, next, list) {
        this.list = list;
        this.value = value;
        if (prev) {
            prev.next = this;
            this.prev = prev;
        }
        else {
            this.prev = undefined;
        }
        if (next) {
            next.prev = this;
            this.next = next;
        }
        else {
            this.next = undefined;
        }
    }
}
//# sourceMappingURL=index.js.map