package rivet.core.labels;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import com.carrotsearch.hppc.IntDoubleHashMap;
import com.carrotsearch.hppc.predicates.IntDoublePredicate;
import com.carrotsearch.hppc.procedures.DoubleProcedure;
import com.carrotsearch.hppc.procedures.IntDoubleProcedure;
import com.carrotsearch.hppc.procedures.IntProcedure;

import rivet.core.vectorpermutations.Permutations;

public class HPPCRIV extends IntDoubleHashMap implements RIV {

    private final int size;

    private HPPCRIV(final int size) {
        super();
        this.size = size;
    }

    public HPPCRIV(final int size, final int[] indices, final double[] values) {
        this(size);
        for (int i = 0; i < indices.length; i++)
            put(indices[i], values[i]);
    }

    public HPPCRIV(final HPPCRIV riv) {
        super(riv);
        size = riv.size;
    }

    public HPPCRIV(final int size, final VectorElement[] points) {
        this(size);
        for (final VectorElement point : points)
            put(point.index(), point.value());
    }

    @Override
    public boolean contains(final int index) {
        return super.containsKey(index);
    }

    @Override
    public HPPCRIV copy() {
        return new HPPCRIV(this);
    }

    @Override
    public int count() {
        return super.size();
    }

    @Override
    public HPPCRIV destructiveAdd(final RIV other) {
        other.keyStream()
             .forEach(i -> addTo(i, other.get(i)));
        return this;
    }

    @Override
    public HPPCRIV destructiveAdd(final RIV...rivs) {
        for (final RIV riv : rivs)
            destructiveAdd(riv);
        return this;
    }

    @Override
    public HPPCRIV destructiveSub(final RIV other) {
        other.keyStream()
             .forEach(i -> addTo(i, -other.get(i)));
        return this;
    }

    @Override
    public HPPCRIV destructiveSub(final RIV...rivs) {
        for (final RIV riv : rivs)
            destructiveSub(riv);
        return this;
    }

    @Override
    public HPPCRIV destructiveDiv(final double scalar) {
        forEach((IntDoubleProcedure) (k, v) -> indexReplace(k, v / scalar));
        return this;
    }

    @Override
    public boolean equals(final RIV other) {
        if (other.getClass()
                 .equals(HPPCRIV.class))
            return equals(other);
        else
            return size == other.size()
                   && Arrays.deepEquals(points(), other.points());
    }

    @Override
    public double get(final int index) throws IndexOutOfBoundsException {
        if (size <= index || index < 0)
            throw new IndexOutOfBoundsException(
                    index + " is outside the bounds of this RIV");
        return getOrDefault(index, 0);
    }

    @Override
    public IntStream keyStream() {
        final IntStream.Builder sb = IntStream.builder();
        keys().forEach((IntProcedure) sb::accept);
        return sb.build();
    }

    @Override
    public HPPCRIV destructiveMult(final double scalar) {
        forEach((IntDoubleProcedure) (k, v) -> indexReplace(k, v * scalar));
        return this;
    }

    private static void permute(final VectorElement[] points,
            final int[] permutation) {
        for (final VectorElement point : points)
            point.destructiveSet(permutation[point.index()]);
    }

    @Override
    public HPPCRIV permute(final Permutations permutations, final int times) {
        if (times == 0)
            return this;
        final int[] perm = (times > 0)
                ? permutations.left
                : permutations.right;
        final int t = Math.abs(times);
        final VectorElement[] points = points();
        for (int i = 0; i < t; i++)
            permute(points, perm);
        Arrays.sort(points);
        return new HPPCRIV(size, points);
    }

    @Override
    public VectorElement[] points() {
        final VectorElement[] points = new VectorElement[count()];
        final AtomicInteger c = new AtomicInteger();
        forEach((IntDoubleProcedure) (k, v) -> points[c.getAndIncrement()] =
                VectorElement.elt(k, v));
        Arrays.sort(points);
        return points;
    }

    @Override
    public Stream<VectorElement> pointStream() {
        final Stream.Builder<VectorElement> sb = Stream.builder();
        forEach((IntDoubleProcedure) (k,
                v) -> sb.accept(VectorElement.elt(k, v)));
        return sb.build();
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public DoubleStream valStream() {
        final DoubleStream.Builder sb = DoubleStream.builder();
        values().forEach((DoubleProcedure) sb::accept);
        return sb.build();
    }

    @Override
    public HPPCRIV destructiveRemoveZeros() {
        removeAll((IntDoublePredicate) (k, v) -> v == 0);
        return this;
    }

    @Override
    public int hashCode() {
        final AtomicInteger c = new AtomicInteger();
        keys().forEach(c::addAndGet);
        return c.get();
    }

    @Override
    public int[] keyArr() {
        return keys().toArray();
    }

    @Override
    public double[] valArr() {
        return values().toArray();
    }

}

@@ -1525,32 +1525,51 @@
 other) %7B%0A       
+ for (final int i :
  other.keyStream
@@ -1554,39 +1554,37 @@
 nt i : other.key
-Stream(
+Arr()
 )%0A             .
@@ -1576,39 +1576,24 @@
 %0A           
-  .forEach(i -%3E
  addTo(i, ot
@@ -1595,33 +1595,32 @@
 i, other.get(i))
-)
 ;%0A        return
@@ -1871,16 +1871,35 @@
 %0A       
+ for (final int i :
  other.k
@@ -1900,23 +1900,21 @@
 ther.key
-Stream(
+Arr()
 )%0A      
@@ -1922,23 +1922,8 @@
     
-  .forEach(i -%3E
  add
@@ -1942,17 +1942,16 @@
 .get(i))
-)
 ;%0A      
@@ -2953,133 +2953,37 @@
     
-final IntStream.Builder sb = IntStream.builder();%0A        keys().forEach((IntProcedure) sb::accept);%0A        return sb.build(
+return Arrays.stream(keyArr()
 );%0A 
@@ -4301,463 +4301,202 @@
     
-final Stream.Builder%3CVectorElement%3E sb = Stream.builder();%0A        forEach((IntDoubleProcedure) (k,%0A                v) -%3E sb.accept(VectorElement.elt(k, v)));%0A        return sb.build();%0A    %7D%0A%0A    @Override%0A    public int size() %7B%0A        return size;%0A    %7D%0A%0A    @Override%0A    public DoubleStream valStream() %7B%0A        final DoubleStream.Builder sb = DoubleStream.builder();%0A        values().forEach((DoubleProcedure) sb::accept);%0A        return sb.build(
+return Arrays.stream(points());%0A    %7D%0A%0A    @Override%0A    public int size() %7B%0A        return size;%0A    %7D%0A%0A    @Override%0A    public DoubleStream valStream() %7B%0A        return Arrays.stream(valArr()
 );%0A 
@@ -4765,16 +4765,31 @@
 forEach(
+(IntProcedure) 
 c::addAn

fix ambiguity in KeysContainer.forEach() call