001/*
002 * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.tribuo.common.tree.impl;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.logging.Logger;
022
023/**
024 * An array container which maintains the array and the size.
025 * This class is barely more than a Tuple, it's up to you to maintain the size invariant.
026 */
027public class IntArrayContainer {
028    private static final Logger logger = Logger.getLogger(IntArrayContainer.class.getName());
029
030    /**
031     * The array of ints.
032     */
033    public int[] array;
034    /**
035     * The number of elements in the array.
036     */
037    public int size;
038
039    /**
040     * Constructs a new int array container with the specified initial backing array size.
041     * @param initialCapacity The initial capacity of the backing array.
042     */
043    public IntArrayContainer(int initialCapacity) {
044        array = new int[initialCapacity];
045        size = 0;
046    }
047
048    /**
049     * Grows the backing array, copying the elements.
050     * @param requestedSize The size to grow the array to.
051     */
052    public void grow(int requestedSize) {
053        if (requestedSize > array.length) {
054            // overflow-conscious code
055            int oldCapacity = array.length;
056            int newCapacity = oldCapacity + (oldCapacity >> 1);
057            if (newCapacity - requestedSize < 0) {
058                newCapacity = requestedSize;
059            }
060            // minCapacity is usually close to size, so this is a win:
061            array = Arrays.copyOf(array, newCapacity);
062        }
063    }
064
065    /**
066     * Returns a copy of the elements in use.
067     * @return A copy of the elements.
068     */
069    public int[] copy() {
070        return Arrays.copyOf(array,size);
071    }
072
073    /**
074     * Overwrites values from the supplied array into this array.
075     * <p>
076     * Copies in all the other values and sets the length of this array to the length of the other array.
077     * @param otherArray The array to copy from.
078     */
079    public void fill(int[] otherArray) {
080        if (otherArray.length > array.length) {
081            array = Arrays.copyOf(otherArray,otherArray.length);
082        } else {
083            System.arraycopy(otherArray,0,array,0,otherArray.length);
084        }
085        size = otherArray.length;
086    }
087
088    /**
089     * Overwrites values in this array with the supplied array.
090     * <p>
091     * Copies in all the other values and sets the length of this array to the length of the other array.
092     * @param other The array to copy from.
093     */
094    public void fill(IntArrayContainer other) {
095        if (other.size > array.length) {
096            array = Arrays.copyOf(other.array,other.size);
097        } else {
098            System.arraycopy(other.array,0,array,0,other.size);
099        }
100        size = other.size;
101    }
102
103    /**
104     * Copies from input to output excluding the values in otherArray.
105     * <p>
106     * This assumes both input and otherArray are sorted. Behaviour is undefined if they aren't.
107     * @param input The input container.
108     * @param otherArray Another (sorted) int array.
109     * @param output The container to write the output to.
110     */
111    public static void removeOther(IntArrayContainer input, int[] otherArray, IntArrayContainer output) {
112        //logger.info("input.size = " + input.size + ", otherArray.length = " + otherArray.length + ", output.length = " + output.array.length);
113        int newSize = input.size - otherArray.length;
114        if (newSize > output.array.length) {
115            output.grow(newSize);
116        }
117
118        int[] inputArray = input.array;
119        int inputSize = input.size;
120        int[] outputArray = output.array;
121        //logger.info("input = " + Arrays.toString(inputArray));
122        //logger.info("otherArray = " + Arrays.toString(otherArray));
123
124        int i = 0; //index into input
125        int j = 0; //index into otherArray
126        int k = 0; //index into output
127        while (i < inputSize) {
128            if (j == otherArray.length) {
129                // Reached end of other, copy from input
130                outputArray[k] = inputArray[i];
131                i++;
132                k++;
133            } else if (inputArray[i] < otherArray[j]) {
134                // Input less than other, copy input
135                outputArray[k] = inputArray[i];
136                i++;
137                k++;
138            } else if (inputArray[i] == otherArray[j]) {
139                // skip both
140                i++;
141                j++;
142            } else {
143                // other less than input, skip
144                j++;
145            }
146        }
147        output.size = k;
148        //logger.info("output = " + Arrays.toString(outputArray));
149    }
150
151    /**
152     * Merges the list of int arrays into a single int array, using the two supplied buffers.
153     * Requires that all arrays in the list are sorted, and that they contain unique values.
154     * @param input A list of int arrays.
155     * @param firstBuffer A buffer.
156     * @param secondBuffer Another buffer.
157     * @return A sorted array containing all the elements from the input.
158     */
159    public static int[] merge(List<int[]> input, IntArrayContainer firstBuffer, IntArrayContainer secondBuffer) {
160        if (!input.isEmpty()) {
161            firstBuffer.fill(input.get(0));
162            for (int i = 1; i < input.size(); i++) {
163                merge(firstBuffer,input.get(i),secondBuffer);
164                IntArrayContainer tmp = secondBuffer;
165                secondBuffer = firstBuffer;
166                firstBuffer = tmp;
167            }
168            return firstBuffer.copy();
169        } else {
170            return new int[0];
171        }
172    }
173
174    /**
175     * Merges input and otherArray writing to output.
176     * This assumes both input and otherArray are sorted. Behaviour is undefined if they aren't.
177     * @param input The input container.
178     * @param otherArray Another (sorted) int array.
179     * @param output The container to write the output to.
180     */
181    public static void merge(IntArrayContainer input, int[] otherArray, IntArrayContainer output) {
182        int newSize = input.size + otherArray.length;
183        if (newSize > output.array.length) {
184            output.grow(newSize);
185        }
186
187        int[] inputArray = input.array;
188        int inputSize = input.size;
189        int[] outputArray = output.array;
190
191        int i = 0; //index into input
192        int j = 0; //index into otherArray
193        int k = 0; //index into output
194        while ((i < inputSize) || (j < otherArray.length)) {
195            if (i == inputSize) {
196                // Reached end of input, copy from other
197                outputArray[k] = otherArray[j];
198                j++;
199                k++;
200            } else if (j == otherArray.length) {
201                // Reached end of other, copy from input
202                outputArray[k] = inputArray[i];
203                i++;
204                k++;
205            } else if (inputArray[i] < otherArray[j]) {
206                // Input less than other, copy input
207                outputArray[k] = inputArray[i];
208                i++;
209                k++;
210            } else {
211                // other less than input, copy other
212                outputArray[k] = otherArray[j];
213                j++;
214                k++;
215            }
216        }
217        output.size = k;
218        assert(k == newSize);
219        //logger.info("input = " + Arrays.toString(inputArray));
220        //logger.info("otherArray = " + Arrays.toString(otherArray));
221        //logger.info("output = " + Arrays.toString(outputArray));
222    }
223}