public void xor(BitmapValue other) throws IOException {
        switch (other.bitmapType) {
            case EMPTY:
                break;
            case SINGLE_VALUE:
                switch (bitmapType) {
                    case EMPTY:
                        add(other.singleValue);
                        break;
                    case SINGLE_VALUE:
                        if (other.singleValue == this.singleValue) {
                            clear();
                        } else {
                            add(other.singleValue);
                        }
                        break;
                    case BITMAP_VALUE:
                        if (this.bitmap.contains(other.singleValue)) {
                            this.bitmap.removeLong(other.singleValue);
                        } else {
                            this.bitmap.addLong(other.singleValue);
                        }
                        break;
                    case SET_VALUE:
                        if (this.set.contains(other.singleValue)) {
                            this.set.remove(other.singleValue);
                        } else {
                            this.set.add(other.singleValue);
                        }
                        break;
                }
                break;
            case BITMAP_VALUE:
                switch (this.bitmapType) {
                    case EMPTY:
                        this.bitmap = new Roaring64Map();
                        this.bitmap.or(other.bitmap);
                        bitmapType = BITMAP_VALUE;
                        break;
                    case SINGLE_VALUE:
                        this.bitmap = new Roaring64Map();
                        this.bitmap.or(other.bitmap);
                        bitmapType = BITMAP_VALUE;
                        if (this.bitmap.contains(this.singleValue)) {
                            this.bitmap.removeLong(this.singleValue);
                        } else {
                            this.bitmap.addLong(this.singleValue);
                        }
                        break;
                    case BITMAP_VALUE:
                        this.bitmap.xor(other.bitmap);
                        break;
                    case SET_VALUE:
                        this.bitmap = fromSetToBitmap(this.set);
                        this.bitmap.xor(other.bitmap);
                        this.bitmapType = BITMAP_VALUE;
                        this.set = null;
                        break;
                }
                break;
            case SET_VALUE:
                switch (this.bitmapType) {
                    case EMPTY:
                        this.set = new HashSet<>(other.set);
                        this.bitmapType = SET_VALUE;
                        break;
                    case SINGLE_VALUE:
                        this.set = new HashSet<>(other.set);
                        if (this.set.contains(this.singleValue)) {
                            this.set.remove(this.singleValue);
                        } else {
                            this.set.add(this.singleValue);
                        }
                        this.bitmapType = SET_VALUE;
                        break;
                    case BITMAP_VALUE:
                        Roaring64Map tmpBitmap = fromSetToBitmap(other.set);
                        this.bitmap.xor(tmpBitmap);
                        break;
                    case SET_VALUE:
                        for (Long v : other.set) {
                            if (this.set.contains(v)) {
                                this.set.remove(v);
                            } else {
                                this.set.add(v);
                            }
                        }
                        break;
                }
                break;
        }
    }
@Test
    public void testBitmapValueXor() throws IOException {
        // empty xor empty
        BitmapValue bitmap = new BitmapValue(emptyBitmap);
        bitmap.xor(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // empty xor single
        bitmap = new BitmapValue(emptyBitmap);
        bitmap.xor(singleBitmap);
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 1, 2);

        // empty xor set
        bitmap = new BitmapValue(emptyBitmap);
        bitmap.xor(mediumBitmap);
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 0, 10);

        // empty xor bitmap
        bitmap = new BitmapValue(emptyBitmap);
        bitmap.xor(largeBitmap);
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 40);

        // single xor empty
        bitmap = new BitmapValue(singleBitmap);
        bitmap.xor(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 1, 2);

        // single xor single (equal)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.xor(singleBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // single xor single (not equal)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.xor(new BitmapValue(2, 3));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 1, 3);

        // single xor set (contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.xor(new BitmapValue(1, 8));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 2, 8);

        // single xor set (not contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.xor(new BitmapValue(2, 8));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 1, 8);

        // single xor bitmap (contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.xor(largeBitmap);
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 1, 2, 40);

        // single xor bitmap (not contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.xor(new BitmapValue(2, 40));
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 1, 40);

        // set xor empty
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.xor(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 0, 10);

        // set xor single (contains)
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.xor(singleBitmap);
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 0, 1, 2, 10);

        // set xor single (not contains)
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.xor(new BitmapValue(10));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 0, 11);

        // set xor set
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.xor(new BitmapValue(8, 20));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 0, 8, 10, 20);

        // set xor bitmap
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.xor(new BitmapValue(8, 100));
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 8, 10, 100);

        // bitmap xor empty
        bitmap = new BitmapValue(largeBitmap);
        bitmap.xor(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 40);

        // bitmap xor single (contains)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.xor(singleBitmap);
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 1, 2, 40);

        // bitmap xor single (not contains)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.xor(new BitmapValue(40));
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 41);

        // bitmap xor set
        bitmap = new BitmapValue(largeBitmap);
        bitmap.xor(new BitmapValue(35, 45));
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 35, 40, 45);

        // bitmap xor bitmap
        bitmap = new BitmapValue(largeBitmap);
        bitmap.xor(new BitmapValue(35, 95));
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 0, 35, 40, 95);
    }