001/*
002 * Copyright (c) 2015-2020, 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     * @param otherArray The array to copy from.
076     */
077    public void fill(int[] otherArray) {
078        if (otherArray.length > array.length) {
079            array = Arrays.copyOf(otherArray,otherArray.length);
080        } else {
081            System.arraycopy(otherArray,0,array,0,otherArray.length);
082        }
083        size = otherArray.length;
084    }
085
086    /**
087     * Overwrites values in this array with the supplied array.
088     * @param other The array to copy from.
089     */
090    public void fill(IntArrayContainer other) {
091        if (other.array.length > array.length) {
092            array = Arrays.copyOf(other.array,other.size);
093        } else {
094            System.arraycopy(other.array,0,array,0,other.size);
095        }
096        size = other.size;
097    }
098
099    /**
100     * Copies from input to output excluding the values in otherArray.
101     * <p>
102     * This assumes both input and otherArray are sorted. Behaviour is undefined if they aren't.
103     * @param input The input container.
104     * @param otherArray Another (sorted) int array.
105     * @param output The container to write the output to.
106     */
107    public static void removeOther(IntArrayContainer input, int[] otherArray, IntArrayContainer output) {
108        //logger.info("input.size = " + input.size + ", otherArray.length = " + otherArray.length + ", output.length = " + output.array.length);
109        int newSize = input.size - otherArray.length;
110        if (newSize > output.array.length) {
111            output.grow(newSize);
112        }
113
114        int[] inputArray = input.array;
115        int inputSize = input.size;
116        int[] outputArray = output.array;
117        //logger.info("input = " + Arrays.toString(inputArray));
118        //logger.info("otherArray = " + Arrays.toString(otherArray));
119
120        int i = 0; //index into input
121        int j = 0; //index into otherArray
122        int k = 0; //index into output
123        while (i < inputSize) {
124            if (j == otherArray.length) {
125                // Reached end of other, copy from input
126                outputArray[k] = inputArray[i];
127                i++;
128                k++;
129            } else if (inputArray[i] < otherArray[j]) {
130                // Input less than other, copy input
131                outputArray[k] = inputArray[i];
132                i++;
133                k++;
134            } else if (inputArray[i] == otherArray[j]) {
135                // skip both
136                i++;
137                j++;
138            } else {
139                // other less than input, skip
140                j++;
141            }
142        }
143        output.size = k;
144        assert(k == newSize);
145        //logger.info("output = " + Arrays.toString(outputArray));
146    }
147
148    /**
149     * Merges the list of int arrays into a single int array, using the two supplied buffers.
150     * Requires that all arrays in the list are sorted, and that they contain unique values.
151     * @param input A list of int arrays.
152     * @param firstBuffer A buffer.
153     * @param secondBuffer Another buffer.
154     * @return A sorted array containing all the elements from the input.
155     */
156    public static int[] merge(List<int[]> input, IntArrayContainer firstBuffer, IntArrayContainer secondBuffer) {
157        if (input.size() > 0) {
158            firstBuffer.fill(input.get(0));
159            for (int i = 0; i < input.size(); i++) {
160                merge(firstBuffer,input.get(i),secondBuffer);
161                IntArrayContainer tmp = secondBuffer;
162                secondBuffer = firstBuffer;
163                firstBuffer = tmp;
164            }
165            return firstBuffer.copy();
166        } else {
167            return new int[0];
168        }
169    }
170
171    /**
172     * Merges input and otherArray writing to output.
173     * This assumes both input and otherArray are sorted. Behaviour is undefined if they aren't.
174     * @param input The input container.
175     * @param otherArray Another (sorted) int array.
176     * @param output The container to write the output to.
177     */
178    public static void merge(IntArrayContainer input, int[] otherArray, IntArrayContainer output) {
179        int newSize = input.size + otherArray.length;
180        if (newSize > output.array.length) {
181            output.grow(newSize);
182        }
183
184        int[] inputArray = input.array;
185        int inputSize = input.size;
186        int[] outputArray = output.array;
187
188        int i = 0; //index into input
189        int j = 0; //index into otherArray
190        int k = 0; //index into output
191        while ((i < inputSize) || (j < otherArray.length)) {
192            if (i == inputSize) {
193                // Reached end of input, copy from other
194                outputArray[k] = otherArray[j];
195                j++;
196                k++;
197            } else if (j == otherArray.length) {
198                // Reached end of other, copy from input
199                outputArray[k] = inputArray[i];
200                i++;
201                k++;
202            } else if (inputArray[i] < otherArray[j]) {
203                // Input less than other, copy input
204                outputArray[k] = inputArray[i];
205                i++;
206                k++;
207            } else {
208                // other less than input, copy other
209                outputArray[k] = otherArray[j];
210                j++;
211                k++;
212            }
213        }
214        output.size = k;
215        assert(k == newSize);
216        //logger.info("input = " + Arrays.toString(inputArray));
217        //logger.info("otherArray = " + Arrays.toString(otherArray));
218        //logger.info("output = " + Arrays.toString(outputArray));
219    }
220}