-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Ketan Verma <ketan9495@gmail.com>
- Loading branch information
Showing
8 changed files
with
233 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 0 additions & 137 deletions
137
libs/common/src/test/java/org/opensearch/common/hash/HashFunctionTestCase.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
test/framework/src/main/java/org/opensearch/common/hash/AvalancheStats.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
*/ | ||
|
||
package org.opensearch.common.hash; | ||
|
||
import java.util.Locale; | ||
|
||
/** | ||
* Represents the avalanche statistics of a hash function. | ||
*/ | ||
public class AvalancheStats { | ||
private final int inputBits; | ||
private final int outputBits; | ||
private final double bias; | ||
private final double sumOfSquaredErrors; | ||
|
||
public AvalancheStats(int[][] flips, int iterations) { | ||
this.inputBits = flips.length; | ||
this.outputBits = flips[0].length; | ||
double sumOfBiases = 0; | ||
double sumOfSquaredErrors = 0; | ||
|
||
for (int i = 0; i < inputBits; i++) { | ||
for (int o = 0; o < outputBits; o++) { | ||
sumOfSquaredErrors += Math.pow(0.5 - ((double) flips[i][o] / iterations), 2); | ||
sumOfBiases += 2 * ((double) flips[i][o] / iterations) - 1; | ||
} | ||
} | ||
|
||
this.bias = Math.abs(sumOfBiases / (inputBits * outputBits)); | ||
this.sumOfSquaredErrors = sumOfSquaredErrors; | ||
} | ||
|
||
public double bias() { | ||
return bias; | ||
} | ||
|
||
public double diffusion() { | ||
return 1 - bias; | ||
} | ||
|
||
public double sumOfSquaredErrors() { | ||
return sumOfSquaredErrors; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.format( | ||
Locale.ROOT, | ||
"AvalancheStats{inputBits=%d, outputBits=%d, bias=%.4f%%, diffusion=%.4f%%, sumOfSquaredErrors=%.2f}", | ||
inputBits, | ||
outputBits, | ||
bias() * 100, | ||
diffusion() * 100, | ||
sumOfSquaredErrors() | ||
); | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
test/framework/src/main/java/org/opensearch/common/hash/HashFunctionTestCase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
*/ | ||
|
||
package org.opensearch.common.hash; | ||
|
||
import org.opensearch.common.Randomness; | ||
import org.opensearch.test.OpenSearchTestCase; | ||
|
||
import java.util.Arrays; | ||
import java.util.Random; | ||
|
||
/** | ||
* Base class for testing the quality of hash functions. | ||
*/ | ||
public abstract class HashFunctionTestCase extends OpenSearchTestCase { | ||
private static final int[] INPUT_BITS = new int[] { 24, 32, 40, 48, 56, 64, 72, 80, 96, 112, 128, 160, 512, 1024 }; | ||
private static final int ITERATIONS = 1000; | ||
private static final double BIAS_THRESHOLD = 0.01; // 1% | ||
|
||
public abstract byte[] hash(byte[] input); | ||
|
||
public abstract int outputBits(); | ||
|
||
/** | ||
* Tests if the hash function shows an avalanche effect, i.e, flipping a single input bit | ||
* should flip half the output bits. | ||
*/ | ||
public void testAvalanche() { | ||
for (int inputBits : INPUT_BITS) { | ||
AvalancheStats stats = simulate(inputBits); | ||
if (stats.bias() >= BIAS_THRESHOLD) { | ||
fail("bias exceeds threshold: " + stats); | ||
} | ||
} | ||
} | ||
|
||
private AvalancheStats simulate(int inputBits) { | ||
int outputBits = outputBits(); | ||
assert inputBits % 8 == 0; // using full bytes for simplicity | ||
assert outputBits % 8 == 0; // using full bytes for simplicity | ||
byte[] input = new byte[inputBits >>> 3]; | ||
Random random = Randomness.get(); | ||
int[][] flips = new int[inputBits][outputBits]; | ||
|
||
for (int iter = 0; iter < ITERATIONS; iter++) { | ||
random.nextBytes(input); | ||
byte[] hash = Arrays.copyOf(hash(input), outputBits >>> 3); // copying since the underlying byte-array is reused | ||
|
||
for (int i = 0; i < inputBits; i++) { | ||
flipBit(input, i); // flip one bit | ||
byte[] newHash = hash(input); // recompute the hash; half the bits should have flipped | ||
flipBit(input, i); // return to original | ||
|
||
for (int o = 0; o < outputBits; o++) { | ||
flips[i][o] += getBit(hash, o) ^ getBit(newHash, o); | ||
} | ||
} | ||
} | ||
|
||
return new AvalancheStats(flips, ITERATIONS); | ||
} | ||
|
||
private static void flipBit(byte[] input, int position) { | ||
int offset = position / 8; | ||
int bit = position & 7; | ||
input[offset] ^= (1 << bit); | ||
} | ||
|
||
private static int getBit(byte[] input, int position) { | ||
int offset = position / 8; | ||
int bit = position & 7; | ||
return (input[offset] >>> bit) & 1; | ||
} | ||
} |
Oops, something went wrong.