/* ***** BEGIN LICENSE BLOCK *****
 * JLargeArrays
 * Copyright (C) 2013 onward University of Warsaw, ICM
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */
package pl.edu.icm.jlargearrays;

import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.apache.commons.math3.util.FastMath;

/**
 * The base class for all large arrays. All implementations of this abstract
 * class can store up to 263 elements of primitive data types.
 *
 * @author Piotr Wendykier (p.wendykier@icm.edu.pl)
 */
public abstract class LargeArray implements java.io.Serializable
{

    private static final long serialVersionUID = 7921589398878016801L;
    protected LargeArrayType type;
    protected long length;
    protected boolean isConstant = false;
    protected Object parent = null;
    protected transient long ptr = 0;

    /**
     * The largest array size for which a regular 1D Java array is used to store data.
     */
    private static int maxSizeOf32bitArray = 1073741824; // 2^30;

    /**
     * If the number of elements in an array is greater than the value of LARGEST_SUBARRAY, then getXXXData() methods return null.
     */
    public static final int LARGEST_SUBARRAY = 1073741824; // 2^30;

    /**
     * Default value of maximal string length.
     */
    public static final int DEFAULT_MAX_STRING_LENGTH = 100;

    /**
     * Default value of maximal object size.
     */
    public static final int DEFAULT_MAX_OBJECT_SIZE = 1024; //1KB

    /**
     * Creates new instance of this class.
     */
    protected LargeArray()
    {
        super();
    }

    /**
     * Creates new instance of this class by wrapping a native pointer.
     * Providing an invalid pointer, parent or length will result in
     * unpredictable behavior and likely JVM crash. The assumption is that the
     * pointer is valid as long as the parent is not garbage collected.
     *
     * @param parent         class instance responsible for handling the pointer's life
     *                       cycle, the created instance of LargeArray will prevent the GC from
     *                       reclaiming the parent.
     * @param nativePointer  native pointer to wrap.
     * @param largeArrayType type of array
     * @param length         array length
     */
    public LargeArray(final Object parent,
                      final long nativePointer,
                      final LargeArrayType largeArrayType,
                      final long length)
    {
        super();
        this.parent = parent;
        this.ptr = nativePointer;
        this.type = largeArrayType;
        if (length < 0) {
            throw new IllegalArgumentException(length + " is not a nonnegative long value");
        }
        this.length = length;
    }

    /**
     * Returns the internal pointer address (if isLarge)
     *
     * @return native pointer address or 0 if not large array.
     */
    public long nativePointer()
    {
        return ptr;
    }

    /**
     * Returns the length of an array.
     *
     * @return the length of an array
     */
    public long length()
    {
        return length;
    }

    /**
     * Returns the type of an array.
     *
     * @return the type of an array
     */
    public LargeArrayType getType()
    {
        return type;
    }

    /**
     * Returns a value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract Object get(long i);

    /**
     * Returns a value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public Object get_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return get(i);
    }

    /**
     * Returns a value at index i. Array bounds are not checked. If isLarge()
     * returns false for a given array or the index argument is invalid, then
     * calling this method will cause JVM crash.
     *
     * @param i index
     *
     * @return a value at index i. The type of returned value is the same as the
     *         type of this array.
     */
    public abstract Object getFromNative(long i);

    /**
     * Returns a boolean value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a boolean value at index i.
     */
    public abstract boolean getBoolean(long i);

    /**
     * Returns a boolean value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a boolean value at index i.
     */
    public boolean getBoolean_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getBoolean(i);
    }

    /**
     * Returns a signed byte value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract byte getByte(long i);

    /**
     * Returns a signed byte value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public byte getByte_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getByte(i);
    }

    /**
     * Returns an unsigned byte value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract short getUnsignedByte(long i);

    /**
     * Returns an unsigned byte value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public short getUnsignedByte_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getUnsignedByte(i);
    }

    /**
     * Returns a short value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract short getShort(long i);

    /**
     * Returns a short value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public short getShort_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getShort(i);
    }

    /**
     * Returns an int value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract int getInt(long i);

    /**
     * Returns an int value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public int getInt_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getInt(i);
    }

    /**
     * Returns a long value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract long getLong(long i);

    /**
     * Returns a long value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public long getLong_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getLong(i);
    }

    /**
     * Returns a float value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract float getFloat(long i);

    /**
     * Returns a float value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public float getFloat_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getFloat(i);
    }

    /**
     * Returns a double value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public abstract double getDouble(long i);

    /**
     * Returns a double value at index i. Array bounds are checked.
     *
     * @param i an index
     *
     * @return a value at index i.
     */
    public double getDouble_safe(final long i)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        return getDouble(i);
    }

    /**
     * Returns a reference to the internal data array. For constant arrays the length of the returned array is always equal to 1.
     * If isLarge() returns true, then this method returns null.
     *
     * @return reference to the internal data array or null
     */
    public abstract Object getData();

    /**
     * If the size of the array is smaller than LargeArray.LARGEST_SUBARRAY, then this
     * method returns boolean data. Otherwise, it returns null.
     *
     * @return an array containing the elements of this object or null
     */
    public abstract boolean[] getBooleanData();

    /**
     * If (endPos - startPos) / step is smaller than LargeArray.LARGEST_SUBARRAY, then
     * this method returns selected elements of this object. Otherwise, it returns
     * null. If (endPos - startPos) / step is smaller or equal to a.length, it
     * is returned therein. Otherwise, a new array is allocated and returned.
     * Array bounds are checked.
     *
     * @param a        the array into which the elements are to be stored, if it is big
     *                 enough; otherwise, a new array of is allocated for this purpose.
     * @param startPos starting position (included)
     * @param endPos   ending position (excluded)
     * @param step     step size
     *
     * @return an array containing the elements of this object or null
     */
    public abstract boolean[] getBooleanData(boolean[] a,
                                             long startPos,
                                             long endPos,
                                             long step);

    /**
     * If the size of the array is smaller than LargeArray.LARGEST_SUBARRAY, then this
     * method returns byte data. Otherwise, it returns null.
     *
     * @return an array containing the elements of this object or null
     */
    public abstract byte[] getByteData();

    /**
     * If (endPos - startPos) / step is smaller than LargeArray.LARGEST_SUBARRAY, then
     * this method returns selected elements of this object. Otherwise, it returns
     * null. If (endPos - startPos) / step is smaller or equal to a.length, it
     * is returned therein. Otherwise, a new array is allocated and returned.
     * Array bounds are checked.
     *
     * @param a        the array into which the elements are to be stored, if it is big
     *                 enough; otherwise, a new array of is allocated for this purpose.
     * @param startPos starting position (included)
     * @param endPos   ending position (excluded)
     * @param step     step size
     *
     * @return an array containing the elements of this object or null
     */
    public abstract byte[] getByteData(byte[] a,
                                       long startPos,
                                       long endPos,
                                       long step);

    /**
     * If the size of the array is smaller than LargeArray.LARGEST_SUBARRAY, then this
     * method returns short data. Otherwise, it returns null.
     *
     * @return an array containing the elements of this object or null
     */
    public abstract short[] getShortData();

    /**
     * If (endPos - startPos) / step is smaller than LargeArray.LARGEST_SUBARRAY, then
     * this method returns selected elements of this object. Otherwise, it returns
     * null. If (endPos - startPos) / step is smaller or equal to a.length, it
     * is returned therein. Otherwise, a new array is allocated and returned.
     * Array bounds are checked.
     *
     * @param a        the array into which the elements are to be stored, if it is big
     *                 enough; otherwise, a new array of is allocated for this purpose.
     * @param startPos starting position (included)
     * @param endPos   ending position (excluded)
     * @param step     step size
     *
     * @return an array containing the elements of this object or null
     */
    public abstract short[] getShortData(short[] a,
                                         long startPos,
                                         long endPos,
                                         long step);

    /**
     * If the size of the array is smaller than LargeArray.LARGEST_SUBARRAY, then this
     * method returns int data. Otherwise, it returns null.
     *
     * @return an array containing the elements of this object or null
     */
    public abstract int[] getIntData();

    /**
     * If (endPos - startPos) / step is smaller than LargeArray.LARGEST_SUBARRAY, then
     * this method returns selected elements of this object. Otherwise, it returns
     * null. If (endPos - startPos) / step is smaller or equal to a.length, it
     * is returned therein. Otherwise, a new array is allocated and returned.
     * Array bounds are checked.
     *
     * @param a        the array into which the elements are to be stored, if it is big
     *                 enough; otherwise, a new array of is allocated for this purpose.
     * @param startPos starting position (included)
     * @param endPos   ending position (excluded)
     * @param step     step size
     *
     * @return an array containing the elements of this object or null
     */
    public abstract int[] getIntData(int[] a,
                                     long startPos,
                                     long endPos,
                                     long step);

    /**
     * If the size of the array is smaller than LargeArray.LARGEST_SUBARRAY, then this
     * method returns long data. Otherwise, it returns null.
     *
     * @return an array containing the elements of this object or null
     */
    public abstract long[] getLongData();

    /**
     * If (endPos - startPos) / step is smaller than LargeArray.LARGEST_SUBARRAY, then
     * this method returns selected elements of this object. Otherwise, it returns
     * null. If (endPos - startPos) / step is smaller or equal to a.length, it
     * is returned therein. Otherwise, a new array is allocated and returned.
     * Array bounds are checked.
     *
     * @param a        the array into which the elements are to be stored, if it is big
     *                 enough; otherwise, a new array of is allocated for this purpose.
     * @param startPos starting position (included)
     * @param endPos   ending position (excluded)
     * @param step     step size
     *
     * @return an array containing the elements of this object or null
     */
    public abstract long[] getLongData(long[] a,
                                       long startPos,
                                       long endPos,
                                       long step);

    /**
     * If the size of the array is smaller than LargeArray.LARGEST_SUBARRAY, then this
     * method returns float data. Otherwise, it returns null.
     *
     * @return an array containing the elements of this object or null
     */
    public abstract float[] getFloatData();

    /**
     * If (endPos - startPos) / step is smaller than LargeArray.LARGEST_SUBARRAY, then
     * this method returns selected elements of this object. Otherwise, it returns
     * null. If (endPos - startPos) / step is smaller or equal to a.length, it
     * is returned therein. Otherwise, a new array is allocated and returned.
     * Array bounds are checked.
     *
     * @param a        the array into which the elements are to be stored, if it is big
     *                 enough; otherwise, a new array of is allocated for this purpose.
     * @param startPos starting position (included)
     * @param endPos   ending position (excluded)
     * @param step     step size
     *
     * @return an array containing the elements of this object or null
     */
    public abstract float[] getFloatData(float[] a,
                                         long startPos,
                                         long endPos,
                                         long step);

    /**
     * If the size of the array is smaller than LargeArray.LARGEST_SUBARRAY, then this
     * method returns double data. Otherwise, it returns null.
     *
     * @return an array containing the elements of this object or null
     */
    public abstract double[] getDoubleData();

    /**
     * If (endPos - startPos) / step is smaller than LargeArray.LARGEST_SUBARRAY, then
     * this method returns selected elements of this object. Otherwise, it returns
     * null. If (endPos - startPos) / step is smaller or equal to a.length, it
     * is returned therein. Otherwise, a new array is allocated and returned.
     * Array bounds are checked.
     *
     * @param a        the array into which the elements are to be stored, if it is big
     *                 enough; otherwise, a new array of is allocated for this purpose.
     * @param startPos starting position (included)
     * @param endPos   ending position (excluded)
     * @param step     step size
     *
     * @return an array containing the elements of this object or null
     */
    public abstract double[] getDoubleData(double[] a,
                                           long startPos,
                                           long endPos,
                                           long step);

    /**
     * Sets a value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public void set(final long i, final Object value)
    {
        if (value instanceof Boolean) {
            setBoolean(i, (Boolean) value);
        } else if (value instanceof Byte) {
            setByte(i, (Byte) value);
        } else if (value instanceof Short) {
            setShort(i, (Short) value);
        } else if (value instanceof Integer) {
            setInt(i, (Integer) value);
        } else if (value instanceof Long) {
            setLong(i, (Long) value);
        } else if (value instanceof Float) {
            setFloat(i, (Float) value);
        } else if (value instanceof Double) {
            setDouble(i, (Double) value);
        } else {
            throw new IllegalArgumentException("Unsupported type.");
        }
    }

    /**
     * Sets a value at index i. Array bounds are not checked. If isLarge()
     * returns false for a given array or the index argument is invalid, then
     * calling this method will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     *
     * @throws ClassCastException if the type of value argument is different
     *                            than the type of the array
     */
    public abstract void setToNative(long i, Object value);

    /**
     * Sets a value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void set_safe(final long i, final Object value)
    {
        if (value instanceof Boolean) {
            setBoolean_safe(i, (Boolean) value);
        } else if (value instanceof Byte) {
            setByte_safe(i, (Byte) value);
        } else if (value instanceof Short) {
            setShort_safe(i, (Short) value);
        } else if (value instanceof Integer) {
            setInt_safe(i, (Integer) value);
        } else if (value instanceof Long) {
            setLong_safe(i, (Long) value);
        } else if (value instanceof Float) {
            setFloat_safe(i, (Float) value);
        } else if (value instanceof Double) {
            setDouble_safe(i, (Double) value);
        } else {
            throw new IllegalArgumentException("Unsupported type.");
        }
    }

    /**
     * Sets a boolean value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setBoolean(long i, boolean value);

    /**
     * Sets a boolean value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setBoolean_safe(final long i, final boolean value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setBoolean(i, value);
    }

    /**
     * Sets a byte value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setByte(long i, byte value);

    /**
     * Sets a byte value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setByte_safe(final long i, final byte value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setByte(i, value);
    }

    /**
     * Sets an unsigned byte value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setUnsignedByte(long i, short value);

    /**
     * Sets an unsigned value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setUnsignedByte_safe(final long i, final byte value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setUnsignedByte(i, value);
    }

    /**
     * Sets a short value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setShort(long i, short value);

    /**
     * Sets a short value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setShort_safe(final long i, final short value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setShort(i, value);
    }

    /**
     * Sets an int value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setInt(long i, int value);

    /**
     * Sets an int value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setInt_safe(final long i, final int value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setInt(i, value);
    }

    /**
     * Sets a long value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setLong(long i, long value);

    /**
     * Sets a long value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setLong_safe(final long i, final long value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setLong(i, value);
    }

    /**
     * Sets a float value at index i. Array bounds are not checked. Calling this
     * method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setFloat(long i, float value);

    /**
     * Sets a float value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setFloat_safe(final long i, final float value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setFloat(i, value);
    }

    /**
     * Sets a double value at index i. Array bounds are not checked. Calling
     * this method with invalid index argument will cause JVM crash.
     *
     * @param i     index
     * @param value value to set
     */
    public abstract void setDouble(long i, double value);

    /**
     * Sets a double value at index i. Array bounds are checked.
     *
     * @param i     index
     * @param value value to set
     */
    public void setDouble_safe(final long i, final double value)
    {
        if (i < 0 || i >= length) {
            throw new ArrayIndexOutOfBoundsException(Long.toString(i));
        }
        setDouble(i, value);
    }

    /**
     * Returns true if the off-heap memory is used to store array elements.
     *
     * @return true if the off-heap memory is used to store array elements,
     *         false otherwise.
     */
    public boolean isLarge()
    {
        return ptr != 0;
    }

    /**
     * Returns true if the type of the array is numeric, false otherwise.
     *
     * @return true if the type of the array is numeric, false otherwise.
     */
    public boolean isNumeric()
    {
        return type.isNumericType();
    }

    /**
     * Return true if the array is constant. Constant arrays cannot be modified,
     * i.e. all setters throw an IllegalAccessError exception.
     *
     * @return true if the arrays is constant, false otherwise
     */
    public boolean isConstant()
    {
        return isConstant;
    }

    /**
     * Sets the maximal size of a 32-bit array. For arrays of the size larger
     * than index, the data is stored in the memory allocated by
     * sun.misc.Unsafe.allocateMemory().
     *
     * @param index the maximal size of a 32-bit array.
     */
    public static void setMaxSizeOf32bitArray(final int index)
    {
        if (index < 0) {
            throw new IllegalArgumentException("index cannot be negative");
        }
        maxSizeOf32bitArray = index;
    }

    /**
     * Returns the maximal size of a 32-bit array.
     *
     * @return the maximal size of a 32-bit array.
     */
    public static int getMaxSizeOf32bitArray()
    {
        return maxSizeOf32bitArray;
    }

    public LargeArray clone()
    {
        if (isConstant) {
            return LargeArrayUtils.createConstant(type, length, get(0));
        } else {
            LargeArray v = LargeArrayUtils.create(type, length, false);
            LargeArrayUtils.arraycopy(this, 0, v, 0, length);
            return v;
        }
    }

    /**
     * Indicates whether some other object is "equal to" this one.
     * The {@code equals} method implements "deep comparison": all properties are compared and all corresponding elements in both arrays are compared.
     *
     * @param o the reference object with which to compare.
     *
     * @return {@code true} if this object is the same as the o
     *         argument; {@code false} otherwise.
     *
     * @see #hashCode()
     * @see java.util.HashMap
     */
    @Override
    public abstract boolean equals(Object o);

    @Override
    public int hashCode()
    {
        return hashCode(1f);
    }

    /**
     * Generates an approximate hash code using a quality argument. For quality equal to 1 this method is equivalent to hashCode().
     *
     * @param quality a number between 0 and 1.
     *
     * @return an approximate hash code
     */
    public int hashCode(float quality)
    {
        if (quality < 0 || quality > 1) {
            throw new IllegalArgumentException("The quality argument should be between 0 and 1");
        }
        int fprint = 7;
        fprint = 29 * fprint + (this.type != null ? this.type.hashCode() : 0);
        fprint = 29 * fprint + (int) (this.length ^ (this.length >>> 32));
        fprint = 29 * fprint + (this.isConstant ? 1 : 0);
        fprint = 29 * fprint + (this.parent != null ? this.parent.hashCode() : 0);
        return fprint;
    }

    /**
     * Memory deallocator.
     */
    protected static class Deallocator implements Runnable
    {

        private long ptr;
        private final long length;
        private final long sizeof;

        public Deallocator(final long ptr,
                           final long length,
                           final long sizeof)
        {
            this.ptr = ptr;
            this.length = length;
            this.sizeof = sizeof;
        }

        @Override
        public void run()
        {
            if (ptr != 0) {
                LargeArrayUtils.UNSAFE.freeMemory(ptr);
                ptr = 0;
                MemoryCounter.decreaseCounter(length * sizeof);
            }
        }
    }

    /**
     * Initializes allocated native memory.
     *
     * @param length      the length of native memory block
     * @param initValue initialization value
     */
    protected final void initializeNativeMemory(final long length, final Number initValue)
    {
        final long sizeof = type.sizeOf();
        if (ptr != 0) {
            final int nthreads = (int) FastMath.min(length, ConcurrencyUtils.getNumberOfThreads());
            if (nthreads <= 2 || length < ConcurrencyUtils.getConcurrentThreshold()) {
                switch (type) {
                    case LOGIC:
                    case BYTE:
                    case UNSIGNED_BYTE:
                    case STRING:
                    case OBJECT: {
                        byte value = initValue.byteValue();
                        for (long k = 0; k < length; k++) {
                            LargeArrayUtils.UNSAFE.putByte(ptr + sizeof * k, value);
                        }
                        break;
                    }
                    case SHORT: {
                        short value = initValue.shortValue();
                        for (long k = 0; k < length; k++) {
                            LargeArrayUtils.UNSAFE.putShort(ptr + sizeof * k, value);
                        }
                        break;
                    }
                    case INT: {
                        int value = initValue.intValue();
                        for (long k = 0; k < length; k++) {
                            LargeArrayUtils.UNSAFE.putInt(ptr + sizeof * k, value);
                        }
                        break;
                    }
                    case LONG: {
                        long value = initValue.longValue();
                        for (long k = 0; k < length; k++) {
                            LargeArrayUtils.UNSAFE.putLong(ptr + sizeof * k, value);
                        }
                        break;
                    }
                    case FLOAT: {
                        float value = initValue.floatValue();
                        for (long k = 0; k < length; k++) {
                            LargeArrayUtils.UNSAFE.putFloat(ptr + sizeof * k, value);
                        }
                        break;
                    }
                    case DOUBLE: {
                        double value = initValue.doubleValue();
                        for (long k = 0; k < length; k++) {
                            LargeArrayUtils.UNSAFE.putDouble(ptr + sizeof * k, value);
                        }
                        break;
                    }
                    default:
                        throw new IllegalArgumentException("Invalid array type.");
                }
            } else {
                final long k = length / nthreads;
                final Future[] threads = new Future[nthreads];
                for (int j = 0; j < nthreads; j++) {
                    final long firstIdx = j * k;
                    final long lastIdx = (j == nthreads - 1) ? length : firstIdx + k;
                    threads[j] = ConcurrencyUtils.submit(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            switch (type) {
                                case LOGIC:
                                case BYTE:
                                case UNSIGNED_BYTE:
                                case STRING:
                                case OBJECT: {
                                    byte value = initValue.byteValue();
                                    for (long k = firstIdx; k < lastIdx; k++) {
                                        LargeArrayUtils.UNSAFE.putByte(ptr + sizeof * k, value);
                                    }
                                    break;
                                }
                                case SHORT: {
                                    short value = initValue.shortValue();
                                    for (long k = firstIdx; k < lastIdx; k++) {
                                        LargeArrayUtils.UNSAFE.putShort(ptr + sizeof * k, value);
                                    }
                                    break;
                                }
                                case INT: {
                                    int value = initValue.intValue();
                                    for (long k = firstIdx; k < lastIdx; k++) {
                                        LargeArrayUtils.UNSAFE.putInt(ptr + sizeof * k, value);
                                    }
                                    break;
                                }
                                case LONG: {
                                    long value = initValue.longValue();
                                    for (long k = 0; k < lastIdx; k++) {
                                        LargeArrayUtils.UNSAFE.putLong(ptr + sizeof * k, value);
                                    }
                                    break;
                                }
                                case FLOAT: {
                                    float value = initValue.floatValue();
                                    for (long k = firstIdx; k < lastIdx; k++) {
                                        LargeArrayUtils.UNSAFE.putFloat(ptr + sizeof * k, value);
                                    }
                                    break;
                                }
                                case DOUBLE: {
                                    double value = initValue.doubleValue();
                                    for (long k = firstIdx; k < lastIdx; k++) {
                                        LargeArrayUtils.UNSAFE.putDouble(ptr + sizeof * k, value);
                                    }
                                    break;
                                }
                                default:
                                    throw new IllegalArgumentException("Invalid array type.");
                            }
                        }
                    });
                }
                try {
                    ConcurrencyUtils.waitForCompletion(threads);
                } catch (InterruptedException | ExecutionException ex) {
                    throw new IllegalStateException(ex);
                }
            }
        }
    }

    private void readObjectNoData() throws ObjectStreamException
    {
        throw new InvalidObjectException("Stream data required");
    }
}

 

Ads help maintain this website.