@blackglory/structures 0.13.3 → 0.13.4
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.
- package/package.json +3 -2
- package/src/array.ts +19 -0
- package/src/big-map.ts +98 -0
- package/src/big-set.ts +72 -0
- package/src/bit-set.ts +124 -0
- package/src/box.ts +15 -0
- package/src/cons.ts +15 -0
- package/src/dynamic-typed-array.ts +201 -0
- package/src/emitter.ts +76 -0
- package/src/expirable-map.ts +122 -0
- package/src/hash-map.ts +34 -0
- package/src/hash-set.ts +38 -0
- package/src/index.ts +24 -0
- package/src/lru-map.ts +69 -0
- package/src/queue.ts +30 -0
- package/src/radix-tree.ts +264 -0
- package/src/sorted-set.ts +34 -0
- package/src/sparse-map.ts +83 -0
- package/src/sparse-set.ts +67 -0
- package/src/string-radix-tree.ts +263 -0
- package/src/string-trie-map.ts +102 -0
- package/src/tlru-map.ts +162 -0
- package/src/trie-map.ts +102 -0
- package/src/typed-bit-set.ts +129 -0
- package/src/typed-sparse-map.ts +97 -0
- package/src/typed-sparse-set.ts +71 -0
- package/src/utils/skip-list.ts +195 -0
- 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
|
+
}
|