public IndexProducer indices()

in src/main/java/org/apache/commons/collections4/bloomfilter/EnhancedDoubleHasher.java [133:210]


    public IndexProducer indices(final Shape shape) {
        Objects.requireNonNull(shape, "shape");

        return new IndexProducer() {

            @Override
            public boolean forEachIndex(final IntPredicate consumer) {
                Objects.requireNonNull(consumer, "consumer");
                final int bits = shape.getNumberOfBits();
                // Enhanced double hashing:
                // hash[i] = ( h1(x) + i*h2(x) + (i*i*i - i)/6 ) mod bits
                // See: https://en.wikipedia.org/wiki/Double_hashing#Enhanced_double_hashing
                //
                // Essentially this is computing a wrapped modulus from a start point and an
                // increment and an additional term as a tetrahedral number.
                // You only need two modulus operations before the loop. Within the loop
                // the modulus is handled using the sign bit to detect wrapping to ensure:
                // 0 <= index < bits
                // 0 <= inc < bits
                // The final hash is:
                // hash[i] = ( h1(x) - i*h2(x) - (i*i*i - i)/6 ) wrapped in [0, bits)

                int index = BitMap.mod(initial, bits);
                int inc = BitMap.mod(increment, bits);

                final int k = shape.getNumberOfHashFunctions();
                if (k > bits) {
                    for (int j = k; j > 0;) {
                        // handle k > bits
                        final int block = Math.min(j, bits);
                        j -= block;
                        for (int i = 0; i < block; i++) {
                            if (!consumer.test(index)) {
                                return false;
                            }
                            // Update index and handle wrapping
                            index -= inc;
                            index = index < 0 ? index + bits : index;

                            // Incorporate the counter into the increment to create a
                            // tetrahedral number additional term, and handle wrapping.
                            inc -= i;
                            inc = inc < 0 ? inc + bits : inc;
                        }
                    }
                } else {
                    for (int i = 0; i < k; i++) {
                        if (!consumer.test(index)) {
                            return false;
                        }
                        // Update index and handle wrapping
                        index -= inc;
                        index = index < 0 ? index + bits : index;

                        // Incorporate the counter into the increment to create a
                        // tetrahedral number additional term, and handle wrapping.
                        inc -= i;
                        inc = inc < 0 ? inc + bits : inc;
                    }
                }
                return true;
            }

            @Override
            public int[] asIndexArray() {
                final int[] result = new int[shape.getNumberOfHashFunctions()];
                final int[] idx = new int[1];

                // This method needs to return duplicate indices

                forEachIndex(i -> {
                    result[idx[0]++] = i;
                    return true;
                });
                return result;
            }
        };
    }