import java.lang.reflect.Array; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.PrimitiveIterator; import java.util.function.Consumer; interface Buffer extends Iterable { static Buffer from(T[] buffer) throws IllegalArgumentException { if (buffer.length < 1) { throw new IllegalArgumentException(); } return new BoundedBufferArrayImpl<>(buffer); } static Buffer array(int capacity, Class classOfT) throws IllegalArgumentException { if (capacity < 1) { throw new IllegalArgumentException(); } var buffer = (T[]) Array.newInstance(classOfT, capacity); return new BoundedBufferArrayImpl<>(buffer); } static Buffer boundedList(int capacity) throws IllegalArgumentException { if (capacity < 1) { throw new IllegalArgumentException(); } return new BoundedBufferListImpl<>(capacity); } static Buffer unboundedList() throws IllegalArgumentException { return new UnboundedBufferListImpl<>(); } int capacity(); int size(); T get(); T get(int index); void add(T value); void clear(); default CappedIterator cappedIterator(int maxCount) { return new CappedIterator<>(iterator(), maxCount); } Iterator descendingIterator(); } static final class CappedIterator implements Iterator { private final Iterator iterator; private final int maxCount; private int count = 0; CappedIterator(Iterator iterator, int maxCount) { this.iterator = iterator; this.maxCount = maxCount; } public int count() { return count; } public boolean hasNext() { return count < maxCount && iterator.hasNext(); } public T next() throws NoSuchElementException { if (hasNext()) { var next = iterator.next(); count++; return next; } throw new NoSuchElementException(); } } static final class BoundedBufferArrayImpl implements Buffer { private final T[] buffer; private int size = 0, cursor = 0; BoundedBufferArrayImpl(T[] buffer) throws IllegalArgumentException { this.buffer = buffer; } public int capacity() { return buffer.length; } public int size() { return size; } public T get() { return buffer[cursor]; } public T get(int index) { return buffer[(cursor + index) % buffer.length]; } public void add(T value) { cursor = Math.floorMod(cursor - 1, buffer.length); buffer[cursor] = value; size = Math.max(size + 1, buffer.length); } public void clear() { size = 0; Arrays.fill(buffer, null); } public Iterator iterator() { return new AscendingIterator(); } public Iterator descendingIterator() { return new DescendingIterator(); } private class AscendingIterator implements Iterator { private int pos = cursor, i = 0; public boolean hasNext() { return i < size; } public T next() throws NoSuchElementException { if (hasNext()) { var s = buffer[pos]; pos = (pos + 1) % buffer.length; i++; return s; } throw new NoSuchElementException(); } } private class DescendingIterator implements Iterator { private int pos = size - 1, i = 0; public boolean hasNext() { return i > 0; } public T next() throws NoSuchElementException { if (hasNext()) { var s = buffer[pos]; pos = Math.floorMod(pos - 1, buffer.length); i--; return s; } throw new NoSuchElementException(); } } } static final class BoundedBufferListImpl implements Buffer { private final LinkedList buffer = new LinkedList<>(); private final int capacity; BoundedBufferListImpl(int capacity) { this.capacity = capacity; } public int capacity() { return capacity; } public int size() { return buffer.size(); } public T get() { return buffer.peekFirst(); } public T get(int index) { return buffer.get(index); } public void add(T value) { if (size() == capacity) { buffer.pollLast(); } buffer.addFirst(value); } public void clear() { buffer.clear(); } public Iterator iterator() { return buffer.iterator(); } public Iterator descendingIterator() { return buffer.descendingIterator(); } } static final class UnboundedBufferListImpl implements Buffer { private final LinkedList buffer = new LinkedList<>(); UnboundedBufferListImpl() { } public int capacity() { return Integer.MAX_VALUE; } public int size() { return buffer.size(); } public T get() { return buffer.peekFirst(); } public T get(int index) { return buffer.get(index); } public void add(T value) { buffer.addFirst(value); } public void clear() { buffer.clear(); } public Iterator iterator() { return buffer.iterator(); } public Iterator descendingIterator() { return buffer.descendingIterator(); } }