All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.additions.arrays.ArrayMinMaxFinder Maven / Gradle / Ivy

Go to download

Open-source libraries, providing the base core functionality for SciChains product.

There is a newer version: 4.4.7
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.additions.arrays;

import net.algart.arrays.*;

import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.IntStream;

public final class ArrayMinMaxFinder {
    public interface Min {
        boolean isMinFound();

        long indexOfMin();

        long indexOfMin(Supplier exceptionSupplierWhenNotFound);

        double min();

        long exactMin();

        Min find(PArray data);

        Min find(Matrix data);
    }

    public interface Max {
        boolean isMaxFound();

        long indexOfMax();

        long indexOfMax(Supplier exceptionSupplierWhenNotFound);

        double max();

        long exactMax();

        Max find(PArray data);

        Max find(Matrix data);
    }

    public interface MinMax extends Min, Max {
        MinMax find(PArray data);

        MinMax find(Matrix data);
    }

    private final boolean multithreading;
    private final ExtendedMinMaxInfo[] threadMinMax;
    private final ExtendedMinMaxInfo resultMinMax;
    private final long[] splitters;

    private ArrayMinMaxFinder(boolean multithreading) {
        final int cpuCount = Arrays.SystemSettings.cpuCount();
        final int numberOfTasks = !multithreading || cpuCount == 1 ? 1 : 4 * cpuCount;
        // - splitting into more than cpuCount ranges provides better performance
        this.multithreading = numberOfTasks > 1;
        this.resultMinMax = new ExtendedMinMaxInfo();
        this.threadMinMax = new ExtendedMinMaxInfo[numberOfTasks];
        if (numberOfTasks == 1) {
            this.threadMinMax[0] = this.resultMinMax;
            // - the same reference
        } else {
            java.util.Arrays.setAll(threadMinMax, k -> new ExtendedMinMaxInfo());
        }
        this.splitters = new long[numberOfTasks + 1];
    }

    public static ArrayMinMaxFinder newInstance(boolean multithreading) {
        return new ArrayMinMaxFinder(multithreading);
    }

    public Min getMinFinder() {
        return new Min() {
            @Override
            public boolean isMinFound() {
                return resultMinMax().isMinFound();
            }

            @Override
            public long indexOfMin() {
                return resultMinMax().getIndexOfMin();
            }

            @Override
            public long indexOfMin(Supplier exceptionSupplierWhenNotFound) {
                return throwForNegative(indexOfMin(), exceptionSupplierWhenNotFound);
            }

            @Override
            public double min() {
                return resultMinMax().getMin();
            }

            @Override
            public long exactMin() {
                return resultMinMax().getExactMin();
            }

            @Override
            public Min find(PArray data) {
                doFind(data, true, false);
                return this;
            }

            @Override
            public Min find(Matrix matrix) {
                doFind(matrix, true, false);
                return this;
            }
        };
    }

    public Max getMaxFinder() {
        return new Max() {
            @Override
            public boolean isMaxFound() {
                return resultMinMax().isMaxFound();
            }

            @Override
            public long indexOfMax() {
                return resultMinMax().getIndexOfMax();
            }

            @Override
            public long indexOfMax(Supplier exceptionSupplierWhenNotFound) {
                return throwForNegative(indexOfMax(), exceptionSupplierWhenNotFound);
            }

            @Override
            public double max() {
                return resultMinMax().getMax();
            }

            public long exactMax() {
                return resultMinMax().getExactMax();
            }

            @Override
            public Max find(PArray data) {
                doFind(data, false, true);
                return this;
            }

            @Override
            public Max find(Matrix matrix) {
                doFind(matrix, false, true);
                return this;
            }
        };
    }

    public MinMax getMinMaxFinder() {
        return new MinMax() {
            @Override
            public boolean isMinFound() {
                return resultMinMax().isMinFound();
            }

            @Override
            public long indexOfMin() {
                return resultMinMax().getIndexOfMin();
            }

            @Override
            public long indexOfMin(Supplier exceptionSupplierWhenNotFound) {
                return throwForNegative(indexOfMin(), exceptionSupplierWhenNotFound);
            }

            @Override
            public double min() {
                return resultMinMax().getMin();
            }

            @Override
            public long exactMin() {
                return resultMinMax().getExactMin();
            }

            @Override
            public boolean isMaxFound() {
                return resultMinMax().isMaxFound();
            }

            @Override
            public long indexOfMax() {
                return resultMinMax().getIndexOfMax();
            }

            @Override
            public long indexOfMax(Supplier exceptionSupplierWhenNotFound) {
                return throwForNegative(indexOfMax(), exceptionSupplierWhenNotFound);
            }

            @Override
            public double max() {
                return resultMinMax().getMax();
            }

            public long exactMax() {
                return resultMinMax().getExactMax();
            }

            @Override
            public MinMax find(PArray data) {
                doFind(data, true, true);
                return this;
            }

            @Override
            public MinMax find(Matrix matrix) {
                doFind(matrix, true, true);
                return this;
            }
        };
    }

    private void doFind(PArray data, boolean needMin, boolean needMax) {
        Objects.requireNonNull(data, "Null data");
        if (!needMin && !needMax) {
            throw new AssertionError("At least one of needMin/needMax must be set");
        }
        Arrays.splitToRanges(splitters, data.length());
        final Class elementType = data.elementType();
        if (elementType == boolean.class) {
            Arrays.MinMaxInfo mm = new Arrays.MinMaxInfo();
            Arrays.rangeOf(data, mm);
            // - standard algorithm is good enough
            resultMinMax.setElementType(elementType)
                    .setExactAll(mm.indexOfMin(), (int) mm.min(), mm.indexOfMax(), (int) mm.max());
            return;
        }
        IntStream stream = IntStream.range(0, splitters.length - 1);
        if (multithreading) {
            stream = stream.parallel();
        }
        if (elementType == char.class) {
            final CharArray array = (CharArray) data;
            stream.forEach(rangeIndex -> minMaxForChars(array, rangeIndex, needMin, needMax));
        } else if (elementType == byte.class) {
            final ByteArray array = (ByteArray) data;
            stream.forEach(rangeIndex -> minMaxForBytes(array, rangeIndex, needMin, needMax));
        } else if (elementType == short.class) {
            final ShortArray array = (ShortArray) data;
            stream.forEach(rangeIndex -> minMaxForShorts(array, rangeIndex, needMin, needMax));
        } else if (elementType == int.class) {
            final IntArray array = (IntArray) data;
            stream.forEach(rangeIndex -> minMaxForInts(array, rangeIndex, needMin, needMax));
        } else if (elementType == long.class) {
            final LongArray array = (LongArray) data;
            stream.forEach(rangeIndex -> minMaxForLongs(array, rangeIndex, needMin, needMax));
        } else if (elementType == float.class) {
            final FloatArray array = (FloatArray) data;
            stream.forEach(rangeIndex -> minMaxForFloats(array, rangeIndex, needMin, needMax));
        } else if (elementType == double.class) {
            final DoubleArray array = (DoubleArray) data;
            stream.forEach(rangeIndex -> minMaxForDoubles(array, rangeIndex, needMin, needMax));
        } else {
            throw new AssertionError("Impossible element type " + elementType);
        }
        completeResult(needMin, needMax);
    }

    private void doFind(Matrix matrix, boolean needMin, boolean needMax) {
        Objects.requireNonNull(matrix, "Null data matrix");
        doFind(matrix.array(), needMin, needMax);
    }

    private void completeResult(boolean needMin, boolean needMax) {
        if (threadMinMax.length == 1) {
            // - nothing to do: results are already stored in this reference
            return;
        }
        resultMinMax.copyFrom(threadMinMax[0]);
        for (int k = 1; k < threadMinMax.length; k++) {
            if (needMin) {
                resultMinMax.updateMin(threadMinMax[k]);
            }
            if (needMax) {
                resultMinMax.updateMax(threadMinMax[k]);
            }
        }
    }

    private ExtendedMinMaxInfo resultMinMax() {
        checkReady();
        return resultMinMax;
    }

    private static long throwForNegative(
            long index,
            Supplier exceptionSupplierForNegativeIndex) {
        if (index < 0) {
            throw exceptionSupplierForNegativeIndex.get();
        }
        return index;
    }

    private void checkReady() {
        if (!resultMinMax.isReady()) {
            throw new IllegalStateException("Minimum/maximum are not found yet");
        }
    }

    /*Repeat() Char   ==> Byte,,Short,,Int,,Long,,Float,,Double;;
               char   ==> byte,,short,,int,,long,,float,,double;;
               int\s+v\s*=\s*(.*?);     ==> int v = ($1) & 0xFF;,,int v = ($1) & 0xFFFF;,,
                            int v = $1;,,long v = $1;,,float v = $1;,,double v = $1; ;;
               (Integer.MAX_VALUE)      ==> $1,,$1,,$1,,Long.MAX_VALUE,,
                            Float.POSITIVE_INFINITY,,Double.POSITIVE_INFINITY;;
               (Integer.MIN_VALUE)      ==> $1,,$1,,$1,,Long.MIN_VALUE,,
                            Float.NEGATIVE_INFINITY,,Double.NEGATIVE_INFINITY;;
               int\s+(min|max)          ==> int $1,,int $1,,int $1,,long $1,,float $1,,double $1;;
               (setExact)(Min|Max|All)  ==> $1$2,,$1$2,,$1$2,,$1$2,,set$2,,... */

    private void minMaxForChars(CharArray data, int rangeIndex, boolean needMin, boolean needMax) {
        final long from = splitters[rangeIndex];
        final long to = splitters[rangeIndex + 1];
        final ExtendedMinMaxInfo minMax = threadMinMax[rangeIndex];
        minMax.setElementType(char.class);
        DirectAccessible da;
        if (data instanceof DirectAccessible && (da = (DirectAccessible) data).hasJavaArray()) {
            final int offset = da.javaArrayOffset();
            final char[] array = (char[]) da.javaArray();
            final int intTo = (int) to;
            final int intFrom = (int) from;
            assert intFrom == from && intTo == to;
            if (!needMax) {
                assert needMin;
                charRangeMin(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
            } else if (!needMin) {
                charRangeMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMax -= offset;
            } else {
                charRangeMinMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
                minMax.indexOfMax -= offset;
            }
        } else {
            if (!needMax) {
                assert needMin;
                charRangeMin(data, minMax, from, to - from);
            } else if (!needMin) {
                charRangeMax(data, minMax, from, to - from);
            } else {
                charRangeMinMax(data, minMax, from, to - from);
            }
        }
    }

    private void charRangeMin(CharArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = data.getChar(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void charRangeMax(CharArray data, ExtendedMinMaxInfo result, long p, long length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = data.getChar(i);
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void charRangeMinMax(CharArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = data.getChar(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void charRangeMin(char[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void charRangeMax(char[] data, ExtendedMinMaxInfo result, int p, int length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = data[i];
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void charRangeMinMax(char[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    /*Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! */

    private void minMaxForBytes(ByteArray data, int rangeIndex, boolean needMin, boolean needMax) {
        final long from = splitters[rangeIndex];
        final long to = splitters[rangeIndex + 1];
        final ExtendedMinMaxInfo minMax = threadMinMax[rangeIndex];
        minMax.setElementType(byte.class);
        DirectAccessible da;
        if (data instanceof DirectAccessible && (da = (DirectAccessible) data).hasJavaArray()) {
            final int offset = da.javaArrayOffset();
            final byte[] array = (byte[]) da.javaArray();
            final int intTo = (int) to;
            final int intFrom = (int) from;
            assert intFrom == from && intTo == to;
            if (!needMax) {
                assert needMin;
                byteRangeMin(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
            } else if (!needMin) {
                byteRangeMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMax -= offset;
            } else {
                byteRangeMinMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
                minMax.indexOfMax -= offset;
            }
        } else {
            if (!needMax) {
                assert needMin;
                byteRangeMin(data, minMax, from, to - from);
            } else if (!needMin) {
                byteRangeMax(data, minMax, from, to - from);
            } else {
                byteRangeMinMax(data, minMax, from, to - from);
            }
        }
    }

    private void byteRangeMin(ByteArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = (data.getByte(i)) & 0xFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void byteRangeMax(ByteArray data, ExtendedMinMaxInfo result, long p, long length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = (data.getByte(i)) & 0xFF;
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void byteRangeMinMax(ByteArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = (data.getByte(i)) & 0xFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void byteRangeMin(byte[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = (data[i]) & 0xFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void byteRangeMax(byte[] data, ExtendedMinMaxInfo result, int p, int length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = (data[i]) & 0xFF;
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void byteRangeMinMax(byte[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = (data[i]) & 0xFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void minMaxForShorts(ShortArray data, int rangeIndex, boolean needMin, boolean needMax) {
        final long from = splitters[rangeIndex];
        final long to = splitters[rangeIndex + 1];
        final ExtendedMinMaxInfo minMax = threadMinMax[rangeIndex];
        minMax.setElementType(short.class);
        DirectAccessible da;
        if (data instanceof DirectAccessible && (da = (DirectAccessible) data).hasJavaArray()) {
            final int offset = da.javaArrayOffset();
            final short[] array = (short[]) da.javaArray();
            final int intTo = (int) to;
            final int intFrom = (int) from;
            assert intFrom == from && intTo == to;
            if (!needMax) {
                assert needMin;
                shortRangeMin(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
            } else if (!needMin) {
                shortRangeMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMax -= offset;
            } else {
                shortRangeMinMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
                minMax.indexOfMax -= offset;
            }
        } else {
            if (!needMax) {
                assert needMin;
                shortRangeMin(data, minMax, from, to - from);
            } else if (!needMin) {
                shortRangeMax(data, minMax, from, to - from);
            } else {
                shortRangeMinMax(data, minMax, from, to - from);
            }
        }
    }

    private void shortRangeMin(ShortArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = (data.getShort(i)) & 0xFFFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void shortRangeMax(ShortArray data, ExtendedMinMaxInfo result, long p, long length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = (data.getShort(i)) & 0xFFFF;
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void shortRangeMinMax(ShortArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = (data.getShort(i)) & 0xFFFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void shortRangeMin(short[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = (data[i]) & 0xFFFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void shortRangeMax(short[] data, ExtendedMinMaxInfo result, int p, int length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = (data[i]) & 0xFFFF;
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void shortRangeMinMax(short[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = (data[i]) & 0xFFFF;
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void minMaxForInts(IntArray data, int rangeIndex, boolean needMin, boolean needMax) {
        final long from = splitters[rangeIndex];
        final long to = splitters[rangeIndex + 1];
        final ExtendedMinMaxInfo minMax = threadMinMax[rangeIndex];
        minMax.setElementType(int.class);
        DirectAccessible da;
        if (data instanceof DirectAccessible && (da = (DirectAccessible) data).hasJavaArray()) {
            final int offset = da.javaArrayOffset();
            final int[] array = (int[]) da.javaArray();
            final int intTo = (int) to;
            final int intFrom = (int) from;
            assert intFrom == from && intTo == to;
            if (!needMax) {
                assert needMin;
                intRangeMin(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
            } else if (!needMin) {
                intRangeMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMax -= offset;
            } else {
                intRangeMinMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
                minMax.indexOfMax -= offset;
            }
        } else {
            if (!needMax) {
                assert needMin;
                intRangeMin(data, minMax, from, to - from);
            } else if (!needMin) {
                intRangeMax(data, minMax, from, to - from);
            } else {
                intRangeMinMax(data, minMax, from, to - from);
            }
        }
    }

    private void intRangeMin(IntArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = data.getInt(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void intRangeMax(IntArray data, ExtendedMinMaxInfo result, long p, long length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = data.getInt(i);
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void intRangeMinMax(IntArray data, ExtendedMinMaxInfo result, long p, long length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final int v = data.getInt(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void intRangeMin(int[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        long indexOfMin = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void intRangeMax(int[] data, ExtendedMinMaxInfo result, int p, int length) {
        int max = Integer.MIN_VALUE;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = data[i];
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void intRangeMinMax(int[] data, ExtendedMinMaxInfo result, int p, int length) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final int v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void minMaxForLongs(LongArray data, int rangeIndex, boolean needMin, boolean needMax) {
        final long from = splitters[rangeIndex];
        final long to = splitters[rangeIndex + 1];
        final ExtendedMinMaxInfo minMax = threadMinMax[rangeIndex];
        minMax.setElementType(long.class);
        DirectAccessible da;
        if (data instanceof DirectAccessible && (da = (DirectAccessible) data).hasJavaArray()) {
            final int offset = da.javaArrayOffset();
            final long[] array = (long[]) da.javaArray();
            final int intTo = (int) to;
            final int intFrom = (int) from;
            assert intFrom == from && intTo == to;
            if (!needMax) {
                assert needMin;
                longRangeMin(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
            } else if (!needMin) {
                longRangeMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMax -= offset;
            } else {
                longRangeMinMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
                minMax.indexOfMax -= offset;
            }
        } else {
            if (!needMax) {
                assert needMin;
                longRangeMin(data, minMax, from, to - from);
            } else if (!needMin) {
                longRangeMax(data, minMax, from, to - from);
            } else {
                longRangeMinMax(data, minMax, from, to - from);
            }
        }
    }

    private void longRangeMin(LongArray data, ExtendedMinMaxInfo result, long p, long length) {
        long min = Long.MAX_VALUE;
        long indexOfMin = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final long v = data.getLong(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void longRangeMax(LongArray data, ExtendedMinMaxInfo result, long p, long length) {
        long max = Long.MIN_VALUE;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final long v = data.getLong(i);
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void longRangeMinMax(LongArray data, ExtendedMinMaxInfo result, long p, long length) {
        long min = Long.MAX_VALUE;
        long max = Long.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final long v = data.getLong(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void longRangeMin(long[] data, ExtendedMinMaxInfo result, int p, int length) {
        long min = Long.MAX_VALUE;
        long indexOfMin = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final long v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setExactMin(indexOfMin, min);
    }

    private void longRangeMax(long[] data, ExtendedMinMaxInfo result, int p, int length) {
        long max = Long.MIN_VALUE;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final long v = data[i];
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactMax(indexOfMax, max);
    }

    private void longRangeMinMax(long[] data, ExtendedMinMaxInfo result, int p, int length) {
        long min = Long.MAX_VALUE;
        long max = Long.MIN_VALUE;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final long v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setExactAll(indexOfMin, min, indexOfMax, max);
    }

    private void minMaxForFloats(FloatArray data, int rangeIndex, boolean needMin, boolean needMax) {
        final long from = splitters[rangeIndex];
        final long to = splitters[rangeIndex + 1];
        final ExtendedMinMaxInfo minMax = threadMinMax[rangeIndex];
        minMax.setElementType(float.class);
        DirectAccessible da;
        if (data instanceof DirectAccessible && (da = (DirectAccessible) data).hasJavaArray()) {
            final int offset = da.javaArrayOffset();
            final float[] array = (float[]) da.javaArray();
            final int intTo = (int) to;
            final int intFrom = (int) from;
            assert intFrom == from && intTo == to;
            if (!needMax) {
                assert needMin;
                floatRangeMin(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
            } else if (!needMin) {
                floatRangeMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMax -= offset;
            } else {
                floatRangeMinMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
                minMax.indexOfMax -= offset;
            }
        } else {
            if (!needMax) {
                assert needMin;
                floatRangeMin(data, minMax, from, to - from);
            } else if (!needMin) {
                floatRangeMax(data, minMax, from, to - from);
            } else {
                floatRangeMinMax(data, minMax, from, to - from);
            }
        }
    }

    private void floatRangeMin(FloatArray data, ExtendedMinMaxInfo result, long p, long length) {
        float min = Float.POSITIVE_INFINITY;
        long indexOfMin = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final float v = data.getFloat(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setMin(indexOfMin, min);
    }

    private void floatRangeMax(FloatArray data, ExtendedMinMaxInfo result, long p, long length) {
        float max = Float.NEGATIVE_INFINITY;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final float v = data.getFloat(i);
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setMax(indexOfMax, max);
    }

    private void floatRangeMinMax(FloatArray data, ExtendedMinMaxInfo result, long p, long length) {
        float min = Float.POSITIVE_INFINITY;
        float max = Float.NEGATIVE_INFINITY;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final float v = data.getFloat(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setAll(indexOfMin, min, indexOfMax, max);
    }

    private void floatRangeMin(float[] data, ExtendedMinMaxInfo result, int p, int length) {
        float min = Float.POSITIVE_INFINITY;
        long indexOfMin = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final float v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setMin(indexOfMin, min);
    }

    private void floatRangeMax(float[] data, ExtendedMinMaxInfo result, int p, int length) {
        float max = Float.NEGATIVE_INFINITY;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final float v = data[i];
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setMax(indexOfMax, max);
    }

    private void floatRangeMinMax(float[] data, ExtendedMinMaxInfo result, int p, int length) {
        float min = Float.POSITIVE_INFINITY;
        float max = Float.NEGATIVE_INFINITY;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final float v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setAll(indexOfMin, min, indexOfMax, max);
    }

    private void minMaxForDoubles(DoubleArray data, int rangeIndex, boolean needMin, boolean needMax) {
        final long from = splitters[rangeIndex];
        final long to = splitters[rangeIndex + 1];
        final ExtendedMinMaxInfo minMax = threadMinMax[rangeIndex];
        minMax.setElementType(double.class);
        DirectAccessible da;
        if (data instanceof DirectAccessible && (da = (DirectAccessible) data).hasJavaArray()) {
            final int offset = da.javaArrayOffset();
            final double[] array = (double[]) da.javaArray();
            final int intTo = (int) to;
            final int intFrom = (int) from;
            assert intFrom == from && intTo == to;
            if (!needMax) {
                assert needMin;
                doubleRangeMin(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
            } else if (!needMin) {
                doubleRangeMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMax -= offset;
            } else {
                doubleRangeMinMax(array, minMax, offset + intFrom, intTo - intFrom);
                minMax.indexOfMin -= offset;
                minMax.indexOfMax -= offset;
            }
        } else {
            if (!needMax) {
                assert needMin;
                doubleRangeMin(data, minMax, from, to - from);
            } else if (!needMin) {
                doubleRangeMax(data, minMax, from, to - from);
            } else {
                doubleRangeMinMax(data, minMax, from, to - from);
            }
        }
    }

    private void doubleRangeMin(DoubleArray data, ExtendedMinMaxInfo result, long p, long length) {
        double min = Double.POSITIVE_INFINITY;
        long indexOfMin = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final double v = data.getDouble(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setMin(indexOfMin, min);
    }

    private void doubleRangeMax(DoubleArray data, ExtendedMinMaxInfo result, long p, long length) {
        double max = Double.NEGATIVE_INFINITY;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final double v = data.getDouble(i);
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setMax(indexOfMax, max);
    }

    private void doubleRangeMinMax(DoubleArray data, ExtendedMinMaxInfo result, long p, long length) {
        double min = Double.POSITIVE_INFINITY;
        double max = Double.NEGATIVE_INFINITY;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (long i = p, to = p + length; i < to; i++) {
            final double v = data.getDouble(i);
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setAll(indexOfMin, min, indexOfMax, max);
    }

    private void doubleRangeMin(double[] data, ExtendedMinMaxInfo result, int p, int length) {
        double min = Double.POSITIVE_INFINITY;
        long indexOfMin = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final double v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
        }
        result.setMin(indexOfMin, min);
    }

    private void doubleRangeMax(double[] data, ExtendedMinMaxInfo result, int p, int length) {
        double max = Double.NEGATIVE_INFINITY;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final double v = data[i];
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setMax(indexOfMax, max);
    }

    private void doubleRangeMinMax(double[] data, ExtendedMinMaxInfo result, int p, int length) {
        double min = Double.POSITIVE_INFINITY;
        double max = Double.NEGATIVE_INFINITY;
        long indexOfMin = -1;
        long indexOfMax = -1;
        for (int i = p, to = p + length; i < to; i++) {
            final double v = data[i];
            if (v < min) {
                min = v;
                indexOfMin = i;
            }
            if (v > max) {
                max = v;
                indexOfMax = i;
            }
        }
        result.setAll(indexOfMin, min, indexOfMax, max);
    }

    /*Repeat.AutoGeneratedEnd*/

    private static class ExtendedMinMaxInfo {
        private Class elementType = null;
        private boolean floatingPoint;
        private double min;
        private double max;
        private long exactMin;
        private long exactMax;
        long indexOfMin = -157;
        long indexOfMax = -157;
        // - such values are impossible in ready results

        public boolean isReady() {
            return elementType != null;
        }

        public Class getElementType() {
            return elementType;
        }

        public ExtendedMinMaxInfo setElementType(Class elementType) {
            this.elementType = Objects.requireNonNull(elementType, "Null elementType");
            this.floatingPoint = elementType == float.class || elementType == double.class;
            return this;
        }

        public boolean isFloatingPoint() {
            return floatingPoint;
        }

        public boolean isMinFound() {
            return indexOfMin >= 0;
        }

        public long getIndexOfMin() {
            return indexOfMin;
        }

        public double getMin() {
            return min;
        }

        public ExtendedMinMaxInfo setMin(long indexOfMin, double min) {
            this.indexOfMin = indexOfMin;
            this.min = min;
            this.exactMin = (long) min;
            return this;
        }

        public long getIndexOfMax() {
            return indexOfMax;
        }

        public boolean isMaxFound() {
            return indexOfMax >= 0;
        }

        public double getMax() {
            return max;
        }

        public ExtendedMinMaxInfo setMax(long indexOfMax, double max) {
            this.indexOfMax = indexOfMax;
            this.max = max;
            this.exactMax = (long) max;
            return this;
        }

        public ExtendedMinMaxInfo setAll(long indexOfMin, double min, long indexOfMax, double max) {
            return setMin(indexOfMin, min).setMax(indexOfMax, max);
        }

        public long getExactMin() {
            checkFloatingPoint();
            return exactMin;
        }

        public long getExactMax() {
            checkFloatingPoint();
            return exactMax;
        }

        public ExtendedMinMaxInfo setExactMin(long indexOfMin, long min) {
            checkFloatingPoint();
            this.indexOfMin = indexOfMin;
            this.exactMin = min;
            this.min = (double) min;
            return this;
        }

        public ExtendedMinMaxInfo setExactMax(long indexOfMax, long max) {
            checkFloatingPoint();
            this.indexOfMax = indexOfMax;
            this.exactMax = max;
            this.max = (double) max;
            return this;
        }

        public ExtendedMinMaxInfo setExactAll(long indexOfMin, long min, long indexOfMax, long max) {
            return setExactMin(indexOfMin, min).setExactMax(indexOfMax, max);
        }

        public void copyFrom(ExtendedMinMaxInfo other) {
            this.setElementType(other.elementType);
            this.indexOfMin = other.indexOfMin;
            this.indexOfMax = other.indexOfMax;
            this.min = other.min;
            this.max = other.max;
            this.exactMin = other.exactMin;
            this.exactMax = other.exactMax;
        }

        public void updateMin(ExtendedMinMaxInfo other) {
            final boolean better = floatingPoint ?
                    other.min < min || (other.min == min && other.indexOfMin < indexOfMin) :
                    other.exactMin < exactMin || (other.exactMin == exactMin && other.indexOfMin < indexOfMin);
            // - note: we must also check indexes, for a case of calculating this in several parallel threads
            if (better) {
                indexOfMin = other.indexOfMin;
                min = other.min;
                exactMin = other.exactMin;
            }
        }

        public void updateMax(ExtendedMinMaxInfo other) {
            final boolean better = floatingPoint ?
                    other.max > max || (other.max == max && other.indexOfMax < indexOfMax) :
                    other.exactMax > exactMax || (other.exactMax == exactMax && other.indexOfMax < indexOfMax);
            // - note: we must also check indexes, for a case of calculating this in several parallel threads
            if (better) {
                indexOfMax = other.indexOfMax;
                max = other.max;
                exactMax = other.exactMax;
            }
        }

        private void checkFloatingPoint() {
            if (floatingPoint) {
                throw new IllegalStateException("Exact minimum/maximum are not allowed for "
                        + elementType.getSimpleName() + "[]");
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy