You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datasketches.apache.org by le...@apache.org on 2020/05/27 15:34:15 UTC

[incubator-datasketches-java] 01/01: First round changes to extend tuple sketch to allow set operations with a theta sketch.

This is an automated email from the ASF dual-hosted git repository.

leerho pushed a commit to branch Tuple_Theta_Extension
in repository https://gitbox.apache.org/repos/asf/incubator-datasketches-java.git

commit d1d6b4caa9f22cae8f1e7362160f442cef2199c8
Author: Lee Rhodes <le...@users.noreply.github.com>
AuthorDate: Wed May 27 08:33:03 2020 -0700

    First round changes to extend tuple sketch to allow set operations with
    a theta sketch.
---
 .../java/org/apache/datasketches/tuple/AnotB.java  | 352 ++++++++++++++++++---
 .../apache/datasketches/tuple/Intersection.java    |  79 ++++-
 .../datasketches/tuple/QuickSelectSketch.java      |   2 +-
 .../java/org/apache/datasketches/tuple/Union.java  |  24 ++
 .../tuple/adouble/AdoubleAnotBTest.java            | 245 ++++++++++++++
 .../UpdatableSketchWithDoubleSummaryTest.java      | 189 -----------
 .../tuple/aninteger/IntegerSketchTest.java         |  30 +-
 .../tuple/arrayofdoubles/package-info.java         |  24 --
 8 files changed, 678 insertions(+), 267 deletions(-)

diff --git a/src/main/java/org/apache/datasketches/tuple/AnotB.java b/src/main/java/org/apache/datasketches/tuple/AnotB.java
index c3db651..cd9f3f6 100644
--- a/src/main/java/org/apache/datasketches/tuple/AnotB.java
+++ b/src/main/java/org/apache/datasketches/tuple/AnotB.java
@@ -22,45 +22,327 @@ package org.apache.datasketches.tuple;
 import static org.apache.datasketches.Util.MIN_LG_NOM_LONGS;
 import static org.apache.datasketches.Util.REBUILD_THRESHOLD;
 import static org.apache.datasketches.Util.ceilingPowerOf2;
+import static org.apache.datasketches.Util.simpleIntLog2;
 
 import java.lang.reflect.Array;
 import java.util.Arrays;
 
 import org.apache.datasketches.HashOperations;
+import org.apache.datasketches.theta.HashIterator;
 
 /**
- * Computes a set difference of two generic tuple sketches
+ * Computes a set difference, A-AND-NOT-B, of two generic tuple sketches
  * @param <S> Type of Summary
  */
 public final class AnotB<S extends Summary> {
-  private boolean isEmpty_ = true;
-  private long theta_ = Long.MAX_VALUE;
-  private long[] keys_;
-  private S[] summaries_;
-  private int count_;
+  private boolean empty_ = true;
+  private long thetaLong_ = Long.MAX_VALUE;
+  private long[] keys_ = null;   //always in compact form, not necessarily sorted
+  private S[] summaries_ = null; //always in compact form, not necessarily sorted
+  private int count_ = 0;
+
+  /**
+   * Sets the given Tuple sketch as the first argument <i>A</i>. This overwrites the internal state of
+   * this AnotB operator with the contents of the given sketch. This sets the stage for multiple
+   * following <i>notB</i> operations.
+   * @param skA The incoming sketch for the first argument, <i>A</i>.
+   */
+  public void setA(final Sketch<S> skA) {
+    if ((skA == null) || skA.isEmpty()) { return; }
+    //skA is not empty
+    empty_ = false;
+    thetaLong_ = skA.getThetaLong();
+    final CompactSketch<S> cskA = (skA instanceof CompactSketch)
+        ? (CompactSketch<S>)skA
+        : ((QuickSelectSketch<S>)skA).compact();
+    keys_ = cskA.keys_;
+    summaries_ = cskA.summaries_;
+    count_ = cskA.getRetainedEntries();
+  }
+
+  /**
+   * Performs an <i>AND NOT</i> operation with the existing internal state of this AnoB operator.
+   * @param skB The incoming Tuple sketch for the second (or following) argument <i>B</i>.
+   */
+  @SuppressWarnings("unchecked")
+  public void notB(final Sketch<S> skB) {
+    if (empty_) { return; }
+    if ((skB == null) || skB.isEmpty()) { return; }
+    //skB is not empty
+    final long thetaLongB = skB.getThetaLong();
+    thetaLong_ = Math.min(thetaLong_, thetaLongB);
+    //Build hashtable and removes keys of skB >= theta
+    final int countB = skB.getRetainedEntries();
+    final long[] hashTableKeysB = convertToHashTable(skB.keys_, countB, thetaLong_);
+
+    //build temporary arrays of skA
+    final long[] tmpKeysA = new long[count_];
+    final S[] tmpSummariesA =
+        (S[]) Array.newInstance(summaries_.getClass().getComponentType(), count_);
+
+    //search for non matches and build temp arrays
+    int nonMatches = 0;
+    for (int i = 0; i < count_; i++) {
+      final long key = keys_[i];
+      if ((key != 0) && (key < thetaLong_)) { //skips keys of A >= theta
+        final int index =
+            HashOperations.hashSearch(hashTableKeysB, simpleIntLog2(hashTableKeysB.length), key);
+        if (index == -1) {
+          tmpKeysA[nonMatches] = key;
+          tmpSummariesA[nonMatches] = summaries_[i];
+          nonMatches++;
+        }
+      }
+    }
+    keys_ = Arrays.copyOfRange(tmpKeysA, 0, nonMatches);
+    summaries_ = Arrays.copyOfRange(tmpSummariesA, 0, nonMatches);
+    count_ = nonMatches;
+  }
+
+  /**
+   * Performs an <i>AND NOT</i> operation with the existing internal state of this AnoB operator.
+   * @param skB The incoming Theta sketch for the second (or following) argument <i>B</i>.
+   */
+  @SuppressWarnings("unchecked")
+  public void notB(final org.apache.datasketches.theta.Sketch skB) {
+    if (empty_) { return; }
+    if ((skB == null) || skB.isEmpty()) { return; }
+    //skB is not empty
+    final long thetaLongB = skB.getThetaLong();
+    thetaLong_ = Math.min(thetaLong_, thetaLongB);
+    //Build hashtable and removes keys of skB >= theta
+    final int countB = skB.getRetainedEntries();
+    final long[] hashTableKeysB =
+        convertToHashTable(extractThetaHashArray(skB, countB), countB, thetaLong_);
+
+    //build temporary arrays of skA
+    final long[] tmpKeysA = new long[count_];
+    final S[] tmpSummariesA =
+        (S[]) Array.newInstance(summaries_.getClass().getComponentType(), count_);
+
+    //search for non matches and build temp arrays
+    int nonMatches = 0;
+    for (int i = 0; i < count_; i++) {
+      final long key = keys_[i];
+      if ((key != 0) && (key < thetaLong_)) { //skips keys of A >= theta
+        final int index =
+            HashOperations.hashSearch(hashTableKeysB, simpleIntLog2(hashTableKeysB.length), key);
+        if (index == -1) {
+          tmpKeysA[nonMatches] = key;
+          tmpSummariesA[nonMatches] = summaries_[i];
+          nonMatches++;
+        }
+      }
+    }
+    keys_ = Arrays.copyOfRange(tmpKeysA, 0, nonMatches);
+    summaries_ = Arrays.copyOfRange(tmpSummariesA, 0, nonMatches);
+    count_ = nonMatches;
+  }
+
+
+  /**
+   * Returns the A-and-not-B set operation on the two given sketches.
+   * A null sketch argument is interpreted as an empty sketch.
+   * This is not an accumulating update.
+   *
+   * @param skA The incoming sketch for the first argument
+   * @param skB The incoming sketch for the second argument
+   * @param <S> Type of Summary
+   * @return the result as a compact sketch
+   */
+  @SuppressWarnings("unchecked")
+  public static <S extends Summary>
+        CompactSketch<S> aNotB(final Sketch<S> skA, final Sketch<S> skB) {
+    if ((skA == null) || skA.isEmpty()) {
+      return new CompactSketch<>(null, null, Long.MAX_VALUE, true);
+    }
+    //skA is not empty
+    final boolean empty = false;
+    final long thetaLongA = skA.getThetaLong();
+    final CompactSketch<S> cskA = (skA instanceof CompactSketch)
+        ? (CompactSketch<S>)skA
+        : ((QuickSelectSketch<S>)skA).compact();
+    final long[] keysA = cskA.keys_;
+    final S[] summariesA = cskA.summaries_;
+    final int countA = cskA.getRetainedEntries();
+
+    if ((skB == null) || skB.isEmpty()) {
+      return new CompactSketch<>(keysA, summariesA, thetaLongA, empty);
+    }
+    //skB is not empty
+    final long thetaLongB = skB.getThetaLong();
+    final long thetaLong = Math.min(thetaLongA, thetaLongB);
+    //Build hashtable and removes keys of skB >= theta
+    final int countB = skB.getRetainedEntries();
+    final long[] hashTableKeysB =
+        convertToHashTable(skB.keys_, countB, thetaLong);
+
+    //build temporary arrays of skA
+    final long[] tmpKeysA = new long[countA];
+    final S[] tmpSummariesA =
+        (S[]) Array.newInstance(summariesA.getClass().getComponentType(), countA);
+
+    //search for non matches and build temp arrays
+    int nonMatches = 0;
+    for (int i = 0; i < countA; i++) {
+      final long key = keysA[i];
+      if ((key != 0) && (key < thetaLong)) { //skips keys of A >= theta
+        final int index =
+            HashOperations.hashSearch(hashTableKeysB, simpleIntLog2(hashTableKeysB.length), key);
+        if (index == -1) {
+          tmpKeysA[nonMatches] = key;
+          tmpSummariesA[nonMatches] = summariesA[i];
+          nonMatches++;
+        }
+      }
+    }
+    final long[] keys = Arrays.copyOfRange(tmpKeysA, 0, nonMatches);
+    final S[] summaries = Arrays.copyOfRange(tmpSummariesA, 0, nonMatches);
+    final CompactSketch<S> result =
+        new CompactSketch<>(keys, summaries, thetaLong, empty);
+    return result;
+  }
+
+  /**
+   * Returns the A-and-not-B set operation on a Tuple sketch and a Theta sketch.
+   * A null sketch argument is interpreted as an empty sketch.
+   * This is not an accumulating update.
+   *
+   * @param skA The incoming Tuple sketch for the first argument
+   * @param skB The incoming Theta sketch for the second argument
+   * @param <S> Type of Summary
+   * @return the result as a compact sketch
+   */
+  @SuppressWarnings("unchecked")
+  public static <S extends Summary>
+        CompactSketch<S> aNotB(final Sketch<S> skA, final org.apache.datasketches.theta.Sketch skB) {
+    if ((skA == null) || skA.isEmpty()) {
+      return new CompactSketch<>(null, null, Long.MAX_VALUE, true);
+    }
+    //skA is not empty
+    final boolean empty = false;
+    final long thetaLongA = skA.getThetaLong();
+    final CompactSketch<S> cskA = (skA instanceof CompactSketch)
+        ? (CompactSketch<S>)skA
+        : ((QuickSelectSketch<S>)skA).compact();
+    final long[] keysA = cskA.keys_;
+    final S[] summariesA = cskA.summaries_;
+    final int countA = cskA.getRetainedEntries();
+
+    if ((skB == null) || skB.isEmpty()) {
+      return new CompactSketch<>(keysA, summariesA, thetaLongA, empty);
+    }
+    //skB is not empty
+    final long thetaLongB = skB.getThetaLong();
+    final long thetaLong = Math.min(thetaLongA, thetaLongB);
+    //Build hashtable and removes keys of skB >= theta
+    final int countB = skB.getRetainedEntries();
+    final long[] hashTableKeysB =
+        convertToHashTable(extractThetaHashArray(skB, countB), countB, thetaLong);
+
+    //build temporary arrays of skA
+    final long[] tmpKeysA = new long[countA];
+    final S[] tmpSummariesA =
+        (S[]) Array.newInstance(summariesA.getClass().getComponentType(), countA);
+
+    //search for non matches and build temp arrays
+    int nonMatches = 0;
+    for (int i = 0; i < countA; i++) {
+      final long key = keysA[i];
+      if ((key != 0) && (key < thetaLong)) { //skips keys of A >= theta
+        final int index =
+            HashOperations.hashSearch(hashTableKeysB, simpleIntLog2(hashTableKeysB.length), key);
+        if (index == -1) {
+          tmpKeysA[nonMatches] = key;
+          tmpSummariesA[nonMatches] = summariesA[i];
+          nonMatches++;
+        }
+      }
+    }
+    final long[] keys = Arrays.copyOfRange(tmpKeysA, 0, nonMatches);
+    final S[] summaries = Arrays.copyOfRange(tmpSummariesA, 0, nonMatches);
+    final CompactSketch<S> result =
+        new CompactSketch<>(keys, summaries, thetaLong, empty);
+    return result;
+  }
+
+  /**
+   * Gets the result of this operation.
+   * @param reset if true, clears this operator to the empty state after result is returned.
+   * @return the result of this operation as a CompactSketch.
+   */
+  public CompactSketch<S> getResult(final boolean reset) {
+    if (count_ == 0) {
+      return new CompactSketch<>(null, null, thetaLong_, empty_);
+    }
+    final CompactSketch<S> result =
+        new CompactSketch<>(Arrays.copyOfRange(keys_, 0, count_),
+            Arrays.copyOfRange(summaries_, 0, count_), thetaLong_, empty_);
+    if (reset) { reset(); }
+    return result;
+  }
+
+  private static long[] extractThetaHashArray(
+      final org.apache.datasketches.theta.Sketch sketch,
+      final int count) {
+    final HashIterator itr = sketch.iterator();
+    final long[] hashArr = new long[count];
+    int ctr = 0;
+    while (itr.next()) {
+      hashArr[ctr++] = itr.get();
+    }
+    assert ctr == count;
+    return hashArr;
+  }
+
+  private static long[] convertToHashTable(final long[] keysArr, final int count, final long thetaLong) {
+    final int size = Math.max(
+      ceilingPowerOf2((int) Math.ceil(count / REBUILD_THRESHOLD)),
+      1 << MIN_LG_NOM_LONGS
+    );
+    final long[] hashTable = new long[size];
+    HashOperations.hashArrayInsert(
+        keysArr, hashTable, Integer.numberOfTrailingZeros(size), thetaLong);
+    return hashTable;
+  }
+
+  private void reset() {
+    empty_ = true;
+    thetaLong_ = Long.MAX_VALUE;
+    keys_ = null;
+    summaries_ = null;
+    count_ = 0;
+  }
+
+  //Deprecated methods
 
   /**
    * Perform A-and-not-B set operation on the two given sketches.
    * A null sketch is interpreted as an empty sketch.
-   * This is not an accumulating update. Calling update() more than once
-   * without calling getResult() will discard the result of previous update()
+   * This is not an accumulating update. Calling this update() more than once
+   * without calling getResult() will discard the result of previous update() by this method.
+   * The result is obtained by calling getResult();
    *
    * @param a The incoming sketch for the first argument
    * @param b The incoming sketch for the second argument
+   * @deprecated After release 2.0.0. Instead please use {@link #aNotB(Sketch, Sketch)}
+   * or a combination of {@link #setA(Sketch)} and
+   * {@link #notB(Sketch)} with {@link #getResult(boolean)}.
    */
   @SuppressWarnings("unchecked")
+  @Deprecated
   public void update(final Sketch<S> a, final Sketch<S> b) {
-    if (a != null) { isEmpty_ = a.isEmpty(); } //stays this way even if we end up with no result entries
+    if (a != null) { empty_ = a.isEmpty(); } //stays this way even if we end up with no result entries
     final long thetaA = a == null ? Long.MAX_VALUE : a.getThetaLong();
     final long thetaB = b == null ? Long.MAX_VALUE : b.getThetaLong();
-    theta_ = Math.min(thetaA, thetaB);
-    if (a == null || a.getRetainedEntries() == 0) { return; }
-    if (b == null || b.getRetainedEntries() == 0) {
-      getNoMatchSetFromSketch(a);
+    thetaLong_ = Math.min(thetaA, thetaB);
+    if ((a == null) || (a.getRetainedEntries() == 0)) { return; }
+    if ((b == null) || (b.getRetainedEntries() == 0)) {
+      loadCompactedArrays(a);
     } else {
       final long[] hashTable;
       if (b instanceof CompactSketch) {
-        hashTable = convertToHashTable(b);
+        hashTable = convertToHashTable(b.keys_, b.getRetainedEntries(), thetaLong_);
       } else {
         hashTable = b.keys_;
       }
@@ -69,7 +351,7 @@ public final class AnotB<S extends Summary> {
       keys_ = new long[noMatchSize];
       summaries_ = (S[]) Array.newInstance(a.summaries_.getClass().getComponentType(), noMatchSize);
       for (int i = 0; i < a.keys_.length; i++) {
-        if (a.keys_[i] != 0 && a.keys_[i] < theta_) {
+        if ((a.keys_[i] != 0) && (a.keys_[i] < thetaLong_)) {
           final int index = HashOperations.hashSearch(hashTable, lgHashTableSize, a.keys_[i]);
           if (index == -1) {
             keys_[count_] = a.keys_[i];
@@ -82,40 +364,31 @@ public final class AnotB<S extends Summary> {
   }
 
   /**
-   * Gets the result of this operation
+   * Gets the result of this operation. This clears the state of this operator after the result is
+   * returned.
    * @return the result of this operation as a CompactSketch
+   * @deprecated Only used with deprecated {@link #update(Sketch,Sketch)}.
+   * Instead use {@link #aNotB(Sketch, Sketch)} or a combination of {@link #setA(Sketch)} and
+   * {@link #notB(Sketch)} with {@link #getResult(boolean)}.
    */
+  @Deprecated
   public CompactSketch<S> getResult() {
     if (count_ == 0) {
-      return new CompactSketch<S>(null, null, theta_, isEmpty_);
+      return new CompactSketch<>(null, null, thetaLong_, empty_);
     }
     final CompactSketch<S> result =
-        new CompactSketch<S>(Arrays.copyOfRange(keys_, 0, count_),
-            Arrays.copyOfRange(summaries_, 0, count_), theta_, isEmpty_);
+        new CompactSketch<>(Arrays.copyOfRange(keys_, 0, count_),
+            Arrays.copyOfRange(summaries_, 0, count_), thetaLong_, empty_);
     reset();
     return result;
   }
 
-  private long[] convertToHashTable(final Sketch<S> sketch) {
-    final int size = Math.max(
-      ceilingPowerOf2((int) Math.ceil(sketch.getRetainedEntries() / REBUILD_THRESHOLD)),
-      1 << MIN_LG_NOM_LONGS
-    );
-    final long[] hashTable = new long[size];
-    HashOperations.hashArrayInsert(
-        sketch.keys_, hashTable, Integer.numberOfTrailingZeros(size), theta_);
-    return hashTable;
-  }
-
-  private void reset() {
-    isEmpty_ = true;
-    theta_ = Long.MAX_VALUE;
-    keys_ = null;
-    summaries_ = null;
-    count_ = 0;
-  }
-
-  private void getNoMatchSetFromSketch(final Sketch<S> sketch) {
+  /**
+   * Only used by deprecated {@link #update(Sketch, Sketch)}.
+   * Remove at the same time other deprecated methods are removed.
+   * @param sketch the given sketch to extract arrays from.
+   */
+  private void loadCompactedArrays(final Sketch<S> sketch) {
     if (sketch instanceof CompactSketch) {
       keys_ = sketch.keys_.clone();
       summaries_ = sketch.summaries_.clone();
@@ -126,4 +399,5 @@ public final class AnotB<S extends Summary> {
     }
     count_ = sketch.getRetainedEntries();
   }
+
 }
diff --git a/src/main/java/org/apache/datasketches/tuple/Intersection.java b/src/main/java/org/apache/datasketches/tuple/Intersection.java
index 826d241..0c32089 100644
--- a/src/main/java/org/apache/datasketches/tuple/Intersection.java
+++ b/src/main/java/org/apache/datasketches/tuple/Intersection.java
@@ -71,7 +71,6 @@ public class Intersection<S extends Summary> {
       sketch_ = null;
       return;
     }
-    // assumes that constructor of QuickSelectSketch bumps the requested size up to the nearest power of 2
     if (isFirstCall) {
       sketch_ = new QuickSelectSketch<>(sketchIn.getRetainedEntries(), ResizeFactor.X1.lg(), null);
       final SketchIterator<S> it = sketchIn.iterator();
@@ -89,19 +88,21 @@ public class Intersection<S extends Summary> {
       int matchCount = 0;
       final SketchIterator<S> it = sketchIn.iterator();
       while (it.next()) {
-        final S summary = sketch_.find(it.getKey());
-        if (summary != null) {
-          matchKeys[matchCount] = it.getKey();
+        final long key = it.getKey();
+        final S summary = sketch_.find(key);
+        if (summary != null) { //key found
+          matchKeys[matchCount] = key;
           if (matchSummaries == null) {
             matchSummaries = (S[]) Array.newInstance(summary.getClass(), matchSize);
           }
-          matchSummaries[matchCount] =
-              summarySetOps_.intersection(summary, it.getSummary());
+          matchSummaries[matchCount] = summarySetOps_.intersection(summary, it.getSummary());
           matchCount++;
         }
       }
       sketch_ = null;
-      if (matchCount > 0) {
+      if (matchCount > 0) { //therefore matchSummaries != null.
+        // assumes that constructor of QuickSelectSketch bumps the requested size
+        // up to the nearest power of 2
         sketch_ = new QuickSelectSketch<>(matchCount, ResizeFactor.X1.lg(), null);
         for (int i = 0; i < matchCount; i++) {
           sketch_.insert(matchKeys[i], matchSummaries[i]);
@@ -115,6 +116,70 @@ public class Intersection<S extends Summary> {
   }
 
   /**
+   * Updates the internal set by intersecting it with the given Theta sketch
+   * @param sketchIn input Theta Sketch to intersect with the internal set
+   * @param summary the given proxy summary for the Theta Sketch, which doesn't have one.
+   */
+  @SuppressWarnings({ "unchecked", "null" })
+  public void update(final org.apache.datasketches.theta.Sketch sketchIn, final S summary) {
+    final boolean isFirstCall = isFirstCall_;
+    isFirstCall_ = false;
+    if (sketchIn == null) {
+      isEmpty_ = true;
+      sketch_ = null;
+      return;
+    }
+    theta_ = min(theta_, sketchIn.getThetaLong());
+    isEmpty_ |= sketchIn.isEmpty();
+    if (isEmpty_ || (sketchIn.getRetainedEntries() == 0)) {
+      sketch_ = null;
+      return;
+    }
+    if (isFirstCall) {
+      sketch_ = new QuickSelectSketch<>(sketchIn.getRetainedEntries(), ResizeFactor.X1.lg(), null);
+      final org.apache.datasketches.theta.HashIterator it = sketchIn.iterator();
+      while (it.next()) {
+        sketch_.insert(it.get(), (S)summary.copy());
+      }
+    } else {
+      if (sketch_ == null) {
+        return;
+      }
+      final int matchSize = min(sketch_.getRetainedEntries(), sketchIn.getRetainedEntries());
+      final long[] matchKeys = new long[matchSize];
+      S[] matchSummaries = null;
+      int matchCount = 0;
+      final org.apache.datasketches.theta.HashIterator it = sketchIn.iterator();
+      while (it.next()) {
+        final long key = it.get();
+        final S mySummary = sketch_.find(key);
+        if (mySummary != null) { //key found
+          matchKeys[matchCount] = key;
+          if (matchSummaries == null) {
+            matchSummaries = (S[]) Array.newInstance(mySummary.getClass(), matchSize);
+          }
+          matchSummaries[matchCount] = summarySetOps_.intersection(mySummary, (S)summary.copy());
+          matchCount++;
+        }
+      }
+      sketch_ = null;
+      if (matchCount > 0) { //therefore matchSummaries != null.
+        // assumes that constructor of QuickSelectSketch bumps the requested size
+        // up to the nearest power of 2
+        sketch_ = new QuickSelectSketch<>(matchCount, ResizeFactor.X1.lg(), null);
+        for (int i = 0; i < matchCount; i++) {
+          sketch_.insert(matchKeys[i], matchSummaries[i]);
+        }
+      }
+    }
+    if (sketch_ != null) {
+      sketch_.setThetaLong(theta_);
+      sketch_.setNotEmpty();
+    }
+  }
+
+
+  /**
    * Gets the internal set as a CompactSketch
    * @return result of the intersections so far
    */
diff --git a/src/main/java/org/apache/datasketches/tuple/QuickSelectSketch.java b/src/main/java/org/apache/datasketches/tuple/QuickSelectSketch.java
index a50d920..be41ed0 100644
--- a/src/main/java/org/apache/datasketches/tuple/QuickSelectSketch.java
+++ b/src/main/java/org/apache/datasketches/tuple/QuickSelectSketch.java
@@ -375,7 +375,7 @@ class QuickSelectSketch<S extends Summary> extends Sketch<S> {
     if (key < theta_) {
       final int index = findOrInsert(key);
       if (index < 0) {
-        insertSummary(~index, (S)summary.copy());
+        insertSummary(~index, (S)summary.copy()); //did not find so insert
       } else {
         insertSummary(index, summarySetOps.union(summaries_[index], summary));
       }
diff --git a/src/main/java/org/apache/datasketches/tuple/Union.java b/src/main/java/org/apache/datasketches/tuple/Union.java
index 7800b55..d691cc4 100644
--- a/src/main/java/org/apache/datasketches/tuple/Union.java
+++ b/src/main/java/org/apache/datasketches/tuple/Union.java
@@ -25,6 +25,7 @@ import static org.apache.datasketches.Util.DEFAULT_NOMINAL_ENTRIES;
 import java.lang.reflect.Array;
 
 import org.apache.datasketches.QuickSelect;
+import org.apache.datasketches.SketchesArgumentException;
 
 /**
  * Compute a union of two or more tuple sketches.
@@ -78,6 +79,29 @@ public class Union<S extends Summary> {
   }
 
   /**
+   * Updates the internal set by combining entries using the hash keys from the Theta Sketch and
+   * summary values from the given summary and rules from the summarySetOps defined by the
+   * Union constructor.
+   * @param sketchIn the given Theta Sketch input.
+   * @param summary the given proxy summary for the Theta Sketch, which doesn't have one.
+   */
+  @SuppressWarnings("unchecked")
+  public void update(final org.apache.datasketches.theta.Sketch sketchIn, final S summary) {
+    if ((sketchIn == null) || sketchIn.isEmpty()) { return; }
+    if (summary == null) { throw new SketchesArgumentException("Summary cannot be null."); }
+    isEmpty_ = false;
+    final long thetaIn = sketchIn.getThetaLong();
+    if (thetaIn < theta_) { theta_ = thetaIn; }
+    final org.apache.datasketches.theta.HashIterator it = sketchIn.iterator();
+    while (it.next()) {
+      sketch_.merge(it.get(), (S)summary.copy(), summarySetOps_);
+    }
+    if (sketch_.theta_ < theta_) {
+      theta_ = sketch_.theta_;
+    }
+  }
+
+  /**
    * Gets the internal set as a CompactSketch
    * @return result of the unions so far
    */
diff --git a/src/test/java/org/apache/datasketches/tuple/adouble/AdoubleAnotBTest.java b/src/test/java/org/apache/datasketches/tuple/adouble/AdoubleAnotBTest.java
new file mode 100644
index 0000000..bd9d630
--- /dev/null
+++ b/src/test/java/org/apache/datasketches/tuple/adouble/AdoubleAnotBTest.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.datasketches.tuple.adouble;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.datasketches.theta.UpdateSketch;
+import org.apache.datasketches.theta.UpdateSketchBuilder;
+import org.apache.datasketches.tuple.AnotB;
+import org.apache.datasketches.tuple.CompactSketch;
+import org.apache.datasketches.tuple.Sketch;
+import org.apache.datasketches.tuple.SketchIterator;
+import org.apache.datasketches.tuple.UpdatableSketch;
+import org.apache.datasketches.tuple.UpdatableSketchBuilder;
+import org.apache.datasketches.tuple.adouble.DoubleSummary.Mode;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * @author Lee Rhodes
+ */
+@SuppressWarnings("javadoc")
+public class AdoubleAnotBTest {
+  private final DoubleSummary.Mode mode = Mode.Sum;
+  private final Results results = new Results();
+
+  private static void threeMethodsWithTheta(
+      final AnotB<DoubleSummary> aNotB,
+      final Sketch<DoubleSummary> sketchA,
+      final Sketch<DoubleSummary> sketchB,
+      final org.apache.datasketches.theta.Sketch skThetaB,
+      final Results results) {
+    //deprecated
+    aNotB.update(sketchA, sketchB);
+    CompactSketch<DoubleSummary> result = aNotB.getResult();
+    results.check(result);
+    //Stateless
+    result = AnotB.aNotB(sketchA, sketchB);
+    results.check(result);
+    result = AnotB.aNotB(sketchA, skThetaB);
+    //Stateful w B = Tuple
+    aNotB.setA(sketchA);
+    aNotB.notB(sketchB);
+    result = aNotB.getResult(true);
+    results.check(result);
+    //Stateful w B = Theta
+    aNotB.setA(sketchA);
+    aNotB.notB(skThetaB);
+    result = aNotB.getResult(true);
+    results.check(result);
+  }
+
+  private static class Results {
+    private int retEnt = 0;
+    private boolean empty = true;
+    private double expect = 0.0;
+    private double toll = 0.0;
+    private double sum = 0.0;
+
+    Results() {}
+
+    Results set(final int retEnt, final boolean empty,
+        final double expect, final double toll, final double sum) {
+      this.retEnt = retEnt;
+      this.empty = empty;
+      this.expect = expect;
+      this.toll = toll;
+      this.sum = sum;
+      return this;
+    }
+
+    void check(final CompactSketch<DoubleSummary> result) {
+      assertEquals(result.getRetainedEntries(), retEnt);
+      assertEquals(result.isEmpty(), empty);
+      if (result.getTheta() < 1.0) {
+        double est = result.getEstimate();
+        assertEquals(est, expect, expect * toll);
+        assertTrue(result.getUpperBound(1) > est);
+        assertTrue(result.getLowerBound(1) <= est);
+      } else {
+        assertEquals(result.getEstimate(), expect, 0.0);
+        assertEquals(result.getUpperBound(1), expect, 0.0);
+        assertEquals(result.getLowerBound(1), expect, 0.0);
+      }
+      SketchIterator<DoubleSummary> it = result.iterator();
+      while (it.next()) {
+        Assert.assertEquals(it.getSummary().getValue(), sum);
+      }
+    }
+  }
+
+  private UpdatableSketch<Double, DoubleSummary> buildUpdatableTuple() {
+    return new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
+  }
+
+  private static UpdateSketch buildUpdateTheta() {
+    return new UpdateSketchBuilder().build();
+  }
+
+  /*****************************************/
+
+  @Test
+  public void aNotBNullEmptyCombinations() {
+    AnotB<DoubleSummary> aNotB = new AnotB<>();
+    // calling getResult() before calling update() should yield an empty set
+    CompactSketch<DoubleSummary> result = aNotB.getResult();
+    results.set(0, true, 0.0, 0.0, 0.0).check(result);
+
+    UpdatableSketch<Double, DoubleSummary> sketch = buildUpdatableTuple();
+    UpdateSketch skTheta = buildUpdateTheta();
+
+    threeMethodsWithTheta(aNotB, null, null, null, results);
+    threeMethodsWithTheta(aNotB, sketch, null, null, results);
+    threeMethodsWithTheta(aNotB, null, sketch, null, results);
+    threeMethodsWithTheta(aNotB, sketch, sketch, null, results);
+    threeMethodsWithTheta(aNotB, null, null, skTheta, results);
+    threeMethodsWithTheta(aNotB, sketch, null, skTheta, results);
+    threeMethodsWithTheta(aNotB, null, sketch, skTheta, results);
+    threeMethodsWithTheta(aNotB, sketch, sketch, skTheta, results);
+  }
+
+  @Test
+  public void aNotBEmptyExact() {
+    UpdatableSketch<Double, DoubleSummary> sketchA = buildUpdatableTuple();
+    UpdatableSketch<Double, DoubleSummary> sketchB = buildUpdatableTuple();
+    sketchB.update(1, 1.0);
+    sketchB.update(2, 1.0);
+    UpdateSketch skThetaB = buildUpdateTheta();
+    skThetaB.update(1);
+    skThetaB.update(2);
+
+    AnotB<DoubleSummary> aNotB = new AnotB<>();
+    results.set(0, true, 0.0, 0.0, 0.0);
+    threeMethodsWithTheta(aNotB, sketchA, sketchB, skThetaB, results);
+  }
+
+  @Test
+  public void aNotBExactEmpty() {
+    UpdatableSketch<Double, DoubleSummary> sketchA = buildUpdatableTuple();
+    sketchA.update(1, 1.0);
+    sketchA.update(2, 1.0);
+    UpdatableSketch<Double, DoubleSummary> sketchB = buildUpdatableTuple();
+    UpdateSketch skThetaB = buildUpdateTheta();
+
+    AnotB<DoubleSummary> aNotB = new AnotB<>();
+    results.set(2, false, 2.0, 0.0, 1.0);
+    threeMethodsWithTheta(aNotB, sketchA, sketchB, skThetaB, results);
+
+    // same thing, but compact sketches
+    threeMethodsWithTheta(aNotB, sketchA.compact(), sketchB.compact(), skThetaB.compact(), results);
+  }
+
+  @Test
+  public void aNotBExactOverlap() {
+    UpdatableSketch<Double, DoubleSummary> sketchA = buildUpdatableTuple();
+    sketchA.update(1, 1.0);
+    sketchA.update(1, 1.0);
+    sketchA.update(2, 1.0);
+    sketchA.update(2, 1.0);
+
+    UpdatableSketch<Double, DoubleSummary> sketchB = buildUpdatableTuple();
+    sketchB.update(2, 1.0);
+    sketchB.update(2, 1.0);
+    sketchB.update(3, 1.0);
+    sketchB.update(3, 1.0);
+
+    UpdateSketch skThetaB = buildUpdateTheta();
+    skThetaB.update(2);
+    skThetaB.update(3);
+
+    AnotB<DoubleSummary> aNotB = new AnotB<>();
+    results.set(1, false, 1.0, 0.0, 2.0);
+    threeMethodsWithTheta(aNotB, sketchA, sketchB, skThetaB, results);
+  }
+
+  @Test
+  public void aNotBEstimationOverlap() {
+    UpdatableSketch<Double, DoubleSummary> sketchA = buildUpdatableTuple();
+    for (int i = 0; i < 8192; i++) {
+      sketchA.update(i, 1.0);
+    }
+
+    UpdatableSketch<Double, DoubleSummary> sketchB = buildUpdatableTuple();
+    for (int i = 0; i < 4096; i++) {
+      sketchB.update(i, 1.0);
+    }
+
+    UpdateSketch skThetaB = buildUpdateTheta();
+    for (int i = 0; i < 4096; i++) {
+      skThetaB.update(i);
+    }
+
+    AnotB<DoubleSummary> aNotB = new AnotB<>();
+    results.set(2124, false, 4096.0, 0.03, 1.0);
+    threeMethodsWithTheta(aNotB, sketchA, sketchB, skThetaB, results);
+
+    // same thing, but compact sketches
+    threeMethodsWithTheta(aNotB, sketchA.compact(), sketchB.compact(), skThetaB.compact(), results);
+  }
+
+  @Test
+  public void aNotBEstimationOverlapLargeB() {
+    UpdatableSketch<Double, DoubleSummary> sketchA = buildUpdatableTuple();
+    for (int i = 0; i < 10_000; i++) {
+      sketchA.update(i, 1.0);
+    }
+
+    UpdatableSketch<Double, DoubleSummary> sketchB = buildUpdatableTuple();
+    for (int i = 0; i < 100_000; i++) {
+      sketchB.update(i + 8000, 1.0);
+    }
+
+    UpdateSketch skThetaB = buildUpdateTheta();
+    for (int i = 0; i < 100_000; i++) {
+      skThetaB.update(i + 8000);
+    }
+
+    final int expected = 8_000;
+    AnotB<DoubleSummary> aNotB = new AnotB<>();
+    results.set(376, false, expected, 0.1, 1.0);
+    threeMethodsWithTheta(aNotB, sketchA, sketchB, skThetaB, results);
+
+    // same thing, but compact sketches
+    threeMethodsWithTheta(aNotB, sketchA.compact(), sketchB.compact(), skThetaB.compact(), results);
+  }
+
+}
diff --git a/src/test/java/org/apache/datasketches/tuple/adouble/UpdatableSketchWithDoubleSummaryTest.java b/src/test/java/org/apache/datasketches/tuple/adouble/UpdatableSketchWithDoubleSummaryTest.java
index e78e404..2d5e075 100644
--- a/src/test/java/org/apache/datasketches/tuple/adouble/UpdatableSketchWithDoubleSummaryTest.java
+++ b/src/test/java/org/apache/datasketches/tuple/adouble/UpdatableSketchWithDoubleSummaryTest.java
@@ -24,7 +24,6 @@ import static org.testng.Assert.assertEquals;
 import org.apache.datasketches.ResizeFactor;
 import org.apache.datasketches.SketchesArgumentException;
 import org.apache.datasketches.memory.Memory;
-import org.apache.datasketches.tuple.AnotB;
 import org.apache.datasketches.tuple.CompactSketch;
 import org.apache.datasketches.tuple.Intersection;
 import org.apache.datasketches.tuple.Sketch;
@@ -691,194 +690,6 @@ public class UpdatableSketchWithDoubleSummaryTest {
     }
   }
 
-  @Test
-  public void aNotBEmpty() {
-    AnotB<DoubleSummary> aNotB = new AnotB<>();
-    // calling getResult() before calling update() should yield an empty set
-    CompactSketch<DoubleSummary> result = aNotB.getResult();
-    Assert.assertEquals(result.getRetainedEntries(), 0);
-    Assert.assertTrue(result.isEmpty());
-    Assert.assertEquals(result.getEstimate(), 0.0);
-    Assert.assertEquals(result.getLowerBound(1), 0.0);
-    Assert.assertEquals(result.getUpperBound(1), 0.0);
-
-    aNotB.update(null, null);
-    result = aNotB.getResult();
-    Assert.assertEquals(result.getRetainedEntries(), 0);
-    Assert.assertTrue(result.isEmpty());
-    Assert.assertEquals(result.getEstimate(), 0.0);
-    Assert.assertEquals(result.getLowerBound(1), 0.0);
-    Assert.assertEquals(result.getUpperBound(1), 0.0);
-
-    UpdatableSketch<Double, DoubleSummary> sketch =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    aNotB.update(sketch, sketch);
-    result = aNotB.getResult();
-    Assert.assertEquals(result.getRetainedEntries(), 0);
-    Assert.assertTrue(result.isEmpty());
-    Assert.assertEquals(result.getEstimate(), 0.0);
-    Assert.assertEquals(result.getLowerBound(1), 0.0);
-    Assert.assertEquals(result.getUpperBound(1), 0.0);
-  }
-
-  @Test
-  public void aNotBEmptyA() {
-    UpdatableSketch<Double, DoubleSummary> sketchA =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-
-    UpdatableSketch<Double, DoubleSummary> sketchB =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    sketchB.update(1, 1.0);
-    sketchB.update(2, 1.0);
-
-    AnotB<DoubleSummary> aNotB = new AnotB<>();
-    aNotB.update(sketchA, sketchB);
-    CompactSketch<DoubleSummary> result = aNotB.getResult();
-    Assert.assertEquals(result.getRetainedEntries(), 0);
-    Assert.assertTrue(result.isEmpty());
-    Assert.assertEquals(result.getEstimate(), 0.0);
-    Assert.assertEquals(result.getLowerBound(1), 0.0);
-    Assert.assertEquals(result.getUpperBound(1), 0.0);
-  }
-
-  @Test
-  public void aNotBEmptyB() {
-    UpdatableSketch<Double, DoubleSummary> sketchA =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    sketchA.update(1, 1.0);
-    sketchA.update(2, 1.0);
-
-    UpdatableSketch<Double, DoubleSummary> sketchB =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-
-    AnotB<DoubleSummary> aNotB = new AnotB<>();
-    aNotB.update(sketchA, sketchB);
-    CompactSketch<DoubleSummary> result = aNotB.getResult();
-    Assert.assertEquals(result.getRetainedEntries(), 2);
-    Assert.assertFalse(result.isEmpty());
-    Assert.assertEquals(result.getEstimate(), 2.0);
-    Assert.assertEquals(result.getLowerBound(1), 2.0);
-    Assert.assertEquals(result.getUpperBound(1), 2.0);
-
-    // same thing, but compact sketches
-    aNotB.update(sketchA.compact(), sketchB.compact());
-    result = aNotB.getResult();
-    Assert.assertEquals(result.getRetainedEntries(), 2);
-    Assert.assertFalse(result.isEmpty());
-    Assert.assertEquals(result.getEstimate(), 2.0);
-    Assert.assertEquals(result.getLowerBound(1), 2.0);
-    Assert.assertEquals(result.getUpperBound(1), 2.0);
-  }
-
-  @Test
-  public void aNotBExactMode() {
-    UpdatableSketch<Double, DoubleSummary> sketchA =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    sketchA.update(1, 1.0);
-    sketchA.update(1, 1.0);
-    sketchA.update(2, 1.0);
-    sketchA.update(2, 1.0);
-
-    UpdatableSketch<Double, DoubleSummary> sketchB =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    sketchB.update(2, 1.0);
-    sketchB.update(2, 1.0);
-    sketchB.update(3, 1.0);
-    sketchB.update(3, 1.0);
-
-    AnotB<DoubleSummary> aNotB = new AnotB<>();
-    aNotB.update(sketchA, sketchB);
-    CompactSketch<DoubleSummary> result = aNotB.getResult();
-    Assert.assertEquals(result.getRetainedEntries(), 1);
-    Assert.assertFalse(result.isEmpty());
-    Assert.assertEquals(result.getEstimate(), 1.0);
-    Assert.assertEquals(result.getLowerBound(1), 1.0);
-    Assert.assertEquals(result.getUpperBound(1), 1.0);
-    SketchIterator<DoubleSummary> it = result.iterator();
-    Assert.assertTrue(it.next());
-    Assert.assertEquals(it.getSummary().getValue(), 2.0);
-    Assert.assertFalse(it.next());
-  }
-
-  @Test
-  public void aNotBEstimationMode() {
-    int key = 0;
-    UpdatableSketch<Double, DoubleSummary> sketchA =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    for (int i = 0; i < 8192; i++) {
-      sketchA.update(key++, 1.0);
-    }
-
-    key -= 4096; // overlap half of the entries
-    UpdatableSketch<Double, DoubleSummary> sketchB =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    for (int i = 0; i < 8192; i++) {
-      sketchB.update(key++, 1.0);
-    }
-
-    AnotB<DoubleSummary> aNotB = new AnotB<>();
-    aNotB.update(sketchA, sketchB);
-    CompactSketch<DoubleSummary> result = aNotB.getResult();
-    Assert.assertFalse(result.isEmpty());
-    // crude estimate of RSE(95%) = 2 / sqrt(result.getRetainedEntries())
-    Assert.assertEquals(result.getEstimate(), 4096.0, 4096 * 0.03);
-    Assert.assertTrue(result.getLowerBound(1) <= result.getEstimate());
-    Assert.assertTrue(result.getUpperBound(1) > result.getEstimate());
-    SketchIterator<DoubleSummary> it = result.iterator();
-    while (it.next()) {
-      Assert.assertEquals(it.getSummary().getValue(), 1.0);
-    }
-
-    // same thing, but compact sketches
-    aNotB.update(sketchA.compact(), sketchB.compact());
-    result = aNotB.getResult();
-    Assert.assertFalse(result.isEmpty());
-    // crude estimate of RSE(95%) = 2 / sqrt(result.getRetainedEntries())
-    Assert.assertEquals(result.getEstimate(), 4096.0, 4096 * 0.03);
-    Assert.assertTrue(result.getLowerBound(1) <= result.getEstimate());
-    Assert.assertTrue(result.getUpperBound(1) > result.getEstimate());
-    it = result.iterator();
-    while (it.next()) {
-      Assert.assertEquals(it.getSummary().getValue(), 1.0);
-    }
-  }
-
-  @Test
-  public void aNotBEstimationModeLargeB() {
-    int key = 0;
-    UpdatableSketch<Double, DoubleSummary> sketchA =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    for (int i = 0; i < 10000; i++) {
-      sketchA.update(key++, 1.0);
-    }
-
-    key -= 2000; // overlap
-    UpdatableSketch<Double, DoubleSummary> sketchB =
-        new UpdatableSketchBuilder<>(new DoubleSummaryFactory(mode)).build();
-    for (int i = 0; i < 100000; i++) {
-      sketchB.update(key++, 1.0);
-    }
-
-    final int expected = 10000 - 2000;
-    AnotB<DoubleSummary> aNotB = new AnotB<>();
-    aNotB.update(sketchA, sketchB);
-    CompactSketch<DoubleSummary> result = aNotB.getResult();
-    Assert.assertFalse(result.isEmpty());
-    // crude estimate of RSE(95%) = 2 / sqrt(result.getRetainedEntries())
-    Assert.assertEquals(result.getEstimate(), expected, expected * 0.1);
-    Assert.assertTrue(result.getLowerBound(1) <= result.getEstimate());
-    Assert.assertTrue(result.getUpperBound(1) > result.getEstimate());
-
-    // same thing, but compact sketches
-    aNotB.update(sketchA.compact(), sketchB.compact());
-    result = aNotB.getResult();
-    Assert.assertFalse(result.isEmpty());
-    // crude estimate of RSE(95%) = 2 / sqrt(result.getRetainedEntries())
-    Assert.assertEquals(result.getEstimate(), expected, expected * 0.1);
-    Assert.assertTrue(result.getLowerBound(1) <= result.getEstimate());
-    Assert.assertTrue(result.getUpperBound(1) > result.getEstimate());
-  }
-
   @Test(expectedExceptions = SketchesArgumentException.class)
   public void invalidSamplingProbability() {
     new UpdatableSketchBuilder<>
diff --git a/src/test/java/org/apache/datasketches/tuple/aninteger/IntegerSketchTest.java b/src/test/java/org/apache/datasketches/tuple/aninteger/IntegerSketchTest.java
index 34b586f..217b066 100644
--- a/src/test/java/org/apache/datasketches/tuple/aninteger/IntegerSketchTest.java
+++ b/src/test/java/org/apache/datasketches/tuple/aninteger/IntegerSketchTest.java
@@ -22,6 +22,7 @@ package org.apache.datasketches.tuple.aninteger;
 import static org.testng.Assert.assertEquals;
 
 import org.apache.datasketches.memory.Memory;
+import org.apache.datasketches.tuple.AnotB;
 import org.apache.datasketches.tuple.CompactSketch;
 import org.apache.datasketches.tuple.Intersection;
 import org.testng.annotations.Test;
@@ -39,8 +40,8 @@ public class IntegerSketchTest {
     IntegerSummary.Mode a1Mode = IntegerSummary.Mode.AlwaysOne;
     IntegerSketch a1Sk = new IntegerSketch(lgK, a1Mode);
     int m = 2 * K;
-    for (int key = 0; key < m; key++) {
-      a1Sk.update(key, 1);
+    for (int i = 0; i < m; i++) {
+      a1Sk.update(i, 1);
     }
     double est1 = a1Sk.getEstimate();
     Memory mem = Memory.wrap(a1Sk.toByteArray());
@@ -57,9 +58,9 @@ public class IntegerSketchTest {
     IntegerSketch a1Sk1 = new IntegerSketch(lgK, a1Mode);
     IntegerSketch a1Sk2 = new IntegerSketch(lgK, a1Mode);
     int m = 2 * K;
-    for (int key = 0; key < m; key++) {
-      a1Sk1.update(key, 1);
-      a1Sk2.update(key + (m/2), 1);
+    for (int i = 0; i < m; i++) {
+      a1Sk1.update(i, 1);
+      a1Sk2.update(i + (m/2), 1);
     }
     Intersection<IntegerSummary> inter =
         new Intersection<>(new IntegerSummarySetOperations(a1Mode, a1Mode));
@@ -70,6 +71,21 @@ public class IntegerSketchTest {
   }
 
   @Test
+  public void aNotBTest() {
+    int lgK = 4;
+    //int m = 2 * K;
+    IntegerSummary.Mode a1Mode = IntegerSummary.Mode.AlwaysOne;
+    IntegerSketch a1Sk1 = new IntegerSketch(lgK, a1Mode);
+    IntegerSketch a1Sk2 = null;//new IntegerSketch(lgK, a1Mode);
+    AnotB<IntegerSummary> anotb = new AnotB<>();
+    for (int i = 0; i < 5; i++) {
+      a1Sk1.update(i, 1);
+    }
+    anotb.update(a1Sk1, a1Sk2);
+    CompactSketch<IntegerSummary> cSk = anotb.getResult();
+  }
+
+  @Test
   public void checkMinMaxMode() {
     int lgK = 12;
     int K = 1 << lgK;
@@ -108,7 +124,7 @@ public class IntegerSketchTest {
    * @param o object to print
    */
   static void println(Object o) {
-    //System.out.println(o.toString()); //Disable
+    System.out.println(o.toString()); //Disable
   }
 
   /**
@@ -116,6 +132,6 @@ public class IntegerSketchTest {
    * @param args arguments
    */
   static void printf(String fmt, Object ... args) {
-    //System.out.printf(fmt, args); //Disable
+    System.out.printf(fmt, args); //Disable
   }
 }
diff --git a/src/test/java/org/apache/datasketches/tuple/arrayofdoubles/package-info.java b/src/test/java/org/apache/datasketches/tuple/arrayofdoubles/package-info.java
deleted file mode 100644
index 3810001..0000000
--- a/src/test/java/org/apache/datasketches/tuple/arrayofdoubles/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * @author Lee Rhodes
- */
-
-package org.apache.datasketches.tuple.arrayofdoubles;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org