@@ -61,28 +61,8 @@ public static double[] jaccard(final ThetaSketch sketchA, final ThetaSketch sket
6161 if (sketchA .isEmpty () && sketchB .isEmpty ()) { return ONES .clone (); }
6262 if (sketchA .isEmpty () || sketchB .isEmpty ()) { return ZEROS .clone (); }
6363
64- final int countA = sketchA .getRetainedEntries (true );
65- final int countB = sketchB .getRetainedEntries (true );
66-
67- //Create the ThetaUnion
68- final int minK = 1 << ThetaUtil .MIN_LG_NOM_LONGS ;
69- final int maxK = 1 << ThetaUtil .MAX_LG_NOM_LONGS ;
70- final int newK = max (min (ceilingPowerOf2 (countA + countB ), maxK ), minK );
71- final ThetaUnion union =
72- ThetaSetOperation .builder ().setNominalEntries (newK ).buildUnion ();
73- union .union (sketchA );
74- union .union (sketchB );
75- final ThetaSketch unionAB = union .getResult (false , null );
76- final long thetaLongUAB = unionAB .getThetaLong ();
77- final long thetaLongA = sketchA .getThetaLong ();
78- final long thetaLongB = sketchB .getThetaLong ();
79- final int countUAB = unionAB .getRetainedEntries (true );
80-
81- //Check for identical data
82- if (countUAB == countA && countUAB == countB
83- && thetaLongUAB == thetaLongA && thetaLongUAB == thetaLongB ) {
84- return ONES .clone ();
85- }
64+ final ThetaSketch unionAB = computeUnion (sketchA , sketchB );
65+ if (identicalSets (sketchA , sketchB , unionAB )) { return ONES .clone (); }
8666
8767 //Create the ThetaIntersection
8868 final ThetaIntersection inter = ThetaSetOperation .builder ().buildIntersection ();
@@ -100,6 +80,10 @@ public static double[] jaccard(final ThetaSketch sketchA, final ThetaSketch sket
10080 /**
10181 * Returns true if the two given sketches have exactly the same hash values and the same
10282 * theta values. Thus, they are equivalent.
83+ *
84+ * <p>Note: For very large pairs of sketches, where the configured nominal entries of the sketches
85+ * are 2^26, this method may return false even if the two sketches are equal.
86+ *
10387 * @param sketchA the given sketch A
10488 * @param sketchB the given sketch B
10589 * @return true if the two given sketches have exactly the same hash values and the same
@@ -112,26 +96,8 @@ public static boolean exactlyEqual(final ThetaSketch sketchA, final ThetaSketch
11296 if (sketchA .isEmpty () && sketchB .isEmpty ()) { return true ; }
11397 if (sketchA .isEmpty () || sketchB .isEmpty ()) { return false ; }
11498
115- final int countA = sketchA .getRetainedEntries (true );
116- final int countB = sketchB .getRetainedEntries (true );
117-
118- //Create the ThetaUnion
119- final ThetaUnion union =
120- ThetaSetOperation .builder ().setNominalEntries (ceilingPowerOf2 (countA + countB )).buildUnion ();
121- union .union (sketchA );
122- union .union (sketchB );
123- final ThetaSketch unionAB = union .getResult ();
124- final long thetaLongUAB = unionAB .getThetaLong ();
125- final long thetaLongA = sketchA .getThetaLong ();
126- final long thetaLongB = sketchB .getThetaLong ();
127- final int countUAB = unionAB .getRetainedEntries (true );
128-
129- //Check for identical counts and thetas
130- if (countUAB == countA && countUAB == countB
131- && thetaLongUAB == thetaLongA && thetaLongUAB == thetaLongB ) {
132- return true ;
133- }
134- return false ;
99+ final ThetaSketch unionAB = computeUnion (sketchA , sketchB );
100+ return identicalSets (sketchA , sketchB , unionAB );
135101 }
136102
137103 /**
@@ -178,4 +144,43 @@ public static boolean dissimilarityTest(final ThetaSketch measured, final ThetaS
178144 return jRatioUB <= threshold ;
179145 }
180146
147+ /**
148+ * Computes the union of the two given sketches. The union is sized to hold the retained entries
149+ * of both sketches, clamped to the legal range of nominal entries. The result is unordered,
150+ * because only its retained entries and theta are used.
151+ * @param sketchA the given sketch A
152+ * @param sketchB the given sketch B
153+ * @return the unordered union of sketch A and sketch B
154+ */
155+ private static ThetaSketch computeUnion (final ThetaSketch sketchA , final ThetaSketch sketchB ) {
156+ final int countA = sketchA .getRetainedEntries (true );
157+ final int countB = sketchB .getRetainedEntries (true );
158+ final int minK = 1 << ThetaUtil .MIN_LG_NOM_LONGS ;
159+ final int maxK = 1 << ThetaUtil .MAX_LG_NOM_LONGS ;
160+ final int newK = max (min (ceilingPowerOf2 (countA + countB ), maxK ), minK );
161+ final ThetaUnion union =
162+ ThetaSetOperation .builder ().setNominalEntries (newK ).buildUnion ();
163+ union .union (sketchA );
164+ union .union (sketchB );
165+ return union .getResult (false , null );
166+ }
167+
168+ /**
169+ * Returns true if the two given sketches and their union all have the same number of retained
170+ * entries and the same theta, which means the two sketches hold identical sets.
171+ * @param sketchA the given sketch A
172+ * @param sketchB the given sketch B
173+ * @param unionAB the union of sketch A and sketch B
174+ * @return true if the two given sketches hold identical sets
175+ */
176+ private static boolean identicalSets (final ThetaSketch sketchA , final ThetaSketch sketchB ,
177+ final ThetaSketch unionAB ) {
178+ final int countUAB = unionAB .getRetainedEntries (true );
179+ final long thetaLongUAB = unionAB .getThetaLong ();
180+ return countUAB == sketchA .getRetainedEntries (true )
181+ && countUAB == sketchB .getRetainedEntries (true )
182+ && thetaLongUAB == sketchA .getThetaLong ()
183+ && thetaLongUAB == sketchB .getThetaLong ();
184+ }
185+
181186}
0 commit comments