PerturbedAtomHashGenerator extends AbstractHashGenerator implements AtomHashGenerator { long[] combine(long[][] perturbed) { int n = perturbed.length; int m = perturbed[0].length; long[] combined = new long[n]; long[] rotated = new long[m]; for (int i = 0; i < n; i++) { Arrays.sort(perturbed[i]); for (int j = 0; j < m; j++) { if (j > 0 && perturbed[i][j] == perturbed[i][j - 1]) { combined[i] ^= rotated[j] = rotate(rotated[j - 1]); } else { combined[i] ^= rotated[j] = perturbed[i][j]; } } } return combined; } PerturbedAtomHashGenerator(SeedGenerator seeds, AbstractAtomHashGenerator simple, Pseudorandom pseudorandom,
            StereoEncoderFactory factory, EquivalentSetFinder finder, AtomSuppression suppression); @Override long[] generate(IAtomContainer container);  }
@Test public void testCombine() throws Exception { Xorshift prng = new Xorshift(); PerturbedAtomHashGenerator generator = new PerturbedAtomHashGenerator(new SeedGenerator( BasicAtomEncoder.ATOMIC_NUMBER), new BasicAtomHashGenerator(new SeedGenerator( BasicAtomEncoder.ATOMIC_NUMBER), prng, 8), prng, StereoEncoderFactory.EMPTY, new MinimumEquivalentCyclicSet(), AtomSuppression.unsuppressed()); long[][] perturbed = new long[][]{{1, 2, 3, 4}, {1, 1, 1, 1}, {1, 2, 2, 4}, {2, 2, 2, 2},}; long _0 = 1 ^ 2 ^ 3 ^ 4; long _1 = 1 ^ prng.next(1) ^ prng.next(prng.next(1)) ^ prng.next(prng.next(prng.next(1))); long _2 = 1 ^ 2 ^ prng.next(2) ^ 4; long _3 = 2 ^ prng.next(2) ^ prng.next(prng.next(2)) ^ prng.next(prng.next(prng.next(2))); long[] values = generator.combine(perturbed); Assert.assertArrayEquals(values, new long[]{_0, _1, _2, _3}); }