/******************************************************************************
* Copyright (c) 2009 - 2015 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.memsat.representation;
import static com.ibm.wala.memsat.util.Strings.prettyPrint;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import com.ibm.wala.memsat.util.Nodes;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.IntExpression;
import kodkod.ast.Relation;
import kodkod.engine.Evaluator;
import kodkod.util.ints.SparseSequence;
import kodkod.util.ints.TreeSequence;
/**
* Wraps a collection of Kodkod expressions that represent the contents of a given array.
*
* @specfield refs: Expression // expression representing the arrays whose contents are represented by this array expression
* @specfield card: [0..) // maximum number of entries, in each array from this.arrays, storing a value other than the default
* @specfield indices: [0..maxCard) lone->one Expression // indices[i] maps each member of this.arrays to a bitset encoding of its ith index
* @specfield values: [0..maxCard) lone->one (Node+RealExpression) // values[i] maps each member of this.arrays to the value it stores at the ith index
* @specfield zero: Expression // default value
* @invariant all i: [0..maxCard) | indices[i].arity() = 2 and values[i].arity() = zero.arity() + 1
* @author Emina Torlak
*/
public final class ArrayExpression<T> extends HeapExpression<T> {
private final Expression domain;
private final Expression[] indices, values;
private final Interpreter<T> valMeaning;
private final Interpreter<IntExpression> idxMeaning;
/**
* Constructs a new array from the given arguments.
* @effects
* this.refs' = refs and
* this.card' = card and
* this.zero' = valMeaning.zero() and
* all i: [0..card) |
* this.indeces'[i] = Relation.binary(name+"@idx"+i) and
* this.values'[i] = Relation.nary(name+"@val"+i,this.zero'.arity+1)
*/
private ArrayExpression(String name, int card, Expression refs,
Interpreter<IntExpression> idxMeaning,
Interpreter<T> valMeaning) {
this.idxMeaning = idxMeaning;
this.valMeaning = valMeaning;
this.indices = new Expression[card];
this.values = new Expression[card];
this.domain = refs;
final int valArity = valMeaning.defaultObj().arity() + 1;
for(int i = 0; i < card; i++) {
indices[i] = Relation.binary(name+"@idx"+i);
values[i] = Relation.nary(name + "@val"+i, valArity);
}
}
/**
* Constructs a new array expression using the given template and values.
* specified instance at the specified index.
* @requires values.length = old.values.length
* @effects this.indeces' = old.indeces' and this.refs' = old.refs and
* this.zero' = old.zero and this.card' = old.card and
* this.values' = values
*/
private ArrayExpression(ArrayExpression<T> old, Expression[] values) {
this.idxMeaning = old.idxMeaning;
this.valMeaning = old.valMeaning;
this.indices = old.indices;
this.domain = old.domain;
this.values = values;
}
/**
* Returns an array expression constructed from the given arguments.
* @return { a: ArrayExpression<T> |
* a.card = card and a.zero = valM.zero() and
* a.refs = refs and
* all i: [0..card) |
* a.indeces[i] = Relation.binary(name+"@idx"+i) and
* a.values[i] = Relation.nary(name+"@val"+i,this.zero'.arity+1) }
*/
static <T> ArrayExpression<T> array(String name, int card, Expression refs, Interpreter<IntExpression> intM, Interpreter<T> valM) {
return new ArrayExpression<T>(name,card,refs,intM,valM);
}
/**
* Returns this.indeces[i].
* @return this.indeces[i]
*/
final Expression index(int i) { return indices[i]; }
/**
* Returns this.values[i].
* @return this.values[i]
*/
final Expression value(int i) { return values[i]; }
/**
* Returns true.
* @return true
* @see com.ibm.wala.memsat.representation.HeapExpression#isArray()
*/
public final boolean isArray() { return true; }
/**
* Returns the interpreter used for interpreting this.indeces.
* @return interpreter used for interpreting this.indeces.
*/
public final Interpreter<IntExpression> indexInterpreter() { return idxMeaning; }
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.representation.HeapExpression#valueInterpreter()
*/
public final Interpreter<T> valueInterpreter() { return valMeaning; }
/**
* Returns this.card.
* @return this.card
*/
public final int cardinality() { return indices.length; }
/**
* Returns the Expression encoding the set of instances whose contents field is
* represented by this array expression.
* @return this.refs
*/
public final Expression instances() { return domain; }
/**
* Returns the ith non-default index for the specified array reference.
* @requires 0 <= i < this.card
* @return [[ref]] . [[ this.indices[i] ]]
*/
public final IntExpression index(Expression ref, int i) {
return idxMeaning.fromObj(ref.join(indices[i]));
}
/**
* Returns the value at the ith non-default index for the specified array reference.
* @requires 0 <= i < this.card
* @return [[ref]] . [[ this.values[i] ]]
*/
public final T value(Expression ref, int i) {
return valMeaning.fromObj(ref.join(values[i]));
}
/**
* Returns the value that the given array stores at the specified index.
* @requires ref.arity = 1
* @return
* let i = this.indices, v = this.values, c = this.card, empty = Utils.empty(this.zero.arity)) |
* { t: T | one e: this.convert[t] | [[e]] =
* ([[ref]].[[i[0]]] = [[index]] => [[ref]].[[v[0]]] else empty) + ... +
* ([[ref]].[[i[c-1]]] = [[index]] => [[ref]].[[v[c-1]]] else empty) +
* ([[ref]].[[i[0]]] != [[index]] and ... and [[ref]].[[i[c-1]]] != [[index]] =>
* zero else empty) }
*/
public final T read(Expression ref, IntExpression index) {
final int size = indices.length;
final Expression zero = valMeaning.defaultObj();
final Expression idx = idxMeaning.toObj(index);
final Formula[] idxTest = new Formula[size];
final Expression[] valsAtIdx = new Expression[size+1];
final Expression empty = Nodes.empty(zero.arity());
for(int i = 0; i < size; i++) {
idxTest[i] = ref.join(indices[i]).eq(idx);
valsAtIdx[i] = idxTest[i].thenElse(ref.join(values[i]), empty);
}
for(int i = 0; i < size; i++) {
idxTest[i] = idxTest[i].not();
}
valsAtIdx[size] = Formula.and(idxTest).thenElse(zero, empty);
return valMeaning.fromObj(Expression.union(valsAtIdx));
}
/**
* Returns an array expression that represents the state of this ArrayExpression
* after the specified value has been written to the given index of <tt>ref</tt>.
* @requires ref.arity = 1
* @return
* let i = this.indices, v = this.values, c = this.card, empty = Utils.empty(this.zero.arity) |
* { a: ArrayExpression | a.indices = i and a.card = c and a.zero = this.zero and
* (all j: [0..c) | [[a.values[j]]] =
* [[v[j]]] ++ ([[ref]].[[i[j]]] = [[index]] => [[ref]]->[[value]] else empty)) }
*/
public final ArrayExpression<T> write(Expression ref, IntExpression index, T value) {
final Expression idx = idxMeaning.toObj(index), val = valMeaning.toObj(value);
final Expression[] retValues = new Expression[values.length];
final Expression empty = Nodes.empty(valMeaning.defaultObj().arity()+1);
final Expression overrider = ref.product(val);
for(int i = 0; i < values.length; i++) {
Formula idxTest = ref.join(indices[i]).eq(idx);
retValues[i] = values[i].override(idxTest.thenElse(overrider, empty));
}
return new ArrayExpression<T>(this, retValues);
}
/**
* Returns the sparse sequence mapping each index in this.indeces[ref]
* to its corresponding value in this.values[ref], as given by the
* specified evaluator. In particular, if the
* type if this.values is RealExpression, then the returned value will be
* a sequence of Floats; if the type of
* this value is an IntExpression, the returned value will be a sequence of Integers;
* if the type of this value is an Expression, the returned value will be a sequence of Objects;
* finally, if the type of this.values is Formula, the returned value will be a sequence of Booleans.
* @requires the given evaluator's instance has bindings
* for all relations (if any) at the leaves of this array expression.
* @requires eval.evaluate(ref) in eval.evaluate(this.refs)
* @return sparse sequence mapping each index in this.indeces[ref]
* to its corresponding value in this.values[ref], as given by the
* specified evaluator.
*/
@SuppressWarnings("unchecked")
public final <V> SparseSequence<V> evaluate(Expression ref, Evaluator eval) {
final SparseSequence<V> ret = new TreeSequence<V>();
for(int i = 0, card = cardinality(); i < card; i++) {
int idx = eval.evaluate(idxMeaning.fromObj(ref.join(indices[i])));
V value = (V) valMeaning.evaluate(valMeaning.fromObj(ref.join(values[i])),eval);
ret.put(idx, value);
}
return ret;
}
/**
* Returns an array expression that evaluates to the ith value returned by the
* arrays' iterator iff the ith guard returned by the guards' iterator evaluates
* to true. This method assumes that at most one of the given guards
* evaluates to true. If none do, the returned value will evaluate to
* an empty array (i.e. an array that maps everything in its domain to
* a T whose Expression value is the empty set).
* @requires guards and values contain the same (positive) number of objects
* @requires at most one of the guards can ever evaluate to true.
* @requires all array expressions in the given collection represent the same array
* @return an array expression that evaluates to the ith value returned by the
* arrays' iterator iff the ith guard returned by the guards' iterator evaluates
* to true.
*/
@SuppressWarnings("unchecked")
static <T> ArrayExpression<T> phi(Collection<Formula> guards, Collection<ArrayExpression<T>> arrays) {
final int phiNum = guards.size();
assert phiNum == arrays.size();
assert phiNum > 0;
final ArrayExpression<T> first = arrays.iterator().next();
final Expression empty = Nodes.empty(first.valMeaning.defaultObj().arity()+1);
final int card = first.cardinality();
final List<Expression>[] phis = new ArrayList[card];
for(int i = 0; i < card; i++) {
phis[i] = new ArrayList<Expression>(phiNum);
}
final Iterator<Formula> gItr = guards.iterator();
final Iterator<ArrayExpression<T>> aItr = arrays.iterator();
while(gItr.hasNext()) {
Formula guard = gItr.next();
ArrayExpression<T> array = aItr.next();
assert array.indices == first.indices;
for(int i = 0; i < card; i++) {
phis[i].add(guard.thenElse(array.values[i], empty));
}
}
final Expression[] phiVals = new Expression[card];
for(int i = 0; i < card; i++) {
phiVals[i] = phis[i].isEmpty() ? empty : Expression.union(phis[i]);
}
return new ArrayExpression<T>(first, phiVals);
}
public String toString(){
String res = prettyPrint(domain, 0);
res += "[";
for(int i = 0; i < indices.length; i++){
res += prettyPrint(values[i],0);
}
res += "]";
return res;
}
}