001package org.clafer.common;
002
003import gnu.trove.iterator.TIntIterator;
004import gnu.trove.list.array.TIntArrayList;
005import java.lang.reflect.Array;
006import java.util.ArrayList;
007import java.util.Arrays;
008import java.util.Collections;
009import java.util.Iterator;
010import java.util.List;
011import java.util.Random;
012import util.iterators.IntIterator;
013
014/**
015 * Various static utility functions.
016 *
017 * @author jimmy
018 */
019public class Util {
020
021    private Util() {
022    }
023
024    /**
025     * Check equality in a null-friendly fashion.
026     *
027     * @param a an object
028     * @param b an object to compare with a
029     * @return true if and only if a equals b, false otherwise
030     */
031    public static boolean equals(Object a, Object b) {
032        return (a == b) || (a != null && a.equals(b));
033    }
034
035    /**
036     * Compute a hash code in a null-friendly fashion.
037     *
038     * @param o the object to compute the hash for
039     * @return the hash code of the object or 0 if null
040     */
041    public static int hashCode(Object o) {
042        return o != null ? o.hashCode() : 0;
043    }
044
045    public static int permutation(int n, int r) {
046        if (n < 0) {
047            throw new IllegalArgumentException();
048        }
049        int permutation = 1;
050        for (int i = 0; i < r; i++) {
051            permutation *= n - i;
052        }
053        return permutation;
054    }
055
056    /**
057     * @param low the lowest integer in the range
058     * @param high the highest integer in the range
059     * @return an array of all the values between low (inclusive) and high
060     * (inclusive) in order
061     */
062    public static int[] range(int low, int high) {
063        if (low > high) {
064            throw new IllegalArgumentException();
065        }
066        int[] range = new int[high - low + 1];
067        for (int i = 0; i < range.length; i++) {
068            range[i] = i + low;
069        }
070        return range;
071    }
072
073    /**
074     * @param from the lowest integer in the range
075     * @param to the integer after the highest integer in the range
076     * @return an array of all the values between from (inclusive) and to
077     * (exclusive) in order
078     */
079    public static int[] fromTo(int from, int to) {
080        if (from > to) {
081            throw new IllegalArgumentException();
082        }
083        int[] range = new int[to - from];
084        for (int i = 0; i < range.length; i++) {
085            range[i] = i + from;
086        }
087        return range;
088    }
089
090    /**
091     * Returns the position of all the {@code true} elements in the array. The
092     * positions are returned in sorted order.
093     *
094     * @param array the array
095     * @return the position of all the {@code true} elements
096     */
097    public static int[] trues(boolean[] array) {
098        return boolIndices(array, true);
099    }
100
101    /**
102     * Returns the position of all the {@code false} elements in the array. The
103     * positions are returned in sorted order.
104     *
105     * @param array the array
106     * @return the position of all the {@code false} elements
107     */
108    public static int[] falses(boolean[] array) {
109        return boolIndices(array, false);
110    }
111
112    private static int[] boolIndices(boolean[] bs, boolean val) {
113        int count = 0;
114        for (boolean b : bs) {
115            if (b == val) {
116                count++;
117            }
118        }
119        int[] vals = new int[count];
120        count = 0;
121        for (int i = 0; i < bs.length && count < vals.length; i++) {
122            if (bs[i] == val) {
123                vals[count++] = i;
124            }
125        }
126        assert count == vals.length;
127        return vals;
128    }
129
130    /**
131     * Randomly shuffle an array in place.
132     *
133     * @param array the array to shuffle
134     * @param rand the random number generator
135     */
136    public static <T> void shuffle(T[] array, Random rand) {
137        for (int i = 0; i < array.length; i++) {
138            int index = rand.nextInt(array.length);
139            // Simple swap
140            T temp = array[index];
141            array[index] = array[i];
142            array[i] = temp;
143        }
144    }
145
146    /**
147     * Reverse part of an array in place.
148     *
149     * @param array the array to reverse
150     * @param to reverse from index 0 to here
151     */
152    public static void reverse(int[] array, int to) {
153        for (int j = 0; j < to / 2; j++) {
154            int temp = array[j];
155            array[j] = array[to - j - 1];
156            array[to - j - 1] = temp;
157        }
158    }
159
160    /**
161     * Reverse an array in place.
162     *
163     * @param array the array to reverse
164     */
165    public static void reverse(int[] array) {
166        reverse(array, array.length);
167    }
168
169    /**
170     * Reverse part of an array in place.
171     *
172     * @param <T> the type of the elements
173     * @param array the array to reverse
174     * @param to reverse from index 0 to here
175     */
176    public static <T> void reverse(T[] array, int to) {
177        for (int j = 0; j < to / 2; j++) {
178            T temp = array[j];
179            array[j] = array[to - j - 1];
180            array[to - j - 1] = temp;
181        }
182    }
183
184    /**
185     * Reverse an array in place.
186     *
187     * @param <T> the type of the elements
188     * @param array the array to reverse
189     */
190    public static <T> void reverse(T[] array) {
191        reverse(array, array.length);
192    }
193
194    /**
195     * Check if the array contains the item at least once.
196     *
197     * @param item check if this item exists in the array
198     * @param array the array that may contain the item
199     * @return {@code true} if and only if item is in array, {@code false}
200     * otherwise
201     */
202    public static boolean in(int item, int[] array) {
203        for (int a : array) {
204            if (a == item) {
205                return true;
206            }
207        }
208        return false;
209    }
210
211    /**
212     * Check if the array contains the item at least once.
213     *
214     * @param <T> the type of the elements
215     * @param item check if this item exists in the array
216     * @param array the array that may contain the item
217     * @return {@code true} if and only if item is in array, {@code false}
218     * otherwise
219     */
220    public static <T> boolean in(T item, T[] array) {
221        for (T a : array) {
222            if (equals(item, a)) {
223                return true;
224            }
225        }
226        return false;
227    }
228
229    /**
230     * Check if every element in the array is unique.
231     *
232     * @param array the array
233     * @return {@code true} if and only if the elements in the array never
234     * repeat, {@code false} otherwise
235     */
236    public static boolean isUnique(int[] array) {
237        for (int i = 0; i < array.length; i++) {
238            for (int j = i + 1; j < array.length; j++) {
239                if (array[i] == array[j]) {
240                    return false;
241                }
242            }
243        }
244        return true;
245    }
246
247    /**
248     * Check if every element in the array is unique.
249     *
250     * @param <T> the type of the elements
251     * @param array the array
252     * @return {@code true} if and only if the elements in the array never
253     * repeat, {@code false} otherwise
254     */
255    public static <T> boolean isUnique(T[] array) {
256        for (int i = 0; i < array.length; i++) {
257            for (int j = i + 1; j < array.length; j++) {
258                if (equals(array[i], array[j])) {
259                    return false;
260                }
261            }
262        }
263        return true;
264    }
265
266    /**
267     * Functional-programming cons. Nondestructive.
268     *
269     * @param <T> the type of the elements
270     * @param head the beginning of the new list
271     * @param tail the end of the new list
272     * @return a copy of the original list with head appended at the start
273     */
274    public static <T> List<T> cons(T head, List<? extends T> tail) {
275        List<T> r = new ArrayList<>(tail.size() + 1);
276        r.add(head);
277        r.addAll(tail);
278        return r;
279    }
280
281    /**
282     * Functional-programming snoc. Nondestructive.
283     *
284     * @param <T> the type of the elements
285     * @param head the beginning of the new list
286     * @param tail the end of the new list
287     * @return a copy of the original list with tail appended at the end
288     */
289    public static <T> List<T> snoc(List<? extends T> head, T tail) {
290        List<T> r = new ArrayList<>(head.size() + 1);
291        r.addAll(head);
292        r.add(tail);
293        return r;
294    }
295
296    /**
297     * Append the item at the start of the array. Nondestructive.
298     *
299     * @param <T> the type of the elements
300     * @param item the beginning of the new array
301     * @param array the end of the new array
302     * @return a copy of the original array with item appended at the start
303     */
304    public static <T> T[] cons(T item, T[] array) {
305        T[] r = Arrays.copyOf(array, array.length + 1);
306        for (int i = r.length - 1; i > 0; i--) {
307            r[i] = r[i - 1];
308        }
309        r[0] = item;
310        return r;
311    }
312
313    /**
314     * Append the item at the end of the array. Nondestructive.
315     *
316     * @param <T> the type of the elements
317     * @param array the beginning of the new array
318     * @param item the end of the new array
319     * @return a copy of the original array with item appended at the end
320     */
321    public static <T> T[] snoc(T[] array, T item) {
322        T[] r = Arrays.copyOf(array, array.length + 1);
323        r[array.length] = item;
324        return r;
325    }
326
327    /**
328     * Append the item at the start of the array. Nondestructive.
329     *
330     * @param item the beginning of the new array
331     * @param array the end of the new array
332     * @return a copy of the original array with item appended at the start
333     */
334    public static int[] cons(int item, int[] array) {
335        int[] r = new int[array.length + 1];
336        System.arraycopy(array, 0, r, 1, array.length);
337        r[0] = item;
338        return r;
339    }
340
341    /**
342     * Append the item at the end of the array. Nondestructive.
343     *
344     * @param array the beginning of the new array
345     * @param item the end of the new array
346     * @return a copy of the original array with item appended at the end
347     */
348    public static int[] snoc(int[] array, int item) {
349        int[] r = Arrays.copyOf(array, array.length + 1);
350        r[array.length] = item;
351        return r;
352    }
353
354    /**
355     * Concatenates all the arrays in the given order into one array. Must be
356     * supplied at least one array. Nondestructive.
357     *
358     * @param <T> the type of the elements
359     * @param arrays the array of arrays
360     * @return the concatenation of all the arrays
361     */
362    public static <T> T[] concat(T[][] arrays) {
363        switch (arrays.length) {
364            case 0:
365                @SuppressWarnings("unchecked") T[] empty = (T[]) Array.newInstance(arrays.getClass().getComponentType().getComponentType(), 0);
366                return empty;
367            case 1:
368                return arrays[0];
369            default:
370                int length = 0;
371                for (T[] array : arrays) {
372                    length += array.length;
373                }
374                int offset = 0;
375                @SuppressWarnings("unchecked") T[] concat = (T[]) Array.newInstance(arrays.getClass().getComponentType().getComponentType(), length);
376                for (T[] array : arrays) {
377                    System.arraycopy(array, 0, concat, offset, array.length);
378                    offset += array.length;
379                }
380                return concat;
381        }
382    }
383
384    /**
385     * Repeat the item.
386     *
387     * @param <T> the type of the item
388     * @param item the item
389     * @param times the number of times to repeat
390     * @return an array containing only the item
391     */
392    public static <T> T[] replicate(T item, int times) {
393        @SuppressWarnings("unchecked")
394        T[] array = (T[]) Array.newInstance(item.getClass(), times);
395        for (int i = 0; i < array.length; i++) {
396            array[i] = item;
397        }
398        return array;
399    }
400
401    /**
402     * Returns all permutations of picking a fixed number of distinct elements
403     * in the array.
404     *
405     * @param <T> the type of the elements
406     * @param array the array
407     * @param choose the number of elements to pick
408     * @return the permutations
409     */
410    public static <T> T[][] permutations(T[] array, int choose) {
411        if (choose > array.length) {
412            throw new IllegalArgumentException();
413        }
414        if (choose == 0) {
415            @SuppressWarnings("unchecked")
416            T[][] permutations = (T[][]) Array.newInstance(array.getClass(), 1);
417            @SuppressWarnings("unchecked")
418            T[] permutation = (T[]) Array.newInstance(array.getClass().getComponentType(), 0);
419            permutations[0] = permutation;
420            return permutations;
421        }
422
423        @SuppressWarnings("unchecked")
424        T[][] permutations = (T[][]) Array.newInstance(
425                array.getClass().getComponentType(),
426                permutation(array.length, choose), choose);
427
428        int[] indices = new int[choose];
429        indices[indices.length - 1]--;
430        for (int i = 0; i < permutations.length; i++) {
431            do {
432                int j = indices.length - 1;
433                indices[j]++;
434                while (indices[j] >= array.length) {
435                    indices[j] = 0;
436                    j--;
437                    indices[j]++;
438                }
439            } while (!isUnique(indices));
440            for (int k = 0; k < indices.length; k++) {
441                permutations[i][k] = array[indices[k]];
442            }
443        }
444        return permutations;
445    }
446
447    /**
448     * Returns all possibilities of picking a fixed number of elements in the
449     * array.
450     *
451     * @param <T> the type of the elements
452     * @param array the array
453     * @param choose the number of elements to pick
454     * @return the sequence
455     */
456    public static <T> T[][] sequence(T[] array, int choose) {
457        if (choose <= 0) {
458            throw new IllegalArgumentException();
459        }
460        return sequence(replicate(array, choose));
461    }
462
463    /**
464     * Returns all possibilities of picking one element in each array. Similar
465     * to Haskell's {@code sequence} for lists, except for edge cases.
466     *
467     * @param <T> the type of the elements
468     * @param arrays the arrays
469     * @return the sequence
470     */
471    public static <T> T[][] sequence(T[][] arrays) {
472        if (arrays.length == 0) {
473            @SuppressWarnings("unchecked")
474            T[][] sequence = (T[][]) Array.newInstance(arrays.getClass().getComponentType(), 1);
475            @SuppressWarnings("unchecked")
476            T[] permutation = (T[]) Array.newInstance(arrays.getClass().getComponentType().getComponentType(), 0);
477            sequence[0] = permutation;
478            return sequence;
479        }
480
481        int product = 1;
482        for (T[] array : arrays) {
483            product *= array.length;
484        }
485        @SuppressWarnings("unchecked")
486        T[][] sequence = (T[][]) Array.newInstance(
487                arrays.getClass().getComponentType().getComponentType(),
488                product, arrays.length);
489
490        int[] indices = new int[arrays.length];
491        indices[indices.length - 1]--;
492        for (int i = 0; i < sequence.length; i++) {
493            int j = indices.length - 1;
494            indices[j]++;
495            while (indices[j] >= arrays[j].length) {
496                indices[j] = 0;
497                j--;
498                indices[j]++;
499            }
500            for (int k = 0; k < indices.length; k++) {
501                sequence[i][k] = arrays[k][indices[k]];
502            }
503        }
504        return sequence;
505    }
506
507    /**
508     * Returns a sublist from index 0 to the index of the item (inclusive).
509     * Equivalent to the Haskell code {@code takeWhile (== item) list}
510     *
511     * @param <T> the element type
512     * @param item the item to find
513     * @param list the list of items
514     * @return a prefix of the {@code list} ending at the first {@code item} if
515     * found, otherwise the entire list
516     */
517    public static <T> List<T> takeUntil(T item, List<T> list) {
518        int index = 0;
519        for (T t : list) {
520            index++;
521            if (item.equals(t)) {
522                return list.subList(0, index);
523            }
524        }
525        return list;
526    }
527
528    /**
529     * Returns a sublist from the index of the item (inclusive) to the end of
530     * the list. Equivalent to the Haskell code {@code dropWhile (/= item) list}
531     *
532     * @param <T> the element type
533     * @param item the item to find
534     * @param list the list of items
535     * @return a suffix of the {@code list} starting at the first {@code item}
536     * if found, otherwise the entire list
537     */
538    public static <T> List<T> dropUntil(T item, List<T> list) {
539        int index = 0;
540        for (T t : list) {
541            if (item.equals(t)) {
542                return list.subList(index, list.size());
543            }
544            index++;
545        }
546        return Collections.emptyList();
547    }
548
549    /**
550     * Checks if the list starts with specific elements.
551     *
552     * @param <T> the element type
553     * @param string the list
554     * @param prefix the starting elements of the list
555     * @return {@code true} if and only if {@code string} starts with {@code prefix},
556     *         {@code false} otherwise
557     */
558    public static <T> boolean startsWith(List<T> string, List<T> prefix) {
559        final int stringSize = string.size();
560        final int prefixSize = prefix.size();
561        if (prefixSize > stringSize) {
562            return false;
563        }
564        return prefix.equals(string.subList(0, prefixSize));
565    }
566
567    /**
568     * Checks if the list ends with specific elements.
569     *
570     * @param <T> the element type
571     * @param string the list
572     * @param suffix the ending elements of the list
573     * @return {@code true} if and only if {@code string} ends with {@code suffix},
574     *         {@code false} otherwise
575     */
576    public static <T> boolean endsWith(List<T> string, List<T> suffix) {
577        final int stringSize = string.size();
578        final int suffixSize = suffix.size();
579        if (suffixSize > stringSize) {
580            return false;
581        }
582        return suffix.equals(string.subList(stringSize - suffixSize, stringSize));
583    }
584
585    /**
586     * @param array an array of integers
587     * @return the sum of the integers in the array
588     */
589    public static int sum(int... array) {
590        int sum = 0;
591        for (int a : array) {
592            sum += a;
593        }
594        return sum;
595    }
596
597    /**
598     * @param iter an iterator of integers
599     * @return the sum of the integers in the iterator
600     */
601    public static int sum(TIntIterator iter) {
602        int sum = 0;
603        while (iter.hasNext()) {
604            sum += iter.next();
605        }
606        return sum;
607    }
608
609    /**
610     * @param array an array of integers
611     * @return the minimum integer in the array
612     */
613    public static int min(int... array) {
614        if (array.length == 0) {
615            throw new IllegalArgumentException();
616        }
617        int min = array[0];
618        for (int i = 1; i < array.length; i++) {
619            min = Math.min(min, array[i]);
620        }
621        return min;
622    }
623
624    /**
625     * @param iter an iterator of integers
626     * @return the minimum integer in the iterator
627     */
628    public static int min(TIntIterator iter) {
629        if (!iter.hasNext()) {
630            throw new IllegalArgumentException();
631        }
632        int min = iter.next();
633        while (iter.hasNext()) {
634            min = Math.min(min, iter.next());
635        }
636        return min;
637    }
638
639    /**
640     * @param array an array of integers
641     * @return the maximum integer in the array
642     */
643    public static int max(int... array) {
644        if (array.length == 0) {
645            throw new IllegalArgumentException();
646        }
647        int max = array[0];
648        for (int i = 1; i < array.length; i++) {
649            max = Math.max(max, array[i]);
650        }
651        return max;
652    }
653
654    /**
655     * @param iter an iterator of integers
656     * @return the maximum integer in the iterator
657     */
658    public static int max(TIntIterator iter) {
659        if (!iter.hasNext()) {
660            throw new IllegalArgumentException();
661        }
662        int max = iter.next();
663        while (iter.hasNext()) {
664            max = Math.max(max, iter.next());
665        }
666        return max;
667    }
668
669    /**
670     * Enumerate the iterator and return the values discovered. The iterator is
671     * exhausted on return.
672     *
673     * @param iter an iterator
674     * @return the values found in the iterator
675     */
676    public static int[] iterate(IntIterator iter) {
677        TIntArrayList i = new TIntArrayList();
678        while (iter.hasNext()) {
679            i.add(iter.next());
680        }
681        return i.toArray();
682    }
683
684    /**
685     * Concatenate the string representation of the items with a comma
686     * separating each item.
687     *
688     * @param <T> the type of the elements
689     * @param items the items to display
690     * @return the items string form separated by commas
691     */
692    public static <T> String commaSeparate(T[] items) {
693        return intercalate(", ", items);
694    }
695
696    /**
697     * Concatenate the string representation of the items with a comma
698     * separating each item.
699     *
700     * @param items the items to display
701     * @return the items string form separated by commas
702     */
703    public static String commaSeparate(Iterable<?> items) {
704        return intercalate(", ", items);
705    }
706
707    /**
708     * Concatenate the string representation of the items with a separator
709     * separating each item.
710     *
711     * @param <T> the type of the elements
712     * @param separator the string to separate each item
713     * @param items the items to display
714     * @return the items string form separated by the separator
715     */
716    public static <T> String intercalate(String separator, T[] items) {
717        StringBuilder result = new StringBuilder();
718        if (items.length > 0) {
719            result.append(items[0]);
720            for (int i = 1; i < items.length; i++) {
721                result.append(separator).append(items[i]);
722            }
723        }
724        return result.toString();
725    }
726
727    /**
728     * Concatenate the string representation of the items with a separator
729     * separating each item.
730     *
731     * @param separator the string to separate each item
732     * @param items the items to display
733     * @return the items string form separated by the separator
734     */
735    public static String intercalate(String separator, Iterable<?> items) {
736        StringBuilder result = new StringBuilder();
737        Iterator<?> iter = items.iterator();
738        if (iter.hasNext()) {
739            result.append(iter.next());
740            while (iter.hasNext()) {
741                result.append(separator).append(iter.next());
742            }
743        }
744        return result.toString();
745    }
746}