Package net.algart.matrices.morphology

Source Code of net.algart.matrices.morphology.Summator

/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2014 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.matrices.morphology;

import net.algart.arrays.*;
import net.algart.math.IRange;
import net.algart.math.functions.Func;
import net.algart.math.functions.LinearFunc;
import net.algart.math.patterns.Pattern;
import net.algart.math.patterns.QuickPointCountPattern;
import net.algart.math.patterns.TooManyPointsInPatternError;
import net.algart.math.patterns.UniformGridPattern;

import java.util.ArrayList;
import java.util.List;

class Summator extends RankOperationProcessor {
    private final boolean optimizeGetData = OPTIMIZE_GET_DATA;
    private final boolean optimizeDirectArrays = OPTIMIZE_DIRECT_ARRAYS;
    private final boolean optimizeSegmentsAlongAxes = OPTIMIZE_SEGMENTS_ALONG_AXES;
    private final boolean specialOptimizeThinPatternsPowersOfTwo = SPECIAL_OPTIMIZE_THIN_PATTERNS_POWERS_OF_TWO;

    private final Func processingFunc;
    private final boolean linearFunc;
    private final boolean dividingByPowerOfTwoWithRoundingFunc;
    private final double lfA, lfB;
    private final long halfDivisor;
    private final int logDivisor;

    Summator(ArrayContext context, Func processingFunc) {
        super(context, RankPrecision.BITS_1.bitLevels); // bitLevels are not used by this class
        if (processingFunc == null)
            throw new NullPointerException("Null contrastingFunc");
        this.processingFunc = processingFunc;
        if (processingFunc == Func.IDENTITY) {
            this.linearFunc = true;
            this.dividingByPowerOfTwoWithRoundingFunc = true;
            this.lfA = 1.0;
            this.lfB = 0.0;
            this.halfDivisor = 0;
            this.logDivisor = 0;
        } else if (processingFunc instanceof LinearFunc) {
            this.linearFunc = true;
            this.lfA = ((LinearFunc)processingFunc).a(0); // IndexOutOfBoundsException possible for invalid LinearFunc
            this.lfB = ((LinearFunc)processingFunc).b();
            long m = Math.round(1.0 / lfA);
            if (this.lfB == 0.5 && m > 0 && this.lfA == 1.0 / (double)m && (m & (m - 1)) == 0) {
                this.dividingByPowerOfTwoWithRoundingFunc = true;
                this.halfDivisor = m >> 1;
                this.logDivisor = 63 - Long.numberOfLeadingZeros(m);
            } else {
                this.dividingByPowerOfTwoWithRoundingFunc = false;
                this.halfDivisor = 0;
                this.logDivisor = -1;
            }
        } else {
            this.linearFunc = false;
            this.dividingByPowerOfTwoWithRoundingFunc = false;
            this.lfA = Double.NaN;
            this.lfB = Double.NaN;
            this.halfDivisor = 0;
            this.logDivisor = -1;
        }
    }

    @Override
    public void process(Matrix<? extends UpdatablePArray> dest, Matrix<? extends PArray> src,
        List<? extends Matrix<? extends PArray>> additionalMatrices, Pattern pattern)
    {
        if (additionalMatrices == null)
            throw new NullPointerException("Null additionalMatrices argument");
        if (optimizeSegmentsAlongAxes
            && pattern instanceof UniformGridPattern
            && pattern instanceof QuickPointCountPattern
            && ((UniformGridPattern) pattern).isActuallyRectangular())
        {
            additionalMatrices = new ArrayList<Matrix<? extends PArray>>(additionalMatrices);
            // - to avoid changing by parallel threads
            checkArguments(dest, src, additionalMatrices, pattern);
            final long pointCount = pattern.pointCount();
            if (pointCount > 1) {
                if (pointCount > Integer.MAX_VALUE)
                    throw new TooManyPointsInPatternError("Too large number of points in the pattern: "
                        + pointCount + " > Integer.MAX_VALUE");
                // There is an analogous requirement in the usual branch in RankOperationProcessor:
                // we build a Java array of all pattern points, so it is impossible to process more
                // than Integer.MAX_VALUE points.
                // In this branch, it is theoretically possible to process up to Long.MAX_VALUE points,
                // but it is practically useless; this check allows to avoid overflow while integer summarizing.
                for (int k = 1, n = pattern.dimCount(); k < n; k++) {
                    // Starting from k=1: no sense to specially optimize summarizing along x-axis
                    IRange range = pattern.roundedCoordRange(k);
                    if (range.size() == pointCount) {
                        processAlongAxis(dest.array(), src.array(), src.dimensions(), k, range.min(), range.max());
                        return;
                    }
                }
            }
        }
        super.process(dest, src, additionalMatrices, pattern);
    }

    @Override
    PArray asProcessed(Class<? extends PArray> desiredType, PArray src, PArray[] additional,
        long[] dimensions, final long[] shifts, final long[] left, final long[] right)
    {
        assert shifts.length > 0;
        assert left.length == right.length;
        final boolean direct = optimizeDirectArrays &&
            src instanceof DirectAccessible && ((DirectAccessible)src).hasJavaArray();
        final boolean meanFunc = linearFunc && lfA >= 0.0 && lfA <= 1.0 / shifts.length && lfB >= 0.0 && lfB < 1.0;
        // in this case we can be sure that the linear function for byte will be <=255, for short <=65535
        final boolean meanByPowerOfTwoFunc = meanFunc && dividingByPowerOfTwoWithRoundingFunc;
        if (src instanceof BitArray) {
            final BitArray a = (BitArray)src;
            return new AbstractDoubleArray(src.length(), true, src) {
                public double getDouble(long index) {
                    long sum = 0;
                    for (long shift : shifts) {
                        long i = index - shift;
                        if (i < 0) {
                            i += length;
                        }
                        if (a.getBit(i)) {
                            sum++;
                        }
                    }
                    return processingFunc.get(sum);
                }

                public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                    if (!optimizeGetData) {
                        super.getData(arrayPos, destArray, destArrayOffset, count);
                        return;
                    }
                    if (destArray == null)
                        throw new NullPointerException("Null destArray argument");
                    checkRanges(length, arrayPos, count);
                    if (count == 0) {
                        return;
                    }
                    double[] dest = (double[])destArray;
                    long sum = 0;
                    for (long shift : shifts) {
                        long i = arrayPos - shift;
                        if (i < 0) {
                            i += length;
                        }
                        if (a.getBit(i)) {
                            sum++;
                        }
                    }
                    for (int k = 0; k < count; k++, destArrayOffset++) {
                        dest[destArrayOffset] = processingFunc.get(sum);
                        for (long shift : right) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            if (a.getBit(i)) {
                                sum--;
                                assert sum >= 0 : "Unbalanced 0 and 1 bits";
                            }
                        }
                        arrayPos++;
                        if (arrayPos == length) {
                            arrayPos = 0;
                        }
                        for (long shift : left) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            if (a.getBit(i)) {
                                sum++;
                            }
                        }
                    }
                }
            };
        }

        //[[Repeat() 0xFFFF          ==> 0xFF,,0xFFFF;;
        //           (return)\s+\(char\) ==> $1 (int),,$1 (int);;
        //           char            ==> byte,,short;;
        //           (\w+\s+)getChar ==> int getByte,,int getShort;;
        //           Char            ==> Byte,,Short;;
        //           16              ==> 8,,16 ]]
        if (src instanceof CharArray) {
            final CharArray a = (CharArray)src;
            if (direct) {
                final char[] ja = (char[])((DirectAccessible)a).javaArray();
                final int jaOfs = ((DirectAccessible)a).javaArrayOffset();
                if (meanFunc && desiredType == CharArray.class) { // char A: direct, returning CharArray
                    return new AbstractCharArray(src.length(), true, src) {
                        public char getChar(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            double v = processingFunc.get(sum);
                            return (char)v;
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            char[] dest = (char[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (specialOptimizeThinPatternsPowersOfTwo && meanByPowerOfTwoFunc && jaOfs == 0) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            long v = (sum + halfDivisor) >> logDivisor;
                                            dest[destArrayOffset] = (char)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[(int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[(int)i] & 0xFFFF;
                                        }
                                    } else if (linearFunc) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            double v = lfA * sum + lfB;
                                            dest[destArrayOffset] = (char)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFFFF;
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            double v = processingFunc.get(sum);
                                            dest[destArrayOffset] = (char)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFFFF;
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                double v = processingFunc.get(sum);
                                dest[destArrayOffset] = (char)v;
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i] & 0xFFFF;
                                }
                            }
                        }
                    };
                } else { // char B: direct, returning DoubleArray
                    return new AbstractDoubleArray(src.length(), true, src) {
                        public double getDouble(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            double v = processingFunc.get(sum);
                            return (char)v;
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            double[] dest = (double[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc && jaOfs == 0) {
                                        if (lfB != 0.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum + lfB;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFFFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFFFF;
                                            }
                                        } else if (lfA != 1.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFFFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFFFF;
                                            }
                                        } else {
                                            assert lfA == 1.0 && lfB == 0.0;
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFFFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFFFF;
                                            }
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFFFF;
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i] & 0xFFFF;
                                }
                            }
                        }
                    };
                }
            } else { // char C: indirect
                return new AbstractDoubleArray(src.length(), true, src) {
                    public double getDouble(long index) {
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = index - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        return processingFunc.get(sum);
                    }

                    public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                        if (!optimizeGetData) {
                            super.getData(arrayPos, destArray, destArrayOffset, count);
                            return;
                        }
                        if (destArray == null)
                            throw new NullPointerException("Null destArray argument");
                        checkRanges(length, arrayPos, count);
                        if (count == 0) {
                            return;
                        }
                        double[] dest = (double[])destArray;
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        for (int k = 0; k < count; k++, destArrayOffset++) {
                            dest[destArrayOffset] = processingFunc.get(sum);
                            for (long shift : right) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum -= a.getInt(i);
                            }
                            arrayPos++;
                            if (arrayPos == length) {
                                arrayPos = 0;
                            }
                            for (long shift : left) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += a.getInt(i);
                            }
                        }
                    }
                };
            }
        }
        //[[Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! ]]
        if (src instanceof ByteArray) {
            final ByteArray a = (ByteArray)src;
            if (direct) {
                final byte[] ja = (byte[])((DirectAccessible)a).javaArray();
                final int jaOfs = ((DirectAccessible)a).javaArrayOffset();
                if (meanFunc && desiredType == ByteArray.class) { // byte A: direct, returning ByteArray
                    return new AbstractByteArray(src.length(), true, src) {
                        public int getByte(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFF; // &0xFF for preprocessing
                            }
                            double v = processingFunc.get(sum);
                            return (int)v;
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            byte[] dest = (byte[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFF; // &0xFF for preprocessing
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (specialOptimizeThinPatternsPowersOfTwo && meanByPowerOfTwoFunc && jaOfs == 0) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            long v = (sum + halfDivisor) >> logDivisor;
                                            dest[destArrayOffset] = (byte)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[(int)i] & 0xFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[(int)i] & 0xFF;
                                        }
                                    } else if (linearFunc) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            double v = lfA * sum + lfB;
                                            dest[destArrayOffset] = (byte)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFF;
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            double v = processingFunc.get(sum);
                                            dest[destArrayOffset] = (byte)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFF;
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                double v = processingFunc.get(sum);
                                dest[destArrayOffset] = (byte)v;
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i] & 0xFF;
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i] & 0xFF;
                                }
                            }
                        }
                    };
                } else { // byte B: direct, returning DoubleArray
                    return new AbstractDoubleArray(src.length(), true, src) {
                        public double getDouble(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFF; // &0xFF for preprocessing
                            }
                            double v = processingFunc.get(sum);
                            return (int)v;
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            double[] dest = (double[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFF; // &0xFF for preprocessing
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc && jaOfs == 0) {
                                        if (lfB != 0.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum + lfB;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFF;
                                            }
                                        } else if (lfA != 1.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFF;
                                            }
                                        } else {
                                            assert lfA == 1.0 && lfB == 0.0;
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFF;
                                            }
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFF;
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i] & 0xFF;
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i] & 0xFF;
                                }
                            }
                        }
                    };
                }
            } else { // byte C: indirect
                return new AbstractDoubleArray(src.length(), true, src) {
                    public double getDouble(long index) {
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = index - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        return processingFunc.get(sum);
                    }

                    public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                        if (!optimizeGetData) {
                            super.getData(arrayPos, destArray, destArrayOffset, count);
                            return;
                        }
                        if (destArray == null)
                            throw new NullPointerException("Null destArray argument");
                        checkRanges(length, arrayPos, count);
                        if (count == 0) {
                            return;
                        }
                        double[] dest = (double[])destArray;
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        for (int k = 0; k < count; k++, destArrayOffset++) {
                            dest[destArrayOffset] = processingFunc.get(sum);
                            for (long shift : right) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum -= a.getInt(i);
                            }
                            arrayPos++;
                            if (arrayPos == length) {
                                arrayPos = 0;
                            }
                            for (long shift : left) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += a.getInt(i);
                            }
                        }
                    }
                };
            }
        }
        if (src instanceof ShortArray) {
            final ShortArray a = (ShortArray)src;
            if (direct) {
                final short[] ja = (short[])((DirectAccessible)a).javaArray();
                final int jaOfs = ((DirectAccessible)a).javaArrayOffset();
                if (meanFunc && desiredType == ShortArray.class) { // short A: direct, returning ShortArray
                    return new AbstractShortArray(src.length(), true, src) {
                        public int getShort(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            double v = processingFunc.get(sum);
                            return (int)v;
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            short[] dest = (short[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (specialOptimizeThinPatternsPowersOfTwo && meanByPowerOfTwoFunc && jaOfs == 0) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            long v = (sum + halfDivisor) >> logDivisor;
                                            dest[destArrayOffset] = (short)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[(int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[(int)i] & 0xFFFF;
                                        }
                                    } else if (linearFunc) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            double v = lfA * sum + lfB;
                                            dest[destArrayOffset] = (short)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFFFF;
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            double v = processingFunc.get(sum);
                                            dest[destArrayOffset] = (short)v;
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFFFF;
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                double v = processingFunc.get(sum);
                                dest[destArrayOffset] = (short)v;
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i] & 0xFFFF;
                                }
                            }
                        }
                    };
                } else { // short B: direct, returning DoubleArray
                    return new AbstractDoubleArray(src.length(), true, src) {
                        public double getDouble(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            double v = processingFunc.get(sum);
                            return (int)v;
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            double[] dest = (double[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i] & 0xFFFF; // &0xFFFF for preprocessing
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc && jaOfs == 0) {
                                        if (lfB != 0.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum + lfB;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFFFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFFFF;
                                            }
                                        } else if (lfA != 1.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFFFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFFFF;
                                            }
                                        } else {
                                            assert lfA == 1.0 && lfB == 0.0;
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i] & 0xFFFF;
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i] & 0xFFFF;
                                            }
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i] & 0xFFFF;
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i] & 0xFFFF;
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i] & 0xFFFF;
                                }
                            }
                        }
                    };
                }
            } else { // short C: indirect
                return new AbstractDoubleArray(src.length(), true, src) {
                    public double getDouble(long index) {
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = index - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        return processingFunc.get(sum);
                    }

                    public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                        if (!optimizeGetData) {
                            super.getData(arrayPos, destArray, destArrayOffset, count);
                            return;
                        }
                        if (destArray == null)
                            throw new NullPointerException("Null destArray argument");
                        checkRanges(length, arrayPos, count);
                        if (count == 0) {
                            return;
                        }
                        double[] dest = (double[])destArray;
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        for (int k = 0; k < count; k++, destArrayOffset++) {
                            dest[destArrayOffset] = processingFunc.get(sum);
                            for (long shift : right) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum -= a.getInt(i);
                            }
                            arrayPos++;
                            if (arrayPos == length) {
                                arrayPos = 0;
                            }
                            for (long shift : left) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += a.getInt(i);
                            }
                        }
                    }
                };
            }
        }
        //[[Repeat.AutoGeneratedEnd]]

        //[[Repeat() int\s+getInt     ==> long getLong;;
        //           getInt           ==> getLong;;
        //           Integer(?!Array) ==> Long;;
        //           IntArray         ==> LongArray;;
        //           int(\s+[ABCD]\:|\[\]\s+ja|\[\]\s+dest|\)processingFunc\b|\)\(lfA|\)\(\(double) ==> long$1;;
        //           \(int\[\]\)      ==> (long[]);;
        //           long\s+sum       ==> double sum;;
        //           \(double\)sum    ==> sum ]]
        if (src instanceof IntArray) {
            final IntArray a = (IntArray)src;
            if (direct) {// int A: direct
                final int[] ja = (int[])((DirectAccessible)a).javaArray();
                final int jaOfs = ((DirectAccessible)a).javaArrayOffset();
                if (desiredType == IntArray.class) { // int A: direct, returning IntArray
                    return new AbstractIntArray(src.length(), true, src) {
                        public int getInt(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return (int)processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            int[] dest = (int[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = (int)(lfA * sum + lfB);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = (int)processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = (int)processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                } else { // int B: direct, returning DoubleArray
                    return new AbstractDoubleArray(src.length(), true, src) {
                        public double getDouble(long index) {
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            double[] dest = (double[])destArray;
                            long sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    for (int k = 0; k < count; k++, destArrayOffset++) {
                                        dest[destArrayOffset] = processingFunc.get(sum);
                                        long i = arrayPos + leftInvShift;
                                        sum -= ja[jaOfs + (int)i];
                                        arrayPos++;
                                        i = arrayPos + rightInvShift;
                                        sum += ja[jaOfs + (int)i];
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                }
            } else { // int C: indirect
                return new AbstractDoubleArray(src.length(), true, src) {
                    public double getDouble(long index) {
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = index - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        return processingFunc.get(sum);
                    }

                    public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                        if (!optimizeGetData) {
                            super.getData(arrayPos, destArray, destArrayOffset, count);
                            return;
                        }
                        if (destArray == null)
                            throw new NullPointerException("Null destArray argument");
                        checkRanges(length, arrayPos, count);
                        if (count == 0) {
                            return;
                        }
                        double[] dest = (double[])destArray;
                        long sum = 0;
                        for (long shift : shifts) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getInt(i);
                        }
                        for (int k = 0; k < count; k++, destArrayOffset++) {
                            dest[destArrayOffset] = processingFunc.get(sum);
                            for (long shift : right) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum -= a.getInt(i);
                            }
                            arrayPos++;
                            if (arrayPos == length) {
                                arrayPos = 0;
                            }
                            for (long shift : left) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += a.getInt(i);
                            }
                        }
                    }
                };
            }
        }
        //[[Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! ]]
        if (src instanceof LongArray) {
            final LongArray a = (LongArray)src;
            if (direct) {// long A: direct
                final long[] ja = (long[])((DirectAccessible)a).javaArray();
                final int jaOfs = ((DirectAccessible)a).javaArrayOffset();
                if (desiredType == LongArray.class) { // long A: direct, returning LongArray
                    return new AbstractLongArray(src.length(), true, src) {
                        public long getLong(long index) {
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return (long)processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            long[] dest = (long[])destArray;
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc) {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = (long)(lfA * sum + lfB);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = (long)processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = (long)processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                } else { // long B: direct, returning DoubleArray
                    return new AbstractDoubleArray(src.length(), true, src) {
                        public double getDouble(long index) {
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            double[] dest = (double[])destArray;
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    for (int k = 0; k < count; k++, destArrayOffset++) {
                                        dest[destArrayOffset] = processingFunc.get(sum);
                                        long i = arrayPos + leftInvShift;
                                        sum -= ja[jaOfs + (int)i];
                                        arrayPos++;
                                        i = arrayPos + rightInvShift;
                                        sum += ja[jaOfs + (int)i];
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                }
            } else { // long C: indirect
                return new AbstractDoubleArray(src.length(), true, src) {
                    public double getDouble(long index) {
                        double sum = 0;
                        for (long shift : shifts) {
                            long i = index - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getLong(i);
                        }
                        return processingFunc.get(sum);
                    }

                    public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                        if (!optimizeGetData) {
                            super.getData(arrayPos, destArray, destArrayOffset, count);
                            return;
                        }
                        if (destArray == null)
                            throw new NullPointerException("Null destArray argument");
                        checkRanges(length, arrayPos, count);
                        if (count == 0) {
                            return;
                        }
                        double[] dest = (double[])destArray;
                        double sum = 0;
                        for (long shift : shifts) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getLong(i);
                        }
                        for (int k = 0; k < count; k++, destArrayOffset++) {
                            dest[destArrayOffset] = processingFunc.get(sum);
                            for (long shift : right) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum -= a.getLong(i);
                            }
                            arrayPos++;
                            if (arrayPos == length) {
                                arrayPos = 0;
                            }
                            for (long shift : left) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += a.getLong(i);
                            }
                        }
                    }
                };
            }
        }
        //[[Repeat.AutoGeneratedEnd]]

        //[[Repeat() FloatArray(\s+a|\)) ==> DoubleArray$1;;
        //           float(\s+[ABC]|\[\]\s*ja|\[\]\)\(\() ==> double$1 ]]
        if (src instanceof FloatArray) {
            final FloatArray a = (FloatArray)src;
            if (direct) {
                final float[] ja = (float[])((DirectAccessible)a).javaArray();
                final int jaOfs = ((DirectAccessible)a).javaArrayOffset();
                if (desiredType == FloatArray.class) { // float A: direct, returning FloatArray
                    return new AbstractFloatArray(src.length(), true, src) {
                        public strictfp float getFloat(long index) {
                            double sum = 0.0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return (float)processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            float[] dest = (float[])destArray;
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc && jaOfs == 0) {
                                        if (lfB != 0.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = (float)(lfA * sum + lfB);
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else if (lfA != 1.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = (float)(lfA * sum);
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else {
                                            assert lfA == 1.0 && lfB == 0.0;
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = (float)sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = (float)processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = (float)processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                } else { // float B: direct, returning DoubleArray
                    return new AbstractDoubleArray(src.length(), true, src) {
                        public strictfp double getDouble(long index) {
                            double sum = 0.0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            double[] dest = (double[])destArray;
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc && jaOfs == 0) {
                                        if (lfB != 0.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum + lfB;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else if (lfA != 1.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else {
                                            assert lfA == 1.0 && lfB == 0.0;
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                }
            } else { // float C: indirect
                return new AbstractDoubleArray(src.length(), true, src) {
                    public strictfp double getDouble(long index) {
                        double sum = 0.0;
                        for (long shift : shifts) {
                            long i = index - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getDouble(i);
                        }
                        return processingFunc.get(sum);
                    }

                    public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                        if (!optimizeGetData) {
                            super.getData(arrayPos, destArray, destArrayOffset, count);
                            return;
                        }
                        if (destArray == null)
                            throw new NullPointerException("Null destArray argument");
                        checkRanges(length, arrayPos, count);
                        if (count == 0) {
                            return;
                        }
                        double[] dest = (double[])destArray;
                        double sum = 0;
                        for (long shift : shifts) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getDouble(i);
                        }
                        for (int k = 0; k < count; k++, destArrayOffset++) {
                            dest[destArrayOffset] = processingFunc.get(sum);
                            for (long shift : right) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum -= a.getDouble(i);
                            }
                            arrayPos++;
                            if (arrayPos == length) {
                                arrayPos = 0;
                            }
                            for (long shift : left) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += a.getDouble(i);
                            }
                        }
                    }
                };
            }
        }
        //[[Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! ]]
        if (src instanceof DoubleArray) {
            final DoubleArray a = (DoubleArray)src;
            if (direct) {
                final double[] ja = (double[])((DirectAccessible)a).javaArray();
                final int jaOfs = ((DirectAccessible)a).javaArrayOffset();
                if (desiredType == FloatArray.class) { // double A: direct, returning FloatArray
                    return new AbstractFloatArray(src.length(), true, src) {
                        public strictfp float getFloat(long index) {
                            double sum = 0.0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return (float)processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            float[] dest = (float[])destArray;
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc && jaOfs == 0) {
                                        if (lfB != 0.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = (float)(lfA * sum + lfB);
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else if (lfA != 1.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = (float)(lfA * sum);
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else {
                                            assert lfA == 1.0 && lfB == 0.0;
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = (float)sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = (float)processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = (float)processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                } else { // double B: direct, returning DoubleArray
                    return new AbstractDoubleArray(src.length(), true, src) {
                        public strictfp double getDouble(long index) {
                            double sum = 0.0;
                            for (long shift : shifts) {
                                long i = index - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            return processingFunc.get(sum);
                        }

                        public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                            if (!optimizeGetData) {
                                super.getData(arrayPos, destArray, destArrayOffset, count);
                                return;
                            }
                            if (destArray == null)
                                throw new NullPointerException("Null destArray argument");
                            checkRanges(length, arrayPos, count);
                            if (count == 0) {
                                return;
                            }
                            double[] dest = (double[])destArray;
                            double sum = 0;
                            for (long shift : shifts) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += ja[jaOfs + (int)i];
                            }
                            if (left.length == 1) {
                                long rightInvShift = left[0] == 0 ? 0 : length - left[0];
                                long leftInvShift = -right[0];
                                assert leftInvShift <= 0 && rightInvShift >= 0;
                                if (arrayPos + leftInvShift >= 0 && arrayPos < length - rightInvShift - count) {
                                    if (linearFunc && jaOfs == 0) {
                                        if (lfB != 0.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum + lfB;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else if (lfA != 1.0) {
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = lfA * sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        } else {
                                            assert lfA == 1.0 && lfB == 0.0;
                                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                                dest[destArrayOffset] = sum;
                                                long i = arrayPos + leftInvShift;
                                                sum -= ja[(int)i];
                                                arrayPos++;
                                                i = arrayPos + rightInvShift;
                                                sum += ja[(int)i];
                                            }
                                        }
                                    } else {
                                        for (int k = 0; k < count; k++, destArrayOffset++) {
                                            dest[destArrayOffset] = processingFunc.get(sum);
                                            long i = arrayPos + leftInvShift;
                                            sum -= ja[jaOfs + (int)i];
                                            arrayPos++;
                                            i = arrayPos + rightInvShift;
                                            sum += ja[jaOfs + (int)i];
                                        }
                                    }
                                    return;
                                }
                            }
                            for (int k = 0; k < count; k++, destArrayOffset++) {
                                dest[destArrayOffset] = processingFunc.get(sum);
                                for (long shift : right) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum -= ja[jaOfs + (int)i];
                                }
                                arrayPos++;
                                if (arrayPos == length) {
                                    arrayPos = 0;
                                }
                                for (long shift : left) {
                                    long i = arrayPos - shift;
                                    if (i < 0) {
                                        i += length;
                                    }
                                    sum += ja[jaOfs + (int)i];
                                }
                            }
                        }
                    };
                }
            } else { // double C: indirect
                return new AbstractDoubleArray(src.length(), true, src) {
                    public strictfp double getDouble(long index) {
                        double sum = 0.0;
                        for (long shift : shifts) {
                            long i = index - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getDouble(i);
                        }
                        return processingFunc.get(sum);
                    }

                    public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) {
                        if (!optimizeGetData) {
                            super.getData(arrayPos, destArray, destArrayOffset, count);
                            return;
                        }
                        if (destArray == null)
                            throw new NullPointerException("Null destArray argument");
                        checkRanges(length, arrayPos, count);
                        if (count == 0) {
                            return;
                        }
                        double[] dest = (double[])destArray;
                        double sum = 0;
                        for (long shift : shifts) {
                            long i = arrayPos - shift;
                            if (i < 0) {
                                i += length;
                            }
                            sum += a.getDouble(i);
                        }
                        for (int k = 0; k < count; k++, destArrayOffset++) {
                            dest[destArrayOffset] = processingFunc.get(sum);
                            for (long shift : right) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum -= a.getDouble(i);
                            }
                            arrayPos++;
                            if (arrayPos == length) {
                                arrayPos = 0;
                            }
                            for (long shift : left) {
                                long i = arrayPos - shift;
                                if (i < 0) {
                                    i += length;
                                }
                                sum += a.getDouble(i);
                            }
                        }
                    }
                };
            }
        }
        //[[Repeat.AutoGeneratedEnd]]
        throw new AssertionError("Illegal array type (" + src.getClass()
            + "): it must implement one of primitive XxxArray interfaces");
    }

    private static final int BIT_TYPE_CODE = 1;
    private static final int CHAR_TYPE_CODE = 2;
    private static final int BYTE_TYPE_CODE = 3;
    private static final int SHORT_TYPE_CODE = 4;
    private static final int INT_TYPE_CODE = 5;
    private static final int LONG_TYPE_CODE = 6;
    private static final int FLOAT_TYPE_CODE = 7;
    private static final int DOUBLE_TYPE_CODE = 8;

    private void processAlongAxis(UpdatablePArray dest, PArray src, long[] dimensions,
        final int coordIndex, final long min, final long max)
    {
        assert coordIndex > 0; // we don't try to optimize summarizing along x: the general algorithm works well here
        assert max - min + 1 <= Integer.MAX_VALUE;
        final int n = (int)(max - min + 1);
        final long length = src.length();
        if (length == 0) {
            return;
        }
        final boolean meanFunc = linearFunc && lfA >= 0.0 && lfA <= 1.0 / n && lfB >= 0.0 && lfB < 1.0;
        // in this case we can be sure that the linear function for byte will be <=255, for short <=65535
        final boolean meanByPowerOfTwoFunc = meanFunc && dividingByPowerOfTwoWithRoundingFunc;
        long layerSize = dimensions[0];
        for (int k = 1; k < coordIndex; k++) {
            layerSize *= dimensions[k];
        }
        long m = dimensions[coordIndex];
        for (int k = coordIndex + 1; k < dimensions.length; k++) {
            m *= dimensions[k];
        }
        assert m * layerSize == length;
        final long srcBitsPerElement = src.bitsPerElement();
        final Class<?> accElementType = srcBitsPerElement <= 16 && n <= Integer.MAX_VALUE >> srcBitsPerElement ?
            int.class : double.class;
        final boolean direct = optimizeDirectArrays
            && layerSize <= Integer.MAX_VALUE // necessary for allocation Java arrays
            && (double)layerSize * (Arrays.sizeOf(accElementType)
            + (src instanceof BitArray ? 1.0 : Arrays.sizeOf(src.elementType()))
            + (dest instanceof BitArray ? 1.0 : Arrays.sizeOf(dest.elementType())))
            < Arrays.SystemSettings.maxTempJavaMemory();
        double startPart = (double)n / ((double)m + (double)n);
        ArrayContext contextStart = contextPart(0.0, startPart);
        ArrayContext contextMain = contextPart(startPart, 1.0);
        if (direct) {
            final int len = (int)layerSize;
            assert len == layerSize;
            final int[] intAcc = accElementType == int.class ? new int[len] : null;
            final double[] doubleAcc = accElementType == double.class ? new double[len] : null;
            final int srcTypeCode;
            final Object srcBuffer;
            long elementCounter = 0;
            if (src instanceof BitArray) {
                srcTypeCode = BIT_TYPE_CODE;
                boolean[] srcBuf = new boolean[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                    for (int j = 0; j < len; j++) {
                        if (srcBuf[j]) {
                            intAcc[j]++;
                        }
                    }
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
                // The last command of the following Repeat instruction removes all lines with "intAcc" combination
                //[[Repeat() (\s*\&)\s*0xFFFF  ==> $1 0xFF,,$1 0xFFFF,, ,, ,, ,, ;;
                //           char              ==> byte,,short,,int,,long,,float,,double;;
                //           Char              ==> Byte,,Short,,Int,,Long,,Float,,Double;;
                //           CHAR              ==> BYTE,,SHORT,,INT,,LONG,,FLOAT,,DOUBLE;;
                //           ([ \t]*[^\n\r]*intAcc.*?(?:\r(?!\n)|\n|\r\n)) ==> $1,,$1,, ,, ...]]
            } else if (src instanceof CharArray) {
                srcTypeCode = CHAR_TYPE_CODE;
                char[] srcBuf = new char[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                    if (intAcc != null) {
                        for (int j = 0; j < len; j++) { // intAcc
                            intAcc[j] += srcBuf[j] & 0xFFFF;
                        } // intAcc
                    } else { // intAcc
                        for (int j = 0; j < len; j++) {
                            doubleAcc[j] += srcBuf[j] & 0xFFFF;
                        }
                    } // intAcc
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
                //[[Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! ]]
            } else if (src instanceof ByteArray) {
                srcTypeCode = BYTE_TYPE_CODE;
                byte[] srcBuf = new byte[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                    if (intAcc != null) {
                        for (int j = 0; j < len; j++) { // intAcc
                            intAcc[j] += srcBuf[j] & 0xFF;
                        } // intAcc
                    } else { // intAcc
                        for (int j = 0; j < len; j++) {
                            doubleAcc[j] += srcBuf[j] & 0xFF;
                        }
                    } // intAcc
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
            } else if (src instanceof ShortArray) {
                srcTypeCode = SHORT_TYPE_CODE;
                short[] srcBuf = new short[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                    if (intAcc != null) {
                        for (int j = 0; j < len; j++) { // intAcc
                            intAcc[j] += srcBuf[j] & 0xFFFF;
                        } // intAcc
                    } else { // intAcc
                        for (int j = 0; j < len; j++) {
                            doubleAcc[j] += srcBuf[j] & 0xFFFF;
                        }
                    } // intAcc
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
            } else if (src instanceof IntArray) {
                srcTypeCode = INT_TYPE_CODE;
                int[] srcBuf = new int[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                        for (int j = 0; j < len; j++) {
                            doubleAcc[j] += srcBuf[j];
                        }
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
            } else if (src instanceof LongArray) {
                srcTypeCode = LONG_TYPE_CODE;
                long[] srcBuf = new long[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                        for (int j = 0; j < len; j++) {
                            doubleAcc[j] += srcBuf[j];
                        }
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
            } else if (src instanceof FloatArray) {
                srcTypeCode = FLOAT_TYPE_CODE;
                float[] srcBuf = new float[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                        for (int j = 0; j < len; j++) {
                            doubleAcc[j] += srcBuf[j];
                        }
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
            } else if (src instanceof DoubleArray) {
                srcTypeCode = DOUBLE_TYPE_CODE;
                double[] srcBuf = new double[len];
                srcBuffer = srcBuf;
                for (long shift = min; shift <= max; shift++) {
                    long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                    if (i < 0) {
                        i += length;
                    }
                    src.getData(i, srcBuf);
                        for (int j = 0; j < len; j++) {
                            doubleAcc[j] += srcBuf[j];
                        }
                    if ((elementCounter += len) > 65536 && contextStart != null) {
                        elementCounter = 0;
                        contextStart.checkInterruptionAndUpdateProgress(null,  shift - min + 1, n);
                    }
                }
                //[[Repeat.AutoGeneratedEnd]]

            } else
                throw new AssertionError("Illegal source array type (" + src.getClass()
                    + "): it must implement one of primitive XxxArray interfaces");

            final int destTypeCode;
            final Object destBuffer;
            if (dest instanceof BitArray) {
                destTypeCode = BIT_TYPE_CODE;
                destBuffer = new boolean[len];
            } else if (dest instanceof CharArray) {
                destTypeCode = CHAR_TYPE_CODE;
                destBuffer = new char[len];
            } else if (dest instanceof ByteArray) {
                destTypeCode = BYTE_TYPE_CODE;
                destBuffer = new byte[len];
            } else if (dest instanceof ShortArray) {
                destTypeCode = SHORT_TYPE_CODE;
                destBuffer = new short[len];
            } else if (dest instanceof IntArray) {
                destTypeCode = INT_TYPE_CODE;
                destBuffer = new int[len];
            } else if (dest instanceof LongArray) {
                destTypeCode = LONG_TYPE_CODE;
                destBuffer = new long[len];
            } else if (dest instanceof FloatArray) {
                destTypeCode = FLOAT_TYPE_CODE;
                destBuffer = new float[len];
            } else if (dest instanceof DoubleArray) {
                destTypeCode = DOUBLE_TYPE_CODE;
                destBuffer = new double[len];
            } else
                throw new AssertionError("Illegal destination array type (" + dest.getClass()
                    + "): it must implement one of primitive XxxArray interfaces");

            for (long arrayPos = 0; ; ) {
                switch (destTypeCode) {
                    case BIT_TYPE_CODE: {
                        boolean[] destBuf = (boolean[])destBuffer;
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) {
                                destBuf[j] = processingFunc.get(intAcc[j]) != 0.0;
                            }
                        } else {
                            for (int j = 0; j < len; j++) {
                                destBuf[j] = processingFunc.get(doubleAcc[j]) != 0.0;
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }

                    case CHAR_TYPE_CODE: {
                        char[] destBuf = (char[])destBuffer;
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) {
                                int v = (int)processingFunc.get(intAcc[j]);
                                destBuf[j] = v < Character.MIN_VALUE ? Character.MIN_VALUE :
                                    v > Character.MAX_VALUE ? Character.MAX_VALUE :
                                        (char)v;
                            }
                        } else {
                            for (int j = 0; j < len; j++) {
                                int v = (int)processingFunc.get(doubleAcc[j]);
                                destBuf[j] = v < Character.MIN_VALUE ? Character.MIN_VALUE :
                                    v > Character.MAX_VALUE ? Character.MAX_VALUE :
                                        (char)v;
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }

                    //[[Repeat() byte ==> short;;
                    //           BYTE ==> SHORT;;
                    //           0xFF ==> 0xFFFF]]
                    case BYTE_TYPE_CODE: {
                        byte[] destBuf = (byte[])destBuffer;
                        if (intAcc != null) {
                            if (specialOptimizeThinPatternsPowersOfTwo && meanByPowerOfTwoFunc) {
                                for (int j = 0; j < len; j++) {
                                    long v = (intAcc[j] + halfDivisor) >> logDivisor;
                                    destBuf[j] = (byte)v;
                                }
                            } else if (meanFunc) {
                                for (int j = 0; j < len; j++) {
                                    int v = (int)(lfA * intAcc[j] + lfB);
                                    destBuf[j] = (byte)v;
                                }
                            } else if (linearFunc) {
                                if (lfA == 1.0 && lfB == 0.0) {
                                    for (int j = 0; j < len; j++) {
                                        int v = intAcc[j];
                                        destBuf[j] = (byte)(v < 0 ? 0 : v > 0xFF ? 0xFF : v);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        int v = (int)(lfA * intAcc[j] + lfB);
                                        destBuf[j] = (byte)(v < 0 ? 0 : v > 0xFF ? 0xFF : v);
                                    }
                                }
                            } else {
                                for (int j = 0; j < len; j++) {
                                    int v = (int)processingFunc.get(intAcc[j]);
                                    destBuf[j] = (byte)(v < 0 ? 0 : v > 0xFF ? 0xFF : v);
                                }
                            }
                        } else {
                            for (int j = 0; j < len; j++) {
                                int v = (int)processingFunc.get(doubleAcc[j]);
                                destBuf[j] = (byte)(v < 0 ? 0 : v > 0xFF ? 0xFF : v);
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }
                    //[[Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! ]]
                    case SHORT_TYPE_CODE: {
                        short[] destBuf = (short[])destBuffer;
                        if (intAcc != null) {
                            if (specialOptimizeThinPatternsPowersOfTwo && meanByPowerOfTwoFunc) {
                                for (int j = 0; j < len; j++) {
                                    long v = (intAcc[j] + halfDivisor) >> logDivisor;
                                    destBuf[j] = (short)v;
                                }
                            } else if (meanFunc) {
                                for (int j = 0; j < len; j++) {
                                    int v = (int)(lfA * intAcc[j] + lfB);
                                    destBuf[j] = (short)v;
                                }
                            } else if (linearFunc) {
                                if (lfA == 1.0 && lfB == 0.0) {
                                    for (int j = 0; j < len; j++) {
                                        int v = intAcc[j];
                                        destBuf[j] = (short)(v < 0 ? 0 : v > 0xFFFF ? 0xFFFF : v);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        int v = (int)(lfA * intAcc[j] + lfB);
                                        destBuf[j] = (short)(v < 0 ? 0 : v > 0xFFFF ? 0xFFFF : v);
                                    }
                                }
                            } else {
                                for (int j = 0; j < len; j++) {
                                    int v = (int)processingFunc.get(intAcc[j]);
                                    destBuf[j] = (short)(v < 0 ? 0 : v > 0xFFFF ? 0xFFFF : v);
                                }
                            }
                        } else {
                            for (int j = 0; j < len; j++) {
                                int v = (int)processingFunc.get(doubleAcc[j]);
                                destBuf[j] = (short)(v < 0 ? 0 : v > 0xFFFF ? 0xFFFF : v);
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }
                    //[[Repeat.AutoGeneratedEnd]]

                    case INT_TYPE_CODE: {
                        int[] destBuf = (int[])destBuffer;
                        if (linearFunc) {
                            if (lfB != 0.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (int)(lfA * intAcc[j] + lfB);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (int)(lfA * doubleAcc[j] + lfB);
                                    }
                                }
                            } else if (lfA != 1.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (int)(lfA * intAcc[j]);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (int)(lfA * doubleAcc[j]);
                                    }
                                }
                            } else {
                                assert lfA == 1.0 && lfB == 0.0;
                                if (intAcc != null) {
                                    dest.setData(arrayPos, intAcc);
                                    break;
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (int)doubleAcc[j];
                                    }
                                }
                            }
                        } else {
                            if (intAcc != null) {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = (int)processingFunc.get(intAcc[j]);
                                }
                            } else {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = (int)processingFunc.get(doubleAcc[j]);
                                }
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }

                    //[[Repeat() long(?!Buf)  ==> float;;
                    //           LONG         ==> FLOAT;;
                    //           \(double\)   ==> ]]
                    case LONG_TYPE_CODE: {
                        long[] destBuf = (long[])destBuffer;
                        if (linearFunc) {
                            if (lfB != 0.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (long)(lfA * intAcc[j] + lfB);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (long)(lfA * doubleAcc[j] + lfB);
                                    }
                                }
                            } else if (lfA != 1.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (long)(lfA * intAcc[j]);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (long)(lfA * doubleAcc[j]);
                                    }
                                }
                            } else {
                                assert lfA == 1.0 && lfB == 0.0;
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = intAcc[j];
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (long)doubleAcc[j];
                                    }
                                }
                            }
                        } else {
                            if (intAcc != null) {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = (long)processingFunc.get(intAcc[j]);
                                }
                            } else {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = (long)processingFunc.get(doubleAcc[j]);
                                }
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }
                    //[[Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! ]]
                    case FLOAT_TYPE_CODE: {
                        float[] destBuf = (float[])destBuffer;
                        if (linearFunc) {
                            if (lfB != 0.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (float)(lfA * intAcc[j] + lfB);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (float)(lfA * doubleAcc[j] + lfB);
                                    }
                                }
                            } else if (lfA != 1.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (float)(lfA * intAcc[j]);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (float)(lfA * doubleAcc[j]);
                                    }
                                }
                            } else {
                                assert lfA == 1.0 && lfB == 0.0;
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = intAcc[j];
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (float)doubleAcc[j];
                                    }
                                }
                            }
                        } else {
                            if (intAcc != null) {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = (float)processingFunc.get(intAcc[j]);
                                }
                            } else {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = (float)processingFunc.get(doubleAcc[j]);
                                }
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }
                    //[[Repeat.AutoGeneratedEnd]]

                    case DOUBLE_TYPE_CODE: {
                        double[] destBuf = (double[])destBuffer;
                        if (linearFunc) {
                            if (lfB != 0.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (lfA * intAcc[j] + lfB);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (lfA * doubleAcc[j] + lfB);
                                    }
                                }
                            } else if (lfA != 1.0) {
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (lfA * intAcc[j]);
                                    }
                                } else {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = (lfA * doubleAcc[j]);
                                    }
                                }
                            } else {
                                assert lfA == 1.0 && lfB == 0.0;
                                if (intAcc != null) {
                                    for (int j = 0; j < len; j++) {
                                        destBuf[j] = intAcc[j];
                                    }
                                } else {
                                    dest.setData(arrayPos, doubleAcc);
                                    break;
                                }
                            }
                        } else {
                            if (intAcc != null) {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = processingFunc.get(intAcc[j]);
                                }
                            } else {
                                for (int j = 0; j < len; j++) {
                                    destBuf[j] = processingFunc.get(doubleAcc[j]);
                                }
                            }
                        }
                        dest.setData(arrayPos, destBuf);
                        break;
                    }

                    default: {
                        throw new InternalError("Cannot occur");
                    }
                }
                if (arrayPos + layerSize == length) {
                    break;
                }
                long i1 = (arrayPos - max * layerSize) % length;
                if (i1 < 0) {
                    i1 += length;
                }
                arrayPos += layerSize;
                assert arrayPos <= length - layerSize;
                long i2 = (arrayPos - min * layerSize) % length;
                if (i2 < 0) {
                    i2 += length;
                }

                switch (srcTypeCode) {
                    case BIT_TYPE_CODE: {
                        boolean[] srcBuf = (boolean[])srcBuffer;
                        src.getData(i1, srcBuf);
                        for (int j = 0; j < len; j++) {
                            if (srcBuf[j]) {
                                intAcc[j]--;
                            }
                        }
                        src.getData(i2, srcBuf);
                        for (int j = 0; j < len; j++) {
                            if (srcBuf[j]) {
                                intAcc[j]++;
                            }
                        }
                        break;
                    }

                    // The last command of the following Repeat instruction removes all lines with "intAcc" combination
                    //[[Repeat() (\s*\&)\s*0xFFFF  ==> $1 0xFF,,$1 0xFFFF,, ,, ,, ,, ;;
                    //           char              ==> byte,,short,,int,,long,,float,,double;;
                    //           CHAR              ==> BYTE,,SHORT,,INT,,LONG,,FLOAT,,DOUBLE;;
                    //           ([ \t]*[^\n\r]*intAcc.*?(?:\r(?!\n)|\n|\r\n)) ==> $1,,$1,, ,, ...]]
                    case CHAR_TYPE_CODE: {
                        char[] srcBuf = (char[])srcBuffer;
                        src.getData(i1, srcBuf);
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) { // intAcc
                                intAcc[j] -= srcBuf[j] & 0xFFFF;
                            } // intAcc
                        } else { // intAcc
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] -= srcBuf[j] & 0xFFFF;
                            }
                        } // intAcc
                        src.getData(i2, srcBuf);
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) { // intAcc
                                intAcc[j] += srcBuf[j] & 0xFFFF;
                            } // intAcc
                        } else { // intAcc
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] += srcBuf[j] & 0xFFFF;
                            }
                        } // intAcc
                        break;
                    }
                    //[[Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! ]]
                    case BYTE_TYPE_CODE: {
                        byte[] srcBuf = (byte[])srcBuffer;
                        src.getData(i1, srcBuf);
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) { // intAcc
                                intAcc[j] -= srcBuf[j] & 0xFF;
                            } // intAcc
                        } else { // intAcc
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] -= srcBuf[j] & 0xFF;
                            }
                        } // intAcc
                        src.getData(i2, srcBuf);
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) { // intAcc
                                intAcc[j] += srcBuf[j] & 0xFF;
                            } // intAcc
                        } else { // intAcc
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] += srcBuf[j] & 0xFF;
                            }
                        } // intAcc
                        break;
                    }
                    case SHORT_TYPE_CODE: {
                        short[] srcBuf = (short[])srcBuffer;
                        src.getData(i1, srcBuf);
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) { // intAcc
                                intAcc[j] -= srcBuf[j] & 0xFFFF;
                            } // intAcc
                        } else { // intAcc
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] -= srcBuf[j] & 0xFFFF;
                            }
                        } // intAcc
                        src.getData(i2, srcBuf);
                        if (intAcc != null) {
                            for (int j = 0; j < len; j++) { // intAcc
                                intAcc[j] += srcBuf[j] & 0xFFFF;
                            } // intAcc
                        } else { // intAcc
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] += srcBuf[j] & 0xFFFF;
                            }
                        } // intAcc
                        break;
                    }
                    case INT_TYPE_CODE: {
                        int[] srcBuf = (int[])srcBuffer;
                        src.getData(i1, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] -= srcBuf[j];
                            }
                        src.getData(i2, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] += srcBuf[j];
                            }
                        break;
                    }
                    case LONG_TYPE_CODE: {
                        long[] srcBuf = (long[])srcBuffer;
                        src.getData(i1, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] -= srcBuf[j];
                            }
                        src.getData(i2, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] += srcBuf[j];
                            }
                        break;
                    }
                    case FLOAT_TYPE_CODE: {
                        float[] srcBuf = (float[])srcBuffer;
                        src.getData(i1, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] -= srcBuf[j];
                            }
                        src.getData(i2, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] += srcBuf[j];
                            }
                        break;
                    }
                    case DOUBLE_TYPE_CODE: {
                        double[] srcBuf = (double[])srcBuffer;
                        src.getData(i1, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] -= srcBuf[j];
                            }
                        src.getData(i2, srcBuf);
                            for (int j = 0; j < len; j++) {
                                doubleAcc[j] += srcBuf[j];
                            }
                        break;
                    }
                    //[[Repeat.AutoGeneratedEnd]]
                    default: {
                        throw new InternalError("Impossible switch case");
                    }
                }
                if ((elementCounter += len) > 65536 && contextMain != null) {
                    elementCounter = 0;
                    contextMain.checkInterruptionAndUpdateProgress(null, arrayPos, length);
                }
            }

        } else { // not direct
            // here we are sure that layers are large, so multiprocessing can be useful: we shall not disable it
            final Func updater = LinearFunc.getInstance(0.0, 1.0, -1.0, 1.0);
            UpdatablePArray acc = (UpdatablePArray) memoryModel().newUnresizableArray(accElementType, layerSize);
            for (long shift = min; shift <= max; shift++) {
                long i = (-shift * layerSize) % length; // "% length", because min..max can be negative or large
                if (i < 0) {
                    i += length;
                }
                Arrays.applyFunc(contextStart == null ? null : contextStart.part(shift - min, shift - min + 1, n),
                    false, Func.X_PLUS_Y, acc, acc, (PArray)src.subArr(i, layerSize));
            }
            for (long arrayPos = 0; ; ) {
                long halfPos = arrayPos + layerSize / 2;
                Arrays.applyFunc(contextMain == null ? null : contextMain.part(arrayPos, halfPos, length),
                    true, processingFunc, dest.subArr(arrayPos, layerSize), acc);
                if (arrayPos + layerSize == length) {
                    break;
                }
                long i1 = (arrayPos - max * layerSize) % length;
                if (i1 < 0) {
                    i1 += length;
                }
                arrayPos += layerSize;
                assert arrayPos <= length - layerSize;
                long i2 = (arrayPos - min * layerSize) % length;
                if (i2 < 0) {
                    i2 += length;
                }
                Arrays.applyFunc(contextMain == null ? null : contextMain.part(halfPos, arrayPos, length),
                    false, updater, acc,
                    acc,
                    (PArray)src.subArr(i1, layerSize),
                    (PArray)src.subArr(i2, layerSize));
            }
        }
        if (contextMain != null) {
            contextMain.checkInterruptionAndUpdateProgress(accElementType, length, length);
        }
    }

    private static void checkRanges(long length, long arrayPos, int count) {
        if (count < 0)
            throw new IllegalArgumentException("Negative number of loaded elements (" + count + ")");
        if (arrayPos < 0)
            throw new IndexOutOfBoundsException("arrayPos = " + arrayPos + " < 0");
        if (arrayPos > length - count)
            throw new IndexOutOfBoundsException("arrayPos+count = " + arrayPos + "+" + count + " > length=" + length);
    }
}
TOP

Related Classes of net.algart.matrices.morphology.Summator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.