Subversion Repositories splitter

Rev

Rev 548 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 2016
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * 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.
 */


package uk.me.parabola.splitter;

import java.util.Arrays;
import java.util.Iterator;

import it.unimi.dsi.fastutil.ints.IntArrayList;

/**
 * A partly set implementation. Used as a replacement for BitSet which is slow when
 * values are rather high, e.g. > 50000.
 *  
 * @author Gerd Petermann
 *
 */

public final class AreaSet implements Iterable<Integer> {
        private static final int BIN_SEARCH_LIMIT = 10;
        private final IntArrayList list;
        private boolean locked;
       
        /** Create empty set. */
        public AreaSet() {
                list = new IntArrayList();
        }
       
        /** Copy constructor creates set with the same entries.
         * @param other set to clone
         */

        public AreaSet(final AreaSet other) {
                if (!other.isEmpty()) {
                        list = new IntArrayList(other.list);
                } else
                        list = new IntArrayList();
        }
       
        /**
         * Create new set with one element.
         * @param index the index of the element
         */

        AreaSet(final int index) {
                list = new IntArrayList();
                list.add(index);
        }
       
        /**
         * Lock this set. A locked set cannot be changed.
         */

        public void lock() {
                this.list.trim();
                this.locked = true;
        }
       
    /**
     * Returns true if the element with the index
     * {@code bitIndex} is currently in this set; false otherwise.
     *
     * @param  index   the bit index
     * @return the value of the bit with the specified index
     */

        public boolean get(final int index) {
                if (list.size() < BIN_SEARCH_LIMIT) {
                        return list.contains(index);
                }
                return Arrays.binarySearch(list.elements(), 0, list.size(), index) >= 0;
        }

        /**
         * Add the element to the set. No effect if index is already in the set.
         * @param index the element
         */

        public void set(final int index) {
                if (locked)
                        throw new IllegalAccessError("AreaSet is locked");
                if (list.isEmpty()) {
                        list.add(index);
                } else {
                        int p = Arrays.binarySearch(list.elements(), 0, list.size(), index);
                        if (p < 0) {
                                list.add(-p - 1, index);
                        }
                }
        }

        /**
         * Remove the element from the set.
         * @param index the element
         */

        public void clear(final int index) {
                if (locked)
                        throw new IllegalAccessError("AreaSet is locked");
                int pos;
                if (list.size() < BIN_SEARCH_LIMIT) {
                        list.rem(index);
                } else {
                        pos = Arrays.binarySearch(list.elements(), 0, list.size(), index);
                        if (pos >= 0) {
                                list.removeInt(pos);
                }
                }
        }

        /**
         * Merge with other set. Result contains elements of both sets.
         * @param other the other set
         */

        void or(final AreaSet other) {
                if (locked)
                        throw new IllegalAccessError("AreaSet is locked");
                if (other.isEmpty())
                        return;
                if (list.isEmpty()) {
                        list.addAll(other.list);
                } else {
                        for (int i : other.list) {
                                set(i);
                        }
                }
        }
       
        /**
         * Remove elements in this set which are contained in the other set.
         * @param other the other set
         */

        public void subtract(final AreaSet other) {
                if (locked)
                        throw new IllegalAccessError("AreaSet is locked");
                for (int i : other.list) {
                        clear(i);
                }
        }
       
        /**
         * @return number of elements in this set
         */

        public int cardinality() {
                return list.size();
        }

        /**
         * @return true if this set contains no elements.
         */

        public boolean isEmpty() {
                return cardinality() == 0;
        }
       
        /**
         * remove all elements from the set. Doesn't free storage.
         */

        public void clear() {
                if (locked)
                        throw new IllegalAccessError("AreaSet is locked");
                list.clear();
        }
       

        /**
         * @return an iterator over this set.
         */

        @Override
        public Iterator<Integer> iterator() {
                return list.iterator();
               
        }
       
        @Override
        public int hashCode() {
                return list.hashCode();
        }

        @Override
        public boolean equals(final Object obj) {
                if (!(obj instanceof AreaSet))
                        return false;
                if (this == obj)
                        return true;
                AreaSet other = (AreaSet) obj;
                if (isEmpty() && other.isEmpty())
                        return true;
                return list.equals(other.list);
        }

        @Override
        public String toString() {
                return list.toString();
        }
}