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

com.landawn.abacus.util.Matrixes Maven / Gradle / Ivy

/*
 * Copyright (C) 2020 HaiYang Li
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.landawn.abacus.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.util.stream.IntStream;
import com.landawn.abacus.util.stream.Stream;

public final class Matrixes {

    static final Logger logger = LoggerFactory.getLogger(Matrixes.class);

    static final int MIN_COUNT_FOR_PARALLEL = 8192;

    static final boolean IS_PARALLEL_STREAM_SUPPORTED;
    static final ThreadLocal isParallelEnabled_TL = ThreadLocal.withInitial(() -> ParallelEnabled.DEFAULT);

    static {
        boolean tmp = false;

        try {
            if (ClassUtil.forClass("com.landawn.abacus.util.stream.ParallelArrayIntStream") != null
                    && ClassUtil.forClass("com.landawn.abacus.util.stream.ParallelIteratorIntStream") != null) {
                tmp = true;
            }
        } catch (final Exception e) {
            // ignore.
        }

        IS_PARALLEL_STREAM_SUPPORTED = tmp;
    }

    private Matrixes() {
        // singleton: utility class.
    }

    /**
     *
     *
     * @return
     */
    public static ParallelEnabled getParallelEnabled() {
        return isParallelEnabled_TL.get();
    }

    /**
     *
     *
     * @param flag
     * @throws IllegalArgumentException
     */
    public static void setParallelEnabled(final ParallelEnabled flag) throws IllegalArgumentException {
        N.checkArgNotNull(flag);

        isParallelEnabled_TL.set(flag);
    }

    /**
     *
     *
     * @param x
     * @return
     */
    public static boolean isParallelable(final AbstractMatrix x) {
        return isParallelable(x, x.count);
    }

    /**
     *
     *
     * @param x
     * @param count
     * @return
     */
    public static boolean isParallelable(@SuppressWarnings("unused") final AbstractMatrix x, final long count) { // NOSONAR
        return IS_PARALLEL_STREAM_SUPPORTED && (Matrixes.isParallelEnabled_TL.get() == ParallelEnabled.YES
                || (Matrixes.isParallelEnabled_TL.get() == ParallelEnabled.DEFAULT && count >= MIN_COUNT_FOR_PARALLEL));
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @return
     */
    public static > boolean isSameShape(final X a, final X b) {
        return a.rows == b.rows && a.cols == b.cols;
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @return
     */
    public static > boolean isSameShape(final X a, final X b, final X c) {
        return a.rows == b.rows && a.rows == c.rows && a.cols == b.cols && a.cols == c.cols;
    }

    /**
     *
     *
     * @param 
     * @param xs
     * @return
     */
    public static > boolean isSameShape(final Collection xs) {
        if (N.isEmpty(xs) || xs.size() == 1) {
            return true;
        }

        final Iterator iterator = xs.iterator();
        final X first = iterator.next();
        final int rows = first.rows;
        final int cols = first.cols;
        X next = null;

        while (iterator.hasNext()) {
            next = iterator.next();

            if (next.rows != rows || next.cols != cols) {
                return false;
            }
        }

        return true;
    }

    /**
     *
     *
     * @param 
     * @param rows
     * @param cols
     * @param targetElementType
     * @return
     */
    public static  T[][] newArray(final int rows, final int cols, final Class targetElementType) {
        final Class eleType = (Class) ClassUtil.wrap(targetElementType);
        final Class subArrayType = (Class) N.newArray(eleType, 0).getClass();

        final T[][] result = N.newArray(subArrayType, rows);

        for (int i = 0; i < rows; i++) {
            result[i] = N.newArray(eleType, cols);
        }

        return result;
    }

    /**
     * Executes the specified {@code cmd} under the specified {@code parallelEnabled} and reset {@code ParallelEnabled} after the command is completed.
     * @param cmd
     * @param parallelEnabled
     *
     * @param 
     * @throws E
     */
    public static  void run(final Throwables.Runnable cmd, final ParallelEnabled parallelEnabled) throws E {
        final ParallelEnabled original = Matrixes.getParallelEnabled();
        Matrixes.setParallelEnabled(parallelEnabled);

        try {
            cmd.run();
        } finally {
            Matrixes.setParallelEnabled(original);
        }
    }

    /**
     *
     * @param 
     * @param rows
     * @param cols
     * @param cmd
     * @param inParallel
     * @throws E
     */
    public static  void run(final int rows, final int cols, final Throwables.IntBiConsumer cmd, final boolean inParallel) throws E {
        run(0, rows, 0, cols, cmd, inParallel);
    }

    /**
     *
     *
     * @param 
     * @param fromRowIndex
     * @param toRowIndex
     * @param fromColumnIndex
     * @param toColumnIndex
     * @param cmd
     * @param inParallel
     * @throws IndexOutOfBoundsException
     * @throws E
     */
    public static  void run(final int fromRowIndex, final int toRowIndex, final int fromColumnIndex, final int toColumnIndex,
            final Throwables.IntBiConsumer cmd, final boolean inParallel) throws IndexOutOfBoundsException, E {
        N.checkFromToIndex(fromRowIndex, toRowIndex, Integer.MAX_VALUE);
        N.checkFromToIndex(fromColumnIndex, toColumnIndex, Integer.MAX_VALUE);

        final int rows = toRowIndex - fromRowIndex;
        final int cols = toColumnIndex - fromColumnIndex;

        if (inParallel) {
            if (rows <= cols) {
                //noinspection resource
                IntStream.range(fromRowIndex, toRowIndex).parallel().forEach(i -> {
                    for (int j = fromColumnIndex; j < toColumnIndex; j++) {
                        cmd.accept(i, j);
                    }
                });
            } else {
                //noinspection resource
                IntStream.range(fromColumnIndex, toColumnIndex).parallel().forEach(j -> {
                    for (int i = fromRowIndex; i < toRowIndex; i++) {
                        cmd.accept(i, j);
                    }
                });
            }
        } else {
            if (rows <= cols) {
                for (int i = fromRowIndex; i < toRowIndex; i++) {
                    for (int j = fromColumnIndex; j < toColumnIndex; j++) {
                        cmd.accept(i, j);
                    }
                }
            } else {
                for (int j = fromColumnIndex; j < toColumnIndex; j++) {
                    for (int i = fromRowIndex; i < toRowIndex; i++) {
                        cmd.accept(i, j);
                    }
                }
            }
        }
    }

    /**
     *
     *
     * @param 
     * @param rows
     * @param cols
     * @param cmd
     * @param inParallel
     * @return
     */
    public static  Stream call(final int rows, final int cols, final Throwables.IntBiFunction cmd,
            final boolean inParallel) {
        return call(0, rows, 0, cols, cmd, inParallel);
    }

    /**
     *
     *
     * @param 
     * @param fromRowIndex
     * @param toRowIndex
     * @param fromColumnIndex
     * @param toColumnIndex
     * @param cmd
     * @param inParallel
     * @return
     * @throws IndexOutOfBoundsException
     */
    @SuppressWarnings("resource")
    public static  Stream call(final int fromRowIndex, final int toRowIndex, final int fromColumnIndex, final int toColumnIndex,
            final Throwables.IntBiFunction cmd, final boolean inParallel) throws IndexOutOfBoundsException {
        N.checkFromToIndex(fromRowIndex, toRowIndex, Integer.MAX_VALUE);
        N.checkFromToIndex(fromColumnIndex, toColumnIndex, Integer.MAX_VALUE);

        final int rows = toRowIndex - fromRowIndex;
        final int cols = toColumnIndex - fromColumnIndex;

        if (rows <= cols) {
            return IntStream.range(fromRowIndex, toRowIndex).transform(s -> inParallel ? s.parallel() : s).flatmapToObj(i -> {
                final List ret = new ArrayList<>(cols);

                try {
                    for (int j = fromColumnIndex; j < toColumnIndex; j++) {
                        ret.add(cmd.apply(i, j));
                    }
                } catch (final Exception e) {
                    throw N.toRuntimeException(e);
                }

                return ret;
            });
        } else {
            return IntStream.range(fromColumnIndex, toColumnIndex).transform(s -> inParallel ? s.parallel() : s).flatmapToObj(j -> {
                final List ret = new ArrayList<>(rows);

                try {
                    for (int i = fromRowIndex; i < toRowIndex; i++) {
                        ret.add(cmd.apply(i, j));
                    }
                } catch (final Exception e) {
                    throw N.toRuntimeException(e);
                }

                return ret;
            });
        }
    }

    /**
     *
     *
     * @param rows
     * @param cols
     * @param cmd
     * @param inParallel
     * @return
     */
    public static IntStream callToInt(final int rows, final int cols, final Throwables.IntBinaryOperator cmd, final boolean inParallel) {
        return callToInt(0, rows, 0, cols, cmd, inParallel);
    }

    /**
     *
     *
     * @param fromRowIndex
     * @param toRowIndex
     * @param fromColumnIndex
     * @param toColumnIndex
     * @param cmd
     * @param inParallel
     * @return
     * @throws IndexOutOfBoundsException
     */
    @SuppressWarnings("resource")
    public static IntStream callToInt(final int fromRowIndex, final int toRowIndex, final int fromColumnIndex, final int toColumnIndex,
            final Throwables.IntBinaryOperator cmd, final boolean inParallel) throws IndexOutOfBoundsException {
        N.checkFromToIndex(fromRowIndex, toRowIndex, Integer.MAX_VALUE);
        N.checkFromToIndex(fromColumnIndex, toColumnIndex, Integer.MAX_VALUE);

        final int rows = toRowIndex - fromRowIndex;
        final int cols = toColumnIndex - fromColumnIndex;

        if (rows <= cols) {
            return IntStream.range(fromRowIndex, toRowIndex).transform(s -> inParallel ? s.parallel() : s).flatmap(i -> {
                final int[] ret = new int[cols];

                try {
                    for (int j = fromColumnIndex; j < toColumnIndex; j++) {
                        ret[j - fromColumnIndex] = cmd.applyAsInt(i, j);
                    }
                } catch (final Exception e) {
                    throw N.toRuntimeException(e);
                }

                return ret;
            });
        } else {
            return IntStream.range(fromColumnIndex, toColumnIndex).transform(s -> inParallel ? s.parallel() : s).flatmap(j -> {
                final int[] ret = new int[rows];

                try {
                    for (int i = fromRowIndex; i < toRowIndex; i++) {
                        ret[i - fromRowIndex] = cmd.applyAsInt(i, j);
                    }
                } catch (final Exception e) {
                    throw N.toRuntimeException(e);
                }

                return ret;
            });
        }
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param cmd
     * @throws IllegalArgumentException
     */
    public static > void multiply(final X a, final X b, final Throwables.IntTriConsumer cmd)
            throws IllegalArgumentException {
        N.checkArgument(a.cols == b.rows, "Illegal matrix dimensions");

        multiply(a, b, cmd, Matrixes.isParallelable(a, a.count * b.cols));
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param cmd
     * @param inParallel
     * @throws IllegalArgumentException
     */
    public static > void multiply(final X a, final X b, final Throwables.IntTriConsumer cmd, // NOSONAR
            final boolean inParallel) throws IllegalArgumentException {
        N.checkArgument(a.cols == b.rows, "Illegal matrix dimensions");

        final int rowsA = a.rows;
        final int colsA = a.cols;
        final int colsB = b.cols;

        if (inParallel) {
            if (N.min(rowsA, colsA, colsB) == rowsA) {
                if (N.min(colsA, colsB) == colsA) {
                    //noinspection resource
                    IntStream.range(0, rowsA).parallel().forEach(i -> {
                        for (int k = 0; k < colsA; k++) {
                            for (int j = 0; j < colsB; j++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    });
                } else {
                    //noinspection resource
                    IntStream.range(0, rowsA).parallel().forEach(i -> {
                        for (int j = 0; j < colsB; j++) {
                            for (int k = 0; k < colsA; k++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    });
                }
            } else if (N.min(rowsA, colsA, colsB) == colsA) {
                if (N.min(rowsA, colsB) == rowsA) {
                    //noinspection resource
                    IntStream.range(0, colsA).parallel().forEach(k -> {
                        for (int i = 0; i < rowsA; i++) {
                            for (int j = 0; j < colsB; j++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    });
                } else {
                    //noinspection resource
                    IntStream.range(0, colsA).parallel().forEach(k -> {
                        for (int j = 0; j < colsB; j++) {
                            for (int i = 0; i < rowsA; i++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    });
                }
            } else {
                if (N.min(rowsA, colsA) == rowsA) {
                    //noinspection resource
                    IntStream.range(0, colsB).parallel().forEach(j -> {
                        for (int i = 0; i < rowsA; i++) {
                            for (int k = 0; k < colsA; k++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    });
                } else {
                    //noinspection resource
                    IntStream.range(0, colsB).parallel().forEach(j -> {
                        for (int k = 0; k < colsA; k++) {
                            for (int i = 0; i < rowsA; i++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    });
                }
            }
        } else {
            if (N.min(rowsA, colsA, colsB) == rowsA) {
                if (N.min(colsA, colsB) == colsA) {
                    for (int i = 0; i < rowsA; i++) {
                        for (int k = 0; k < colsA; k++) {
                            for (int j = 0; j < colsB; j++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    }
                } else {
                    for (int i = 0; i < rowsA; i++) {
                        for (int j = 0; j < colsB; j++) {
                            for (int k = 0; k < colsA; k++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    }
                }
            } else if (N.min(rowsA, colsA, colsB) == colsA) {
                if (N.min(rowsA, colsB) == rowsA) {
                    for (int k = 0; k < colsA; k++) {
                        for (int i = 0; i < rowsA; i++) {
                            for (int j = 0; j < colsB; j++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    }
                } else {
                    for (int k = 0; k < colsA; k++) {
                        for (int j = 0; j < colsB; j++) {
                            for (int i = 0; i < rowsA; i++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    }
                }
            } else {
                if (N.min(rowsA, colsA) == rowsA) {
                    for (int j = 0; j < colsB; j++) {
                        for (int i = 0; i < rowsA; i++) {
                            for (int k = 0; k < colsA; k++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    }
                } else {
                    for (int j = 0; j < colsB; j++) {
                        for (int k = 0; k < colsA; k++) {
                            for (int i = 0; i < rowsA; i++) {
                                cmd.accept(i, j, k);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  ByteMatrix zip(final ByteMatrix a, final ByteMatrix b, final Throwables.ByteBinaryOperator zipFunction) throws E {
        return a.zipWith(b, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  ByteMatrix zip(final ByteMatrix a, final ByteMatrix b, final ByteMatrix c,
            final Throwables.ByteTernaryOperator zipFunction) throws E {
        return a.zipWith(b, c, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  ByteMatrix zip(final Collection c, final Throwables.ByteBinaryOperator zipFunction)
            throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final ByteMatrix[] matrixes = c.toArray(new ByteMatrix[size]);

        if (c.size() == 1) {
            return matrixes[0].copy();
        } else if (c.size() == 2) {
            return matrixes[0].zipWith(matrixes[1], zipFunction);
        }

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final byte[][] result = new byte[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final byte[] ret = result[i];
            ret[j] = matrixes[0].a[i][j];

            for (int k = 1; k < size; k++) {
                ret[j] = zipFunction.applyAsByte(ret[j], matrixes[k].a[i][j]);
            }
        };

        run(rows, cols, cmd, Matrixes.isParallelable(matrixes[0]));

        return new ByteMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param targetElementType
     * @return
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.ByteNFunction zipFunction,
            final Class targetElementType) throws E {
        return zip(c, zipFunction, false, targetElementType);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @param targetElementType
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.ByteNFunction zipFunction,
            final boolean shareIntermediateArray, final Class targetElementType) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final ByteMatrix[] matrixes = c.toArray(new ByteMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final byte[] intermediateArray = new byte[size];
        final R[][] result = newArray(rows, cols, targetElementType);

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final byte[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new Matrix<>(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  IntMatrix zipToInt(final ByteMatrix a, final ByteMatrix b, final Throwables.ByteBiFunction zipFunction)
            throws E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final byte[][] aa = a.a;
        final byte[][] ba = b.a;
        final int[][] result = new int[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new IntMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  IntMatrix zipToInt(final ByteMatrix a, final ByteMatrix b, final ByteMatrix c,
            final Throwables.ByteTriFunction zipFunction) throws IllegalArgumentException, E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final byte[][] aa = a.a;
        final byte[][] ba = b.a;
        final byte[][] ca = c.a;
        final int[][] result = new int[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j], ca[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new IntMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  IntMatrix zipToInt(final Collection c, final Throwables.ByteNFunction zipFunction) throws E {
        return zipToInt(c, zipFunction, false);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  IntMatrix zipToInt(final Collection c, final Throwables.ByteNFunction zipFunction,
            final boolean shareIntermediateArray) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final ByteMatrix[] matrixes = c.toArray(new ByteMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final byte[] intermediateArray = new byte[size];
        final int[][] result = new int[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final byte[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new IntMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  IntMatrix zip(final IntMatrix a, final IntMatrix b, final Throwables.IntBinaryOperator zipFunction) throws E {
        return a.zipWith(b, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  IntMatrix zip(final IntMatrix a, final IntMatrix b, final IntMatrix c,
            final Throwables.IntTernaryOperator zipFunction) throws E {
        return a.zipWith(b, c, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  IntMatrix zip(final Collection c, final Throwables.IntBinaryOperator zipFunction)
            throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final IntMatrix[] matrixes = c.toArray(new IntMatrix[size]);

        if (c.size() == 1) {
            return matrixes[0].copy();
        } else if (c.size() == 2) {
            return matrixes[0].zipWith(matrixes[1], zipFunction);
        }

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final int[][] result = new int[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final int[] ret = result[i];
            ret[j] = matrixes[0].a[i][j];

            for (int k = 1; k < size; k++) {
                ret[j] = zipFunction.applyAsInt(ret[j], matrixes[k].a[i][j]);
            }
        };

        run(rows, cols, cmd, Matrixes.isParallelable(matrixes[0]));

        return new IntMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param targetElementType
     * @return
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.IntNFunction zipFunction,
            final Class targetElementType) throws E {
        return zip(c, zipFunction, false, targetElementType);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @param targetElementType
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.IntNFunction zipFunction,
            final boolean shareIntermediateArray, final Class targetElementType) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final IntMatrix[] matrixes = c.toArray(new IntMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final int[] intermediateArray = new int[size];
        final R[][] result = newArray(rows, cols, targetElementType);

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final int[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new Matrix<>(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  LongMatrix zipToLong(final IntMatrix a, final IntMatrix b, final Throwables.IntBiFunction zipFunction)
            throws E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final int[][] aa = a.a;
        final int[][] ba = b.a;
        final long[][] result = new long[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new LongMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  LongMatrix zipToLong(final IntMatrix a, final IntMatrix b, final IntMatrix c,
            final Throwables.IntTriFunction zipFunction) throws IllegalArgumentException, E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final int[][] aa = a.a;
        final int[][] ba = b.a;
        final int[][] ca = c.a;
        final long[][] result = new long[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j], ca[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new LongMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  LongMatrix zipToLong(final Collection c, final Throwables.IntNFunction zipFunction) throws E {
        return zipToLong(c, zipFunction, false);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  LongMatrix zipToLong(final Collection c, final Throwables.IntNFunction zipFunction,
            final boolean shareIntermediateArray) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final IntMatrix[] matrixes = c.toArray(new IntMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final int[] intermediateArray = new int[size];
        final long[][] result = new long[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final int[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new LongMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final IntMatrix a, final IntMatrix b, final Throwables.IntBiFunction zipFunction)
            throws E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final int[][] aa = a.a;
        final int[][] ba = b.a;
        final double[][] result = new double[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new DoubleMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final IntMatrix a, final IntMatrix b, final IntMatrix c,
            final Throwables.IntTriFunction zipFunction) throws IllegalArgumentException, E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final int[][] aa = a.a;
        final int[][] ba = b.a;
        final int[][] ca = c.a;
        final double[][] result = new double[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j], ca[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new DoubleMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final Collection c, final Throwables.IntNFunction zipFunction)
            throws IllegalArgumentException, E {
        return zipToDouble(c, zipFunction, false);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final Collection c, final Throwables.IntNFunction zipFunction,
            final boolean shareIntermediateArray) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final IntMatrix[] matrixes = c.toArray(new IntMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final int[] intermediateArray = new int[size];
        final double[][] result = new double[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final int[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new DoubleMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  LongMatrix zip(final LongMatrix a, final LongMatrix b, final Throwables.LongBinaryOperator zipFunction) throws E {
        return a.zipWith(b, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  LongMatrix zip(final LongMatrix a, final LongMatrix b, final LongMatrix c,
            final Throwables.LongTernaryOperator zipFunction) throws E {
        return a.zipWith(b, c, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  LongMatrix zip(final Collection c, final Throwables.LongBinaryOperator zipFunction)
            throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final LongMatrix[] matrixes = c.toArray(new LongMatrix[size]);

        if (c.size() == 1) {
            return matrixes[0].copy();
        } else if (c.size() == 2) {
            return matrixes[0].zipWith(matrixes[1], zipFunction);
        }

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final long[][] result = new long[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final long[] ret = result[i];
            ret[j] = matrixes[0].a[i][j];

            for (int k = 1; k < size; k++) {
                ret[j] = zipFunction.applyAsLong(ret[j], matrixes[k].a[i][j]);
            }
        };

        run(rows, cols, cmd, Matrixes.isParallelable(matrixes[0]));

        return new LongMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param targetElementType
     * @return
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.LongNFunction zipFunction,
            final Class targetElementType) throws E {
        return zip(c, zipFunction, false, targetElementType);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @param targetElementType
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.LongNFunction zipFunction,
            final boolean shareIntermediateArray, final Class targetElementType) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final LongMatrix[] matrixes = c.toArray(new LongMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final long[] intermediateArray = new long[size];
        final R[][] result = newArray(rows, cols, targetElementType);

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final long[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new Matrix<>(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final LongMatrix a, final LongMatrix b, final Throwables.LongBiFunction zipFunction)
            throws E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final long[][] aa = a.a;
        final long[][] ba = b.a;
        final double[][] result = new double[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new DoubleMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final LongMatrix a, final LongMatrix b, final LongMatrix c,
            final Throwables.LongTriFunction zipFunction) throws IllegalArgumentException, E {
        checkShapeForZip(a, b);

        final int rows = a.rows;
        final int cols = a.cols;
        final long[][] aa = a.a;
        final long[][] ba = b.a;
        final long[][] ca = c.a;
        final double[][] result = new double[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> result[i][j] = zipFunction.apply(aa[i][j], ba[i][j], ca[i][j]);

        run(rows, cols, cmd, Matrixes.isParallelable(a));

        return new DoubleMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final Collection c, final Throwables.LongNFunction zipFunction)
            throws E {
        return zipToDouble(c, zipFunction, false);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  DoubleMatrix zipToDouble(final Collection c, final Throwables.LongNFunction zipFunction,
            final boolean shareIntermediateArray) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final LongMatrix[] matrixes = c.toArray(new LongMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final long[] intermediateArray = new long[size];
        final double[][] result = new double[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final long[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new DoubleMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  DoubleMatrix zip(final DoubleMatrix a, final DoubleMatrix b, final Throwables.DoubleBinaryOperator zipFunction)
            throws E {
        return a.zipWith(b, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  DoubleMatrix zip(final DoubleMatrix a, final DoubleMatrix b, final DoubleMatrix c,
            final Throwables.DoubleTernaryOperator zipFunction) throws E {
        return a.zipWith(b, c, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  DoubleMatrix zip(final Collection c, final Throwables.DoubleBinaryOperator zipFunction)
            throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final DoubleMatrix[] matrixes = c.toArray(new DoubleMatrix[size]);

        if (c.size() == 1) {
            return matrixes[0].copy();
        } else if (c.size() == 2) {
            return matrixes[0].zipWith(matrixes[1], zipFunction);
        }

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final double[][] result = new double[rows][cols];

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final double[] ret = result[i];
            ret[j] = matrixes[0].a[i][j];

            for (int k = 1; k < size; k++) {
                ret[j] = zipFunction.applyAsDouble(ret[j], matrixes[k].a[i][j]);
            }
        };

        run(rows, cols, cmd, Matrixes.isParallelable(matrixes[0]));

        return new DoubleMatrix(result);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param targetElementType
     * @return
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.DoubleNFunction zipFunction,
            final Class targetElementType) throws E {
        return zip(c, zipFunction, false, targetElementType);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @param targetElementType
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  Matrix zip(final Collection c, final Throwables.DoubleNFunction zipFunction,
            final boolean shareIntermediateArray, final Class targetElementType) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final DoubleMatrix[] matrixes = c.toArray(new DoubleMatrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final double[] intermediateArray = new double[size];
        final R[][] result = newArray(rows, cols, targetElementType);

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final double[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new Matrix<>(result);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  Matrix zip(final Matrix a, final Matrix b,
            final Throwables.BiFunction zipFunction) throws E {
        return a.zipWith(b, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param 
     * @param 
     * @param a
     * @param b
     * @param zipFunction
     * @param targetElementType
     * @return
     * @throws E
     */
    public static  Matrix zip(final Matrix a, final Matrix b,
            final Throwables.BiFunction zipFunction, final Class targetElementType) throws E {
        return a.zipWith(b, zipFunction, targetElementType);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param 
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @return
     * @throws E
     */
    public static  Matrix zip(final Matrix a, final Matrix b, final Matrix c,
            final Throwables.TriFunction zipFunction) throws E {
        return a.zipWith(b, c, zipFunction);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param 
     * @param 
     * @param 
     * @param a
     * @param b
     * @param c
     * @param zipFunction
     * @param targetElementType
     * @return
     * @throws E
     */
    public static  Matrix zip(final Matrix a, final Matrix b, final Matrix c,
            final Throwables.TriFunction zipFunction, final Class targetElementType) throws E {
        return a.zipWith(b, c, zipFunction, targetElementType);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  Matrix zip(final Collection> c, final Throwables.BinaryOperator zipFunction)
            throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final Matrix[] matrixes = c.toArray(new Matrix[size]);

        if (c.size() == 1) {
            return matrixes[0].copy();
        } else if (c.size() == 2) {
            return matrixes[0].zipWith(matrixes[1], zipFunction);
        }

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final T[][] result = newArray(rows, cols, matrixes[0].elementType);

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final T[] ret = result[i];
            ret[j] = matrixes[0].a[i][j];

            for (int k = 1; k < size; k++) {
                ret[j] = zipFunction.apply(ret[j], matrixes[k].a[i][j]);
            }
        };

        run(rows, cols, cmd, Matrixes.isParallelable(matrixes[0]));

        return new Matrix<>(result);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param targetElementType
     * @return
     * @throws E
     */
    public static  Matrix zip(final Collection> c, final Throwables.Function zipFunction,
            final Class targetElementType) throws E {
        return zip(c, zipFunction, false, targetElementType);
    }

    /**
     *
     *
     * @param 
     * @param 
     * @param 
     * @param c
     * @param zipFunction
     * @param shareIntermediateArray
     * @param targetElementType
     * @return
     * @throws IllegalArgumentException
     * @throws E
     */
    public static  Matrix zip(final Collection> c, final Throwables.Function zipFunction,
            final boolean shareIntermediateArray, final Class targetElementType) throws IllegalArgumentException, E {
        checkShapeForZip(c);

        final int size = c.size();
        final Matrix[] matrixes = c.toArray(new Matrix[size]);

        final int rows = matrixes[0].rows;
        final int cols = matrixes[0].cols;
        final boolean zipInParallel = Matrixes.isParallelable(matrixes[0]);
        final boolean shareArray = shareIntermediateArray && !zipInParallel;
        final T[] intermediateArray = N.newArray(matrixes[0].elementType, size);
        final R[][] result = newArray(rows, cols, targetElementType);

        final Throwables.IntBiConsumer cmd = (i, j) -> {
            final T[] tmp = shareArray ? intermediateArray : N.clone(intermediateArray);

            for (int k = 0; k < size; k++) {
                tmp[k] = matrixes[k].a[i][j];
            }

            result[i][j] = zipFunction.apply(tmp);
        };

        run(rows, cols, cmd, zipInParallel);

        return new Matrix<>(result);
    }

    private static void checkShapeForZip(final AbstractMatrix a, final AbstractMatrix b) {
        N.checkArgument(isSameShape(a, b), "Can't zip two or more matrices which don't have same shape.");
    }

    private static void checkShapeForZip(final Collection> c) {
        N.checkArgNotEmpty(c, "matrixes");

        N.checkArgument(isSameShape(c), "Can't zip two or more matrices which don't have same shape");
    }
}