private static final HllSketchImpl unionImpl(final HllSketchImpl incomingImpl,
      final HllSketchImpl gadgetImpl, final int lgMaxK) {
    assert gadgetImpl.getTgtHllType() == HLL_8;
    HllSketchImpl srcImpl = incomingImpl; //default
    HllSketchImpl dstImpl = gadgetImpl; //default
    if ((incomingImpl == null) || incomingImpl.isEmpty()) { return gadgetImpl; }

    final int hi2bits = (gadgetImpl.isEmpty()) ? 3 : gadgetImpl.getCurMode().ordinal();
    final int lo2bits = incomingImpl.getCurMode().ordinal();

    final int sw = (hi2bits << 2) | lo2bits;

    switch (sw) {
      case 0: { //src: LIST, gadget: LIST
        final PairIterator srcItr = srcImpl.iterator(); //LIST
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        //whichever is True wins:
        dstImpl.putOutOfOrderFlag(dstImpl.isOutOfOrderFlag() | srcImpl.isOutOfOrderFlag());
        break;
      }
      case 1: { //src: SET, gadget: LIST
        //consider a swap here
        final PairIterator srcItr = srcImpl.iterator(); //SET
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(true); //SET oooFlag is always true
        break;
      }
      case 2: { //src: HLL, gadget: LIST
        //swap so that src is gadget-LIST, tgt is HLL
        //use lgMaxK because LIST has effective K of 2^26
        srcImpl = gadgetImpl;
        dstImpl = copyOrDownsampleHll(incomingImpl, lgMaxK);
        final PairIterator srcItr = srcImpl.iterator();
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        //whichever is True wins:
        dstImpl.putOutOfOrderFlag(srcImpl.isOutOfOrderFlag() | dstImpl.isOutOfOrderFlag());
        break;
      }
      case 4: { //src: LIST, gadget: SET
        final PairIterator srcItr = srcImpl.iterator(); //LIST
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(true); //SET oooFlag is always true
        break;
      }
      case 5: { //src: SET, gadget: SET
        final PairIterator srcItr = srcImpl.iterator(); //SET
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(true); //SET oooFlag is always true
        break;
      }
      case 6: { //src: HLL, gadget: SET
        //swap so that src is gadget-SET, tgt is HLL
        //use lgMaxK because LIST has effective K of 2^26
        srcImpl = gadgetImpl;
        dstImpl = copyOrDownsampleHll(incomingImpl, lgMaxK);
        final PairIterator srcItr = srcImpl.iterator(); //LIST
        assert dstImpl.getCurMode() == HLL;
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(true); //merging SET into non-empty HLL -> true
        break;
      }
      case 8: { //src: LIST, gadget: HLL
        assert dstImpl.getCurMode() == HLL;
        final PairIterator srcItr = srcImpl.iterator(); //LIST
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        //whichever is True wins:
        dstImpl.putOutOfOrderFlag(dstImpl.isOutOfOrderFlag() | srcImpl.isOutOfOrderFlag());
        break;
      }
      case 9: { //src: SET, gadget: HLL
        assert dstImpl.getCurMode() == HLL;
        final PairIterator srcItr = srcImpl.iterator(); //SET
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(true); //merging SET into existing HLL -> true
        break;
      }
      case 10: { //src: HLL, gadget: HLL
        final int srcLgK = srcImpl.getLgConfigK();
        final int dstLgK = dstImpl.getLgConfigK();
        if ((srcLgK < dstLgK) || (dstImpl.getTgtHllType() != HLL_8)) {
          dstImpl = copyOrDownsampleHll(dstImpl, min(dstLgK, srcLgK)); //TODO Fix for off-heap
        }
        final PairIterator srcItr = srcImpl.iterator(); //HLL
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(true); //union of two HLL modes is always true
        break;
      }
      case 12: { //src: LIST, gadget: empty
        final PairIterator srcItr = srcImpl.iterator(); //LIST
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(srcImpl.isOutOfOrderFlag()); //whatever source is
        break;
      }
      case 13: { //src: SET, gadget: empty
        final PairIterator srcItr = srcImpl.iterator(); //SET
        while (srcItr.nextValid()) {
          dstImpl = dstImpl.couponUpdate(srcItr.getPair()); //assignment required
        }
        dstImpl.putOutOfOrderFlag(true); //SET oooFlag is always true
        break;
      }
      case 14: { //src: HLL, gadget: empty
        dstImpl = copyOrDownsampleHll(srcImpl, lgMaxK);
        dstImpl.putOutOfOrderFlag(srcImpl.isOutOfOrderFlag()); //whatever source is.
        break;
      }
    }
    if (gadgetImpl.isMemory() && !dstImpl.isMemory()) {
      //dstImpl is on heap, gadget is Memory; we have to put dstImpl back into the gadget
      final WritableMemory gadgetWmem = gadgetImpl.getWritableMemory();
      assert gadgetWmem != null;
      final int bytes =
          HllSketch.getMaxUpdatableSerializationBytes(dstImpl.getLgConfigK(), HLL_8);
      gadgetWmem.clear(0, bytes);
      final byte[] dstByteArr = dstImpl.toUpdatableByteArray();
      gadgetWmem.putByteArray(0, dstByteArr, 0, dstByteArr.length);
      dstImpl = HllSketch.writableWrap(gadgetWmem).hllSketchImpl;
    }
    return dstImpl;
  }
private static final HllSketchImpl unionImpl ( final HllSketchImpl incomingImpl , final HllSketchImpl gadgetImpl , final int lgMaxK ) { assert gadgetImpl . getTgtHllType ( ) == HLL_8 ; HllSketchImpl srcImpl = incomingImpl ; HllSketchImpl dstImpl = gadgetImpl ; if ( ( incomingImpl == null ) || incomingImpl . isEmpty ( ) ) { return gadgetImpl ; } final int hi2bits = ( gadgetImpl . isEmpty ( ) ) ? 3 : gadgetImpl . getCurMode ( ) . ordinal ( ) ; final int lo2bits = incomingImpl . getCurMode ( ) . ordinal ( ) ; final int sw = ( hi2bits << 2 ) | lo2bits ; switch ( sw ) { case 0 : { final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( dstImpl . isOutOfOrderFlag ( ) | srcImpl . isOutOfOrderFlag ( ) ) ; break ; } case 1 : { final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( true ) ; break ; } case 2 : { srcImpl = gadgetImpl ; dstImpl = copyOrDownsampleHll ( incomingImpl , lgMaxK ) ; final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( srcImpl . isOutOfOrderFlag ( ) | dstImpl . isOutOfOrderFlag ( ) ) ; break ; } case 4 : { final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( true ) ; break ; } case 5 : { final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( true ) ; break ; } case 6 : { srcImpl = gadgetImpl ; dstImpl = copyOrDownsampleHll ( incomingImpl , lgMaxK ) ; final PairIterator srcItr = srcImpl . iterator ( ) ; assert dstImpl . getCurMode ( ) == HLL ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( true ) ; break ; } case 8 : { assert dstImpl . getCurMode ( ) == HLL ; final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( dstImpl . isOutOfOrderFlag ( ) | srcImpl . isOutOfOrderFlag ( ) ) ; break ; } case 9 : { assert dstImpl . getCurMode ( ) == HLL ; final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( true ) ; break ; } case 10 : { final int srcLgK = srcImpl . getLgConfigK ( ) ; final int dstLgK = dstImpl . getLgConfigK ( ) ; if ( ( srcLgK < dstLgK ) || ( dstImpl . getTgtHllType ( ) != HLL_8 ) ) { dstImpl = copyOrDownsampleHll ( dstImpl , min ( dstLgK , srcLgK ) ) ; } final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( true ) ; break ; } case 12 : { final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( srcImpl . isOutOfOrderFlag ( ) ) ; break ; } case 13 : { final PairIterator srcItr = srcImpl . iterator ( ) ; while ( srcItr . nextValid ( ) ) { dstImpl = dstImpl . couponUpdate ( srcItr . getPair ( ) ) ; } dstImpl . putOutOfOrderFlag ( true ) ; break ; } case 14 : { dstImpl = copyOrDownsampleHll ( srcImpl , lgMaxK ) ; dstImpl . putOutOfOrderFlag ( srcImpl . isOutOfOrderFlag ( ) ) ; break ; } } if ( gadgetImpl . isMemory ( ) && ! dstImpl . isMemory ( ) ) { final WritableMemory gadgetWmem = gadgetImpl . getWritableMemory ( ) ; assert gadgetWmem != null ; final int bytes = HllSketch . getMaxUpdatableSerializationBytes ( dstImpl . getLgConfigK ( ) , HLL_8 ) ; gadgetWmem . clear ( 0 , bytes ) ; final byte [ ] dstByteArr = dstImpl . toUpdatableByteArray ( ) ; gadgetWmem . putByteArray ( 0 , dstByteArr , 0 , dstByteArr . length ) ; dstImpl = HllSketch . writableWrap ( gadgetWmem ) . hllSketchImpl ; } return dstImpl ; }
Union the given source and destination sketches. This static method examines the state of
the current internal gadget and the incoming sketch and determines the optimum way to
perform the union. This may involve swapping, down-sampling, transforming, and / or
copying one of the arguments and may completely replace the internals of the union.

@param incomingImpl the given incoming sketch, which may not be modified.
@param gadgetImpl the given gadget sketch, which must have a target of HLL_8 and may be
modified.
@param lgMaxK the maximum value of log2 K for this union.
@return the union of the two sketches in the form of the internal HllSketchImpl, which for
the union is always in HLL_8 form.