// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. /*----------------------------------------------------------------------------- | Copyright (c) 2014-2017, PhosphorJS Contributors | | Distributed under the terms of the BSD 3-Clause License. | | The full license is in the file LICENSE, distributed with this software. |----------------------------------------------------------------------------*/ import { IRetroable } from '@lumino/algorithm'; /** * A generic doubly-linked list. */ export class LinkedList implements Iterable, IRetroable { /** * Whether the list is empty. * * #### Complexity * Constant. */ get isEmpty(): boolean { return this._size === 0; } /** * The size of the list. * * #### Complexity * `O(1)` * * #### Notes * This is equivalent to `length`. */ get size(): number { return this._size; } /** * The length of the list. * * #### Complexity * Constant. * * #### Notes * This is equivalent to `size`. * * This property is deprecated. */ get length(): number { return this._size; } /** * The first value in the list. * * This is `undefined` if the list is empty. * * #### Complexity * Constant. */ get first(): T | undefined { return this._first ? this._first.value : undefined; } /** * The last value in the list. * * This is `undefined` if the list is empty. * * #### Complexity * Constant. */ get last(): T | undefined { return this._last ? this._last.value : undefined; } /** * The first node in the list. * * This is `null` if the list is empty. * * #### Complexity * Constant. */ get firstNode(): LinkedList.INode | null { return this._first; } /** * The last node in the list. * * This is `null` if the list is empty. * * #### Complexity * Constant. */ get lastNode(): LinkedList.INode | null { return this._last; } /** * Create an iterator over the values in the list. * * @returns A new iterator starting with the first value. * * #### Complexity * Constant. */ *[Symbol.iterator](): IterableIterator { let node = this._first; while (node) { yield node.value; node = node.next; } } /** * Create a reverse iterator over the values in the list. * * @returns A new iterator starting with the last value. * * #### Complexity * Constant. */ *retro(): IterableIterator { let node = this._last; while (node) { yield node.value; node = node.prev; } } /** * Create an iterator over the nodes in the list. * * @returns A new iterator starting with the first node. * * #### Complexity * Constant. */ *nodes(): IterableIterator> { let node = this._first; while (node) { yield node; node = node.next; } } /** * Create a reverse iterator over the nodes in the list. * * @returns A new iterator starting with the last node. * * #### Complexity * Constant. */ *retroNodes(): IterableIterator> { let node = this._last; while (node) { yield node; node = node.prev; } } /** * Assign new values to the list, replacing all current values. * * @param values - The values to assign to the list. * * #### Complexity * Linear. */ assign(values: Iterable): void { this.clear(); for (const value of values) { this.addLast(value); } } /** * Add a value to the end of the list. * * @param value - The value to add to the end of the list. * * #### Complexity * Constant. * * #### Notes * This is equivalent to `addLast`. */ push(value: T): void { this.addLast(value); } /** * Remove and return the value at the end of the list. * * @returns The removed value, or `undefined` if the list is empty. * * #### Complexity * Constant. * * #### Notes * This is equivalent to `removeLast`. */ pop(): T | undefined { return this.removeLast(); } /** * Add a value to the beginning of the list. * * @param value - The value to add to the beginning of the list. * * #### Complexity * Constant. * * #### Notes * This is equivalent to `addFirst`. */ shift(value: T): void { this.addFirst(value); } /** * Remove and return the value at the beginning of the list. * * @returns The removed value, or `undefined` if the list is empty. * * #### Complexity * Constant. * * #### Notes * This is equivalent to `removeFirst`. */ unshift(): T | undefined { return this.removeFirst(); } /** * Add a value to the beginning of the list. * * @param value - The value to add to the beginning of the list. * * @returns The list node which holds the value. * * #### Complexity * Constant. */ addFirst(value: T): LinkedList.INode { let node = new Private.LinkedListNode(this, value); if (!this._first) { this._first = node; this._last = node; } else { node.next = this._first; this._first.prev = node; this._first = node; } this._size++; return node; } /** * Add a value to the end of the list. * * @param value - The value to add to the end of the list. * * @returns The list node which holds the value. * * #### Complexity * Constant. */ addLast(value: T): LinkedList.INode { let node = new Private.LinkedListNode(this, value); if (!this._last) { this._first = node; this._last = node; } else { node.prev = this._last; this._last.next = node; this._last = node; } this._size++; return node; } /** * Insert a value before a specific node in the list. * * @param value - The value to insert before the reference node. * * @param ref - The reference node of interest. If this is `null`, * the value will be added to the beginning of the list. * * @returns The list node which holds the value. * * #### Notes * The reference node must be owned by the list. * * #### Complexity * Constant. */ insertBefore(value: T, ref: LinkedList.INode | null): LinkedList.INode { if (!ref || ref === this._first) { return this.addFirst(value); } if (!(ref instanceof Private.LinkedListNode) || ref.list !== this) { throw new Error('Reference node is not owned by the list.'); } let node = new Private.LinkedListNode(this, value); let _ref = ref as Private.LinkedListNode; let prev = _ref.prev!; node.next = _ref; node.prev = prev; _ref.prev = node; prev.next = node; this._size++; return node; } /** * Insert a value after a specific node in the list. * * @param value - The value to insert after the reference node. * * @param ref - The reference node of interest. If this is `null`, * the value will be added to the end of the list. * * @returns The list node which holds the value. * * #### Notes * The reference node must be owned by the list. * * #### Complexity * Constant. */ insertAfter(value: T, ref: LinkedList.INode | null): LinkedList.INode { if (!ref || ref === this._last) { return this.addLast(value); } if (!(ref instanceof Private.LinkedListNode) || ref.list !== this) { throw new Error('Reference node is not owned by the list.'); } let node = new Private.LinkedListNode(this, value); let _ref = ref as Private.LinkedListNode; let next = _ref.next!; node.next = next; node.prev = _ref; _ref.next = node; next.prev = node; this._size++; return node; } /** * Remove and return the value at the beginning of the list. * * @returns The removed value, or `undefined` if the list is empty. * * #### Complexity * Constant. */ removeFirst(): T | undefined { let node = this._first; if (!node) { return undefined; } if (node === this._last) { this._first = null; this._last = null; } else { this._first = node.next; this._first!.prev = null; } node.list = null; node.next = null; node.prev = null; this._size--; return node.value; } /** * Remove and return the value at the end of the list. * * @returns The removed value, or `undefined` if the list is empty. * * #### Complexity * Constant. */ removeLast(): T | undefined { let node = this._last; if (!node) { return undefined; } if (node === this._first) { this._first = null; this._last = null; } else { this._last = node.prev; this._last!.next = null; } node.list = null; node.next = null; node.prev = null; this._size--; return node.value; } /** * Remove a specific node from the list. * * @param node - The node to remove from the list. * * #### Complexity * Constant. * * #### Notes * The node must be owned by the list. */ removeNode(node: LinkedList.INode): void { if (!(node instanceof Private.LinkedListNode) || node.list !== this) { throw new Error('Node is not owned by the list.'); } let _node = node as Private.LinkedListNode; if (_node === this._first && _node === this._last) { this._first = null; this._last = null; } else if (_node === this._first) { this._first = _node.next; this._first!.prev = null; } else if (_node === this._last) { this._last = _node.prev; this._last!.next = null; } else { _node.next!.prev = _node.prev; _node.prev!.next = _node.next; } _node.list = null; _node.next = null; _node.prev = null; this._size--; } /** * Remove all values from the list. * * #### Complexity * Linear. */ clear(): void { let node = this._first; while (node) { let next = node.next; node.list = null; node.prev = null; node.next = null; node = next; } this._first = null; this._last = null; this._size = 0; } private _first: Private.LinkedListNode | null = null; private _last: Private.LinkedListNode | null = null; private _size = 0; } /** * The namespace for the `LinkedList` class statics. */ export namespace LinkedList { /** * An object which represents a node in a linked list. * * #### Notes * User code will not create linked list nodes directly. Nodes * are created automatically when values are added to a list. */ export interface INode { /** * The linked list which created and owns the node. * * This will be `null` when the node is removed from the list. */ readonly list: LinkedList | null; /** * The next node in the list. * * This will be `null` when the node is the last node in the list * or when the node is removed from the list. */ readonly next: INode | null; /** * The previous node in the list. * * This will be `null` when the node is the first node in the list * or when the node is removed from the list. */ readonly prev: INode | null; /** * The user value stored in the node. */ readonly value: T; } /** * Create a linked list from an iterable of values. * * @param values - The iterable object of interest. * * @returns A new linked list initialized with the given values. * * #### Complexity * Linear. */ export function from(values: Iterable): LinkedList { let list = new LinkedList(); list.assign(values); return list; } } /** * The namespace for the module implementation details. */ namespace Private { /** * The internal linked list node implementation. */ export class LinkedListNode { /** * The linked list which created and owns the node. */ list: LinkedList | null = null; /** * The next node in the list. */ next: LinkedListNode | null = null; /** * The previous node in the list. */ prev: LinkedListNode | null = null; /** * The user value stored in the node. */ readonly value: T; /** * Construct a new linked list node. * * @param list - The list which owns the node. * * @param value - The value for the link. */ constructor(list: LinkedList, value: T) { this.list = list; this.value = value; } } }