ga6 almost done

This commit is contained in:
Claudio Maggioni 2023-04-17 16:33:47 +02:00
parent 2b94c67c9a
commit 53ac4a1cbd
3 changed files with 504 additions and 0 deletions

139
SQ-2023-H6/pom.xml Normal file
View 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>

View 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 &lt; 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);
}
};
}
}

View File

@ -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));
}
}