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

com.ibm.wala.util.intset.SparseIntSet Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.util.intset;

import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;

/** A sparse ordered, duplicate-free, fully-encapsulated set of integers; not necessary mutable */
public class SparseIntSet implements IntSet {

  private static final long serialVersionUID = 2394141733718319022L;

  private static final int SINGLETON_CACHE_SIZE = 5000;

  private static final SparseIntSet[] singletonCache = new SparseIntSet[SINGLETON_CACHE_SIZE];

  static {
    for (int i = 0; i < SINGLETON_CACHE_SIZE; i++) {
      singletonCache[i] = new SparseIntSet(new int[] {i});
    }
  }

  // TODO: I'm not thrilled with exposing these to subclasses, but
  // it seems expedient for now.
  /** The backing store of int arrays */
  @SuppressWarnings("NullAway.Init")
  protected int[] elements;

  /** The number of entries in the backing store that are valid. */
  protected int size = 0;

  protected SparseIntSet(int size) {
    elements = new int[size];
    this.size = size;
  }

  /**
   * Subclasses should use this with extreme care. Do not allow the backing array to escape
   * elsewhere.
   */
  protected SparseIntSet(int[] backingArray) {
    if (backingArray == null) {
      throw new IllegalArgumentException("backingArray is null");
    }
    elements = backingArray;
    this.size = backingArray.length;
  }

  /** Subclasses should use this with extreme care. */
  @NullUnmarked
  public SparseIntSet() {
    elements = null;
    this.size = 0;
  }

  protected SparseIntSet(SparseIntSet S) {
    cloneState(S);
  }

  @NullUnmarked
  private void cloneState(SparseIntSet S) {
    if (S.elements != null) {
      elements = S.elements.clone();
    } else {
      elements = null;
    }
    this.size = S.size;
  }

  public SparseIntSet(IntSet S) throws IllegalArgumentException {
    if (S == null) {
      throw new IllegalArgumentException("S == null");
    }
    if (S instanceof SparseIntSet) {
      cloneState((SparseIntSet) S);
    } else {
      elements = new int[S.size()];
      size = S.size();
      S.foreach(
          new IntSetAction() {
            private int index = 0;

            @Override
            public void act(int i) {
              elements[index++] = i;
            }
          });
    }
  }

  /** Does this set contain value x? */
  @Override
  public final boolean contains(int x) {
    if (elements == null) {
      return false;
    }
    return IntSetUtil.binarySearch(elements, x, 0, size - 1) >= 0;
  }

  /**
   * @return index i s.t. elements[i] == x, or -1 if not found.
   */
  public final int getIndex(int x) {
    if (elements == null) {
      return -1;
    }
    return IntSetUtil.binarySearch(elements, x, 0, size - 1);
  }

  @Override
  public final int size() {
    return size;
  }

  @Override
  public final boolean isEmpty() {
    return size == 0;
  }

  public final int elementAt(int idx) throws NoSuchElementException {
    if (idx < 0) {
      throw new IllegalArgumentException("invalid idx: " + idx);
    }
    if (elements == null || idx >= size) {
      throw new NoSuchElementException("Index: " + idx);
    }
    return elements[idx];
  }

  private boolean sameValueInternal(SparseIntSet that) {
    if (size != that.size) {
      return false;
    } else {
      for (int i = 0; i < size; i++) {
        if (elements[i] != that.elements[i]) {
          return false;
        }
      }
      return true;
    }
  }

  @Override
  public boolean sameValue(IntSet that) throws IllegalArgumentException, UnimplementedError {
    if (that == null) {
      throw new IllegalArgumentException("that == null");
    }
    if (that instanceof SparseIntSet) {
      return sameValueInternal((SparseIntSet) that);
    } else if (that instanceof BimodalMutableIntSet) {
      return that.sameValue(this);
    } else if (that instanceof BitVectorIntSet) {
      return that.sameValue(this);
    } else if (that instanceof MutableSharedBitVectorIntSet) {
      return sameValue(((MutableSharedBitVectorIntSet) that).makeSparseCopy());
    } else {
      Assertions.UNREACHABLE(that.getClass().toString());
      return false;
    }
  }

  /**
   * @return true iff {@code this} is a subset of {@code that}.
   *     

Faster than: {@code this.diff(that) == EMPTY}. */ private boolean isSubsetInternal(SparseIntSet that) { if (elements == null) return true; if (that.elements == null) return false; if (this.equals(that)) return true; if (this.sameValue(that)) { return true; } int[] ar = this.elements; int ai = 0; int al = size; int[] br = that.elements; int bi = 0; int bl = that.size; while (ai < al && bi < bl) { int cmp = (ar[ai] - br[bi]); // (fail when element only found in 'a') if (cmp > 0) { // a greater bi++; } else if (cmp < 0) { // b greater return false; } else { ai++; bi++; } } if (bi == bl && ai < al) { // we ran off the end of b without finding an a return false; } return true; } /** Compute the asymmetric difference of two sets, a \ b. */ public static SparseIntSet diff(SparseIntSet A, SparseIntSet B) { return new SparseIntSet(diffInternal(A, B)); } public static int[] diffInternal(SparseIntSet A, SparseIntSet B) { if (A == null) { throw new IllegalArgumentException("A is null"); } if (B == null) { throw new IllegalArgumentException("B is null"); } if (A.isEmpty()) { return new int[0]; } else if (B.isEmpty()) { int[] newElts = new int[A.size]; System.arraycopy(A.elements, 0, newElts, 0, A.size); return newElts; } else if (A.equals(B)) { return new int[0]; } else if (A.sameValue(B)) { return new int[0]; } int[] ar = A.elements; int ai = 0; int al = A.size; int[] br = B.elements; int bi = 0; int bl = B.size; int[] cr = new int[al]; // allocate enough (i.e. too much) int ci = 0; while (ai < al && bi < bl) { int cmp = (ar[ai] - br[bi]); // (accept element when only found in 'a') if (cmp > 0) { // a greater bi++; } else if (cmp < 0) { // b greater cr[ci++] = ar[ai]; ai++; } else { ai++; bi++; } } // append a's tail if any if (ai < al) { int tail = al - ai; System.arraycopy(ar, ai, cr, ci, tail); ci += tail; } // now compact cr to 'just enough' ar = new int[ci]; System.arraycopy(cr, 0, ar, 0, ci); // ar := cr return ar; } @Override public String toString() { StringBuilder sb = new StringBuilder(6 * size); sb.append("{ "); if (elements != null) { for (int ii = 0; ii < size; ii++) { sb.append(elements[ii]); sb.append(' '); } } sb.append('}'); return sb.toString(); } /** * Reverse of toString(): "{2,3}" -> [2,3] * * @throws IllegalArgumentException if str is null */ public static int[] parseIntArray(String str) { if (str == null) { throw new IllegalArgumentException("str is null"); } int len = str.length(); if (len == 0 || str.charAt(0) != '{' || str.charAt(len - 1) != '}') { throw new IllegalArgumentException(str); } str = str.substring(1, len - 1); StringTokenizer tok = new StringTokenizer(str, " ,"); // XXX not very efficient: TreeSet set = new TreeSet<>(); while (tok.hasMoreTokens()) { set.add(Integer.decode(tok.nextToken())); } int[] result = new int[set.size()]; int i = 0; for (Integer I : set) { result[i++] = I; } return result; } public static SparseIntSet singleton(int i) { if (i >= 0 && i < SINGLETON_CACHE_SIZE) { return singletonCache[i]; } else { return new SparseIntSet(new int[] {i}); } } public static SparseIntSet pair(int i, int j) { if (i == j) { return SparseIntSet.singleton(i); } if (j > i) { return new SparseIntSet(new int[] {i, j}); } else { return new SparseIntSet(new int[] {j, i}); } } @Override public IntSet intersection(IntSet that) { if (that == null) { throw new IllegalArgumentException("that == null"); } if (that instanceof SparseIntSet) { MutableSparseIntSet temp = MutableSparseIntSet.make(this); temp.intersectWith((SparseIntSet) that); return temp; } else if (that instanceof BitVectorIntSet) { SparseIntSet s = ((BitVectorIntSet) that).toSparseIntSet(); MutableSparseIntSet temp = MutableSparseIntSet.make(this); temp.intersectWith(s); return temp; } else if (that instanceof MutableSharedBitVectorIntSet) { MutableSparseIntSet temp = MutableSparseIntSet.make(this); temp.intersectWith(that); return temp; } else { // this is really slow. optimize as needed. MutableSparseIntSet temp = MutableSparseIntSet.makeEmpty(); for (IntIterator it = intIterator(); it.hasNext(); ) { int x = it.next(); if (that.contains(x)) { temp.add(x); } } return temp; } } @Override public IntSet union(IntSet that) { MutableSparseIntSet temp = new MutableSparseIntSet(); temp.addAll(this); temp.addAll(that); return temp; } @Override public IntIterator intIterator() { return new IntIterator() { int i = 0; @Override public boolean hasNext() { return (i < size); } @Override public int next() throws NoSuchElementException { if (elements == null) { throw new NoSuchElementException(); } return elements[i++]; } }; } /** * @return the largest element in the set */ @Override public final int max() throws IllegalStateException { if (elements == null) { throw new IllegalStateException("Illegal to ask max() on an empty int set"); } return (size > 0) ? elements[size - 1] : -1; } @Override public void foreach(IntSetAction action) { if (action == null) { throw new IllegalArgumentException("null action"); } for (int i = 0; i < size; i++) action.act(elements[i]); } /** * @see com.ibm.wala.util.intset.IntSet#foreach(com.ibm.wala.util.intset.IntSetAction) */ @Override public void foreachExcluding(IntSet X, IntSetAction action) { if (action == null) { throw new IllegalArgumentException("null action"); } for (int i = 0; i < size; i++) { if (!X.contains(elements[i])) { action.act(elements[i]); } } } /** * @return a new sparse int set which adds j to s * @throws IllegalArgumentException if s is null */ public static SparseIntSet add(SparseIntSet s, int j) { if (s == null) { throw new IllegalArgumentException("s is null"); } if (s.elements == null) { return SparseIntSet.singleton(j); } SparseIntSet result = new SparseIntSet(s.size + 1); int k = 0; int m = 0; while (k < s.elements.length && s.elements[k] < j) { result.elements[k++] = s.elements[m++]; } if (k == s.size) { result.elements[k] = j; } else { if (s.elements[k] == j) { result.size--; } else { result.elements[k++] = j; } while (k < result.size) { result.elements[k++] = s.elements[m++]; } } return result; } @Override public boolean isSubset(@Nullable IntSet that) { if (that == null) { throw new IllegalArgumentException("null that"); } if (that instanceof SparseIntSet) { return isSubsetInternal((SparseIntSet) that); } else if (that instanceof BitVectorIntSet) { return isSubsetInternal((BitVectorIntSet) that); } else { // really slow. optimize as needed. for (IntIterator it = intIterator(); it.hasNext(); ) { if (!that.contains(it.next())) { return false; } } return true; } } private boolean isSubsetInternal(BitVectorIntSet set) { for (int i = 0; i < size; i++) { if (!set.contains(elements[i])) { return false; } } return true; } @Override public boolean containsAny(IntSet set) { if (set instanceof SparseIntSet) { return containsAny((SparseIntSet) set); } else if (set instanceof BimodalMutableIntSet) { return set.containsAny(this); } else { for (int i = 0; i < size; i++) { if (set.contains(elements[i])) { return true; } } return false; } } public boolean containsAny(SparseIntSet set) throws IllegalArgumentException { if (set == null) { throw new IllegalArgumentException("set == null"); } int i = 0; for (int j = 0; j < set.size; j++) { int x = set.elements[j]; while (i < size && elements[i] < x) { i++; } if (i == size) { return false; } else if (elements[i] == x) { return true; } } return false; } /** * @return contents as an int[] */ public int[] toIntArray() { int[] result = new int[size]; if (size > 0) { System.arraycopy(elements, 0, result, 0, size); } return result; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy