ga6 almost done
This commit is contained in:
parent
2b94c67c9a
commit
53ac4a1cbd
3 changed files with 504 additions and 0 deletions
139
SQ-2023-H6/pom.xml
Normal file
139
SQ-2023-H6/pom.xml
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.usi.sq</groupId>
|
||||||
|
<artifactId>SQ-2023-H6</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<junit.platform.version>1.7.0-M1</junit.platform.version>
|
||||||
|
<junit.jupiter.version>5.6.2</junit.jupiter.version>
|
||||||
|
<junit.vintage.version>5.6.2</junit.vintage.version>
|
||||||
|
<junit.version>4.13</junit.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>central</id>
|
||||||
|
<url>https://repo.maven.apache.org/maven2</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.0.0-M5</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>8</source>
|
||||||
|
<target>8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>0.8.5</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>prepare-agent</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<!-- attached to Maven test phase -->
|
||||||
|
<execution>
|
||||||
|
<id>report</id>
|
||||||
|
<phase>test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>report</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.pitest</groupId>
|
||||||
|
<artifactId>pitest-maven</artifactId>
|
||||||
|
<version>1.5.2</version>
|
||||||
|
<configuration>
|
||||||
|
<targetClasses>
|
||||||
|
<param>org.usi.sq.util*</param>
|
||||||
|
</targetClasses>
|
||||||
|
<targetTests>
|
||||||
|
<param>org.usi.sq.util*</param>
|
||||||
|
<param>*</param>
|
||||||
|
</targetTests>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.pitest</groupId>
|
||||||
|
<artifactId>pitest-junit5-plugin</artifactId>
|
||||||
|
<version>0.12</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<version>${junit.jupiter.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>${junit.jupiter.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.vintage</groupId>
|
||||||
|
<artifactId>junit-vintage-engine</artifactId>
|
||||||
|
<version>${junit.vintage.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>0.8.5</version>
|
||||||
|
<reportSets>
|
||||||
|
<reportSet>
|
||||||
|
<reports>
|
||||||
|
<!-- select non-aggregate reports -->
|
||||||
|
<report>report</report>
|
||||||
|
</reports>
|
||||||
|
</reportSet>
|
||||||
|
</reportSets>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||||
|
<version>3.0.0-M5</version>
|
||||||
|
<configuration>
|
||||||
|
<!-- place configuration here if required-->
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jxr-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
</project>
|
276
SQ-2023-H6/src/main/java/org/usi/sq/util/CircularFifoQueue.java
Normal file
276
SQ-2023-H6/src/main/java/org/usi/sq/util/CircularFifoQueue.java
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.usi.sq.util;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CircularFifoQueue is a first-in first-out queue with a fixed size that
|
||||||
|
* replaces its oldest element if full.
|
||||||
|
* <p>
|
||||||
|
* The removal order of a {@link CircularFifoQueue} is based on the
|
||||||
|
* insertion order; elements are removed in the same order in which they
|
||||||
|
* were added. The iteration order is the same as the removal order.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This queue prevents null objects from being added.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <E> the type of elements in this collection
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public class CircularFifoQueue<E> {
|
||||||
|
|
||||||
|
/** Serialization version. */
|
||||||
|
private static final long serialVersionUID = -8423413834657610406L;
|
||||||
|
|
||||||
|
/** Underlying storage array. */
|
||||||
|
private transient E[] elements;
|
||||||
|
|
||||||
|
/** Array index of first (oldest) queue element. */
|
||||||
|
private transient int start = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index mod maxElements of the array position following the last queue
|
||||||
|
* element. Queue elements start at elements[start] and "wrap around"
|
||||||
|
* elements[maxElements-1], ending at elements[decrement(end)].
|
||||||
|
* For example, elements = {c,a,b}, start=1, end=1 corresponds to
|
||||||
|
* the queue [a,b,c].
|
||||||
|
*/
|
||||||
|
private transient int end = 0;
|
||||||
|
|
||||||
|
/** Flag to indicate if the queue is currently full. */
|
||||||
|
private transient boolean full = false;
|
||||||
|
|
||||||
|
/** Capacity of the queue. */
|
||||||
|
private final int maxElements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that creates a queue with the default size of 32.
|
||||||
|
*/
|
||||||
|
public CircularFifoQueue() {
|
||||||
|
this(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that creates a queue with the specified size.
|
||||||
|
*
|
||||||
|
* @param size the size of the queue (cannot be changed)
|
||||||
|
* @throws IllegalArgumentException if the size is < 1
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public CircularFifoQueue(final int size) {
|
||||||
|
if (size <= 0) {
|
||||||
|
throw new IllegalArgumentException("The size must be greater than 0");
|
||||||
|
}
|
||||||
|
elements = (E[]) new Object[size];
|
||||||
|
maxElements = elements.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the capacity limit of this queue has been reached,
|
||||||
|
* i.e. the number of elements stored in the queue equals its maximum size.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the capacity limit has been reached, {@code false} otherwise
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public boolean isAtFullCapacity() {
|
||||||
|
return size() == maxElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given element to this queue. If the queue is full, the least recently added
|
||||||
|
* element is discarded so that a new element can be inserted.
|
||||||
|
*
|
||||||
|
* @param element the element to add
|
||||||
|
* @return true, always
|
||||||
|
* @throws NullPointerException if the given element is null
|
||||||
|
*/
|
||||||
|
public boolean add(final E element) {
|
||||||
|
if (null == element) {
|
||||||
|
throw new NullPointerException("Attempted to add null object to queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAtFullCapacity()) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
elements[end++] = element;
|
||||||
|
|
||||||
|
if (end >= maxElements) {
|
||||||
|
end = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end == start) {
|
||||||
|
full = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given element to this queue. If the queue is full, the least recently added
|
||||||
|
* element is discarded so that a new element can be inserted.
|
||||||
|
*
|
||||||
|
* @param element the element to add
|
||||||
|
* @return true, always
|
||||||
|
* @throws NullPointerException if the given element is null
|
||||||
|
*/
|
||||||
|
public boolean offer(final E element) {
|
||||||
|
return add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the internal index.
|
||||||
|
*
|
||||||
|
* @param index the index to increment
|
||||||
|
* @return the updated index
|
||||||
|
*/
|
||||||
|
private int increment(int index) {
|
||||||
|
index++;
|
||||||
|
if (index >= maxElements) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements the internal index.
|
||||||
|
*
|
||||||
|
* @param index the index to decrement
|
||||||
|
* @return the updated index
|
||||||
|
*/
|
||||||
|
private int decrement(int index) {
|
||||||
|
index--;
|
||||||
|
if (index < 0) {
|
||||||
|
index = maxElements - 1;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this queue is empty; false otherwise.
|
||||||
|
*
|
||||||
|
* @return true if this queue is empty
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E remove() {
|
||||||
|
if (isEmpty()) {
|
||||||
|
throw new NoSuchElementException("queue is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
final E element = elements[start];
|
||||||
|
if (null != element) {
|
||||||
|
elements[start++] = null;
|
||||||
|
|
||||||
|
if (start >= maxElements) {
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
full = false;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of elements stored in the queue.
|
||||||
|
*
|
||||||
|
* @return this queue's size
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
if (end < start) {
|
||||||
|
size = maxElements - start + end;
|
||||||
|
} else if (end == start) {
|
||||||
|
size = full ? maxElements : 0;
|
||||||
|
} else {
|
||||||
|
size = end - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over this queue's elements.
|
||||||
|
*
|
||||||
|
* @return an iterator over this queue's elements
|
||||||
|
*/
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return new Iterator<E>() {
|
||||||
|
|
||||||
|
private int index = start;
|
||||||
|
private int lastReturnedIndex = -1;
|
||||||
|
private boolean isFirst = full;
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return isFirst || index != end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
isFirst = false;
|
||||||
|
lastReturnedIndex = index;
|
||||||
|
index = increment(index);
|
||||||
|
return elements[lastReturnedIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
if (lastReturnedIndex == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// First element can be removed quickly
|
||||||
|
if (lastReturnedIndex == start) {
|
||||||
|
CircularFifoQueue.this.remove();
|
||||||
|
lastReturnedIndex = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = lastReturnedIndex + 1;
|
||||||
|
if (start < lastReturnedIndex && pos < end) {
|
||||||
|
// shift in one part
|
||||||
|
System.arraycopy(elements, pos, elements, lastReturnedIndex, end - pos);
|
||||||
|
} else {
|
||||||
|
// Other elements require us to shift the subsequent elements
|
||||||
|
while (pos != end) {
|
||||||
|
if (pos >= maxElements) {
|
||||||
|
elements[pos - 1] = elements[0];
|
||||||
|
pos = 0;
|
||||||
|
} else {
|
||||||
|
elements[decrement(pos)] = elements[pos];
|
||||||
|
pos = increment(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastReturnedIndex = -1;
|
||||||
|
end = decrement(end);
|
||||||
|
elements[end] = null;
|
||||||
|
full = false;
|
||||||
|
index = decrement(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package org.usi.sq.util;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class CircularFifoQueueTest {
|
||||||
|
|
||||||
|
private void testIteratorEmpty(Iterator<?> it) {
|
||||||
|
assertFalse(it.hasNext());
|
||||||
|
assertThrows(NoSuchElementException.class, it::next);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void testIteratorNext(Iterator<T> it, T e) {
|
||||||
|
assertTrue(it.hasNext());
|
||||||
|
assertEquals(e, it.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void testQueueAll(int initQueueSize, List<T> values, long removeQueueCount, List<Integer> itrRemoveIndexes) {
|
||||||
|
final Set<Integer> removeIndexesSet = new HashSet<>(itrRemoveIndexes);
|
||||||
|
|
||||||
|
final CircularFifoQueue<T> queue = new CircularFifoQueue<>(initQueueSize);
|
||||||
|
for (T e : values) assertTrue(queue.offer(e));
|
||||||
|
|
||||||
|
int queueSizeAfterOffers = Math.min(initQueueSize, values.size());
|
||||||
|
for (int i = 0; i < removeQueueCount; i++) {
|
||||||
|
assertEquals(values.get(values.size() - queueSizeAfterOffers + i), queue.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(queueSizeAfterOffers - removeQueueCount, queue.size());
|
||||||
|
assertEquals(queue.size() == initQueueSize, queue.isAtFullCapacity());
|
||||||
|
|
||||||
|
final Iterator<T> it = queue.iterator();
|
||||||
|
final List<T> notRemovedValues = new ArrayList<>(values.size());
|
||||||
|
|
||||||
|
final List<T> expected = values.subList(Math.max(0, values.size() - queue.size()), values.size());
|
||||||
|
|
||||||
|
// Test Iteration
|
||||||
|
for (int i = 0; i < expected.size(); i++) {
|
||||||
|
T e = expected.get(i);
|
||||||
|
testIteratorNext(it, e);
|
||||||
|
|
||||||
|
if (removeIndexesSet.contains(i)) {
|
||||||
|
it.remove();
|
||||||
|
assertThrows(IllegalStateException.class, it::remove); // removing again causes explosion
|
||||||
|
} else {
|
||||||
|
notRemovedValues.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testIteratorEmpty(it);
|
||||||
|
|
||||||
|
// Test removal
|
||||||
|
assertEquals(notRemovedValues.size(), queue.size());
|
||||||
|
final Iterator<T> itNew = queue.iterator();
|
||||||
|
for (T e : notRemovedValues) testIteratorNext(itNew, e);
|
||||||
|
testIteratorEmpty(itNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIterator() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new CircularFifoQueue<Void>(0));
|
||||||
|
assertThrows(NoSuchElementException.class, () -> new CircularFifoQueue<Void>().remove());
|
||||||
|
assertThrows(NullPointerException.class, () -> new CircularFifoQueue<Void>().offer(null));
|
||||||
|
|
||||||
|
testQueueIteratorRemove(10, Arrays.asList(1, 2, 3), 0, 2);
|
||||||
|
testQueueIteratorRemove(3, Arrays.asList(1, 2, 3), 0, 2);
|
||||||
|
testQueueIteratorRemove(3, Arrays.asList(1, 2, 3), 0, 2);
|
||||||
|
testQueueIteratorRemove(2, Arrays.asList(1, 2, 3));
|
||||||
|
testQueueIteratorRemove(4, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9), 1, 3);
|
||||||
|
testQueueAll(7, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), 4, Arrays.asList(1, 3));
|
||||||
|
|
||||||
|
testQueueIteratorRemove(4, Arrays.asList(1, 2, 3, 4, 5), 1, 3);
|
||||||
|
testQueueIteratorRemove(10, Collections.emptyList());
|
||||||
|
testQueueQueueRemove(4, Arrays.asList(1, 2, 3, 4, 5), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testQueueQueueRemove(int i, List<Integer> asList, int i1) {
|
||||||
|
testQueueAll(i, asList, i1, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testQueueIteratorRemove(int i, List<Integer> asList, Integer... asList1) {
|
||||||
|
testQueueAll(i, asList, 0, Arrays.asList(asList1));
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue