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}