implemented of() and named() for CollectionFlagSets

This commit is contained in:
2026-03-05 13:15:19 +01:00
parent efea7ad407
commit 0a06d238de
13 changed files with 405 additions and 90 deletions

View File

@@ -2,13 +2,23 @@ import js from '@eslint/js'
import globals from 'globals' import globals from 'globals'
import tseslint from 'typescript-eslint' import tseslint from 'typescript-eslint'
import { defineConfig } from 'eslint/config' import { defineConfig } from 'eslint/config'
import simpleImportSort from 'eslint-plugin-simple-import-sort'
export default defineConfig([ export default defineConfig([
{ {
files: ['**/*.ts'], files: ['**/*.ts'],
plugins: { js }, plugins: { js, 'simple-import-sort': simpleImportSort },
extends: ['js/recommended'], extends: ['js/recommended'],
languageOptions: { globals: { ...globals.browser, ...globals.node } }, languageOptions: {
globals: { ...globals.browser, ...globals.node },
parserOptions: {
projectService: true,
}, },
tseslint.configs.recommended, },
rules: {
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
},
},
tseslint.configs.recommendedTypeChecked,
]) ])

View File

@@ -0,0 +1,79 @@
import { CollectionFlagSet } from '~/flagsets'
import {
applyDeclarations,
FlagWithValue,
FlagWithValueOrOrdinal,
ListOfFlagsWithValue,
ListOfFlagsWithValueOrOrdinal,
NamedFlagWithValue,
NamedFlagWithValueOrOrdinal,
} from './declarative'
import { FlagDefinition } from '~/definitions'
import {
DefineWithValueOrOrdinal,
RequireParentsOrDefineWithValueOrOrdinal,
WithValueOrOrdinal,
WithValueOrOrdinalOrCompose,
} from './syntax'
import { FlagSetBuilder } from './generic'
import { CollectionFlagDefinitionFactory } from '~/definitions/collection'
export class CollectionFlagSetBuilder<T>
implements
WithValueOrOrdinalOrCompose<T, Set<T>, CollectionFlagSet<T>>,
RequireParentsOrDefineWithValueOrOrdinal<T, Set<T>, CollectionFlagSet<T>>
{
private readonly _underlying: FlagSetBuilder<T>
public constructor() {
this._underlying = new FlagSetBuilder()
}
public define(): WithValueOrOrdinal<T, Set<T>, CollectionFlagSet<T>>
public define(alias: string): WithValueOrOrdinalOrCompose<T, Set<T>, CollectionFlagSet<T>>
public define(alias?: string): WithValueOrOrdinalOrCompose<T, Set<T>, CollectionFlagSet<T>> {
this._underlying.define(alias)
return this
}
public compose(...flags: string[]): DefineWithValueOrOrdinal<T, Set<T>, CollectionFlagSet<T>> {
this._underlying.compose(flags)
return this
}
public withValue(
value: T,
): RequireParentsOrDefineWithValueOrOrdinal<T, Set<T>, CollectionFlagSet<T>> {
this._underlying.withValue(value)
return this
}
public withOrdinal(
ordinal: number,
): RequireParentsOrDefineWithValueOrOrdinal<T, Set<T>, CollectionFlagSet<T>> {
throw new Error('Method not implemented.')
}
public requires(...flags: string[]): DefineWithValueOrOrdinal<T, Set<T>, CollectionFlagSet<T>> {
this._underlying.requires(flags)
return this
}
public getResult(): CollectionFlagSet<T> {
const graph = this._underlying.finish()
const factory = new CollectionFlagDefinitionFactory<T>()
return new CollectionFlagSet(graph.intoDictionary(factory))
}
}
export function createCollectionFlagSet<T>(declarations: FlagWithValue<T>[]): CollectionFlagSet<T>
export function createCollectionFlagSet<T, D extends string>(
declarations: Record<D, NamedFlagWithValue<T>>,
): CollectionFlagSet<T> & Record<D, FlagDefinition<Set<T>>>
export function createCollectionFlagSet<T>(
declarations: ListOfFlagsWithValue<T>,
): CollectionFlagSet<T> {
const builder = new CollectionFlagSetBuilder<T>()
applyDeclarations(declarations, builder)
return builder.getResult()
}

View File

@@ -1 +1,2 @@
export { BitFlagSetBuilder, createBitFlagSet } from './number' export { BitFlagSetBuilder, createBitFlagSet } from './number'
export { createCollectionFlagSet, CollectionFlagSetBuilder } from './collection'

View File

@@ -57,7 +57,6 @@ export class BitFlagSetBuilder
public getResult(): BitFlagSet { public getResult(): BitFlagSet {
const graph = this._underlying.finish() const graph = this._underlying.finish()
const definitions = graph.sortedDefinitions()
const factory = new BitFlagDefinitionFactory() const factory = new BitFlagDefinitionFactory()
return new BitFlagSet(graph.intoDictionary(factory)) return new BitFlagSet(graph.intoDictionary(factory))
} }
@@ -66,7 +65,7 @@ export class BitFlagSetBuilder
export function createBitFlagSet(declarations: FlagWithValueOrOrdinal<number>[]): BitFlagSet export function createBitFlagSet(declarations: FlagWithValueOrOrdinal<number>[]): BitFlagSet
export function createBitFlagSet<D extends string>( export function createBitFlagSet<D extends string>(
declarations: Record<D, NamedFlagWithValueOrOrdinal<number>>, declarations: Record<D, NamedFlagWithValueOrOrdinal<number>>,
): BitFlagSet & Record<D, FlagDefinition<number, number>> ): BitFlagSet & Record<D, FlagDefinition<number>>
export function createBitFlagSet(declarations: ListOfFlagsWithValueOrOrdinal<number>): BitFlagSet { export function createBitFlagSet(declarations: ListOfFlagsWithValueOrOrdinal<number>): BitFlagSet {
const builder = new BitFlagSetBuilder() const builder = new BitFlagSetBuilder()
applyDeclarations(declarations, builder) applyDeclarations(declarations, builder)

View File

@@ -0,0 +1,104 @@
import { FlagDefinition, FlagDefinitionFactory, PartialFlagDefinition } from '.'
import { ENV_SET } from '~/env'
export class CollectionFlagDefinition<T> implements FlagDefinition<Set<T>> {
private readonly _value: T | undefined
private readonly _alias: string | undefined
private readonly _parents: CollectionFlagDefinition<T>[]
private readonly _children: CollectionFlagDefinition<T>[]
public constructor(
value: T | undefined,
alias: string | undefined,
parents: CollectionFlagDefinition<T>[],
) {
this._value = value
this._alias = alias
this._parents = parents
this._children = []
for (const parent of this._parents) {
parent._children.push(this)
}
}
public get alias(): string | undefined {
return this._alias
}
public get values(): Set<T> {
let values = new Set<T>()
if (this._value === undefined) {
for (const parent of this._parents) {
values = ENV_SET.union.call(values, parent.values) as Set<T>
}
} else {
values.add(this._value)
}
return values
}
public isIn(set: Set<T>): boolean {
let result = this._value === undefined || set.has(this._value)
for (const parent of this._parents) {
result &&= parent.isIn(set)
}
return result
}
public addTo(set: Set<T>): Set<T> {
const result = new Set(set)
if (this._value !== undefined) {
result.add(this._value)
}
for (const parent of this._parents) {
parent.addToMutable(result)
}
return result
}
private addToMutable(set: Set<T>): void {
if (this._value !== undefined) {
set.add(this._value)
}
for (const parent of this._parents) {
parent.addToMutable(set)
}
}
public removeFrom(set: Set<T>): Set<T> {
const result = new Set(set)
if (this._value !== undefined) {
result.delete(this._value)
}
for (const parent of this._parents) {
parent.removeFromMutable(result)
}
return result
}
public removeFromMutable(set: Set<T>): void {
if (this._value !== undefined) {
set.delete(this._value)
}
for (const child of this._children) {
child.removeFromMutable(set)
}
}
}
export class CollectionFlagDefinitionFactory<T> implements FlagDefinitionFactory<T, Set<T>> {
public makeDefinitions(
sortedPartialDefinitions: PartialFlagDefinition<T>[],
results: Map<PartialFlagDefinition<T>, FlagDefinition<Set<T>>>,
): void {
for (const pfd of sortedPartialDefinitions) {
const parents: CollectionFlagDefinition<T>[] = []
for (const parentPfd of pfd.parents) {
parents.push(results.get(parentPfd) as CollectionFlagDefinition<T>)
}
results.set(pfd, new CollectionFlagDefinition(pfd.value, pfd.alias, parents))
}
}
}

View File

@@ -1,10 +1,10 @@
export interface FlagsDictionary<F, S> { export interface FlagsDictionary<F, S> {
findByAlias(alias: string): FlagDefinition<F, S> | undefined findByAlias(alias: string): FlagDefinition<S> | undefined
findByValue(value: F): FlagDefinition<F, S> | undefined findByValue(value: F): FlagDefinition<S> | undefined
} }
export interface FlagDefinition<F, S> { export interface FlagDefinition<S> {
/** /**
* The alias of the flag. * The alias of the flag.
*/ */
@@ -39,9 +39,9 @@ export interface FlagDefinition<F, S> {
export const valueToString = Symbol() export const valueToString = Symbol()
export function printFlagValue(flag: FlagDefinition<unknown, unknown>): string { export function printFlagValue(flag: FlagDefinition<unknown>): string {
if (valueToString in flag) { if (valueToString in flag) {
return (flag[valueToString] as Function)() return (flag[valueToString] as () => string)()
} else { } else {
return String(flag.values) return String(flag.values)
} }

View File

@@ -6,7 +6,7 @@ interface PrecomputedValues {
subtractive: number subtractive: number
} }
export class BitFlagDefinition implements FlagDefinition<number, number> { export class BitFlagDefinition implements FlagDefinition<number> {
private readonly _baseValue: number private readonly _baseValue: number
private readonly _additiveValue: number private readonly _additiveValue: number
private readonly _subtractiveValue: number private readonly _subtractiveValue: number
@@ -43,7 +43,7 @@ export class BitFlagDefinition implements FlagDefinition<number, number> {
export class BitFlagDefinitionFactory implements FlagDefinitionFactory<number, number> { export class BitFlagDefinitionFactory implements FlagDefinitionFactory<number, number> {
public makeDefinitions( public makeDefinitions(
sortedPartialDefinitions: PartialFlagDefinition<number>[], sortedPartialDefinitions: PartialFlagDefinition<number>[],
results: Map<PartialFlagDefinition<number>, FlagDefinition<number, number>>, results: Map<PartialFlagDefinition<number>, FlagDefinition<number>>,
): void { ): void {
const precomputedValues = new Map<PartialFlagDefinition<number>, PrecomputedValues>() const precomputedValues = new Map<PartialFlagDefinition<number>, PrecomputedValues>()

View File

@@ -11,18 +11,12 @@ export const ENV_BI = Object.freeze(
type SetBinaryOperation = <T>(this: Set<T>, other: Set<T>) => Set<T> type SetBinaryOperation = <T>(this: Set<T>, other: Set<T>) => Set<T>
type SetBinaryPredicate = <T>(this: Set<T>, other: Set<T>) => boolean type SetBinaryPredicate = <T>(this: Set<T>, other: Set<T>) => boolean
export const ENV_SET = Object.freeze( export const ENV_SET = Object.freeze({
typeof Set === 'function'
? {
AVAILABLE: true,
union: polyfillUnion(Set.prototype), union: polyfillUnion(Set.prototype),
intersection: polyfillIntersection(Set.prototype), intersection: polyfillIntersection(Set.prototype),
difference: polyfillDifference(Set.prototype), difference: polyfillDifference(Set.prototype),
isSupersetOf: polyfillIsSupersetOf(Set.prototype), isSupersetOf: polyfillIsSupersetOf(Set.prototype),
} }) as {
: { AVAILABLE: false },
) as {
readonly AVAILABLE: boolean
readonly union: SetBinaryOperation readonly union: SetBinaryOperation
readonly intersection: SetBinaryOperation readonly intersection: SetBinaryOperation
readonly difference: SetBinaryOperation readonly difference: SetBinaryOperation

View File

@@ -1,23 +1,35 @@
import { FlagDefinition, FlagsDictionary } from '~/definitions'
import { EnumerateFlags } from '~/enumeration'
import { ENV_SET } from '~/env'
import type { FlagSet } from '.' import type { FlagSet } from '.'
import { EnumerateFlags, useIterator } from '../enumeration'
import { ENV_SET } from '../env'
import { UnavailableFeatureError } from '../errors'
export class CollectionFlagSet<T> implements FlagSet<T, Set<T>> { export class CollectionFlagSet<T> implements FlagSet<T, Set<T>> {
/** private readonly _dictionary: FlagsDictionary<T, Set<T>>
* Creates a new empty flag set.
* public constructor(dictionary: FlagsDictionary<T, Set<T>>) {
* @throws UnavailableFeatureError When this constructor is called in an this._dictionary = dictionary
* environment that does not natively support {@link Set}s.
*/
public constructor() {
if (!ENV_SET.AVAILABLE) {
throw new UnavailableFeatureError('Sets')
}
} }
public none(): Set<T> { public none(): Set<T> {
return new Set() return new Set<T>()
}
public of(...values: T[]): Set<T> {
return new Set(values)
}
public named(...aliases: string[]): Set<T> {
const result = new Set<T>()
for (const alias of aliases) {
const definition = this.getFlag(alias)
if (definition !== undefined) {
for (const value of definition.values) {
result.add(value)
}
}
}
return result
} }
public union(first: Set<T>, second: Set<T>): Set<T> { public union(first: Set<T>, second: Set<T>): Set<T> {
@@ -36,18 +48,57 @@ export class CollectionFlagSet<T> implements FlagSet<T, Set<T>> {
return ENV_SET.isSupersetOf.call(first, second) return ENV_SET.isSupersetOf.call(first, second)
} }
public hasAny(flags: Set<T>, required: Set<T>): boolean {
let result = false
for (const value of required) {
const definition = this._dictionary.findByValue(value)
if (definition !== undefined && definition.isIn(flags)) {
result = true
break
}
}
return result
}
public hasAll(flags: Set<T>, required: Set<T>): boolean {
let result = true
for (const value of required) {
const definition = this._dictionary.findByValue(value)
if (definition !== undefined && !definition.isIn(flags)) {
result = false
break
}
}
return result
}
public enumerate(flags: Set<T>): EnumerateFlags<T> { public enumerate(flags: Set<T>): EnumerateFlags<T> {
return flags return flags
} }
minimum(flags: Set<T>): Set<T> { public maximum(flags: Set<T>): Set<T> {
throw new Error('Method not implemented.') let result = new Set<T>()
for (const value of flags) {
const definition = this._dictionary.findByValue(value)
if (definition !== undefined) {
result = definition.addTo(result)
} }
maximum(flags: Set<T>): Set<T> { }
throw new Error('Method not implemented.') return result
} }
public getFlag(alias: string): FlagDefinition<number, number> | undefined { public minimum(flags: Set<T>): Set<T> {
return this._dictionary.lookUp(alias) let result = new Set<T>()
for (const value of flags) {
const definition = this._dictionary.findByValue(value)
if (definition !== undefined && definition.isIn(flags)) {
result = definition.addTo(result)
}
}
return result
}
public getFlag(alias: string): FlagDefinition<Set<T>> | undefined {
return this._dictionary.findByAlias(alias)
} }
} }

View File

@@ -91,28 +91,28 @@ export class BitFlagSet implements FlagSet<number, number> {
} }
public maximum(flags: number): number { public maximum(flags: number): number {
let result = this.none() let result = 0
for (const value of this.enumerate(flags)) { for (const value of this.enumerate(flags)) {
const flag = this._dictionary.findByValue(value) const definition = this._dictionary.findByValue(value)
if (flag !== undefined) { if (definition !== undefined) {
result = flag.addTo(result) result = definition.addTo(result)
} }
} }
return result return result
} }
public minimum(flags: number): number { public minimum(flags: number): number {
let result = this.none() let result = 0
for (const value of this.enumerate(flags)) { for (const value of this.enumerate(flags)) {
const flag = this._dictionary.findByValue(value) const definition = this._dictionary.findByValue(value)
if (flag !== undefined && flag.isIn(flags)) { if (definition !== undefined && definition.isIn(flags)) {
result = flag.addTo(result) result = definition.addTo(result)
} }
} }
return result return result
} }
public getFlag(alias: string): FlagDefinition<number, number> | undefined { public getFlag(alias: string): FlagDefinition<number> | undefined {
return this._dictionary.findByAlias(alias) return this._dictionary.findByAlias(alias)
} }
} }

View File

@@ -1,4 +1,4 @@
export { createBitFlagSet } from './builders' export { createBitFlagSet, createCollectionFlagSet } from './builders'
export { FlagDefinition } from './definitions' export { FlagDefinition } from './definitions'
export { InvalidBitFlagValueError } from './errors' export { InvalidBitFlagValueError } from './errors'
export { export {

View File

@@ -1,19 +1,41 @@
import { CollectionFlagSet } from '~'
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { CollectionFlagSet, createCollectionFlagSet } from '~'
function set<T>(...values: T[]): Set<T> { function set<T>(...values: T[]): Set<T> {
return new Set<T>(values) return new Set<T>(values)
} }
describe(CollectionFlagSet, () => { describe(CollectionFlagSet, () => {
test('none', () => { test('none', () => {
const flags = new CollectionFlagSet<string>() const flags = createCollectionFlagSet<string>([])
expect(flags.none()).toEqual(set()) expect(flags.none()).toEqual(set())
}) })
test('of', () => {
const flags = createCollectionFlagSet<string>([])
expect(flags.of()).toEqual(set())
expect(flags.of('a')).toEqual(set('a'))
expect(flags.of('x', 'y', 'z')).toEqual(set('x', 'y', 'z'))
})
test('named', () => {
const flags = createCollectionFlagSet([
{ value: 12, as: 'a' },
{ value: 45, as: 'b' },
{ value: 78, as: 'c' },
{ compose: ['a', 'b'], as: 'ab' },
])
expect(flags.named()).toEqual(set())
expect(flags.named('a')).toEqual(set(12))
expect(flags.named('ab', 'c')).toEqual(set(12, 45, 78))
})
test('union', () => { test('union', () => {
const flags = new CollectionFlagSet<string>() const flags = createCollectionFlagSet<string>([])
expect(flags.union(set(), set())).toEqual(set()) expect(flags.union(set(), set())).toEqual(set())
expect(flags.union(set('A'), set())).toEqual(set('A')) expect(flags.union(set('A'), set())).toEqual(set('A'))
@@ -23,7 +45,7 @@ describe(CollectionFlagSet, () => {
}) })
test('difference', () => { test('difference', () => {
const flags = new CollectionFlagSet<string>() const flags = createCollectionFlagSet<string>([])
expect(flags.difference(set(), set())).toEqual(set()) expect(flags.difference(set(), set())).toEqual(set())
expect(flags.difference(set('A'), set())).toEqual(set('A')) expect(flags.difference(set('A'), set())).toEqual(set('A'))
@@ -33,7 +55,7 @@ describe(CollectionFlagSet, () => {
}) })
test('intersection', () => { test('intersection', () => {
const flags = new CollectionFlagSet<string>() const flags = createCollectionFlagSet<string>([])
expect(flags.intersection(set(), set())).toEqual(set()) expect(flags.intersection(set(), set())).toEqual(set())
expect(flags.intersection(set('A'), set())).toEqual(set()) expect(flags.intersection(set('A'), set())).toEqual(set())
@@ -44,7 +66,7 @@ describe(CollectionFlagSet, () => {
}) })
test('isSuperset', () => { test('isSuperset', () => {
const flags = new CollectionFlagSet<string>() const flags = createCollectionFlagSet<string>([])
expect(flags.isSuperset(set(), set())).toBe(true) expect(flags.isSuperset(set(), set())).toBe(true)
expect(flags.isSuperset(set('A', 'B'), set())).toBe(true) expect(flags.isSuperset(set('A', 'B'), set())).toBe(true)
@@ -55,7 +77,7 @@ describe(CollectionFlagSet, () => {
}) })
test('enumerate', () => { test('enumerate', () => {
const flags = new CollectionFlagSet<string>() const flags = createCollectionFlagSet<string>([])
expect([...flags.enumerate(set())]).toEqual([]) expect([...flags.enumerate(set())]).toEqual([])
expect([...flags.enumerate(set('A'))]).toEqual(['A']) expect([...flags.enumerate(set('A'))]).toEqual(['A'])

View File

@@ -34,37 +34,45 @@ describe(BitFlagSet, () => {
}) })
test('union', () => { test('union', () => {
expect(BitFlags.union(0, 0)).toEqual(0) const flags = createBitFlagSet([])
expect(BitFlags.union(1, 0)).toEqual(1)
expect(BitFlags.union(0, 2)).toEqual(2) expect(flags.union(0, 0)).toEqual(0)
expect(BitFlags.union(1, 2)).toEqual(3) expect(flags.union(1, 0)).toEqual(1)
expect(BitFlags.union(3, 6)).toEqual(7) expect(flags.union(0, 2)).toEqual(2)
expect(flags.union(1, 2)).toEqual(3)
expect(flags.union(3, 6)).toEqual(7)
}) })
test('difference', () => { test('difference', () => {
expect(BitFlags.difference(0, 0)).toEqual(0) const flags = createBitFlagSet([])
expect(BitFlags.difference(1, 0)).toEqual(1)
expect(BitFlags.difference(3, 6)).toEqual(1) expect(flags.difference(0, 0)).toEqual(0)
expect(BitFlags.difference(6, 3)).toEqual(4) expect(flags.difference(1, 0)).toEqual(1)
expect(BitFlags.difference(8, 17)).toEqual(8) expect(flags.difference(3, 6)).toEqual(1)
expect(flags.difference(6, 3)).toEqual(4)
expect(flags.difference(8, 17)).toEqual(8)
}) })
test('intersection', () => { test('intersection', () => {
expect(BitFlags.intersection(0, 0)).toEqual(0) const flags = createBitFlagSet([])
expect(BitFlags.intersection(1, 0)).toEqual(0)
expect(BitFlags.intersection(1, 2)).toEqual(0) expect(flags.intersection(0, 0)).toEqual(0)
expect(BitFlags.intersection(1, 3)).toEqual(1) expect(flags.intersection(1, 0)).toEqual(0)
expect(BitFlags.intersection(11, 5)).toEqual(1) expect(flags.intersection(1, 2)).toEqual(0)
expect(BitFlags.intersection(11, 7)).toEqual(3) expect(flags.intersection(1, 3)).toEqual(1)
expect(flags.intersection(11, 5)).toEqual(1)
expect(flags.intersection(11, 7)).toEqual(3)
}) })
test('isSuperset', () => { test('isSuperset', () => {
expect(BitFlags.isSuperset(0, 0)).toBe(true) const flags = createBitFlagSet([])
expect(BitFlags.isSuperset(3, 0)).toBe(true)
expect(BitFlags.isSuperset(3, 1)).toBe(true) expect(flags.isSuperset(0, 0)).toBe(true)
expect(BitFlags.isSuperset(3, 3)).toBe(true) expect(flags.isSuperset(3, 0)).toBe(true)
expect(BitFlags.isSuperset(0, 3)).toBe(false) expect(flags.isSuperset(3, 1)).toBe(true)
expect(BitFlags.isSuperset(8, 4)).toBe(false) expect(flags.isSuperset(3, 3)).toBe(true)
expect(flags.isSuperset(0, 3)).toBe(false)
expect(flags.isSuperset(8, 4)).toBe(false)
}) })
test('hasAny', () => { test('hasAny', () => {
@@ -100,12 +108,14 @@ describe(BitFlagSet, () => {
}) })
test('enumerate', () => { test('enumerate', () => {
expect([...BitFlags.enumerate(0)]).toEqual([]) const flags = createBitFlagSet([])
expect([...BitFlags.enumerate(1)]).toEqual([1])
expect([...BitFlags.enumerate(2)]).toEqual([2]) expect([...flags.enumerate(0)]).toEqual([])
expect([...BitFlags.enumerate(3)]).toEqual([1, 2]) expect([...flags.enumerate(1)]).toEqual([1])
expect([...BitFlags.enumerate(11)]).toEqual([1, 2, 8]) expect([...flags.enumerate(2)]).toEqual([2])
expect([...BitFlags.enumerate(100)]).toEqual([4, 32, 64]) expect([...flags.enumerate(3)]).toEqual([1, 2])
expect([...flags.enumerate(11)]).toEqual([1, 2, 8])
expect([...flags.enumerate(100)]).toEqual([4, 32, 64])
}) })
test('maximum', () => { test('maximum', () => {
@@ -140,3 +150,48 @@ describe(BitFlagSet, () => {
expect(flags.minimum(13)).toEqual(5) expect(flags.minimum(13)).toEqual(5)
}) })
}) })
describe('BitFlags', () => {
test('union', () => {
expect(BitFlags.union(0, 0)).toEqual(0)
expect(BitFlags.union(1, 0)).toEqual(1)
expect(BitFlags.union(0, 2)).toEqual(2)
expect(BitFlags.union(1, 2)).toEqual(3)
expect(BitFlags.union(3, 6)).toEqual(7)
})
test('difference', () => {
expect(BitFlags.difference(0, 0)).toEqual(0)
expect(BitFlags.difference(1, 0)).toEqual(1)
expect(BitFlags.difference(3, 6)).toEqual(1)
expect(BitFlags.difference(6, 3)).toEqual(4)
expect(BitFlags.difference(8, 17)).toEqual(8)
})
test('intersection', () => {
expect(BitFlags.intersection(0, 0)).toEqual(0)
expect(BitFlags.intersection(1, 0)).toEqual(0)
expect(BitFlags.intersection(1, 2)).toEqual(0)
expect(BitFlags.intersection(1, 3)).toEqual(1)
expect(BitFlags.intersection(11, 5)).toEqual(1)
expect(BitFlags.intersection(11, 7)).toEqual(3)
})
test('isSuperset', () => {
expect(BitFlags.isSuperset(0, 0)).toBe(true)
expect(BitFlags.isSuperset(3, 0)).toBe(true)
expect(BitFlags.isSuperset(3, 1)).toBe(true)
expect(BitFlags.isSuperset(3, 3)).toBe(true)
expect(BitFlags.isSuperset(0, 3)).toBe(false)
expect(BitFlags.isSuperset(8, 4)).toBe(false)
})
test('enumerate', () => {
expect([...BitFlags.enumerate(0)]).toEqual([])
expect([...BitFlags.enumerate(1)]).toEqual([1])
expect([...BitFlags.enumerate(2)]).toEqual([2])
expect([...BitFlags.enumerate(3)]).toEqual([1, 2])
expect([...BitFlags.enumerate(11)]).toEqual([1, 2, 8])
expect([...BitFlags.enumerate(100)]).toEqual([4, 32, 64])
})
})