Files
paradigms/java/queue/Queues.java
2026-04-08 21:25:17 +03:00

491 lines
14 KiB
Java

package queue;
import base.ExtendedRandom;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
@SuppressWarnings("UnusedReturnValue")
public final class Queues {
private Queues() {}
protected interface QueueModel {
@ReflectionTest.Ignore
ArrayDeque<Object> model();
default Object dequeue() {
return model().removeFirst();
}
default int size() {
return model().size();
}
default boolean isEmpty() {
return model().isEmpty();
}
default void clear() {
model().clear();
}
default void enqueue(final Object element) {
model().addLast(element);
}
default Object element() {
return model().getFirst();
}
}
protected interface QueueChecker<M extends QueueModel> {
M wrap(ArrayDeque<Object> reference);
default List<M> linearTest(final M queue, final ExtendedRandom random) {
// Do nothing by default
return List.of();
}
default void check(final M queue, final ExtendedRandom random) {
queue.element();
}
default void add(
final M queue,
final Object element,
final ExtendedRandom random
) {
queue.enqueue(element);
}
default Object randomElement(final ExtendedRandom random) {
return ArrayQueueTester
.ELEMENTS[random.nextInt(ArrayQueueTester.ELEMENTS.length)];
}
default void remove(final M queue, final ExtendedRandom random) {
queue.dequeue();
}
@SuppressWarnings("unchecked")
default M cast(final QueueModel model) {
return (M) model;
}
}
@FunctionalInterface
protected interface Splitter<M extends QueueModel> {
List<M> split(
final QueueChecker<? extends M> tester,
final M queue,
final ExtendedRandom random
);
}
@FunctionalInterface
/* package-private */ interface LinearTester<
M extends QueueModel
> extends Splitter<M> {
void test(
final QueueChecker<? extends M> tester,
final M queue,
final ExtendedRandom random
);
@Override
default List<M> split(
final QueueChecker<? extends M> tester,
final M queue,
final ExtendedRandom random
) {
test(tester, queue, random);
return List.of();
}
}
// === 3637 (ArrayQueue)
/* package-private */ interface CountModel extends ReflectionModel {
default int count(final Object element) {
return reduce(0, element, (v, i) -> v + 1);
}
}
/* package-private */ static final Queues.LinearTester<CountModel> COUNT = (
tester,
queue,
random
) -> queue.count(tester.randomElement(random));
/* package-private */ interface DequeCountModel
extends DequeModel, CountModel {}
/* package-private */ static final Queues.LinearTester<
DequeCountModel
> DEQUE_COUNT = COUNT::test;
// === 3839 (ArrayQueue)
/* package-private */ interface CountIfModel extends ReflectionModel {
default int countIf(final Predicate<Object> p) {
return reduce(0, p, (v, i) -> v + 1);
}
}
/* package-private */ static final Queues.LinearTester<
CountIfModel
> COUNT_IF = (t, q, r) -> q.countIf(randomPredicate(t, r));
/* package-private */ interface DequeCountIfModel
extends DequeModel, CountIfModel {}
/* package-private */ static final Queues.LinearTester<
DequeCountIfModel
> DEQUE_COUNT_IF = COUNT_IF::test;
record NamedPredicate<T>(
String name,
Predicate<? super T> predicate
) implements Predicate<T> {
@Override
public String toString() {
return name;
}
@Override
public boolean test(final T t) {
return predicate.test(t);
}
}
/* package-private */ static Predicate<Object> randomPredicate(
final Queues.QueueChecker<? extends Queues.QueueModel> tester,
final ExtendedRandom random
) {
final Object item = tester.randomElement(random);
return random
.<Supplier<Predicate<Object>>>randomItem(
() -> new NamedPredicate<>("item == ", o -> item == o),
() -> new NamedPredicate<>(item + ".equals", item::equals),
() -> new NamedPredicate<>("null == ", Objects::isNull)
)
.get();
}
@SafeVarargs
private static <M extends Queues.QueueModel> Queues.LinearTester<
M
> randomOf(final Queues.LinearTester<? super M>... variants) {
return (tester, queue, random) ->
random.randomItem(variants).test(tester, queue, random);
}
// === 3435 (ArrayQueue)
/* package-private */
interface IndexIfModel extends ReflectionModel {
default int indexIf(final Predicate<Object> p) {
return reduce(-1, p, (v, i) -> v == -1 ? i : v);
}
default int lastIndexIf(final Predicate<Object> p) {
return reduce(-1, p, (v, i) -> i);
}
}
/* package-private */ static final Queues.LinearTester<
IndexIfModel
> INDEX_IF = randomOf(
(t, q, r) -> q.indexIf(randomPredicate(t, r)),
(t, q, r) -> q.lastIndexIf(randomPredicate(t, r))
);
// === 3233 (ArrayQueue)
/* package-private */
interface IndexModel extends ReflectionModel {
default int indexOf(final Object element) {
return reduce(-1, element, (v, i) -> v == -1 ? i : v);
}
default int lastIndexOf(final Object element) {
return reduce(-1, element, (v, i) -> i);
}
}
/* package-private */ static final Queues.LinearTester<IndexModel> INDEX =
randomOf(
(t, q, r) -> q.indexOf(t.randomElement(r)),
(t, q, r) -> q.lastIndexOf(t.randomElement(r))
);
// === 3637 (Queues)
/* package-private */ interface ContainsModel extends Queues.QueueModel {
default boolean contains(final Object element) {
return model().contains(element);
}
@SuppressWarnings("UnusedReturnValue")
default boolean removeFirst(final Object element) {
return model().removeFirstOccurrence(element);
}
}
/* package-private */ static final Queues.LinearTester<
ContainsModel
> CONTAINS = (tester, queue, random) -> {
final Object element = random.nextBoolean()
? tester.randomElement(random)
: random.nextInt();
if (random.nextBoolean()) {
queue.contains(element);
} else {
queue.removeFirst(element);
}
};
// === 3839 (Queues)
/* package-private */ interface NthModel extends Queues.QueueModel {
// Deliberately ugly implementation
@ReflectionTest.Wrap
default NthModel getNth(final int n) {
final ArrayDeque<Object> deque = new ArrayDeque<>();
final int[] index = { 0 };
model().forEach(e -> {
if (++index[0] % n == 0) {
deque.add(e);
}
});
return () -> deque;
}
// Deliberately ugly implementation
@ReflectionTest.Wrap
default NthModel removeNth(final int n) {
final ArrayDeque<Object> deque = new ArrayDeque<>();
final int[] index = { 0 };
model().removeIf(e -> {
if (++index[0] % n == 0) {
deque.add(e);
return true;
} else {
return false;
}
});
return () -> deque;
}
default void dropNth(final int n) {
final int[] index = { 0 };
model().removeIf(e -> ++index[0] % n == 0);
}
}
/* package-private */ static final Queues.Splitter<NthModel> NTH = (
tester,
queue,
random
) -> {
final int n = random.nextInt(5) + 1;
return switch (random.nextInt(3)) {
case 0 -> {
final NthModel model = queue.removeNth(n);
yield List.of(tester.cast(model));
}
case 1 -> {
queue.dropNth(n);
yield List.of();
}
case 2 -> List.of(tester.cast(queue.getNth(n)));
default -> throw new AssertionError();
};
};
// === 3435 (Queues)
/* package-private */ interface RemoveIfModel extends Queues.QueueModel {
default void removeIf(final Predicate<Object> p) {
model().removeIf(p);
}
default void retainIf(final Predicate<Object> p) {
model().removeIf(Predicate.not(p));
}
}
private static final Queues.LinearTester<RemoveIfModel> REMOVE_IF = (
tester,
queue,
random
) -> queue.removeIf(randomPredicate(tester, random));
private static final Queues.LinearTester<RemoveIfModel> RETAIN_IF = (
tester,
queue,
random
) -> queue.retainIf(randomPredicate(tester, random));
/* package-private */ static final Queues.LinearTester<
RemoveIfModel
> REMOVE_IF_TEST = randomOf(REMOVE_IF, RETAIN_IF);
// 3233 (Queues)
/* package-private */ interface RemoveEqModel extends Queues.QueueModel {
default void removeAll(final Object element) {
model().removeIf(Predicate.isEqual(element));
}
default void retainAll(final Object element) {
model().removeIf(Predicate.not(Predicate.isEqual(element)));
}
}
private static final Queues.LinearTester<RemoveEqModel> REMOVE_ALL = (
tester,
queue,
random
) -> queue.removeAll(tester.randomElement(random));
private static final Queues.LinearTester<RemoveEqModel> RETAIL_ALL = (
tester,
queue,
random
) -> queue.retainAll(tester.randomElement(random));
/* package-private */ static final Queues.LinearTester<
RemoveEqModel
> REMOVE_EQ_TEST = randomOf(REMOVE_ALL, RETAIL_ALL);
// === Reflection
/* package-private */ interface ReflectionModel extends QueueModel {
Field ELEMENTS = getField("elements");
Field HEAD = getField("head");
@SuppressWarnings("unchecked")
private <Z> Z get(final Field field) {
try {
return (Z) field.get(model());
} catch (final IllegalAccessException e) {
throw new AssertionError(
"Cannot access field " +
field.getName() +
": " +
e.getMessage(),
e
);
}
}
private static Field getField(final String name) {
try {
final Field field = ArrayDeque.class.getDeclaredField(name);
field.setAccessible(true);
return field;
} catch (final NoSuchFieldException e) {
throw new AssertionError(
"Reflection error: " + e.getMessage(),
e
);
}
}
@ReflectionTest.Ignore
default int head() {
return get(HEAD);
}
@ReflectionTest.Ignore
default Object[] elements() {
return get(ELEMENTS);
}
@ReflectionTest.Ignore
default <R> R reduce(
final R zero,
final Predicate<Object> p,
final BiFunction<R, Integer, R> f
) {
final int size = size();
final Object[] elements = elements();
final int head = head();
R result = zero;
for (int i = 0; i < size; i++) {
if (p.test(elements[(head + i) % elements.length])) {
result = f.apply(result, i);
}
}
return result;
}
@ReflectionTest.Ignore
@SuppressWarnings("unused")
default <R> R reduce(
final R zero,
final Object v,
final BiFunction<R, Integer, R> f
) {
return reduce(zero, o -> Objects.equals(v, o), f);
}
}
// === Deque
/* package-private */ interface DequeModel extends QueueModel {
default void push(final Object element) {
model().addFirst(element);
}
@SuppressWarnings("UnusedReturnValue")
default Object peek() {
return model().getLast();
}
default Object remove() {
return model().removeLast();
}
}
/* package-private */ interface DequeChecker<
M extends DequeModel
> extends QueueChecker<M> {
@Override
default void add(
final M queue,
final Object element,
final ExtendedRandom random
) {
if (random.nextBoolean()) {
QueueChecker.super.add(queue, element, random);
} else {
queue.push(element);
}
}
@Override
default void check(final M queue, final ExtendedRandom random) {
if (random.nextBoolean()) {
QueueChecker.super.check(queue, random);
} else {
queue.peek();
}
}
@Override
default void remove(final M queue, final ExtendedRandom random) {
if (random.nextBoolean()) {
QueueChecker.super.remove(queue, random);
} else {
queue.remove();
}
}
}
}