package de.jjedele.sbf;

import java.util.Random;

/**
 * Created by jeff on 14/05/16.
 */
public class StableBloomFilter<E> implements BloomFilter<E> {

    private HashProvider<Object> hashProvider = new DefaultHashProvider();

    private final byte[] cells;
    private final int numberOfCells;
    private final int numberOfHashes;
    private final float unlearnRate;

    public StableBloomFilter(int numberOfCells,
                             int numberOfHashes,
                             float unlearnRate) {
        this.numberOfCells = numberOfCells;
        this.numberOfHashes = numberOfHashes;
        this.cells = new byte[numberOfCells];
        this.unlearnRate = unlearnRate;
    }

    public void add(E element) {
        int[] indices = indices(element);

        for (int i = 0; i < numberOfHashes; i++) {
            cells[indices[i]] += 1;
        }

        unlearn();
    }

    public boolean mayContain(E element) {
        boolean mayContain = true;

        int[] indices = indices(element);
        for (int i = 0; i < numberOfHashes; i++) {
            mayContain &= cells[indices[i]] > 0;
        }

        return mayContain;
    }

    private void unlearn() {
        int unlearnedCells = Math.round(numberOfCells * unlearnRate);
        for (int i = 0; i < unlearnedCells; i++) {
            int index = new Random().nextInt(numberOfCells);
            this.cells[index] -= 1;
        }
    }

    private int[] indices(E element) {
        int[] hashes = new int[numberOfHashes];

        long h1 = hashProvider.hash1(element);
        long h2 = hashProvider.hash2(element);
        for (int i = 0; i < numberOfHashes; i++) {
            hashes[i] = Math.abs((int) ((h1 + i * h2) % numberOfCells));
        }

        return hashes;
    }

}

@@ -837,22 +837,26 @@
         
-cells%5B
+increment(
 indices%5B
@@ -861,14 +861,9 @@
 s%5Bi%5D
-%5D += 1
+)
 ;%0A  
@@ -1095,22 +1095,22 @@
 tain &= 
-cells%5B
+isSet(
 indices%5B
@@ -1115,13 +1115,9 @@
 s%5Bi%5D
-%5D %3E 0
+)
 ;%0A  
@@ -1386,30 +1386,24 @@
     
-this.cells%5Bindex%5D -= 1
+decrement(index)
 ;%0A  
@@ -1726,32 +1726,32 @@
 s));%0A        %7D%0A%0A
-
         return h
@@ -1764,10 +1764,326 @@
 %0A    %7D%0A%0A
+    private void decrement(int idx) %7B%0A        if (cells%5Bidx%5D %3E 0) %7B%0A            cells%5Bidx%5D -= 1;%0A        %7D%0A    %7D%0A%0A    private void increment(int idx) %7B%0A        if (cells%5Bidx%5D %3C Byte.MAX_VALUE) %7B%0A            cells%5Bidx%5D += 1;%0A        %7D%0A    %7D%0A%0A    private boolean isSet(int idx) %7B%0A        return cells%5Bidx%5D %3E 0;%0A    %7D%0A%0A
 %7D%0A

Check bounds when changing cells.