1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dexgen.util;
18
19import java.util.Arrays;
20
21/**
22 * Simple list of {@code int}s.
23 */
24public final class IntList extends MutabilityControl {
25    /** {@code non-null;} immutable, no-element instance */
26    public static final IntList EMPTY = new IntList(0);
27
28    /** {@code non-null;} array of elements */
29    private int[] values;
30
31    /** {@code >= 0;} current size of the list */
32    private int size;
33
34    /** whether the values are currently sorted */
35    private boolean sorted;
36
37    static {
38        EMPTY.setImmutable();
39    }
40
41    /**
42     * Constructs a new immutable instance with the given element.
43     *
44     * @param value the sole value in the list
45     */
46    public static IntList makeImmutable(int value) {
47        IntList result = new IntList(1);
48
49        result.add(value);
50        result.setImmutable();
51
52        return result;
53    }
54
55    /**
56     * Constructs a new immutable instance with the given elements.
57     *
58     * @param value0 the first value in the list
59     * @param value1 the second value in the list
60     */
61    public static IntList makeImmutable(int value0, int value1) {
62        IntList result = new IntList(2);
63
64        result.add(value0);
65        result.add(value1);
66        result.setImmutable();
67
68        return result;
69    }
70
71    /**
72     * Constructs an empty instance with a default initial capacity.
73     */
74    public IntList() {
75        this(4);
76    }
77
78    /**
79     * Constructs an empty instance.
80     *
81     * @param initialCapacity {@code >= 0;} initial capacity of the list
82     */
83    public IntList(int initialCapacity) {
84        super(true);
85
86        try {
87            values = new int[initialCapacity];
88        } catch (NegativeArraySizeException ex) {
89            // Translate the exception.
90            throw new IllegalArgumentException("size < 0");
91        }
92
93        size = 0;
94        sorted = true;
95    }
96
97    /** {@inheritDoc} */
98    @Override
99    public int hashCode() {
100        int result = 0;
101
102        for (int i = 0; i < size; i++) {
103            result = (result * 31) + values[i];
104        }
105
106        return result;
107    }
108
109    /** {@inheritDoc} */
110    @Override
111    public boolean equals(Object other) {
112        if (other == this) {
113            return true;
114        }
115
116        if (! (other instanceof IntList)) {
117            return false;
118        }
119
120        IntList otherList = (IntList) other;
121
122        if (sorted != otherList.sorted) {
123            return false;
124        }
125
126        if (size != otherList.size) {
127            return false;
128        }
129
130        for (int i = 0; i < size; i++) {
131            if (values[i] != otherList.values[i]) {
132                return false;
133            }
134        }
135
136        return true;
137    }
138
139    /** {@inheritDoc} */
140    @Override
141    public String toString() {
142        StringBuffer sb = new StringBuffer(size * 5 + 10);
143
144        sb.append('{');
145
146        for (int i = 0; i < size; i++) {
147            if (i != 0) {
148                sb.append(", ");
149            }
150            sb.append(values[i]);
151        }
152
153        sb.append('}');
154
155        return sb.toString();
156    }
157
158    /**
159     * Gets the number of elements in this list.
160     */
161    public int size() {
162        return size;
163    }
164
165    /**
166     * Gets the indicated value.
167     *
168     * @param n {@code >= 0, < size();} which element
169     * @return the indicated element's value
170     */
171    public int get(int n) {
172        if (n >= size) {
173            throw new IndexOutOfBoundsException("n >= size()");
174        }
175
176        try {
177            return values[n];
178        } catch (ArrayIndexOutOfBoundsException ex) {
179            // Translate exception.
180            throw new IndexOutOfBoundsException("n < 0");
181        }
182    }
183
184    /**
185     * Sets the value at the given index.
186     *
187     * @param n {@code >= 0, < size();} which element
188     * @param value value to store
189     */
190    public void set(int n, int value) {
191        throwIfImmutable();
192
193        if (n >= size) {
194            throw new IndexOutOfBoundsException("n >= size()");
195        }
196
197        try {
198            values[n] = value;
199            sorted = false;
200        } catch (ArrayIndexOutOfBoundsException ex) {
201            // Translate the exception.
202            if (n < 0) {
203                throw new IllegalArgumentException("n < 0");
204            }
205        }
206    }
207
208    /**
209     * Adds an element to the end of the list. This will increase the
210     * list's capacity if necessary.
211     *
212     * @param value the value to add
213     */
214    public void add(int value) {
215        throwIfImmutable();
216
217        growIfNeeded();
218
219        values[size++] = value;
220
221        if (sorted && (size > 1)) {
222            sorted = (value >= values[size - 2]);
223        }
224    }
225
226    /**
227     * Inserts element into specified index, moving elements at and above
228     * that index up one. May not be used to insert at an index beyond the
229     * current size (that is, insertion as a last element is legal but
230     * no further).
231     *
232     * @param n {@code >= 0, <=size();} index of where to insert
233     * @param value value to insert
234     */
235    public void insert(int n, int value) {
236        if (n > size) {
237            throw new IndexOutOfBoundsException("n > size()");
238        }
239
240        growIfNeeded();
241
242        System.arraycopy (values, n, values, n+1, size - n);
243        values[n] = value;
244        size++;
245
246        sorted = sorted
247                && (n == 0 || value > values[n-1])
248                && (n == (size - 1) || value < values[n+1]);
249    }
250
251    /**
252     * Removes an element at a given index, shifting elements at greater
253     * indicies down one.
254     *
255     * @param n  {@code >=0, < size();} index of element to remove
256     */
257    public void removeIndex(int n) {
258        if (n >= size) {
259            throw new IndexOutOfBoundsException("n >= size()");
260        }
261
262        System.arraycopy (values, n + 1, values, n, size - n - 1);
263        size--;
264
265        // sort status is unchanged
266    }
267
268    /**
269     * Increases size of array if needed
270     */
271    private void growIfNeeded() {
272        if (size == values.length) {
273            // Resize.
274            int[] newv = new int[size * 3 / 2 + 10];
275            System.arraycopy(values, 0, newv, 0, size);
276            values = newv;
277        }
278    }
279
280    /**
281     * Returns the last element in the array without modifying the array
282     *
283     * @return last value in the array.
284     * @exception IndexOutOfBoundsException if stack is empty.
285     */
286    public int top() {
287        return get(size - 1);
288    }
289
290    /**
291     * Pops an element off the end of the list and decreasing the size by one.
292     *
293     * @return value from what was the last element.
294     * @exception IndexOutOfBoundsException if stack is empty.
295     */
296    public int pop() {
297        throwIfImmutable();
298
299        int result;
300
301        result = get(size-1);
302        size--;
303
304        return result;
305    }
306
307    /**
308     * Pops N elements off the end of the list and decreasing the size by N.
309     *
310     * @param n {@code >= 0;} number of elements to remove from end.
311     * @exception IndexOutOfBoundsException if stack is smaller than N
312     */
313    public void pop(int n) {
314        throwIfImmutable();
315
316        size -= n;
317    }
318
319    /**
320     * Shrinks the size of the list.
321     *
322     * @param newSize {@code >= 0;} the new size
323     */
324    public void shrink(int newSize) {
325        if (newSize < 0) {
326            throw new IllegalArgumentException("newSize < 0");
327        }
328
329        if (newSize > size) {
330            throw new IllegalArgumentException("newSize > size");
331        }
332
333        throwIfImmutable();
334
335        size = newSize;
336    }
337
338    /**
339     * Makes and returns a mutable copy of the list.
340     *
341     * @return {@code non-null;} an appropriately-constructed instance
342     */
343    public IntList mutableCopy() {
344        int sz = size;
345        IntList result = new IntList(sz);
346
347        for (int i = 0; i < sz; i++) {
348            result.add(values[i]);
349        }
350
351        return result;
352    }
353
354    /**
355     * Sorts the elements in the list in-place.
356     */
357    public void sort() {
358        throwIfImmutable();
359
360        if (!sorted) {
361            Arrays.sort(values, 0, size);
362            sorted = true;
363        }
364    }
365
366    /**
367     * Returns the index of the given value, or -1 if the value does not
368     * appear in the list.  This will do a binary search if the list is
369     * sorted or a linear search if not.
370     * @param value value to find
371     * @return index of value or -1
372     */
373    public int indexOf(int value) {
374        int ret = binarysearch(value);
375
376        return ret >= 0 ? ret : -1;
377
378    }
379
380    /**
381     * Performs a binary search on a sorted list, returning the index of
382     * the given value if it is present or
383     * {@code (-(insertion point) - 1)} if the value is not present.
384     * If the list is not sorted, then reverts to linear search and returns
385     * {@code -size()} if the element is not found.
386     *
387     * @param value value to find
388     * @return index of value or {@code (-(insertion point) - 1)} if the
389     * value is not present
390     */
391    public int binarysearch(int value) {
392        int sz = size;
393
394        if (!sorted) {
395            // Linear search.
396            for (int i = 0; i < sz; i++) {
397                if (values[i] == value) {
398                    return i;
399                }
400            }
401
402            return -sz;
403        }
404
405        /*
406         * Binary search. This variant does only one value comparison
407         * per iteration but does one more iteration on average than
408         * the variant that includes a value equality check per
409         * iteration.
410         */
411
412        int min = -1;
413        int max = sz;
414
415        while (max > (min + 1)) {
416            /*
417             * The guessIdx calculation is equivalent to ((min + max)
418             * / 2) but won't go wonky when min and max are close to
419             * Integer.MAX_VALUE.
420             */
421            int guessIdx = min + ((max - min) >> 1);
422            int guess = values[guessIdx];
423
424            if (value <= guess) {
425                max = guessIdx;
426            } else {
427                min = guessIdx;
428            }
429        }
430
431        if ((max != sz)) {
432            return (value == values[max]) ? max : (-max - 1);
433        } else {
434            return -sz - 1;
435        }
436    }
437
438
439    /**
440     * Returns whether or not the given value appears in the list.
441     * This will do a binary search if the list is sorted or a linear
442     * search if not.
443     *
444     * @see #sort
445     *
446     * @param value value to look for
447     * @return whether the list contains the given value
448     */
449    public boolean contains(int value) {
450        return indexOf(value) >= 0;
451    }
452}
453