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