Skip to content

Commit d47d4e7

Browse files
committed
feat: extract bitwise
1 parent 951f104 commit d47d4e7

File tree

20 files changed

+858
-320
lines changed

20 files changed

+858
-320
lines changed

package-lock.json

Lines changed: 148 additions & 111 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,9 @@
7878
"release-it": "^17.11.0",
7979
"typescript": "^5.7.2",
8080
"typescript-eslint": "^8.18.2",
81-
"vite": "^6.0.5",
81+
"vite": "^6.0.6",
8282
"vite-node": "^2.1.8",
8383
"vite-plugin-dts": "^4.4.0",
8484
"vitest": "^2.1.8"
85-
},
86-
"dependencies": {
87-
"long": "^5.2.3"
8885
}
8986
}

src/bitwise/long.test.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { BitwiseLong, BitwiseLongAlloc, LongUtils } from './long';
3+
import { Long } from './types';
4+
5+
describe('BitwiseLong and BitwiseLongAlloc', () => {
6+
const implementations = [BitwiseLong, BitwiseLongAlloc];
7+
const createLong = (low: number, high: number): Long =>
8+
new Uint32Array([low, high]);
9+
10+
implementations.forEach((implementation) => {
11+
const name =
12+
implementation === BitwiseLong ? 'BitwiseLong' : 'BitwiseLongAlloc';
13+
14+
describe(name, () => {
15+
describe('and', () => {
16+
it('performs bitwise AND operation', () => {
17+
const a = createLong(0b1010, 0b1100);
18+
const b = createLong(0b1100, 0b1010);
19+
const result = implementation.and(a, b);
20+
expect(Array.from(result)).toEqual([0b1000, 0b1000]);
21+
});
22+
});
23+
24+
describe('or', () => {
25+
it('performs bitwise OR operation', () => {
26+
const a = createLong(0b1010, 0b1100);
27+
const b = createLong(0b1100, 0b1010);
28+
const result = implementation.or(a, b);
29+
expect(Array.from(result)).toEqual([0b1110, 0b1110]);
30+
});
31+
});
32+
33+
describe('xor', () => {
34+
it('performs bitwise XOR operation', () => {
35+
const a = createLong(0b1010, 0b1100);
36+
const b = createLong(0b1100, 0b1010);
37+
const result = implementation.xor(a, b);
38+
expect(Array.from(result)).toEqual([0b0110, 0b0110]);
39+
});
40+
});
41+
42+
describe('not', () => {
43+
it('performs bitwise NOT operation', () => {
44+
const a = createLong(0b1010, 0b1100);
45+
const result = implementation.not(a);
46+
expect(Array.from(result)).toEqual([0xfffffff5, 0xfffffff3]);
47+
});
48+
});
49+
50+
describe('rotLeft', () => {
51+
it('rotates bits left within 32-bit boundary', () => {
52+
const a = createLong(0b1, 0);
53+
const result = implementation.rotLeft(a, 1);
54+
expect(Array.from(result)).toEqual([0b10, 0]);
55+
});
56+
57+
it('rotates bits left across 32-bit boundary', () => {
58+
const a = createLong(0x80000000, 0);
59+
const result = implementation.rotLeft(a, 1);
60+
expect(Array.from(result)).toEqual([0, 1]);
61+
});
62+
63+
it('handles rotation greater than 63 bits', () => {
64+
const a = createLong(0b1, 0);
65+
const result = implementation.rotLeft(a, 65);
66+
expect(Array.from(result)).toEqual([0b10, 0]);
67+
});
68+
});
69+
70+
describe('rotRight', () => {
71+
it('rotates bits right within 32-bit boundary', () => {
72+
const a = createLong(0b10, 0);
73+
const result = implementation.rotRight(a, 1);
74+
expect(Array.from(result)).toEqual([0b1, 0]);
75+
});
76+
77+
it('rotates bits right across 32-bit boundary', () => {
78+
const a = createLong(0, 1);
79+
const result = implementation.rotRight(a, 1);
80+
expect(Array.from(result)).toEqual([0x80000000, 0]);
81+
});
82+
83+
it('handles rotation greater than 63 bits', () => {
84+
const a = createLong(0b10, 0);
85+
const result = implementation.rotRight(a, 65);
86+
expect(Array.from(result)).toEqual([0b1, 0]);
87+
});
88+
});
89+
90+
describe('cardinality', () => {
91+
it('counts number of set bits', () => {
92+
const a = createLong(0b1010, 0b1100);
93+
expect(implementation.cardinality(a)).toBe(4);
94+
});
95+
});
96+
97+
describe('decompose', () => {
98+
it('decomposes long into powers of 2', () => {
99+
const a = createLong(0b1010, 0b1100);
100+
const result = implementation.decompose(a);
101+
expect(result.length).toBe(4);
102+
expect(result.map((x) => Array.from(x))).toEqual([
103+
[2, 0],
104+
[8, 0],
105+
[0, 4],
106+
[0, 8],
107+
]);
108+
});
109+
});
110+
});
111+
});
112+
});
113+
114+
describe('LongUtils', () => {
115+
describe('create', () => {
116+
it('creates a Long with specified low and high values', () => {
117+
const result = LongUtils.create(0x12345678, 0x90abcdef);
118+
expect(Array.from(result)).toEqual([0x12345678, 0x90abcdef]);
119+
});
120+
121+
it('handles values outside 32-bit range', () => {
122+
const result = LongUtils.create(0x100000000, 0x100000000);
123+
expect(Array.from(result)).toEqual([0, 0]);
124+
});
125+
});
126+
127+
describe('createAlloc', () => {
128+
it('creates a new Long with specified low and high values', () => {
129+
const result = LongUtils.createAlloc(0x12345678, 0x90abcdef);
130+
expect(Array.from(result)).toEqual([0x12345678, 0x90abcdef]);
131+
});
132+
133+
it('handles values outside 32-bit range', () => {
134+
const result = LongUtils.createAlloc(0x100000000, 0x100000000);
135+
expect(Array.from(result)).toEqual([0, 0]);
136+
});
137+
});
138+
});

src/bitwise/long.ts

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { IBitwiseOps, Long } from './types';
2+
import { BitwiseNumber } from './number';
3+
4+
export const BitwiseLongAlloc: IBitwiseOps<Long> = {
5+
and(...args: Long[]): Long {
6+
return new Uint32Array(
7+
args.reduce(
8+
(a, b) => [a[0] & b[0], a[1] & b[1]],
9+
[0xffffffff, 0xffffffff]
10+
)
11+
);
12+
},
13+
14+
or(...args: Long[]): Long {
15+
return new Uint32Array(
16+
args.reduce((a, b) => [a[0] | b[0], a[1] | b[1]], [0, 0])
17+
);
18+
},
19+
20+
xor(a: Long, b: Long): Long {
21+
return new Uint32Array([a[0] ^ b[0], a[1] ^ b[1]]);
22+
},
23+
24+
not(a: Long): Long {
25+
return new Uint32Array([~a[0] >>> 0, ~a[1] >>> 0]);
26+
},
27+
28+
rotLeft(a: Long, shift: number): Long {
29+
const rotation = shift & 63;
30+
if (rotation === 0) {
31+
return new Uint32Array(a);
32+
}
33+
34+
if (rotation < 32) {
35+
const lowToHigh = a[0] >>> (32 - rotation);
36+
const highToLow = a[1] >>> (32 - rotation);
37+
return new Uint32Array([
38+
((a[0] << rotation) | highToLow) >>> 0,
39+
((a[1] << rotation) | lowToHigh) >>> 0,
40+
]);
41+
}
42+
43+
const actualRotation = rotation - 32;
44+
const lowToHigh = a[0] << actualRotation;
45+
const highToLow = a[1] << actualRotation;
46+
return new Uint32Array([
47+
((a[1] >>> (32 - actualRotation)) | highToLow) >>> 0,
48+
((a[0] >>> (32 - actualRotation)) | lowToHigh) >>> 0,
49+
]);
50+
},
51+
52+
rotRight(a: Long, shift: number): Long {
53+
const rotation = shift & 63;
54+
if (rotation === 0) {
55+
return new Uint32Array(a);
56+
}
57+
58+
if (rotation < 32) {
59+
const lowToHigh = a[0] << (32 - rotation);
60+
const highToLow = a[1] << (32 - rotation);
61+
return new Uint32Array([
62+
((a[0] >>> rotation) | highToLow) >>> 0,
63+
((a[1] >>> rotation) | lowToHigh) >>> 0,
64+
]);
65+
}
66+
67+
const actualRotation = rotation - 32;
68+
return new Uint32Array([
69+
(a[1] >>> actualRotation) >>> 0,
70+
(a[0] >>> actualRotation) >>> 0,
71+
]);
72+
},
73+
74+
cardinality(a: Long): number {
75+
return BitwiseNumber.cardinality(a[0]) + BitwiseNumber.cardinality(a[1]);
76+
},
77+
78+
decompose(a: Long): Long[] {
79+
const result: Long[] = [];
80+
let remainingLow = a[0];
81+
let remainingHigh = a[1];
82+
83+
for (let bit = 1; remainingLow; bit <<= 1) {
84+
if (remainingLow & bit) {
85+
result.push(new Uint32Array([bit, 0]));
86+
remainingLow ^= bit;
87+
}
88+
}
89+
90+
for (let bit = 1; remainingHigh; bit <<= 1) {
91+
if (remainingHigh & bit) {
92+
result.push(new Uint32Array([0, bit]));
93+
remainingHigh ^= bit;
94+
}
95+
}
96+
97+
return result;
98+
},
99+
} as IBitwiseOps<Long>;
100+
101+
const TEMP_AND = new Uint32Array(2);
102+
const TEMP_OR = new Uint32Array(2);
103+
const TEMP_XOR = new Uint32Array(2);
104+
const TEMP_NOT = new Uint32Array(2);
105+
const TEMP_ROT_LEFT = new Uint32Array(2);
106+
const TEMP_ROT_RIGHT = new Uint32Array(2);
107+
108+
export const BitwiseLong: IBitwiseOps<Long> = {
109+
and(...args: Long[]): Long {
110+
TEMP_AND[0] = args.reduce((a, b) => a & b[0], 0xffffffff);
111+
TEMP_AND[1] = args.reduce((a, b) => a & b[1], 0xffffffff);
112+
return TEMP_AND;
113+
},
114+
115+
or(...args: Long[]): Long {
116+
TEMP_OR[0] = args.reduce((a, b) => a | b[0], 0);
117+
TEMP_OR[1] = args.reduce((a, b) => a | b[1], 0);
118+
return TEMP_OR;
119+
},
120+
121+
xor(a: Long, b: Long): Long {
122+
TEMP_XOR[0] = a[0] ^ b[0];
123+
TEMP_XOR[1] = a[1] ^ b[1];
124+
return TEMP_XOR;
125+
},
126+
127+
not(a: Long): Long {
128+
TEMP_NOT[0] = ~a[0] >>> 0;
129+
TEMP_NOT[1] = ~a[1] >>> 0;
130+
return TEMP_NOT;
131+
},
132+
133+
rotLeft(a: Long, shift: number): Long {
134+
const rotation = shift & 63;
135+
if (rotation === 0) {
136+
TEMP_ROT_LEFT.set(a);
137+
return TEMP_ROT_LEFT;
138+
}
139+
140+
if (rotation < 32) {
141+
const lowToHigh = a[0] >>> (32 - rotation);
142+
const highToLow = a[1] >>> (32 - rotation);
143+
TEMP_ROT_LEFT[0] = ((a[0] << rotation) | highToLow) >>> 0;
144+
TEMP_ROT_LEFT[1] = ((a[1] << rotation) | lowToHigh) >>> 0;
145+
return TEMP_ROT_LEFT;
146+
}
147+
148+
const actualRotation = rotation - 32;
149+
const lowToHigh = a[0] << actualRotation;
150+
const highToLow = a[1] << actualRotation;
151+
TEMP_ROT_LEFT[0] = ((a[1] >>> (32 - actualRotation)) | highToLow) >>> 0;
152+
TEMP_ROT_LEFT[1] = ((a[0] >>> (32 - actualRotation)) | lowToHigh) >>> 0;
153+
return TEMP_ROT_LEFT;
154+
},
155+
156+
rotRight(a: Long, shift: number): Long {
157+
const rotation = shift & 63;
158+
if (rotation === 0) {
159+
TEMP_ROT_RIGHT.set(a);
160+
return TEMP_ROT_RIGHT;
161+
}
162+
163+
if (rotation < 32) {
164+
const lowToHigh = a[0] << (32 - rotation);
165+
const highToLow = a[1] << (32 - rotation);
166+
TEMP_ROT_RIGHT[0] = ((a[0] >>> rotation) | highToLow) >>> 0;
167+
TEMP_ROT_RIGHT[1] = ((a[1] >>> rotation) | lowToHigh) >>> 0;
168+
return TEMP_ROT_RIGHT;
169+
}
170+
171+
const actualRotation = rotation - 32;
172+
TEMP_ROT_RIGHT[0] = (a[1] >>> actualRotation) >>> 0;
173+
TEMP_ROT_RIGHT[1] = (a[0] >>> actualRotation) >>> 0;
174+
return TEMP_ROT_RIGHT;
175+
},
176+
177+
cardinality(a: Long): number {
178+
return BitwiseLongAlloc.cardinality(a);
179+
},
180+
181+
decompose(a: Long): Long[] {
182+
return BitwiseLongAlloc.decompose(a);
183+
},
184+
} as IBitwiseOps<Long>;
185+
186+
const TEMP_CREATE = new Uint32Array(2);
187+
188+
export const LongUtils = {
189+
createAlloc(low: number, high: number): Long {
190+
return new Uint32Array([low >>> 0, high >>> 0]);
191+
},
192+
193+
create(low: number, high: number): Long {
194+
TEMP_CREATE[0] = low >>> 0;
195+
TEMP_CREATE[1] = high >>> 0;
196+
return TEMP_CREATE;
197+
},
198+
};

0 commit comments

Comments
 (0)