/* * Copyright (C) 2014 University of Freiburg * * This file is part of SMTInterpol. * * SMTInterpol is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SMTInterpol 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>. */ package de.uni_freiburg.informatik.ultimate.smtinterpol.model; import java.util.Iterator; import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol; import de.uni_freiburg.informatik.ultimate.logic.Sort; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.Theory; public class ArrayValue { private static class Entry { private final int mKey; private int mValue; private Entry mNext; private Entry mListNext, mListPrev; // NOPMD Entry() { mKey = -2; mValue = -2; mNext = null; mListNext = mListPrev = this; } public Entry(int key, int value, Entry next, Entry sentinel) { mKey = key; mValue = value; mNext = next; mListNext = sentinel; mListPrev = sentinel.mListPrev; sentinel.mListPrev.mListNext = this; sentinel.mListPrev = this; } public void unlink() { mListNext.mListPrev = mListPrev; mListPrev.mListNext = mListNext; } public int getKey() { return mKey; } public int getValue() { return mValue; } public int setValue(int value) { final int res = mValue; mValue = value; return res; } @Override public int hashCode() { return mKey + 3643 * mValue; } @Override public String toString() { return "[" + mKey + "," + mValue + "]"; } } private static class IntIntMap { Entry[] mTable; int mSize; int mThreshold; final Entry mList; private final static float LOAD_FACTOR = 0.75f; public IntIntMap() { this(8); } public IntIntMap(int capacity) { mTable = new Entry[capacity]; mSize = 0; mThreshold = (int) (capacity * LOAD_FACTOR); mList = new Entry(); } public IntIntMap(IntIntMap other) { mTable = new Entry[other.mTable.length]; mSize = 0; mThreshold = other.mThreshold; mList = new Entry(); for (final Entry e : other.forwardInsertionOrder()) { add(e.getKey(), e.getValue()); } } private int idx2bucket(int idx) { return idx & (mTable.length - 1); } private void grow() { final Entry[] old = mTable; mTable = new Entry[mTable.length << 1]; mThreshold = (int) (mTable.length * LOAD_FACTOR); for (final Entry e : old) { for (Entry bucket = e; bucket != null; ) { final int idx = idx2bucket(bucket.mKey); final Entry reinsert = bucket; bucket = bucket.mNext; reinsert.mNext = mTable[idx]; mTable[idx] = reinsert; } } } public int add(int key, int value) { if (mSize >= mThreshold) { grow(); } final int hash = idx2bucket(key); Entry e = findEntry(hash, key); if (e == null) { ++mSize; e = new Entry(key, value, mTable[hash], mList); mTable[hash] = e; return value; } return e.setValue(value); } public int remove(int key) { final int hash = idx2bucket(key); Entry prev = null; for (Entry bucket = mTable[hash]; bucket != null; bucket = bucket.mNext) { if (bucket.getKey() == key) { bucket.unlink(); if (prev == null) { mTable[hash] = bucket.mNext; } else { prev.mNext = bucket.mNext; } --mSize; return bucket.getValue(); } prev = bucket; } return 0; } public int get(int key, boolean partial) { final int hash = idx2bucket(key); final Entry e = findEntry(hash, key); if (e == null) { return partial ? -1 : 0; } return e.getValue(); } private Entry findEntry(int hash, int key) { for (Entry bucket = mTable[hash]; bucket != null; bucket = bucket.mNext) { if (bucket.getKey() == key) { return bucket; } } return null; } public boolean containsKey(int key) { final int hash = idx2bucket(key); return findEntry(hash, key) != null; } public int getSize() { return mSize; } @Override public int hashCode() { int hash = 0; for (final Entry e : forwardInsertionOrder()) { hash += e.hashCode(); } return hash; } @Override public boolean equals(Object other) { if (!(other instanceof IntIntMap)) { return false; } final IntIntMap o = (IntIntMap) other; if (o.getSize() != mSize) { return false; } for (final Entry e : o.forwardInsertionOrder()) { final int hash = idx2bucket(e.getKey()); final Entry my = findEntry(hash, e.getKey()); if (my == null || my.getValue() != e.getValue()) { return false; } } return true; } public Iterable<Entry> forwardInsertionOrder() { return new Iterable<ArrayValue.Entry>() { @Override public Iterator<Entry> iterator() { return new Iterator<ArrayValue.Entry>() { private Entry mCur = mList.mListNext; @Override public boolean hasNext() { return mCur != mList; } @Override public Entry next() { final Entry cur = mCur; mCur = mCur.mListNext; return cur; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } public Iterable<Entry> backwardInsertionOrder() { return new Iterable<ArrayValue.Entry>() { @Override public Iterator<Entry> iterator() { return new Iterator<ArrayValue.Entry>() { private Entry mCur = mList.mListPrev; @Override public boolean hasNext() { return mCur != mList; } @Override public Entry next() { final Entry cur = mCur; mCur = mCur.mListPrev; return cur; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append('{'); for (final Entry e : forwardInsertionOrder()) { sb.append(e); } sb.append('}'); return sb.toString(); } } public final static ArrayValue DEFAULT_ARRAY = new ArrayValue(null); private final IntIntMap mValues; private IntIntMap mDiffMap; public ArrayValue() { this(new IntIntMap()); } private ArrayValue(IntIntMap map) { mValues = map; mDiffMap = null; } public int store(int idx, int val) { if (val == 0) { return mValues.remove(idx); } return mValues.add(idx, val); } public int select(int idx, boolean partial) { return mValues == null ? 0 : mValues.get(idx, partial); } @Override public int hashCode() { return mValues == null ? 0 : mValues.hashCode(); } @Override public boolean equals(Object other) { if (!(other instanceof ArrayValue)) { return false; } if (this == DEFAULT_ARRAY) { return other == DEFAULT_ARRAY; } if (mValues.getSize() == 0) { return other == DEFAULT_ARRAY; } return mValues.equals(((ArrayValue) other).mValues); } ArrayValue copy() { final IntIntMap copy = mValues == null ? new IntIntMap() : new IntIntMap(mValues); return new ArrayValue(copy); } public Term toSMTLIB(Sort s, Theory t, SortInterpretation index, SortInterpretation value) { Term res = t.term(t.getFunctionWithResult("@0", null, s)); if (mValues.getSize() != 0) { final Sort indexSort = s.getArguments()[0]; final Sort valueSort = s.getArguments()[1]; final FunctionSymbol store = t.getFunction( "store", s, indexSort, valueSort); for (final Entry e : mValues.backwardInsertionOrder()) { res = t.term(store, res, index.get(e.getKey(), indexSort, t), value.get(e.getValue(), valueSort, t)); } } return res; } public boolean containsMapping(int val) { return mValues == null ? false : mValues.containsKey(val); } public void addDiff(int snd, int val) { if (mDiffMap == null) { mDiffMap = new IntIntMap(); } mDiffMap.add(snd, val); } public int computeDiff(int idx, ArrayValue other) { if (mValues == null) { return other == this ? 0 : other.computeDiff(0, this); } // diffs if (mDiffMap != null && mDiffMap.containsKey(idx)) { return mDiffMap.get(idx, false); } // no diff matches for (final Entry e : mValues.forwardInsertionOrder()) { if (other.select(e.getKey(), false) != e.getValue()) { return e.getKey(); } } assert this == other; return 0; } @Override public String toString() { return mValues == null ? "@0" : mValues.toString(); } }