copied set implementations from MultiFlag JS

This commit is contained in:
2026-02-09 22:11:23 +01:00
parent 80c17ac3ac
commit 140896bd7c
56 changed files with 8055 additions and 24 deletions

View File

@@ -0,0 +1,81 @@
import { ArrayFlagSet } from '@module'
test('Normalise to minimum', () => {
const flags = new ArrayFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B', flagA)
const flagC = flags.flag('C', flagA)
const flagD = flags.flag('D', flagC)
expect(flags.minimum([])).toEqual([])
expect(flags.minimum(['A'])).toEqual(['A'])
expect(flags.minimum(['B'])).toEqual([])
expect(flags.minimum(['A', 'B'])).toEqual(['A', 'B'])
expect(flags.minimum(['A', 'B', 'D'])).toEqual(['A', 'B'])
expect(flags.minimum(['A', 'C', 'D'])).toEqual(['A', 'C', 'D'])
expect(flags.minimum(['A', 'E'])).toEqual(['A'])
})
test('Normalise to maximum', () => {
const flags = new ArrayFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B', flagA)
const flagC = flags.flag('C', flagA)
const flagD = flags.flag('D', flagC)
expect(flags.maximum([])).toEqual([])
expect(flags.maximum(['A'])).toEqual(['A'])
expect(flags.maximum(['B'])).toEqual(['B', 'A'])
expect(flags.maximum(['A', 'B'])).toEqual(['A', 'B'])
expect(flags.maximum(['A', 'B', 'D'])).toEqual(['A', 'B', 'D', 'C'])
expect(flags.maximum(['A', 'C', 'D'])).toEqual(['A', 'C', 'D'])
expect(flags.maximum(['A', 'E'])).toEqual(['A'])
})
test('Add to array', () => {
const flags = new ArrayFlagSet<string>()
const flagB = flags.flag('B')
const flagC = flags.flag('C')
const flagsBAndC = flags.flag(flagB, flagC)
expect(flagB.addTo(['A'])).toEqual(['A', 'B'])
expect(flagC.addTo(['A'])).toEqual(['A', 'C'])
expect(flagsBAndC.addTo(['A'])).toEqual(['A', 'B', 'C'])
})
test('Remove from array', () => {
const flags = new ArrayFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B')
const flagC = flags.flag('C', flagA)
expect(flagA.removeFrom(['A', 'B', 'C'])).toEqual(['B'])
expect(flagB.removeFrom(['A', 'B', 'C'])).toEqual(['A', 'C'])
expect(flagC.removeFrom(['A', 'B', 'C'])).toEqual(['A', 'B'])
})
test('Is in array', () => {
const flags = new ArrayFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B')
const flagC = flags.flag('C', flagA)
expect(flagA.isIn(['A'])).toBe(true)
expect(flagB.isIn(['A', 'B'])).toBe(true)
expect(flagC.isIn(['C'])).toBe(false)
expect(flagC.isIn(['A', 'C'])).toBe(true)
})
test('Is abstract', () => {
const flags = new ArrayFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B')
const flagsAAndB = flags.flag(flagA, flagB)
const flagC = flags.flag('C', flagsAAndB)
expect(flagA.isAbstract).toBe(false)
expect(flagB.isAbstract).toBe(false)
expect(flagsAAndB.isAbstract).toBe(true)
expect(flagC.isAbstract).toBe(false)
})

View File

@@ -0,0 +1,270 @@
import { Base64BitflagSet } from '@module'
import { decodeByte, encodeByte } from '../src/base64'
test('Create from an index', () => {
const flags = new Base64BitflagSet()
const flag2 = flags.flag(2)
expect(flag2.addTo('')).toEqual('C')
expect(() => flags.flag(0)).toThrow(RangeError)
expect(() => flags.flag(-2)).toThrow(RangeError)
})
test('Union of two base-64 strings', () => {
const flags = new Base64BitflagSet()
expect(flags.union('', '')).toEqual('A')
expect(flags.union('A', 'A')).toEqual('A')
expect(flags.union('B', 'A')).toEqual('B')
expect(flags.union('A', 'C')).toEqual('C')
expect(flags.union('B', 'C')).toEqual('D')
expect(flags.union('D', 'G')).toEqual('H')
})
test('Difference of two base-64 strings', () => {
const flags = new Base64BitflagSet()
expect(flags.difference('', '')).toEqual('A')
expect(flags.difference('A', 'A')).toEqual('A')
expect(flags.difference('B', 'A')).toEqual('B')
expect(flags.difference('D', 'G')).toEqual('B')
expect(flags.difference('G', 'D')).toEqual('E')
expect(flags.difference('IB', 'R')).toEqual('IB')
})
test('Intersection of two base-64 strings', () => {
const flags = new Base64BitflagSet()
expect(flags.intersection('', '')).toEqual('A')
expect(flags.intersection('A', 'A')).toEqual('A')
expect(flags.intersection('B', 'A')).toEqual('A')
expect(flags.intersection('B', 'C')).toEqual('A')
expect(flags.intersection('B', 'D')).toEqual('B')
expect(flags.intersection('L', 'F')).toEqual('B')
expect(flags.intersection('L', 'H')).toEqual('D')
})
test('Iterate over a base-64 string', () => {
const flags = new Base64BitflagSet()
expect([...flags.iterate('A')]).toEqual([])
expect([...flags.iterate('B')]).toEqual([1])
expect([...flags.iterate('C')]).toEqual([2])
expect([...flags.iterate('D')]).toEqual([1, 2])
expect([...flags.iterate('L')]).toEqual([1, 2, 4])
expect([...flags.iterate('kB')]).toEqual([3, 6, 7])
})
test('Normalise to minimum', () => {
const flags = new Base64BitflagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2, flag1)
const flag3 = flags.flag(3, flag1)
const flag4 = flags.flag(4, flag3)
expect(flags.minimum('A')).toEqual('A')
expect(flags.minimum('B')).toEqual('B')
expect(flags.minimum('C')).toEqual('A')
expect(flags.minimum('D')).toEqual('D')
expect(flags.minimum('L')).toEqual('D')
expect(flags.minimum('N')).toEqual('N')
expect(flags.minimum('R')).toEqual('B')
})
test('Normalise to maximum', () => {
const flags = new Base64BitflagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2, flag1)
const flag3 = flags.flag(3, flag1)
const flag4 = flags.flag(4, flag3)
expect(flags.maximum('A')).toEqual('A')
expect(flags.maximum('B')).toEqual('B')
expect(flags.maximum('C')).toEqual('D')
expect(flags.maximum('D')).toEqual('D')
expect(flags.maximum('L')).toEqual('P')
expect(flags.maximum('N')).toEqual('N')
expect(flags.maximum('R')).toEqual('B')
})
test('Add to base64 string', () => {
const flags = new Base64BitflagSet()
const flag2 = flags.flag(2)
const flag3 = flags.flag(3)
const flags2And3 = flags.flag(flag2, flag3)
expect(flag2.addTo('B')).toEqual('D')
expect(flag3.addTo('B')).toEqual('F')
expect(flags2And3.addTo('B')).toEqual('H')
})
test('Remove from base64 string', () => {
const flags = new Base64BitflagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2)
const flag3 = flags.flag(3, flag1)
expect(flag1.removeFrom('H')).toEqual('C')
expect(flag2.removeFrom('H')).toEqual('F')
expect(flag3.removeFrom('H')).toEqual('D')
})
test('Is in base64 string', () => {
const flags = new Base64BitflagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2)
const flag3 = flags.flag(3, flag1)
expect(flag1.isIn('B')).toBe(true)
expect(flag2.isIn('D')).toBe(true)
expect(flag3.isIn('E')).toBe(false)
expect(flag3.isIn('F')).toBe(true)
})
test('Is abstract', () => {
const flags = new Base64BitflagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2)
const flags1And2 = flags.flag(flag1, flag2)
const flag3 = flags.flag(3, flags1And2)
expect(flag1.isAbstract).toBe(false)
expect(flag2.isAbstract).toBe(false)
expect(flags1And2.isAbstract).toBe(true)
expect(flag3.isAbstract).toBe(false)
})
test('encode a base-64 byte', () => {
expect(encodeByte(0)).toEqual('A')
expect(encodeByte(1)).toEqual('B')
expect(encodeByte(2)).toEqual('C')
expect(encodeByte(3)).toEqual('D')
expect(encodeByte(4)).toEqual('E')
expect(encodeByte(5)).toEqual('F')
expect(encodeByte(6)).toEqual('G')
expect(encodeByte(7)).toEqual('H')
expect(encodeByte(8)).toEqual('I')
expect(encodeByte(9)).toEqual('J')
expect(encodeByte(10)).toEqual('K')
expect(encodeByte(11)).toEqual('L')
expect(encodeByte(12)).toEqual('M')
expect(encodeByte(13)).toEqual('N')
expect(encodeByte(14)).toEqual('O')
expect(encodeByte(15)).toEqual('P')
expect(encodeByte(16)).toEqual('Q')
expect(encodeByte(17)).toEqual('R')
expect(encodeByte(18)).toEqual('S')
expect(encodeByte(19)).toEqual('T')
expect(encodeByte(20)).toEqual('U')
expect(encodeByte(21)).toEqual('V')
expect(encodeByte(22)).toEqual('W')
expect(encodeByte(23)).toEqual('X')
expect(encodeByte(24)).toEqual('Y')
expect(encodeByte(25)).toEqual('Z')
expect(encodeByte(26)).toEqual('a')
expect(encodeByte(27)).toEqual('b')
expect(encodeByte(28)).toEqual('c')
expect(encodeByte(29)).toEqual('d')
expect(encodeByte(30)).toEqual('e')
expect(encodeByte(31)).toEqual('f')
expect(encodeByte(32)).toEqual('g')
expect(encodeByte(33)).toEqual('h')
expect(encodeByte(34)).toEqual('i')
expect(encodeByte(35)).toEqual('j')
expect(encodeByte(36)).toEqual('k')
expect(encodeByte(37)).toEqual('l')
expect(encodeByte(38)).toEqual('m')
expect(encodeByte(39)).toEqual('n')
expect(encodeByte(40)).toEqual('o')
expect(encodeByte(41)).toEqual('p')
expect(encodeByte(42)).toEqual('q')
expect(encodeByte(43)).toEqual('r')
expect(encodeByte(44)).toEqual('s')
expect(encodeByte(45)).toEqual('t')
expect(encodeByte(46)).toEqual('u')
expect(encodeByte(47)).toEqual('v')
expect(encodeByte(48)).toEqual('w')
expect(encodeByte(49)).toEqual('x')
expect(encodeByte(50)).toEqual('y')
expect(encodeByte(51)).toEqual('z')
expect(encodeByte(52)).toEqual('0')
expect(encodeByte(53)).toEqual('1')
expect(encodeByte(54)).toEqual('2')
expect(encodeByte(55)).toEqual('3')
expect(encodeByte(56)).toEqual('4')
expect(encodeByte(57)).toEqual('5')
expect(encodeByte(58)).toEqual('6')
expect(encodeByte(59)).toEqual('7')
expect(encodeByte(60)).toEqual('8')
expect(encodeByte(61)).toEqual('9')
expect(encodeByte(62)).toEqual('-')
expect(encodeByte(63)).toEqual('_')
})
test('encode a base-64 byte', () => {
expect(decodeByte('A')).toEqual(0)
expect(decodeByte('B')).toEqual(1)
expect(decodeByte('C')).toEqual(2)
expect(decodeByte('D')).toEqual(3)
expect(decodeByte('E')).toEqual(4)
expect(decodeByte('F')).toEqual(5)
expect(decodeByte('G')).toEqual(6)
expect(decodeByte('H')).toEqual(7)
expect(decodeByte('I')).toEqual(8)
expect(decodeByte('J')).toEqual(9)
expect(decodeByte('K')).toEqual(10)
expect(decodeByte('L')).toEqual(11)
expect(decodeByte('M')).toEqual(12)
expect(decodeByte('N')).toEqual(13)
expect(decodeByte('O')).toEqual(14)
expect(decodeByte('P')).toEqual(15)
expect(decodeByte('Q')).toEqual(16)
expect(decodeByte('R')).toEqual(17)
expect(decodeByte('S')).toEqual(18)
expect(decodeByte('T')).toEqual(19)
expect(decodeByte('U')).toEqual(20)
expect(decodeByte('V')).toEqual(21)
expect(decodeByte('W')).toEqual(22)
expect(decodeByte('X')).toEqual(23)
expect(decodeByte('Y')).toEqual(24)
expect(decodeByte('Z')).toEqual(25)
expect(decodeByte('a')).toEqual(26)
expect(decodeByte('b')).toEqual(27)
expect(decodeByte('c')).toEqual(28)
expect(decodeByte('d')).toEqual(29)
expect(decodeByte('e')).toEqual(30)
expect(decodeByte('f')).toEqual(31)
expect(decodeByte('g')).toEqual(32)
expect(decodeByte('h')).toEqual(33)
expect(decodeByte('i')).toEqual(34)
expect(decodeByte('j')).toEqual(35)
expect(decodeByte('k')).toEqual(36)
expect(decodeByte('l')).toEqual(37)
expect(decodeByte('m')).toEqual(38)
expect(decodeByte('n')).toEqual(39)
expect(decodeByte('o')).toEqual(40)
expect(decodeByte('p')).toEqual(41)
expect(decodeByte('q')).toEqual(42)
expect(decodeByte('r')).toEqual(43)
expect(decodeByte('s')).toEqual(44)
expect(decodeByte('t')).toEqual(45)
expect(decodeByte('u')).toEqual(46)
expect(decodeByte('v')).toEqual(47)
expect(decodeByte('w')).toEqual(48)
expect(decodeByte('x')).toEqual(49)
expect(decodeByte('y')).toEqual(50)
expect(decodeByte('z')).toEqual(51)
expect(decodeByte('0')).toEqual(52)
expect(decodeByte('1')).toEqual(53)
expect(decodeByte('2')).toEqual(54)
expect(decodeByte('3')).toEqual(55)
expect(decodeByte('4')).toEqual(56)
expect(decodeByte('5')).toEqual(57)
expect(decodeByte('6')).toEqual(58)
expect(decodeByte('7')).toEqual(59)
expect(decodeByte('8')).toEqual(60)
expect(decodeByte('9')).toEqual(61)
expect(decodeByte('-')).toEqual(62)
expect(decodeByte('_')).toEqual(63)
})

View File

@@ -0,0 +1,116 @@
import { BigBitFlagSet } from '~'
const bigPowerOfTwo = 2n ** 100n
test('Iterate over a number', () => {
const flags = new BigBitFlagSet()
expect([...flags.iterate(0n)]).toEqual([])
expect([...flags.iterate(1n)]).toEqual([1n])
expect([...flags.iterate(2n)]).toEqual([2n])
expect([...flags.iterate(3n)]).toEqual([1n, 2n])
expect([...flags.iterate(11n)]).toEqual([1n, 2n, 8n])
expect([...flags.iterate(100n)]).toEqual([4n, 32n, 64n])
})
test('Normalise to minimum', () => {
const flags = new BigBitFlagSet()
const flag1 = flags.flag(1n)
const flag2 = flags.flag(2n, flag1)
const flag4 = flags.flag(4n, flag1)
const flag8 = flags.flag(8n, flag4)
expect(flags.minimum(0n)).toEqual(0n)
expect(flags.minimum(1n)).toEqual(1n)
expect(flags.minimum(2n)).toEqual(0n)
expect(flags.minimum(3n)).toEqual(3n)
expect(flags.minimum(11n)).toEqual(3n)
expect(flags.minimum(13n)).toEqual(13n)
expect(flags.minimum(17n)).toEqual(1n)
})
test('Normalise to maximum', () => {
const flags = new BigBitFlagSet()
const flag1 = flags.flag(1n)
const flag2 = flags.flag(2n, flag1)
const flag4 = flags.flag(4n, flag1)
const flag8 = flags.flag(8n, flag4)
expect(flags.maximum(0n)).toEqual(0n)
expect(flags.maximum(1n)).toEqual(1n)
expect(flags.maximum(2n)).toEqual(3n)
expect(flags.maximum(3n)).toEqual(3n)
expect(flags.maximum(11n)).toEqual(15n)
expect(flags.maximum(13n)).toEqual(13n)
expect(flags.maximum(17n)).toEqual(1n)
})
test('Add to bigint', () => {
const flags = new BigBitFlagSet()
const flag2 = flags.flag(2n)
const flag4 = flags.flag(4n)
const flags2And4 = flags.flag(flag2, flag4)
const flag100 = flags.flag(bigPowerOfTwo)
expect(flag2.addTo(1n)).toEqual(3n)
expect(flag4.addTo(1n)).toEqual(5n)
expect(flags2And4.addTo(1n)).toEqual(7n)
expect(flag100.addTo(1n)).toEqual(bigPowerOfTwo + 1n)
expect(flag2.addTo(bigPowerOfTwo)).toEqual(bigPowerOfTwo + 2n)
})
test('Remove from bigint', () => {
const flags = new BigBitFlagSet()
const flag1 = flags.flag(1n)
const flag2 = flags.flag(2n)
const flag4 = flags.flag(4n, flag1)
const flag100 = flags.flag(bigPowerOfTwo)
expect(flag1.removeFrom(7n)).toEqual(2n)
expect(flag2.removeFrom(7n)).toEqual(5n)
expect(flag4.removeFrom(7n)).toEqual(3n)
expect(flag100.removeFrom(bigPowerOfTwo + 2n)).toEqual(2n)
expect(flag100.removeFrom(2n)).toEqual(2n)
expect(flag1.removeFrom(bigPowerOfTwo - 1n)).toEqual(bigPowerOfTwo - 6n)
})
test('Is in bigint', () => {
const flags = new BigBitFlagSet()
const flag1 = flags.flag(1n)
const flag2 = flags.flag(2n)
const flag4 = flags.flag(4n, flag1)
expect(flag1.isIn(1n)).toBe(true)
expect(flag2.isIn(3n)).toBe(true)
expect(flag4.isIn(4n)).toBe(false)
expect(flag4.isIn(5n)).toBe(true)
expect(flag1.isIn(bigPowerOfTwo + 1n)).toBe(true)
})
test('Is abstract', () => {
const flags = new BigBitFlagSet()
const flag1 = flags.flag(1n)
const flag2 = flags.flag(2n)
const flags1And2 = flags.flag(flag1, flag2)
const flag4 = flags.flag(4n, flags1And2)
expect(flag1.isAbstract).toBe(false)
expect(flag2.isAbstract).toBe(false)
expect(flags1And2.isAbstract).toBe(true)
expect(flag4.isAbstract).toBe(false)
})
test('Environment without bigint', async () => {
const originalBigInt = globalThis.BigInt
// @ts-ignore
delete globalThis.BigInt
let module: { BigBitFlagSet: typeof BigBitFlagSet }
await jest.isolateModulesAsync(async () => {
module = await import('@module')
})
expect(() => new module.BigBitFlagSet()).toThrow()
globalThis.BigInt = originalBigInt
})

View File

@@ -0,0 +1,240 @@
import { CollectionFlagSet } from '@module'
import { ok } from 'node:assert'
function set<T>(...values: T[]): Set<T> {
return new Set<T>(values)
}
test('Union of two sets', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.union(set(), set())).toEqual(set())
expect(flags.union(set('A'), set())).toEqual(set('A'))
expect(flags.union(set(), set('B'))).toEqual(set('B'))
expect(flags.union(set('A'), set('B'))).toEqual(set('A', 'B'))
expect(flags.union(set('A', 'B'), set('B', 'C'))).toEqual(
set('A', 'B', 'C')
)
})
test('Difference of two sets', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.difference(set(), set())).toEqual(set())
expect(flags.difference(set('A'), set())).toEqual(set('A'))
expect(flags.difference(set('A', 'B'), set('B', 'C'))).toEqual(set('A'))
expect(flags.difference(set('B', 'C'), set('A', 'B'))).toEqual(set('C'))
expect(flags.difference(set('D'), set('A', 'E'))).toEqual(set('D'))
})
test('Intersection of two sets', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.intersection(set(), set())).toEqual(set())
expect(flags.intersection(set('A'), set())).toEqual(set())
expect(flags.intersection(set('A'), set('B'))).toEqual(set())
expect(flags.intersection(set('A'), set('A', 'B'))).toEqual(set('A'))
expect(flags.intersection(set('A', 'B', 'D'), set('A', 'C'))).toEqual(
set('A')
)
expect(flags.intersection(set('A', 'B', 'D'), set('A', 'B', 'C'))).toEqual(
set('A', 'B')
)
})
test('Iterate over a set', () => {
const flags = new CollectionFlagSet<string>()
expect([...flags.iterate(set())]).toEqual([])
expect([...flags.iterate(set('A'))]).toEqual(['A'])
expect([...flags.iterate(set('A', 'B', 'C'))]).toEqual(['A', 'B', 'C'])
})
test('Normalise to minimum', () => {
const flags = new CollectionFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B', flagA)
const flagC = flags.flag('C', flagA)
const flagD = flags.flag('D', flagC)
expect(flags.minimum(set())).toEqual(set())
expect(flags.minimum(set('A'))).toEqual(set('A'))
expect(flags.minimum(set('B'))).toEqual(set())
expect(flags.minimum(set('A', 'B'))).toEqual(set('A', 'B'))
expect(flags.minimum(set('A', 'B', 'D'))).toEqual(set('A', 'B'))
expect(flags.minimum(set('A', 'C', 'D'))).toEqual(set('A', 'C', 'D'))
expect(flags.minimum(set('A', 'E'))).toEqual(set('A'))
})
test('Normalise to maximum', () => {
const flags = new CollectionFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B', flagA)
const flagC = flags.flag('C', flagA)
const flagD = flags.flag('D', flagC)
expect(flags.maximum(set())).toEqual(set())
expect(flags.maximum(set('A'))).toEqual(set('A'))
expect(flags.maximum(set('B'))).toEqual(set('B', 'A'))
expect(flags.maximum(set('A', 'B'))).toEqual(set('A', 'B'))
expect(flags.maximum(set('A', 'B', 'D'))).toEqual(set('A', 'B', 'D', 'C'))
expect(flags.maximum(set('A', 'C', 'D'))).toEqual(set('A', 'C', 'D'))
expect(flags.maximum(set('A', 'E'))).toEqual(set('A'))
})
test('Add to set', () => {
const flags = new CollectionFlagSet<string>()
const flagB = flags.flag('B')
const flagC = flags.flag('C')
const flagsBAndC = flags.flag(flagB, flagC)
expect(flagB.addTo(set('A'))).toEqual(set('A', 'B'))
expect(flagC.addTo(set('A'))).toEqual(set('A', 'C'))
expect(flagsBAndC.addTo(set('A'))).toEqual(set('A', 'B', 'C'))
})
test('Remove from set', () => {
const flags = new CollectionFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B')
const flagC = flags.flag('C', flagA)
expect(flagA.removeFrom(set('A', 'B', 'C'))).toEqual(set('B'))
expect(flagB.removeFrom(set('A', 'B', 'C'))).toEqual(set('A', 'C'))
expect(flagC.removeFrom(set('A', 'B', 'C'))).toEqual(set('A', 'B'))
})
test('Is in set', () => {
const flags = new CollectionFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B')
const flagC = flags.flag('C', flagA)
expect(flagA.isIn(set('A'))).toBe(true)
expect(flagB.isIn(set('A', 'B'))).toBe(true)
expect(flagC.isIn(set('C'))).toBe(false)
expect(flagC.isIn(set('A', 'C'))).toBe(true)
})
test('Is abstract', () => {
const flags = new CollectionFlagSet<string>()
const flagA = flags.flag('A')
const flagB = flags.flag('B')
const flagsAAndB = flags.flag(flagA, flagB)
const flagC = flags.flag('C', flagsAAndB)
expect(flagA.isAbstract).toBe(false)
expect(flagB.isAbstract).toBe(false)
expect(flagsAAndB.isAbstract).toBe(true)
expect(flagC.isAbstract).toBe(false)
})
test('Environment without Set', async () => {
const originalSet = globalThis.Set
// @ts-ignore
delete globalThis.Set
let module: { CollectionFlagSet: typeof CollectionFlagSet }
await jest.isolateModulesAsync(async () => {
module = await import('@module')
})
expect(() => new module.CollectionFlagSet()).toThrow()
globalThis.Set = originalSet
})
test('Environment with Set.prototype.union', async () => {
ok(!('union' in globalThis.Set.prototype))
const mockUnion = jest.fn((_) => new Set())
// @ts-ignore
globalThis.Set.prototype.union = mockUnion
let module: { CollectionFlagSet: typeof CollectionFlagSet } | undefined
await jest.isolateModulesAsync(async () => {
module = await import('@module')
})
ok(module !== undefined)
const set1 = new Set()
const set2 = new Set()
const flags = new module.CollectionFlagSet()
flags.union(set1, set2)
expect(mockUnion).toHaveBeenCalledTimes(1)
expect(mockUnion.mock.contexts[0]).toBe(set1)
expect(mockUnion.mock.calls[0][0]).toBe(set2)
// @ts-ignore
delete globalThis.Set.prototype.union
})
test('Environment with Set.prototype.difference', async () => {
ok(!('difference' in globalThis.Set.prototype))
const mockDifference = jest.fn((_) => new Set())
// @ts-ignore
globalThis.Set.prototype.difference = mockDifference
let module: { CollectionFlagSet: typeof CollectionFlagSet } | undefined
await jest.isolateModulesAsync(async () => {
module = await import('@module')
})
ok(module !== undefined)
const set1 = new Set()
const set2 = new Set()
const flags = new module.CollectionFlagSet()
flags.difference(set1, set2)
expect(mockDifference).toHaveBeenCalledTimes(1)
expect(mockDifference.mock.contexts[0]).toBe(set1)
expect(mockDifference.mock.calls[0][0]).toBe(set2)
// @ts-ignore
delete globalThis.Set.prototype.difference
})
test('Environment with Set.prototype.intersection', async () => {
ok(!('intersection' in globalThis.Set.prototype))
const mockIntersection = jest.fn((_) => new Set())
// @ts-ignore
globalThis.Set.prototype.intersection = mockIntersection
let module: { CollectionFlagSet: typeof CollectionFlagSet } | undefined
await jest.isolateModulesAsync(async () => {
module = await import('@module')
})
ok(module !== undefined)
const set1 = new Set()
const set2 = new Set()
const flags = new module.CollectionFlagSet()
flags.intersection(set1, set2)
expect(mockIntersection).toHaveBeenCalledTimes(1)
expect(mockIntersection.mock.contexts[0]).toBe(set1)
expect(mockIntersection.mock.calls[0][0]).toBe(set2)
// @ts-ignore
delete globalThis.Set.prototype.intersection
})
test('Environment with Set.prototype.isSupersetOf', async () => {
ok(!('isSupersetOf' in globalThis.Set.prototype))
const mockIsSupersetOf = jest.fn((_) => new Set())
// @ts-ignore
globalThis.Set.prototype.isSupersetOf = mockIsSupersetOf
let module: { CollectionFlagSet: typeof CollectionFlagSet } | undefined
await jest.isolateModulesAsync(async () => {
module = await import('@module')
})
ok(module !== undefined)
const set1 = new Set()
const set2 = new Set()
const flags = new module.CollectionFlagSet()
flags.isSupersetOf(set1, set2)
expect(mockIsSupersetOf).toHaveBeenCalledTimes(1)
expect(mockIsSupersetOf.mock.contexts[0]).toBe(set1)
expect(mockIsSupersetOf.mock.calls[0][0]).toBe(set2)
// @ts-ignore
delete globalThis.Set.prototype.isSupersetOf
})

View File

@@ -0,0 +1,12 @@
import { NumberBitflagSet, ForeignFlagError } from '@module'
test('cannot create a flag with a foreign parent', () => {
const flags = new NumberBitflagSet()
const flagA = flags.flag(1)
const otherFlags = new NumberBitflagSet()
const flagX = otherFlags.flag(1)
flags.flag(2, flagA) // OK
expect(() => flags.flag(4, flagX)).toThrow(ForeignFlagError)
})

View File

@@ -0,0 +1,25 @@
import { NumberBitflagSet, ReusedFlagValueError } from '@module'
test('cannot create an abstract flag with less than two parents', () => {
const flags = new NumberBitflagSet()
const flag1 = flags.flag(1)
expect(() => flags.flag()).toThrow(TypeError)
expect(() => flags.flag(flag1)).toThrow(TypeError)
expect(() => flags.flag(flag1, flag1)).toThrow(TypeError)
})
test('calls to flag() with arguments in the wrong order throw a TypeError', () => {
const flags = new NumberBitflagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2)
// @ts-ignore
expect(() => flags.flag(flag1, 2, flag2)).toThrow(TypeError)
})
test('Use same value twice', () => {
const flags = new NumberBitflagSet()
const flag = flags.flag(1)
expect(() => flags.flag(1)).toThrow(ReusedFlagValueError)
})

View File

@@ -0,0 +1,39 @@
import { NumberBitflagSet, InvalidBitflagValueError } from '@module'
test('Not powers of two', () => {
const flags = new BitFlagSet()
expect(() => flags.flag(0)).toThrow(InvalidBitFlagValueError)
expect(() => flags.flag(11)).toThrow(InvalidBitFlagValueError)
})
test('Normalise to minimum', () => {
const flags = new BitFlagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2, flag1)
const flag4 = flags.flag(4, flag1)
const flag8 = flags.flag(8, flag4)
expect(flags.minimum(0)).toEqual(0)
expect(flags.minimum(1)).toEqual(1)
expect(flags.minimum(2)).toEqual(0)
expect(flags.minimum(3)).toEqual(3)
expect(flags.minimum(11)).toEqual(3)
expect(flags.minimum(13)).toEqual(13)
expect(flags.minimum(17)).toEqual(1)
})
test('Normalise to maximum', () => {
const flags = new BitFlagSet()
const flag1 = flags.flag(1)
const flag2 = flags.flag(2, flag1)
const flag4 = flags.flag(4, flag1)
const flag8 = flags.flag(8, flag4)
expect(flags.maximum(0)).toEqual(0)
expect(flags.maximum(1)).toEqual(1)
expect(flags.maximum(2)).toEqual(3)
expect(flags.maximum(3)).toEqual(3)
expect(flags.maximum(11)).toEqual(15)
expect(flags.maximum(13)).toEqual(13)
expect(flags.maximum(17)).toEqual(1)
})

View File

@@ -0,0 +1,62 @@
import { ArrayFlagSet } from '~'
describe(ArrayFlagSet, () => {
test('none', () => {
const flags = new ArrayFlagSet<string>()
expect(flags.none()).toEqual([])
})
test('union', () => {
const flags = new ArrayFlagSet<string>()
expect(flags.union([], [])).toEqual([])
expect(flags.union(['A'], [])).toEqual(['A'])
expect(flags.union([], ['B'])).toEqual(['B'])
expect(flags.union(['A'], ['B'])).toEqual(['A', 'B'])
expect(flags.union(['A', 'B'], ['B', 'C'])).toEqual(['A', 'B', 'C'])
})
test('difference', () => {
const flags = new ArrayFlagSet<string>()
expect(flags.difference([], [])).toEqual([])
expect(flags.difference(['A'], [])).toEqual(['A'])
expect(flags.difference(['A', 'B'], ['B', 'C'])).toEqual(['A'])
expect(flags.difference(['B', 'C'], ['A', 'B'])).toEqual(['C'])
expect(flags.difference(['D'], ['A', 'E'])).toEqual(['D'])
})
test('intersection', () => {
const flags = new ArrayFlagSet<string>()
expect(flags.intersection([], [])).toEqual([])
expect(flags.intersection(['A'], [])).toEqual([])
expect(flags.intersection(['A'], ['B'])).toEqual([])
expect(flags.intersection(['A'], ['A', 'B'])).toEqual(['A'])
expect(flags.intersection(['A', 'B', 'D'], ['A', 'C'])).toEqual(['A'])
expect(flags.intersection(['A', 'B', 'D'], ['A', 'B', 'C'])).toEqual([
'A',
'B',
])
})
test('isSuperset', () => {
const flags = new ArrayFlagSet<string>()
expect(flags.isSuperset([], [])).toBe(true)
expect(flags.isSuperset(['A', 'B'], [])).toBe(true)
expect(flags.isSuperset(['A', 'B'], ['A'])).toBe(true)
expect(flags.isSuperset(['A', 'B'], ['A', 'B'])).toBe(true)
expect(flags.isSuperset([], ['A', 'B'])).toBe(false)
expect(flags.isSuperset(['C', 'D'], ['B'])).toBe(false)
})
test('enumerate', () => {
const flags = new ArrayFlagSet<string>()
expect([...flags.enumerate([])]).toEqual([])
expect([...flags.enumerate(['A'])]).toEqual(['A'])
expect([...flags.enumerate(['A', 'B', 'C'])]).toEqual(['A', 'B', 'C'])
})
})

View File

@@ -0,0 +1,66 @@
import { Base64BitFlagSet } from '~'
describe(Base64BitFlagSet, () => {
test('none', () => {
const flags = new Base64BitFlagSet()
expect(flags.none()).toEqual('')
})
test('union', () => {
const flags = new Base64BitFlagSet()
expect(flags.union('', '')).toEqual('')
expect(flags.union('A', 'A')).toEqual('')
expect(flags.union('B', 'A')).toEqual('B')
expect(flags.union('A', 'C')).toEqual('C')
expect(flags.union('B', 'C')).toEqual('D')
expect(flags.union('D', 'G')).toEqual('H')
})
test('difference', () => {
const flags = new Base64BitFlagSet()
expect(flags.difference('', '')).toEqual('')
expect(flags.difference('A', 'A')).toEqual('')
expect(flags.difference('B', 'A')).toEqual('B')
expect(flags.difference('D', 'G')).toEqual('B')
expect(flags.difference('G', 'D')).toEqual('E')
expect(flags.difference('IB', 'R')).toEqual('IB')
})
test('intersection', () => {
const flags = new Base64BitFlagSet()
expect(flags.intersection('', '')).toEqual('')
expect(flags.intersection('A', 'A')).toEqual('')
expect(flags.intersection('B', 'A')).toEqual('')
expect(flags.intersection('B', 'C')).toEqual('')
expect(flags.intersection('B', 'D')).toEqual('B')
expect(flags.intersection('L', 'F')).toEqual('B')
expect(flags.intersection('L', 'H')).toEqual('D')
})
test('isSuperset', () => {
const flags = new Base64BitFlagSet()
expect(flags.isSuperset('A', 'A')).toBe(true)
expect(flags.isSuperset('D', 'A')).toBe(true)
expect(flags.isSuperset('D', 'B')).toBe(true)
expect(flags.isSuperset('D', 'D')).toBe(true)
expect(flags.isSuperset('A', 'D')).toBe(false)
expect(flags.isSuperset('I', 'E')).toBe(false)
})
test('enumerate', () => {
const flags = new Base64BitFlagSet()
expect([...flags.enumerate('A')]).toEqual([])
expect([...flags.enumerate('B')]).toEqual([1])
expect([...flags.enumerate('C')]).toEqual([2])
expect([...flags.enumerate('D')]).toEqual([1, 2])
expect([...flags.enumerate('L')]).toEqual([1, 2, 4])
expect([...flags.enumerate('kB')]).toEqual([3, 6, 7])
expect([...flags.enumerate('AAB')]).toEqual([13])
})
})

View File

@@ -0,0 +1,62 @@
import { BigBitFlagSet } from '~'
describe(BigBitFlagSet, () => {
test('none', () => {
const flags = new BigBitFlagSet()
expect(flags.none()).toEqual(0n)
})
test('union', () => {
const flags = new BigBitFlagSet()
expect(flags.union(0n, 0n)).toEqual(0n)
expect(flags.union(1n, 0n)).toEqual(1n)
expect(flags.union(0n, 2n)).toEqual(2n)
expect(flags.union(1n, 2n)).toEqual(3n)
expect(flags.union(3n, 6n)).toEqual(7n)
})
test('difference', () => {
const flags = new BigBitFlagSet()
expect(flags.difference(0n, 0n)).toEqual(0n)
expect(flags.difference(1n, 0n)).toEqual(1n)
expect(flags.difference(3n, 6n)).toEqual(1n)
expect(flags.difference(6n, 3n)).toEqual(4n)
expect(flags.difference(8n, 17n)).toEqual(8n)
})
test('intersection', () => {
const flags = new BigBitFlagSet()
expect(flags.intersection(0n, 0n)).toEqual(0n)
expect(flags.intersection(1n, 0n)).toEqual(0n)
expect(flags.intersection(1n, 2n)).toEqual(0n)
expect(flags.intersection(1n, 3n)).toEqual(1n)
expect(flags.intersection(11n, 5n)).toEqual(1n)
expect(flags.intersection(11n, 7n)).toEqual(3n)
})
test('isSuperset', () => {
const flags = new BigBitFlagSet()
expect(flags.isSuperset(0n, 0n)).toBe(true)
expect(flags.isSuperset(3n, 0n)).toBe(true)
expect(flags.isSuperset(3n, 1n)).toBe(true)
expect(flags.isSuperset(3n, 3n)).toBe(true)
expect(flags.isSuperset(0n, 3n)).toBe(false)
expect(flags.isSuperset(8n, 4n)).toBe(false)
})
test('enumerate', () => {
const flags = new BigBitFlagSet()
expect([...flags.enumerate(0n)]).toEqual([])
expect([...flags.enumerate(1n)]).toEqual([1n])
expect([...flags.enumerate(2n)]).toEqual([2n])
expect([...flags.enumerate(3n)]).toEqual([1n, 2n])
expect([...flags.enumerate(11n)]).toEqual([1n, 2n, 8n])
expect([...flags.enumerate(100n)]).toEqual([4n, 32n, 64n])
})
})

View File

@@ -0,0 +1,73 @@
import { CollectionFlagSet } from '~'
function set<T>(...values: T[]): Set<T> {
return new Set<T>(values)
}
describe(CollectionFlagSet, () => {
test('none', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.none()).toEqual(set())
})
test('union', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.union(set(), set())).toEqual(set())
expect(flags.union(set('A'), set())).toEqual(set('A'))
expect(flags.union(set(), set('B'))).toEqual(set('B'))
expect(flags.union(set('A'), set('B'))).toEqual(set('A', 'B'))
expect(flags.union(set('A', 'B'), set('B', 'C'))).toEqual(
set('A', 'B', 'C')
)
})
test('difference', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.difference(set(), set())).toEqual(set())
expect(flags.difference(set('A'), set())).toEqual(set('A'))
expect(flags.difference(set('A', 'B'), set('B', 'C'))).toEqual(set('A'))
expect(flags.difference(set('B', 'C'), set('A', 'B'))).toEqual(set('C'))
expect(flags.difference(set('D'), set('A', 'E'))).toEqual(set('D'))
})
test('intersection', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.intersection(set(), set())).toEqual(set())
expect(flags.intersection(set('A'), set())).toEqual(set())
expect(flags.intersection(set('A'), set('B'))).toEqual(set())
expect(flags.intersection(set('A'), set('A', 'B'))).toEqual(set('A'))
expect(flags.intersection(set('A', 'B', 'D'), set('A', 'C'))).toEqual(
set('A')
)
expect(
flags.intersection(set('A', 'B', 'D'), set('A', 'B', 'C'))
).toEqual(set('A', 'B'))
})
test('isSuperset', () => {
const flags = new CollectionFlagSet<string>()
expect(flags.isSuperset(set(), set())).toBe(true)
expect(flags.isSuperset(set('A', 'B'), set())).toBe(true)
expect(flags.isSuperset(set('A', 'B'), set('A'))).toBe(true)
expect(flags.isSuperset(set('A', 'B'), set('A', 'B'))).toBe(true)
expect(flags.isSuperset(set(), set('A', 'B'))).toBe(false)
expect(flags.isSuperset(set('C', 'D'), set('B'))).toBe(false)
})
test('enumerate', () => {
const flags = new CollectionFlagSet<string>()
expect([...flags.enumerate(set())]).toEqual([])
expect([...flags.enumerate(set('A'))]).toEqual(['A'])
expect([...flags.enumerate(set('A', 'B', 'C'))]).toEqual([
'A',
'B',
'C',
])
})
})

View File

@@ -0,0 +1,62 @@
import { BitFlagSet } from '~'
describe(BitFlagSet, () => {
test('none', () => {
const flags = new BitFlagSet()
expect(flags.none()).toEqual(0)
})
test('union', () => {
const flags = new BitFlagSet()
expect(flags.union(0, 0)).toEqual(0)
expect(flags.union(1, 0)).toEqual(1)
expect(flags.union(0, 2)).toEqual(2)
expect(flags.union(1, 2)).toEqual(3)
expect(flags.union(3, 6)).toEqual(7)
})
test('difference', () => {
const flags = new BitFlagSet()
expect(flags.difference(0, 0)).toEqual(0)
expect(flags.difference(1, 0)).toEqual(1)
expect(flags.difference(3, 6)).toEqual(1)
expect(flags.difference(6, 3)).toEqual(4)
expect(flags.difference(8, 17)).toEqual(8)
})
test('intersection', () => {
const flags = new BitFlagSet()
expect(flags.intersection(0, 0)).toEqual(0)
expect(flags.intersection(1, 0)).toEqual(0)
expect(flags.intersection(1, 2)).toEqual(0)
expect(flags.intersection(1, 3)).toEqual(1)
expect(flags.intersection(11, 5)).toEqual(1)
expect(flags.intersection(11, 7)).toEqual(3)
})
test('isSuperset', () => {
const flags = new BitFlagSet()
expect(flags.isSuperset(0, 0)).toBe(true)
expect(flags.isSuperset(3, 0)).toBe(true)
expect(flags.isSuperset(3, 1)).toBe(true)
expect(flags.isSuperset(3, 3)).toBe(true)
expect(flags.isSuperset(0, 3)).toBe(false)
expect(flags.isSuperset(8, 4)).toBe(false)
})
test('enumerate', () => {
const flags = new BitFlagSet()
expect([...flags.enumerate(0)]).toEqual([])
expect([...flags.enumerate(1)]).toEqual([1])
expect([...flags.enumerate(2)]).toEqual([2])
expect([...flags.enumerate(3)]).toEqual([1, 2])
expect([...flags.enumerate(11)]).toEqual([1, 2, 8])
expect([...flags.enumerate(100)]).toEqual([4, 32, 64])
})
})