first commit

This commit is contained in:
me
2026-04-08 21:25:17 +03:00
parent 3681b8eccd
commit 371b14c5e3
173 changed files with 14126 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
package queue;
import java.util.function.Predicate;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public abstract class AbstractQueue implements Queue {
protected int size;
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void clear() {
clearImpl();
size = 0;
}
@Override
public int count(Object element) {
assert element != null : "Element must not be null";
int cnt = 0;
for (int i = 0; i < size; i++) {
if (element.equals(get(i))) cnt++;
}
return cnt;
}
@Override
public int countIf(Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
int cnt = 0;
for (int i = 0; i < size; i++) {
if (predicate.test(get(i))) cnt++;
}
return cnt;
}
@Override
public int indexIf(Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
for (int i = 0; i < size; i++) {
if (predicate.test(get(i))) return i;
}
return -1;
}
@Override
public int lastIndexIf(Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
for (int i = size - 1; i >= 0; i--) {
if (predicate.test(get(i))) return i;
}
return -1;
}
@Override
public int indexOf(Object element) {
assert element != null : "Predicate must not be null";
for (int i = 0; i < size; i++) {
if (get(i).equals(element)) return i;
}
return -1;
}
@Override
public int lastIndexOf(Object element) {
assert element != null : "Predicate must not be null";
for (int i = size - 1; i >= 0; i--) {
if (get(i).equals(element)) return i;
}
return -1;
}
@Override
public boolean contains(Object element) {
assert element != null : "Element must not be null";
return indexOf(element) != -1;
}
@Override
public boolean removeFirst(Object element) {
assert element != null : "Element must not be null";
int originalSize = size;
boolean found = false;
for (int i = 0; i < originalSize; i++) {
Object elem = dequeue();
if (!found && element.equals(elem)) {
found = true;
} else {
enqueue(elem);
}
}
return found;
}
@Override
public Queue getNth(int n) {
Queue result = createEmpty();
for (int i = 0; i < size; i++) {
Object elem = get(i);
if ((i + 1) % n == 0) result.enqueue(elem);
}
return result;
}
@Override
public Queue removeNth(int n) {
Queue result = createEmpty();
int originalSize = size;
for (int i = 0; i < originalSize; i++) {
if ((i + 1) % n == 0) result.enqueue(get(i));
}
Queue temp = createEmpty();
for (int i = 0; i < originalSize; i++) {
Object elem = dequeue();
if ((i + 1) % n != 0) temp.enqueue(elem);
}
while (!temp.isEmpty()) enqueue(temp.dequeue());
return result;
}
@Override
public void dropNth(int n) {
int originalSize = size;
Queue temp = createEmpty();
for (int i = 0; i < originalSize; i++) {
Object elem = dequeue();
if ((i + 1) % n != 0) temp.enqueue(elem);
}
while (!temp.isEmpty()) enqueue(temp.dequeue());
}
@Override
public void removeIf(java.util.function.Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
int originalSize = size;
for (int i = 0; i < originalSize; i++) {
Object elem = dequeue();
if (!predicate.test(elem)) enqueue(elem);
}
}
@Override
public void retainIf(java.util.function.Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
removeIf(predicate.negate());
}
@Override
public void removeAll(Object element) {
assert element != null : "Element must not be null";
removeIf(element::equals);
}
@Override
public void retainAll(Object element) {
assert element != null : "Element must not be null";
removeIf(e -> !element.equals(e));
}
protected abstract AbstractQueue createEmpty();
protected abstract Object get(int i);
protected abstract void clearImpl();
}

View File

@@ -0,0 +1,90 @@
package queue;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class ArrayQueue extends AbstractQueue {
private Object[] queue;
private int head;
public ArrayQueue() {
queue = new Object[4];
head = 0;
}
@Override
public void enqueue(Object element) {
assert element != null : "Element must not be null";
ensureCapacity();
queue[(head + size) % queue.length] = element;
size++;
}
@Override
public void push(Object element) {
assert element != null : "Element must not be null";
ensureCapacity();
head = (head - 1 + queue.length) % queue.length;
queue[head] = element;
size++;
}
@Override
public Object element() {
assert size > 0 : "Queue is empty";
return queue[head];
}
@Override
public Object peek() {
assert size > 0 : "Queue is empty";
return queue[(head + size - 1) % queue.length];
}
@Override
public Object dequeue() {
assert size > 0 : "Queue is empty";
Object result = queue[head];
queue[head] = null;
head = (head + 1) % queue.length;
size--;
return result;
}
@Override
public Object remove() {
assert size > 0 : "Queue is empty";
int tail = (head + size - 1) % queue.length;
Object result = queue[tail];
queue[tail] = null;
size--;
return result;
}
@Override
protected Object get(int i) {
return queue[(head + i) % queue.length];
}
@Override
protected void clearImpl() {
queue = new Object[4];
head = 0;
}
@Override
protected AbstractQueue createEmpty() {
return new ArrayQueue();
}
private void ensureCapacity() {
if (size < queue.length) return;
Object[] newQueue = new Object[queue.length * 2];
for (int i = 0; i < size; i++) {
newQueue[i] = queue[(head + i) % queue.length];
}
queue = newQueue;
head = 0;
}
}

View File

@@ -0,0 +1,150 @@
package queue;
import java.util.function.Predicate;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class ArrayQueueADT {
private Object[] queue;
private int head;
private int size;
public ArrayQueueADT() {
queue = new Object[4];
head = 0;
size = 0;
}
public static ArrayQueueADT create() {
return new ArrayQueueADT();
}
public static void enqueue(ArrayQueueADT q, Object element) {
assert q != null && element != null : "Queue and element must not be null";
ensureCapacity(q);
q.queue[(q.head + q.size) % q.queue.length] = element;
q.size++;
}
public static void push(ArrayQueueADT q, Object element) {
assert q != null && element != null : "Queue and element must not be null";
ensureCapacity(q);
q.head = (q.head - 1 + q.queue.length) % q.queue.length;
q.queue[q.head] = element;
q.size++;
}
public static Object element(ArrayQueueADT q) {
assert q != null && q.size > 0 : "Queue must not be null and not be empty";
return q.queue[q.head];
}
public static Object peek(ArrayQueueADT q) {
assert q != null && q.size > 0 : "Queue must not be null and not be empty";
return q.queue[(q.head + q.size - 1) % q.queue.length];
}
public static Object dequeue(ArrayQueueADT q) {
assert q != null && q.size > 0 : "Queue must not be null and not be empty";
Object result = q.queue[q.head];
q.queue[q.head] = null;
q.head = (q.head + 1) % q.queue.length;
q.size--;
return result;
}
public static Object remove(ArrayQueueADT q) {
assert q != null && q.size > 0 : "Queue must not be null and not be empty";
int tail = (q.head + q.size - 1) % q.queue.length;
Object result = q.queue[tail];
q.queue[tail] = null;
q.size--;
return result;
}
public static int size(ArrayQueueADT q) {
assert q != null : "Queue must not be null";
return q.size;
}
public static boolean isEmpty(ArrayQueueADT q) {
assert q != null : "Queue must not be null";
return q.size == 0;
}
public static void clear(ArrayQueueADT q) {
assert q != null : "Queue must not be null";
q.queue = new Object[4];
q.head = 0;
q.size = 0;
}
public static int count(ArrayQueueADT q, Object element) {
assert q != null && element != null : "Queue and element must not be null";
int cnt = 0;
for (int i = 0; i < q.size; i++) {
if (element.equals(q.queue[(q.head + i) % q.queue.length])) {
cnt++;
}
}
return cnt;
}
public static int countIf(ArrayQueueADT q, Predicate<Object> predicate) {
assert q != null && predicate != null : "Queue and predicate must not be null";
int cnt = 0;
for (int i = 0; i < q.size; i++) {
if (predicate.test(q.queue[(q.head + i) % q.queue.length])) {
cnt++;
}
}
return cnt;
}
public static int indexIf(ArrayQueueADT q, Predicate<Object> predicate) {
assert q != null && predicate != null : "Queue and predicate must not be null";
for (int i = 0; i < q.size; i++) {
if (predicate.test(q.queue[(q.head + i) % q.queue.length])) {
return i;
}
}
return -1;
}
public static int lastIndexIf(ArrayQueueADT q, Predicate<Object> predicate) {
assert q != null && predicate != null : "Queue and predicate must not be null";
for (int i = q.size - 1; i >= 0; i--) {
if (predicate.test(q.queue[(q.head + i) % q.queue.length])) {
return i;
}
}
return -1;
}
public static int indexOf(ArrayQueueADT q, Object element) {
assert element != null : "Predicate must not be null";
for (int i = 0; i < q.size; i++) {
if (q.queue[(q.head + i) % q.queue.length].equals(element)) return i;
}
return -1;
}
public static int lastIndexOf(ArrayQueueADT q, Object element) {
assert element != null : "Predicate must not be null";
for (int i = q.size - 1; i >= 0; i--) {
if (q.queue[(q.head + i) % q.queue.length].equals(element)) return i;
}
return -1;
}
private static void ensureCapacity(ArrayQueueADT q) {
if (q.size < q.queue.length) return;
Object[] newQueue = new Object[q.queue.length * 2];
for (int i = 0; i < q.size; i++) {
newQueue[i] = q.queue[(q.head + i) % q.queue.length];
}
q.queue = newQueue;
q.head = 0;
}
}

View File

@@ -0,0 +1,137 @@
package queue;
import java.util.function.Predicate;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class ArrayQueueModule {
private static Object[] queue = new Object[4];
private static int head = 0;
private static int size = 0;
public static void enqueue(Object element) {
assert element != null : "Element must not be null";
ensureCapacity();
queue[(head + size) % queue.length] = element;
size++;
}
public static void push(Object element) {
assert element != null : "Element must not be null";
ensureCapacity();
head = (head - 1 + queue.length) % queue.length;
queue[head] = element;
size++;
}
public static Object element() {
assert size > 0 : "Queue is empty";
return queue[head];
}
public static Object peek() {
assert size > 0 : "Queue is empty";
return queue[(head + size - 1) % queue.length];
}
public static Object dequeue() {
assert size > 0 : "Queue is empty";
Object result = queue[head];
queue[head] = null;
head = (head + 1) % queue.length;
size--;
return result;
}
public static Object remove() {
assert size > 0 : "Queue is empty";
int tail = (head + size - 1) % queue.length;
Object result = queue[tail];
queue[tail] = null;
size--;
return result;
}
public static int size() {
return size;
}
public static boolean isEmpty() {
return size == 0;
}
public static void clear() {
queue = new Object[4];
head = 0;
size = 0;
}
public static int count(Object element) {
assert element != null : "Element must not be null";
int cnt = 0;
for (int i = 0; i < size; i++) {
if (element.equals(queue[(head + i) % queue.length])) {
cnt++;
}
}
return cnt;
}
public static int countIf(Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
int cnt = 0;
for (int i = 0; i < size; i++) {
if (predicate.test(queue[(head + i) % queue.length])) {
cnt++;
}
}
return cnt;
}
public static int indexIf(Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
for (int i = 0; i < size; i++) {
if (predicate.test(queue[(head + i) % queue.length])) {
return i;
}
}
return -1;
}
public static int lastIndexIf(Predicate<Object> predicate) {
assert predicate != null : "Predicate must not be null";
for (int i = size - 1; i >= 0; i--) {
if (predicate.test(queue[(head + i) % queue.length])) {
return i;
}
}
return -1;
}
public static int indexOf(Object element) {
assert element != null : "Predicate must not be null";
for (int i = 0; i < size; i++) {
if (queue[(head + i) % queue.length].equals(element)) return i;
}
return -1;
}
public static int lastIndexOf(Object element) {
assert element != null : "Predicate must not be null";
for (int i = size - 1; i >= 0; i--) {
if (queue[(head + i) % queue.length].equals(element)) return i;
}
return -1;
}
private static void ensureCapacity() {
if (size < queue.length) return;
Object[] newQueue = new Object[queue.length * 2];
for (int i = 0; i < size; i++) {
newQueue[i] = queue[(head + i) % queue.length];
}
queue = newQueue;
head = 0;
}
}

View File

@@ -0,0 +1,46 @@
package queue;
import base.Selector;
import base.TestCounter;
import java.util.List;
import java.util.function.Consumer;
import static queue.Queues.*;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class ArrayQueueTest {
public static final Selector SELECTOR = new Selector(ArrayQueueTest.class)
.variant("Base", variant(QueueModel.class, d -> () -> d))
.variant("3637", variant(DequeCountModel.class, (DequeChecker<DequeCountModel>) d -> () -> d, DEQUE_COUNT))
.variant("3839", variant(DequeCountIfModel.class, (DequeChecker<DequeCountIfModel>) d -> () -> d, DEQUE_COUNT_IF))
.variant("3435", variant(IndexIfModel.class, d -> () -> d, INDEX_IF))
.variant("3233", variant(IndexModel.class, d -> () -> d, INDEX))
;
private ArrayQueueTest() {
}
public static void main(final String... args) {
SELECTOR.main(args);
}
/* package-private */
static <M extends QueueModel> Consumer<TestCounter> variant(
final Class<M> type,
final QueueChecker<M> tester,
final Splitter<M> splitter
) {
return new ArrayQueueTester<>(type, tester, splitter)::test;
}
/* package-private */
static <M extends QueueModel> Consumer<TestCounter> variant(
final Class<M> type,
final QueueChecker<M> tester
) {
return variant(type, tester, (t, q, r) -> List.of());
}
}

View File

@@ -0,0 +1,224 @@
package queue;
import base.Asserts;
import base.ExtendedRandom;
import base.TestCounter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ArrayQueueTester<M extends Queues.QueueModel> {
private static final int OPERATIONS = 50_000;
/* package-private */ static final Object[] ELEMENTS = new Object[]{
"Hello",
"world",
1, 2, 3,
List.of("a"),
List.of("a"),
List.of("b"),
Map.of()
};
private final Class<M> model;
private final Queues.QueueChecker<M> tester;
protected ArrayQueueTester(final Class<M> model, final Queues.QueueChecker<M> tester, final Queues.Splitter<M> splitter) {
this.model = model;
this.tester = new Queues.QueueChecker<>() {
@Override
public M wrap(final ArrayDeque<Object> reference) {
return tester.wrap(reference);
}
@Override
public void check(final M queue, final ExtendedRandom random) {
tester.check(queue, random);
}
@Override
public void add(final M queue, final Object element, final ExtendedRandom random) {
tester.add(queue, element, random);
}
@Override
public void remove(final M queue, final ExtendedRandom random) {
tester.remove(queue, random);
}
@Override
public List<M> linearTest(final M queue, final ExtendedRandom random) {
if (random.nextInt(50) == 0) {
queue.clear();
return List.of();
} else {
return splitter.split(this, queue, random);
}
}
};
}
protected void test(final TestCounter counter) {
test(counter, "ArrayQueue", ReflectionTest.Mode.values());
}
protected void test(final TestCounter counter, final String className, final ReflectionTest.Mode... modes) {
for (final ReflectionTest.Mode mode : modes) {
final String scope = "Running %s for %s in %s mode%n"
.formatted(model.getEnclosingClass().getSimpleName(), className, mode);
counter.scope(scope, () -> new Variant(counter).test(className, mode));
}
}
protected void checkImplementation(final Class<?> implementation) {
// Do nothing by default
}
private static List<Object> toList(final Queues.QueueModel queue) {
final List<Object> list = Stream.generate(queue::dequeue).limit(queue.size()).toList();
list.forEach(queue::enqueue);
return list;
}
protected static ArrayDeque<Object> collect(final Stream<Object> elements) {
return elements.collect(Collectors.toCollection(ArrayDeque::new));
}
private class Variant extends ReflectionTest {
private final TestCounter counter;
public Variant(final TestCounter counter) {
this.counter = counter;
}
protected void testEmpty(final M queue) {
counter.scope("testEmpty", () -> assertSize(0, queue));
}
protected void testSingleton(final M queue) {
counter.scope("testSingleton", () -> {
assertSize(0, queue);
final String value = "value";
queue.enqueue(value);
assertSize(1, queue);
Asserts.assertEquals("element()", value, queue.element());
Asserts.assertEquals("dequeue()", value, queue.dequeue());
assertSize(0, queue);
});
}
protected void testClear(final M queue) {
counter.scope("testClear", () -> {
assertSize(0, queue);
final String value = "value";
queue.enqueue(value);
queue.enqueue(value);
queue.clear();
assertSize(0, queue);
final String value1 = "value1";
queue.enqueue(value1);
Asserts.assertEquals("deque()", value1, queue.dequeue());
});
}
private int checkAndSize(final M queue) {
final int size = queue.size();
if (!queue.isEmpty() && random().nextBoolean()) {
tester.check(queue, random());
}
return size;
}
protected Object randomElement() {
return ELEMENTS[random().nextInt(ELEMENTS.length)];
}
protected void assertSize(final int size, final M queue) {
counter.test(() -> {
Asserts.assertEquals("size()", size, queue.size());
Asserts.assertTrue("Expected isEmpty() " + (size == 0) + ", found " + queue.isEmpty(), (size == 0) == queue.isEmpty());
});
}
@Override
protected void checkResult(final String call, final Object expected, final Object actual) {
if (expected instanceof Queues.QueueModel model) {
super.checkResult(call, toList(model), toList((Queues.QueueModel) actual));
} else {
super.checkResult(call, expected, actual);
}
}
protected Supplier<M> factory(final String name, final Mode mode) {
final ProxyFactory<M> factory = new ProxyFactory<>(model, mode, "queue." + name);
checkImplementation(factory.implementation);
return () -> checking(counter, model, tester.wrap(new ArrayDeque<>()), factory.create());
}
private void test(final String className, final Mode mode) {
final Supplier<M> factory = factory(className, mode);
testEmpty(factory.get());
testSingleton(factory.get());
testClear(factory.get());
for (int i = 0; i <= 10; i += 2) {
testRandom(factory.get(), (double) i / 10);
}
}
private void testRandom(final M initial, final double addFreq) {
counter.scope("testRandom, add frequency = " + addFreq, () -> {
final List<M> queues = new ArrayList<>();
queues.add(initial);
int ops = 0;
for (int i = 0; i < OPERATIONS / TestCounter.DENOMINATOR / TestCounter.DENOMINATOR; i++) {
final M queue = queues.get(random().nextInt(queues.size()));
final int size = counter.testV(() -> {
if (queue.isEmpty() || random().nextDouble() < addFreq) {
tester.add(queue, randomElement(), random());
} else {
tester.remove(queue, random());
}
return checkAndSize(queue);
});
if (ops++ >= size && random().nextInt(4) == 0) {
ops -= size;
counter.test(() -> {
queues.addAll(tester.linearTest(queue, random()));
checkAndSize(queue);
});
}
}
for (final M queue : queues) {
counter.test(() -> {
tester.linearTest(queue, random());
checkAndSize(queue);
for (int i = queue.size(); i > 0; i--) {
tester.remove(queue, random());
checkAndSize(queue);
}
});
}
});
}
private ExtendedRandom random() {
return counter.random();
}
}
}

114
java/queue/LinkedQueue.java Normal file
View File

@@ -0,0 +1,114 @@
package queue;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public class LinkedQueue extends AbstractQueue {
private Node head;
private Node tail;
private static class Node {
Object value;
Node next;
Node prev;
Node(Object value) {
this.value = value;
}
}
public LinkedQueue() {
head = null;
tail = null;
}
@Override
public void enqueue(Object element) {
assert element != null : "Element must not be null";
Node node = new Node(element);
if (tail == null) {
head = tail = node;
} else {
node.prev = tail;
tail.next = node;
tail = node;
}
size++;
}
@Override
public void push(Object element) {
assert element != null : "Element must not be null";
Node node = new Node(element);
if (head == null) {
head = tail = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
size++;
}
@Override
public Object element() {
assert size > 0 : "Queue is empty";
return head.value;
}
@Override
public Object peek() {
assert size > 0 : "Queue is empty";
return tail.value;
}
@Override
public Object dequeue() {
assert size > 0 : "Queue is empty";
Object result = head.value;
head = head.next;
if (head != null) {
head.prev = null;
} else {
tail = null;
}
size--;
return result;
}
@Override
public Object remove() {
assert size > 0 : "Queue is empty";
Object result = tail.value;
tail = tail.prev;
if (tail != null) {
tail.next = null;
} else {
head = null;
}
size--;
return result;
}
@Override
protected Object get(int i) {
Node cur = head;
for (int j = 0; j < i; j++) {
cur = cur.next;
}
return cur.value;
}
@Override
protected void clearImpl() {
head = null;
tail = null;
}
@Override
protected AbstractQueue createEmpty() {
return new LinkedQueue();
}
}

56
java/queue/Queue.java Normal file
View File

@@ -0,0 +1,56 @@
package queue;
import java.util.function.Predicate;
/**
* @author Doschennikov Nikita (me@fymio.us)
*/
public interface Queue {
void enqueue(Object element);
void push(Object element);
Object element();
Object peek();
Object dequeue();
Object remove();
int size();
boolean isEmpty();
void clear();
int count(Object element);
int countIf(Predicate<Object> predicate);
int indexIf(Predicate<Object> predicate);
int lastIndexIf(Predicate<Object> predicate);
int indexOf(Object element);
int lastIndexOf(Object element);
boolean contains(Object element);
boolean removeFirst(Object element);
Queue getNth(int n);
Queue removeNth(int n);
void dropNth(int n);
void removeIf(java.util.function.Predicate<Object> predicate);
void retainIf(java.util.function.Predicate<Object> predicate);
void removeAll(Object element);
void retainAll(Object element);
}

51
java/queue/QueueTest.java Normal file
View File

@@ -0,0 +1,51 @@
package queue;
import static queue.Queues.*;
import base.Selector;
import base.TestCounter;
import java.util.List;
import java.util.function.Consumer;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class QueueTest {
public static final Selector SELECTOR = new Selector(QueueTest.class)
.variant("Base", variant(QueueModel.class, d -> () -> d))
.variant("3637", variant(ContainsModel.class, d -> () -> d, CONTAINS))
.variant("3839", variant(NthModel.class, d -> () -> d, NTH))
.variant(
"3435",
variant(RemoveIfModel.class, d -> () -> d, REMOVE_IF_TEST)
)
.variant(
"3233",
variant(RemoveEqModel.class, d -> () -> d, REMOVE_EQ_TEST)
);
private QueueTest() {}
public static void main(final String... args) {
SELECTOR.main(args);
}
/* package-private */ static <
M extends QueueModel,
T extends QueueChecker<M>
> Consumer<TestCounter> variant(
final Class<M> type,
final T tester,
final Queues.Splitter<M> splitter
) {
return new QueueTester<>(type, tester, splitter)::test;
}
/* package-private */ static <
M extends QueueModel,
T extends QueueChecker<M>
> Consumer<TestCounter> variant(final Class<M> type, final T tester) {
return variant(type, tester, (t, q, r) -> List.of());
}
}

View File

@@ -0,0 +1,32 @@
package queue;
import base.Asserts;
import base.TestCounter;
import java.util.stream.Stream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class QueueTester<M extends Queues.QueueModel> extends ArrayQueueTester<M> {
/* package-private */ QueueTester(final Class<M> type, final Queues.QueueChecker<M> tester, final Queues.Splitter<M> splitter) {
super(type, tester, splitter);
}
public void test(final TestCounter counter) {
test(counter, "LinkedQueue", ReflectionTest.Mode.CLASS);
test(counter, "ArrayQueue", ReflectionTest.Mode.CLASS);
}
private static boolean implementsQueue(final Class<?> type) {
return type != Object.class
&& (Stream.of(type.getInterfaces()).map(Class::getName).anyMatch("queue.Queue"::equals)
|| implementsQueue(type.getSuperclass()));
}
@Override
protected void checkImplementation(final Class<?> type) {
Asserts.assertTrue(type + " should extend AbstractQueue", "queue.AbstractQueue".equals(type.getSuperclass().getName()));
Asserts.assertTrue(type + " should implement interface Queue", implementsQueue(type));
}
}

490
java/queue/Queues.java Normal file
View File

@@ -0,0 +1,490 @@
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();
}
}
}
}

View File

@@ -0,0 +1,201 @@
package queue;
import base.Asserts;
import base.TestCounter;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public class ReflectionTest {
private static final boolean DEBUG = false;
public static final Collector<CharSequence, ?, String> JOINER = Collectors.joining(", ", "(", ")");
protected final Set<Method> methods = new HashSet<>();
public ReflectionTest() {
Asserts.checkAssert(getClass());
}
protected void checkResult(final String call, final Object expected, final Object actual) {
Asserts.assertEquals(call, expected, actual);
}
protected <T> T checking(final TestCounter counter, final Class<T> type, final T reference, final T tested) {
return proxy(type, (proxy, method, args) -> {
final String call = method.getName() + (args == null ? "()" : args(args));
if (DEBUG) {
counter.format("\t%s%n", call);
}
methods.add(method);
final Object expected;
final Object actual;
try {
expected = method.invoke(reference, args);
actual = method.invoke(tested, args);
} catch (final InvocationTargetException e) {
throw e.getCause();
}
checkResult(call, expected, actual);
return actual;
});
}
protected static String args(final Object[] args) {
return Stream.of(args).map(Objects::toString).collect(JOINER);
}
protected static <T> T proxy(final Class<T> type, final InvocationHandler handler) {
return type.cast(Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, handler));
}
private interface IMethod {
Object invoke(final Object instance, final Object[] args) throws InvocationTargetException, IllegalAccessException;
}
protected enum Mode {
MODULE("Module") {
@Override
IMethod lookupMethod(final Class<?> type, final Method method) {
return findMethod(true, type, method);
}
},
ADT("ADT") {
@Override
IMethod lookupMethod(final Class<?> type, final Method method) {
final Class<?>[] argTypes = Stream.concat(Stream.of(type), Stream.of(method.getParameterTypes()))
.toArray(Class<?>[]::new);
final Method actual = findMethod(true, type, method.getName(), argTypes);
final Object[] a = new Object[method.getParameterTypes().length + 1];
return (instance, args) -> {
a[0] = instance;
if (args != null) {
System.arraycopy(args, 0, a, 1, args.length);
}
return actual.invoke(null, a);
};
}
},
CLASS("") {
@Override
IMethod lookupMethod(final Class<?> type, final Method method) {
return findMethod(false, type, method);
}
};
private static IMethod findMethod(final boolean isStatic, final Class<?> type, final Method method) {
return findMethod(isStatic, type, method.getName(), method.getParameterTypes())::invoke;
}
private static Method findMethod(final boolean isStatic, final Class<?> type, final String name, final Class<?>[] parameterTypes) {
final String description = name + args(parameterTypes);
final Method method;
try {
method = type.getMethod(name, parameterTypes);
} catch (final NoSuchMethodException e) {
throw Asserts.error("Missing method %s in %s", description, type);
}
if (isStatic != Modifier.isStatic(method.getModifiers())) {
throw Asserts.error("Method %s in %s %s be static", description, type, isStatic ? "must" : "must not");
}
return method;
}
private final String suffix;
Mode(final String suffix) {
this.suffix = suffix;
}
abstract IMethod lookupMethod(final Class<?> type, final Method method);
private Class<?> loadClass(final String baseName) {
final String className = baseName + suffix;
try {
final URL url = Paths.get(".").toUri().toURL();
//noinspection resource
return new URLClassLoader(new URL[]{url}).loadClass(className);
} catch (final MalformedURLException e) {
throw new AssertionError("Cannot load classes from .", e);
} catch (final ClassNotFoundException e) {
throw new AssertionError("Cannot find class %s: %s".formatted(className, e.getMessage()), e);
}
}
}
protected static class ProxyFactory<T> {
private final Class<T> type;
private final Map<Method, IMethod> methods;
private final Constructor<?> constructor;
protected final Class<?> implementation;
protected ProxyFactory(final Class<T> type, final Mode mode, final String baseName) {
implementation = mode.loadClass(baseName);
try {
constructor = implementation.getConstructor();
} catch (final NoSuchMethodException e) {
throw Asserts.error("%s should have default constructor", implementation.getName());
}
this.type = type;
methods = Stream.of(type.getMethods())
.filter(method -> !method.isAnnotationPresent(Ignore.class))
.collect(Collectors.toMap(Function.identity(), method -> mode.lookupMethod(implementation, method)));
}
public T create() {
try {
return wrap(constructor.newInstance());
} catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new AssertionError("Cannot create " + implementation.getName() + ": " + e.getMessage(), e);
}
}
private T wrap(final Object instance) {
Asserts.assertEquals("same class", implementation, instance.getClass());
return proxy(type, (proxy, method, args) -> {
if (method.getName().equals("toString")) {
return instance.toString();
}
final Object result;
try {
result = methods.get(method).invoke(instance, args);
} catch (final InvocationTargetException e) {
throw e.getCause();
}
if (method.isAnnotationPresent(Wrap.class)) {
return wrap(result);
}
return result;
});
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
protected @interface Ignore {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
protected @interface Wrap {}
}

View File

@@ -0,0 +1,7 @@
/**
* Tests for <a href="https://www.kgeorgiy.info/courses/paradigms/homeworks.html#java-array-queue">Queue</a> homeworks
* of <a href="https://www.kgeorgiy.info/courses/paradigms/">Paradigms of Programming</a> course.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
package queue;