diff --git a/drv/ArrayFIFOQueue.drv b/drv/ArrayFIFOQueue.drv index 5c3697da..ce6c58bd 100644 --- a/drv/ArrayFIFOQueue.drv +++ b/drv/ArrayFIFOQueue.drv @@ -97,11 +97,19 @@ public class ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, return t; } - /** Dequeues the {@linkplain PriorityQueue#last() last} element from the queue. - * - * @return the dequeued element. - * @throws NoSuchElementException if the queue is empty. - */ + @Override + public KEY_GENERIC_TYPE DEQUEUE(KEY_GENERIC_TYPE x) { + if (start == end) return x; + final KEY_GENERIC_TYPE t = array[start]; +#if KEYS_REFERENCE + array[start] = null; // Clean-up for the garbage collector. +#endif + if (++start == length) start = 0; + reduce(); + return t; + } + + @Override public KEY_GENERIC_TYPE DEQUEUE_LAST() { if (start == end) throw new NoSuchElementException(); if (end == 0) end = length; @@ -113,6 +121,18 @@ public class ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, return t; } + @Override + public KEY_GENERIC_TYPE DEQUEUE_LAST(KEY_GENERIC_TYPE x) { + if (start == end) return x; + if (end == 0) end = length; + final KEY_GENERIC_TYPE t = array[--end]; +#if KEYS_REFERENCE + array[end] = null; // Clean-up for the garbage collector. +#endif + reduce(); + return t; + } + SUPPRESS_WARNINGS_KEY_UNCHECKED private final void resize(final int size, final int newLength) { final KEY_GENERIC_TYPE[] newArray = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[newLength]; @@ -139,19 +159,19 @@ public class ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, } @Override - public void enqueue(KEY_GENERIC_TYPE x) { + public boolean enqueue(KEY_GENERIC_TYPE x) { array[end++] = x; if (end == length) end = 0; if (end == start) expand(); + return true; } - /** Enqueues a new element as the first element (in dequeuing order) of the queue. - * @param x the element to enqueue. - */ - public void enqueueFirst(KEY_GENERIC_TYPE x) { + @Override + public boolean enqueueFirst(KEY_GENERIC_TYPE x) { if (start == 0) start = length; array[--start] = x; if (end == start) expand(); + return true; } @Override @@ -160,6 +180,12 @@ public class ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, return array[start]; } + @Override + public KEY_GENERIC_TYPE FIRST(KEY_GENERIC_TYPE x) { + if (start == end) return x; + return array[start]; + } + @Override public KEY_GENERIC_TYPE LAST() { @@ -167,6 +193,12 @@ public class ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, return array[(end == 0 ? length : end) - 1]; } + @Override + public KEY_GENERIC_TYPE LAST(KEY_GENERIC_TYPE x) { + if (start == end) return x; + return array[(end == 0 ? length : end) - 1]; + } + @Override public void clear() { #if KEYS_REFERENCE diff --git a/drv/ArrayPriorityQueue.drv b/drv/ArrayPriorityQueue.drv index f3465a2d..4e571853 100644 --- a/drv/ArrayPriorityQueue.drv +++ b/drv/ArrayPriorityQueue.drv @@ -154,13 +154,9 @@ public class ARRAY_PRIORITY_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENE return this.firstIndex = firstIndex; } - private void ensureNonEmpty() { - if (size == 0) throw new NoSuchElementException(); - } - @Override SUPPRESS_WARNINGS_KEY_UNCHECKED - public void enqueue(KEY_GENERIC_TYPE x) { + public boolean enqueue(KEY_GENERIC_TYPE x) { if (size == array.length) array = ARRAYS.grow(array, size + 1); if (firstIndexValid) { if (c == null) { if (KEY_LESS(x, array[firstIndex])) firstIndex = size; } @@ -168,11 +164,25 @@ public class ARRAY_PRIORITY_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENE } else firstIndexValid = false; array[size++] = x; + return true; } @Override public KEY_GENERIC_TYPE DEQUEUE() { - ensureNonEmpty(); + if (size == 0) throw new NoSuchElementException(); + final int first = findFirst(); + final KEY_GENERIC_TYPE result = array[first]; + System.arraycopy(array, first + 1, array, first, --size - first); +#if KEY_CLASS_Object + array[size] = null; +#endif + firstIndexValid = false; + return result; + } + + @Override + public KEY_GENERIC_TYPE DEQUEUE(KEY_GENERIC_TYPE x) { + if (size == 0) return x; final int first = findFirst(); final KEY_GENERIC_TYPE result = array[first]; System.arraycopy(array, first + 1, array, first, --size - first); @@ -185,13 +195,19 @@ public class ARRAY_PRIORITY_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENE @Override public KEY_GENERIC_TYPE FIRST() { - ensureNonEmpty(); + if (size == 0) throw new NoSuchElementException(); + return array[findFirst()]; + } + + @Override + public KEY_GENERIC_TYPE FIRST(KEY_GENERIC_TYPE x) { + if (size == 0) return x; return array[findFirst()]; } @Override public void changed() { - ensureNonEmpty(); + if (size == 0) throw new NoSuchElementException(); firstIndexValid = false; } diff --git a/drv/FixedArrayFIFOQueue.drv b/drv/FixedArrayFIFOQueue.drv new file mode 100644 index 00000000..7c81700b --- /dev/null +++ b/drv/FixedArrayFIFOQueue.drv @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010-2023 Sebastiano Vigna + * + * Licensed 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 PACKAGE; + +#if KEY_CLASS_Object +import java.util.Arrays; +import java.util.Comparator; +#endif + +import java.io.Serializable; +import it.unimi.dsi.fastutil.HashCommon; +import it.unimi.dsi.fastutil.PriorityQueue; + +import java.util.NoSuchElementException; + +/** A type-specific array-based FIFO queue, supporting also deque operations. + * + *

Instances of this class represent a FIFO queue using a backing + * array in a circular way. + * + *

This class provides additional methods that implement a deque (double-ended queue). + */ + +public class FIXED_ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, Serializable { + private static final long serialVersionUID = 0L; + + /** The backing array. */ + protected transient KEY_GENERIC_TYPE array[]; + + /** The current (cached) length of {@link #array}. */ + protected transient int length; + + /** The start position in {@link #array}. It is always strictly smaller than {@link #length}.*/ + protected transient int start; + + /** The end position in {@link #array}. It is always strictly smaller than {@link #length}. + * Might be actually smaller than {@link #start} because {@link #array} is used cyclically. */ + protected transient int end; + + /** Creates a new empty queue with given capacity. + * + * @implNote Because of inner limitations of the JVM, the initial + * capacity cannot exceed {@link it.unimi.dsi.fastutil.Arrays#MAX_ARRAY_SIZE} − 1. + * + * @param capacity the initial capacity of this queue. + */ + SUPPRESS_WARNINGS_KEY_UNCHECKED + public FIXED_ARRAY_FIFO_QUEUE(final int capacity) { + if (capacity > it.unimi.dsi.fastutil.Arrays.MAX_ARRAY_SIZE - 1) throw new IllegalArgumentException("Initial capacity (" + capacity + ") exceeds " + (it.unimi.dsi.fastutil.Arrays.MAX_ARRAY_SIZE - 1)); + if (capacity < 0) throw new IllegalArgumentException("Initial capacity (" + capacity + ") is negative"); + // We never build a queue with a zero-sized backing array; moreover, to + // avoid resizing at the given capacity we need one additional element. + array = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[Math.max(1, capacity + 1)]; + length = array.length; + } + + /** {@inheritDoc} + * @implSpec This implementation returns {@code null} (FIFO queues have no comparator). */ + @Override + public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { + return null; + } + + @Override + public KEY_GENERIC_TYPE DEQUEUE() { + if (start == end) throw new NoSuchElementException(); + final KEY_GENERIC_TYPE t = array[start]; +#if KEYS_REFERENCE + array[start] = null; // Clean-up for the garbage collector. +#endif + if (++start == length) start = 0; + return t; + } + + @Override + public KEY_GENERIC_TYPE DEQUEUE(KEY_GENERIC_TYPE x) { + if (start == end) return x; + final KEY_GENERIC_TYPE t = array[start]; +#if KEYS_REFERENCE + array[start] = null; // Clean-up for the garbage collector. +#endif + if (++start == length) start = 0; + return t; + } + + @Override + public KEY_GENERIC_TYPE DEQUEUE_LAST() { + if (start == end) throw new NoSuchElementException(); + if (end == 0) end = length; + final KEY_GENERIC_TYPE t = array[--end]; +#if KEYS_REFERENCE + array[end] = null; // Clean-up for the garbage collector. +#endif + return t; + } + + @Override + public KEY_GENERIC_TYPE DEQUEUE_LAST(KEY_GENERIC_TYPE x) { + if (start == end) return x; + if (end == 0) end = length; + final KEY_GENERIC_TYPE t = array[--end]; +#if KEYS_REFERENCE + array[end] = null; // Clean-up for the garbage collector. +#endif + return t; + } + + SUPPRESS_WARNINGS_KEY_UNCHECKED + private final void resize(final int size, final int newLength) { + final KEY_GENERIC_TYPE[] newArray = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[newLength]; + if (start >= end) { + if (size != 0) { + System.arraycopy(array, start, newArray, 0, length - start); + System.arraycopy(array, 0, newArray, length - start, end); + } + } + else System.arraycopy(array, start, newArray, 0, end - start); + start = 0; + end = size; + array = newArray; + length = newLength; + } + + @Override + public boolean enqueue(KEY_GENERIC_TYPE x) { + final int next = end++; + if (end == start) return false; + array[next] = x; + if (end == length) end = 0; + return true; + } + + @Override + public boolean enqueueFirst(KEY_GENERIC_TYPE x) { + if (start == 0) start = length; + final int next = --start; + if (end == start) return false; + array[next] = x; + return true; + } + + @Override + public KEY_GENERIC_TYPE FIRST() { + if (start == end) throw new NoSuchElementException(); + return array[start]; + } + + @Override + public KEY_GENERIC_TYPE FIRST(KEY_GENERIC_TYPE x) { + if (start == end) return x; + return array[start]; + } + + + @Override + public KEY_GENERIC_TYPE LAST() { + if (start == end) throw new NoSuchElementException(); + return array[(end == 0 ? length : end) - 1]; + } + + @Override + public KEY_GENERIC_TYPE LAST(KEY_GENERIC_TYPE x) { + if (start == end) return x; + return array[(end == 0 ? length : end) - 1]; + } + + @Override + public void clear() { +#if KEYS_REFERENCE + if (start <= end) Arrays.fill(array, start, end, null); + else { + Arrays.fill(array, start, length, null); + Arrays.fill(array, 0, end, null); + } +#endif + start = end = 0; + } + + @Override + public int size() { + final int apparentLength = end - start; + return apparentLength >= 0 ? apparentLength : length + apparentLength; + } + + public int capacity() { + return array.length - 1; + } + + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + s.defaultWriteObject(); + int size = size(); + s.writeInt(size); + for(int i = start; size-- != 0;) { + s.WRITE_KEY(array[i++]); + if (i == length) i = 0; + } + } + + SUPPRESS_WARNINGS_KEY_UNCHECKED + private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + end = s.readInt(); + array = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[length = HashCommon.nextPowerOfTwo(end + 1)]; + for(int i = 0; i < end; i++) array[i] = KEY_GENERIC_CAST s.READ_KEY(); + } +} diff --git a/drv/HeapPriorityQueue.drv b/drv/HeapPriorityQueue.drv index 549349d4..72072efd 100644 --- a/drv/HeapPriorityQueue.drv +++ b/drv/HeapPriorityQueue.drv @@ -213,11 +213,12 @@ public class HEAP_PRIORITY_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENER #endif @Override - public void enqueue(KEY_GENERIC_TYPE x) { + public boolean enqueue(KEY_GENERIC_TYPE x) { if (size == heap.length) heap = ARRAYS.grow(heap, size + 1); heap[size++] = x; HEAPS.upHeap(heap, size, size - 1, c); + return true; } @Override @@ -233,12 +234,31 @@ public class HEAP_PRIORITY_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENER return result; } + @Override + public KEY_GENERIC_TYPE DEQUEUE(KEY_GENERIC_TYPE x) { + if (size == 0) return x; + + final KEY_GENERIC_TYPE result = heap[0]; + heap[0] = heap[--size]; +#if KEY_CLASS_Object + heap[size] = null; +#endif + if (size != 0) HEAPS.downHeap(heap, size, 0, c); + return result; + } + @Override public KEY_GENERIC_TYPE FIRST() { if (size == 0) throw new NoSuchElementException(); return heap[0]; } + @Override + public KEY_GENERIC_TYPE FIRST(KEY_GENERIC_TYPE x) { + if (size == 0) return x; + return heap[0]; + } + @Override public void changed() { HEAPS.downHeap(heap, size, 0, c); diff --git a/drv/PriorityQueue.drv b/drv/PriorityQueue.drv index 0c891345..8016da3e 100644 --- a/drv/PriorityQueue.drv +++ b/drv/PriorityQueue.drv @@ -31,9 +31,19 @@ public interface PRIORITY_QUEUE extends PriorityQueue { /** Enqueues a new element. * @see PriorityQueue#enqueue(Object) * @param x the element to enqueue. + * @return true if element was added into this queue. */ - void enqueue(KEY_GENERIC_TYPE x); + boolean enqueue(KEY_GENERIC_TYPE x); + + /** Enqueues a new element as the first element (in dequeuing order) of the queue. + * @see PriorityQueue#enqueueFirst(Object) + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @param x the element to enqueue. + * @return true if element was added into this queue. + */ + + default boolean enqueueFirst(KEY_GENERIC_TYPE x) { throw new UnsupportedOperationException(); } /** Dequeues the {@linkplain #first() first} element from the queue. * @see #dequeue() @@ -43,6 +53,36 @@ public interface PRIORITY_QUEUE extends PriorityQueue { KEY_GENERIC_TYPE DEQUEUE(); + /** Dequeues the {@linkplain #first() first} element from the queue + * or return {@code x} if the queue is empty. + * + * @see #dequeue() + * @param x the element to return when queue is empty. + * @return the dequeued element or {@code x} if the queue is empty. + */ + + KEY_GENERIC_TYPE DEQUEUE(KEY_GENERIC_TYPE x); + + /** Dequeues the {@linkplain #last() last} element from the queue. + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @see #dequeue() + * @return the dequeued element. + * @throws NoSuchElementException if the queue is empty. + */ + + default KEY_GENERIC_TYPE DEQUEUE_LAST() { throw new UnsupportedOperationException(); } + + /** Dequeues the {@linkplain #last() last} element from the queue + * or return {@code x} if the queue is empty. + * + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @see #dequeue() + * @param x the element to return when queue is empty. + * @return the dequeued element or {@code x} if the queue is empty. + */ + + default KEY_GENERIC_TYPE DEQUEUE_LAST(KEY_GENERIC_TYPE x) { throw new UnsupportedOperationException(); } + /** Returns the first element of the queue. * @see #first() * @return the first element. @@ -51,6 +91,14 @@ public interface PRIORITY_QUEUE extends PriorityQueue { KEY_GENERIC_TYPE FIRST(); + /** Returns the first element of the queue or {@code x} if the queue is empty. + * @see #first() + * @param x the element to return when queue is empty. + * @return the first element. + */ + + KEY_GENERIC_TYPE FIRST(KEY_GENERIC_TYPE x); + /** Returns the last element of the queue, that is, the element the would be dequeued last (optional operation). *

This default implementation just throws an {@link UnsupportedOperationException}. * @see #last() @@ -60,6 +108,16 @@ public interface PRIORITY_QUEUE extends PriorityQueue { default KEY_GENERIC_TYPE LAST() { throw new UnsupportedOperationException(); } + /** Returns the last element of the queue or {@code x} if the queue is empty, + * that is, the element the would be dequeued last (optional operation). + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @see #last() + * @return the last element. + * @throws NoSuchElementException if the queue is empty. + */ + + default KEY_GENERIC_TYPE LAST(KEY_GENERIC_TYPE x) { throw new UnsupportedOperationException(); } + /** Returns the comparator associated with this priority queue, or null if it uses its elements' natural ordering. * * @apiNote Note that this specification strengthens the one given in {@link PriorityQueue#comparator()}. @@ -74,7 +132,14 @@ public interface PRIORITY_QUEUE extends PriorityQueue { * @deprecated Please use the corresponding type-specific method instead. */ @Deprecated @Override - default void enqueue(final KEY_GENERIC_CLASS x) { enqueue(x.KEY_VALUE()); } + default boolean enqueue(final KEY_GENERIC_CLASS x) { return enqueue(x.KEY_VALUE()); } + + /** {@inheritDoc} + *

This default implementation delegates to the corresponding type-specific method. + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + default boolean enqueueFirst(final KEY_GENERIC_CLASS x) { return enqueueFirst(x.KEY_VALUE()); } /** {@inheritDoc} *

This default implementation delegates to the corresponding type-specific method. @@ -83,6 +148,27 @@ public interface PRIORITY_QUEUE extends PriorityQueue { @Override default KEY_GENERIC_CLASS dequeue() { return KEY2OBJ(DEQUEUE()); } + /** {@inheritDoc} + *

This default implementation delegates to the corresponding type-specific method. + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + default KEY_GENERIC_CLASS dequeue(KEY_GENERIC_CLASS x) { try { return dequeue(); } catch (NoSuchElementException ignored) { return x; } } + + /** {@inheritDoc} + *

This default implementation delegates to the corresponding type-specific method. + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + default KEY_GENERIC_CLASS dequeueLast() { return KEY2OBJ(DEQUEUE_LAST()); } + + /** {@inheritDoc} + *

This default implementation delegates to the corresponding type-specific method. + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + default KEY_GENERIC_CLASS dequeueLast(KEY_GENERIC_CLASS x) { try { return dequeueLast(); } catch (NoSuchElementException ignored) { return x; } } + /** {@inheritDoc} *

This default implementation delegates to the corresponding type-specific method. * @deprecated Please use the corresponding type-specific method instead. */ @@ -90,10 +176,24 @@ public interface PRIORITY_QUEUE extends PriorityQueue { @Override default KEY_GENERIC_CLASS first() { return KEY2OBJ(FIRST()); } + /** {@inheritDoc} + *

This default implementation delegates to the corresponding type-specific method. + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + default KEY_GENERIC_CLASS first(KEY_GENERIC_CLASS x) { return KEY2OBJ(FIRST(x)); } + /** {@inheritDoc} *

This default implementation delegates to the corresponding type-specific method. * @deprecated Please use the corresponding type-specific method instead. */ @Deprecated @Override default KEY_GENERIC_CLASS last() { return KEY2OBJ(LAST()); } + + /** {@inheritDoc} + *

This default implementation delegates to the corresponding type-specific method. + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + default KEY_GENERIC_CLASS last(KEY_GENERIC_CLASS x) { return KEY2OBJ(LAST(x)); } } diff --git a/drv/PriorityQueues.drv b/drv/PriorityQueues.drv index 9075108d..0cd19287 100644 --- a/drv/PriorityQueues.drv +++ b/drv/PriorityQueues.drv @@ -43,17 +43,26 @@ public final class PRIORITY_QUEUES { } @Override - public void enqueue(KEY_GENERIC_TYPE x) { synchronized(sync) { q.enqueue(x); } } + public boolean enqueue(KEY_GENERIC_TYPE x) { synchronized(sync) { return q.enqueue(x); } } @Override public KEY_GENERIC_TYPE DEQUEUE() { synchronized(sync) { return q.DEQUEUE(); } } + @Override + public KEY_GENERIC_TYPE DEQUEUE(KEY_GENERIC_TYPE x) { synchronized(sync) { return q.DEQUEUE(x); } } + @Override public KEY_GENERIC_TYPE FIRST() { synchronized(sync) { return q.FIRST(); } } + @Override + public KEY_GENERIC_TYPE FIRST(KEY_GENERIC_TYPE x) { synchronized(sync) { return q.FIRST(x); } } + @Override public KEY_GENERIC_TYPE LAST() { synchronized(sync) { return q.LAST(); } } + @Override + public KEY_GENERIC_TYPE LAST(KEY_GENERIC_TYPE x) { synchronized(sync) { return q.LAST(x); } } + @Override public boolean isEmpty() { synchronized(sync) { return q.isEmpty(); } } @@ -73,20 +82,32 @@ public final class PRIORITY_QUEUES { @Deprecated @Override - public void enqueue(KEY_CLASS x) { synchronized(sync) { q.enqueue(x); } } + public boolean enqueue(KEY_CLASS x) { synchronized(sync) { return q.enqueue(x); } } @Deprecated @Override public KEY_CLASS dequeue() { synchronized(sync) { return q.dequeue(); } } + @Deprecated + @Override + public KEY_CLASS dequeue(KEY_CLASS x) { synchronized(sync) { return q.dequeue(x); } } + @Deprecated @Override public KEY_CLASS first() { synchronized(sync) { return q.first(); } } + @Deprecated + @Override + public KEY_CLASS first(KEY_CLASS x) { synchronized(sync) { return q.first(x); } } + @Deprecated @Override public KEY_CLASS last() { synchronized(sync) { return q.last(); } } + @Deprecated + @Override + public KEY_CLASS last(KEY_CLASS x) { synchronized(sync) { return q.last(x); } } + #endif @Override public int hashCode() { synchronized(sync) { return q.hashCode(); } } diff --git a/gencsource.sh b/gencsource.sh index b7b5f26e..407ea69c 100755 --- a/gencsource.sh +++ b/gencsource.sh @@ -577,6 +577,7 @@ $(if [[ "${CLASS[$k]}" != "" && "${CLASS[$v]}" != "" ]]; then\ "#define HEAP_SESQUI_INDIRECT_DOUBLE_PRIORITY_QUEUE ${TYPE_CAP2[$k]}HeapSesquiIndirectDoublePriorityQueue\n"\ "#define HEAP_INDIRECT_DOUBLE_PRIORITY_QUEUE ${TYPE_CAP2[$k]}HeapIndirectDoublePriorityQueue\n"\ "#define ARRAY_FIFO_QUEUE ${TYPE_CAP2[$k]}ArrayFIFOQueue\n"\ +"#define FIXED_ARRAY_FIFO_QUEUE ${TYPE_CAP2[$k]}FixedArrayFIFOQueue\n"\ "#define ARRAY_PRIORITY_QUEUE ${TYPE_CAP2[$k]}ArrayPriorityQueue\n"\ "#define ARRAY_INDIRECT_PRIORITY_QUEUE ${TYPE_CAP2[$k]}ArrayIndirectPriorityQueue\n"\ "#define ARRAY_INDIRECT_DOUBLE_PRIORITY_QUEUE ${TYPE_CAP2[$k]}ArrayIndirectDoublePriorityQueue\n"\ diff --git a/makefile b/makefile index 9239a3ee..91beaeab 100644 --- a/makefile +++ b/makefile @@ -476,6 +476,11 @@ $(ARRAY_FIFO_QUEUES): drv/ArrayFIFOQueue.drv; ./gencsource.sh $< $@ >$@ CSOURCES += $(ARRAY_FIFO_QUEUES) +FIXED_ARRAY_FIFO_QUEUES := $(foreach k,$(TYPE_NOBOOL_NOREF), $(GEN_SRCDIR)/$(PKG_PATH)/$(PACKAGE_$(k))/$(k)FixedArrayFIFOQueue.c) +$(FIXED_ARRAY_FIFO_QUEUES): drv/FixedArrayFIFOQueue.drv; ./gencsource.sh $< $@ >$@ + +CSOURCES += $(FIXED_ARRAY_FIFO_QUEUES) + HEAP_SEMI_INDIRECT_PRIORITY_QUEUES := $(foreach k, $(TYPE_NOBOOL_NOREF), $(GEN_SRCDIR)/$(PKG_PATH)/$(PACKAGE_$(k))/$(k)HeapSemiIndirectPriorityQueue.c) $(HEAP_SEMI_INDIRECT_PRIORITY_QUEUES): drv/HeapSemiIndirectPriorityQueue.drv; ./gencsource.sh $< $@ >$@ diff --git a/src/it/unimi/dsi/fastutil/PriorityQueue.java b/src/it/unimi/dsi/fastutil/PriorityQueue.java index fe0084bf..800ee8d7 100644 --- a/src/it/unimi/dsi/fastutil/PriorityQueue.java +++ b/src/it/unimi/dsi/fastutil/PriorityQueue.java @@ -41,9 +41,19 @@ public interface PriorityQueue { /** Enqueues a new element. * * @param x the element to enqueue. + * @return true if element was added into this queue. */ - void enqueue(K x); + boolean enqueue(K x); + + /** Enqueues a new element as the first element (in dequeuing order) of the queue. + * + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @param x the element to enqueue. + * @return true if element was added into this queue. + */ + + default boolean enqueueFirst(K x) { throw new UnsupportedOperationException(); } /** Dequeues the {@linkplain #first() first} element from the queue. * @@ -53,6 +63,32 @@ public interface PriorityQueue { K dequeue(); + /** Dequeues the {@linkplain PriorityQueue#first() first} element from the queue + * or return {@code x} if the queue is empty. + * + * @param x the element to return when queue is empty. + * @return the dequeued element or {@code x} if the queue is empty. + */ + K dequeue(K x); + + /** Dequeues the {@linkplain #last() last} element from the queue. + * + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @return the dequeued element. + * @throws NoSuchElementException if the queue is empty. + */ + + default K dequeueLast() { throw new UnsupportedOperationException(); } + + /** Dequeues the {@linkplain PriorityQueue#last() last} element from the queue + * or return {@code x} if the queue is empty. + * + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @param x the element to return when queue is empty. + * @return the dequeued element or {@code x} if the queue is empty. + */ + default K dequeueLast(K x) { throw new UnsupportedOperationException(); } + /** Checks whether this queue is empty. * *

This default implementation checks whether {@link #size()} is zero. @@ -83,6 +119,14 @@ default boolean isEmpty() { K first(); + /** Returns the first element of the queue or {@code x} if the queue is empty. + * + * @param x the element to return when queue is empty. + * @return the first element or {@code x} if the queue is empty. + */ + + K first(K x); + /** Returns the last element of the queue, that is, the element the would be dequeued last (optional operation). * *

This default implementation just throws an {@link UnsupportedOperationException}. @@ -92,6 +136,15 @@ default boolean isEmpty() { default K last() { throw new UnsupportedOperationException(); } + /** Returns the last element of the queue or {@code x} if the queue is empty, + * that is, the element the would be dequeued last (optional operation). + * + *

This default implementation just throws an {@link UnsupportedOperationException}. + * @return the last element or {@code x} if the queue is empty. + */ + + default K last(K x) { throw new UnsupportedOperationException(); } + /** Notifies the queue that the {@linkplain #first() first} element has changed (optional operation). *

This default implementation just throws an {@link UnsupportedOperationException}. */ diff --git a/src/it/unimi/dsi/fastutil/PriorityQueues.java b/src/it/unimi/dsi/fastutil/PriorityQueues.java index 817f55a1..d6ba3d99 100644 --- a/src/it/unimi/dsi/fastutil/PriorityQueues.java +++ b/src/it/unimi/dsi/fastutil/PriorityQueues.java @@ -42,11 +42,14 @@ public static class EmptyPriorityQueue implements PriorityQueue, Serializable { protected EmptyPriorityQueue() {} @Override - public void enqueue(final Object o) { throw new UnsupportedOperationException(); } + public boolean enqueue(final Object o) { throw new UnsupportedOperationException(); } @Override public Object dequeue() { throw new NoSuchElementException(); } + @Override + public Object dequeue(Object x) {return x;} + @Override public boolean isEmpty() { return true; } @@ -59,9 +62,15 @@ public void clear() {} @Override public Object first() { throw new NoSuchElementException(); } + @Override + public Object first(Object x) { return x; } + @Override public Object last() { throw new NoSuchElementException(); } + @Override + public Object last(Object x) { return x; } + @Override public void changed() { throw new NoSuchElementException(); } @@ -114,17 +123,26 @@ protected SynchronizedPriorityQueue(final PriorityQueue q) { } @Override - public void enqueue(final K x) { synchronized(sync) { q.enqueue(x); } } + public boolean enqueue(final K x) { synchronized(sync) { return q.enqueue(x); } } @Override public K dequeue() { synchronized(sync) { return q.dequeue(); } } + @Override + public K dequeue(K x) { synchronized(sync) { return q.dequeue(x); } } + @Override public K first() { synchronized(sync) { return q.first(); } } + @Override + public K first(K x) { synchronized(sync) { return q.first(x); } } + @Override public K last() { synchronized(sync) { return q.last(); } } + @Override + public K last(K x) { synchronized(sync) { return q.last(x); } } + @Override public boolean isEmpty() { synchronized(sync) { return q.isEmpty(); } } diff --git a/test/it/unimi/dsi/fastutil/ints/IntFixedArrayFIFOQueueTest.java b/test/it/unimi/dsi/fastutil/ints/IntFixedArrayFIFOQueueTest.java new file mode 100644 index 00000000..39ab6ee2 --- /dev/null +++ b/test/it/unimi/dsi/fastutil/ints/IntFixedArrayFIFOQueueTest.java @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2017-2022 Sebastiano Vigna + * + * Licensed 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 it.unimi.dsi.fastutil.ints; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class IntFixedArrayFIFOQueueTest { + + @Test + public void testWorkflow() { + IntFixedArrayFIFOQueue queue = new IntFixedArrayFIFOQueue(8); + + assertEquals(0, queue.size()); + assertEquals(Integer.MIN_VALUE, queue.dequeueInt(Integer.MIN_VALUE)); + + assertTrue(queue.enqueue(1)); + assertTrue(queue.enqueue(2)); + assertTrue(queue.enqueue(3)); + assertTrue(queue.enqueue(4)); + + assertEquals(4, queue.size()); + + assertEquals(1, queue.firstInt()); + assertEquals(1, queue.firstInt(Integer.MIN_VALUE)); + assertEquals(4, queue.lastInt()); + assertEquals(4, queue.lastInt(Integer.MIN_VALUE)); + + assertEquals(4, queue.size()); + + assertEquals(1, queue.dequeueInt()); + assertEquals(2, queue.dequeueInt()); + assertEquals(3, queue.dequeueInt()); + assertEquals(4, queue.dequeueInt()); + + assertEquals(0, queue.size()); + + assertEquals(Integer.MIN_VALUE, queue.firstInt(Integer.MIN_VALUE)); + assertEquals(Integer.MIN_VALUE, queue.lastInt(Integer.MIN_VALUE)); + assertEquals(Integer.MIN_VALUE, queue.dequeueInt(Integer.MIN_VALUE)); + + assertTrue(queue.enqueue(13)); + assertTrue(queue.enqueue(14)); + + assertEquals(2, queue.size()); + assertEquals(13, queue.firstInt()); + assertEquals(14, queue.lastInt()); + + assertTrue(queue.enqueueFirst(15)); + assertTrue(queue.enqueueFirst(16)); + + assertEquals(4, queue.size()); + assertEquals(16, queue.firstInt()); + assertEquals(14, queue.lastInt()); + + assertEquals(16, queue.dequeueInt()); + assertEquals(15, queue.dequeueInt()); + assertEquals(13, queue.dequeueInt()); + assertEquals(14, queue.dequeueInt()); + + assertEquals(0, queue.size()); + + assertEquals(Integer.MIN_VALUE, queue.firstInt(Integer.MIN_VALUE)); + assertEquals(Integer.MIN_VALUE, queue.lastInt(Integer.MIN_VALUE)); + assertEquals(Integer.MIN_VALUE, queue.dequeueInt(Integer.MIN_VALUE)); + + assertTrue(queue.enqueue(21)); + assertTrue(queue.enqueue(22)); + assertTrue(queue.enqueue(23)); + assertTrue(queue.enqueue(24)); + + assertEquals(4, queue.size()); + assertEquals(21, queue.firstInt()); + assertEquals(24, queue.lastInt()); + + assertEquals(24, queue.dequeueLastInt()); + assertEquals(23, queue.dequeueLastInt()); + assertEquals(22, queue.dequeueLastInt()); + assertEquals(21, queue.dequeueLastInt()); + } + + @Test + public void testForward() { + IntFixedArrayFIFOQueue queue = new IntFixedArrayFIFOQueue(32); + + for (int i = 0; i < queue.capacity(); i++) { + final int lim = queue.capacity() / 2; + + assertEquals(0, queue.size()); + + for (int j = 0; j < lim; j++) { + assertTrue(queue.enqueue(j)); + assertTrue(queue.enqueue(-j)); + + assertEquals(0, queue.firstInt()); + assertEquals(0, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(-j, queue.lastInt()); + assertEquals(-j, queue.lastInt(Integer.MAX_VALUE)); + } + + assertEquals(lim * 2, queue.size()); + + + for (int j = 0; j < lim; j++) { + assertEquals(j, queue.firstInt()); + assertEquals(j, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(1 - lim, queue.lastInt()); + assertEquals(1 - lim, queue.lastInt(Integer.MAX_VALUE)); + + assertEquals(j, queue.dequeueInt()); + assertEquals(-j, queue.dequeueInt(Integer.MAX_VALUE)); + } + + assertEquals(0, queue.size()); + } + } + + @Test + public void testBackward() { + IntFixedArrayFIFOQueue queue = new IntFixedArrayFIFOQueue(32); + + for (int i = 0; i < queue.capacity(); i++) { + final int lim = queue.capacity() / 2; + + assertEquals(0, queue.size()); + + for (int j = 0; j < lim; j++) { + assertTrue(queue.enqueueFirst(j)); + assertTrue(queue.enqueueFirst(-j)); + + assertEquals(-j, queue.firstInt()); + assertEquals(-j, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(0, queue.lastInt()); + assertEquals(0, queue.lastInt(Integer.MAX_VALUE)); + } + + assertEquals(lim * 2, queue.size()); + + + for (int j = 0; j < lim; j++) { + assertEquals(1 - lim, queue.firstInt()); + assertEquals(1 - lim, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(j, queue.lastInt()); + assertEquals(j, queue.lastInt(Integer.MAX_VALUE)); + + assertEquals(j, queue.dequeueLastInt()); + assertEquals(-j, queue.dequeueLastInt(Integer.MAX_VALUE)); + } + + assertEquals(0, queue.size()); + } + } + + @Test + public void testBothDirection() { + IntFixedArrayFIFOQueue queue = new IntFixedArrayFIFOQueue(32); + + for (int i = 0; i < queue.capacity(); i++) { + final int lim = queue.capacity() / 2; + + assertEquals(0, queue.size()); + + for (int j = 0; j < lim; j++) { + assertTrue(queue.enqueue(j)); + assertTrue(queue.enqueueFirst(-j)); + + assertEquals(-j, queue.firstInt()); + assertEquals(-j, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(j, queue.lastInt()); + assertEquals(j, queue.lastInt(Integer.MAX_VALUE)); + } + + assertEquals(lim * 2, queue.size()); + + + for (int j = lim - 1; j >= 0; j--) { + assertEquals(-j, queue.firstInt()); + assertEquals(-j, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(j, queue.lastInt()); + assertEquals(j, queue.lastInt(Integer.MAX_VALUE)); + + assertEquals(j, queue.dequeueLastInt()); + assertEquals(-j, queue.dequeueInt(Integer.MAX_VALUE)); + } + + assertEquals(0, queue.size()); + } + } + + @Test + public void testMixedDirection() { + IntFixedArrayFIFOQueue queue = new IntFixedArrayFIFOQueue(32); + + for (int i = 0; i < queue.capacity(); i++) { + final int lim = queue.capacity() / 2; + + assertEquals(0, queue.size()); + + for (int j = 0; j < lim; j++) { + assertTrue(queue.enqueue(j)); + assertTrue(queue.enqueue(-j)); + + assertEquals(0, queue.firstInt()); + assertEquals(0, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(-j, queue.lastInt()); + assertEquals(-j, queue.lastInt(Integer.MAX_VALUE)); + } + + assertEquals(lim * 2, queue.size()); + + + for (int j = lim - 1; j >= 0; j--) { + assertEquals(0, queue.firstInt()); + assertEquals(0, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(-j, queue.lastInt()); + assertEquals(-j, queue.lastInt(Integer.MAX_VALUE)); + + assertEquals(-j, queue.dequeueLastInt()); + assertEquals(j, queue.dequeueLastInt(Integer.MAX_VALUE)); + } + + assertEquals(0, queue.size()); + } + } + + @Test + public void testMixedDirection2() { + IntFixedArrayFIFOQueue queue = new IntFixedArrayFIFOQueue(32); + + for (int i = 0; i < queue.capacity(); i++) { + final int lim = queue.capacity() / 2; + + assertEquals(0, queue.size()); + + for (int j = 0; j < lim; j++) { + assertTrue(queue.enqueueFirst(j)); + assertTrue(queue.enqueueFirst(-j)); + + assertEquals(-j, queue.firstInt()); + assertEquals(-j, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(0, queue.lastInt()); + assertEquals(0, queue.lastInt(Integer.MAX_VALUE)); + } + + assertEquals(lim * 2, queue.size()); + + + for (int j = lim - 1; j >= 0; j--) { + assertEquals(-j, queue.firstInt()); + assertEquals(-j, queue.firstInt(Integer.MAX_VALUE)); + + assertEquals(0, queue.lastInt()); + assertEquals(0, queue.lastInt(Integer.MAX_VALUE)); + + assertEquals(-j, queue.dequeueInt()); + assertEquals(j, queue.dequeueInt(Integer.MAX_VALUE)); + } + + assertEquals(0, queue.size()); + } + } + + @Test + public void testClean() { + IntFixedArrayFIFOQueue queue = new IntFixedArrayFIFOQueue(16); + + assertEquals(16, queue.capacity()); + assertEquals(0, queue.size()); + + for (int i = 0; i < queue.capacity(); i++) { + assertTrue(queue.enqueue(i + queue.capacity())); + } + + assertEquals(16, queue.capacity()); + assertEquals(16, queue.size()); + + queue.clear(); + + assertEquals(16, queue.capacity()); + assertEquals(0, queue.size()); + + for (int i = 0; i < queue.capacity(); i++) { + assertTrue(queue.enqueue(i)); + } + + for (int i = 0; i < queue.capacity(); i++) { + assertEquals(i, queue.dequeueInt()); + } + } +}