001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009package imagingbook.common.util;
010
011import java.util.Arrays;
012import java.util.Comparator;
013import java.util.Objects;
014import java.util.PriorityQueue;
015
016/**
017 * Defines a generic container for "comparable" elements that allows sorted insertions without exceeding the predefined
018 * capacity. This implementation is based on {@link PriorityQueue}. New items are inserted using the elements natural
019 * ordering or a supplied comparator. Once the predefined capacity is exceeded, redundant items are removed from the
020 * underlying priority queue. Example (using natural ordering of Integer):
021 * <pre>
022 *     SortedVector&lt;Integer&gt; sv = new SortedVector&lt;&gt;(new Integer[3]);
023 *     sv.insert(5);
024 *     sv.insert(2);
025 *     sv.insert(9);
026 *     sv.insert(1);
027 *     Integer[] arr = sv.getArray();       // [9, 5, 2]
028 * </pre>
029 *
030 * @param <T> the generic element type
031 * @author WB
032 * @version 2022/12/14
033 */
034public class SortedVector<T extends Comparable<T>> {    // extends PriorityQueue<T>
035
036    private final int capacity;
037    private final PriorityQueue<T> queue;
038    private final  Comparator<T> comp;
039    private final T[] arr;
040
041    /**
042     * Constructor using a specific comparator. The length of the supplied array specifies the capacity of the
043     * new container.
044     *
045     * @param arr an array of the generic element type (content is ignored)
046     * @param comp comparator
047     */
048    public SortedVector(T[] arr, Comparator<T> comp) {
049        if (arr.length < 1) {
050            throw new IllegalArgumentException("supplied array must be non-empty");
051        }
052        this.capacity = arr.length;
053        this.queue = new PriorityQueue<T>(capacity, comp);
054        this.comp = comp;
055        this.arr = arr;
056        Arrays.fill(this.arr, null);
057    }
058
059    /**
060     * Constructor, using the natural-order comparator of the generic element type. The length of the supplied array
061     * specifies the capacity of this container.
062     *
063     * @param arr an array of the generic element type (content is ignored)
064     */
065    public SortedVector(T[] arr) {
066        this(arr, Comparator.naturalOrder());
067    }
068
069    /**
070     * Tries to adds a new item to this {@link SortedVector} instance. It is inserted into the vector if it is "greater"
071     * than the current "smallest" element (head) in this vector, otherwise it is discarded. The meaning of "greater"
072     * and "smallest" depend on the associated comparator (see {@link #SortedVector(Comparable[], Comparator)}).
073     * Items contained in the vector are not replaced by new items with identical values.
074     *
075     * @param item to add
076     */
077    public void add(T item) {
078        if (Objects.isNull(item)) {
079            throw new IllegalArgumentException("cannot add null item");
080        }
081        // System.out.println("     head = " + queue.peek());
082        // case1: queue is not yet full, add item without trimming the queue:
083        if (queue.size() < capacity) {
084            queue.add(item);
085            return;
086        }
087        // case2: queue is full, add item only if greater than head
088        T head = queue.peek();                  // head should never be null
089
090        if (comp.compare(item, head) > 0) {     // item is greater than head (= current min.)
091            // System.out.println("   removing " + head);
092            queue.poll();                       // remove "smallest" queue item (head)
093            queue.add(item);
094        }
095    }
096
097    /**
098     * Returns an array holding the elements of this {@link SortedVector} instance. The elements are sorted according to
099     * the specified comparator. The length of the array is equal to the value returned by {@link #size()}; the maximum
100     * length equals the capacity of this vector. If the {@link SortedVector} was completely filled to its capacity, the
101     * returned array is identical to the one that was passed to the constructor. The array contains no {@code null}
102     * elements. The returned array is sorted in descending order, such that the "greatest" item added to the
103     * {@link SortedVector} appears at its front (i.e., in position 0).
104     *
105     * @return a sorted array of elements
106     */
107    public T[] getArray() {
108        T[] arr2 = queue.toArray(arr);                      // arr = arr2
109        T[] arr3 = (size() < capacity) ?
110                Arrays.copyOf(arr2, queue.size()) :  arr2;  // trim null elements if necessary
111        // we need to sort once more since PriorityQueue#toArray() does not guarantee any order!
112        Arrays.sort(arr3, comp.reversed());     // move largest item to front (arr3[0] = max)
113        return arr3;
114    }
115
116    /**
117     * Returns the current number of non-null items in this vector.
118     * @return the number of contained items
119     */
120    public int size() {
121        return queue.size();
122    }
123
124    @Override
125    public String toString() {
126        return this.getClass().getSimpleName() + Arrays.toString(this.getArray());
127    }
128
129}