Skip to content

Commit 602aaaf

Browse files
committed
✨ feat(binary-search-tree): export createBinaryIndexTree1Mod and createBinaryIndexTree2Mod
1 parent dea9c50 commit 602aaaf

File tree

9 files changed

+356
-24
lines changed

9 files changed

+356
-24
lines changed

Diff for: packages/binary-index-tree/README-zh.md

+22
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,28 @@
204204
bit.query(/* any integer between [5, 10] */) // => 0n
205205
```
206206

207+
* With Mod
208+
209+
```typescript
210+
import { createBinaryIndexTree1Mod } from '@algorithm.ts/binary-index-tree'
211+
212+
const MOD = 1e9 + 7
213+
const bit = createBinaryIndexTree1Mod<number>(0, MOD)
214+
215+
bit.add(2, <value>) // <value> should in the range of (-MOD, MOD)
216+
bit.query(3)
217+
```
218+
219+
```typescript
220+
import { createBinaryIndexTree2Mod } from '@algorithm.ts/binary-index-tree'
221+
222+
const MOD = 1e9 + 7
223+
const bit = createBinaryIndexTree1Mod<bigint>(0, BigInt(MOD))
224+
225+
bit.add(2, <value>) // <value> should in the range of (-MOD, MOD)
226+
bit.query(3)
227+
```
228+
207229

208230
## Related
209231

Diff for: packages/binary-index-tree/README.md

+22
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,28 @@ implementation is simpler and easier to understand.
211211
bit.query(/* any integer between [5, 10] */) // => 0n
212212
```
213213

214+
* With Mod
215+
216+
```typescript
217+
import { createBinaryIndexTree1Mod } from '@algorithm.ts/binary-index-tree'
218+
219+
const MOD = 1e9 + 7
220+
const bit = createBinaryIndexTree1Mod<number>(0, MOD)
221+
222+
bit.add(2, <value>) // <value> should in the range of (-MOD, MOD)
223+
bit.query(3)
224+
```
225+
226+
```typescript
227+
import { createBinaryIndexTree2Mod } from '@algorithm.ts/binary-index-tree'
228+
229+
const MOD = 1e9 + 7
230+
const bit = createBinaryIndexTree1Mod<bigint>(0, BigInt(MOD))
231+
232+
bit.add(2, <value>) // <value> should in the range of (-MOD, MOD)
233+
bit.query(3)
234+
```
235+
214236

215237
## Related
216238

Diff for: packages/binary-index-tree/__test__/binary-saerch-tree.spec.ts

+151-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { randomInt } from 'crypto'
2-
import { createBinaryIndexTree1, createBinaryIndexTree2, lowbit } from '../src'
2+
import {
3+
createBinaryIndexTree1,
4+
createBinaryIndexTree1Mod,
5+
createBinaryIndexTree2,
6+
createBinaryIndexTree2Mod,
7+
lowbit,
8+
} from '../src'
39

410
describe('tree1', function () {
511
const MAX_N = 1000
@@ -143,6 +149,150 @@ describe('tree2', function () {
143149
})
144150
})
145151

152+
describe('tree1-mod', function () {
153+
const MAX_N = 1000
154+
const MOD = 1000 + 17
155+
156+
test('number', function () {
157+
const bit = createBinaryIndexTree1Mod<number>(0, MOD)
158+
bit.init(MAX_N)
159+
160+
const A: number[] = new Array(MAX_N)
161+
.fill(0)
162+
.map((_x, i) => randomInt(MOD - 1))
163+
for (let x = 0; x < MAX_N; ++x) bit.add(x + 1, A[x])
164+
165+
const getSum = (x: number): number => {
166+
let result = 0
167+
for (let i = 0; i < x; ++i) result += A[i]
168+
return result % MOD
169+
}
170+
171+
for (let q = 0; q < 1000; ++q) {
172+
const x = Math.max(1, Math.ceil(Math.random() * MAX_N))
173+
const value = Math.round(Math.random() * (MOD - 1))
174+
175+
A[x - 1] += value
176+
bit.add(x, value)
177+
expect(bit.query(x)).toBe(getSum(x))
178+
}
179+
180+
// Test illegal input.
181+
bit.add(0, 1)
182+
bit.add(-1, 1)
183+
bit.query(0)
184+
bit.query(-1)
185+
})
186+
187+
test('bigint', function () {
188+
const bit = createBinaryIndexTree1Mod<bigint>(0n, BigInt(MOD))
189+
bit.init(MAX_N)
190+
191+
const A: Array<bigint> = new Array(MAX_N)
192+
.fill(0)
193+
.map((_x, i) => BigInt(randomInt(MOD)))
194+
for (let x = 0; x < MAX_N; ++x) bit.add(x + 1, A[x])
195+
196+
const getSum = (x: number): bigint => {
197+
let result = 0n
198+
for (let i = 0; i < x; ++i) result += A[i]
199+
return result % BigInt(MOD)
200+
}
201+
202+
for (let q = 0; q < 1000; ++q) {
203+
const x = Math.max(1, Math.ceil(Math.random() * MAX_N))
204+
const value = BigInt(Math.round(Math.random() * (MOD - 1)))
205+
206+
A[x - 1] += value
207+
bit.add(x, value)
208+
expect(bit.query(x)).toBe(getSum(x))
209+
}
210+
211+
// Test illegal input.
212+
bit.add(0, 1n)
213+
bit.add(-1, 1n)
214+
bit.query(0)
215+
bit.query(-1)
216+
})
217+
})
218+
219+
describe('tree2-mod', function () {
220+
const MAX_N = 1000
221+
const MOD = 1000 + 17
222+
223+
test('number', function () {
224+
const bit = createBinaryIndexTree2Mod<number>(0, MOD)
225+
bit.init(MAX_N)
226+
227+
// Initialize
228+
const A: number[] = new Array(MAX_N)
229+
.fill(0)
230+
.map((_x, i) => randomInt(MOD - 1))
231+
for (let x = 0; x < MAX_N; ++x) {
232+
bit.add(x, -A[x])
233+
bit.add(x + 1, A[x])
234+
}
235+
236+
// Test for initialization.
237+
for (let x = 1; x <= MAX_N; ++x) expect(bit.query(x)).toBe(A[x - 1])
238+
239+
const performAdd = (x: number, value: number): void => {
240+
for (let i = 0; i < x; ++i) A[i] += value
241+
}
242+
243+
for (let q = 0; q < 1000; ++q) {
244+
const x = Math.max(1, Math.ceil(Math.random() * MAX_N))
245+
const value = Math.round(Math.random() * (MOD - 1))
246+
247+
performAdd(x, value)
248+
bit.add(x, value)
249+
expect(bit.query(x)).toBe(A[x - 1] % MOD)
250+
}
251+
252+
// Test illegal input.
253+
bit.add(0, 1)
254+
bit.add(-1, 1)
255+
bit.query(0)
256+
bit.query(1)
257+
})
258+
259+
test('bigint', function () {
260+
const bit = createBinaryIndexTree2Mod<bigint>(0n, BigInt(MOD))
261+
bit.init(MAX_N)
262+
263+
// Initialize
264+
const A: Array<bigint> = new Array(MAX_N)
265+
.fill(0)
266+
.map((_x, i) => BigInt(randomInt(MOD - 1)))
267+
for (let x = 0; x < MAX_N; ++x) {
268+
bit.add(x, -A[x])
269+
bit.add(x + 1, A[x])
270+
}
271+
272+
// Test for initialization.
273+
for (let x = 1; x <= MAX_N; ++x) expect(bit.query(x)).toBe(A[x - 1])
274+
275+
const performAdd = (x: number, value: bigint): void => {
276+
for (let i = 0; i < x; ++i) A[i] += value
277+
}
278+
279+
for (let q = 0; q < 1000; ++q) {
280+
const x = Math.max(1, Math.ceil(Math.random() * MAX_N))
281+
const value = BigInt(Math.round(Math.random() * (MOD - 1)))
282+
283+
performAdd(x, value)
284+
bit.add(x, value)
285+
expect(bit.query(x)).toBe(A[x - 1] % BigInt(MOD))
286+
}
287+
288+
// Test illegal input.
289+
bit.add(0, 1n)
290+
bit.add(-1, 1n)
291+
bit.query(0)
292+
bit.query(-1)
293+
})
294+
})
295+
146296
describe('util', function () {
147297
test('lowbit', function () {
148298
const nums: number[] = new Array(1000).fill(0).map((_x, i) => i + 1)

Diff for: packages/binary-index-tree/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from './tree1'
2+
export * from './tree1-mod'
23
export * from './tree2'
4+
export * from './tree2-mod'
35
export * from './types'
46
export * from './util'

Diff for: packages/binary-index-tree/src/tree1-mod.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { BinaryIndexTree } from './types'
2+
import { lowbit } from './util'
3+
4+
/**
5+
* 创建一棵支持 *单点更新,区间查询* 的树状数组,所有的更新和查询操作均需对某个
6+
* 数进行取模,且所有数值应在范围 (-MOD, MOD) 之间。
7+
*
8+
* Create a binary search tree for *single-point update with interval query*.
9+
* All update and query operations need to be modulo a certain number, and all
10+
* values should be in the range (-MOD, MOD).
11+
*
12+
* @param ZERO 0 for number, 0n for bigint.
13+
* @param MOD
14+
* @returns
15+
*/
16+
export function createBinaryIndexTree1Mod<T extends number | bigint>(
17+
ZERO: T,
18+
MOD: T,
19+
): BinaryIndexTree<T> {
20+
let _size = 0
21+
const _nodes: T[] = [ZERO]
22+
return { init, add, query }
23+
24+
/**
25+
* Initialize the BinarySearchTree.
26+
* @param N
27+
*/
28+
function init(N: number): void {
29+
_size = N
30+
if (_nodes.length <= _size) _nodes.length = _size + 1
31+
_nodes.fill(ZERO, 1, _size + 1)
32+
}
33+
34+
/**
35+
* Add value to the xth number
36+
* @param xth
37+
* @param value value \in (-MOD, MOD)
38+
*/
39+
function add(xth: number, value: T): void {
40+
if (xth < 1) return
41+
// eslint-disable-next-line no-param-reassign
42+
if (value < 0) value += MOD as any
43+
for (let i = xth; i <= _size; i += lowbit(i)) {
44+
_nodes[i] = modAdd(_nodes[i], value)
45+
}
46+
}
47+
48+
/**
49+
* Calculate the prefix sum of the first x elements of the array.
50+
* @param xth
51+
*/
52+
function query(xth: number): T {
53+
let result = ZERO
54+
for (let i = xth; i > 0; i -= lowbit(i)) {
55+
result = modAdd(result, _nodes[i])
56+
}
57+
return result
58+
}
59+
60+
/**
61+
* @param x x \in [0, MOD)
62+
* @param y y \in [0, MOD)
63+
* @returns
64+
*/
65+
function modAdd(x: T, y: T): T {
66+
const result: any = (x as any) + (y as any)
67+
return result >= MOD ? result - MOD : result
68+
}
69+
}

Diff for: packages/binary-index-tree/src/tree1.ts

+8-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { lowbit } from './util'
66
*
77
* Create a binary search tree for *single-point update with interval query*.
88
*
9-
* @param MAX_N
109
* @param ZERO 0 for number, 0n for bigint.
1110
* @returns
1211
*/
@@ -15,6 +14,7 @@ export function createBinaryIndexTree1<T extends number | bigint>(
1514
): BinaryIndexTree<T> {
1615
let _size = 0
1716
const _nodes: T[] = [ZERO]
17+
return { init, add, query }
1818

1919
/**
2020
* Initialize the BinarySearchTree.
@@ -28,27 +28,25 @@ export function createBinaryIndexTree1<T extends number | bigint>(
2828

2929
/**
3030
* Add value to the xth number
31-
* @param x
31+
* @param xth
3232
* @param value
3333
*/
34-
function add(x: number, value: T): void {
35-
if (x < 1) return
36-
for (let i = x; i <= _size; i += lowbit(i)) {
34+
function add(xth: number, value: T): void {
35+
if (xth < 1) return
36+
for (let i = xth; i <= _size; i += lowbit(i)) {
3737
_nodes[i] += value as any
3838
}
3939
}
4040

4141
/**
4242
* Calculate the prefix sum of the first x elements of the array.
43-
* @param x
43+
* @param xth
4444
*/
45-
function query(x: number): T {
45+
function query(xth: number): T {
4646
let result = ZERO
47-
for (let i = x; i > 0; i -= lowbit(i)) {
47+
for (let i = xth; i > 0; i -= lowbit(i)) {
4848
result += _nodes[i] as any
4949
}
5050
return result
5151
}
52-
53-
return { init, add, query }
5452
}

0 commit comments

Comments
 (0)