LinearCounting implements IRichCardinality { @Override public IRichCardinality merge(IRichCardinality... estimators) throws LinearCountingMergeException { if (estimators == null) { return new LinearCounting(map); } LinearCounting[] lcs = Arrays.copyOf(estimators, estimators.length + 1, LinearCounting[].class); lcs[lcs.length - 1] = this; return LinearCounting.mergeEstimators(lcs); } LinearCounting(int size);  LinearCounting(byte[] map); @Override long cardinality(); @Override byte[] getBytes(); @Override boolean offerHashed(long hashedLong); @Override boolean offerHashed(int hashedInt); @Override boolean offer(Object o); @Override int sizeof(); int computeCount(); double getUtilization(); int getCount(); boolean isSaturated(); @Override IRichCardinality merge(IRichCardinality... estimators); static LinearCounting mergeEstimators(LinearCounting... estimators);  }
@Test public void testMerge() throws LinearCounting.LinearCountingMergeException { int numToMerge = 5; int size = 65536; int cardinality = 1000; LinearCounting[] lcs = new LinearCounting[numToMerge]; LinearCounting baseline = new LinearCounting(size); for (int i = 0; i < numToMerge; i++) { lcs[i] = new LinearCounting(size); for (int j = 0; j < cardinality; j++) { double val = Math.random(); lcs[i].offer(val); baseline.offer(val); } } int expectedCardinality = numToMerge * cardinality; long mergedEstimate = LinearCounting.mergeEstimators(lcs).cardinality(); double error = Math.abs(mergedEstimate - expectedCardinality) / (double) expectedCardinality; assertEquals(0.01, error, 0.01); LinearCounting lc = lcs[0]; lcs = Arrays.asList(lcs).subList(1, lcs.length).toArray(new LinearCounting[0]); mergedEstimate = lc.merge(lcs).cardinality(); error = Math.abs(mergedEstimate - expectedCardinality) / (double) expectedCardinality; assertEquals(0.01, error, 0.01); long baselineEstimate = baseline.cardinality(); assertEquals(baselineEstimate, mergedEstimate); }