ProxySorter.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-8.0.0).
003  * Copyright (c) 2007-2024 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.util;
021 
022 import static java.util.Objects.checkFromToIndex;
023 
024 import java.util.List;
025 
026 /**
027  * This sorting methods doesn't sort a given array directly; instead,
028  * an index lookup array is returned which allows accessing the array in a
029  * sorted order.
030  * {@snippet lang="java":
031  * final double[] array = new Random().doubles(100).toArray();
032  * final int[] proxy = ProxySorter.sort(array);
033  *
034  * // 'Classical' array sort.
035  * final double[] sorted = array.clone();
036  * Arrays.sort(sorted);
037  *
038  * // Iterating the array in ascending order.
039  * for (int i = 0; i < array.length; ++i) {
040  *     assert sorted[i] == array[proxy[i]];
041  * }
042  * }
043  *
044  * The minimal requirement of the proxy-sorter will be an access function and
045  * the number of elements you want to sort.
046  * {@snippet lang="java":
047  * final IntFunction<String> access = null; // @replace substring='null' replacement="..."
048  * final int length = 100;
049  * final int[] proxy = ProxySorter.sort(
050  *     access, length,
051  *     (a, i, j) -> a.apply(i).compareTo(a.apply(j))
052  * );
053  * }
054  * @apiNote
055  * The most general sorting method is {@link #sort(Object, int, Comparator)}.
056  * All other sorting methods can be created with this method.
057  *
058  @see #sort(Object, int, Comparator)
059  @see Comparator
060  *
061  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
062  @version 6.3
063  @since 5.1
064  */
065 public final class ProxySorter {
066 
067     /**
068      * The comparator used for comparing two array elements at the specified
069      * indexes.
070      * {@snippet lang="java":
071      * final ProxySorter.Comparator<double[]> comparator =
072      *     (a, i, j) -> Double.compare(a[i], a[j]);
073      * }
074      * The example above shows how to create a comparator for {@code double[]}
075      * arrays.
076      *
077      @see ProxySorter#sort(Object, int, Comparator)
078      @see ProxySorter
079      *
080      @param <T> the array type, e.g. {@code int[]}, {@code double[]} or
081      *            {@code Seq<String>}
082      *
083      @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
084      @version 5.1
085      @since 5.1
086      */
087     @FunctionalInterface
088     public interface Comparator<T> {
089 
090         /**
091          * Compares the two array elements, specified by its indices, for order.
092          * Returns a negative integer, zero, or a positive integer as the first
093          * argument is less than, equal to, or greater than the second.
094          *
095          @see java.util.Comparator#compare(Object, Object)
096          *
097          @param array the array where the two comparing elements are fetched
098          @param i the index of the first array element
099          @param j the index of the second array element
100          @return a negative integer, zero, or a positive integer as the first
101          *         argument is less than, equal to, or greater than the second.
102          @throws NullPointerException if an argument is null and this
103          *         comparator does not permit null arguments
104          */
105         int compare(final T array, final int i, final int j);
106 
107         /**
108          * Returns a comparator that imposes the reverse ordering of this
109          * comparator.
110          *
111          @return a comparator that imposes the reverse ordering of this
112          *         comparator.
113          */
114         default Comparator<T> reversed() {
115             return (a, i, j-> compare(a, j, i);
116         }
117 
118     }
119 
120     @FunctionalInterface
121     interface Sorter<T> {
122         int[] sort(
123             final T array,
124             final int from,
125             final int to,
126             final Comparator<? super T> comparator
127         );
128 
129         default int[] sort(
130             final T array,
131             final int length,
132             final Comparator<? super T> comparator
133         ) {
134             return sort(array, 0, length, comparator);
135         }
136     }
137 
138     private ProxySorter() {
139     }
140 
141     /**
142      * Sorting the given array by creating an index lookup array. The original
143      * array is not touched, and the returned array can then be used for
144      * iterating the array in ascending order.
145      * {@snippet lang="java":
146      * final double[] array = null; // @replace substring='null' replacement="..."
147      * final int[] sorted = ProxySorter.sort(
148      *     array, 5, array.length,
149      *     (a, i, j) -> Doubler.compare(a[i], a[j])
150      * );
151      * for (int i : sorted) {
152      *     System.out.println(array[i]);
153      * }
154      * }
155      *
156      @since 6.3
157      *
158      @param array the array which is sorted
159      @param from the index of the first element (inclusive) to be sorted
160      @param to the index of the last element (exclusive) to be sorted
161      @param comparator the array element comparator
162      @param <T> the array type
163      @return the sorted index array
164      @throws NullPointerException if one of the arrays or comparator is
165      *         {@code null}
166      @throws IllegalArgumentException if {@code from > to}
167      @throws ArrayIndexOutOfBoundsException if {@code from < 0}
168      */
169     public static <T> int[] sort(
170         final T array,
171         final int from,
172         final int to,
173         final Comparator<? super T> comparator
174     ) {
175         return TimProxySorter.sort(array, from, to, comparator);
176     }
177 
178     /**
179      * Sorting the given array by creating an index lookup array. The original
180      * array is not touched, and the returned array can then be used for
181      * iterating the array in ascending order.
182      * {@snippet lang="java":
183      * final double[] array = null; // @replace substring='null' replacement="..."
184      * final int[] sorted = ProxySorter.sort(
185      *     array, array.length,
186      *     (a, i, j) -> Doubler.compare(a[i], a[j])
187      * );
188      * for (int i : sorted) {
189      *     System.out.println(array[i]);
190      * }
191      * }
192      *
193      @param array the array which is sorted
194      @param length the array length
195      @param comparator the array element comparator
196      @param <T> the array type
197      @return the sorted index array
198      @throws NullPointerException if one of the arrays is {@code null}
199      @throws IllegalArgumentException if {@code length < 0}
200      */
201     public static <T> int[] sort(
202         final T array,
203         final int length,
204         final Comparator<? super T> comparator
205     ) {
206         return sort(array, 0, length, comparator);
207     }
208 
209 
210     /* *************************************************************************
211      * Derived sorting methods.
212      * ************************************************************************/
213 
214     /**
215      * Sorting the given array by creating an index lookup array.
216      *
217      @see #sort(Object, int, Comparator)
218      *
219      @param array the array to sort
220      @return the <em>sorted</em> index lookup array
221      @throws NullPointerException if the array is {@code null}
222      */
223     public static int[] sort(final int[] array) {
224         return sort(array, array.length, ProxySorter::compare);
225     }
226 
227     private static int compare(final int[] a, final int i, final int j) {
228         return Integer.compare(a[i], a[j]);
229     }
230 
231     /**
232      * Sorting the given array by creating an index lookup array.
233      *
234      @see #sort(Object, int, int, Comparator)
235      *
236      @since 6.3
237      *
238      @param array the array to sort
239      @param from the index of the first element (inclusive) to be sorted
240      @param to the index of the last element (exclusive) to be sorted
241      @return the <em>sorted</em> index lookup array
242      @throws IllegalArgumentException if {@code from > to}
243      @throws IndexOutOfBoundsException if the sub-range is out of bounds
244      */
245     public static int[] sort(final int[] array, final int from, final int to) {
246         checkFromToIndex(from, to, array.length);
247         return sort(array, from, to, ProxySorter::compare);
248     }
249 
250     /**
251      * Sorting the given array by creating an index lookup array.
252      *
253      @see #sort(Object, int, Comparator)
254      *
255      @param array the array to sort
256      @return the <em>sorted</em> index lookup array
257      @throws NullPointerException if the array is {@code null}
258      */
259     public static int[] sort(final long[] array) {
260         return sort(array, array.length, ProxySorter::compare);
261     }
262 
263     private static int compare(final long[] a, final int i, final int j) {
264         return Long.compare(a[i], a[j]);
265     }
266 
267     /**
268      * Sorting the given array by creating an index lookup array.
269      *
270      @see #sort(Object, int, int, Comparator)
271      *
272      @since 6.3
273      *
274      @param array the array to sort
275      @param from the index of the first element (inclusive) to be sorted
276      @param to the index of the last element (exclusive) to be sorted
277      @return the <em>sorted</em> index lookup array
278      @throws IllegalArgumentException if {@code from > to}
279      @throws IndexOutOfBoundsException if the sub-range is out of bounds
280      */
281     public static int[] sort(final long[] array, final int from, final int to) {
282         checkFromToIndex(from, to, array.length);
283         return sort(array, from, to, ProxySorter::compare);
284     }
285 
286     /**
287      * Sorting the given array by creating an index lookup array.
288      *
289      @see #sort(Object, int, Comparator)
290      *
291      @param array the array to sort
292      @return the <em>sorted</em> index lookup array
293      @throws NullPointerException if the array is {@code null}
294      */
295     public static int[] sort(final double[] array) {
296         return sort(array, array.length, ProxySorter::compare);
297     }
298 
299     private static int compare(final double[] a, final int i, final int j) {
300         return Double.compare(a[i], a[j]);
301     }
302 
303     /**
304      * Sorting the given array by creating an index lookup array.
305      *
306      @see #sort(Object, int, int, Comparator)
307      *
308      @since 6.3
309      *
310      @param array the array to sort
311      @param from the index of the first element (inclusive) to be sorted
312      @param to the index of the last element (exclusive) to be sorted
313      @return the <em>sorted</em> index lookup array
314      @throws IllegalArgumentException if {@code from > to}
315      @throws IndexOutOfBoundsException if the sub-range is out of bounds
316      */
317     public static int[] sort(final double[] array, final int from, final int to) {
318         checkFromToIndex(from, to, array.length);
319         return sort(array, from, to, ProxySorter::compare);
320     }
321 
322     /**
323      * Sorting the given array by creating an index lookup array.
324      *
325      @see #sort(Object, int, Comparator)
326      *
327      @param <T> the array element type
328      @param array the array to sort
329      @param comparator the array element comparator
330      @return the <em>sorted</em> index lookup array
331      @throws NullPointerException if one of the arguments is {@code null}
332      */
333     public static <T> int[] sort(
334         final T[] array,
335         final java.util.Comparator<? super T> comparator
336     ) {
337         return sort(
338             array, array.length,
339             (a, i, j-> comparator.compare(a[i], a[j])
340         );
341     }
342 
343     /**
344      * Sorting the given array by creating an index lookup array.
345      *
346      @see #sort(Object, int, int, Comparator)
347      *
348      @since 6.3
349      *
350      @param <T> the array element type
351      @param array the array to sort
352      @param from the index of the first element (inclusive) to be sorted
353      @param to the index of the last element (exclusive) to be sorted
354      @param comparator the array element comparator
355      @return the <em>sorted</em> index lookup array
356      @throws NullPointerException if one of the arguments is {@code null}
357      @throws IllegalArgumentException if {@code from > to}
358      @throws IndexOutOfBoundsException if the sub-range is out of bounds
359      */
360     public static <T> int[] sort(
361         final T[] array,
362         final int from,
363         final int to,
364         final java.util.Comparator<? super T> comparator
365     ) {
366         checkFromToIndex(from, to, array.length);
367         return sort(
368             array, from, to,
369             (a, i, j-> comparator.compare(a[i], a[j])
370         );
371     }
372 
373     /**
374      * Sorting the given array by creating an index lookup array.
375      *
376      @see #sort(Object, int, Comparator)
377      *
378      @param <T> the array element type
379      @param array the array to sort
380      @return the <em>sorted</em> index lookup array
381      @throws NullPointerException if the array is {@code null}
382      */
383     public static <T extends Comparable<? super T>> int[] sort(final T[] array) {
384         return sort(
385             array, array.length,
386             (a, i, j-> a[i].compareTo(a[j])
387         );
388     }
389 
390     /**
391      * Sorting the given array by creating an index lookup array.
392      *
393      @see #sort(Object, int, int, Comparator)
394      *
395      @since 6.3
396      *
397      @param <T> the array element type
398      @param array the array to sort
399      @param from the index of the first element (inclusive) to be sorted
400      @param to the index of the last element (exclusive) to be sorted
401      @return the <em>sorted</em> index lookup array
402      @throws NullPointerException if the array is {@code null}
403      @throws IllegalArgumentException if {@code from > to}
404      @throws IndexOutOfBoundsException if the sub-range is out of bounds
405      */
406     public static <T extends Comparable<? super T>> int[] sort(
407         final T[] array,
408         final int from,
409         final int to
410     ) {
411         checkFromToIndex(from, to, array.length);
412         return sort(
413             array, from, to,
414             (a, i, j-> a[i].compareTo(a[j])
415         );
416     }
417 
418     /**
419      * Sorting the given array by creating an index lookup array.
420      *
421      @see #sort(Object, int, Comparator)
422      *
423      @param <T> the array element type
424      @param array the array to sort
425      @param comparator the array element comparator
426      @return the <em>sorted</em> index lookup array
427      @throws NullPointerException if one of the arguments is {@code null}
428      */
429     public static <T> int[] sort(
430         final BaseSeq<? extends T> array,
431         final java.util.Comparator<? super T> comparator
432     ) {
433         return sort(
434             array, array.length(),
435             (a, i, j-> comparator.compare(a.get(i), a.get(j))
436         );
437     }
438 
439     /**
440      * Sorting the given array by creating an index lookup array.
441      *
442      @see #sort(Object, int, int, Comparator)
443      *
444      @since 6.3
445      *
446      @param <T> the array element type
447      @param array the array to sort
448      @param from the index of the first element (inclusive) to be sorted
449      @param to the index of the last element (exclusive) to be sorted
450      @param comparator the array element comparator
451      @return the <em>sorted</em> index lookup array
452      @throws NullPointerException if one of the arguments is {@code null}
453      @throws IllegalArgumentException if {@code from > to}
454      @throws IndexOutOfBoundsException if the sub-range is out of bounds
455      */
456     public static <T> int[] sort(
457         final BaseSeq<? extends T> array,
458         final int from,
459         final int to,
460         final java.util.Comparator<? super T> comparator
461     ) {
462         checkFromToIndex(from, to, array.length());
463         return sort(
464             array, from, to,
465             (a, i, j-> comparator.compare(a.get(i), a.get(j))
466         );
467     }
468 
469     /**
470      * Sorting the given array by creating an index lookup array.
471      *
472      @see #sort(Object, int, Comparator)
473      *
474      @param <T> the array element type
475      @param array the array to sort
476      @return the <em>sorted</em> index lookup array
477      @throws NullPointerException if the array is {@code null}
478      */
479     public static <T extends Comparable<? super T>>
480     int[] sort(final BaseSeq<? extends T> array) {
481         return sort(
482             array, array.length(),
483             (a, i, j-> a.get(i).compareTo(a.get(j))
484         );
485     }
486 
487     /**
488      * Sorting the given array by creating an index lookup array.
489      *
490      @see #sort(Object, int, int, Comparator)
491      *
492      @since 6.3
493      *
494      @param <T> the array element type
495      @param array the array to sort
496      @param from the index of the first element (inclusive) to be sorted
497      @param to the index of the last element (exclusive) to be sorted
498      @return the <em>sorted</em> index lookup array
499      @throws NullPointerException if the array is {@code null}
500      @throws IllegalArgumentException if {@code from > to}
501      @throws IndexOutOfBoundsException if the sub-range is out of bounds
502      */
503     public static <T extends Comparable<? super T>>
504     int[] sort(final BaseSeq<? extends T> array, final int from, final int to) {
505         checkFromToIndex(from, to, array.length());
506         return sort(
507             array, from, to,
508             (a, i, j-> a.get(i).compareTo(a.get(j))
509         );
510     }
511 
512     /**
513      * Sorting the given array by creating an index lookup array.
514      *
515      @see #sort(Object, int, Comparator)
516      *
517      @param <T> the array element type
518      @param array the array to sort
519      @param comparator the array element comparator
520      @return the <em>sorted</em> index lookup array
521      @throws NullPointerException if one of the arguments is {@code null}
522      */
523     public static <T> int[] sort(
524         final List<? extends T> array,
525         final java.util.Comparator<? super T> comparator
526     ) {
527         return sort(
528             array, array.size(),
529             (a, i, j-> comparator.compare(a.get(i), a.get(j))
530         );
531     }
532 
533     /**
534      * Sorting the given array by creating an index lookup array.
535      *
536      @see #sort(Object, int, int, Comparator)
537      *
538      @since 6.3
539      *
540      @param <T> the array element type
541      @param array the array to sort
542      @param from the index of the first element (inclusive) to be sorted
543      @param to the index of the last element (exclusive) to be sorted
544      @param comparator the array element comparator
545      @return the <em>sorted</em> index lookup array
546      @throws NullPointerException if one of the arguments is {@code null}
547      @throws IllegalArgumentException if {@code from > to}
548      @throws IndexOutOfBoundsException if the sub-range is out of bounds
549      */
550     public static <T> int[] sort(
551         final List<? extends T> array,
552         final int from,
553         final int to,
554         final java.util.Comparator<? super T> comparator
555     ) {
556         checkFromToIndex(from, to, array.size());
557         return sort(
558             array, from, to,
559             (a, i, j-> comparator.compare(a.get(i), a.get(j))
560         );
561     }
562 
563     /**
564      * Sorting the given array by creating an index lookup array.
565      *
566      @see #sort(Object, int, Comparator)
567      *
568      @param <T> the array element type
569      @param array the array to sort
570      @return the <em>sorted</em> index lookup array
571      @throws NullPointerException if the array is {@code null}
572      */
573     public static <T extends Comparable<? super T>>
574     int[] sort(final List<? extends T> array) {
575         return sort(
576             array, array.size(),
577             (a, i, j-> a.get(i).compareTo(a.get(j))
578         );
579     }
580 
581     /**
582      * Sorting the given array by creating an index lookup array.
583      *
584      @see #sort(Object, int, int, Comparator)
585      *
586      @since 6.3
587      *
588      @param <T> the array element type
589      @param array the array to sort
590      @param from the index of the first element (inclusive) to be sorted
591      @param to the index of the last element (exclusive) to be sorted
592      @return the <em>sorted</em> index lookup array
593      @throws NullPointerException if the array is {@code null}
594      @throws IllegalArgumentException if {@code from > to}
595      @throws IndexOutOfBoundsException if the sub-range is out of bounds
596      */
597     public static <T extends Comparable<? super T>>
598     int[] sort(final List<? extends T> array, final int from, final int to) {
599         checkFromToIndex(from, to, array.size());
600         return sort(
601             array, from, to,
602             (a, i, j-> a.get(i).compareTo(a.get(j))
603         );
604     }
605 
606     /* *************************************************************************
607      * Some helper methods.
608      * ************************************************************************/
609 
610     /**
611      * Create an initial indexes array of the given {@code length}.
612      *
613      @param length the length of the indexes array
614      @return the initialized indexes array
615      */
616     static int[] indexes(final int length) {
617         return init(new int[length]);
618     }
619 
620     /**
621      * Initializes the given {@code indexes} array.
622      *
623      @param indexes the index array to initialize
624      @return the initialized indexes array
625      @throws NullPointerException if the given {@code indexes} array is
626      *         {@code null}
627      */
628     private static int[] init(final int[] indexes) {
629         for (int i = 0; i < indexes.length; ++i) {
630             indexes[i= i;
631         }
632         return indexes;
633     }
634 
635 }