package simdbitset;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public final class SimdBitSetTest {

  @Nested
  public class Q1 {

    @Test
    public void andSameCapacityAllZeros() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();

      var result = a.and(b);

      assertEquals(0, result.bitCount());
    }

    @Test
    public void andSameCapacityProducesCorrectBits() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(1);
      a.set(3);
      b.set(1);
      b.set(3);
      b.set(5);

      var result = a.and(b);

      assertTrue(result.get(1));
      assertTrue(result.get(3));
      assertFalse(result.get(0));
      assertFalse(result.get(5));
    }

    @Test
    public void andSameCapacityNoOverlap() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(2);
      b.set(1);
      b.set(3);

      var result = a.and(b);

      assertEquals(0, result.bitCount());
    }

    @Test
    public void andShorterLeft() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      b.set(0);
      b.set(63);

      var result = a.and(b);

      assertTrue(result.get(0));
      assertFalse(result.get(63));
    }

    @Test
    public void andShorterRight() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(63);
      b.set(0);

      var result = a.and(b);

      assertTrue(result.get(0));
      assertFalse(result.get(63));
    }

    @Test
    public void andAcrossWordBoundary() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(31);
      a.set(32);
      b.set(31);
      b.set(32);

      var result = a.and(b);

      assertTrue(result.get(31));
      assertTrue(result.get(32));
    }

    @Test
    public void andMediumCapacityVectorizedPath() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(511);
      a.set(1023);
      b.set(0);
      b.set(511);
      b.set(1023);

      var result = a.and(b);

      assertTrue(result.get(0));
      assertTrue(result.get(511));
      assertTrue(result.get(1023));
      assertEquals(3, result.bitCount());
    }

    @Test
    public void andLargeCapacityVectorizedPath() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      for(var i = 0; i < 1_000_000; i++) {
        a.set(i);
        b.set(i);
      }

      var result = a.and(b);

      assertEquals(1_000_000, result.bitCount());
      assertEquals(a, result);
    }



    @Test
    public void andNullThrows() {
      var a = new SimdBitSet();
      assertThrows(NullPointerException.class, () -> a.and(null));
    }
  }


  /*@Nested
  public class Q2 {

    @Test
    public void andSameCapacityAllZeros() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();

      var result = a.parallelAnd(b);

      assertEquals(0, result.bitCount());
    }

    @Test
    public void parallelAndSameCapacityProducesCorrectBits() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(1);
      a.set(3);
      b.set(1);
      b.set(3);
      b.set(5);

      var result = a.parallelAnd(b);

      assertTrue(result.get(1));
      assertTrue(result.get(3));
      assertFalse(result.get(0));
      assertFalse(result.get(5));
    }

    @Test
    public void parallelAndSameCapacityNoOverlap() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(2);
      b.set(1);
      b.set(3);

      var result = a.parallelAnd(b);

      assertEquals(0, result.bitCount());
    }

    @Test
    public void parallelAndShorterLeft() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      b.set(0);
      b.set(63);

      var result = a.parallelAnd(b);

      assertTrue(result.get(0));
      assertFalse(result.get(63));
    }

    @Test
    public void parallelAndShorterRight() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(63);
      b.set(0);

      var result = a.parallelAnd(b);

      assertTrue(result.get(0));
      assertFalse(result.get(63));
    }

    @Test
    public void parallelAndAcrossWordBoundary() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(31);
      a.set(32);
      b.set(31);
      b.set(32);

      var result = a.parallelAnd(b);

      assertTrue(result.get(31));
      assertTrue(result.get(32));
    }

    @Test
    public void parallelAndMediumCapacityVectorizedPath() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(511);
      a.set(1023);
      b.set(0);
      b.set(511);
      b.set(1023);

      var result = a.parallelAnd(b);

      assertTrue(result.get(0));
      assertTrue(result.get(511));
      assertTrue(result.get(1023));
      assertEquals(3, result.bitCount());
    }

    @Test
    public void parallelAndLargeCapacityVectorizedPath() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      for(var i = 0; i < 1_000_000; i++) {
        a.set(i);
        b.set(i);
      }

      var result = a.parallelAnd(b);

      assertEquals(1_000_000, result.bitCount());
      assertEquals(a, result);
    }



    @Test
    public void parallelAndNullThrows() {
      var a = new SimdBitSet();
      assertThrows(NullPointerException.class, () -> a.and(null));
    }
  }


  @Nested
  public class Q4 {

    @Test
    public void bitCountEmpty() {
      var a = new SimdBitSet();
      assertEquals(0, a.bitCount());
    }

    @Test
    public void bitCountSingleBit() {
      var a = new SimdBitSet();
      a.set(0);
      assertEquals(1, a.bitCount());
    }

    @Test
    public void bitCountMultipleBits() {
      var a = new SimdBitSet();
      a.set(0);
      a.set(1);
      a.set(5);
      assertEquals(3, a.bitCount());
    }

    @Test
    public void bitCountAcrossWordBoundary() {
      var a = new SimdBitSet();
      a.set(31);
      a.set(32);
      assertEquals(2, a.bitCount());
    }

    @Test
    public void bitCountHighBit() {
      var a = new SimdBitSet();
      a.set(1023);
      assertEquals(1, a.bitCount());
    }

    @Test
    public void bitCountSettingSameBitTwiceCountsOnce() {
      var a = new SimdBitSet();
      a.set(7);
      a.set(7);
      assertEquals(1, a.bitCount());
    }
  }


  @Nested
  public class Q5 {

    @Test
    public void orSameCapacityAllZeros() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();

      var result = a.or(b);

      assertEquals(0, result.bitCount());
    }

    @Test
    public void orSameCapacityProducesCorrectBits() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(2);
      b.set(1);
      b.set(2);

      var result = a.or(b);

      assertTrue(result.get(0));
      assertTrue(result.get(1));
      assertTrue(result.get(2));
      assertFalse(result.get(3));
    }

    @Test
    public void orSameCapacityDisjointSets() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(10);
      b.set(20);
      b.set(30);

      var result = a.or(b);

      assertTrue(result.get(0));
      assertTrue(result.get(10));
      assertTrue(result.get(20));
      assertTrue(result.get(30));
    }

    @Test
    public void orShorterLeft() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      b.set(0);
      b.set(63);

      var result = a.or(b);

      assertTrue(result.get(0));
      assertTrue(result.get(63));
    }

    @Test
    public void orShorterRight() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(63);
      b.set(0);

      var result = a.or(b);

      assertTrue(result.get(0));
      assertTrue(result.get(63));
    }

    @Test
    public void orTailNotCorrupted() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      b.set(200);

      var result = a.or(b);

      assertTrue(result.get(200));
      assertFalse(result.get(199));
      assertFalse(result.get(201));
    }

    @Test
    public void orAcrossWordBoundary() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(31);
      b.set(32);

      var result = a.or(b);

      assertTrue(result.get(31));
      assertTrue(result.get(32));
    }

    @Test
    public void orMediumCapacityVectorizedPath() {
      var a = new SimdBitSet();
      var b = new SimdBitSet();
      a.set(0);
      a.set(500);
      b.set(500);
      b.set(1023);

      var result = a.or(b);

      assertTrue(result.get(0));
      assertTrue(result.get(500));
      assertTrue(result.get(1023));
      assertEquals(3, result.bitCount());
    }

    @Test
    public void orNullThrows() {
      var a = new SimdBitSet();
      assertThrows(NullPointerException.class, () -> a.or(null));
    }
  }*/
}