diff --git a/SQ-2023-H6/pom.xml b/SQ-2023-H6/pom.xml
new file mode 100644
index 0000000..acd2026
--- /dev/null
+++ b/SQ-2023-H6/pom.xml
@@ -0,0 +1,139 @@
+
+
+ 4.0.0
+ org.usi.sq
+ SQ-2023-H6
+ 1.0
+
+ UTF-8
+ 1.7.0-M1
+ 5.6.2
+ 5.6.2
+ 4.13
+
+
+
+
+ central
+ https://repo.maven.apache.org/maven2
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 8
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.5
+
+
+
+ prepare-agent
+
+
+
+
+ report
+ test
+
+ report
+
+
+
+
+
+ org.pitest
+ pitest-maven
+ 1.5.2
+
+
+ org.usi.sq.util*
+
+
+ org.usi.sq.util*
+ *
+
+
+
+
+
+ org.pitest
+ pitest-junit5-plugin
+ 0.12
+
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.jupiter.version}
+ test
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ ${junit.vintage.version}
+ test
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.5
+
+
+
+
+ report
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+ 3.0.0-M5
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+ 3.3.0
+
+
+
+
diff --git a/SQ-2023-H6/src/main/java/org/usi/sq/util/CircularFifoQueue.java b/SQ-2023-H6/src/main/java/org/usi/sq/util/CircularFifoQueue.java
new file mode 100644
index 0000000..a6b2001
--- /dev/null
+++ b/SQ-2023-H6/src/main/java/org/usi/sq/util/CircularFifoQueue.java
@@ -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.
+ *
+ * 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.
+ *
+ *
+ * This queue prevents null objects from being added.
+ *
+ *
+ * @param the type of elements in this collection
+ * @since 4.0
+ */
+public class CircularFifoQueue {
+
+ /** 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 iterator() {
+ return new Iterator() {
+
+ 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);
+ }
+
+ };
+ }
+}
diff --git a/SQ-2023-H6/src/test/java/org/usi/sq/util/CircularFifoQueueTest.java b/SQ-2023-H6/src/test/java/org/usi/sq/util/CircularFifoQueueTest.java
new file mode 100644
index 0000000..264e6de
--- /dev/null
+++ b/SQ-2023-H6/src/test/java/org/usi/sq/util/CircularFifoQueueTest.java
@@ -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 void testIteratorNext(Iterator it, T e) {
+ assertTrue(it.hasNext());
+ assertEquals(e, it.next());
+ }
+
+ private void testQueueAll(int initQueueSize, List values, long removeQueueCount, List itrRemoveIndexes) {
+ final Set removeIndexesSet = new HashSet<>(itrRemoveIndexes);
+
+ final CircularFifoQueue 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 it = queue.iterator();
+ final List notRemovedValues = new ArrayList<>(values.size());
+
+ final List 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 itNew = queue.iterator();
+ for (T e : notRemovedValues) testIteratorNext(itNew, e);
+ testIteratorEmpty(itNew);
+ }
+
+
+
+
+ @Test
+ public void testIterator() {
+ assertThrows(IllegalArgumentException.class, () -> new CircularFifoQueue(0));
+ assertThrows(NoSuchElementException.class, () -> new CircularFifoQueue().remove());
+ assertThrows(NullPointerException.class, () -> new CircularFifoQueue().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 asList, int i1) {
+ testQueueAll(i, asList, i1, Collections.emptyList());
+ }
+
+ private void testQueueIteratorRemove(int i, List asList, Integer... asList1) {
+ testQueueAll(i, asList, 0, Arrays.asList(asList1));
+ }
+}