(java) added BitFlags helper class

This commit is contained in:
2026-03-23 23:46:55 +01:00
parent 7de7d5e6cb
commit a56febbbb5
37 changed files with 1269 additions and 404 deletions

View File

@@ -16,7 +16,7 @@ dependencies {
}
java {
sourceCompatibility JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_17
withSourcesJar()
toolchain {

View File

@@ -1,76 +0,0 @@
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

@@ -1,75 +0,0 @@
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,75 @@
package fr.louisdevie.tatsuki;
import java.util.Optional;
/**
* Represents a single flag defined in a {@link FlagGraph}.
*
* @param <S> A set of this type of flags.
*/
public interface FlagDefinition<S> {
/**
* The alias of the flag.
*/
Optional<String> getAlias();
/**
* A set containing the value of the flag, or multiple values if it is a composed flag.
*/
S values();
/**
* Test if this flag and all its parents are present in the set.
*
* @param set A set of flags.
* @return {@code true} if the set includes this flags and its parents.
*/
boolean isIn(S set);
/**
* Add this flag and all its parents to the set.
*
* @param set A set of flags.
* @return A new set of flags containing the flags from the {@code set}, this flag and its parents.
*/
S addTo(S set);
/**
* Removes this flag and all its children from the set.
*
* @param set A set of flags.
* @return A new set of flags containing the flags from the {@code set} except this flag and its children.
*/
S removeFrom(S set);
/**
* Adds or removes this flag from the set depending on the specified boolean value.
* If {@code value} is {@code true}, flags will be added so that {@link #isIn} returns {@code true.}
* If {@code value} is {@code false}, flags will be removed so that {@link #isIn} returns {@code false.}
*
* @param set A set of flags.
* @return A new set of flags.
*/
default S setIn(S set, boolean value) {
if (value) {
return this.addTo(set);
} else {
return this.removeFrom(set);
}
}
/**
* Add this flag and all its parents to the set if they aren't already present,
* and removes this flag otherwise.
*
* @param set A set of flags.
* @return A new set of flags.
*/
default S toggle(S set) {
if (!this.isIn(set)) {
return this.addTo(set);
} else {
return this.removeFrom(set);
}
}
}

View File

@@ -0,0 +1,123 @@
package fr.louisdevie.tatsuki;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
/**
* Represents a group of flags of type {@link V} and the relationships between
* them.
*
* @param <V> The type of the flag values.
* @param <S> The type used to contain the flags.
*/
public interface FlagGraph<V, S> {
/**
* Returns the number of definitions in this graph.
*/
int size();
/**
* Returns {@code true} if this graph contains no definitions.
*/
boolean isEmpty();
/**
* Returns {@code true} if this graph contains a definition with the specified alias.
*/
boolean containsAlias(String alias);
/**
* Returns {@code true} if this graph contains a definition with the specified value.
*/
boolean containsValue(V value);
/**
* Returns {@code true} if the specified definition belongs to this graph.
*/
boolean containsDefinition(FlagDefinition<S> definition);
/**
* Returns the flag definition with the specified alias.
*
* @param alias The alias of the flag.
*
* @return An {@link Optional} of the corresponding definition, or an empty
* {@link Optional} if there is no definition with this alias.
*/
Optional<FlagDefinition<S>> definitionNamed(String alias);
/**
* Returns the flag definition with the specified value.
*
* @param value The base value of the flag.
*
* @return An {@link Optional} of the corresponding definition, or an empty
* {@link Optional} if there is no definition with this value.
*/
Optional<FlagDefinition<S>> definitionOf(V value);
/**
* Returns a {@link Set} view of the aliases defined in this graph.
*/
Set<String> aliasSet();
/**
* Returns a {@link Set} view of the values defined in this graph.
*/
Set<V> valueSet();
/**
* Returns a list of all the defined flags.
*/
Collection<FlagDefinition<S>> definitions();
/**
* Creates a set of flags from a list of aliases.
*/
S named(String... aliases);
/**
* Checks whether the first set of flags includes at least one of the flags
* from the second set. If there is no required flags, the result will always be `false`.
* A flag is considered to be part of the set only if all of its parents are
* present too.
*
* @param flags - A set of flags.
* @param required - The flags to search for in the first set.
*/
boolean hasAny(S flags, S required);
/**
* Checks whether the first set of flags includes all the flags from the
* second set. If there is no required flags, the result will always be `true`.
* A flag is considered to be part of the set only if all of its parents are
* present too.
*
* @param flags - A set of flags.
* @param required - The flags to search for in the first set.
*/
boolean hasAll(S flags, S required);
/**
* 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

@@ -1,82 +0,0 @@
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

@@ -1,12 +0,0 @@
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,75 +0,0 @@
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,88 @@
package fr.louisdevie.tatsuki.bitflags;
import fr.louisdevie.tatsuki.FlagGraph;
import fr.louisdevie.tatsuki.FlagDefinition;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class BigBitFlagGraph implements FlagGraph<BigInteger, BigInteger> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsAlias(String alias) {
return false;
}
@Override
public boolean containsValue(BigInteger value) {
return false;
}
@Override
public boolean containsDefinition(FlagDefinition<BigInteger> definition) {
return false;
}
@Override
public Optional<FlagDefinition<BigInteger>> definitionNamed(String alias) {
return Optional.empty();
}
@Override
public Optional<FlagDefinition<BigInteger>> definitionOf(BigInteger value) {
return Optional.empty();
}
@Override
public Set<String> aliasSet() {
return Set.of();
}
@Override
public Set<BigInteger> valueSet() {
return Set.of();
}
@Override
public Collection<FlagDefinition<BigInteger>> definitions() {
return List.of();
}
@Override
public BigInteger named(String... aliases) {
return null;
}
@Override
public boolean hasAny(BigInteger flags, BigInteger required) {
return false;
}
@Override
public boolean hasAll(BigInteger flags, BigInteger required) {
return false;
}
@Override
public BigInteger minimum(BigInteger flags) {
return null;
}
@Override
public BigInteger maximum(BigInteger flags) {
return null;
}
}

View File

@@ -0,0 +1,36 @@
package fr.louisdevie.tatsuki.bitflags;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class BigBitFlagsIterator implements Iterator<BigInteger> {
private BigInteger value;
private BigInteger current;
public BigBitFlagsIterator(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,86 @@
package fr.louisdevie.tatsuki.bitflags;
import fr.louisdevie.tatsuki.FlagGraph;
import fr.louisdevie.tatsuki.FlagDefinition;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class BitFlagGraph implements FlagGraph<Integer, Integer> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsAlias(String alias) {
return false;
}
@Override
public boolean containsValue(Integer value) {
return false;
}
@Override
public boolean containsDefinition(FlagDefinition<Integer> definition) {
return false;
}
@Override
public Optional<FlagDefinition<Integer>> definitionNamed(String alias) {
return Optional.empty();
}
@Override
public Optional<FlagDefinition<Integer>> definitionOf(Integer value) {
return Optional.empty();
}
@Override
public Set<String> aliasSet() {
return Set.of();
}
@Override
public Set<Integer> valueSet() {
return Set.of();
}
@Override
public Collection<FlagDefinition<Integer>> definitions() {
return List.of();
}
@Override
public Integer named(String... aliases) {
return 0;
}
@Override
public boolean hasAny(Integer flags, Integer required) {
return false;
}
@Override
public boolean hasAll(Integer flags, Integer required) {
return false;
}
@Override
public Integer minimum(Integer flags) {
return 0;
}
@Override
public Integer maximum(Integer flags) {
return 0;
}
}

View File

@@ -0,0 +1,141 @@
package fr.louisdevie.tatsuki.bitflags;
import java.math.BigInteger;
import java.util.Iterator;
public final class BitFlags {
/**
* Computes the union of two sets of bitflags.
* Any bits that are set in either of the inputs will be set in the result.
*/
public static int union(int first, int second) {
return first | second;
}
/**
* Computes the union of two sets of bitflags.
* Any bits that are set in either of the inputs will be set in the result.
*/
public static long union(long first, long second) {
return first | second;
}
/**
* Computes the union of two sets of bitflags.
* Any bits that are set in either of the inputs will be set in the result.
*/
public static BigInteger union(BigInteger first, BigInteger second) {
return first.or(second);
}
/**
* Computes the intersection of two sets of bitflags.
* Only bits that are set in both of the inputs will be set in the result.
*/
public static int intersection(int first, int second) {
return first & second;
}
/**
* Computes the intersection of two sets of bitflags.
* Only bits that are set in both of the inputs will be set in the result.
*/
public static long intersection(long first, long second) {
return first & second;
}
/**
* Computes the intersection of two sets of bitflags.
* Only bits that are set in both of the inputs will be set in the result.
*/
public static BigInteger intersection(BigInteger first, BigInteger second) {
return first.and(second);
}
/**
* Computes the difference between two sets of bitflags.
* Only bits that are set in the first input and not set in the second will be set in the result.
*/
public static int difference(int first, int second) {
return first & ~second;
}
/**
* Computes the difference between two sets of bitflags.
* Only bits that are set in the first input and not set in the second will be set in the result.
*/
public static long difference(long first, long second) {
return first & ~second;
}
/**
* Computes the difference between two sets of bitflags.
* Only bits that are set in the first input and not set in the second will be set in the result.
*/
public static BigInteger difference(BigInteger first, BigInteger second) {
return first.andNot(second);
}
/**
* Tests if a set of bitflags is a superset of the other.
* Return `true` if every bit that is set in the second input is also set in the first.
*/
public static boolean isSuperset(int first, int second) {
return (first & second) == second;
}
/**
* Tests if a set of bitflags is a superset of the other.
* Return `true` if every bit that is set in the second input is also set in the first.
*/
public static boolean isSuperset(long first, long second) {
return (first & second) == second;
}
/**
* Tests if a set of bitflags is a superset of the other.
* Return `true` if every bit that is set in the second input is also set in the first.
*/
public static boolean isSuperset(BigInteger first, BigInteger second) {
return first.and(second).equals(second);
}
/**
* Returns an iterable over the individual bits that are set.
*/
public static Iterable<Integer> enumerate(int flags) {
return new IterableInteger(flags);
}
private record IterableInteger(int value) implements Iterable<Integer> {
public Iterator<Integer> iterator() {
return new BitFlagsIterator(this.value);
}
}
/**
* Returns an iterable over the individual bits that are set.
*/
public static Iterable<Long> enumerate(long flags) {
return new IterableLong(flags);
}
private record IterableLong(long value) implements Iterable<Long> {
public Iterator<Long> iterator() {
return new LongBitFlagsIterator(this.value);
}
}
/**
* Returns an iterable over the individual bits that are set.
*/
public static Iterable<BigInteger> enumerate(BigInteger flags) {
return new IterableBigInteger(flags);
}
private record IterableBigInteger(BigInteger value) implements Iterable<BigInteger> {
public Iterator<BigInteger> iterator() {
return new BigBitFlagsIterator(this.value);
}
}
}

View File

@@ -0,0 +1,35 @@
package fr.louisdevie.tatsuki.bitflags;
import java.util.Iterator;
import java.util.NoSuchElementException;
public 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,87 @@
package fr.louisdevie.tatsuki.bitflags;
import fr.louisdevie.tatsuki.FlagGraph;
import fr.louisdevie.tatsuki.FlagDefinition;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class LongBitFlagGraph implements FlagGraph<Long, Long> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsAlias(String alias) {
return false;
}
@Override
public boolean containsValue(Long value) {
return false;
}
@Override
public boolean containsDefinition(FlagDefinition<Long> definition) {
return false;
}
@Override
public Optional<FlagDefinition<Long>> definitionNamed(String alias) {
return Optional.empty();
}
@Override
public Optional<FlagDefinition<Long>> definitionOf(Long value) {
return Optional.empty();
}
@Override
public Set<String> aliasSet() {
return Set.of();
}
@Override
public Set<Long> valueSet() {
return Set.of();
}
@Override
public Collection<FlagDefinition<Long>> definitions() {
return List.of();
}
@Override
public Long named(String... aliases) {
return 0L;
}
@Override
public boolean hasAny(Long flags, Long required) {
return false;
}
@Override
public boolean hasAll(Long flags, Long required) {
return false;
}
@Override
public Long minimum(Long flags) {
return 0L;
}
@Override
public Long maximum(Long flags) {
return 0L;
}
}

View File

@@ -0,0 +1,36 @@
package fr.louisdevie.tatsuki.bitflags;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class LongBitFlagsIterator implements Iterator<Long> {
private long value;
private long current;
public LongBitFlagsIterator(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

@@ -1,15 +0,0 @@
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,29 @@
package fr.louisdevie.tatsuki.builders.syntax;
public interface AsOrComposeValues<V, R> {
/**
* Give the flag an alias to reference it elsewhere.
*
* @param alias An alias for the flag.
*
* @throws NullPointerException when {@code alias} is {@code null}.
* @throws IllegalArgumentException when two flags are given the same alias.
*/
DefineOrComposeValues<V, R> as(String alias);
/**
* Combine multiple flags together.
*
* @param values The values of the flags to combine.
*/
@SuppressWarnings("unchecked")
AsOrComposeValues<V, R> composeValues(V... values);
/**
* Combine multiple flags together.
*
* @param aliases The aliases of the flags to combine.
*/
AsOrComposeValues<V, R> compose(String... aliases);
}

View File

@@ -0,0 +1,13 @@
package fr.louisdevie.tatsuki.builders.syntax;
public interface AsOrRequiresOrDefineOrComposeValues<V, R> extends RequiresOrDefineOrComposeValues<V, R> {
/**
* Give the flag an alias to reference it elsewhere.
*
* @param alias An alias for the flag.
*
* @throws NullPointerException when {@code alias} is {@code null}.
* @throws IllegalArgumentException when two flags are given the same alias.
*/
RequiresOrDefineOrComposeValues<V, R> as(String alias);
}

View File

@@ -1,9 +0,0 @@
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,29 @@
package fr.louisdevie.tatsuki.builders.syntax;
public interface DefineOrComposeValues<V, R> extends Root<R> {
/**
* Define a flag with the given value.
*
* @param value The value of the flag.
* @throws NullPointerException when {@code value} is {@code null}.
* @throws IllegalArgumentException when two flags are defined with the same value, or when {@code value} is not an
* appropriate value for the type of flag set you are building.
*/
AsOrRequiresOrDefineOrComposeValues<V, R> defineValue(V value);
/**
* Combine multiple flags together.
*
* @param aliases The aliases of the flags to combine.
*/
AsOrComposeValues<V, R> compose(String... aliases);
/**
* Combine multiple flags together.
*
* @param values The values of the flags to combine.
*/
@SuppressWarnings("unchecked")
AsOrComposeValues<V, R> composeValues(V... values);
}

View File

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

View File

@@ -0,0 +1,16 @@
package fr.louisdevie.tatsuki.builders.syntax;
public interface RequiresOrDefineOrComposeValues<V, R> extends DefineOrComposeValues<V, R> {
/**
* Define the "parents" of this flag. A flag is not considered enabled unless all its parents are also enabled.
* @param aliases The names of the required flags.
*/
RequiresOrDefineOrComposeValues<V, R> requires(String... aliases);
/**
* Define the "parents" of this flag. A flag is not considered enabled unless all its parents are also enabled.
* @param values The values of the required flags.
*/
@SuppressWarnings("unchecked")
RequiresOrDefineOrComposeValues<V, R> requiresValues(V... values);
}

View File

@@ -0,0 +1,12 @@
package fr.louisdevie.tatsuki.builders.syntax;
/**
* Builder syntax root.
* @param <R> The result type produced by the builder.
*/
public interface Root<R> {
/**
* Build a flag set with the flags previously defined.
*/
R build();
}

View File

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

View File

@@ -1,7 +0,0 @@
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,27 @@
package fr.louisdevie.tatsuki.collections;
import fr.louisdevie.tatsuki.builders.syntax.AsOrComposeValues;
import fr.louisdevie.tatsuki.builders.syntax.AsOrRequiresOrDefineOrComposeValues;
import fr.louisdevie.tatsuki.builders.syntax.DefineOrComposeValues;
class CollectionFlagGraphBuilder<E> implements DefineOrComposeValues<E, SetFlagGraph<E>> {
@Override
public AsOrRequiresOrDefineOrComposeValues<E, SetFlagGraph<E>> defineValue(E value) {
return null;
}
@Override
public AsOrComposeValues<E, SetFlagGraph<E>> compose(String... aliases) {
return null;
}
@Override
public AsOrComposeValues<E, SetFlagGraph<E>> composeValues(E... values) {
return null;
}
@Override
public SetFlagGraph<E> build() {
return null;
}
}

View File

@@ -0,0 +1,33 @@
package fr.louisdevie.tatsuki.collections;
import fr.louisdevie.tatsuki.FlagDefinition;
import java.util.Optional;
import java.util.Set;
class SetFlagDefinition<E> implements FlagDefinition<Set<E>> {
@Override
public Optional<String> getAlias() {
return Optional.empty();
}
@Override
public Set<E> values() {
return Set.of();
}
@Override
public boolean isIn(Set<E> set) {
return false;
}
@Override
public Set<E> addTo(Set<E> set) {
return Set.of();
}
@Override
public Set<E> removeFrom(Set<E> set) {
return Set.of();
}
}

View File

@@ -0,0 +1,95 @@
package fr.louisdevie.tatsuki.collections;
import fr.louisdevie.tatsuki.FlagGraph;
import fr.louisdevie.tatsuki.builders.syntax.DefineOrComposeValues;
import fr.louisdevie.tatsuki.FlagDefinition;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class SetFlagGraph<E> implements FlagGraph<E, Set<E>> {
public static <E> DefineOrComposeValues<E, SetFlagGraph<E>> builder() {
return new CollectionFlagGraphBuilder<>();
}
SetFlagGraph(Collection<SetFlagDefinition<E>> c) {
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsAlias(String alias) {
return false;
}
@Override
public boolean containsValue(E value) {
return false;
}
@Override
public boolean containsDefinition(FlagDefinition<Set<E>> definition) {
return false;
}
@Override
public Optional<FlagDefinition<Set<E>>> definitionNamed(String alias) {
return Optional.empty();
}
@Override
public Optional<FlagDefinition<Set<E>>> definitionOf(E value) {
return Optional.empty();
}
@Override
public Set<String> aliasSet() {
return Set.of();
}
@Override
public Set<E> valueSet() {
return Set.of();
}
@Override
public Collection<FlagDefinition<Set<E>>> definitions() {
return List.of();
}
@Override
public Set<E> named(String... aliases) {
return Set.of();
}
@Override
public boolean hasAny(Set<E> flags, Set<E> required) {
return false;
}
@Override
public boolean hasAll(Set<E> flags, Set<E> required) {
return false;
}
@Override
public Set<E> minimum(Set<E> flags) {
return Set.of();
}
@Override
public Set<E> maximum(Set<E> flags) {
return Set.of();
}
}

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
package fr.louisdevie.tatsuki;
import fr.louisdevie.tatsuki.bitflags.BigBitFlagGraph;
import org.junit.jupiter.api.Test;
import java.math.BigInteger;
@@ -8,21 +9,21 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class BigBitFlagSetTests {
private static BigInteger big(int value) {
public class BigBitFlagGraphTests {
/*private static BigInteger big(int value) {
return BigInteger.valueOf(value);
}
@Test
public void empty() {
var flags = new BigBitFlagSet();
var flags = new BigBitFlagGraph();
assertEquals(big(0), flags.empty());
}
@Test
public void union() {
final var flags = new BigBitFlagSet();
final var flags = new BigBitFlagGraph();
assertEquals(big(0), flags.union(big(0), big(0)));
assertEquals(big(1), flags.union(big(1), big(0)));
@@ -33,7 +34,7 @@ public class BigBitFlagSetTests {
@Test
public void difference() {
var flags = new BigBitFlagSet();
var flags = new BigBitFlagGraph();
assertEquals(big(0), flags.difference(big(0), big(0)));
assertEquals(big(1), flags.difference(big(1), big(0)));
@@ -44,7 +45,7 @@ public class BigBitFlagSetTests {
@Test
public void intersection() {
var flags = new BigBitFlagSet();
var flags = new BigBitFlagGraph();
assertEquals(big(0), flags.intersection(big(0), big(0)));
assertEquals(big(0), flags.intersection(big(1), big(0)));
@@ -56,7 +57,7 @@ public class BigBitFlagSetTests {
@Test
public void isSuperset() {
var flags = new BigBitFlagSet();
var flags = new BigBitFlagGraph();
assertTrue(flags.isSuperset(big(0), big(0)));
assertTrue(flags.isSuperset(big(3), big(0)));
@@ -68,7 +69,7 @@ public class BigBitFlagSetTests {
@Test
public void enumerate() {
var flags = new BigBitFlagSet();
var flags = new BigBitFlagGraph();
assertIterableEquals(List.of(), flags.enumerate(big(0)));
assertIterableEquals(List.of(big(1)), flags.enumerate(big(1)));
@@ -76,5 +77,5 @@ public class BigBitFlagSetTests {
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

@@ -1,5 +1,6 @@
package fr.louisdevie.tatsuki;
import fr.louisdevie.tatsuki.bitflags.BitFlagGraph;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -7,17 +8,17 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class BitFlagSetTests {
@Test
public class BitFlagGraphTests {
/*@Test
public void empty() {
var flags = new BitFlagSet();
var flags = new BitFlagGraph();
assertEquals(0, flags.empty());
}
@Test
public void union() {
final var flags = new BitFlagSet();
final var flags = new BitFlagGraph();
assertEquals(0, flags.union(0, 0));
assertEquals(1, flags.union(1, 0));
@@ -28,7 +29,7 @@ public class BitFlagSetTests {
@Test
public void difference() {
var flags = new BitFlagSet();
var flags = new BitFlagGraph();
assertEquals(0, flags.difference(0, 0));
assertEquals(1, flags.difference(1, 0));
@@ -39,7 +40,7 @@ public class BitFlagSetTests {
@Test
public void intersection() {
var flags = new BitFlagSet();
var flags = new BitFlagGraph();
assertEquals(0, flags.intersection(0, 0));
assertEquals(0, flags.intersection(1, 0));
@@ -51,7 +52,7 @@ public class BitFlagSetTests {
@Test
public void isSuperset() {
var flags = new BitFlagSet();
var flags = new BitFlagGraph();
assertTrue(flags.isSuperset(0, 0));
assertTrue(flags.isSuperset(3, 0));
@@ -63,7 +64,7 @@ public class BitFlagSetTests {
@Test
public void enumerate() {
var flags = new BitFlagSet();
var flags = new BitFlagGraph();
assertIterableEquals(List.of(), flags.enumerate(0));
assertIterableEquals(List.of(1), flags.enumerate(1));
@@ -71,5 +72,5 @@ public class BitFlagSetTests {
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,5 +1,6 @@
package fr.louisdevie.tatsuki;
import fr.louisdevie.tatsuki.bitflags.LongBitFlagGraph;
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -7,17 +8,17 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class LongBitFlagSetTests {
@Test
public class LongBitFlagGraphTests {
/*@Test
public void empty() {
var flags = new LongBitFlagSet();
var flags = new LongBitFlagGraph();
assertEquals(0L, flags.empty());
}
@Test
public void union() {
final var flags = new LongBitFlagSet();
final var flags = new LongBitFlagGraph();
assertEquals(0L, flags.union(0L, 0L));
assertEquals(1L, flags.union(1L, 0L));
@@ -28,7 +29,7 @@ public class LongBitFlagSetTests {
@Test
public void difference() {
var flags = new LongBitFlagSet();
var flags = new LongBitFlagGraph();
assertEquals(0L, flags.difference(0L, 0L));
assertEquals(1L, flags.difference(1L, 0L));
@@ -39,7 +40,7 @@ public class LongBitFlagSetTests {
@Test
public void intersection() {
var flags = new LongBitFlagSet();
var flags = new LongBitFlagGraph();
assertEquals(0L, flags.intersection(0L, 0L));
assertEquals(0L, flags.intersection(1L, 0L));
@@ -51,7 +52,7 @@ public class LongBitFlagSetTests {
@Test
public void isSuperset() {
var flags = new LongBitFlagSet();
var flags = new LongBitFlagGraph();
assertTrue(flags.isSuperset(0L, 0L));
assertTrue(flags.isSuperset(3L, 0L));
@@ -63,7 +64,7 @@ public class LongBitFlagSetTests {
@Test
public void enumerate() {
var flags = new LongBitFlagSet();
var flags = new LongBitFlagGraph();
assertIterableEquals(List.of(), flags.enumerate(0L));
assertIterableEquals(List.of(1L), flags.enumerate(1L));
@@ -71,5 +72,5 @@ public class LongBitFlagSetTests {
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));
}
}*/
}

View File

@@ -0,0 +1,122 @@
package fr.louisdevie.tatsuki.bitflags;
import org.junit.jupiter.api.Test;
import java.util.List;
import static fr.louisdevie.tatsuki.testing.Assertions.assertYields;
import static fr.louisdevie.tatsuki.testing.Factory.big;
import static org.junit.jupiter.api.Assertions.*;
public class BitFlagsTests {
@Test
public void union() {
assertEquals(0, BitFlags.union(0, 0));
assertEquals(1, BitFlags.union(1, 0));
assertEquals(2, BitFlags.union(0, 2));
assertEquals(3, BitFlags.union(1, 2));
assertEquals(7, BitFlags.union(3, 6));
assertEquals(0L, BitFlags.union(0L, 0L));
assertEquals(1L, BitFlags.union(1L, 0L));
assertEquals(2L, BitFlags.union(0L, 2L));
assertEquals(3L, BitFlags.union(1L, 2L));
assertEquals(7L, BitFlags.union(3L, 6L));
assertEquals(big(0), BitFlags.union(big(0), big(0)));
assertEquals(big(1), BitFlags.union(big(1), big(0)));
assertEquals(big(2), BitFlags.union(big(0), big(2)));
assertEquals(big(3), BitFlags.union(big(1), big(2)));
assertEquals(big(7), BitFlags.union(big(3), big(6)));
}
@Test
public void difference() {
assertEquals(0, BitFlags.difference(0, 0));
assertEquals(1, BitFlags.difference(1, 0));
assertEquals(1, BitFlags.difference(3, 6));
assertEquals(4, BitFlags.difference(6, 3));
assertEquals(8, BitFlags.difference(8, 17));
assertEquals(0L, BitFlags.difference(0L, 0L));
assertEquals(1L, BitFlags.difference(1L, 0L));
assertEquals(1L, BitFlags.difference(3L, 6L));
assertEquals(4L, BitFlags.difference(6L, 3L));
assertEquals(8L, BitFlags.difference(8L, 17L));
assertEquals(big(0), BitFlags.difference(big(0), big(0)));
assertEquals(big(1), BitFlags.difference(big(1), big(0)));
assertEquals(big(1), BitFlags.difference(big(3), big(6)));
assertEquals(big(4), BitFlags.difference(big(6), big(3)));
assertEquals(big(8), BitFlags.difference(big(8), big(17)));
}
@Test
public void intersection() {
assertEquals(0, BitFlags.intersection(0, 0));
assertEquals(0, BitFlags.intersection(1, 0));
assertEquals(0, BitFlags.intersection(1, 2));
assertEquals(1, BitFlags.intersection(1, 3));
assertEquals(1, BitFlags.intersection(11, 5));
assertEquals(3, BitFlags.intersection(11, 7));
assertEquals(0L, BitFlags.intersection(0L, 0L));
assertEquals(0L, BitFlags.intersection(1L, 0L));
assertEquals(0L, BitFlags.intersection(1L, 2L));
assertEquals(1L, BitFlags.intersection(1L, 3L));
assertEquals(1L, BitFlags.intersection(11L, 5L));
assertEquals(3L, BitFlags.intersection(11L, 7L));
assertEquals(big(0), BitFlags.intersection(big(0), big(0)));
assertEquals(big(0), BitFlags.intersection(big(1), big(0)));
assertEquals(big(0), BitFlags.intersection(big(1), big(2)));
assertEquals(big(1), BitFlags.intersection(big(1), big(3)));
assertEquals(big(1), BitFlags.intersection(big(11), big(5)));
assertEquals(big(3), BitFlags.intersection(big(11), big(7)));
}
@Test
public void isSuperset() {
assertTrue(BitFlags.isSuperset(0, 0));
assertTrue(BitFlags.isSuperset(3, 0));
assertTrue(BitFlags.isSuperset(3, 1));
assertTrue(BitFlags.isSuperset(3, 3));
assertFalse(BitFlags.isSuperset(0, 3));
assertFalse(BitFlags.isSuperset(8, 4));
assertTrue(BitFlags.isSuperset(0L, 0L));
assertTrue(BitFlags.isSuperset(3L, 0L));
assertTrue(BitFlags.isSuperset(3L, 1L));
assertTrue(BitFlags.isSuperset(3L, 3L));
assertFalse(BitFlags.isSuperset(0L, 3L));
assertFalse(BitFlags.isSuperset(8L, 4L));
assertTrue(BitFlags.isSuperset(big(0), big(0)));
assertTrue(BitFlags.isSuperset(big(3), big(0)));
assertTrue(BitFlags.isSuperset(big(3), big(1)));
assertTrue(BitFlags.isSuperset(big(3), big(3)));
assertFalse(BitFlags.isSuperset(big(0), big(3)));
assertFalse(BitFlags.isSuperset(big(8), big(4)));
}
@Test
public void enumerate() {
assertYields(List.of(), BitFlags.enumerate(0));
assertYields(List.of(1), BitFlags.enumerate(1));
assertYields(List.of(2), BitFlags.enumerate(2));
assertYields(List.of(1, 2), BitFlags.enumerate(3));
assertYields(List.of(4, 32, 64), BitFlags.enumerate(100));
assertYields(List.of(), BitFlags.enumerate(0L));
assertYields(List.of(1L), BitFlags.enumerate(1L));
assertYields(List.of(2L), BitFlags.enumerate(2L));
assertYields(List.of(1L, 2L), BitFlags.enumerate(3L));
assertYields(List.of(4L, 32L, 64L), BitFlags.enumerate(100L));
assertYields(List.of(), BitFlags.enumerate(big(0)));
assertYields(List.of(big(1)), BitFlags.enumerate(big(1)));
assertYields(List.of(big(2)), BitFlags.enumerate(big(2)));
assertYields(List.of(big(1), big(2)), BitFlags.enumerate(big(3)));
assertYields(List.of(big(4), big(32), big(64)), BitFlags.enumerate(big(100)));
}
}

View File

@@ -0,0 +1,85 @@
package fr.louisdevie.tatsuki.collections;
import org.junit.jupiter.api.Test;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
public class CollectionFlagGraphTests {
@Test
public void named() {
final var flags = SetFlagGraph.<Integer>builder()
.defineValue(12).as("a")
.defineValue(45).as("b")
.defineValue(78).as("c")
.compose("a", "b").as("ab")
.build();
assertEquals(Set.of(), flags.named()) ;
assertEquals(Set.of(12), flags.named("a")) ;
assertEquals(Set.of(12, 45, 78), flags.named("ab", "c")) ;
}
@Test
public void hasAny() {
final var flags = SetFlagGraph.<Integer>builder()
.defineValue(12)
.defineValue(45).requiresValues(12)
.defineValue(78)
.build();
assertFalse(flags.hasAny(Set.of(), Set.of()));
assertFalse(flags.hasAny(Set.of(12, 45, 78), Set.of()));
assertTrue(flags.hasAny(Set.of(12, 45, 78), Set.of(12)));
assertTrue(flags.hasAny(Set.of(12), Set.of(12, 78)));
assertFalse(flags.hasAny(Set.of(45, 78), Set.of(45)));
}
@Test
public void hasAll() {
final var flags = SetFlagGraph.<Integer>builder()
.defineValue(12)
.defineValue(45).requiresValues(12)
.defineValue(78)
.build();
assertTrue(flags.hasAll(Set.of(), Set.of()));
assertTrue(flags.hasAll(Set.of(12, 45, 78), Set.of()));
assertTrue(flags.hasAll(Set.of(12, 45, 78), Set.of(12)));
assertFalse(flags.hasAll(Set.of(12), Set.of(12, 78)));
assertFalse(flags.hasAll(Set.of(45, 78), Set.of(45)));
}
@Test
public void maximum() {
final var flags = SetFlagGraph.<Integer>builder()
.defineValue(12)
.defineValue(45).requiresValues(12)
.defineValue(78).requiresValues(45)
.build();
assertEquals(Set.of(), flags.maximum(Set.of())) ;
assertEquals(Set.of(12), flags.maximum(Set.of(12))) ;
assertEquals(Set.of(12, 45), flags.maximum(Set.of(45))) ;
assertEquals(Set.of(12, 45, 78), flags.maximum(Set.of(78))) ;
assertEquals(Set.of(), flags.maximum(Set.of(99))) ;
}
@Test
public void minimum() {
final var flags = SetFlagGraph.<Integer>builder()
.defineValue(12)
.defineValue(45).requiresValues(12)
.defineValue(78).requiresValues(45)
.build();
assertEquals(Set.of(), flags.minimum(Set.of())) ;
assertEquals(Set.of(12), flags.minimum(Set.of(12))) ;
assertEquals(Set.of(), flags.minimum(Set.of(45))) ;
assertEquals(Set.of(12, 45), flags.minimum(Set.of(12, 45))) ;
assertEquals(Set.of(12), flags.minimum(Set.of(12, 78))) ;
assertEquals(Set.of(12, 45, 78), flags.minimum(Set.of(12, 45, 78))) ;
assertEquals(Set.of(), flags.minimum(Set.of(99))) ;
}
}

View File

@@ -0,0 +1,61 @@
package fr.louisdevie.tatsuki.testing;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.fail;
public class Assertions {
public static void assertYields(List<?> expected, Iterable<?> actual) {
Queue<?> remaining = new ArrayDeque<>(expected);
for (Object actualElement : actual) {
if (remaining.isEmpty()) {
fail(String.format("Iterable yielded <%s> (no more elements were expected)", actualElement.toString()));
}
Object expectedElement = remaining.poll();
if (!expectedElement.equals(actualElement)) {
fail(String.format("Iterable yielded <%s> (expected <%s>)", actualElement.toString(), expectedElement.toString()));
}
}
if (!remaining.isEmpty()) {
fail(
"Iterable did not yield " +
remaining.stream()
.map(obj -> String.format("<%s>", obj.toString()))
.collect(Collectors.joining(", "))
);
}
}
public static void assertYieldsUnordered(List<?> expected, Iterable<?> actual) {
List<?> remaining = new ArrayList<>(expected);
for (Object actualElement : actual) {
int i = remaining.indexOf(actualElement);
if (i == -1) {
String expectation;
if (remaining.isEmpty()) {
expectation = "no more elements were expected";
} else {
expectation = "expected one of " +
remaining.stream()
.map(obj -> String.format("<%s>", obj.toString()))
.collect(Collectors.joining(", "));
}
fail(String.format("Iterable yielded <%s> (%s)", actualElement.toString(), expectation));
} else {
remaining.remove(i);
}
}
if (!remaining.isEmpty()) {
fail(
"Iterable did not yield " +
remaining.stream()
.map(obj -> String.format("<%s>", obj.toString()))
.collect(Collectors.joining(", "))
);
}
}
}

View File

@@ -0,0 +1,9 @@
package fr.louisdevie.tatsuki.testing;
import java.math.BigInteger;
public class Factory {
public static BigInteger big(int value) {
return BigInteger.valueOf(value);
}
}