@Test
public void testClassMapping() {
    Collection<Class<?>> interfaces = new AnnotatedInterfaceStrategy().withClassMapping(false).getInterfaces(GenericTypeReflector.annotate(Child.class)).stream().map(inter -> ClassUtils.getRawType(inter.getType())).collect(Collectors.toList());
    assertEquals(5, interfaces.size());
    assertTrue(interfaces.containsAll(Arrays.asList(Zero.class, One.class, Two.class, Three.class, Four.class)));
}

public AbstractInterfaceMappingStrategy withClassMapping(boolean mapClasses) {
    this.mapClasses = mapClasses;
    return this;
}


@Test
public void testAllowedInvalidPaginationArguments() {
    new TestSchemaGenerator().withRelayConnectionCheckRelaxed().withOperationsFromSingleton(new InvalidArgsPagingService()).generate();
    new TestSchemaGenerator().withRelayConnectionCheckRelaxed().withOperationsFromSingleton(new MissingArgsPagingService()).generate();
}

public GraphQLSchemaGenerator withRelayConnectionCheckRelaxed() {
    this.relayMappingConfig.strictConnectionSpec = false;
    return this;
}


@Test(expected = IllegalStateException.class)
public void testNoMappers() {
    GraphQLSchemaGenerator generator = new GraphQLSchemaGenerator().withOperationsFromSingleton(new Dummy()).withTypeMappers((conf, mappers) -> Collections.emptyList());
    generator.generate();
}

public GraphQLSchemaGenerator withTypeMappers(ExtensionProvider<TypeMapper> provider) {
    this.typeMapperProviders.add(provider);
    return this;
}


@Test
public void testRelayId() {
    GraphQLSchema schema = new GraphQLSchemaGenerator().withOperationsFromSingletons(new BookService()).generate();
    assertNotNull(schema.getQueryType().getFieldDefinition("node"));
    String globalId = new Relay().toGlobalId(Book.class.getSimpleName(), "x123");
    GraphQL exe = GraphQL.newGraphQL(schema).build();
    ExecutionResult result = exe.execute("{node(id: \"" + globalId + "\") {id ... on Book {title}}}");
    assertTrue(result.getErrors().isEmpty());
    assertValueAtPathEquals(globalId, result, "node.id");
    assertValueAtPathEquals("Node Book", result, "node.title");
}

public GraphQLSchema generate() {
    init();
    BuildContext buildContext = new BuildContext(basePackages, environment, new OperationRepository(operationSourceRepository, operationBuilder, inclusionStrategy, typeTransformer, basePackages), new TypeMapperRepository(typeMappers), valueMapperFactory, typeInfoGenerator, interfaceStrategy, scalarStrategy, typeTransformer, abstractInputHandler, inputFieldStrategy, inclusionStrategy, relayMappingConfig, additionalTypes, aliasGroups, implDiscoveryStrategy);
    OperationMapper operationMapper = new OperationMapper(buildContext);
    GraphQLSchema.Builder builder = GraphQLSchema.newSchema().query(newObject().name(queryRoot).description("Query root type").fields(operationMapper.getQueries()).build());
    List<GraphQLFieldDefinition> mutations = operationMapper.getMutations();
    if (!mutations.isEmpty()) {
        builder.mutation(newObject().name(mutationRoot).description("Mutation root type").fields(mutations).build());
    }
    List<GraphQLFieldDefinition> subscriptions = operationMapper.getSubscriptions();
    if (!subscriptions.isEmpty()) {
        builder.subscription(newObject().name(subscriptionRoot).description("Subscription root type").fields(subscriptions).build());
    }
    applyProcessors(builder, buildContext);
    additionalTypes.addAll(buildContext.typeRepository.getDiscoveredTypes());
    return builder.build(additionalTypes);
}

public GraphQLSchema generate() {
    init();
    BuildContext buildContext = new BuildContext(basePackages, environment, new OperationRepository(operationSourceRepository, operationBuilder, inclusionStrategy, typeTransformer, basePackages), new TypeMapperRepository(typeMappers), valueMapperFactory, typeInfoGenerator, interfaceStrategy, scalarStrategy, typeTransformer, abstractInputHandler, inputFieldStrategy, inclusionStrategy, relayMappingConfig, additionalTypes, aliasGroups, implDiscoveryStrategy);
    OperationMapper operationMapper = new OperationMapper(buildContext);
    GraphQLSchema.Builder builder = GraphQLSchema.newSchema().query(newObject().name(queryRoot).description("Query root type").fields(operationMapper.getQueries()).build());
    List<GraphQLFieldDefinition> mutations = operationMapper.getMutations();
    if (!mutations.isEmpty()) {
        builder.mutation(newObject().name(mutationRoot).description("Mutation root type").fields(mutations).build());
    }
    List<GraphQLFieldDefinition> subscriptions = operationMapper.getSubscriptions();
    if (!subscriptions.isEmpty()) {
        builder.subscription(newObject().name(subscriptionRoot).description("Subscription root type").fields(subscriptions).build());
    }
    additionalTypes.addAll(buildContext.typeRepository.getDiscoveredTypes());
    builder.additionalTypes(additionalTypes);
    applyProcessors(builder, buildContext);
    return builder.build();
}


@Test
public void example() throws Exception {
    EWAHCompressedBitmap ewahBitmap1 = EWAHCompressedBitmap.bitmapOf(0, 2, 55, 64, 1 << 30);
    EWAHCompressedBitmap ewahBitmap2 = EWAHCompressedBitmap.bitmapOf(1, 3, 64, 1 << 30);
    System.out.println("bitmap 1: " + ewahBitmap1);
    System.out.println("bitmap 2: " + ewahBitmap2);
    EWAHCompressedBitmap orbitmap = ewahBitmap1.or(ewahBitmap2);
    System.out.println("bitmap 1 OR bitmap 2: " + orbitmap);
    System.out.println("memory usage: " + orbitmap.sizeInBytes() + " bytes");
    EWAHCompressedBitmap andbitmap = ewahBitmap1.and(ewahBitmap2);
    System.out.println("bitmap 1 AND bitmap 2: " + andbitmap);
    System.out.println("memory usage: " + andbitmap.sizeInBytes() + " bytes");
    EWAHCompressedBitmap xorbitmap = ewahBitmap1.xor(ewahBitmap2);
    System.out.println("bitmap 1 XOR bitmap 2:" + xorbitmap);
    System.out.println("memory usage: " + xorbitmap.sizeInBytes() + " bytes");
    EWAHCompressedBitmap ewahBitmap3 = EWAHCompressedBitmap.bitmapOf(5, 55, 1 << 30);
    EWAHCompressedBitmap ewahBitmap4 = EWAHCompressedBitmap.bitmapOf(4, 66, 1 << 30);
    System.out.println("bitmap 3: " + ewahBitmap3);
    System.out.println("bitmap 4: " + ewahBitmap4);
    andbitmap = EWAHCompressedBitmap.and(ewahBitmap1, ewahBitmap2, ewahBitmap3, ewahBitmap4);
    System.out.println("b1 AND b2 AND b3 AND b4: " + andbitmap);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ewahBitmap1.serialize(new DataOutputStream(bos));
    EWAHCompressedBitmap ewahBitmap1new = new EWAHCompressedBitmap();
    byte[] bout = bos.toByteArray();
    ewahBitmap1new.deserialize(new DataInputStream(new ByteArrayInputStream(bout)));
    System.out.println("bitmap 1 (recovered) : " + ewahBitmap1new);
    if (!ewahBitmap1.equals(ewahBitmap1new))
        throw new RuntimeException("Will not happen");
    ByteBuffer bb = ByteBuffer.wrap(bout);
    EWAHCompressedBitmap rmap = new EWAHCompressedBitmap(bb);
    System.out.println("bitmap 1 (mapped) : " + rmap);
    if (!rmap.equals(ewahBitmap1))
        throw new RuntimeException("Will not happen");
    EWAHCompressedBitmap threshold2 = EWAHCompressedBitmap.threshold(2, ewahBitmap1, ewahBitmap2, ewahBitmap3, ewahBitmap4);
    System.out.println("threshold 2 : " + threshold2);
}

public void deserialize(DataInput in) throws IOException {
    this.sizeInBits = in.readInt();
    int sizeInWords = in.readInt();
    this.buffer.clear();
    this.buffer.ensureCapacity(sizeInWords);
    for (int i = 0; i < sizeInWords; ++i) {
        this.buffer.push_back(in.readLong());
    }
    this.rlw = new RunningLengthWord(this.buffer, in.readInt());
}

public void deserialize(DataInput in) throws IOException {
    this.sizeInBits = in.readInt();
    int sizeInWords = in.readInt();
    this.buffer.clear();
    this.buffer.removeLastWord();
    this.buffer.ensureCapacity(sizeInWords);
    for (int i = 0; i < sizeInWords; ++i) {
        this.buffer.push_back(in.readLong());
    }
    this.rlw = new RunningLengthWord(this.buffer, in.readInt());
}

@Test
public void safeSerialization() throws IOException {
    EWAHCompressedBitmap ewahBitmap = EWAHCompressedBitmap.bitmapOf(0, 2, 55, 64, 1 << 30);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ewahBitmap.serialize(new DataOutputStream(bos));
    EWAHCompressedBitmap ewahBitmapnew = new EWAHCompressedBitmap();
    byte[] bout = bos.toByteArray();
    ewahBitmapnew.deserialize(new DataInputStream(new ByteArrayInputStream(bout)));
    assertEquals(ewahBitmapnew, ewahBitmap);
    Assert.assertEquals(ewahBitmapnew.serializedSizeInBytes(), ewahBitmap.serializedSizeInBytes());
}


@Test
public void safeSerialization() throws IOException {
    EWAHCompressedBitmap32 ewahBitmap = EWAHCompressedBitmap32.bitmapOf(0, 2, 55, 64, 1 << 30);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ewahBitmap.serialize(new DataOutputStream(bos));
    EWAHCompressedBitmap32 ewahBitmapnew = new EWAHCompressedBitmap32();
    byte[] bout = bos.toByteArray();
    ewahBitmapnew.deserialize(new DataInputStream(new ByteArrayInputStream(bout)));
    assertEquals(ewahBitmapnew, ewahBitmap);
    Assert.assertEquals(ewahBitmapnew.serializedSizeInBytes(), ewahBitmap.serializedSizeInBytes());
}

public void deserialize(ObjectInput in) throws IOException {
    this.sizeInBits = in.readInt();
    int sizeInWords = in.readInt();
    this.buffer.clear();
    this.buffer.ensureCapacity(sizeInWords);
    for (int i = 0; i < sizeInWords; ++i) {
        this.buffer.push_back(in.readInt());
    }
    this.rlw = new RunningLengthWord32(this.buffer, in.readInt());
}

public void deserialize(DataInput in) throws IOException {
    this.sizeInBits = in.readInt();
    int sizeInWords = in.readInt();
    this.buffer.clear();
    this.buffer.removeLastWord();
    this.buffer.ensureCapacity(sizeInWords);
    for (int i = 0; i < sizeInWords; ++i) {
        this.buffer.push_back(in.readInt());
    }
    this.rlw = new RunningLengthWord32(this.buffer, in.readInt());
}


@Test
public void setSizeInBits() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS / 2, true);
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    bitmap.setSizeInBits(WORD_IN_BITS + WORD_IN_BITS / 2, true);
    IntIterator iterator = bitmap.intIterator();
    for (int i = 0; i < WORD_IN_BITS / 2; ++i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    for (int i = WORD_IN_BITS; i < WORD_IN_BITS + WORD_IN_BITS / 2; ++i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits)
        return false;
    if (!defaultValue) {
        extendEmptyBits(this, this.sizeInBits, size);
    } else {
        if ((this.sizeInBits % WORD_IN_BITS) != 0) {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                addLiteralWord(0);
            }
            final int maskWidth;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            if (this.sizeInBits + WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS < size) {
                maskWidth = WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS;
            } else {
                maskWidth = size - this.sizeInBits;
            }
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == ~0l) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                addEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
        this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - this.sizeInBits / WORD_IN_BITS);
        if (this.sizeInBits < size) {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                addLiteralWord(0);
            }
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
    }
    this.sizeInBits = size;
    return true;
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits)
        return false;
    if (!defaultValue) {
        extendEmptyBits(this, this.sizeInBits, size);
    } else {
        if ((this.sizeInBits % WORD_IN_BITS) != 0) {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                addLiteralWord(0);
            }
            final int maskWidth;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            if (this.sizeInBits + WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS < size) {
                maskWidth = WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS;
            } else {
                maskWidth = size - this.sizeInBits;
            }
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == ~0l) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                addEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
        this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
        if (this.sizeInBits < size) {
            final int dist = (size + WORD_IN_BITS) / WORD_IN_BITS - (this.sizeInBits + WORD_IN_BITS - 1) / WORD_IN_BITS;
            if (dist > 0 || this.rlw.getNumberOfLiteralWords() == 0) {
                addLiteralWord(0);
            }
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
    }
    this.sizeInBits = size;
    return true;
}


@Test
public void setSizeInBits() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS / 2, true);
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    bitmap.setSizeInBits(WORD_IN_BITS + WORD_IN_BITS / 2, true);
    IntIterator iterator = bitmap.intIterator();
    for (int i = 0; i < WORD_IN_BITS / 2; ++i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    for (int i = WORD_IN_BITS; i < WORD_IN_BITS + WORD_IN_BITS / 2; ++i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits)
        return false;
    if (!defaultValue) {
        extendEmptyBits(this, this.sizeInBits, size);
    } else {
        if ((this.sizeInBits % WORD_IN_BITS) != 0) {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                addLiteralWord(0);
            }
            final int maskWidth;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            if (this.sizeInBits + WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS < size) {
                maskWidth = WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS;
            } else {
                maskWidth = size - this.sizeInBits;
            }
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == (~0)) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                addEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
        this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - this.sizeInBits / WORD_IN_BITS);
        if (this.sizeInBits < size) {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                addLiteralWord(0);
            }
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
    }
    this.sizeInBits = size;
    return true;
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits)
        return false;
    if (!defaultValue) {
        extendEmptyBits(this, this.sizeInBits, size);
    } else {
        if ((this.sizeInBits % WORD_IN_BITS) != 0) {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                addLiteralWord(0);
            }
            final int maskWidth;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            if (this.sizeInBits + WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS < size) {
                maskWidth = WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS;
            } else {
                maskWidth = size - this.sizeInBits;
            }
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == (~0)) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                addEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
        this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
        if (this.sizeInBits < size) {
            final int dist = (size + WORD_IN_BITS) / WORD_IN_BITS - (this.sizeInBits + WORD_IN_BITS - 1) / WORD_IN_BITS;
            if (dist > 0 || this.rlw.getNumberOfLiteralWords() == 0) {
                addLiteralWord(0);
            }
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
    }
    this.sizeInBits = size;
    return true;
}


@Test
public void reverseIntIteratorOverBitmapsOfOnes() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, true);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

public IntIterator reverseIntIterator() {
    return new ReverseIntIterator(this.getReverseEWAHIterator(), this.sizeInBits);
}

@Test
public void reverseIntIterator() {
    int[] positions = new int[] { 0, 1, 2, 3, 5, 8, 13, 21 };
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf(positions);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = positions.length - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(positions[i], iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIteratorOverBitmapsOfOnesAndZeros() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS - 10, true);
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 10; i > 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i - 1, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIteratorOverBitmapsOfZeros() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    Assert.assertFalse(iterator.hasNext());
}


@Test
public void reverseIntIteratorOverBitmapsOfOnesAndZeros() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS - 10, true);
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 10; i > 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i - 1, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

public IntIterator reverseIntIterator() {
    return new ReverseIntIterator32(this.getReverseEWAHIterator(), this.sizeInBits);
}

@Test
public void reverseIntIteratorOverBitmapsOfZeros() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIterator() {
    int[] positions = new int[] { 0, 1, 2, 3, 5, 8, 13, 21 };
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf(positions);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = positions.length - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(positions[i], iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIteratorOverBitmapsOfOnes() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, true);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}


@Test
public void basicTest() throws IOException, CloneNotSupportedException {
    EWAHCompressedBitmap ewahBitmap = EWAHCompressedBitmap.bitmapOf(0, 2, 55, 64, 1 << 30);
    EWAHCompressedBitmap newewahBitmap = ewahBitmap.clone();
    Assert.assertEquals(newewahBitmap, ewahBitmap);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ewahBitmap.serialize(new DataOutputStream(bos));
    ByteBuffer bb = ByteBuffer.wrap(bos.toByteArray());
    EWAHCompressedBitmap mapped = new EWAHCompressedBitmap(bb);
    Assert.assertEquals(mapped, ewahBitmap);
    EWAHCompressedBitmap newmapped;
    newmapped = mapped.clone();
    Assert.assertEquals(newmapped, ewahBitmap);
}

@Override
public EWAHCompressedBitmap clone() {
    EWAHCompressedBitmap clone = new EWAHCompressedBitmap(this.buffer.clone());
    clone.sizeInBits = this.sizeInBits;
    clone.rlw = new RunningLengthWord(clone.buffer, this.rlw.position);
    return clone;
}

@Override
public EWAHCompressedBitmap clone() throws CloneNotSupportedException {
    EWAHCompressedBitmap clone = new EWAHCompressedBitmap(this.buffer.clone());
    clone.sizeInBits = this.sizeInBits;
    clone.rlw = new RunningLengthWord(clone.buffer, this.rlw.position);
    return clone;
}


@BenchmarkOptions(benchmarkRounds = 3, warmupRounds = 3)
@Test
public void bigunion() throws Exception {
    for (int k = 1; k < N; k += 10) {
        EWAHCompressedBitmap bitmapor = EWAHCompressedBitmap.or(Arrays.copyOf(ewah, k + 1));
        bogus += bitmapor.cardinality();
    }
}

public int cardinality() {
    int counter = 0;
    final EWAHIterator i = this.getEWAHIterator();
    while (i.hasNext()) {
        RunningLengthWord localrlw = i.next();
        if (localrlw.getRunningBit()) {
            counter += WORD_IN_BITS * localrlw.getRunningLength();
        }
        for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) {
            counter += Long.bitCount(i.buffer().getWord(i.literalWords() + j));
        }
    }
    return counter;
}

public int cardinality() {
    int counter = 0;
    final EWAHIterator i = this.getEWAHIterator();
    while (i.hasNext()) {
        RunningLengthWord localrlw = i.next();
        if (localrlw.getRunningBit()) {
            counter += WORD_IN_BITS * localrlw.getRunningLength();
        }
        for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) {
            counter += Long.bitCount(i.buffer()[i.literalWords() + j]);
        }
    }
    return counter;
}


@Test
public void testMat() {
    System.out.println("testMat ");
    EWAHCompressedBitmap32 b = EWAHCompressedBitmap32.bitmapOf(0, 3);
    EWAHCompressedBitmap32 n = IteratorUtil32.materialize(b.getIteratingRLW());
    assertTrue(n.sizeInBits() == 32);
    n.setSizeInBitsWithinLastWord(b.sizeInBits());
    assertTrue(n.sizeInBits() == b.sizeInBits());
    assertTrue(n.equals(b));
    EWAHCompressedBitmap32 neg = IteratorUtil32.materialize(IteratorAggregation32.not(b.getIteratingRLW()));
    neg.setSizeInBitsWithinLastWord(b.sizeInBits());
    EWAHCompressedBitmap32 x = ((EWAHCompressedBitmap32) b.clone());
    x.not();
    assertTrue(x.equals(neg));
    for (int k = 145; k < 1024; ++k) b.set(k);
    n = IteratorUtil32.materialize(b.getIteratingRLW());
    assertTrue(n.sizeInBits() / 64 * 64 == n.sizeInBits());
    n.setSizeInBitsWithinLastWord(b.sizeInBits());
    assertTrue(n.sizeInBits() == b.sizeInBits());
    assertTrue(n.equals(b));
    neg = IteratorUtil32.materialize(IteratorAggregation32.not(b.getIteratingRLW()));
    neg.setSizeInBitsWithinLastWord(b.sizeInBits());
    x = ((EWAHCompressedBitmap32) b.clone());
    x.not();
    assertTrue(x.equals(neg));
}

@Override
public void not() {
    final EWAHIterator32 i = this.getEWAHIterator();
    if (!i.hasNext())
        return;
    while (true) {
        final RunningLengthWord32 rlw1 = i.next();
        rlw1.setRunningBit(!rlw1.getRunningBit());
        for (int j = 0; j < rlw1.getNumberOfLiteralWords(); ++j) {
            i.buffer()[i.literalWords() + j] = ~i.buffer()[i.literalWords() + j];
        }
        if (!i.hasNext()) {
            final int usedbitsinlast = this.sizeInBits % WORD_IN_BITS;
            if (usedbitsinlast == 0)
                return;
            if (rlw1.getNumberOfLiteralWords() == 0) {
                if ((rlw1.getRunningLength() > 0) && (rlw1.getRunningBit())) {
                    if ((rlw1.getRunningLength() == 1) && (rlw1.position > 0)) {
                        final EWAHIterator32 j = this.getEWAHIterator();
                        int newrlwpos = this.rlw.position;
                        while (j.hasNext()) {
                            RunningLengthWord32 r = j.next();
                            if (r.position < rlw1.position) {
                                newrlwpos = r.position;
                            } else
                                break;
                        }
                        this.rlw.position = newrlwpos;
                        this.actualSizeInWords -= 1;
                    } else
                        rlw1.setRunningLength(rlw1.getRunningLength() - 1);
                    this.addLiteralWord((~0) >>> (WORD_IN_BITS - usedbitsinlast));
                }
                return;
            }
            i.buffer()[i.literalWords() + rlw1.getNumberOfLiteralWords() - 1] &= ((~0) >>> (WORD_IN_BITS - usedbitsinlast));
            return;
        }
    }
}

@Override
public void not() {
    final EWAHIterator32 i = this.getEWAHIterator();
    if (!i.hasNext())
        return;
    while (true) {
        final RunningLengthWord32 rlw1 = i.next();
        rlw1.setRunningBit(!rlw1.getRunningBit());
        for (int j = 0; j < rlw1.getNumberOfLiteralWords(); ++j) {
            i.buffer()[i.literalWords() + j] = ~i.buffer()[i.literalWords() + j];
        }
        if (!i.hasNext()) {
            final int usedbitsinlast = this.sizeInBits % WORD_IN_BITS;
            if (usedbitsinlast == 0)
                return;
            if (rlw1.getNumberOfLiteralWords() == 0) {
                if ((rlw1.getRunningLength() > 0) && (rlw1.getRunningBit())) {
                    if ((rlw1.getRunningLength() == 1) && (rlw1.position > 0)) {
                        final EWAHIterator32 j = this.getEWAHIterator();
                        int newrlwpos = this.rlw.position;
                        while (j.hasNext()) {
                            RunningLengthWord32 r = j.next();
                            if (r.position < rlw1.position) {
                                newrlwpos = r.position;
                            } else
                                break;
                        }
                        this.rlw.position = newrlwpos;
                        this.actualSizeInWords -= 1;
                    } else
                        rlw1.setRunningLength(rlw1.getRunningLength() - 1);
                    this.insertLiteralWord((~0) >>> (WORD_IN_BITS - usedbitsinlast));
                }
                return;
            }
            i.buffer()[i.literalWords() + rlw1.getNumberOfLiteralWords() - 1] &= ((~0) >>> (WORD_IN_BITS - usedbitsinlast));
            return;
        }
    }
}


@Test
public void testBug090() {
    EWAHCompressedBitmap bm = new EWAHCompressedBitmap();
    bm.setSizeInBits(8, false);
    EWAHCompressedBitmap bm1 = bm.clone();
    bm1.not();
    bm1 = bm1.and(bm);
    Assert.assertEquals(0, bm.cardinality());
    Assert.assertEquals(0, bm1.cardinality());
    Assert.assertEquals(bm.sizeInBits(), bm1.sizeInBits());
    Assert.assertTrue(bm.equals(bm1));
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer[this.actualSizeInWords - 1] == 0l) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.actualSizeInWords--;
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                addLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == ~0l) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                addEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            addLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
        this.sizeInBits = size;
    }
    return true;
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer[this.actualSizeInWords - 1] == 0l) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.actualSizeInWords--;
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                insertLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == ~0l) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                insertEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            insertLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
        this.sizeInBits = size;
    }
    return true;
}


@Test
public void testBug090() {
    EWAHCompressedBitmap32 bm = new EWAHCompressedBitmap32();
    bm.setSizeInBits(8, false);
    EWAHCompressedBitmap32 bm1 = bm.clone();
    bm1.not();
    bm1 = bm1.and(bm);
    Assert.assertEquals(0, bm.cardinality());
    Assert.assertEquals(0, bm1.cardinality());
    Assert.assertEquals(bm.sizeInBits(), bm1.sizeInBits());
    Assert.assertTrue(bm.equals(bm1));
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer[this.actualSizeInWords - 1] == 0) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.actualSizeInWords--;
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                addLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == (~0)) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                addEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            addLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
        this.sizeInBits = size;
    }
    return true;
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer[this.actualSizeInWords - 1] == 0) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.actualSizeInWords--;
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                insertLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == (~0)) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                insertEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            addLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
        this.sizeInBits = size;
    }
    return true;
}


@Test
public void simpleExample() throws IOException {
    BitSet Bitmap1 = BitSet.bitmapOf(0, 2, 55, 64, 512);
    BitSet Bitmap2 = BitSet.bitmapOf(1, 3, 64, 512);
    System.out.println("bitmap 1: " + Bitmap1);
    System.out.println("bitmap 2: " + Bitmap2);
    BitSet orbitmap = Bitmap1.clone();
    orbitmap.or(Bitmap2);
    System.out.println("bitmap 1 OR bitmap 2: " + orbitmap);
    BitSet andbitmap = Bitmap1.clone();
    andbitmap.and(Bitmap2);
    System.out.println("bitmap 1 AND bitmap 2: " + andbitmap);
    BitSet xorbitmap = Bitmap1.clone();
    xorbitmap.xor(Bitmap2);
    System.out.println("bitmap 1 XOR bitmap 2:" + xorbitmap);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    Bitmap1.serialize(new DataOutputStream(bos));
    BitSet Bitmap1new = new BitSet();
    byte[] bout = bos.toByteArray();
    Bitmap1new.deserialize(new DataInputStream(new ByteArrayInputStream(bout)));
    System.out.println("bitmap 1 (recovered) : " + Bitmap1new);
    if (!Bitmap1.equals(Bitmap1new))
        throw new RuntimeException("Will not happen");
    ByteBuffer bb = ByteBuffer.wrap(bout);
    ImmutableBitSet rmap = new ImmutableBitSet(bb.asLongBuffer());
    System.out.println("bitmap 1 (mapped) : " + rmap);
    if (!rmap.equals(Bitmap1))
        throw new RuntimeException("Will not happen");
}

public void serialize(DataOutput out) throws IOException {
    out.writeLong(this.getNumberOfWords());
    for (long w : this.data) out.writeLong(w);
}


@Test
public void serializationExample() throws IOException {
    File tmpfile = File.createTempFile("javaewah", "bin");
    tmpfile.deleteOnExit();
    final FileOutputStream fos = new FileOutputStream(tmpfile);
    BitSet Bitmap = BitSet.bitmapOf(0, 2, 55, 64, 512);
    System.out.println("Created the bitmap " + Bitmap);
    Bitmap.serialize(new DataOutputStream(fos));
    long totalcount = fos.getChannel().position();
    System.out.println("Serialized total count = " + totalcount + " bytes");
    fos.close();
    RandomAccessFile memoryMappedFile = new RandomAccessFile(tmpfile, "r");
    ByteBuffer bb = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, totalcount);
    ImmutableBitSet mapped = new ImmutableBitSet(bb.asLongBuffer());
    System.out.println("Mapped the bitmap " + mapped);
    memoryMappedFile.close();
    if (!mapped.equals(Bitmap))
        throw new RuntimeException("Will not happen");
}

@Test
public void simpleExample() throws IOException {
    BitSet Bitmap1 = BitSet.bitmapOf(0, 2, 55, 64, 1 << 30);
    BitSet Bitmap2 = BitSet.bitmapOf(1, 3, 64, 1 << 30);
    System.out.println("bitmap 1: " + Bitmap1);
    System.out.println("bitmap 2: " + Bitmap2);
    BitSet orbitmap = Bitmap1.clone();
    orbitmap.or(Bitmap2);
    System.out.println("bitmap 1 OR bitmap 2: " + orbitmap);
    BitSet andbitmap = Bitmap1.clone();
    andbitmap.and(Bitmap2);
    System.out.println("bitmap 1 AND bitmap 2: " + andbitmap);
    BitSet xorbitmap = Bitmap1.clone();
    xorbitmap.xor(Bitmap2);
    System.out.println("bitmap 1 XOR bitmap 2:" + xorbitmap);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    Bitmap1.serialize(new DataOutputStream(bos));
    BitSet Bitmap1new = new BitSet();
    byte[] bout = bos.toByteArray();
    Bitmap1new.deserialize(new DataInputStream(new ByteArrayInputStream(bout)));
    System.out.println("bitmap 1 (recovered) : " + Bitmap1new);
    if (!Bitmap1.equals(Bitmap1new))
        throw new RuntimeException("Will not happen");
    ByteBuffer bb = ByteBuffer.wrap(bout);
    ImmutableBitSet rmap = new ImmutableBitSet(bb.asLongBuffer());
    System.out.println("bitmap 1 (mapped) : " + rmap);
    if (!rmap.equals(Bitmap1))
        throw new RuntimeException("Will not happen");
}


@Test
public void clear() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf(0, 1, 3, 199, 666);
    Assert.assertEquals(667, bitmap.sizeInBits());
    bitmap.clear(900);
    Assert.assertEquals(901, bitmap.sizeInBits());
    for (int i = 667; i < 901; ++i) {
        Assert.assertFalse(bitmap.get(i));
    }
    Assert.assertTrue(bitmap.get(199));
    bitmap.clear(199);
    Assert.assertFalse(bitmap.get(199));
}

public boolean clear(final int i) {
    return set(i, false);
}

@Test
public void clearStressTest() {
    System.out.println("clear stress test");
    int n = 10 * WORD_IN_BITS;
    for (int k = 0; k < 100; ++k) {
        List<Integer> setPositions = new ArrayList<Integer>(n);
        List<Integer> clearPositions = new ArrayList<Integer>(n);
        for (int i = 0; i < n; ++i) {
            setPositions.add(i);
            clearPositions.add(i);
        }
        Collections.shuffle(setPositions);
        Collections.shuffle(clearPositions);
        EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
        for (int i = 0; i < n; ++i) {
            bitmap.set(setPositions.get(i));
            bitmap.clear(clearPositions.get(i));
        }
        for (int i = 0; i < n; ++i) {
            bitmap.clear(i);
        }
        Assert.assertEquals(0, bitmap.cardinality());
        Assert.assertEquals(WORD_IN_BITS / 8, bitmap.sizeInBytes());
    }
}


@Test
public void clearStressTest() {
    System.out.println("clear stress test");
    int n = 10 * WORD_IN_BITS;
    for (int k = 0; k < 100; ++k) {
        List<Integer> setPositions = new ArrayList<Integer>(n);
        List<Integer> clearPositions = new ArrayList<Integer>(n);
        for (int i = 0; i < n; ++i) {
            setPositions.add(i);
            clearPositions.add(i);
        }
        Collections.shuffle(setPositions);
        Collections.shuffle(clearPositions);
        EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
        for (int i = 0; i < n; ++i) {
            bitmap.set(setPositions.get(i));
            bitmap.clear(clearPositions.get(i));
        }
        for (int i = 0; i < n; ++i) {
            bitmap.clear(i);
        }
        Assert.assertEquals(0, bitmap.cardinality());
        Assert.assertEquals(WORD_IN_BITS / 8, bitmap.sizeInBytes());
    }
}

@Test
public void clear() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf(0, 1, 3, 199, 666);
    Assert.assertEquals(667, bitmap.sizeInBits());
    bitmap.clear(900);
    Assert.assertEquals(901, bitmap.sizeInBits());
    for (int i = 667; i < 901; ++i) {
        Assert.assertFalse(bitmap.get(i));
    }
    Assert.assertTrue(bitmap.get(199));
    bitmap.clear(199);
    Assert.assertFalse(bitmap.get(199));
}


@Test
public void simpleTestWithIntBuffer() {
    java.nio.IntBuffer buffer = java.nio.IntBuffer.wrap(new int[10]);
    EWAHCompressedBitmap32 bitmap = new EWAHCompressedBitmap32(buffer);
    int maxPosition = 666;
    int[] positions = new int[] { 1, maxPosition, 99, 5 };
    for (int position : positions) {
        bitmap.set(position);
    }
    Assert.assertEquals(positions.length, bitmap.cardinality());
    int[] sortedPositions = positions.clone();
    Arrays.sort(sortedPositions);
    Assert.assertArrayEquals(sortedPositions, bitmap.toArray());
    bitmap.not();
    Assert.assertEquals(maxPosition + 1 - positions.length, bitmap.cardinality());
    for (int i = 0; i <= maxPosition; i++) {
        bitmap.set(i);
    }
    Assert.assertEquals(maxPosition + 1, bitmap.cardinality());
    bitmap.clear();
    Assert.assertEquals(0, bitmap.cardinality());
    bitmap.swap(EWAHCompressedBitmap32.bitmapOf(1));
    Assert.assertEquals(1, bitmap.cardinality());
}

public void swap(final EWAHCompressedBitmap32 other) {
    IntArray tmp = this.buffer;
    this.buffer = other.buffer;
    other.buffer = tmp;
    int tmp2 = this.rlw.position;
    this.rlw.position = other.rlw.position;
    other.rlw.position = tmp2;
    IntArray tmp3 = this.rlw.buffer;
    this.rlw.buffer = other.rlw.buffer;
    other.rlw.buffer = tmp3;
    int tmp4 = this.sizeInBits;
    this.sizeInBits = other.sizeInBits;
    other.sizeInBits = tmp4;
}

public void swap(final EWAHCompressedBitmap32 other) {
    this.buffer.swap(other.buffer);
    int tmp2 = this.rlw.position;
    this.rlw.position = other.rlw.position;
    other.rlw.position = tmp2;
    int tmp3 = this.sizeInBits;
    this.sizeInBits = other.sizeInBits;
    other.sizeInBits = tmp3;
}


@Test
public void simpleTestWithIntBuffer() {
    EWAHCompressedBitmap32 bitmap = new EWAHCompressedBitmap32(IntBuffer.wrap(new int[10]));
    int maxPosition = 666;
    int[] positions = new int[] { 1, maxPosition, 99, 5 };
    for (int position : positions) {
        bitmap.set(position);
    }
    Assert.assertEquals(positions.length, bitmap.cardinality());
    int[] sortedPositions = positions.clone();
    Arrays.sort(sortedPositions);
    Assert.assertArrayEquals(sortedPositions, bitmap.toArray());
    bitmap.not();
    Assert.assertEquals(maxPosition + 1 - positions.length, bitmap.cardinality());
    for (int i = 0; i <= maxPosition; i++) {
        bitmap.set(i);
    }
    Assert.assertEquals(maxPosition + 1, bitmap.cardinality());
    bitmap.clear();
    Assert.assertEquals(0, bitmap.cardinality());
    bitmap.swap(EWAHCompressedBitmap32.bitmapOf(1));
    Assert.assertEquals(1, bitmap.cardinality());
}


@Test
public void basicTest() throws IOException, CloneNotSupportedException {
    EWAHCompressedBitmap32 ewahBitmap = EWAHCompressedBitmap32.bitmapOf(0, 2, 55, 64, 1 << 30);
    EWAHCompressedBitmap32 newewahBitmap = ewahBitmap.clone();
    Assert.assertEquals(newewahBitmap, ewahBitmap);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ewahBitmap.serialize(new DataOutputStream(bos));
    ByteBuffer bb = ByteBuffer.wrap(bos.toByteArray());
    EWAHCompressedBitmap32 mapped = new EWAHCompressedBitmap32(bb);
    Assert.assertEquals(mapped, ewahBitmap);
    EWAHCompressedBitmap32 newmapped;
    newmapped = mapped.clone();
    Assert.assertEquals(newmapped, ewahBitmap);
}

@Override
public EWAHCompressedBitmap32 clone() {
    EWAHCompressedBitmap32 clone = null;
    try {
        clone = (EWAHCompressedBitmap32) super.clone();
        clone.buffer = this.buffer.clone();
        clone.sizeInBits = this.sizeInBits;
        clone.rlw = new RunningLengthWord32(clone.buffer, this.rlw.position);
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return clone;
}

@Override
public EWAHCompressedBitmap32 clone() throws CloneNotSupportedException {
    EWAHCompressedBitmap32 clone = new EWAHCompressedBitmap32(this.buffer.clone());
    clone.sizeInBits = this.sizeInBits;
    clone.rlw = new RunningLengthWord32(clone.buffer, this.rlw.position);
    return clone;
}


@BenchmarkOptions(benchmarkRounds = 3, warmupRounds = 3)
@Test
public void toarraybuf() throws Exception {
    for (int k = 1; k < N * 100; ++k) {
        bogus += ewahbuf[k % N].toArray().length;
    }
}

public int[] toArray() {
    int[] ans = new int[this.cardinality()];
    int inanspos = 0;
    int pos = 0;
    final EWAHIterator32 i = this.getEWAHIterator();
    while (i.hasNext()) {
        RunningLengthWord32 localrlw = i.next();
        if (localrlw.getRunningBit()) {
            for (int j = 0; j < localrlw.getRunningLength(); ++j) {
                for (int c = 0; c < WORD_IN_BITS; ++c) {
                    ans[inanspos++] = pos++;
                }
            }
        } else {
            pos += WORD_IN_BITS * localrlw.getRunningLength();
        }
        for (int j = 0; j < localrlw.getNumberOfLiteralWords(); ++j) {
            int data = i.buffer().getWord(i.literalWords() + j);
            while (data != 0) {
                final int t = data & -data;
                ans[inanspos++] = Integer.bitCount(t - 1) + pos;
                data ^= t;
            }
            pos += WORD_IN_BITS;
        }
    }
    return ans;
}

public int[] toArray() {
    int[] ans = new int[this.cardinality()];
    int inanspos = 0;
    int pos = 0;
    final EWAHIterator32 i = this.getEWAHIterator();
    while (i.hasNext()) {
        RunningLengthWord32 localrlw = i.next();
        final int runningLength = localrlw.getRunningLength();
        if (localrlw.getRunningBit()) {
            for (int j = 0; j < runningLength; ++j) {
                for (int c = 0; c < WORD_IN_BITS; ++c) {
                    ans[inanspos++] = pos++;
                }
            }
        } else {
            pos += WORD_IN_BITS * runningLength;
        }
        final int numberOfLiteralWords = localrlw.getNumberOfLiteralWords();
        final int literalWords = i.literalWords();
        for (int j = 0; j < numberOfLiteralWords; ++j) {
            int data = i.buffer().getWord(literalWords + j);
            while (data != 0) {
                final int t = data & -data;
                ans[inanspos++] = Integer.bitCount(t - 1) + pos;
                data ^= t;
            }
            pos += WORD_IN_BITS;
        }
    }
    return ans;
}

@BenchmarkOptions(benchmarkRounds = 3, warmupRounds = 3)
@Test
public void toarray() throws Exception {
    for (int k = 1; k < N * 100; ++k) {
        bogus += ewah[k % N].toArray().length;
    }
}


@Test
public void simpleTestWithLongBuffer() {
    java.nio.LongBuffer buffer = java.nio.LongBuffer.wrap(new long[10]);
    EWAHCompressedBitmap bitmap = new EWAHCompressedBitmap(buffer);
    int maxPosition = 666;
    int[] positions = new int[] { 1, maxPosition, 99, 5 };
    for (int position : positions) {
        bitmap.set(position);
    }
    Assert.assertEquals(positions.length, bitmap.cardinality());
    int[] sortedPositions = positions.clone();
    Arrays.sort(sortedPositions);
    Assert.assertArrayEquals(sortedPositions, bitmap.toArray());
    bitmap.not();
    Assert.assertEquals(maxPosition + 1 - positions.length, bitmap.cardinality());
    for (int i = 0; i <= maxPosition; i++) {
        bitmap.set(i);
    }
    Assert.assertEquals(maxPosition + 1, bitmap.cardinality());
    bitmap.clear();
    Assert.assertEquals(0, bitmap.cardinality());
    bitmap.swap(EWAHCompressedBitmap.bitmapOf(1));
    Assert.assertEquals(1, bitmap.cardinality());
}

public int[] toArray() {
    int[] ans = new int[this.cardinality()];
    int inAnsPos = 0;
    int pos = 0;
    final EWAHIterator i = this.getEWAHIterator();
    while (i.hasNext()) {
        RunningLengthWord localRlw = i.next();
        if (localRlw.getRunningBit()) {
            for (int j = 0; j < localRlw.getRunningLength(); ++j) {
                for (int c = 0; c < WORD_IN_BITS; ++c) {
                    ans[inAnsPos++] = pos++;
                }
            }
        } else {
            pos += WORD_IN_BITS * localRlw.getRunningLength();
        }
        for (int j = 0; j < localRlw.getNumberOfLiteralWords(); ++j) {
            long data = i.buffer().getWord(i.literalWords() + j);
            while (data != 0) {
                final long T = data & -data;
                ans[inAnsPos++] = Long.bitCount(T - 1) + pos;
                data ^= T;
            }
            pos += WORD_IN_BITS;
        }
    }
    return ans;
}

public int[] toArray() {
    int[] ans = new int[this.cardinality()];
    int inAnsPos = 0;
    int pos = 0;
    final EWAHIterator i = this.getEWAHIterator();
    while (i.hasNext()) {
        RunningLengthWord localRlw = i.next();
        final long runningLength = localRlw.getRunningLength();
        if (localRlw.getRunningBit()) {
            for (int j = 0; j < runningLength; ++j) {
                for (int c = 0; c < WORD_IN_BITS; ++c) {
                    ans[inAnsPos++] = pos++;
                }
            }
        } else {
            pos += WORD_IN_BITS * runningLength;
        }
        final int numberOfLiteralWords = localRlw.getNumberOfLiteralWords();
        final int literalWords = i.literalWords();
        for (int j = 0; j < numberOfLiteralWords; ++j) {
            long data = i.buffer().getWord(literalWords + j);
            while (data != 0) {
                final long T = data & -data;
                ans[inAnsPos++] = Long.bitCount(T - 1) + pos;
                data ^= T;
            }
            pos += WORD_IN_BITS;
        }
    }
    return ans;
}


@Test
public void simpleTestWithLongBuffer() {
    EWAHCompressedBitmap bitmap = new EWAHCompressedBitmap(LongBuffer.wrap(new long[10]));
    int maxPosition = 666;
    int[] positions = new int[] { 1, maxPosition, 99, 5 };
    for (int position : positions) {
        bitmap.set(position);
    }
    Assert.assertEquals(positions.length, bitmap.cardinality());
    int[] sortedPositions = positions.clone();
    Arrays.sort(sortedPositions);
    Assert.assertArrayEquals(sortedPositions, bitmap.toArray());
    bitmap.not();
    Assert.assertEquals(maxPosition + 1 - positions.length, bitmap.cardinality());
    for (int i = 0; i <= maxPosition; i++) {
        bitmap.set(i);
    }
    Assert.assertEquals(maxPosition + 1, bitmap.cardinality());
    bitmap.clear();
    Assert.assertEquals(0, bitmap.cardinality());
    bitmap.swap(EWAHCompressedBitmap.bitmapOf(1));
    Assert.assertEquals(1, bitmap.cardinality());
}


@BenchmarkOptions(benchmarkRounds = 3, warmupRounds = 3)
@Test
public void cardinalitybuf() throws Exception {
    for (int k = 1; k < N * 100; ++k) {
        bogus += ewahbuf[k % N].cardinality();
    }
}

public int cardinality() {
    int counter = 0;
    final EWAHIterator i = this.getEWAHIterator();
    while (i.hasNext()) {
        RunningLengthWord localrlw = i.next();
        if (localrlw.getRunningBit()) {
            counter += WORD_IN_BITS * localrlw.getRunningLength();
        }
        final int numberOfLiteralWords = localrlw.getNumberOfLiteralWords();
        final int literalWords = i.literalWords();
        for (int j = 0; j < numberOfLiteralWords; ++j) {
            counter += Long.bitCount(i.buffer().getWord(literalWords + j));
        }
    }
    return counter;
}

@BenchmarkOptions(benchmarkRounds = 3, warmupRounds = 3)
@Test
public void bigunionbuf() throws Exception {
    for (int k = 1; k < N; k += 10) {
        EWAHCompressedBitmap bitmapor = EWAHCompressedBitmap.or(Arrays.copyOf(ewahbuf, k + 1));
        bogus += bitmapor.cardinality();
    }
}


@Test
public void reverseIntIteratorOverBitmapsOfOnes() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, true);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIterator() {
    int[] positions = new int[] { 0, 1, 2, 3, 5, 8, 13, 21 };
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf(positions);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = positions.length - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(positions[i], iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIteratorOverBitmapsOfOnesAndZeros() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS - 10, true);
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 10; i > 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i - 1, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIteratorOverBitmapsOfZeros() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    Assert.assertFalse(iterator.hasNext());
}


@Test
public void reverseIntIteratorOverBitmapsOfOnesAndZeros() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS - 10, true);
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 10; i > 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i - 1, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIteratorOverBitmapsOfZeros() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, false);
    IntIterator iterator = bitmap.reverseIntIterator();
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIterator() {
    int[] positions = new int[] { 0, 1, 2, 3, 5, 8, 13, 21 };
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf(positions);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = positions.length - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(positions[i], iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}

@Test
public void reverseIntIteratorOverBitmapsOfOnes() {
    EWAHCompressedBitmap32 bitmap = EWAHCompressedBitmap32.bitmapOf();
    bitmap.setSizeInBits(WORD_IN_BITS, true);
    IntIterator iterator = bitmap.reverseIntIterator();
    for (int i = WORD_IN_BITS - 1; i >= 0; --i) {
        Assert.assertTrue(iterator.hasNext());
        Assert.assertEquals(i, iterator.next());
    }
    Assert.assertFalse(iterator.hasNext());
}


@Test
public void clear() {
    EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf(0, 1, 3, 199, 666);
    Assert.assertEquals(667, bitmap.sizeInBits());
    bitmap.clear(900);
    Assert.assertEquals(901, bitmap.sizeInBits());
    for (int i = 667; i < 901; ++i) {
        Assert.assertFalse(bitmap.get(i));
    }
    Assert.assertTrue(bitmap.get(199));
    bitmap.clear(199);
    Assert.assertFalse(bitmap.get(199));
}

@Test
public void clearStressTest() {
    System.out.println("clear stress test");
    int n = 10 * WORD_IN_BITS;
    for (int k = 0; k < 100; ++k) {
        List<Integer> setPositions = new ArrayList<Integer>(n);
        List<Integer> clearPositions = new ArrayList<Integer>(n);
        for (int i = 0; i < n; ++i) {
            setPositions.add(i);
            clearPositions.add(i);
        }
        Collections.shuffle(setPositions);
        Collections.shuffle(clearPositions);
        EWAHCompressedBitmap bitmap = EWAHCompressedBitmap.bitmapOf();
        for (int i = 0; i < n; ++i) {
            bitmap.set(setPositions.get(i));
            bitmap.clear(clearPositions.get(i));
        }
        for (int i = 0; i < n; ++i) {
            bitmap.clear(i);
        }
        Assert.assertEquals(0, bitmap.cardinality());
        Assert.assertEquals(WORD_IN_BITS / 8, bitmap.sizeInBytes());
    }
}


@Test
public void issue68() {
    EWAHCompressedBitmap32 one = new EWAHCompressedBitmap32();
    EWAHCompressedBitmap32 other = new EWAHCompressedBitmap32();
    one.set(18308);
    other.set(24608);
    other = other.and(one);
    Assert.assertEquals((long) other.getFirstSetBit(), -1);
    other.set(82764);
    Assert.assertEquals((long) other.getFirstSetBit(), (long) other.iterator().next());
}

public int getFirstSetBit() {
    int nword = 0;
    final int siw = this.buffer.sizeInWords();
    for (int pos = 0; pos < siw; ++pos) {
        int rl = RunningLengthWord32.getRunningLength(this.buffer, pos);
        boolean rb = RunningLengthWord32.getRunningBit(this.buffer, pos);
        if ((rl > 0) && rb) {
            return nword * WORD_IN_BITS;
        }
        nword += rl;
        int lw = RunningLengthWord32.getNumberOfLiteralWords(this.buffer, pos);
        if (lw > 0) {
            int word = this.buffer.getWord(pos + 1);
            if (word != 0) {
                int T = word & -word;
                return nword * WORD_IN_BITS + Integer.bitCount(T - 1);
            }
        }
    }
    return -1;
}

public int getFirstSetBit() {
    int nword = 0;
    final int siw = this.buffer.sizeInWords();
    for (int pos = 0; pos < siw; ++pos) {
        int rl = RunningLengthWord32.getRunningLength(this.buffer, pos);
        boolean rb = RunningLengthWord32.getRunningBit(this.buffer, pos);
        if ((rl > 0) && rb) {
            return nword * WORD_IN_BITS;
        }
        nword += rl;
        int lw = RunningLengthWord32.getNumberOfLiteralWords(this.buffer, pos);
        if (lw > 0) {
            int word = this.buffer.getWord(pos + 1);
            if (word != 0l) {
                int T = word & -word;
                return nword * WORD_IN_BITS + Integer.bitCount(T - 1);
            }
        }
    }
    return -1;
}


@Test
public void circleTest() {
    CircleGenerator g = new CircleGenerator();
    g.computeUpToRadius(16);
    List<List<Coordinate2D>> circles = g.getResult();
    Coordinate2D center = new Coordinate2D(0, 0);
    for (int radius = 0; radius < circles.size(); radius++) {
        for (Coordinate2D coord : circles.get(radius)) {
            assertThat(center.isInRangeEuclidean(coord, radius)).withFailMessage(coord + " not in range " + radius).isTrue();
            assertThat(center.isInRangeEuclidean(coord, radius - 1)).withFailMessage(coord + " should not be in range " + (radius - 1)).isFalse();
        }
    }
}

public void computeUpToRadius(int radius) {
    for (int i = 0; i < radius + 1; i++) {
        circles.add(new ArrayList<>());
    }
    int grid = radius * 2 + 1;
    for (int x = 0; x < grid; x++) {
        for (int z = 0; z < grid; z++) {
            int centerX = x - radius;
            int centerZ = z - radius;
            int dist = distance(centerX, centerZ);
            if (dist > radius || dist < 2) {
                continue;
            }
            circles.get(dist).add(new Coordinate2D(centerX, centerZ));
        }
    }
}

public void computeUpToRadius(int radius) {
    for (int i = 0; i < radius + 1; i++) {
        circles.add(new ArrayList<>());
    }
    int grid = radius * 2 + 1;
    for (int x = 0; x < grid; x++) {
        for (int z = 0; z < grid; z++) {
            int centerX = x - radius;
            int centerZ = z - radius;
            int dist = distance(centerX, centerZ);
            if (centerX == 0 || centerZ == 0) {
                dist += 1;
            }
            if (dist > radius || dist < 3) {
                continue;
            }
            circles.get(dist).add(new Coordinate2D(centerX, centerZ));
        }
    }
}


@Test
void autodetectExtensions() {
    List<Extension> list = new ArrayList<>();
    MySqlConnectionConfiguration.builder().host(HOST).username(USERNAME).build().getExtensions().forEach(Extension.class, list::add);
    assertThat(list).isNotEmpty();
}

Extensions getExtensions() {
    return extensions;
}

@Test
void nonAutodetectExtensions() {
    List<Extension> list = new ArrayList<>();
    MySqlConnectionConfiguration.builder().host(HOST).username(USERNAME).autodetectExtensions(false).build().getExtensions().forEach(Extension.class, list::add);
    assertThat(list).isEmpty();
}

public Builder autodetectExtensions(boolean autodetectExtensions) {
    this.autodetectExtensions = autodetectExtensions;
    return this;
}


@Test
void addLast() {
    CodecsBuilder builder = Codecs.builder().addLast(mock(Codec.class));
    assertThat(builder.build()).isNotSameAs(builder.build()).isNotSameAs(Codecs.builder().addLast(mock(Codec.class)).build());
}

@Override
public CodecsBuilder addLast(Codec<?> codec) {
    synchronized (this) {
        if (isEmpty()) {
            addAll(InternalArrays.asReadOnlyList(defaultCodecs()));
        }
        add(codec);
    }
    return this;
}

@Test
void defaultSingleton() {
    CodecsBuilder builder = Codecs.builder();
    assertThat(builder.build()).isSameAs(builder.build()).isSameAs(Codecs.builder().build());
}

static CodecsBuilder builder() {
    return new DefaultCodecs.Builder();
}

@Test
void addFirst() {
    CodecsBuilder builder = Codecs.builder().addFirst(mock(Codec.class));
    assertThat(builder.build()).isNotSameAs(builder.build()).isNotSameAs(Codecs.builder().addFirst(mock(Codec.class)).build());
}

@Override
public CodecsBuilder addFirst(Codec<?> codec) {
    synchronized (this) {
        if (isEmpty()) {
            addAll(InternalArrays.asReadOnlyList(defaultCodecs()));
        }
        add(0, codec);
    }
    return this;
}


@Test
void toReadOnlyList() {
    Integer[] arr = new Integer[] { 1, 2, 3, 4 };
    ListAssert<Integer> listAssert = assertThat(InternalArrays.toReadOnlyList(arr)).isEqualTo(Arrays.asList(1, 2, 3, 4)).isNotInstanceOf(Arrays.asList(1, 2, 3, 4).getClass());
    Arrays.fill(arr, 6);
    listAssert.isEqualTo(Arrays.asList(1, 2, 3, 4));
    assertThat(InternalArrays.toReadOnlyList(1)).isEqualTo(Collections.singletonList(1)).isExactlyInstanceOf(Collections.singletonList(1).getClass());
    assertThat(InternalArrays.toReadOnlyList()).isEqualTo(Collections.emptyList()).isExactlyInstanceOf(Collections.emptyList().getClass());
}

@SuppressWarnings("varargs")
@SafeVarargs
public static <E> List<E> toReadOnlyList(E... a) {
    requireNonNull(a, "array must not be null");
    return new ArrList<>(Arrays.copyOf(a, a.length));
}

@SuppressWarnings("varargs")
@SafeVarargs
public static <E> List<E> toReadOnlyList(E... a) {
    requireNonNull(a, "array must not be null");
    switch(a.length) {
        case 0:
            return Collections.emptyList();
        case 1:
            return Collections.singletonList(a[0]);
        default:
            return new ArrList<>(Arrays.copyOf(a, a.length));
    }
}

@Test
void asReadOnlyList() {
    assertThat(InternalArrays.asReadOnlyList(1, 2, 3, 4)).isEqualTo(Arrays.asList(1, 2, 3, 4)).isNotInstanceOf(Arrays.asList(1, 2, 3, 4).getClass());
    assertThat(InternalArrays.asReadOnlyList(1)).isEqualTo(Collections.singletonList(1)).isExactlyInstanceOf(Collections.singletonList(1).getClass());
    assertThat(InternalArrays.asReadOnlyList()).isEqualTo(Collections.emptyList()).isExactlyInstanceOf(Collections.emptyList().getClass());
}

@SuppressWarnings("varargs")
@SafeVarargs
public static <E> List<E> asReadOnlyList(E... a) {
    requireNonNull(a, "array must not be null");
    return new ArrList<>(a);
}

@SuppressWarnings("varargs")
@SafeVarargs
public static <E> List<E> asReadOnlyList(E... a) {
    requireNonNull(a, "array must not be null");
    switch(a.length) {
        case 0:
            return Collections.emptyList();
        case 1:
            return Collections.singletonList(a[0]);
        default:
            return new ArrList<>(a);
    }
}


@Test
void setTwiceServerZoneId() {
    ConnectionContext context = new ConnectionContext(ZeroDateOption.USE_NULL, null);
    context.setServerZoneId(ZoneId.systemDefault());
    assertThatIllegalStateException().isThrownBy(() -> context.setServerZoneId(ZoneId.systemDefault()));
}

void setServerZoneId(ZoneId serverZoneId) {
    if (this.serverZoneId != null) {
        throw new IllegalStateException("Server timezone have been initialized");
    }
    this.serverZoneId = serverZoneId;
}

@Test
void shouldNotSetServerZoneId() {
    ConnectionContext context = new ConnectionContext(ZeroDateOption.USE_NULL, ZoneId.systemDefault());
    assertThat(context.shouldSetServerZoneId()).isFalse();
}

boolean shouldSetServerZoneId() {
    return serverZoneId == null;
}

@Test
void shouldSetServerZoneId() {
    ConnectionContext context = new ConnectionContext(ZeroDateOption.USE_NULL, null);
    assertThat(context.shouldSetServerZoneId()).isTrue();
    context.setServerZoneId(ZoneId.systemDefault());
    assertThat(context.shouldSetServerZoneId()).isFalse();
}

@Test
void getServerZoneId() {
    for (int i = -12; i <= 12; ++i) {
        String id = i < 0 ? "UTC" + i : "UTC+" + i;
        ConnectionContext context = new ConnectionContext(ZeroDateOption.USE_NULL, ZoneId.of(id));
        assertThat(context.getServerZoneId()).isEqualTo(ZoneId.of(id));
    }
}

@Override
public ZoneId getServerZoneId() {
    if (serverZoneId == null) {
        throw new IllegalStateException("Server timezone have not initialization");
    }
    return serverZoneId;
}


@Test
void dispose() {
    RequestQueue queue = new RequestQueue();
    List<Integer> arr = new AddEventList(queue);
    queue.submit(() -> arr.add(1));
    queue.submit(() -> arr.add(2));
    queue.dispose();
    queue.submit(() -> arr.add(3));
    queue.submit(() -> arr.add(4));
    assertEquals(arr, Arrays.asList(1, 2));
}

void dispose() {
    ACTIVE_UPDATER.lazySet(this, 1);
    queue.clear();
}


@Test
void errorDropped() {
    int size = 10;
    int takeSize = 2;
    int halfSize = size >>> 1;
    List<Throwable> es = new ArrayList<>();
    String message = "Some random text just for test";
    Iterator<Integer> items = createItems(size);
    assertThat(halfSize).isGreaterThan(takeSize << 1);
    Hooks.onErrorDropped(es::add);
    Flux.fromIterable(() -> items).doOnNext(it -> {
        if (it == halfSize) {
            throw new IllegalStateException(message);
        }
    }).as(OperatorUtils::discardOnCancel).as(it -> StepVerifier.create(it, 0)).thenRequest(takeSize).expectNext(0, 1).thenCancel().verify();
    assertThat(es).hasSize(1).element(0).isExactlyInstanceOf(IllegalStateException.class).extracting(Throwable::getMessage).isEqualTo(message);
}

@Override
public void subscribe(CoreSubscriber<? super T> actual) {
    this.source.subscribe(new Subscriber<>(actual));
}

@Override
public void subscribe(CoreSubscriber<? super T> actual) {
    this.source.subscribe(new DiscardOnCancelSubscriber<>(actual));
}


@Test
public void simple() {
    JavaVersionHelper.Version version = testTarget.javaVersion("11.0.10");
    assertThat(version.major()).isEqualTo(11);
    assertThat(version.minor()).isEqualTo(0);
    assertThat(version.mini()).isEqualTo(10);
}

Version javaVersion(String versionString) {
    Matcher matcher = VERSION_PATTERN.matcher(versionString);
    if (!matcher.matches()) {
        warn("The java version " + versionString + " cannot be parsed as " + VERSION_REGEXP);
        return null;
    }
    try {
        Version version = new Version(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3)));
        debug("parsed.version -> " + version);
        return version;
    } catch (IndexOutOfBoundsException | NumberFormatException ex) {
        error("The java version " + versionString + " has an invalid format. " + ex.getMessage());
        return null;
    }
}

@Test
public void otherStuff() {
    JavaVersionHelper.Version version = testTarget.javaVersion("1.6.0_23-otherStuff");
    assertThat(version.major()).isEqualTo(1);
    assertThat(version.minor()).isEqualTo(6);
    assertThat(version.mini()).isZero();
}

@Test
public void incomplete() {
    JavaVersionHelper.Version version = testTarget.javaVersion("11");
    assertThat(version).isNull();
}

@Test
public void oldStyle() {
    JavaVersionHelper.Version version = testTarget.javaVersion("1.6.0_23");
    assertThat(version.major()).isEqualTo(1);
    assertThat(version.minor()).isEqualTo(6);
    assertThat(version.mini()).isZero();
}

@Test
public void current() {
    JavaVersionHelper.Version version = testTarget.javaVersion();
    assertThat(version.major()).isPositive();
    assertThat(version.minor()).isNotNull();
    assertThat(version.mini()).isNotNull();
}


@Test
public void testShouldReturnNullForMissingCellPolicy_RETURN_BLANK_AS_NULL() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/blank_cells.xlsx"));
        StreamingReader reader = StreamingReader.builder().read(is)) {
        Row row = reader.iterator().next();
        assertNotNull(row.getCell(0, Row.RETURN_BLANK_AS_NULL));
        assertNull(row.getCell(1, Row.RETURN_BLANK_AS_NULL));
    }
}

@Override
public Cell getCell(int cellnum, MissingCellPolicy policy) {
    throw new NotSupportedException();
}

@Override
public Cell getCell(int cellnum, MissingCellPolicy policy) {
    StreamingCell cell = (StreamingCell) cellMap.get(cellnum);
    if (policy == Row.CREATE_NULL_AS_BLANK) {
        if (cell == null)
            return new StreamingCell(cellnum, rowIndex, false);
    } else if (policy == Row.RETURN_BLANK_AS_NULL) {
        if (cell.getCellType() == Cell.CELL_TYPE_BLANK)
            return null;
    }
    return cell;
}

@Test
public void testShouldReturnBlankForMissingCellPolicy_CREATE_NULL_AS_BLANK() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/null_cell.xlsx"));
        StreamingReader reader = StreamingReader.builder().read(is)) {
        Row row = reader.iterator().next();
        assertEquals("B1 is Null ->", row.getCell(0, Row.CREATE_NULL_AS_BLANK).getStringCellValue());
        assertNotNull(row.getCell(1, Row.CREATE_NULL_AS_BLANK));
    }
}


@Test
public void testShouldReturnBlankForMissingCellPolicy_CREATE_NULL_AS_BLANK() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/null_cell.xlsx"));
        StreamingReader reader = StreamingReader.builder().read(is)) {
        Row row = reader.iterator().next();
        assertEquals("B1 is Null ->", row.getCell(0, Row.CREATE_NULL_AS_BLANK).getStringCellValue());
        assertThat(row.getCell(1), is(nullValue()));
        assertNotNull(row.getCell(1, Row.CREATE_NULL_AS_BLANK));
    }
}


@Test
public void testIterateSheets() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/sheets.xlsx"));
        StreamingWorkbook workbook = StreamingReader.builder().open(is)) {
        assertEquals(2, workbook.getNumberOfSheets());
        Sheet alpha = workbook.getSheetAt(0);
        Sheet zulu = workbook.getSheetAt(1);
        assertEquals("SheetAlpha", alpha.getSheetName());
        assertEquals("SheetZulu", zulu.getSheetName());
        Row rowA = alpha.rowIterator().next();
        Row rowZ = zulu.rowIterator().next();
        assertEquals("stuff", rowA.getCell(0).getStringCellValue());
        assertEquals("yeah", rowZ.getCell(0).getStringCellValue());
    }
}

@Override
public Sheet getSheetAt(int index) {
    return reader.getSheets().get(index);
}


@Test
public void testFormulaWithDifferentTypes() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/formula_test.xlsx"));
        Workbook wb = StreamingReader.builder().open(is)) {
        Sheet sheet = wb.getSheetAt(0);
        Iterator<Row> rowIterator = sheet.rowIterator();
        Row next = rowIterator.next();
        Cell cell = next.getCell(0);
        assertThat(cell.getCellTypeEnum(), is(CellType.STRING));
        next = rowIterator.next();
        cell = next.getCell(0);
        assertThat(cell.getCellTypeEnum(), is(CellType.FORMULA));
        assertThat(cell.getCachedFormulaResultTypeEnum(), is(CellType.STRING));
        next = rowIterator.next();
        cell = next.getCell(0);
        assertThat(cell.getCellTypeEnum(), is(CellType.FORMULA));
        assertThat(cell.getCachedFormulaResultTypeEnum(), is(CellType.BOOLEAN));
        next = rowIterator.next();
        cell = next.getCell(0);
        assertThat(cell.getCellTypeEnum(), is(CellType.FORMULA));
        assertThat(cell.getCachedFormulaResultTypeEnum(), is(CellType.NUMERIC));
    }
}

@Override
public CellType getCachedFormulaResultTypeEnum() {
    if (type != null && "str".equals(type)) {
        if (contentsSupplier.getContent() == null || cachedFormulaResultType == null) {
            return CellType.BLANK;
        } else if ("n".equals(cachedFormulaResultType)) {
            return CellType.NUMERIC;
        } else if ("s".equals(cachedFormulaResultType) || "inlineStr".equals(cachedFormulaResultType)) {
            return CellType.STRING;
        } else if ("str".equals(cachedFormulaResultType)) {
            return CellType.FORMULA;
        } else if ("b".equals(cachedFormulaResultType)) {
            return CellType.BOOLEAN;
        } else if ("e".equals(cachedFormulaResultType)) {
            return CellType.ERROR;
        } else {
            throw new UnsupportedOperationException("Unsupported cell type '" + cachedFormulaResultType + "'");
        }
    } else {
        throw new IllegalStateException("Only formula cells have cached results");
    }
}

@Deprecated
@Override
public CellType getCachedFormulaResultTypeEnum() {
    return getCachedFormulaResultType();
}


@Test
public void testSheetName_missingInFile() throws Exception {
    File f = new File("src/test/resources/sheets.xlsx");
    try (StreamingReader reader = StreamingReader.builder().sheetName("adsfasdfasdfasdf").read(f)) {
        fail("Should have failed");
    } catch (MissingSheetException e) {
        assertTrue(f.exists());
    }
}

public StreamingReader read(File f) {
    try {
        OPCPackage pkg = OPCPackage.open(f);
        XSSFReader reader = new XSSFReader(pkg);
        SharedStringsTable sst = reader.getSharedStringsTable();
        InputStream sheet = findSheet(reader);
        if (sheet == null) {
            throw new MissingSheetException("Unable to find sheet at index [" + sheetIndex + "]");
        }
        XMLEventReader parser = XMLInputFactory.newInstance().createXMLEventReader(sheet);
        return new StreamingReader(sst, parser, rowCacheSize);
    } catch (IOException e) {
        f.delete();
        throw new OpenException("Failed to open file", e);
    } catch (OpenXML4JException | XMLStreamException e) {
        f.delete();
        throw new ReadException("Unable to read workbook", e);
    } catch (RuntimeException e) {
        f.delete();
        throw e;
    }
}

public StreamingReader read(File f) {
    try {
        OPCPackage pkg = OPCPackage.open(f);
        XSSFReader reader = new XSSFReader(pkg);
        SharedStringsTable sst = reader.getSharedStringsTable();
        InputStream sheet = findSheet(reader);
        if (sheet == null) {
            throw new MissingSheetException("Unable to find sheet at index [" + sheetIndex + "]");
        }
        XMLEventReader parser = XMLInputFactory.newInstance().createXMLEventReader(sheet);
        return new StreamingReader(sst, parser, rowCacheSize);
    } catch (IOException e) {
        throw new OpenException("Failed to open file", e);
    } catch (OpenXML4JException | XMLStreamException e) {
        throw new ReadException("Unable to read workbook", e);
    }
}


@Test
public void testFormulaCells() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/formula_cell.xlsx"));
        Workbook workbook = StreamingReader.builder().open(is)) {
        assertEquals(1, workbook.getNumberOfSheets());
        Sheet sheet = workbook.getSheetAt(0);
        Iterator<Row> rowIterator = sheet.rowIterator();
        rowIterator.next();
        rowIterator.next();
        Row row3 = rowIterator.next();
        Cell A3 = row3.getCell(0);
        assertEquals("Cell A3 should be of type formula", CELL_TYPE_FORMULA, A3.getCellType());
        assertEquals("Cell A3's value should be of type numeric", CELL_TYPE_NUMERIC, A3.getCachedFormulaResultType());
        assertEquals("Wrong formula", "SUM(A1:A2)", A3.getCellFormula());
    }
}

@Override
public String getCellFormula() {
    throw new NotSupportedException();
}

@Override
public String getCellFormula() {
    if (type == null || !"str".equals(type))
        throw new IllegalStateException("This cell does not have a formula");
    return formula;
}


@Test
public void testGetFirstCellNum() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/gaps.xlsx"));
        StreamingReader reader = StreamingReader.builder().read(is)) {
        List<List<Cell>> obj = new ArrayList<>();
        List<Row> rows = new ArrayList<>();
        for (Row r : reader) {
            rows.add(r);
            List<Cell> o = new ArrayList<>();
            for (Cell c : r) {
                o.add(c);
            }
            obj.add(o);
        }
        assertEquals(3, rows.size());
        assertEquals(3, rows.get(2).getFirstCellNum());
    }
}

@Override
public short getFirstCellNum() {
    throw new NotSupportedException();
}

@Override
public short getFirstCellNum() {
    if (cellMap.size() == 0) {
        return -1;
    }
    return cellMap.firstKey().shortValue();
}


@Test
public void testHiddenSheets() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/hidden.xlsx"));
        Workbook workbook = StreamingReader.builder().open(is)) {
        assertEquals(3, workbook.getNumberOfSheets());
        assertFalse(workbook.isSheetHidden(0));
        assertTrue(workbook.isSheetHidden(1));
        assertFalse(workbook.isSheetVeryHidden(1));
        assertFalse(workbook.isSheetHidden(2));
        assertTrue(workbook.isSheetVeryHidden(2));
    }
}

@Override
public boolean isSheetHidden(int sheetIx) {
    throw new UnsupportedOperationException();
}

@Override
public boolean isSheetHidden(int sheetIx) {
    return "hidden".equals(reader.getSheetProperties().get(sheetIx).get("state"));
}


@Test
public void testStringCellValue() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/blank_cell_StringCellValue.xlsx"));
        StreamingReader reader = StreamingReader.builder().read(is)) {
        for (Row r : reader) {
            if (r.getRowNum() == 1) {
                assertEquals("", r.getCell(1).getStringCellValue());
            }
        }
    }
}

@Override
public String getStringCellValue() {
    return (String) contents;
}

@Override
public String getStringCellValue() {
    return contents == null ? "" : (String) contents;
}


@Test
public void testSheetName_alpha() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/sheets.xlsx"));
        StreamingReader reader = StreamingReader.builder().sheetName("SheetAlpha").read(is)) {
        List<List<Cell>> obj = new ArrayList<>();
        for (Row r : reader) {
            List<Cell> o = new ArrayList<>();
            for (Cell c : r) {
                o.add(c);
            }
            obj.add(o);
        }
        assertEquals(1, obj.size());
        List<Cell> row;
        row = obj.get(0);
        assertEquals(1, row.size());
        assertEquals("stuff", row.get(0).getStringCellValue());
    }
}

public Builder sheetName(String sheetName) {
    this.sheetName = sheetName;
    return this;
}

@Test
public void testSheetName_zulu() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/sheets.xlsx"));
        StreamingReader reader = StreamingReader.builder().sheetName("SheetZulu").read(is)) {
        List<List<Cell>> obj = new ArrayList<>();
        for (Row r : reader) {
            List<Cell> o = new ArrayList<>();
            for (Cell c : r) {
                o.add(c);
            }
            obj.add(o);
        }
        assertEquals(1, obj.size());
        List<Cell> row;
        row = obj.get(0);
        assertEquals(1, row.size());
        assertEquals("yeah", row.get(0).getStringCellValue());
    }
}


@Test
public void testBlankNumerics() throws Exception {
    File f = new File("src/test/resources/blank_cells.xlsx");
    try (StreamingReader reader = StreamingReader.builder().read(f)) {
        Row row = reader.iterator().next();
        assertThat(row.getCell(1).getStringCellValue(), is(nullValue()));
        assertThat(row.getCell(1).getDateCellValue(), is(nullValue()));
        assertThat(row.getCell(1).getNumericCellValue(), equalTo(0.0));
    }
}

@Override
public double getNumericCellValue() {
    return Double.parseDouble((String) rawContents);
}

@Override
public double getNumericCellValue() {
    return rawContents == null ? 0.0 : Double.parseDouble((String) rawContents);
}


@Test
public void testGetDateCellValue1904() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/1904Dates.xlsx"));
        StreamingReader reader = StreamingReader.builder().read(is)) {
        List<List<Cell>> obj = new ArrayList<>();
        for (Row r : reader) {
            List<Cell> o = new ArrayList<>();
            for (Cell c : r) {
                o.add(c);
            }
            obj.add(o);
        }
        Date dt = obj.get(1).get(5).getDateCellValue();
        assertNotNull(dt);
        final GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(dt);
        assertEquals(cal.get(Calendar.YEAR), 1991);
        try {
            obj.get(0).get(0).getDateCellValue();
            fail("Should have thrown IllegalStateException");
        } catch (IllegalStateException e) {
        }
    }
}

@Override
public Date getDateCellValue() {
    return rawContents == null ? null : HSSFDateUtil.getJavaDate(getNumericCellValue());
}

@Override
public Date getDateCellValue() {
    if (getCellType() == CELL_TYPE_STRING) {
        throw new IllegalStateException("Cell type cannot be CELL_TYPE_STRING");
    }
    return rawContents == null ? null : HSSFDateUtil.getJavaDate(getNumericCellValue(), use1904Dates);
}

@Test
public void testGetDateCellValue() throws Exception {
    try (InputStream is = new FileInputStream(new File("src/test/resources/data_types.xlsx"));
        StreamingReader reader = StreamingReader.builder().read(is)) {
        List<List<Cell>> obj = new ArrayList<>();
        for (Row r : reader) {
            List<Cell> o = new ArrayList<>();
            for (Cell c : r) {
                o.add(c);
            }
            obj.add(o);
        }
        Date dt = obj.get(4).get(1).getDateCellValue();
        assertNotNull(dt);
        final GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(dt);
        assertEquals(cal.get(Calendar.YEAR), 2014);
        try {
            obj.get(0).get(0).getDateCellValue();
            fail("Should have thrown IllegalStateException");
        } catch (IllegalStateException e) {
        }
    }
}


@Test
public void testGetOAuthParameters() {
    HttpParameters params = new HttpParameters();
    params.put("a", "5");
    params.put("oauth_token", "1");
    params.put("x_oauth_token", "1");
    HttpParameters oauthParams = params.getOAuthParameters();
    assertFalse(oauthParams.containsKey("a"));
    assertTrue(oauthParams.containsKey("oauth_token"));
    assertTrue(oauthParams.containsKey("x_oauth_token"));
}

public HttpParameters getOAuthParameters() {
    HttpParameters oauthParams = new HttpParameters();
    for (Entry<String, SortedSet<String>> param : this.entrySet()) {
        String key = param.getKey();
        if (key.startsWith("oauth_") || key.startsWith("x_oauth_")) {
            oauthParams.put(key, param.getValue());
        }
    }
    return oauthParams;
}


@Test
void nullValue() {
    CachedSupplier<String> supplier = new CachedSupplier<>(null);
    assertNull(supplier.get());
}

@Override
public T get() {
    if (cache == null) {
        cache = delegate.get();
    }
    return cache;
}

@Override
public T get() {
    if (this.delegate == null) {
        return this.cache;
    }
    synchronized (this) {
        if (this.delegate != null) {
            this.cache = this.delegate.get();
            this.delegate = null;
        }
    }
    return this.cache;
}


@Test
void iterateWalkValidate() {
    CollectorContext collectorContext = new CollectorContext(new ConcurrentHashMap<>(), new ConcurrentHashMap<>());
    JsonMetaSchema metaSchema = JsonMetaSchema.builder(JsonMetaSchema.getV202012()).keyword(new CollectKeyword()).build();
    JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.metaSchema(metaSchema));
    JsonSchema schema = factory.getSchema("{\n" + "  \"collect\": true\n" + "}");
    for (int i = 0; i < 50; ++i) {
        schema.walk("1", InputFormat.JSON, true, executionContext -> {
            executionContext.setCollectorContext(collectorContext);
        });
    }
    collectorContext.loadCollectors();
    AtomicInteger result = (AtomicInteger) collectorContext.get("collect");
    assertEquals(50, result.get());
}

public ValidationResult walk(String input, InputFormat inputFormat, boolean validate, Consumer<ExecutionContext> executionCustomizer) {
    return walk(createExecutionContext(), deserialize(input, inputFormat), validate, executionCustomizer);
}

@Test
void iterateWalk() {
    CollectorContext collectorContext = new CollectorContext(new ConcurrentHashMap<>(), new ConcurrentHashMap<>());
    JsonMetaSchema metaSchema = JsonMetaSchema.builder(JsonMetaSchema.getV202012()).keyword(new CollectKeyword()).build();
    JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.metaSchema(metaSchema));
    JsonSchema schema = factory.getSchema("{\n" + "  \"collect\": true\n" + "}");
    for (int i = 0; i < 50; ++i) {
        schema.walk("1", InputFormat.JSON, false, executionContext -> {
            executionContext.setCollectorContext(collectorContext);
        });
    }
    collectorContext.loadCollectors();
    AtomicInteger result = (AtomicInteger) collectorContext.get("collect");
    assertEquals(50, result.get());
}


@Test
public void testPrint() {
    ClassLayout.parseInstance(new A()).toPrintable();
    ClassLayout.parseInstance(new B()).toPrintable();
    ClassLayout.parseClass(A.class).toPrintable();
    ClassLayout.parseClass(B.class).toPrintable();
    ClassLayout.parseClass(A.class).toPrintable(new A());
    ClassLayout.parseClass(B.class).toPrintable(new B());
}

public String toPrintable(Object instance) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    int maxTypeLen = 5;
    for (FieldLayout f : fields()) {
        maxTypeLen = Math.max(f.typeClass().length(), maxTypeLen);
    }
    int maxDescrLen = 30;
    for (FieldLayout f : fields()) {
        maxDescrLen = Math.max((f.hostClass() + "" + f.name()).length(), maxDescrLen);
    }
    pw.println(classData.name() + " object internals:");
    pw.printf(" %6s %5s %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", "OFFSET", "SIZE", "TYPE", "DESCRIPTION", "VALUE");
    if (instance != null) {
        VirtualMachine vm = VM.current();
        for (long off = 0; off < headerSize(); off += 4) {
            int word = vm.getInt(instance, off);
            pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", off, 4, "", "(object header)", toHex((word >> 0) & 0xFF) + " " + toHex((word >> 8) & 0xFF) + " " + toHex((word >> 16) & 0xFF) + " " + toHex((word >> 24) & 0xFF) + " " + "(" + toBinary((word >> 0) & 0xFF) + " " + toBinary((word >> 8) & 0xFF) + " " + toBinary((word >> 16) & 0xFF) + " " + toBinary((word >> 24) & 0xFF) + ") " + "(" + word + ")");
        }
    } else {
        pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", 0, headerSize(), "", "(object header)", "N/A");
    }
    long nextFree = headerSize();
    long interLoss = 0;
    long exterLoss = 0;
    for (FieldLayout f : fields()) {
        if (f.offset() > nextFree) {
            pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", nextFree, (f.offset() - nextFree), "", "(alignment/padding gap)", "N/A");
            interLoss += (f.offset() - nextFree);
        }
        pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", f.offset(), f.size(), f.typeClass(), f.hostClass() + "." + f.name(), (instance != null) ? f.safeValue(instance) : "N/A");
        nextFree = f.offset() + f.size();
    }
    long sizeOf = VM.current().sizeOf(instance);
    if (sizeOf != nextFree) {
        exterLoss = sizeOf - nextFree;
        pw.printf(" %6d %5s %" + maxTypeLen + "s %s%n", nextFree, exterLoss, "", "(loss due to the next object alignment)");
    }
    pw.printf("Instance size: %d bytes%n", sizeOf);
    pw.printf("Space losses: %d bytes internal + %d bytes external = %d bytes total%n", interLoss, exterLoss, interLoss + exterLoss);
    pw.close();
    return sw.toString();
}

public String toPrintable(Object instance) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    int maxTypeLen = 5;
    for (FieldLayout f : fields()) {
        maxTypeLen = Math.max(f.typeClass().length(), maxTypeLen);
    }
    int maxDescrLen = 30;
    for (FieldLayout f : fields()) {
        maxDescrLen = Math.max((f.hostClass() + "" + f.name()).length(), maxDescrLen);
    }
    if (instance != null) {
        try {
            Class<?> klass = Class.forName(classData.name());
            if (!klass.isAssignableFrom(instance.getClass())) {
                throw new IllegalArgumentException("Passed instance type " + instance.getClass() + " is not assignable from " + klass + ".");
            }
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Class is not found: " + classData.name() + ".");
        }
    }
    pw.println(classData.name() + " object internals:");
    pw.printf(" %6s %5s %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", "OFFSET", "SIZE", "TYPE", "DESCRIPTION", "VALUE");
    if (instance != null) {
        VirtualMachine vm = VM.current();
        for (long off = 0; off < headerSize(); off += 4) {
            int word = vm.getInt(instance, off);
            pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", off, 4, "", "(object header)", toHex((word >> 0) & 0xFF) + " " + toHex((word >> 8) & 0xFF) + " " + toHex((word >> 16) & 0xFF) + " " + toHex((word >> 24) & 0xFF) + " " + "(" + toBinary((word >> 0) & 0xFF) + " " + toBinary((word >> 8) & 0xFF) + " " + toBinary((word >> 16) & 0xFF) + " " + toBinary((word >> 24) & 0xFF) + ") " + "(" + word + ")");
        }
    } else {
        pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", 0, headerSize(), "", "(object header)", "N/A");
    }
    long nextFree = headerSize();
    long interLoss = 0;
    long exterLoss = 0;
    for (FieldLayout f : fields()) {
        if (f.offset() > nextFree) {
            pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", nextFree, (f.offset() - nextFree), "", "(alignment/padding gap)", "N/A");
            interLoss += (f.offset() - nextFree);
        }
        pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", f.offset(), f.size(), f.typeClass(), f.hostClass() + "." + f.name(), (instance != null) ? f.safeValue(instance) : "N/A");
        nextFree = f.offset() + f.size();
    }
    long sizeOf = (instance != null) ? VM.current().sizeOf(instance) : instanceSize();
    ;
    if (sizeOf != nextFree) {
        exterLoss = sizeOf - nextFree;
        pw.printf(" %6d %5s %" + maxTypeLen + "s %s%n", nextFree, exterLoss, "", "(loss due to the next object alignment)");
    }
    pw.printf("Instance size: %d bytes%n", sizeOf);
    pw.printf("Space losses: %d bytes internal + %d bytes external = %d bytes total%n", interLoss, exterLoss, interLoss + exterLoss);
    pw.close();
    return sw.toString();
}

@Test
public void testInconsistentTypes() {
    try {
        ClassLayout.parseClass(A.class).toPrintable(new B());
        Assert.fail("Trying to pass instance B over the class A succeeded");
    } catch (Exception e) {
    }
    try {
        ClassLayout.parseClass(B.class).toPrintable(new A());
        Assert.fail("Trying to pass instance A over the class B succeeded");
    } catch (Exception e) {
    }
    try {
        ClassLayout.parseClass(B.class).toPrintable(new C());
    } catch (Exception e) {
        Assert.fail("Trying to pass instance C over the class B failed");
    }
}