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

@@ -3,22 +3,28 @@ plugins {
id 'maven-publish'
}
version = '0.1.0'
group = "fr.louisdevie"
version = '0.1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation "org.junit.jupiter:junit-jupiter:6.0.2"
testRuntimeOnly "org.junit.platform:junit-platform-launcher"
}
java {
sourceCompatibility JavaVersion.VERSION_17
withSourcesJar()
}
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
test {
useJUnit()
useJUnitPlatform()
}
publishing {

View File

@@ -0,0 +1,76 @@
package fr.louisdevie.tatsuki;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class BigBitFlagSet implements FlagSet<BigInteger, BigInteger> {
public BigInteger empty() {
return BigInteger.ZERO;
}
public BigInteger union(BigInteger first, BigInteger second) {
return first.or(second);
}
public BigInteger intersection(BigInteger first, BigInteger second) {
return first.and(second);
}
public BigInteger difference(BigInteger first, BigInteger second) {
return first.andNot(second);
}
public boolean isSuperset(BigInteger first, BigInteger second) {
return first.and(second).equals(second);
}
public Iterable<BigInteger> enumerate(BigInteger flags) {
return new IterableBitFlags(flags);
}
public BigInteger minimum(BigInteger flags) {
return BigInteger.ZERO;
}
public BigInteger maximum(BigInteger flags) {
return BigInteger.ZERO;
}
private record IterableBitFlags(BigInteger value) implements Iterable<BigInteger> {
public Iterator<BigInteger> iterator() {
return new BitFlagsIterator(this.value);
}
}
private static class BitFlagsIterator implements Iterator<BigInteger> {
private BigInteger value;
private BigInteger current;
public BitFlagsIterator(BigInteger value) {
this.value = value;
this.current = BigInteger.ONE;
}
public boolean hasNext() {
return !this.value.equals(BigInteger.ZERO);
}
public BigInteger next() {
if (this.value.equals(BigInteger.ZERO)) {
throw new NoSuchElementException();
}
while (!this.value.testBit(0)) {
this.value = this.value.shiftRight(1);
this.current = this.current.shiftLeft(1);
}
BigInteger element = this.current;
this.value = this.value.shiftRight(1);
this.current = this.current.shiftLeft(1);
return element;
}
}
}

View File

@@ -0,0 +1,75 @@
package fr.louisdevie.tatsuki;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class BitFlagSet implements FlagSet<Integer, Integer> {
public Integer empty() {
return 0;
}
public Integer union(Integer first, Integer second) {
return first | second;
}
public Integer intersection(Integer first, Integer second) {
return first & second;
}
public Integer difference(Integer first, Integer second) {
return first & ~second;
}
public boolean isSuperset(Integer first, Integer second) {
return (first & second) == second;
}
public Iterable<Integer> enumerate(Integer flags) {
return new IterableBitFlags(flags);
}
public Integer minimum(Integer flags) {
return 0;
}
public Integer maximum(Integer flags) {
return 0;
}
private record IterableBitFlags(int value) implements Iterable<Integer> {
public Iterator<Integer> iterator() {
return new BitFlagsIterator(this.value);
}
}
private static class BitFlagsIterator implements Iterator<Integer> {
private int value;
private int current;
public BitFlagsIterator(int value) {
this.value = value;
this.current = 1;
}
public boolean hasNext() {
return this.value != 0;
}
public Integer next() {
if (this.value == 0) {
throw new NoSuchElementException();
}
while ((this.value & 1) == 0) {
this.value >>= 1;
this.current <<= 1;
}
int element = this.current;
this.value >>= 1;
this.current <<= 1;
return element;
}
}
}

View File

@@ -0,0 +1,82 @@
package fr.louisdevie.tatsuki;
/**
* Represents a group of flags of type {@link F} and the relationships between
* them. It also provides methods to use {@link S} as a set of those flags.
*
* @param <F> The type of values in the set.
* @param <S> The type to be used as a set.
*/
public interface FlagSet<F, S> {
/**
* Creates an empty set of flags.
*/
S empty();
/**
* Computes the union of two sets of flags.
*
* @param first The first set of flags.
* @param second The second set of flags.
* @return A new set that contains the flags of both sets.
*/
S union(S first, S second);
/**
* Computes the intersection of two set of flags.
*
* @param first The first set of flags.
* @param second The second set of flags.
* @return A new set that contains the flags that appear both in the first
* set and the second set.
*/
S intersection(S first, S second);
/**
* Computes the difference of two set of flags.
*
* @param first The first set of flags.
* @param second The second set of flags (that will be subtracted from the
* first).
* @return A new set that contains the flags of the first set that do not
* appear in the second.
*/
S difference(S first, S second);
/**
* Checks whether the first set of flags is a superset of the second.
*
* @param first The first set of flags.
* @param second The second set of flags.
*/
boolean isSuperset(S first, S second);
/**
* Returns an iterable over the individual flags in a set.
*
* @param flags A set of flags.
*/
Iterable<F> enumerate(S flags);
/**
* Filters a flag set so that it only contains the flags that were defined
* in this set. If a flags is missing some of its parents, it will not be
* included in the result.
*
* @param flags The set of flags to filter.
* @return A new set of flags.
* @see #maximum
*/
S minimum(S flags);
/**
* Filters a flag set so that it only contains the flags that were defined
* in this set. If a flags is missing some of its parents, they will be
* added to the result.
*
* @param flags The set of flags to filter.
* @return A new set of flags.
* @see #minimum
*/
S maximum(S flags);
}

View File

@@ -0,0 +1,12 @@
package fr.louisdevie.tatsuki;
import fr.louisdevie.tatsuki.builders.BitFlagSetBuilder;
import fr.louisdevie.tatsuki.builders.syntax.DefineFlag;
import fr.louisdevie.tatsuki.builders.syntax.SelectFlagSetType;
public class FlagSetBuilder implements SelectFlagSetType {
public DefineFlag<BitFlagSet> useBitFlags() {
return new BitFlagSetBuilder();
}
}

View File

@@ -1,7 +0,0 @@
package fr.louisdevie.tatsuki;
public class Library {
public boolean someLibraryMethod() {
return true;
}
}

View File

@@ -0,0 +1,75 @@
package fr.louisdevie.tatsuki;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class LongBitFlagSet implements FlagSet<Long, Long> {
public Long empty() {
return 0L;
}
public Long union(Long first, Long second) {
return first | second;
}
public Long intersection(Long first, Long second) {
return first & second;
}
public Long difference(Long first, Long second) {
return first & ~second;
}
public boolean isSuperset(Long first, Long second) {
return (first & second) == second;
}
public Iterable<Long> enumerate(Long flags) {
return new IterableBitFlags(flags);
}
public Long minimum(Long flags) {
return 0L;
}
public Long maximum(Long flags) {
return 0L;
}
private record IterableBitFlags(long value) implements Iterable<Long> {
public Iterator<Long> iterator() {
return new BitFlagsIterator(this.value);
}
}
private static class BitFlagsIterator implements Iterator<Long> {
private long value;
private long current;
public BitFlagsIterator(long value) {
this.value = value;
this.current = 1L;
}
public boolean hasNext() {
return this.value != 0L;
}
public Long next() {
if (this.value == 0L) {
throw new NoSuchElementException();
}
while ((this.value & 1L) == 0L) {
this.value >>= 1;
this.current <<= 1;
}
long element = this.current;
this.value >>= 1;
this.current <<= 1;
return element;
}
}
}

View File

@@ -0,0 +1,15 @@
package fr.louisdevie.tatsuki.builders;
import fr.louisdevie.tatsuki.BitFlagSet;
import fr.louisdevie.tatsuki.builders.syntax.DefineFlag;
import fr.louisdevie.tatsuki.builders.syntax.SetValueOrCompose;
public class BitFlagSetBuilder implements DefineFlag<BitFlagSet> {
public SetValueOrCompose<BitFlagSet> define(String name) {
return null;
}
public BitFlagSet build() {
return new BitFlagSet();
}
}

View File

@@ -0,0 +1,9 @@
package fr.louisdevie.tatsuki.builders.syntax;
import fr.louisdevie.tatsuki.BitFlagSet;
public interface DefineFlag<X> {
SetValueOrCompose<X> define(String name);
X build();
}

View File

@@ -0,0 +1,5 @@
package fr.louisdevie.tatsuki.builders.syntax;
public interface RequireParentsOrDefineFlag<X> extends DefineFlag<X> {
DefineFlag<X> requires(String ...flags);
}

View File

@@ -0,0 +1,7 @@
package fr.louisdevie.tatsuki.builders.syntax;
import fr.louisdevie.tatsuki.BitFlagSet;
public interface SelectFlagSetType {
DefineFlag<BitFlagSet> useBitFlags();
}

View File

@@ -0,0 +1,7 @@
package fr.louisdevie.tatsuki.builders.syntax;
public interface SetValueOrCompose<X> {
RequireParentsOrDefineFlag<X> withValue(int value);
DefineFlag<X> compose(String ...flags);
}

View File

@@ -0,0 +1,4 @@
package fr.louisdevie.tatsuki.definitions;
interface FlagDefinition<S> {
}

View File

@@ -0,0 +1,7 @@
package fr.louisdevie.tatsuki.definitions;
import java.util.Map;
public class FlagDefinitionMap<F, S> {
private Map<F, FlagDefinition<S>> _container;
}

View File

@@ -0,0 +1,80 @@
package fr.louisdevie.tatsuki;
import org.junit.jupiter.api.Test;
import java.math.BigInteger;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class BigBitFlagSetTests {
private static BigInteger big(int value) {
return BigInteger.valueOf(value);
}
@Test
public void empty() {
var flags = new BigBitFlagSet();
assertEquals(big(0), flags.empty());
}
@Test
public void union() {
final var flags = new BigBitFlagSet();
assertEquals(big(0), flags.union(big(0), big(0)));
assertEquals(big(1), flags.union(big(1), big(0)));
assertEquals(big(2), flags.union(big(0), big(2)));
assertEquals(big(3), flags.union(big(1), big(2)));
assertEquals(big(7), flags.union(big(3), big(6)));
}
@Test
public void difference() {
var flags = new BigBitFlagSet();
assertEquals(big(0), flags.difference(big(0), big(0)));
assertEquals(big(1), flags.difference(big(1), big(0)));
assertEquals(big(1), flags.difference(big(3), big(6)));
assertEquals(big(4), flags.difference(big(6), big(3)));
assertEquals(big(8), flags.difference(big(8), big(17)));
}
@Test
public void intersection() {
var flags = new BigBitFlagSet();
assertEquals(big(0), flags.intersection(big(0), big(0)));
assertEquals(big(0), flags.intersection(big(1), big(0)));
assertEquals(big(0), flags.intersection(big(1), big(2)));
assertEquals(big(1), flags.intersection(big(1), big(3)));
assertEquals(big(1), flags.intersection(big(11), big(5)));
assertEquals(big(3), flags.intersection(big(11), big(7)));
}
@Test
public void isSuperset() {
var flags = new BigBitFlagSet();
assertTrue(flags.isSuperset(big(0), big(0)));
assertTrue(flags.isSuperset(big(3), big(0)));
assertTrue(flags.isSuperset(big(3), big(1)));
assertTrue(flags.isSuperset(big(3), big(3)));
assertFalse(flags.isSuperset(big(0), big(3)));
assertFalse(flags.isSuperset(big(8), big(4)));
}
@Test
public void enumerate() {
var flags = new BigBitFlagSet();
assertIterableEquals(List.of(), flags.enumerate(big(0)));
assertIterableEquals(List.of(big(1)), flags.enumerate(big(1)));
assertIterableEquals(List.of(big(2)), flags.enumerate(big(2)));
assertIterableEquals(List.of(big(1), big(2)), flags.enumerate(big(3)));
assertIterableEquals(List.of(big(1), big(2), big(8)), flags.enumerate(big(11)));
assertIterableEquals(List.of(big(4), big(32), big(64)), flags.enumerate(big(100)));
}
}

View File

@@ -0,0 +1,75 @@
package fr.louisdevie.tatsuki;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class BitFlagSetTests {
@Test
public void empty() {
var flags = new BitFlagSet();
assertEquals(0, flags.empty());
}
@Test
public void union() {
final var flags = new BitFlagSet();
assertEquals(0, flags.union(0, 0));
assertEquals(1, flags.union(1, 0));
assertEquals(2, flags.union(0, 2));
assertEquals(3, flags.union(1, 2));
assertEquals(7, flags.union(3, 6));
}
@Test
public void difference() {
var flags = new BitFlagSet();
assertEquals(0, flags.difference(0, 0));
assertEquals(1, flags.difference(1, 0));
assertEquals(1, flags.difference(3, 6));
assertEquals(4, flags.difference(6, 3));
assertEquals(8, flags.difference(8, 17));
}
@Test
public void intersection() {
var flags = new BitFlagSet();
assertEquals(0, flags.intersection(0, 0));
assertEquals(0, flags.intersection(1, 0));
assertEquals(0, flags.intersection(1, 2));
assertEquals(1, flags.intersection(1, 3));
assertEquals(1, flags.intersection(11, 5));
assertEquals(3, flags.intersection(11, 7));
}
@Test
public void isSuperset() {
var flags = new BitFlagSet();
assertTrue(flags.isSuperset(0, 0));
assertTrue(flags.isSuperset(3, 0));
assertTrue(flags.isSuperset(3, 1));
assertTrue(flags.isSuperset(3, 3));
assertFalse(flags.isSuperset(0, 3));
assertFalse(flags.isSuperset(8, 4));
}
@Test
public void enumerate() {
var flags = new BitFlagSet();
assertIterableEquals(List.of(), flags.enumerate(0));
assertIterableEquals(List.of(1), flags.enumerate(1));
assertIterableEquals(List.of(2), flags.enumerate(2));
assertIterableEquals(List.of(1, 2), flags.enumerate(3));
assertIterableEquals(List.of(1, 2, 8), flags.enumerate(11));
assertIterableEquals(List.of(4, 32, 64), flags.enumerate(100));
}
}

View File

@@ -1,13 +0,0 @@
package fr.louisdevie.tatsuki;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class LibraryTest {
@Test
public void someLibraryMethodReturnsTrue() {
Library classUnderTest = new Library();
assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());
}
}

View File

@@ -0,0 +1,75 @@
package fr.louisdevie.tatsuki;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class LongBitFlagSetTests {
@Test
public void empty() {
var flags = new LongBitFlagSet();
assertEquals(0L, flags.empty());
}
@Test
public void union() {
final var flags = new LongBitFlagSet();
assertEquals(0L, flags.union(0L, 0L));
assertEquals(1L, flags.union(1L, 0L));
assertEquals(2L, flags.union(0L, 2L));
assertEquals(3L, flags.union(1L, 2L));
assertEquals(7, flags.union(3L, 6L));
}
@Test
public void difference() {
var flags = new LongBitFlagSet();
assertEquals(0L, flags.difference(0L, 0L));
assertEquals(1L, flags.difference(1L, 0L));
assertEquals(1L, flags.difference(3L, 6L));
assertEquals(4, flags.difference(6L, 3L));
assertEquals(8, flags.difference(8L, 17L));
}
@Test
public void intersection() {
var flags = new LongBitFlagSet();
assertEquals(0L, flags.intersection(0L, 0L));
assertEquals(0L, flags.intersection(1L, 0L));
assertEquals(0L, flags.intersection(1L, 2L));
assertEquals(1L, flags.intersection(1L, 3L));
assertEquals(1L, flags.intersection(11L, 5L));
assertEquals(3L, flags.intersection(11L, 7L));
}
@Test
public void isSuperset() {
var flags = new LongBitFlagSet();
assertTrue(flags.isSuperset(0L, 0L));
assertTrue(flags.isSuperset(3L, 0L));
assertTrue(flags.isSuperset(3L, 1L));
assertTrue(flags.isSuperset(3L, 3L));
assertFalse(flags.isSuperset(0L, 3L));
assertFalse(flags.isSuperset(8L, 4L));
}
@Test
public void enumerate() {
var flags = new LongBitFlagSet();
assertIterableEquals(List.of(), flags.enumerate(0L));
assertIterableEquals(List.of(1L), flags.enumerate(1L));
assertIterableEquals(List.of(2L), flags.enumerate(2L));
assertIterableEquals(List.of(1L, 2L), flags.enumerate(3L));
assertIterableEquals(List.of(1L, 2L, 8L), flags.enumerate(11L));
assertIterableEquals(List.of(4L, 32L, 64L), flags.enumerate(100L));
}
}