-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
858 additions
and
320 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { BitwiseLong, BitwiseLongAlloc, LongUtils } from './long'; | ||
import { Long } from './types'; | ||
|
||
describe('BitwiseLong and BitwiseLongAlloc', () => { | ||
const implementations = [BitwiseLong, BitwiseLongAlloc]; | ||
const createLong = (low: number, high: number): Long => | ||
new Uint32Array([low, high]); | ||
|
||
implementations.forEach((implementation) => { | ||
const name = | ||
implementation === BitwiseLong ? 'BitwiseLong' : 'BitwiseLongAlloc'; | ||
|
||
describe(name, () => { | ||
describe('and', () => { | ||
it('performs bitwise AND operation', () => { | ||
const a = createLong(0b1010, 0b1100); | ||
const b = createLong(0b1100, 0b1010); | ||
const result = implementation.and(a, b); | ||
expect(Array.from(result)).toEqual([0b1000, 0b1000]); | ||
}); | ||
}); | ||
|
||
describe('or', () => { | ||
it('performs bitwise OR operation', () => { | ||
const a = createLong(0b1010, 0b1100); | ||
const b = createLong(0b1100, 0b1010); | ||
const result = implementation.or(a, b); | ||
expect(Array.from(result)).toEqual([0b1110, 0b1110]); | ||
}); | ||
}); | ||
|
||
describe('xor', () => { | ||
it('performs bitwise XOR operation', () => { | ||
const a = createLong(0b1010, 0b1100); | ||
const b = createLong(0b1100, 0b1010); | ||
const result = implementation.xor(a, b); | ||
expect(Array.from(result)).toEqual([0b0110, 0b0110]); | ||
}); | ||
}); | ||
|
||
describe('not', () => { | ||
it('performs bitwise NOT operation', () => { | ||
const a = createLong(0b1010, 0b1100); | ||
const result = implementation.not(a); | ||
expect(Array.from(result)).toEqual([0xfffffff5, 0xfffffff3]); | ||
}); | ||
}); | ||
|
||
describe('rotLeft', () => { | ||
it('rotates bits left within 32-bit boundary', () => { | ||
const a = createLong(0b1, 0); | ||
const result = implementation.rotLeft(a, 1); | ||
expect(Array.from(result)).toEqual([0b10, 0]); | ||
}); | ||
|
||
it('rotates bits left across 32-bit boundary', () => { | ||
const a = createLong(0x80000000, 0); | ||
const result = implementation.rotLeft(a, 1); | ||
expect(Array.from(result)).toEqual([0, 1]); | ||
}); | ||
|
||
it('handles rotation greater than 63 bits', () => { | ||
const a = createLong(0b1, 0); | ||
const result = implementation.rotLeft(a, 65); | ||
expect(Array.from(result)).toEqual([0b10, 0]); | ||
}); | ||
}); | ||
|
||
describe('rotRight', () => { | ||
it('rotates bits right within 32-bit boundary', () => { | ||
const a = createLong(0b10, 0); | ||
const result = implementation.rotRight(a, 1); | ||
expect(Array.from(result)).toEqual([0b1, 0]); | ||
}); | ||
|
||
it('rotates bits right across 32-bit boundary', () => { | ||
const a = createLong(0, 1); | ||
const result = implementation.rotRight(a, 1); | ||
expect(Array.from(result)).toEqual([0x80000000, 0]); | ||
}); | ||
|
||
it('handles rotation greater than 63 bits', () => { | ||
const a = createLong(0b10, 0); | ||
const result = implementation.rotRight(a, 65); | ||
expect(Array.from(result)).toEqual([0b1, 0]); | ||
}); | ||
}); | ||
|
||
describe('cardinality', () => { | ||
it('counts number of set bits', () => { | ||
const a = createLong(0b1010, 0b1100); | ||
expect(implementation.cardinality(a)).toBe(4); | ||
}); | ||
}); | ||
|
||
describe('decompose', () => { | ||
it('decomposes long into powers of 2', () => { | ||
const a = createLong(0b1010, 0b1100); | ||
const result = implementation.decompose(a); | ||
expect(result.length).toBe(4); | ||
expect(result.map((x) => Array.from(x))).toEqual([ | ||
[2, 0], | ||
[8, 0], | ||
[0, 4], | ||
[0, 8], | ||
]); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('LongUtils', () => { | ||
describe('create', () => { | ||
it('creates a Long with specified low and high values', () => { | ||
const result = LongUtils.create(0x12345678, 0x90abcdef); | ||
expect(Array.from(result)).toEqual([0x12345678, 0x90abcdef]); | ||
}); | ||
|
||
it('handles values outside 32-bit range', () => { | ||
const result = LongUtils.create(0x100000000, 0x100000000); | ||
expect(Array.from(result)).toEqual([0, 0]); | ||
}); | ||
}); | ||
|
||
describe('createAlloc', () => { | ||
it('creates a new Long with specified low and high values', () => { | ||
const result = LongUtils.createAlloc(0x12345678, 0x90abcdef); | ||
expect(Array.from(result)).toEqual([0x12345678, 0x90abcdef]); | ||
}); | ||
|
||
it('handles values outside 32-bit range', () => { | ||
const result = LongUtils.createAlloc(0x100000000, 0x100000000); | ||
expect(Array.from(result)).toEqual([0, 0]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import { IBitwiseOps, Long } from './types'; | ||
import { BitwiseNumber } from './number'; | ||
|
||
export const BitwiseLongAlloc: IBitwiseOps<Long> = { | ||
and(...args: Long[]): Long { | ||
return new Uint32Array( | ||
args.reduce( | ||
(a, b) => [a[0] & b[0], a[1] & b[1]], | ||
[0xffffffff, 0xffffffff] | ||
) | ||
); | ||
}, | ||
|
||
or(...args: Long[]): Long { | ||
return new Uint32Array( | ||
args.reduce((a, b) => [a[0] | b[0], a[1] | b[1]], [0, 0]) | ||
); | ||
}, | ||
|
||
xor(a: Long, b: Long): Long { | ||
return new Uint32Array([a[0] ^ b[0], a[1] ^ b[1]]); | ||
}, | ||
|
||
not(a: Long): Long { | ||
return new Uint32Array([~a[0] >>> 0, ~a[1] >>> 0]); | ||
}, | ||
|
||
rotLeft(a: Long, shift: number): Long { | ||
const rotation = shift & 63; | ||
if (rotation === 0) { | ||
return new Uint32Array(a); | ||
} | ||
|
||
if (rotation < 32) { | ||
const lowToHigh = a[0] >>> (32 - rotation); | ||
const highToLow = a[1] >>> (32 - rotation); | ||
return new Uint32Array([ | ||
((a[0] << rotation) | highToLow) >>> 0, | ||
((a[1] << rotation) | lowToHigh) >>> 0, | ||
]); | ||
} | ||
|
||
const actualRotation = rotation - 32; | ||
const lowToHigh = a[0] << actualRotation; | ||
const highToLow = a[1] << actualRotation; | ||
return new Uint32Array([ | ||
((a[1] >>> (32 - actualRotation)) | highToLow) >>> 0, | ||
((a[0] >>> (32 - actualRotation)) | lowToHigh) >>> 0, | ||
]); | ||
}, | ||
|
||
rotRight(a: Long, shift: number): Long { | ||
const rotation = shift & 63; | ||
if (rotation === 0) { | ||
return new Uint32Array(a); | ||
} | ||
|
||
if (rotation < 32) { | ||
const lowToHigh = a[0] << (32 - rotation); | ||
const highToLow = a[1] << (32 - rotation); | ||
return new Uint32Array([ | ||
((a[0] >>> rotation) | highToLow) >>> 0, | ||
((a[1] >>> rotation) | lowToHigh) >>> 0, | ||
]); | ||
} | ||
|
||
const actualRotation = rotation - 32; | ||
return new Uint32Array([ | ||
(a[1] >>> actualRotation) >>> 0, | ||
(a[0] >>> actualRotation) >>> 0, | ||
]); | ||
}, | ||
|
||
cardinality(a: Long): number { | ||
return BitwiseNumber.cardinality(a[0]) + BitwiseNumber.cardinality(a[1]); | ||
}, | ||
|
||
decompose(a: Long): Long[] { | ||
const result: Long[] = []; | ||
let remainingLow = a[0]; | ||
let remainingHigh = a[1]; | ||
|
||
for (let bit = 1; remainingLow; bit <<= 1) { | ||
if (remainingLow & bit) { | ||
result.push(new Uint32Array([bit, 0])); | ||
remainingLow ^= bit; | ||
} | ||
} | ||
|
||
for (let bit = 1; remainingHigh; bit <<= 1) { | ||
if (remainingHigh & bit) { | ||
result.push(new Uint32Array([0, bit])); | ||
remainingHigh ^= bit; | ||
} | ||
} | ||
|
||
return result; | ||
}, | ||
} as IBitwiseOps<Long>; | ||
|
||
const TEMP_AND = new Uint32Array(2); | ||
const TEMP_OR = new Uint32Array(2); | ||
const TEMP_XOR = new Uint32Array(2); | ||
const TEMP_NOT = new Uint32Array(2); | ||
const TEMP_ROT_LEFT = new Uint32Array(2); | ||
const TEMP_ROT_RIGHT = new Uint32Array(2); | ||
|
||
export const BitwiseLong: IBitwiseOps<Long> = { | ||
and(...args: Long[]): Long { | ||
TEMP_AND[0] = args.reduce((a, b) => a & b[0], 0xffffffff); | ||
TEMP_AND[1] = args.reduce((a, b) => a & b[1], 0xffffffff); | ||
return TEMP_AND; | ||
}, | ||
|
||
or(...args: Long[]): Long { | ||
TEMP_OR[0] = args.reduce((a, b) => a | b[0], 0); | ||
TEMP_OR[1] = args.reduce((a, b) => a | b[1], 0); | ||
return TEMP_OR; | ||
}, | ||
|
||
xor(a: Long, b: Long): Long { | ||
TEMP_XOR[0] = a[0] ^ b[0]; | ||
TEMP_XOR[1] = a[1] ^ b[1]; | ||
return TEMP_XOR; | ||
}, | ||
|
||
not(a: Long): Long { | ||
TEMP_NOT[0] = ~a[0] >>> 0; | ||
TEMP_NOT[1] = ~a[1] >>> 0; | ||
return TEMP_NOT; | ||
}, | ||
|
||
rotLeft(a: Long, shift: number): Long { | ||
const rotation = shift & 63; | ||
if (rotation === 0) { | ||
TEMP_ROT_LEFT.set(a); | ||
return TEMP_ROT_LEFT; | ||
} | ||
|
||
if (rotation < 32) { | ||
const lowToHigh = a[0] >>> (32 - rotation); | ||
const highToLow = a[1] >>> (32 - rotation); | ||
TEMP_ROT_LEFT[0] = ((a[0] << rotation) | highToLow) >>> 0; | ||
TEMP_ROT_LEFT[1] = ((a[1] << rotation) | lowToHigh) >>> 0; | ||
return TEMP_ROT_LEFT; | ||
} | ||
|
||
const actualRotation = rotation - 32; | ||
const lowToHigh = a[0] << actualRotation; | ||
const highToLow = a[1] << actualRotation; | ||
TEMP_ROT_LEFT[0] = ((a[1] >>> (32 - actualRotation)) | highToLow) >>> 0; | ||
TEMP_ROT_LEFT[1] = ((a[0] >>> (32 - actualRotation)) | lowToHigh) >>> 0; | ||
return TEMP_ROT_LEFT; | ||
}, | ||
|
||
rotRight(a: Long, shift: number): Long { | ||
const rotation = shift & 63; | ||
if (rotation === 0) { | ||
TEMP_ROT_RIGHT.set(a); | ||
return TEMP_ROT_RIGHT; | ||
} | ||
|
||
if (rotation < 32) { | ||
const lowToHigh = a[0] << (32 - rotation); | ||
const highToLow = a[1] << (32 - rotation); | ||
TEMP_ROT_RIGHT[0] = ((a[0] >>> rotation) | highToLow) >>> 0; | ||
TEMP_ROT_RIGHT[1] = ((a[1] >>> rotation) | lowToHigh) >>> 0; | ||
return TEMP_ROT_RIGHT; | ||
} | ||
|
||
const actualRotation = rotation - 32; | ||
TEMP_ROT_RIGHT[0] = (a[1] >>> actualRotation) >>> 0; | ||
TEMP_ROT_RIGHT[1] = (a[0] >>> actualRotation) >>> 0; | ||
return TEMP_ROT_RIGHT; | ||
}, | ||
|
||
cardinality(a: Long): number { | ||
return BitwiseLongAlloc.cardinality(a); | ||
}, | ||
|
||
decompose(a: Long): Long[] { | ||
return BitwiseLongAlloc.decompose(a); | ||
}, | ||
} as IBitwiseOps<Long>; | ||
|
||
const TEMP_CREATE = new Uint32Array(2); | ||
|
||
export const LongUtils = { | ||
createAlloc(low: number, high: number): Long { | ||
return new Uint32Array([low >>> 0, high >>> 0]); | ||
}, | ||
|
||
create(low: number, high: number): Long { | ||
TEMP_CREATE[0] = low >>> 0; | ||
TEMP_CREATE[1] = high >>> 0; | ||
return TEMP_CREATE; | ||
}, | ||
}; |
Oops, something went wrong.