/*
* Copyright (C) 2009-2012 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.interpolate;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol;
import de.uni_freiburg.informatik.ultimate.logic.Rational;
import de.uni_freiburg.informatik.ultimate.logic.Sort;
import de.uni_freiburg.informatik.ultimate.logic.Term;
import de.uni_freiburg.informatik.ultimate.logic.Theory;
import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.SMTAffineTerm;
import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.SharedTerm;
import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.InfinitNumber;
import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.LinVar;
import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.MutableAffinTerm;
/**
* Represents a modifiable affin term, i.e. SUM_i c_i * x_i + c,
* where the x_i are initially nonbasic variable.
*
* @author hoenicke.
*/
public class InterpolatorAffineTerm {
Map<Term, Rational> mSummands = new HashMap<Term, Rational>();
InfinitNumber mConstant;
public InterpolatorAffineTerm() {
mConstant = InfinitNumber.ZERO;
}
public InterpolatorAffineTerm(InterpolatorAffineTerm iat) {
mConstant = iat.getConstant();
mSummands.putAll(iat.getSummands());
}
public InterpolatorAffineTerm(Map<LinVar, Rational> sum, InfinitNumber c) {
mConstant = c;
for (final Entry<LinVar, Rational> entry : sum.entrySet()) {
mSummands.put(entry.getKey().getSharedTerm().getTerm(), entry.getValue());
}
}
public InterpolatorAffineTerm(MutableAffinTerm mat) {
this(mat.getSummands(), mat.getConstant());
}
public InterpolatorAffineTerm add(Rational c) {
mConstant = mConstant.add(new InfinitNumber(c, 0));
return this;
}
public InterpolatorAffineTerm add(InfinitNumber c) {
mConstant = mConstant.add(c);
return this;
}
public InterpolatorAffineTerm add(Rational c, MutableAffinTerm a) {
if (c != Rational.ZERO) {
addLinVarMap(c, a.getSummands());
mConstant = mConstant.add(a.getConstant().mul(c));
}
return this;
}
public InterpolatorAffineTerm add(Rational c, Term term) {
if (!c.equals(Rational.ZERO)) {
addSimple(c, term);
}
return this;
}
public InterpolatorAffineTerm add(Rational c, SharedTerm term) {
if (c.equals(Rational.ZERO)) {
return this;
}
if (term.getTerm() instanceof SMTAffineTerm) {
add(c, term.getClausifier().createMutableAffinTerm(term));
} else {
addSimple(c, term.getLinVar());
}
return this;
}
public InterpolatorAffineTerm add(Rational c, LinVar var) {
if (c.equals(Rational.ZERO)) {
return this;
}
if (var.isInitiallyBasic()) {
for (final Map.Entry<LinVar, BigInteger> me : var.getLinTerm().entrySet()) {
add(c.mul(me.getValue()), me.getKey());
}
} else {
addSimple(c, var);
}
return this;
}
private void addLinVarMap(Rational c, Map<LinVar, Rational> linterm) {
for (final Map.Entry<LinVar, Rational> summand : linterm.entrySet()) {
addSimple(c.mul(summand.getValue()), summand.getKey());
}
}
private void addMap(Rational c, Map<Term, Rational> linterm) {
for (final Map.Entry<Term, Rational> summand : linterm.entrySet()) {
addSimple(c.mul(summand.getValue()), summand.getKey());
}
}
private void addSimple(Rational c, LinVar term) {
addSimple(c, term.getSharedTerm().getRealTerm());
}
private void addSimple(Rational c, Term term) {
assert (/*!term.getLinVar().isInitiallyBasic() &&*/ !c.equals(Rational.ZERO));
final Rational oldc = mSummands.remove(term);
if (oldc != null) {
c = oldc.add(c);
if (c.equals(Rational.ZERO)) {
return;
}
}
mSummands.put(term,c);
}
public InterpolatorAffineTerm add(Rational c, InterpolatorAffineTerm a) {
if (c != Rational.ZERO) {
addMap(c, a.mSummands);
mConstant = mConstant.add(a.mConstant.mul(c));
}
return this;
}
public InterpolatorAffineTerm mul(Rational c) {
if (c.equals(Rational.ZERO)) {
mSummands.clear();
} else if (!c.equals(Rational.ONE)) {
for (final Map.Entry<Term, Rational> summand : mSummands.entrySet()) {
summand.setValue(c.mul(summand.getValue()));
}
mConstant = mConstant.mul(c);
}
return this;
}
public InterpolatorAffineTerm div(Rational c) {
return mul(c.inverse());
}
public InterpolatorAffineTerm negate() {
return mul(Rational.MONE);
}
public boolean isConstant() {
return mSummands.isEmpty();
}
public InfinitNumber getConstant() {
return mConstant;
}
public Map<Term,Rational> getSummands() {
return mSummands;
}
public Rational getGCD() {
assert (!mSummands.isEmpty());
final Iterator<Rational> it = mSummands.values().iterator();
Rational gcd = it.next();
final boolean firstSign = gcd.isNegative();
gcd = gcd.abs();
while (it.hasNext()) {
gcd = gcd.gcd(it.next().abs());
}
if (firstSign) {
gcd = gcd.negate();
}
return gcd;
}
/**
* For integer valued interpolants, convert Rationals to integer valued
* Rational by dividing by the rational greatest common divisor.
*/
void normalize() {
mul(getGCD().inverse());
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for (final Entry<Term,Rational> entry : mSummands.entrySet()) {
final Term var = entry.getKey();
Rational fact = entry.getValue();
if (fact.isNegative()) {
sb.append(isFirst ? "-" : " - ");
} else {
sb.append(isFirst ? "" : " + ");
}
fact = fact.abs();
if (!fact.equals(Rational.ONE)) {
sb.append(fact).append('*');
}
sb.append(var);
isFirst = false;
}
if (isFirst) {
sb.append(mConstant);
} else {
final int signum = mConstant.compareTo(InfinitNumber.ZERO);
if (signum < 0) {
sb.append(" - ");
sb.append(mConstant.mul(Rational.MONE));
} else if (signum > 0) {
sb.append(" + ");
sb.append(mConstant);
}
}
return sb.toString();
}
/**
* Convert the affine term to a term in our core theory.
*/
public Term toSMTLib(Theory t, boolean isInt) {
assert(mConstant.mEps == 0);
final Sort numSort = isInt ? t.getSort("Int") : t.getSort("Real");
assert(numSort != null);
final Sort[] binfunc = new Sort[] {numSort,numSort};
final FunctionSymbol times = t.getFunction("*",binfunc);
final FunctionSymbol plus = t.getFunction("+",binfunc);
FunctionSymbol negate = t.getFunction("-", numSort);
if (negate == null) {
negate = t.getFunction("-", numSort);
}
assert (!isInt || mConstant.mA.isIntegral());
Term comb = mConstant.mA.equals(Rational.ZERO) ? null
: isInt ? t.numeral(mConstant.mA.numerator())
: t.rational(mConstant.mA.numerator(), mConstant.mA.denominator());
for (final Map.Entry<Term,Rational> me : mSummands.entrySet()) {
Term convme = me.getKey();
// if affine term is integral it may only add integers.
assert (!isInt || convme.getSort().getName().equals("Int"));
assert (!isInt || me.getValue().isIntegral());
if (!isInt && convme.getSort().getName().equals("Int")) {
final Sort intSort = t.getSort("Int");
final FunctionSymbol toReal = t.getFunction("to_real", intSort);
convme = t.term(toReal, convme);
}
if (me.getValue().equals(Rational.MONE)) {
convme = t.term(negate, convme);
} else if (!me.getValue().equals(Rational.ONE)) {
final Term convfac = isInt ? t.numeral(me.getValue().numerator())
: t.rational(me.getValue().numerator(),me.getValue().denominator());
convme = t.term(times, convfac, convme);
}
if (comb == null) {
comb = convme;
} else {
comb = t.term(plus, convme, comb);
}
}
if (comb == null) {
return isInt ? t.numeral(BigInteger.ZERO) : t.rational(BigInteger.ZERO, BigInteger.ONE);
}
return comb;
}
public boolean isInt() {
for (final Term v : mSummands.keySet()) {
if (!v.getSort().getName().equals("Int")) {
return false;
}
}
return true;
}
/**
* Create the term <code>this <= val</code>.
* @param t Theory used in conversion.
* @return A term for <code>this <= val</code>.
*/
public Term toLeq0(Theory t) {
assert(mConstant.mEps >= 0);
if (isConstant()) {
return mConstant.compareTo(InfinitNumber.ZERO) <= 0 ? t.mTrue : t.mFalse;
}
final boolean isInt = isInt();
final Sort numSort = isInt ? t.getSort("Int") : t.getSort("Real");
assert(numSort != null);
final Sort[] binfunc = new Sort[] {numSort,numSort};
final FunctionSymbol times = t.getFunction("*",binfunc);
final ArrayList<Term> lcomb = new ArrayList<Term>();
final ArrayList<Term> rcomb = new ArrayList<Term>();
for (final Map.Entry<Term,Rational> me : mSummands.entrySet()) {
Term convme = me.getKey();
// if affine term is integral it may only add integers.
assert (!isInt || convme.getSort().getName().equals("Int"));
assert (!isInt || me.getValue().isIntegral());
if (!isInt && convme.getSort().getName().equals("Int")) {
final Sort intSort = t.getSort("Int");
final FunctionSymbol toReal = t.getFunction("to_real", intSort);
convme = t.term(toReal, convme);
}
if (me.getValue().equals(Rational.MONE)) {
rcomb.add(convme);
} else if (me.getValue().signum() < 0) {
final Rational cf = me.getValue().abs();
final Term convfac = isInt ? t.numeral(cf.numerator())
: t.rational(cf.numerator(),cf.denominator());
rcomb.add(t.term(times, convfac, convme));
} else if (me.getValue().equals(Rational.ONE)) {
lcomb.add(convme);
} else if (me.getValue().signum() > 0) {
final Rational cf = me.getValue();
final Term convfac = isInt ? t.numeral(cf.numerator())
: t.rational(cf.numerator(),cf.denominator());
lcomb.add(t.term(times, convfac, convme));
}
}
final InfinitNumber constant = isInt ? mConstant.ceil() : mConstant;
if (!constant.mA.equals(Rational.ZERO)) {
rcomb.add(isInt ? t.numeral(constant.mA.numerator().negate())
: t.rational(constant.mA.numerator().negate(), constant.mA.denominator()));
}
if (lcomb.isEmpty() && rcomb.isEmpty()) {
// We either have 0<=0 or 0<0
return constant.mEps == 0 ? t.mTrue : t.mFalse;
}
final FunctionSymbol plus = t.getFunction("+",binfunc);
Term tlcomb, trcomb;
switch (lcomb.size()) {
case 0:
tlcomb = isInt ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO);
break;
case 1:
tlcomb = lcomb.get(0);
break;
default:
tlcomb = t.term(plus, lcomb.toArray(new Term[lcomb.size()]));
}
switch (rcomb.size()) {
case 0:
trcomb = isInt ? t.numeral(BigInteger.ZERO) : t.decimal(BigDecimal.ZERO);
break;
case 1:
trcomb = rcomb.get(0);
break;
default:
trcomb = t.term(plus, rcomb.toArray(new Term[rcomb.size()]));
}
return t.term(constant.mEps == 0 ? "<=" : "<",
tlcomb, trcomb);
}
@Override
public int hashCode() {
return mConstant.hashCode() + 1021 * mSummands.hashCode();
}
}