/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.arrays;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.IntPredicate;
import net.ranides.assira.collection.IntComparator;
import net.ranides.assira.collection.Swapper;
import net.ranides.assira.generic.CompareUtils;
import net.ranides.assira.reflection.util.ClassTraits;
import net.ranides.assira.reflection.util.ClassUtils;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public abstract class NativeArray implements Swapper, Comparable<NativeArray>, Cloneable, Serializable {
    
    private static final long serialVersionUID = 1L;
    
    public abstract NativeArray allocate(int size);
    
    public abstract Object array();
    
    @SuppressWarnings("unchecked")
    public <Auto> Auto $array() {
        return (Auto)array();
    }
    
    public abstract int size();
    
    public Class<?> component() {
        return array().getClass().getComponentType();
    }
    
    public abstract void set(int index, Object value);
    
    public abstract void set(int begin, int end, Object value);
    
    public abstract Object get(int index);
    
    public abstract NativeMove move(NativeArray source);
    
    public abstract void move(int index, int source);
    
    public Object replace(int index, Object value) {
        Object prv = get(index);
        set(index, value);
        return prv;
    }
    
    @Override
    @SuppressWarnings({
        "PMD",
        "CloneDoesntCallSuperClone", 
        "CloneDeclaresCloneNotSupported", 
        "SuspiciousSystemArraycopy"
    })
    public NativeArray clone() {
        NativeArray ret = allocate(size());
        System.arraycopy(array(), 0, ret.array(), 0, size());
        return ret;
    }
    
    public NativeArray box() {
        if(ClassTraits.isBoxed(component())) {
            return this;
        }
        NativeArray ret = allocate(ClassUtils.box(component()), size());
        for(int i=0, n=size(); i<n; i++) {
            ret.set(i, get(i));
        }
        return ret;
    }
    
    public NativeArray unbox() {
        if(!ClassTraits.isBoxed(component())) {
            return this;
        }
        NativeArray ret = allocate(ClassUtils.unbox(component()), size());
        for(int i=0, n=size(); i<n; i++) {
            ret.set(i, get(i));
        }
        return ret;
    }
    
    public abstract IntPredicate cmp(Object value);
    
    public abstract NativeComparator lcmp(Object value);
    
    @SuppressWarnings("unchecked")
    public NativeComparator lcmp(Object value, Comparator cmp) {
        return index -> cmp.compare(value, get(index));
    }
    
    public abstract NativeComparator rcmp(Object value);

    @SuppressWarnings("unchecked")    
    public NativeComparator rcmp(Object value, Comparator cmp) {
        return index -> cmp.compare(get(index), value);
    }
    
    public abstract IntComparator comparator(NativeArray values);
    
    public abstract IntComparator comparator(NativeArray values, Comparator cmp);
    
    public abstract IntComparator comparator();
    
    @SuppressWarnings("unchecked")
    public IntComparator comparator(Comparator cmp) {
        return (a,b) -> cmp.compare(get(a), get(b));
    }
    
    @Override
    public int hashCode() {
        return NativeArray.this.hashCode(0, size());
    }
    
    public abstract int hashCode(int index);
    
    public int hashCode(int begin, int end) {
        int result = 1;
        for(int i=begin; i<end; i++) {
            result = 31 * result + hashCode(i);
        }
        return result;
    }
    
    @Override
    public boolean equals(Object object) {
        if(!(object instanceof NativeArray)) {
            return false;
        }
        NativeArray na = (NativeArray)object;
        return na.size()==size() && equals(na, 0, 0, size());
    }
    
    public boolean equals(NativeArray object, int src, int dst, int size) {
        return 0 == compareTo(object, src, dst, size);
    }
        
    @Override
    public int compareTo(NativeArray object) {
        int a = size();
        int b = object.size();
        int c = compareTo(object, 0, 0, Math.min(a,b));
        return 0 != c ? c : Integer.compare(a, b);
    }
    
    public int compareTo(NativeArray object, Comparator comp) {
        int a = size();
        int b = object.size();
        int c = compareTo(object, 0, 0, Math.min(a,b), comp);
        return 0 != c ? c : Integer.compare(a, b);
    }
    
    public int compareTo(NativeArray object, int src, int dst, int size) {
        if(size == 0) {
            return 0;
        }
        if( ((NativeArray)object).array() == array()) {
            return 0;
        }
        IntComparator na = NativeArray.this.comparator(object);
        for(int i=0; i<size; i++) {
            int cmp = na.compare(src++, dst++);
            if(0 != cmp) {
                return cmp;
            }
        }
        return 0;
    }
    
    public int compareTo(NativeArray object, int src, int dst, int size, Comparator comp) {
        if(size == 0) {
            return 0;
        }
        if( ((NativeArray)object).array() == array()) {
            return 0;
        }
        IntComparator na = NativeArray.this.comparator(object, comp);
        for(int i=0; i<size; i++) {
            int cmp = na.compare(src++, dst++);
            if(0 != cmp) {
                return cmp;
            }
        }
        return 0;
    }
    
    
    public interface NativeComparator {

        int compare(int index);
        
    }
    
    public interface NativeMove {
        
        void move(int target, int source);
        
    }
    
    @SuppressWarnings({
        "PMD.NPathComplexity",
        "PMD.CyclomaticComplexity"
    })
    public static NativeArray allocate(Class<?> component, int size) {
        if(!component.isPrimitive()) {
            return new ObjectArray(component, size);
        }
        if(byte.class.equals(component)) {
            return new ByteArray(size);
        }
        if(short.class.equals(component)) {
            return new ShortArray(size);
        }
        if(int.class.equals(component)) {
            return new IntegerArray(size);
        }
        if(long.class.equals(component)) {
            return new LongArray(size);
        }
        if(float.class.equals(component)) {
            return new FloatArray(size);
        }
        if(double.class.equals(component)) {
            return new DoubleArray(size);
        }
        if(char.class.equals(component)) {
            return new CharacterArray(size);
        }
        if(boolean.class.equals(component)) {
            return new BooleanArray(size);
        }
        throw new UnsupportedOperationException("Not supported type: " + component.getName());
    }
    
	@SuppressWarnings({
        "PMD.NPathComplexity",
        "PMD.CyclomaticComplexity"
    })
	public static NativeArray wrap(Object array) {
        Class<?> c = array.getClass().getComponentType();
        if(null == c) {
            throw new IllegalArgumentException(array.getClass().getName() + " " + array + " is not an array.");
        }
        if(!c.isPrimitive()) {
            return new ObjectArray(array);
        }
        if(byte.class.equals(c)) {
            return new ByteArray(array);
        }
        if(short.class.equals(c)) {
            return new ShortArray(array);
        }
        if(int.class.equals(c)) {
            return new IntegerArray(array);
        }
        if(long.class.equals(c)) {
            return new LongArray(array);
        }
        if(float.class.equals(c)) {
            return new FloatArray(array);
        }
        if(double.class.equals(c)) {
            return new DoubleArray(array);
        }
        if(char.class.equals(c)) {
            return new CharacterArray(array);
        }
        if(boolean.class.equals(c)) {
            return new BooleanArray(array);
        }
        throw new UnsupportedOperationException("Not supported type: " + c.getName());
    }
    
    public static NativeArray wrap(Object[] array) {
		return new ObjectArray(array);
    }
    
    public static NativeArray wrap(int[] array) {
		return new IntegerArray(array);
    }
    
    public static NativeArray wrap(char[] array) {
		return new CharacterArray(array);
    }
    
    public static NativeArray wrap(byte[] array) {
		return new ByteArray(array);
    }
    
    public static NativeArray wrap(boolean[] array) {
		return new BooleanArray(array);
    }
    
    static final class ByteArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final byte[] array;

        public ByteArray(Object array) {
            this.array = (byte[])array;
        }
        
        public ByteArray(int size) {
            this.array = new byte[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new ByteArray(size);
        }

        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Byte)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            byte ivalue = (Byte)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            byte[] s = source.$array();
            return (a,b) -> array[a]=s[b];
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            byte ivalue = (Byte)value;
            return index -> ivalue == array[index];
        }

        @Override
        public NativeComparator lcmp(Object value) {
            byte ivalue = (Byte)value;
            return index -> Byte.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            byte ivalue = (Byte)value;
            return index -> Byte.compare(array[index], ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b) -> Byte.compare(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            byte temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            ByteArray that = (ByteArray)values;
            return (a,b) -> Byte.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            ByteArray that = (ByteArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }

        @Override
        public int hashCode(int index) {
            return array[index];
        }

        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class ShortArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final short[] array;

        public ShortArray(Object array) {
            this.array = (short[])array;
        }
        
        public ShortArray(int size) {
            this.array = new short[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new ShortArray(size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Short)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            short ivalue = (Short)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            short[] s = source.$array();
            return (a,b) -> array[a]=s[b];
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            short ivalue = (Short)value;
            return index -> ivalue == array[index];
        }

        @Override
        public NativeComparator lcmp(Object value) {
            short ivalue = (Short)value;
            return index -> Short.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            short ivalue = (Short)value;
            return index -> Short.compare(array[index],ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b) -> Short.compare(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            short temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            ShortArray that = (ShortArray)values;
            return (a,b) -> Short.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            ShortArray that = (ShortArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }
        
        @Override
        public int hashCode(int index) {
            return array[index];
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class IntegerArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final int[] array;

        public IntegerArray(Object array) {
            this.array = (int[])array;
        }
        
        public IntegerArray(int size) {
            this.array = new int[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new IntegerArray(size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Integer)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            int ivalue = (Integer)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            int[] s = source.$array();
            return (a,b) -> array[a]=s[b];
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            int ivalue = (Integer)value;
            return index -> ivalue == array[index];
        }
        
        @SuppressWarnings("unchecked")
        @Override
        public NativeComparator lcmp(Object value, Comparator cmp) {
            if(cmp instanceof IntComparator) {
                IntComparator icmp = (IntComparator)cmp;
                int ivalue = (Integer)value;
                return index -> icmp.compare(ivalue, array[index]);
            } else {
                return index -> cmp.compare(value, array[index]);
            }
        }
        
        @SuppressWarnings("unchecked")
        @Override
        public NativeComparator rcmp(Object value, Comparator cmp) {
            if(cmp instanceof IntComparator) {
                IntComparator icmp = (IntComparator)cmp;
                int ivalue = (Integer)value;
                return index -> icmp.compare(array[index],ivalue);
            } else {
                return index -> cmp.compare(array[index],value);
            }
        }

        @Override
        public NativeComparator lcmp(Object value) {
            int ivalue = (Integer)value;
            return index -> Integer.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            int ivalue = (Integer)value;
            return index -> Integer.compare(array[index],ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b) -> Integer.compare(array[a], array[b]);
        }
        
        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(Comparator cmp) {
            if(cmp instanceof IntComparator) {
                IntComparator icmp = (IntComparator)cmp;
                return (a,b) -> icmp.compare(array[a], array[b]);
            } else {
                return (a,b) -> cmp.compare(array[a], array[b]);
            }
        }

        @Override
        public void swap(int a, int b) {
            int temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            IntegerArray that = (IntegerArray)values;
            return (a,b) -> Integer.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            IntegerArray that = (IntegerArray)values;
            if(cmp instanceof IntComparator) {
                IntComparator icmp = (IntComparator)cmp;
                return (a,b) -> icmp.compare(this.array[a], that.array[b]);
            } else {
                return (a,b) -> cmp.compare(this.array[a], that.array[b]);
            }
        }
        
        @Override
        public int hashCode(int index) {
            return array[index];
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class LongArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final long[] array;

        public LongArray(Object array) {
            this.array = (long[])array;
        }
        
        public LongArray(int size) {
            this.array = new long[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new LongArray(size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Long)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            long ivalue = (Long)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            long[] s = source.$array();
            return (a,b) -> array[a]=s[b];
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            long ivalue = (Long)value;
            return index -> ivalue == array[index];
        }

        @Override
        public NativeComparator lcmp(Object value) {
            long ivalue = (Long)value;
            return index -> Long.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            long ivalue = (Long)value;
            return index -> Long.compare(array[index],ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b) -> Long.compare(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            long temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            LongArray that = (LongArray)values;
            return (a,b) -> Long.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            LongArray that = (LongArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }
        
        @Override
        public int hashCode(int index) {
            return (int)(array[index] ^ (array[index] >>> 32));
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class FloatArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final float[] array;

        public FloatArray(Object array) {
            this.array = (float[])array;
        }
        
        public FloatArray(int size) {
            this.array = new float[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new FloatArray(size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Float)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            float ivalue = (Float)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            float[] s = source.$array();
            return (a,b)->{ array[a]=s[b]; };
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            float ivalue = (Float)value;
            return index -> ivalue == array[index];
        }

        @Override
        public NativeComparator lcmp(Object value) {
            float ivalue = (Float)value;
            return index -> Float.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            float ivalue = (Float)value;
            return index -> Float.compare(array[index],ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b)-> Float.compare(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            float temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            FloatArray that = (FloatArray)values;
            return (a,b) -> Float.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            FloatArray that = (FloatArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }
        
        @Override
        public int hashCode(int index) {
            return Float.floatToIntBits(array[index]);
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class DoubleArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final double[] array;

        public DoubleArray(Object array) {
            this.array = (double[])array;
        }
        
        public DoubleArray(int size) {
            this.array = new double[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new DoubleArray(size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Double)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            double ivalue = (Double)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            double[] s = source.$array();
            return (a,b)->{ array[a]=s[b]; };
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            double ivalue = (Double)value;
            return index -> ivalue == array[index];
        }

        @Override
        public NativeComparator lcmp(Object value) {
            double ivalue = (Double)value;
            return index -> Double.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            double ivalue = (Double)value;
            return index -> Double.compare(array[index],ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b)-> Double.compare(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            double temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            DoubleArray that = (DoubleArray)values;
            return (a,b) -> Double.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            DoubleArray that = (DoubleArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }
        
        @Override
        public int hashCode(int index) {
            long bits = Double.doubleToLongBits(array[index]);
            return (int)(bits ^ (bits >>> 32));
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class CharacterArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final char[] array;

        public CharacterArray(Object array) {
            this.array = (char[])array;
        }
        
        public CharacterArray(int size) {
            this.array = new char[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new CharacterArray(size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Character)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            char ivalue = (Character)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            char[] s = source.$array();
            return (a,b)->{ array[a]=s[b]; };
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            char ivalue = (Character)value;
            return index -> ivalue == array[index];
        }

        @Override
        public NativeComparator lcmp(Object value) {
            char ivalue = (Character)value;
            return index -> Character.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            char ivalue = (Character)value;
            return index -> Character.compare(array[index],ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b) -> Character.compare(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            char temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            CharacterArray that = (CharacterArray)values;
            return (a,b) -> Character.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            CharacterArray that = (CharacterArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }
        
        @Override
        public int hashCode(int index) {
            return array[index];
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class BooleanArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final boolean[] array;

        public BooleanArray(Object array) {
            this.array = (boolean[])array;
        }
        
        public BooleanArray(int size) {
            this.array = new boolean[size];
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new BooleanArray(size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = (Boolean)value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            boolean ivalue = (Boolean)value;
            for(int i=begin, n=end; i<n; i++) {
                array[i] = ivalue;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            boolean[] s = source.$array();
            return (a,b) -> array[a]=s[b];
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            boolean ivalue = (Boolean)value;
            return index -> ivalue == array[index];
        }

        @Override
        public NativeComparator lcmp(Object value) {
            boolean ivalue = (Boolean)value;
            return index -> Boolean.compare(ivalue, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            boolean ivalue = (Boolean)value;
            return index -> Boolean.compare(array[index],ivalue);
        }

        @Override
        public IntComparator comparator() {
            return (a,b)-> Boolean.compare(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            boolean temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            BooleanArray that = (BooleanArray)values;
            return (a,b) -> Boolean.compare(this.array[a], that.array[b]);
        }

        @SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            BooleanArray that = (BooleanArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }
        
        @Override
        public int hashCode(int index) {
            return array[index] ? 1231 : 1237;
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
    static final class ObjectArray extends NativeArray {
        
        private static final long serialVersionUID = 1L;
        
        private final Object[] array;

        public ObjectArray(Object array) {
            this.array = (Object[])array;
        }
        
        public ObjectArray(Class<?> c, int size) {
            this.array = (Object[])Array.newInstance(c, size);
        }
        
        @Override
        public NativeArray allocate(int size) {
            Class<?> c = array.getClass().getComponentType();
            return new ObjectArray(c, size);
        }
        
        @Override
        public Object array() {
            return array;
        }
        
        @Override
        public int size() {
            return array.length;
        }

        @Override
        public void set(int index, Object value) {
            array[index] = value;
        }

        @Override
        public void set(int begin, int end, Object value) {
            for(int i=begin, n=end; i<n; i++) {
                array[i] = value;
            }
        }

        @Override
        public Object get(int index) {
            return array[index];
        }
        
        @Override
        public NativeMove move(NativeArray source) {
            Object[] s = source.$array();
            return (a,b)->{ array[a]=s[b]; };
        }
        
        @Override
        public void move(int a, int b) {
            array[a] = array[b];
        }
        
        @Override
        public IntPredicate cmp(Object value) {
            return index -> CompareUtils.equals(value, array[index]);
        }

        @Override
        public NativeComparator lcmp(Object value) {
            return index -> CompareUtils.cmp(value, array[index]);
        }
        
        @Override
        public NativeComparator rcmp(Object value) {
            return index -> CompareUtils.cmp(array[index],value);
        }

        @Override
        public IntComparator comparator() {
            return (a,b) -> CompareUtils.cmp(array[a], array[b]);
        }

        @Override
        public void swap(int a, int b) {
            Object temp = array[a];
            array[a] = array[b];
            array[b] = temp;
        }
        
        @Override
        public IntComparator comparator(NativeArray values) {
            ObjectArray that = (ObjectArray)values;
            return (a,b) -> CompareUtils.cmp(this.array[a], that.array[b]);
        }

		@SuppressWarnings("unchecked")
        @Override
        public IntComparator comparator(NativeArray values, Comparator cmp) {
            ObjectArray that = (ObjectArray)values;
            return (a,b) -> cmp.compare(this.array[a], that.array[b]);
        }
        
        @Override
        public int hashCode(int index) {
            return null == array[index] ? 0 : array[index].hashCode();
        }
        
        @Override
        public String toString() {
            return Arrays.toString(array);
        }
        
    }
    
}
