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    public int[] array;
031    public int size;
032
033    /**
034     * Constructs a new int array container with the specified initial backing array size.
035     * @param initialCapacity The initial capacity of the backing array.
036     */
037    public IntArrayContainer(int initialCapacity) {
038        array = new int[initialCapacity];
039        size = 0;
040    }
041
042    /**
043     * Grows the backing array, copying the elements.
044     * @param requestedSize The size to grow the array to.
045     */
046    public void grow(int requestedSize) {
047        if (requestedSize > array.length) {
048            // overflow-conscious code
049            int oldCapacity = array.length;
050            int newCapacity = oldCapacity + (oldCapacity >> 1);
051            if (newCapacity - requestedSize < 0) {
052                newCapacity = requestedSize;
053            }
054            // minCapacity is usually close to size, so this is a win:
055            array = Arrays.copyOf(array, newCapacity);
056        }
057    }
058
059    /**
060     * Returns a copy of the elements in use.
061     * @return A copy of the elements.
062     */
063    public int[] copy() {
064        return Arrays.copyOf(array,size);
065    }
066
067    /**
068     * Overwrites values from the supplied array into this array.
069     * @param otherArray The array to copy from.
070     */
071    public void fill(int[] otherArray) {
072        if (otherArray.length > array.length) {
073            array = Arrays.copyOf(otherArray,otherArray.length);
074        } else {
075            System.arraycopy(otherArray,0,array,0,otherArray.length);
076        }
077        size = otherArray.length;
078    }
079
080    /**
081     * Overwrites values in this array with the supplied array.
082     * @param other The array to copy from.
083     */
084    public void fill(IntArrayContainer other) {
085        if (other.array.length > array.length) {
086            array = Arrays.copyOf(other.array,other.size);
087        } else {
088            System.arraycopy(other.array,0,array,0,other.size);
089        }
090        size = other.size;
091    }
092
093    /**
094     * Copies from input to output excluding the values in otherArray.
095     * <p>
096     * This assumes both input and otherArray are sorted. Behaviour is undefined if they aren't.
097     * @param input The input container.
098     * @param otherArray Another (sorted) int array.
099     * @param output The container to write the output to.
100     */
101    public static void removeOther(IntArrayContainer input, int[] otherArray, IntArrayContainer output) {
102        //logger.info("input.size = " + input.size + ", otherArray.length = " + otherArray.length + ", output.length = " + output.array.length);
103        int newSize = input.size - otherArray.length;
104        if (newSize > output.array.length) {
105            output.grow(newSize);
106        }
107
108        int[] inputArray = input.array;
109        int inputSize = input.size;
110        int[] outputArray = output.array;
111        //logger.info("input = " + Arrays.toString(inputArray));
112        //logger.info("otherArray = " + Arrays.toString(otherArray));
113
114        int i = 0; //index into input
115        int j = 0; //index into otherArray
116        int k = 0; //index into output
117        while (i < inputSize) {
118            if (j == otherArray.length) {
119                // Reached end of other, copy from input
120                outputArray[k] = inputArray[i];
121                i++;
122                k++;
123            } else if (inputArray[i] < otherArray[j]) {
124                // Input less than other, copy input
125                outputArray[k] = inputArray[i];
126                i++;
127                k++;
128            } else if (inputArray[i] == otherArray[j]) {
129                // skip both
130                i++;
131                j++;
132            } else {
133                // other less than input, skip
134                j++;
135            }
136        }
137        output.size = k;
138        assert(k == newSize);
139        //logger.info("output = " + Arrays.toString(outputArray));
140    }
141
142    /**
143     * Merges the list of int arrays into a single int array, using the two supplied buffers.
144     * Requires that all arrays in the list are sorted, and that they contain unique values.
145     * @param input A list of int arrays.
146     * @param firstBuffer A buffer.
147     * @param secondBuffer Another buffer.
148     * @return A sorted array containing all the elements from the input.
149     */
150    public static int[] merge(List<int[]> input, IntArrayContainer firstBuffer, IntArrayContainer secondBuffer) {
151        if (input.size() > 0) {
152            firstBuffer.fill(input.get(0));
153            for (int i = 0; i < input.size(); i++) {
154                merge(firstBuffer,input.get(i),secondBuffer);
155                IntArrayContainer tmp = secondBuffer;
156                secondBuffer = firstBuffer;
157                firstBuffer = tmp;
158            }
159            return firstBuffer.copy();
160        } else {
161            return new int[0];
162        }
163    }
164
165    /**
166     * Merges input and otherArray writing to output.
167     * This assumes both input and otherArray are sorted. Behaviour is undefined if they aren't.
168     * @param input The input container.
169     * @param otherArray Another (sorted) int array.
170     * @param output The container to write the output to.
171     */
172    public static void merge(IntArrayContainer input, int[] otherArray, IntArrayContainer output) {
173        int newSize = input.size + otherArray.length;
174        if (newSize > output.array.length) {
175            output.grow(newSize);
176        }
177
178        int[] inputArray = input.array;
179        int inputSize = input.size;
180        int[] outputArray = output.array;
181
182        int i = 0; //index into input
183        int j = 0; //index into otherArray
184        int k = 0; //index into output
185        while ((i < inputSize) || (j < otherArray.length)) {
186            if (i == inputSize) {
187                // Reached end of input, copy from other
188                outputArray[k] = otherArray[j];
189                j++;
190                k++;
191            } else if (j == otherArray.length) {
192                // Reached end of other, copy from input
193                outputArray[k] = inputArray[i];
194                i++;
195                k++;
196            } else if (inputArray[i] < otherArray[j]) {
197                // Input less than other, copy input
198                outputArray[k] = inputArray[i];
199                i++;
200                k++;
201            } else {
202                // other less than input, copy other
203                outputArray[k] = otherArray[j];
204                j++;
205                k++;
206            }
207        }
208        output.size = k;
209        assert(k == newSize);
210        //logger.info("input = " + Arrays.toString(inputArray));
211        //logger.info("otherArray = " + Arrays.toString(otherArray));
212        //logger.info("output = " + Arrays.toString(outputArray));
213    }
214}