/*
 * 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.theta;

import static java.lang.foreign.ValueLayout.JAVA_BYTE;
import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED;
import static org.apache.datasketches.theta.PreambleUtil.PREAMBLE_LONGS_BYTE;
import static org.apache.datasketches.theta.PreambleUtil.SER_VER_BYTE;
import static org.apache.datasketches.theta.ThetaSetOperation.CONST_PREAMBLE_LONGS;
import static org.apache.datasketches.theta.ThetaSetOperation.getMaxIntersectionBytes;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

import java.lang.foreign.MemorySegment;

import org.apache.datasketches.common.Family;
import org.apache.datasketches.common.SketchesArgumentException;
import org.apache.datasketches.common.SketchesReadOnlyException;
import org.apache.datasketches.common.SketchesStateException;
import org.apache.datasketches.common.Util;
import org.testng.annotations.Test;

/**
 * @author Lee Rhodes
 */
public class DirectIntersectionTest {
  private static final int PREBYTES = CONST_PREAMBLE_LONGS << 3; //24

  @Test
  public void checkExactIntersectionNoOverlap() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;

    final UpdatableThetaSketch usk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    final UpdatableThetaSketch usk2 = UpdatableThetaSketch.builder().setNominalEntries(k).build();

    for (int i=0; i<k/2; i++) {
      usk1.update(i);
    }
    for (int i=k/2; i<k; i++) {
      usk2.update(i);
    }

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    inter = ThetaSetOperation.builder().buildIntersection(iSeg);

    inter.intersect(usk1);
    inter.intersect(usk2);

    final long[] cache = inter.getCache(); //only applies to stateful
    assertEquals(cache.length, 32);

    CompactThetaSketch rsk1;
    final boolean ordered = true;

    assertTrue(inter.hasResult());
    rsk1 = inter.getResult(!ordered, null);
    assertEquals(rsk1.getEstimate(), 0.0);

    rsk1 = inter.getResult(ordered, null);
    assertEquals(rsk1.getEstimate(), 0.0);

    final int bytes = rsk1.getCompactBytes();
    final byte[] byteArray = new byte[bytes];
    final MemorySegment seg = MemorySegment.ofArray(byteArray);

    rsk1 = inter.getResult(!ordered, seg);
    assertEquals(rsk1.getEstimate(), 0.0);
    //executed twice to fully exercise the internal state machine
    rsk1 = inter.getResult(ordered, seg);
    assertEquals(rsk1.getEstimate(), 0.0);
  }

  @Test
  public void checkExactIntersectionFullOverlap() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;

    final UpdatableThetaSketch usk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    final UpdatableThetaSketch usk2 = UpdatableThetaSketch.builder().setNominalEntries(k).build();

    for (int i=0; i<k; i++) {
      usk1.update(i);
    }
    for (int i=0; i<k; i++) {
      usk2.update(i);
    }

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(usk1);
    inter.intersect(usk2);

    CompactThetaSketch rsk1;
    final boolean ordered = true;

    rsk1 = inter.getResult(!ordered, null);
    assertEquals(rsk1.getEstimate(), k);

    rsk1 = inter.getResult(ordered, null);
    assertEquals(rsk1.getEstimate(), k);

    final int bytes = rsk1.getCompactBytes();
    final byte[] byteArray = new byte[bytes];
    final MemorySegment seg = MemorySegment.ofArray(byteArray);

    rsk1 = inter.getResult(!ordered, seg); //executed twice to fully exercise the internal state machine
    assertEquals(rsk1.getEstimate(), k);

    rsk1 = inter.getResult(ordered, seg);
    assertEquals(rsk1.getEstimate(), k);
  }

  @Test
  public void checkIntersectionEarlyStop() {
    final int lgK = 10;
    final int k = 1<<lgK;
    final int u = 4*k;

    final UpdatableThetaSketch usk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    final UpdatableThetaSketch usk2 = UpdatableThetaSketch.builder().setNominalEntries(k).build();

    for (int i=0; i<u; i++) {
      usk1.update(i);
    }
    for (int i=u/2; i<u + u/2; i++) {
      usk2.update(i);
    }

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    final CompactThetaSketch csk1 = usk1.compact(true, null);
    final CompactThetaSketch csk2 = usk2.compact(true, null);

    final ThetaIntersection inter =
        ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(csk1);
    inter.intersect(csk2);

    final CompactThetaSketch rsk1 = inter.getResult(true, null);
    println(""+rsk1.getEstimate());
  }

  //Calling getResult on a virgin Intersect is illegal
  @Test(expectedExceptions = SketchesStateException.class)
  public void checkNoCall() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    assertFalse(inter.hasResult());
    inter.getResult(false, null);
  }

  @Test
  public void checkIntersectionNull() {
    final int lgK = 9;
    final int k = 1<<lgK;
    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);
    final ThetaIntersection inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    final UpdatableThetaSketch sk = null;
    try { inter.intersect(sk); fail(); }
    catch (final SketchesArgumentException e) { }

    try { inter.intersect(sk, sk); fail(); }
    catch (final SketchesArgumentException e) { }
  }


  @Test
  public void check1stCall() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;
    UpdatableThetaSketch sk;
    CompactThetaSketch rsk1;
    double est;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    //1st call = empty
    sk = UpdatableThetaSketch.builder().setNominalEntries(k).build(); //empty
    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk);
    rsk1 = inter.getResult(false, null);
    est = rsk1.getEstimate();
    assertEquals(est, 0.0, 0.0);
    println("Est: "+est); // = 0

    //1st call = valid and not empty
    sk = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    sk.update(1);
    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk);
    rsk1 = inter.getResult(false, null);
    est = rsk1.getEstimate();
    assertEquals(est, 1.0, 0.0);
    println("Est: "+est); // = 1
  }

  @Test
  public void check2ndCallAfterEmpty() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;
    UpdatableThetaSketch sk1, sk2;
    CompactThetaSketch comp1;
    double est;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    //1st call = empty
    sk1 = UpdatableThetaSketch.builder().build(); //empty
    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);
    //2nd call = empty
    sk2 = UpdatableThetaSketch.builder().build(); //empty
    inter.intersect(sk2);
    comp1 = inter.getResult(false, null);
    est = comp1.getEstimate();
    assertEquals(est, 0.0, 0.0);
    println("Est: "+est);

    //1st call = empty
    sk1 = UpdatableThetaSketch.builder().build(); //empty
    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);
    //2nd call = valid and not empty
    sk2 = UpdatableThetaSketch.builder().build();
    sk2.update(1);
    inter.intersect(sk2);
    comp1 = inter.getResult(false, null);
    est = comp1.getEstimate();
    assertEquals(est, 0.0, 0.0);
    println("Est: "+est);
  }

  @Test
  public void check2ndCallAfterValid() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;
    UpdatableThetaSketch sk1, sk2;
    CompactThetaSketch comp1;
    double est;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    //1st call = valid
    sk1 = UpdatableThetaSketch.builder().build();
    sk1.update(1);
    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);
    //2nd call = empty
    sk2 = UpdatableThetaSketch.builder().build(); //empty
    inter.intersect(sk2);
    comp1 = inter.getResult(false, null);
    est = comp1.getEstimate();
    assertEquals(est, 0.0, 0.0);
    println("Est: "+est);

    //1st call = valid
    sk1 = UpdatableThetaSketch.builder().build();
    sk1.update(1);
    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);
    //2nd call = valid intersecting
    sk2 = UpdatableThetaSketch.builder().build(); //empty
    sk2.update(1);
    inter.intersect(sk2);
    comp1 = inter.getResult(false, null);
    est = comp1.getEstimate();
    assertEquals(est, 1.0, 0.0);
    println("Est: "+est);

    //1st call = valid
    sk1 = UpdatableThetaSketch.builder().build();
    sk1.update(1);
    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);
    //2nd call = valid not intersecting
    sk2 = UpdatableThetaSketch.builder().build(); //empty
    sk2.update(2);
    inter.intersect(sk2);
    comp1 = inter.getResult(false, null);
    est = comp1.getEstimate();
    assertEquals(est, 0.0, 0.0);
    println("Est: "+est);
  }

  @Test
  public void checkEstimatingIntersect() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;
    UpdatableThetaSketch sk1, sk2;
    CompactThetaSketch comp1;
    double est;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    //1st call = valid
    sk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    for (int i=0; i<2*k; i++)
     {
      sk1.update(i);  //est mode
    }
    println("sk1: "+sk1.getEstimate());

    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);

    //2nd call = valid intersecting
    sk2 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    for (int i=0; i<2*k; i++)
     {
      sk2.update(i);  //est mode
    }
    println("sk2: "+sk2.getEstimate());

    inter.intersect(sk2);
    comp1 = inter.getResult(false, null);
    est = comp1.getEstimate();
    assertTrue(est > k);
    println("Est: "+est);
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkOverflow() {
    final int lgK = 9; //512
    final int k = 1<<lgK;
    ThetaIntersection inter;
    UpdatableThetaSketch sk1;
    final double est;

    final int reqBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[reqBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    //1st call = valid
    sk1 = UpdatableThetaSketch.builder().setNominalEntries(2 * k).build(); // bigger sketch
    for (int i=0; i<4*k; i++)
     {
      sk1.update(i);  //force est mode
    }
    println("sk1est: "+sk1.getEstimate());
    println("sk1cnt: "+sk1.getRetainedEntries(true));

    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);
  }

  @Test
  public void checkHeapify() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter;
    UpdatableThetaSketch sk1, sk2;
    CompactThetaSketch comp1, comp2;
    double est, est2;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    //1st call = valid
    sk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    for (int i=0; i<2*k; i++)
     {
      sk1.update(i);  //est mode
    }
    println("sk1: "+sk1.getEstimate());

    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk1);

    //2nd call = valid intersecting
    sk2 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    for (int i=0; i<2*k; i++)
     {
      sk2.update(i);  //est mode
    }
    println("sk2: "+sk2.getEstimate());

    inter.intersect(sk2);
    comp1 = inter.getResult(false, null);
    est = comp1.getEstimate();
    assertTrue(est > k);
    println("Est: "+est);

    final byte[] byteArray = inter.toByteArray();
    final MemorySegment seg = MemorySegment.ofArray(byteArray);
    final ThetaIntersection inter2 = (ThetaIntersection) ThetaSetOperation.heapify(seg);
    comp2 = inter2.getResult(false, null);
    est2 = comp2.getEstimate();
    println("Est2: "+est2);
  }

  /**
   * This proves that the hash of 7 is &lt; 0.5. This fact will be used in other tests involving P.
   */
  @Test
  public void checkPreject() {
    final UpdatableThetaSketch sk = UpdatableThetaSketch.builder().setP((float) .5).build();
    sk.update(7);
    assertEquals(sk.getRetainedEntries(), 0);
  }

  @Test
  public void checkWrapVirginEmpty() {
    final int lgK = 5;
    final int k = 1 << lgK;
    ThetaIntersection inter1, inter2;
    UpdatableThetaSketch sk1;

    final int segBytes = getMaxIntersectionBytes(k);
    MemorySegment iSeg = MemorySegment.ofArray(new byte[segBytes]);

    inter1 = ThetaSetOperation.builder().buildIntersection(iSeg); //virgin off-heap
    inter2 = ThetaIntersection.wrap(iSeg); //virgin off-heap, identical to inter1
    //both in virgin state, empty = false
    //note: both inter1 and inter2 are tied to the same MemorySegment,
    // so an intersect to one also affects the other.  Don't do what I do!
    assertFalse(inter1.hasResult());
    assertFalse(inter2.hasResult());

    //This constructs a sketch with 0 entries and theta < 1.0
    sk1 = UpdatableThetaSketch.builder().setP((float) .5).setNominalEntries(k).build();
    sk1.update(7); //will be rejected by P, see proof above.

    //A virgin intersection (empty = false) intersected with a not-empty zero cache sketch
    //remains empty = false!
    inter1.intersect(sk1);
    assertFalse(inter1.isEmpty());
    assertTrue(inter1.hasResult());
    //note that inter2 is not independent
    assertFalse(inter2.isEmpty());
    assertTrue(inter2.hasResult());

    //test the path via toByteArray, now in a different state
    iSeg = MemorySegment.ofArray(inter1.toByteArray());
    inter2 = ThetaIntersection.wrap(iSeg);
    assertTrue(inter2.hasResult()); //still true

    //test the compaction path
    final CompactThetaSketch comp = inter2.getResult(true, null);
    assertEquals(comp.getRetainedEntries(false), 0);
    assertFalse(comp.isEmpty());
  }

  @Test
  public void checkWrapNullEmpty2() {
    final int lgK = 5;
    final int k = 1<<lgK;
    ThetaIntersection inter1, inter2;
    UpdatableThetaSketch sk1;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    inter1 = ThetaSetOperation.builder().buildIntersection(iSeg); //virgin
    inter2 = ThetaIntersection.wrap(iSeg);
    //both in virgin state, empty = false
    assertFalse(inter1.hasResult());
    assertFalse(inter2.hasResult());

    sk1 = UpdatableThetaSketch.builder().setP((float) .005).setFamily(Family.QUICKSELECT).setNominalEntries(k).build();
    sk1.update(1); //very unlikely to go into cache due to p.
    //A virgin intersection (empty = false) intersected with a not-empty zero cache sketch
    //remains empty = false.

    inter1.intersect(sk1);
    inter2 = ThetaIntersection.wrap(iSeg);
    assertTrue(inter1.hasResult());
    assertTrue(inter2.hasResult());
    final CompactThetaSketch comp = inter2.getResult(true, null);
    assertEquals(comp.getRetainedEntries(false), 0);
    assertFalse(comp.isEmpty());
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkSizeLowerLimit() {
    final int k = 8;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    ThetaSetOperation.builder().buildIntersection(iSeg);
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkSizedTooSmall() {
    final int lgK = 5;
    final int k = 1<<lgK;
    final int u = 4*k;

    final int segBytes = getMaxIntersectionBytes(k/2);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    final UpdatableThetaSketch usk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();

    for (int i=0; i<u; i++) {
      usk1.update(i);
    }

    final CompactThetaSketch csk1 = usk1.compact(true, null);

    final ThetaIntersection inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(csk1);
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkBadPreambleLongs() {
    final int k = 32;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    final ThetaIntersection inter1 = ThetaSetOperation.builder().buildIntersection(iSeg); //virgin
    final byte[] byteArray = inter1.toByteArray();
    final MemorySegment seg = MemorySegment.ofArray(byteArray);
    //corrupt:
    seg.set(JAVA_BYTE, PREAMBLE_LONGS_BYTE, (byte) 2);//RF not used = 0
    ThetaIntersection.wrap(seg);
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkBadSerVer() {
    final int k = 32;
    ThetaIntersection inter1;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    inter1 = ThetaSetOperation.builder().buildIntersection(iSeg); //virgin
    final byte[] byteArray = inter1.toByteArray();
    final MemorySegment seg = MemorySegment.ofArray(byteArray);
    //corrupt:
    seg.set(JAVA_BYTE, SER_VER_BYTE, (byte) 2);
    ThetaIntersection.wrap(seg); //throws in ThetaSetOperation
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkIncorrectWrap() {
    final int k = 32;
    ThetaUnion union;

    union = ThetaSetOperation.builder().setNominalEntries(k).buildUnion();
    final byte[] byteArray = union.toByteArray();
    final MemorySegment seg = MemorySegment.ofArray(byteArray);
    ThetaIntersection.wrap(seg); //wrong sketch Family
  }

  @Test
  public void checkWrap() {
    final int lgK = 9;
    final int k = 1<<lgK;
    ThetaIntersection inter, inter2, inter3;
    UpdatableThetaSketch sk1, sk2;
    CompactThetaSketch resultComp1, resultComp2;
    double est, est2;

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr1 = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr1);

    //1st call = valid
    sk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    for (int i=0; i<2*k; i++)
     {
      sk1.update(i);  //est mode
    }
    final CompactThetaSketch compSkIn1 = sk1.compact(true, null);
    println("compSkIn1: "+compSkIn1.getEstimate());

    inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(compSkIn1);

    final byte[] segArr2 = inter.toByteArray();
    final MemorySegment srcSeg = MemorySegment.ofArray(segArr2);
    inter2 = ThetaIntersection.wrap(srcSeg);

    //2nd call = valid intersecting
    sk2 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    for (int i=0; i<2*k; i++)
     {
      sk2.update(i);  //est mode
    }
    final CompactThetaSketch compSkIn2 = sk2.compact(true, null);
    println("sk2: "+compSkIn2.getEstimate());

    inter2.intersect(compSkIn2);
    resultComp1 = inter2.getResult(false, null);
    est = resultComp1.getEstimate();
    assertTrue(est > k);
    println("Est: "+est);

    final byte[] segArr3 = inter2.toByteArray();
    final MemorySegment srcSeg2 = MemorySegment.ofArray(segArr3);
    inter3 = ThetaIntersection.wrap(srcSeg2);
    resultComp2 = inter3.getResult(false, null);
    est2 = resultComp2.getEstimate();
    println("Est2: "+est2);

    inter.reset();
    inter2.reset();
    inter3.reset();
  }

  @Test
  public void checkDefaultMinSize() {
    final int k = 32;
    final MemorySegment seg = MemorySegment.ofArray(new byte[k*8 + PREBYTES]);
    ThetaIntersectionImpl.initNewDirectInstance(Util.DEFAULT_UPDATE_SEED, seg);
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkExceptionMinSize() {
    final int k = 16;
    final MemorySegment seg = MemorySegment.ofArray(new byte[k*8 + PREBYTES]);
    ThetaIntersectionImpl.initNewDirectInstance(Util.DEFAULT_UPDATE_SEED, seg);
  }

  @Test
  public void checkGetResult() {
    final int k = 1024;
    final UpdatableThetaSketch sk = UpdatableThetaSketch.builder().build();

    final int segBytes = getMaxIntersectionBytes(k);
    final byte[] segArr = new byte[segBytes];
    final MemorySegment iSeg = MemorySegment.ofArray(segArr);

    final ThetaIntersection inter = ThetaSetOperation.builder().buildIntersection(iSeg);
    inter.intersect(sk);
    final CompactThetaSketch csk = inter.getResult();
    assertEquals(csk.getCompactBytes(), 8);
  }

  @Test
  public void checkFamily() {
    //cheap trick
    final int k = 16;
    final MemorySegment seg = MemorySegment.ofArray(new byte[k*16 + PREBYTES]);
    final ThetaIntersectionImpl impl = ThetaIntersectionImpl.initNewDirectInstance(Util.DEFAULT_UPDATE_SEED, seg);
    assertEquals(impl.getFamily(), Family.INTERSECTION);
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkExceptions1() {
    final int k = 16;
    final MemorySegment seg = MemorySegment.ofArray(new byte[k*16 + PREBYTES]);
    ThetaIntersectionImpl.initNewDirectInstance(Util.DEFAULT_UPDATE_SEED, seg);
    //corrupt SerVer
    seg.set(JAVA_BYTE, PreambleUtil.SER_VER_BYTE, (byte) 2);
    ThetaIntersectionImpl.wrapInstance(seg, Util.DEFAULT_UPDATE_SEED, false);
  }

  @Test(expectedExceptions = SketchesArgumentException.class)
  public void checkExceptions2() {
    final int k = 16;
    final MemorySegment seg = MemorySegment.ofArray(new byte[k*16 + PREBYTES]);
    ThetaIntersectionImpl.initNewDirectInstance(Util.DEFAULT_UPDATE_SEED, seg);
    //seg now has non-empty intersection
    //corrupt empty and CurCount
    Util.setBits(seg, PreambleUtil.FLAGS_BYTE, (byte) PreambleUtil.EMPTY_FLAG_MASK);
    seg.set(JAVA_INT_UNALIGNED, PreambleUtil.RETAINED_ENTRIES_INT, 2);
    ThetaIntersectionImpl.wrapInstance(seg, Util.DEFAULT_UPDATE_SEED, false);
  }

  //Check Alex's bug intersecting 2 direct full sketches with only overlap of 2
  //
  @Test
  public void checkOverlappedDirect() {
    final int k = 1 << 4;
    final int segBytes = 2*k*16 +PREBYTES; //plenty of room
    final UpdatableThetaSketch sk1 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    final UpdatableThetaSketch sk2 = UpdatableThetaSketch.builder().setNominalEntries(k).build();
    for (int i=0; i<k; i++) {
      sk1.update(i);
      sk2.update(k-2 +i); //overlap by 2
    }
    final MemorySegment segIn1 = MemorySegment.ofArray(new byte[segBytes]);
    final MemorySegment segIn2 = MemorySegment.ofArray(new byte[segBytes]);
    final MemorySegment segInter = MemorySegment.ofArray(new byte[segBytes]);
    final MemorySegment segComp = MemorySegment.ofArray(new byte[segBytes]);
    final CompactThetaSketch csk1 = sk1.compact(true, segIn1);
    final CompactThetaSketch csk2 = sk2.compact(true, segIn2);
    final ThetaIntersection inter = ThetaSetOperation.builder().buildIntersection(segInter);
    inter.intersect(csk1);
    inter.intersect(csk2);
    final CompactThetaSketch cskOut = inter.getResult(true, segComp);
    assertEquals(cskOut.getEstimate(), 2.0, 0.0);

    final ThetaIntersection interRO = (ThetaIntersection) ThetaSetOperation.wrap(segInter.asReadOnly());
    try {
      interRO.intersect(sk1, sk2);
      fail();
    } catch (final SketchesReadOnlyException e) { }
    try {
      interRO.reset();
      fail();
    } catch (final SketchesReadOnlyException e) { }
  }

  @Test
  public void printlnTest() {
    println("PRINTING: "+this.getClass().getName());
  }

  /**
   * @param s value to print
   */
  static void println(final String s) {
    //System.out.println(s); //disable here
  }

}
