All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.tum.in.naturals.set.RoaringBoundedNatBitSet Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2018 Tobias Meggendorfer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package de.tum.in.naturals.set;

import static de.tum.in.naturals.set.NatBitSetsUtil.checkNonNegative;
import static de.tum.in.naturals.set.NatBitSetsUtil.checkRange;

import de.tum.in.naturals.bitset.RoaringBitmaps;
import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntIterator;
import java.util.Collection;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.IntPredicate;
import javax.annotation.Nonnegative;
import org.roaringbitmap.RoaringBitmap;

class RoaringBoundedNatBitSet extends AbstractBoundedNatBitSet {
    private static final long INFINITY = Integer.MAX_VALUE + 1L;

    private final RoaringBitmap bitmap;
    private final boolean complement;
    private final RoaringBoundedNatBitSet complementView;

    private RoaringBoundedNatBitSet(RoaringBoundedNatBitSet other) {
        super(other.domainSize());
        // Complement constructor
        this.bitmap = other.bitmap;
        this.complement = !other.complement;
        this.complementView = other;
        assert checkConsistency();
    }

    private RoaringBoundedNatBitSet(RoaringBitmap bitmap, @Nonnegative int domainSize, boolean complement) {
        super(domainSize);
        this.bitmap = bitmap;
        this.complement = complement;
        this.complementView = new RoaringBoundedNatBitSet(this);
        assert checkConsistency();
    }

    RoaringBoundedNatBitSet(RoaringBitmap bitmap, @Nonnegative int domainSize) {
        this(bitmap, domainSize, false);
    }

    @Override
    boolean isComplement() {
        return complement;
    }

    @Override
    public boolean isEmpty() {
        assert checkConsistency();
        if (complement) {
            return bitmap.getCardinality() == domainSize();
        }
        return bitmap.isEmpty();
    }

    @Override
    public int size() {
        assert checkConsistency();
        int bitSetCardinality = bitmap.getCardinality();
        return complement ? domainSize() - bitSetCardinality : bitSetCardinality;
    }

    @Override
    public boolean contains(int k) {
        return 0 <= k && (complement ? k < domainSize() && !bitmap.contains(k) : bitmap.contains(k));
    }

    @Override
    public boolean containsAll(IntCollection indices) {
        assert checkConsistency();
        if (isEmpty()) {
            return indices.isEmpty();
        }
        if (indices.isEmpty()) {
            return true;
        }

        if (indices instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) indices;

            if (complement) {
                if (other.complement) {
                    if (other.domainSize() >= domainSize()) {
                        return other.bitmap.nextAbsentValue(domainSize()) == other.domainSize()
                                && other.bitmap.contains(bitmap);
                    }
                    return RoaringBitmap.andCardinality(bitmap, other.bitmap) == bitmap.rank(other.domainSize() - 1);
                }
                return other.lastInt() < domainSize() && !RoaringBitmap.intersects(bitmap, other.bitmap);
            }

            if (other.complement) {
                if (other.domainSize() >= domainSize()) {
                    return bitmap.getCardinality() + other.bitmap.getCardinality()
                            == other.domainSize() + RoaringBitmap.andCardinality(bitmap, other.bitmap);
                }
                return bitmap.rank(other.domainSize() - 1) + other.bitmap.getCardinality()
                        == other.domainSize() + RoaringBitmap.andCardinality(bitmap, other.bitmap);
            }
            return bitmap.contains(other.bitmap);
        }
        if (indices instanceof RoaringNatBitSet) {
            RoaringNatBitSet other = (RoaringNatBitSet) indices;

            if (complement) {
                return other.lastInt() < domainSize() && !RoaringBitmap.intersects(bitmap, other.bitmap());
            }
            return bitmap.contains(other.bitmap());
        }

        return super.containsAll(indices);
    }

    @Override
    public int firstInt() {
        assert checkConsistency();
        if (complement) {
            int firstInt = Math.toIntExact(bitmap.nextAbsentValue(0));
            if (firstInt >= domainSize()) {
                throw new NoSuchElementException();
            }
            return firstInt;
        }
        return bitmap.first();
    }

    @Override
    public int lastInt() {
        assert checkConsistency();
        int lastInt;
        if (complement) {
            lastInt = Math.toIntExact(bitmap.previousAbsentValue(domainSize() - 1));
            if (lastInt == -1) {
                throw new NoSuchElementException();
            }
        } else {
            lastInt = bitmap.last();
        }
        assert 0 <= lastInt && lastInt < domainSize();
        return lastInt;
    }

    @Override
    public int nextPresentIndex(int index) {
        assert checkConsistency();
        checkNonNegative(index);
        if (index >= domainSize()) {
            return -1;
        }
        if (complement) {
            int nextClear = Math.toIntExact(bitmap.nextAbsentValue(index));
            return nextClear >= domainSize() ? -1 : nextClear;
        }
        return Math.toIntExact(bitmap.nextValue(index));
    }

    @Override
    public int nextAbsentIndex(int index) {
        assert checkConsistency();
        checkNonNegative(index);
        if (index >= domainSize()) {
            return index;
        }
        if (complement) {
            int nextSet = Math.toIntExact(bitmap.nextValue(index));
            return nextSet == -1 ? domainSize() : nextSet;
        }
        return Math.toIntExact(bitmap.nextAbsentValue(index));
    }

    @Override
    public int previousPresentIndex(int index) {
        assert checkConsistency();
        checkNonNegative(index);
        int clampedIndex = Math.min(index, domainSize() - 1);
        if (complement) {
            return Math.toIntExact(bitmap.previousAbsentValue(clampedIndex));
        }
        return bitmap.isEmpty() ? -1 : Math.toIntExact(bitmap.previousValue(clampedIndex));
    }

    @Override
    public int previousAbsentIndex(int index) {
        assert checkConsistency();
        checkNonNegative(index);
        if (index >= domainSize()) {
            return index;
        }
        if (complement) {
            return bitmap.isEmpty() ? -1 : Math.toIntExact(bitmap.previousValue(index));
        }
        return Math.toIntExact(bitmap.previousAbsentValue(index));
    }

    @Override
    public IntIterator iterator() {
        assert checkConsistency();
        return RoaringBitmaps.iterator(complement ? complementBits() : this.bitmap);
    }

    @Override
    public boolean add(int index) {
        assert checkConsistency();
        checkInDomain(index);
        return complement ? bitmap.checkedRemove(index) : bitmap.checkedAdd(index);
    }

    @Override
    public void set(int index) {
        assert checkConsistency();
        checkInDomain(index);
        if (complement) {
            bitmap.remove(index);
        } else {
            bitmap.add(index);
        }
        assert checkConsistency();
    }

    @Override
    public void set(int index, boolean value) {
        assert checkConsistency();
        checkInDomain(index);
        if (value == complement) {
            bitmap.remove(index);
        } else {
            bitmap.add(index);
        }
        assert checkConsistency();
    }

    @Override
    public void set(int from, int to) {
        assert checkConsistency();
        checkInDomain(from, to);
        if (complement) {
            bitmap.remove(from, (long) to);
        } else {
            bitmap.add(from, (long) to);
        }
        assert checkConsistency();
    }

    @Override
    public void clear() {
        assert checkConsistency();
        if (complement) {
            bitmap.add(0L, domainSize());
        } else {
            bitmap.clear();
        }
        assert checkConsistency();
    }

    @Override
    public boolean remove(int index) {
        assert checkConsistency();
        checkInDomain(index);
        return complement ? bitmap.checkedAdd(index) : bitmap.checkedRemove(index);
    }

    @Override
    public void clear(int index) {
        assert checkConsistency();
        if (index >= domainSize()) {
            return;
        }
        if (complement) {
            bitmap.add(index);
        } else {
            bitmap.remove(index);
        }
        assert checkConsistency();
    }

    @Override
    public void clear(int from, int to) {
        assert checkConsistency();
        checkRange(from, to);
        int domainSize = domainSize();
        if (from >= domainSize) {
            return;
        }

        if (complement) {
            bitmap.add(from, (long) Math.min(to, domainSize));
        } else {
            bitmap.remove(from, (long) Math.min(to, domainSize));
        }
        assert checkConsistency();
    }

    @Override
    public void flip(int index) {
        assert checkConsistency();
        checkInDomain(index);
        bitmap.flip(index);
        assert checkConsistency();
    }

    @Override
    public void flip(int from, int to) {
        assert checkConsistency();
        checkInDomain(from, to);
        bitmap.flip(from, (long) to);
        assert checkConsistency();
    }

    @Override
    public boolean intersects(Collection indices) {
        if (indices instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) indices;

            // TODO This is crappy slow
            return RoaringBitmap.intersects(
                    complement ? complementBits() : bitmap,
                    other.isComplement() ? other.complementBits() : other.bitmap());
        }
        if (indices instanceof RoaringNatBitSet) {
            RoaringNatBitSet other = (RoaringNatBitSet) indices;

            if (complement) {
                return other.bitmap().getCardinality() > RoaringBitmap.andCardinality(other.bitmap(), bitmap);
            }
            return RoaringBitmap.intersects(bitmap, other.bitmap());
        }
        return super.intersects(indices);
    }

    @Override
    public void and(IntCollection indices) {
        assert checkConsistency();
        if (indices.isEmpty()) {
            clear();
        } else if (indices instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) indices;
            if (other.complement) {
                doAndComplement(other.bitmap, other.domainSize());
            } else {
                doAnd(other.bitmap);
            }
        } else if (indices instanceof RoaringNatBitSet) {
            RoaringNatBitSet other = (RoaringNatBitSet) indices;
            doAnd(other.bitmap());
        } else {
            doAnd(RoaringBitmaps.of(indices));
        }
        assert checkConsistency();
    }

    private void doAnd(RoaringBitmap other) {
        int domainSize = domainSize();
        if (complement) {
            bitmap.orNot(other, domainSize);
            bitmap.remove(domainSize, INFINITY);
        } else {
            bitmap.and(other);
        }
    }

    private void doAndComplement(RoaringBitmap other, int otherDomainSize) {
        int domainSize = domainSize();

        if (complement) {
            bitmap.or(other);
            bitmap.add(otherDomainSize, (long) domainSize);
            bitmap.remove(domainSize, (long) otherDomainSize);
        } else {
            bitmap.andNot(other);
            bitmap.remove(otherDomainSize, (long) domainSize);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean retainAll(Collection indices) {
        if (indices instanceof IntCollection) {
            return retainAll((IntCollection) indices);
        }
        if (isEmpty()) {
            return false;
        }
        if (indices.isEmpty()) {
            clear();
            return true;
        }
        int size = size();
        RoaringBitmap bitmap = new RoaringBitmap();
        for (Object o : indices) {
            int v = (Integer) o;
            if (v <= domainSize()) {
                bitmap.add(v);
            }
        }
        if (complement) {
            this.bitmap.orNot(bitmap, domainSize());
        } else {
            this.bitmap.and(bitmap);
        }
        return size() < size;
    }

    @Override
    public void andNot(IntCollection indices) {
        assert checkConsistency();
        if (isEmpty() || indices.isEmpty()) {
            return;
        }
        if (indices instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) indices;
            if (other.complement) {
                doAndNotComplement(other.bitmap, other.domainSize());
            } else {
                doAndNot(other.bitmap);
            }
        } else if (indices instanceof RoaringNatBitSet) {
            RoaringNatBitSet other = (RoaringNatBitSet) indices;
            doAndNot(other.bitmap());
        } else {
            if (complement) {
                indices.forEach((int index) -> {
                    if (index < domainSize()) {
                        bitmap.add(index);
                    }
                });
            } else {
                RoaringBitmap toRemove = new RoaringBitmap();

                bitmap.forEach((int index) -> {
                    if (indices.contains(index)) {
                        toRemove.add(index);
                    }
                });

                bitmap.andNot(toRemove);
            }
        }
        assert checkConsistency();
    }

    private void doAndNot(RoaringBitmap other) {
        if (complement) {
            bitmap.or(other);
            bitmap.remove(domainSize(), INFINITY);
        } else {
            bitmap.andNot(other);
        }
    }

    private void doAndNotComplement(RoaringBitmap other, int otherDomainSize) {
        int domainSize = domainSize();
        if (complement) {
            if (otherDomainSize < domainSize) { // TODO orNot bug
                RoaringBitmap tail = RoaringBitmaps.subset(bitmap, otherDomainSize, domainSize);
                bitmap.orNot(other, otherDomainSize);
                bitmap.or(tail);
            } else {
                bitmap.orNot(other, domainSize);
                bitmap.remove(domainSize, INFINITY); // TODO orNot bug
            }
        } else {
            if (otherDomainSize < domainSize) {
                RoaringBitmap tail = RoaringBitmaps.subset(bitmap, otherDomainSize, domainSize);
                bitmap.and(other);
                bitmap.or(tail);
            } else {
                bitmap.and(other);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean removeAll(Collection indices) {
        if (indices instanceof IntCollection) {
            return removeAll((IntCollection) indices);
        }
        if (isEmpty() || indices.isEmpty()) {
            return false;
        }
        int size = size();
        RoaringBitmap bitmap = new RoaringBitmap();
        for (Object o : indices) {
            int v = (Integer) o;
            if (v <= domainSize()) {
                bitmap.add(v);
            }
        }
        if (complement) {
            this.bitmap.or(bitmap);
        } else {
            this.bitmap.andNot(bitmap);
        }
        return size() < size;
    }

    @Override
    public void or(IntCollection indices) {
        assert checkConsistency();
        if (indices.isEmpty()) {
            return;
        }
        if (indices instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) indices;
            checkInDomain(other.lastInt());
            if (other.complement) {
                doOrComplement(other.bitmap, other.domainSize());
            } else {
                doOr(other.bitmap);
            }
        } else if (indices instanceof RoaringNatBitSet) {
            RoaringNatBitSet other = (RoaringNatBitSet) indices;
            checkInDomain(other.lastInt());
            doOr(other.bitmap());
        } else {
            doOr(RoaringBitmaps.of(indices));
        }
        assert checkConsistency();
    }

    private void doOr(RoaringBitmap other) {
        if (complement) {
            bitmap.andNot(other);
        } else {
            bitmap.or(other);
        }
    }

    private void doOrComplement(RoaringBitmap other, int otherDomainSize) {
        if (complement) {
            int domainSize = domainSize();

            if (otherDomainSize < domainSize) {
                RoaringBitmap tail = RoaringBitmaps.subset(bitmap, otherDomainSize, domainSize);
                bitmap.and(other);
                bitmap.or(tail);
            } else {
                bitmap.and(other);
            }
        } else {
            // TODO orNot bugs
            RoaringBitmap tail = RoaringBitmaps.subset(bitmap, otherDomainSize, domainSize());
            bitmap.orNot(other, otherDomainSize);
            bitmap.or(tail);
        }
    }

    @Override
    public void orNot(IntCollection indices) {
        assert checkConsistency();
        if (indices.isEmpty()) {
            set(0, domainSize());
        } else if (indices instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) indices;
            if (other.complement) {
                doOrNotComplement(other.bitmap, other.domainSize());
            } else {
                doOrNot(other.bitmap);
            }
        } else if (indices instanceof RoaringNatBitSet) {
            doOrNot(((RoaringNatBitSet) indices).bitmap());
        } else {
            doOrNot(RoaringBitmaps.of(indices));
        }
        assert checkConsistency();
    }

    private void doOrNot(RoaringBitmap other) {
        if (complement) {
            bitmap.and(other);
        } else {
            int domainSize = domainSize();
            bitmap.orNot(other, domainSize);
            bitmap.remove(domainSize, INFINITY);
        }
    }

    private void doOrNotComplement(RoaringBitmap other, int otherDomainSize) {
        int domainSize = domainSize();
        if (complement) {
            bitmap.andNot(other);
            if (otherDomainSize < domainSize) {
                bitmap.remove(otherDomainSize, (long) domainSize);
            }
        } else {
            bitmap.or(other);
            if (otherDomainSize < domainSize) {
                bitmap.add(otherDomainSize, (long) domainSize);
            } else {
                bitmap.remove(domainSize, (long) otherDomainSize);
            }
        }
    }

    @Override
    public void xor(IntCollection indices) {
        assert checkConsistency();
        if (indices.isEmpty()) {
            return;
        }
        if (indices instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) indices;
            checkInDomain(other.lastInt());

            bitmap.xor(other.bitmap);
            if (other.complement) {
                bitmap.flip(0L, other.domainSize());
            }
        } else if (indices instanceof RoaringNatBitSet) {
            RoaringNatBitSet other = (RoaringNatBitSet) indices;
            checkInDomain(other.lastInt());

            bitmap.xor(other.bitmap());
        } else {
            bitmap.xor(RoaringBitmaps.of(indices));
        }
        assert checkConsistency();
    }

    @Override
    public boolean removeIf(IntPredicate filter) {
        RoaringBitmap remove = new RoaringBitmap();
        if (complement) {
            // TODO is it faster to flip or use a complement iterator?
            bitmap.flip(0L, domainSize());
        }
        bitmap.forEach((int i) -> {
            if (filter.test(i)) {
                remove.add(i);
            }
        });
        if (complement) {
            bitmap.flip(0L, domainSize());
            bitmap.or(remove);
        } else {
            bitmap.andNot(remove);
        }
        return !remove.isEmpty();
    }

    @SuppressWarnings("OverridableMethodCallDuringObjectConstruction")
    @Override
    public RoaringBoundedNatBitSet clone() {
        assert checkConsistency();
        return new RoaringBoundedNatBitSet(bitmap.clone(), domainSize(), complement);
    }

    @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
    @Override
    public RoaringBoundedNatBitSet complement() {
        return complementView;
    }

    @Override
    public boolean equals(Object o) {
        assert checkConsistency();
        if (this == o) {
            return true;
        }
        if (!(o instanceof Set)) {
            return false;
        }
        if (isEmpty()) {
            return ((Collection) o).isEmpty();
        }
        if (((Collection) o).isEmpty()) {
            return false;
        }

        if (o instanceof RoaringBoundedNatBitSet) {
            RoaringBoundedNatBitSet other = (RoaringBoundedNatBitSet) o;

            int domainSize = domainSize();
            int otherDomainSize = other.domainSize();

            if (complement) {
                if (other.complement) {
                    if (domainSize == otherDomainSize) {
                        return bitmap.equals(other.bitmap);
                    }
                    RoaringBoundedNatBitSet smaller;
                    RoaringBoundedNatBitSet larger;
                    int smallerSize;
                    int largerSize;
                    if (domainSize < otherDomainSize) {
                        smaller = this;
                        larger = other;
                        smallerSize = domainSize;
                        largerSize = otherDomainSize;
                    } else {
                        smaller = other;
                        larger = this;
                        smallerSize = otherDomainSize;
                        largerSize = domainSize;
                    }

                    if (larger.bitmap.nextAbsentValue(smallerSize) < (long) largerSize) {
                        return false;
                    }

                    return smaller.bitmap.equals(RoaringBitmaps.subset(larger.bitmap, 0, smallerSize));
                }
            } else if (!other.complement) {
                return bitmap.equals(other.bitmap);
            }

            // complement != otherComplement
            int complementDomainSize = complement ? domainSize : otherDomainSize;
            RoaringBoundedNatBitSet nonComplementSet = complement ? other : this;
            assert !nonComplementSet.complement;

            return !RoaringBitmap.intersects(bitmap, other.bitmap)
                    && bitmap.getCardinality() + other.bitmap.getCardinality() == complementDomainSize
                    && (domainSize == otherDomainSize || nonComplementSet.lastInt() < complementDomainSize);
        }
        if (o instanceof RoaringNatBitSet) {
            RoaringNatBitSet other = (RoaringNatBitSet) o;

            if (other.lastInt() >= domainSize()) {
                return false;
            }
            if (isComplement()) {
                return size() == other.size() && !RoaringBitmap.intersects(bitmap, other.bitmap());
            }
            return bitmap.equals(other.bitmap());
        }
        return super.equals(o);
    }

    @SuppressWarnings("RedundantMethodOverride")
    @Override
    public int hashCode() {
        return super.hashCode();
    }

    RoaringBitmap bitmap() {
        return bitmap;
    }

    RoaringBitmap complementBits() {
        return RoaringBitmap.flip(bitmap, 0L, domainSize());
    }

    private boolean checkConsistency() {
        assert bitmap.isEmpty() || bitmap.last() < domainSize() : bitmap.last() + " " + domainSize();
        return true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy