public void and(BitmapValue other) {
        switch (other.bitmapType) {
            case EMPTY:
                clear();
                break;
            case SINGLE_VALUE:
                switch (this.bitmapType) {
                    case EMPTY:
                        break;
                    case SINGLE_VALUE:
                        if (this.singleValue != other.singleValue) {
                            clear();
                        }
                        break;
                    case BITMAP_VALUE:
                        if (!this.bitmap.contains(other.singleValue)) {
                            clear();
                        } else {
                            clear();
                            this.singleValue = other.singleValue;
                            this.bitmapType = SINGLE_VALUE;
                        }
                        break;
                    case SET_VALUE:
                        if (!this.set.contains(other.singleValue)) {
                            clear();
                        } else {
                            clear();
                            this.singleValue = other.singleValue;
                            this.bitmapType = SINGLE_VALUE;
                        }
                        break;
                }
                break;
            case BITMAP_VALUE:
                switch (this.bitmapType) {
                    case EMPTY:
                        break;
                    case SINGLE_VALUE:
                        if (!other.bitmap.contains(this.singleValue)) {
                            clear();
                        }
                        break;
                    case BITMAP_VALUE:
                        this.bitmap.and(other.bitmap);
                        convertBitmapToSmallerType();
                        break;
                    case SET_VALUE:
                        Set<Long> newSet = new HashSet<>();
                        for (Long v : set) {
                            if (other.bitmap.contains(v)) {
                                newSet.add(v);
                            }
                        }
                        set = newSet;
                        break;
                }
                break;
            case SET_VALUE:
                switch (this.bitmapType) {
                    case EMPTY:
                        break;
                    case SINGLE_VALUE:
                        if (!other.set.contains(this.singleValue)) {
                            clear();
                        }
                        break;
                    case BITMAP_VALUE: {
                        Set<Long> newSet = new HashSet<>();
                        for (Long v : other.set) {
                            if (this.bitmap.contains(v)) {
                                newSet.add(v);
                            }
                        }
                        set = newSet;
                        bitmapType = SET_VALUE;
                        break;
                    }
                    case SET_VALUE: {
                        Set<Long> newSet = new HashSet<>();
                        for (Long v : other.set) {
                            if (this.set.contains(v)) {
                                newSet.add(v);
                            }
                        }
                        set = newSet;
                        break;
                    }
                }
                break;
        }
    }
@Test
    public void testBitmapValueAnd() throws IOException {
        // empty and empty
        BitmapValue bitmap = new BitmapValue(emptyBitmap);
        bitmap.and(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // empty and single
        bitmap = new BitmapValue(emptyBitmap);
        bitmap.and(singleBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // empty and set
        bitmap = new BitmapValue(emptyBitmap);
        bitmap.and(mediumBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // empty and bitmap
        bitmap = new BitmapValue(emptyBitmap);
        bitmap.and(largeBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // single and empty
        bitmap = new BitmapValue(singleBitmap);
        bitmap.and(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

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

        // single and single (not equal)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.and(new BitmapValue(1));
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 1, 2);

        // single and bitmap (not contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.and(new BitmapValue(2, 4));
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // single and bitmap (contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.and(largeBitmap);
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 1, 2);

        // single and set (not contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.and(new BitmapValue(100, 101));
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // single and set (contains)
        bitmap = new BitmapValue(singleBitmap);
        bitmap.and(mediumBitmap);
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 1, 2);

        // bitmap and empty
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // bitmap and single (contains)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(singleBitmap);
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 1, 2);

        // bitmap and single (not contains)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(new BitmapValue(1000));
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // bitmap and bitmap (-> bitmap)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(new BitmapValue(20, 60));
        checkBitmap(bitmap, BitmapValue.BITMAP_VALUE, 20, 40);

        // bitmap and bitmap (-> empty)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(new BitmapValue(100, 180));
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // bitmap and bitmap (-> single)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(new BitmapValue(39, 100));
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 39, 40);

        // bitmap and set (->set)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(mediumBitmap);
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 0, 10);

        // bitmap and set (->empty)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(new BitmapValue(100, 120));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 0, 0);

        // bitmap and set (->single)
        bitmap = new BitmapValue(largeBitmap);
        bitmap.and(new BitmapValue(30, 50));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 30, 40);

        // set and empty
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.and(emptyBitmap);
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // set and single (contains)
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.and(singleBitmap);
        checkBitmap(bitmap, BitmapValue.SINGLE_VALUE, 1, 2);

        // set and single (not contains)
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.and(new BitmapValue(100));
        checkBitmap(bitmap, BitmapValue.EMPTY, 0, 0);

        // set and set
        bitmap = new BitmapValue(mediumBitmap);
        bitmap.and(new BitmapValue(5, 20));
        checkBitmap(bitmap, BitmapValue.SET_VALUE, 5, 10);
    }