@blackglory/structures 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/lib/array.js.map +1 -1
  2. package/lib/big-map.js.map +1 -1
  3. package/lib/big-set.js.map +1 -1
  4. package/lib/bit-set.js.map +1 -1
  5. package/lib/box.js +3 -3
  6. package/lib/box.js.map +1 -1
  7. package/lib/cons.js.map +1 -1
  8. package/lib/dynamic-typed-array.js +10 -10
  9. package/lib/dynamic-typed-array.js.map +1 -1
  10. package/lib/emitter.d.ts +1 -1
  11. package/lib/emitter.js.map +1 -1
  12. package/lib/expirable-map.js.map +1 -1
  13. package/lib/hash-map.js +4 -4
  14. package/lib/hash-map.js.map +1 -1
  15. package/lib/hash-set.js +4 -4
  16. package/lib/hash-set.js.map +1 -1
  17. package/lib/lru-map.js +6 -6
  18. package/lib/lru-map.js.map +1 -1
  19. package/lib/queue.js.map +1 -1
  20. package/lib/radix-tree.js.map +1 -1
  21. package/lib/sorted-set.js +3 -3
  22. package/lib/sorted-set.js.map +1 -1
  23. package/lib/sparse-map.js.map +1 -1
  24. package/lib/sparse-set.js +9 -9
  25. package/lib/sparse-set.js.map +1 -1
  26. package/lib/string-radix-tree.js.map +1 -1
  27. package/lib/string-trie-map.js +1 -1
  28. package/lib/string-trie-map.js.map +1 -1
  29. package/lib/tlru-map.js +6 -6
  30. package/lib/tlru-map.js.map +1 -1
  31. package/lib/trie-map.js +1 -1
  32. package/lib/trie-map.js.map +1 -1
  33. package/lib/typed-bit-set.js.map +1 -1
  34. package/lib/typed-sparse-map.js +6 -6
  35. package/lib/typed-sparse-map.js.map +1 -1
  36. package/lib/typed-sparse-set.js.map +1 -1
  37. package/lib/utils/skip-list.js +3 -3
  38. package/lib/utils/skip-list.js.map +1 -1
  39. package/package.json +31 -31
  40. package/src/array.ts +19 -0
  41. package/src/big-map.ts +98 -0
  42. package/src/big-set.ts +72 -0
  43. package/src/bit-set.ts +124 -0
  44. package/src/box.ts +15 -0
  45. package/src/cons.ts +15 -0
  46. package/src/dynamic-typed-array.ts +201 -0
  47. package/src/emitter.ts +76 -0
  48. package/src/expirable-map.ts +122 -0
  49. package/src/hash-map.ts +34 -0
  50. package/src/hash-set.ts +38 -0
  51. package/src/index.ts +24 -0
  52. package/src/lru-map.ts +69 -0
  53. package/src/queue.ts +30 -0
  54. package/src/radix-tree.ts +264 -0
  55. package/src/sorted-set.ts +34 -0
  56. package/src/sparse-map.ts +83 -0
  57. package/src/sparse-set.ts +67 -0
  58. package/src/string-radix-tree.ts +263 -0
  59. package/src/string-trie-map.ts +102 -0
  60. package/src/tlru-map.ts +162 -0
  61. package/src/trie-map.ts +102 -0
  62. package/src/typed-bit-set.ts +129 -0
  63. package/src/typed-sparse-map.ts +97 -0
  64. package/src/typed-sparse-set.ts +71 -0
  65. package/src/utils/skip-list.ts +195 -0
  66. package/src/utils/trailing-zeros.ts +15 -0
@@ -0,0 +1,129 @@
1
+ import { UnsignedTypedArrayConstructor } from 'justypes'
2
+ import { assert } from '@blackglory/errors'
3
+ import { DynamicTypedArray } from './dynamic-typed-array.js'
4
+ import { trailingZeros } from '@utils/trailing-zeros.js'
5
+
6
+ export class TypedBitSet<T extends UnsignedTypedArrayConstructor> {
7
+ private bitsPerElement: number
8
+ private length = 0
9
+ private _size = 0
10
+
11
+ constructor(private array: DynamicTypedArray<T>) {
12
+ const bitsPerElement = array.BYTES_PER_ELEMENT * 8
13
+
14
+ assert(
15
+ Number.isInteger(bitsPerElement)
16
+ , 'The parameter bitsPerElement must be an integer'
17
+ )
18
+ assert(
19
+ bitsPerElement > 0
20
+ , 'The parameter bitsPerElement must be greater than 0'
21
+ )
22
+ // `31`是该数据结构中能够处理的最大值
23
+ assert(
24
+ bitsPerElement <= 31
25
+ , 'The mask of bitsPerElement must be less than or equal to 31'
26
+ )
27
+
28
+ this.bitsPerElement = bitsPerElement
29
+ }
30
+
31
+ get [Symbol.toStringTag](): string {
32
+ return this.constructor.name
33
+ }
34
+
35
+ get size(): number {
36
+ return this._size
37
+ }
38
+
39
+ [Symbol.iterator](): IterableIterator<number> {
40
+ return this.values()
41
+ }
42
+
43
+ * values(): IterableIterator<number> {
44
+ if (this.length > 0) {
45
+ // maxArrayLength = Math.ceil(this.length / this.bitsPerElement)
46
+ const maxArrayLength = ~~(this.length / this.bitsPerElement) + 1
47
+
48
+ for (let index = 0; index < maxArrayLength; index++) {
49
+ let element = this.array.internalTypedArray[index]
50
+ let offset = 0
51
+ let indexOfBit: number
52
+ while ((indexOfBit = trailingZeros(element)) !== 32) {
53
+ yield index * this.bitsPerElement + offset + indexOfBit
54
+ offset += indexOfBit + 1
55
+ element >>= indexOfBit + 1
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ _dumpBinaryStrings(): string[] {
62
+ const result: string[] = []
63
+ for (let i = 0; i < this.array.length; i++) {
64
+ const binary = this.array.internalTypedArray[i].toString(2)
65
+ if (binary.length < this.bitsPerElement) {
66
+ result.push('0'.repeat(this.bitsPerElement - binary.length) + binary)
67
+ } else {
68
+ result.push(binary)
69
+ }
70
+ }
71
+ return result
72
+ }
73
+
74
+ has(value: number): boolean {
75
+ const [index, mask] = this.getPosition(value)
76
+
77
+ return ((this.array.get(index) ?? 0) & mask) === mask
78
+ }
79
+
80
+ add(value: number): boolean {
81
+ assert(value >= 0, 'value must be greater than or equal to 0')
82
+ if (value >= this.length) {
83
+ this.length = value + 1
84
+ }
85
+
86
+ const [index, mask] = this.getPosition(value)
87
+
88
+ const element = this.array.get(index) ?? 0
89
+ this.array.set(index, element | mask)
90
+
91
+ const added = (element & mask) !== mask
92
+ if (added) {
93
+ this._size++
94
+ }
95
+ return added
96
+ }
97
+
98
+ delete(value: number): boolean {
99
+ const [index, mask] = this.getPosition(value)
100
+
101
+ const element = (this.array.get(index) ?? 0)
102
+ this.array.set(index, element & ~mask)
103
+
104
+ const deleted = (element & mask) === mask
105
+ if (deleted) {
106
+ this._size--
107
+ }
108
+ return deleted
109
+ }
110
+
111
+ clear(): void {
112
+ this._size = 0
113
+ this.array.clear()
114
+ }
115
+
116
+ private getPosition(value: number): [index: number, mask: number] {
117
+ const remainder = value % this.bitsPerElement
118
+ const quotient = (value - remainder) / this.bitsPerElement
119
+ const index = quotient
120
+ const mask = this.getMask(remainder)
121
+ return [index, mask]
122
+ }
123
+
124
+ // 输入一定是一个小于bitsPerElement的值, 取值范围是[0, bitsPerElement)
125
+ private getMask(value: number): number {
126
+ // return 2 ** value
127
+ return 1 << value
128
+ }
129
+ }
@@ -0,0 +1,97 @@
1
+ import { assert } from '@blackglory/errors'
2
+ import { TypedArrayConstructor, TypedArrayOfConstructor } from 'justypes'
3
+ import { DynamicTypedArray } from './dynamic-typed-array.js'
4
+
5
+ export class TypedSparseMap<T extends TypedArrayConstructor> {
6
+ private keyToIndex: Array<number | undefined> = []
7
+ private indexToKey: number[] = []
8
+ private indexToValue: DynamicTypedArray<T>
9
+
10
+ get [Symbol.toStringTag](): string {
11
+ return this.constructor.name
12
+ }
13
+
14
+ get size(): number {
15
+ return this.indexToKey.length
16
+ }
17
+
18
+ get internalTypedArray(): TypedArrayOfConstructor<T> {
19
+ return this.indexToValue.internalTypedArray
20
+ }
21
+
22
+ constructor(array: DynamicTypedArray<T>) {
23
+ assert(array.length === 0, 'array should be empty')
24
+
25
+ this.indexToValue = array
26
+ }
27
+
28
+ * entries(): IterableIterator<[key: number, value: number]> {
29
+ for (let i = 0; i < this.indexToKey.length; i++) {
30
+ yield [
31
+ this.indexToKey[i]
32
+ , this.indexToValue.internalTypedArray[i]
33
+ ]
34
+ }
35
+ }
36
+
37
+ keys(): IterableIterator<number> {
38
+ return this.indexToKey.values()
39
+ }
40
+
41
+ * values(): IterableIterator<number> {
42
+ for (let i = 0; i < this.indexToKey.length; i++) {
43
+ yield this.indexToValue.internalTypedArray[i]
44
+ }
45
+ }
46
+
47
+ getInternalIndexOfKey(key: number): number | undefined {
48
+ return this.keyToIndex[key]
49
+ }
50
+
51
+ has(key: number): boolean {
52
+ return key in this.keyToIndex
53
+ }
54
+
55
+ get(key: number): number | undefined {
56
+ if (this.has(key)) {
57
+ const index = this.keyToIndex[key]!
58
+ return this.indexToValue.internalTypedArray[index]
59
+ } else {
60
+ return undefined
61
+ }
62
+ }
63
+
64
+ set(key: number, value: number): void {
65
+ if (this.has(key)) {
66
+ const index = this.keyToIndex[key]!
67
+ this.indexToValue.internalTypedArray[index] = value
68
+ } else {
69
+ const index = this.indexToKey.length
70
+ this.indexToKey.push(key)
71
+ this.indexToValue.push(value)
72
+ this.keyToIndex[key] = index
73
+ }
74
+ }
75
+
76
+ delete(key: number): void {
77
+ if (this.has(key)) {
78
+ const lastKey = this.indexToKey.pop()!
79
+ const lastValue = this.indexToValue.pop()!
80
+ if (key === lastKey) {
81
+ delete this.keyToIndex[key]
82
+ } else {
83
+ const index = this.keyToIndex[key]!
84
+ this.indexToKey[index] = lastKey
85
+ this.indexToValue.internalTypedArray[index] = lastValue
86
+ this.keyToIndex[lastKey] = index
87
+ delete this.keyToIndex[key]
88
+ }
89
+ }
90
+ }
91
+
92
+ clear(): void {
93
+ this.keyToIndex.length = 0
94
+ this.indexToKey.length = 0
95
+ this.indexToValue.clear()
96
+ }
97
+ }
@@ -0,0 +1,71 @@
1
+ import { UnsignedTypedArrayConstructor } from 'justypes'
2
+ import { DynamicTypedArray } from './dynamic-typed-array.js'
3
+
4
+ export class TypedSparseSet<
5
+ T extends UnsignedTypedArrayConstructor
6
+ > implements Iterable<number> {
7
+ private valueToIndex: Array<number | undefined>
8
+ private indexToValue: DynamicTypedArray<T>
9
+
10
+ constructor(array: DynamicTypedArray<T>) {
11
+ const valueToIndex: Array<number | undefined> = []
12
+ if (array.length > 0) {
13
+ for (const [index, value] of array.internalTypedArray.entries()) {
14
+ valueToIndex[value] = index
15
+ }
16
+ }
17
+
18
+ this.indexToValue = array
19
+ this.valueToIndex = valueToIndex
20
+ }
21
+
22
+ get [Symbol.toStringTag](): string {
23
+ return this.constructor.name
24
+ }
25
+
26
+ [Symbol.iterator](): IterableIterator<number> {
27
+ return this.values()
28
+ }
29
+
30
+ get size(): number {
31
+ return this.indexToValue.length
32
+ }
33
+
34
+ * values(): IterableIterator<number> {
35
+ for (let i = 0; i < this.indexToValue.length; i++) {
36
+ yield this.indexToValue.internalTypedArray[i]
37
+ }
38
+ }
39
+
40
+ has(value: number): boolean {
41
+ const index = this.valueToIndex[value]
42
+ return this.indexToValue.internalTypedArray[index!] === value
43
+ }
44
+
45
+ add(value: number): void {
46
+ if (!this.has(value)) {
47
+ const index = this.indexToValue.length
48
+ this.indexToValue.push(value)
49
+ this.valueToIndex[value] = index
50
+ }
51
+ }
52
+
53
+ delete(value: number): boolean {
54
+ const index = this.valueToIndex[value]
55
+ if (this.indexToValue.internalTypedArray[index!] === value) {
56
+ const lastValue = this.indexToValue.pop()!
57
+ if (value !== lastValue) {
58
+ this.indexToValue.internalTypedArray[index!] = lastValue
59
+ this.valueToIndex[lastValue] = index
60
+ }
61
+ return true
62
+ } else {
63
+ return false
64
+ }
65
+ }
66
+
67
+ clear(): void {
68
+ this.valueToIndex.length = 0
69
+ this.indexToValue.clear()
70
+ }
71
+ }
@@ -0,0 +1,195 @@
1
+ import { isntNull } from 'extra-utils'
2
+
3
+ export class SkipListNode<T> {
4
+ previous: SkipListNode<T> | null = null
5
+ next: SkipListNode<T> | null = null
6
+ up: SkipListNode<T> | null = null
7
+ down: SkipListNode<T> | null = null
8
+
9
+ constructor(public value: T | null) {}
10
+ }
11
+
12
+ export class SkipList<T> {
13
+ // 最顶层的首个节点
14
+ private head: SkipListNode<T> = new SkipListNode<T>(null)
15
+ // 最顶层的最后一个节点
16
+ private tail: SkipListNode<T> = new SkipListNode<T>(null)
17
+
18
+ private height: number = 0
19
+ private _size: number = 0
20
+
21
+ // 元素数量
22
+ get size(): number {
23
+ return this._size
24
+ }
25
+
26
+ constructor(private compare: (a: T, b: T) => number) {
27
+ this.head.next = this.tail
28
+ this.tail.previous = this.head
29
+ }
30
+
31
+ * elements(): IterableIterator<SkipListNode<T>> {
32
+ let currentNode: SkipListNode<T> = this.head
33
+
34
+ while (currentNode.down) {
35
+ currentNode = currentNode.down
36
+ }
37
+
38
+ while (currentNode.next && isntNull(currentNode.next.value)) {
39
+ currentNode = currentNode.next
40
+ yield currentNode
41
+ }
42
+ }
43
+
44
+ dumpList(): Array<Array<T | null | undefined>> {
45
+ const result: Array<Array<T | null | undefined>> = []
46
+ let head: SkipListNode<T> | null = this.head
47
+
48
+ do {
49
+ result.push([])
50
+ let currentNode: SkipListNode<T> | null = head
51
+ do {
52
+ result[result.length - 1].push(currentNode.value)
53
+ currentNode = currentNode.next
54
+ } while (currentNode)
55
+ } while (head = head.down)
56
+
57
+ return result
58
+ }
59
+
60
+ delete(value: T): void {
61
+ let currentNode: SkipListNode<T> | null = this.findNode(value)
62
+
63
+ while (currentNode) {
64
+ // 被删除的节点肯定不会是head或tail, 因此一定有previous
65
+ currentNode.previous!.next = currentNode.next
66
+
67
+ // 被删除的节点肯定不会是head或tail, 因此一定有next
68
+ currentNode.next!.previous = currentNode.previous
69
+
70
+ currentNode = currentNode.down
71
+ }
72
+
73
+ this._size--
74
+ }
75
+
76
+ has(value: T): boolean {
77
+ return isntNull(this.findNode(value))
78
+ }
79
+
80
+ findNode(value: T): SkipListNode<T> | null {
81
+ let currentNode: SkipListNode<T> = this.head
82
+
83
+ while (true) {
84
+ while (currentNode.next && isntNull(currentNode.next.value)) {
85
+ const result = this.compare(currentNode.next.value, value)
86
+ if (result === 0) {
87
+ return currentNode.next
88
+ } else if (result < 0) {
89
+ currentNode = currentNode.next
90
+ } else {
91
+ break
92
+ }
93
+ }
94
+
95
+ if (currentNode.down) {
96
+ currentNode = currentNode.down
97
+ } else {
98
+ return null
99
+ }
100
+ }
101
+ }
102
+
103
+ add(value: T): void {
104
+ let previousNode: SkipListNode<T> = this.findPreviousNode(value)
105
+
106
+ // 如果previousNode.next.value等于value, 则说明value已经存在
107
+ if (
108
+ previousNode.next &&
109
+ isntNull(previousNode.next.value) &&
110
+ this.compare(previousNode.next.value, value) === 0
111
+ ) {
112
+ return
113
+ }
114
+
115
+ let newNode: SkipListNode<T> = new SkipListNode(value)
116
+ // 在底层加入新节点
117
+ if (previousNode.next) {
118
+ newNode.next = previousNode.next
119
+ previousNode.next.previous = newNode
120
+ }
121
+ previousNode.next = newNode
122
+ newNode.previous = previousNode
123
+
124
+ // 在高层加入新节点, 具体加到多高是随机的, 从第一层(将其视作索引值0)开始建造
125
+ // 层数非常高是极为罕见的, 因为每次建造层都会导致下一层被建造的概率减半.
126
+ let level = 0
127
+ while (Math.random() < 0.5) {
128
+ // currentLevel的索引与当前跳跃列表的高度相同, 需要在这之上建立一个新层
129
+ if (level === this.height) {
130
+ const newHead: SkipListNode<T> = new SkipListNode<T>(null)
131
+ const newTail: SkipListNode<T> = new SkipListNode<T>(null)
132
+
133
+ newHead.next = newTail
134
+ newHead.down = this.head
135
+
136
+ newTail.previous = newHead
137
+ newTail.down = this.tail
138
+
139
+ this.head.up = newHead
140
+ this.tail.up = newTail
141
+
142
+ this.head = newHead
143
+ this.tail = newTail
144
+
145
+ this.height++
146
+ }
147
+
148
+ // 从新节点的前一个节点遍历, 直到找到能够通往上层的节点
149
+ while (!previousNode.up) {
150
+ // head节点必然会有up, 所以不可能访问进入循环体, 不可能访问`head.previous`
151
+ previousNode = previousNode.previous!
152
+ }
153
+ // 上一层的对应节点
154
+ previousNode = previousNode.up!
155
+
156
+ // 创建新节点的上层节点
157
+ const upperNewNode: SkipListNode<T> = new SkipListNode<T>(value)
158
+ if (previousNode.next) {
159
+ upperNewNode.next = previousNode.next
160
+ previousNode.next.previous = upperNewNode
161
+ }
162
+ previousNode.next = upperNewNode
163
+ upperNewNode.previous = previousNode
164
+
165
+ upperNewNode.down = newNode
166
+ newNode.up = upperNewNode
167
+ newNode = upperNewNode
168
+
169
+ level++
170
+ }
171
+
172
+ this._size++
173
+ }
174
+
175
+ private findPreviousNode(value: T): SkipListNode<T> {
176
+ let currentNode: SkipListNode<T> = this.head
177
+
178
+ while (true) {
179
+ while (currentNode.next && isntNull(currentNode.next.value)) {
180
+ const result = this.compare(currentNode.next.value, value)
181
+ if (result >= 0) {
182
+ break
183
+ } else {
184
+ currentNode = currentNode.next
185
+ }
186
+ }
187
+
188
+ if (currentNode.down) {
189
+ currentNode = currentNode.down
190
+ } else {
191
+ return currentNode
192
+ }
193
+ }
194
+ }
195
+ }
@@ -0,0 +1,15 @@
1
+ // http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightModLookup
2
+ const mod37BitPositions: number[] = [
3
+ 32, 0, 1, 26, 2, 23, 27, 0, 3, 16
4
+ , 24, 30, 28, 11, 0, 13, 4, 7, 17, 0
5
+ , 25, 22, 31, 15, 29, 10, 12, 6, 0, 21
6
+ , 14, 9, 5, 20, 8, 19, 18
7
+ ]
8
+
9
+ /**
10
+ * 该函数在找不到结果时返回32, 与Rust内置函数的表现一致.
11
+ * https://doc.rust-lang.org/std/primitive.u32.html#method.trailing_zeros
12
+ */
13
+ export function trailingZeros(value: number): number {
14
+ return mod37BitPositions[(-value & value) % 37]
15
+ }