//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using Microsoft.Contracts;
namespace Microsoft.AbstractInterpretationFramework
{
using System;
using System.Compiler;
using System.Collections;
using Microsoft.Basetypes;
using IMutableSet = Microsoft.Boogie.Set;
using HashSet = Microsoft.Boogie.Set;
using ISet = Microsoft.Boogie.Set;
///
/// Represents a single linear constraint, coefficients are stored as Rationals.
///
public class LinearConstraint
{
public enum ConstraintRelation
{
EQ, // equal
LE, // less-than or equal
}
public readonly ConstraintRelation Relation;
internal Hashtable /*IVariable->Rational*/! coefficients = new Hashtable /*IVariable->Rational*/ ();
internal Rational rhs;
public LinearConstraint (ConstraintRelation rel)
{
Relation = rel;
}
[Pure]
public override string! ToString()
{
string s = null;
foreach (DictionaryEntry /*IVariable->Rational*/ entry in coefficients)
{
if (s == null)
{
s = "";
}
else
{
s += " + ";
}
s += String.Format("{0}*{1}", entry.Value, entry.Key);
}
System.Diagnostics.Debug.Assert(s != null, "malformed LinearConstraint: no variables");
s += String.Format(" {0} {1}", Relation == ConstraintRelation.EQ ? "==" : "<=", rhs);
return s;
}
#if DONT_KNOW_HOW_TO_TAKE_THE_TYPE_OF_AN_IVARIABLE_YET
public bool IsOverIntegers
{
get
{
foreach (DictionaryEntry /*IVariable->Rational*/ entry in coefficients)
{
IVariable var = (IVariable)entry.Key;
if ( ! var.TypedIdent.Type.IsInt) { return false; }
}
return true;
}
}
#endif
///
/// Note: This method requires that all dimensions are of type Variable, something that's
/// not required elsewhere in this class.
///
///
public IExpr! ConvertToExpression(ILinearExprFactory! factory)
{
IExpr leftSum = null;
IExpr rightSum = null;
foreach (DictionaryEntry /*object->Rational*/ entry in coefficients)
{
IVariable var = (IVariable)entry.Key;
Rational coeff = (Rational) ((!)entry.Value);
if (coeff.IsPositive)
{
leftSum = AddTerm(factory, leftSum, coeff, var);
}
else if (coeff.IsNegative)
{
rightSum = AddTerm(factory, rightSum, -coeff, var);
}
else
{
// ignore the term is coeff==0
}
}
if (leftSum == null && rightSum == null)
{
// there are no variables in this constraint
if (Relation == ConstraintRelation.EQ ? rhs.IsZero : rhs.IsNonNegative) {
return factory.True;
} else {
return factory.False;
}
}
if (leftSum == null || (rightSum != null && rhs.IsNegative))
{
// show the constant on the left side
leftSum = AddTerm(factory, leftSum, -rhs, null);
}
else if (rightSum == null || rhs.IsPositive)
{
// show the constant on the right side
rightSum = AddTerm(factory, rightSum, rhs, null);
}
assert leftSum != null;
assert rightSum != null;
return Relation == ConstraintRelation.EQ ? factory.Eq(leftSum, rightSum) : factory.AtMost(leftSum, rightSum);
}
///
/// Returns an expression that denotes sum + r*x.
/// If sum==null, drops the "sum +".
/// If x==null, drops the "*x".
/// if x!=null and r==1, drops the "r*".
///
///
///
///
///
static IExpr! AddTerm(ILinearExprFactory! factory, /*MayBeNull*/ IExpr sum, Rational r, /*MayBeNull*/ IVariable x)
{
IExpr! product = factory.Term(r, x);
if (sum == null) {
return product;
} else {
return factory.Add(sum, product);
}
}
public ISet /*IVariable!*/! GetDefinedDimensions()
{
HashSet /*IVariable!*/ dims = new HashSet /*IVariable!*/ (coefficients.Count);
int j = 0;
foreach (IVariable! dim in coefficients.Keys)
{
dims.Add(dim);
j++;
}
System.Diagnostics.Debug.Assert(j == coefficients.Count);
return dims;
}
///
/// Returns true iff all of the coefficients in the constraint are 0. In that
/// case, the constraint has the form 0 <= C for some constant C; hence, the
/// constraint is either unsatisfiable or trivially satisfiable.
///
///
public bool IsConstant()
{
foreach (Rational coeff in coefficients.Values)
{
if (coeff.IsNonZero)
{
return false;
}
}
return true;
}
///
/// For an equality constraint, returns 0 == rhs.
/// For an inequality constraint, returns 0 <= rhs.
///
public bool IsConstantSatisfiable()
{
if (Relation == ConstraintRelation.EQ)
{
return rhs.IsZero;
}
else
{
return rhs.IsNonNegative;
}
}
///
/// Returns 0 if "this" and "c" are not equivalent constraints. If "this" and "c"
/// are equivalent constraints, the non-0 return value "m" satisfies "this == m*c".
///
///
///
public Rational IsEquivalent(LinearConstraint! c)
{
// "m" is the scale factor. If it is 0, it hasn't been used yet. If it
// is non-0, it will remain that value throughout, and it then says that
// for every dimension "d", "this[d] == m * c[d]".
Rational m = Rational.ZERO;
ArrayList /*IVariable*/ dd = new ArrayList /*IVariable*/ ();
foreach (IVariable! d in this.GetDefinedDimensions())
{
if (!dd.Contains(d)) { dd.Add(d); }
}
foreach (IVariable! d in c.GetDefinedDimensions())
{
if (!dd.Contains(d)) { dd.Add(d); }
}
foreach (IVariable! d in dd)
{
Rational a = this[d];
Rational b = c[d];
if (a.IsZero || b.IsZero)
{
if (a.IsNonZero || b.IsNonZero)
{
return Rational.ZERO; // not equivalent
}
}
else if (m.IsZero)
{
m = a / b;
}
else if (a != m * b)
{
return Rational.ZERO; // not equivalent
}
}
// we expect there to have been some non-zero coefficient, so "m" should have been used by now
System.Diagnostics.Debug.Assert(m.IsNonZero);
// finally, check the rhs
if (this.rhs == m * c.rhs)
{
return m; // equivalent
}
else
{
return Rational.ZERO; // not equivalent
}
}
///
/// Splits an equality constraint into two inequality constraints, the conjunction of
/// which equals the equality constraint. Assumes "this" is a equality constraint.
///
///
///
public void GenerateInequalityConstraints(out LinearConstraint a, out LinearConstraint b)
{
System.Diagnostics.Debug.Assert(this.Relation == ConstraintRelation.EQ);
a = new LinearConstraint(ConstraintRelation.LE);
a.coefficients = (Hashtable)this.coefficients.Clone();
a.rhs = this.rhs;
b = new LinearConstraint(ConstraintRelation.LE);
b.coefficients = new Hashtable /*IVariable->Rational*/ ();
foreach (DictionaryEntry entry in this.coefficients)
{
b.coefficients[entry.Key] = -(Rational) ((!)entry.Value);
}
b.rhs = -this.rhs;
}
public void SetCoefficient(IVariable! dimension, Rational coefficient)
{
coefficients[dimension] = coefficient;
}
///
/// Removes dimension "dim" from the constraint. Only dimensions with coefficient 0 can
/// be removed.
///
///
public void RemoveDimension(IVariable! dim)
{
object val = coefficients[dim];
if (val != null)
{
#if FIXED_SERIALIZER
assert ((Rational)val).IsZero;
#endif
coefficients.Remove(dim);
}
}
///
/// The getter returns 0 if the dimension is not present.
///
public Rational this [IVariable! dimension]
{
get
{
object z = coefficients[dimension];
if (z == null)
{
return Rational.ZERO;
}
else
{
return (Rational)z;
}
}
set { SetCoefficient(dimension, value); }
}
public LinearConstraint Rename(IVariable! oldName, IVariable! newName)
{
object /*Rational*/ z = coefficients[oldName];
if (z == null)
{
return this;
}
else
{
System.Diagnostics.Debug.Assert(z is Rational);
Hashtable /*IVariable->Rational*/ newCoeffs = (Hashtable! /*IVariable->Rational*/)coefficients.Clone();
newCoeffs.Remove(oldName);
newCoeffs.Add(newName, z);
LinearConstraint lc = new LinearConstraint(this.Relation);
lc.coefficients = newCoeffs;
lc.rhs = this.rhs;
return lc;
}
}
public LinearConstraint Clone()
{
LinearConstraint z = new LinearConstraint(Relation);
z.coefficients = (Hashtable /*IVariable->Rational*/)this.coefficients.Clone();
z.rhs = this.rhs;
return z;
}
///
/// Returns a constraint like "this", but with the given relation "r".
///
///
public LinearConstraint! ChangeRelation(ConstraintRelation rel)
{
if (Relation == rel)
{
return this;
}
else
{
LinearConstraint z = new LinearConstraint(rel);
z.coefficients = (Hashtable)this.coefficients.Clone();
z.rhs = this.rhs;
return z;
}
}
///
/// Returns a constraint like "this", but, conceptually, with the inequality relation >=.
///
///
public LinearConstraint! ChangeRelationToAtLeast()
{
LinearConstraint z = new LinearConstraint(ConstraintRelation.LE);
foreach (DictionaryEntry /*IVariable->Rational*/ entry in this.coefficients)
{
z.coefficients.Add(entry.Key, -(Rational) ((!)entry.Value));
}
z.rhs = -this.rhs;
return z;
}
///
/// Returns the left-hand side of the constraint evaluated at the point "v".
/// Any coordinate not present in "v" is treated as if it were 0.
/// Stated differently, this routine treats the left-hand side of the constraint
/// as a row vector and "v" as a column vector, and then returns the dot-product
/// of the two.
///
///
///
public Rational EvaluateLhs(FrameElement! v)
{
Rational q = Rational.ZERO;
foreach (DictionaryEntry /*IVariable,Rational*/ term in coefficients)
{
IVariable dim = (IVariable!)term.Key;
Rational a = (Rational) ((!)term.Value);
Rational x = v[dim];
q += a * x;
}
return q;
}
///
/// Determines whether or not a given vertex or ray saturates the constraint.
///
///
/// true if "fe" is a vertex; false if "fe" is a ray
///
public bool IsSaturatedBy(FrameElement! fe, bool vertex)
{
Rational lhs = EvaluateLhs(fe);
Rational rhs = vertex ? this.rhs : Rational.ZERO;
return lhs == rhs;
}
///
/// Changes the current constraint A*X <= B into (A + m*aa)*X <= B + m*bb,
/// where "cc" is the constraint aa*X <= bb.
///
///
///
///
public void AddMultiple(Rational m, LinearConstraint! cc)
{
foreach (DictionaryEntry /*IVariable->Rational*/ entry in cc.coefficients)
{
IVariable dim = (IVariable)entry.Key;
Rational d = m * (Rational) ((!)entry.Value);
if (d.IsNonZero)
{
object prev = coefficients[dim];
if (prev == null)
{
coefficients[dim] = d;
}
else
{
coefficients[dim] = (Rational)prev + d;
}
}
}
rhs += m * cc.rhs;
}
///
/// Try to reduce the magnitude of the coefficients used.
/// Has a side effect on the coefficients, but leaves the meaning of the linear constraint
/// unchanged.
///
public void Normalize() {
// compute the gcd of the numerators and the gcd of the denominators
Rational gcd = rhs;
foreach (Rational r in coefficients.Values) {
gcd = Rational.Gcd(gcd, r);
}
// Change all coefficients, to divide their numerators with gcdNum and to
// divide their denominators with gcdDen.
Hashtable /*IVariable->Rational*/ newCoefficients = new Hashtable /*IVariable->Rational*/ (coefficients.Count);
foreach (DictionaryEntry /*IVarianble->Rational*/ e in coefficients) {
Rational r = (Rational) ((!)e.Value);
if (r.IsNonZero) {
newCoefficients.Add(e.Key, new Rational(r.Numerator / gcd.Numerator, r.Denominator / gcd.Denominator));
} else {
newCoefficients.Add(e.Key, r);
}
}
coefficients = newCoefficients;
rhs = rhs.IsNonZero ? (Rational)new Rational(rhs.Numerator / gcd.Numerator, rhs.Denominator / gcd.Denominator) : rhs;
}
}
///
/// Represents a frame element (vector of dimension/value tuples). Used only
/// internally in class LinearConstraintSystem and its communication with class
/// LinearConstraint.
///
public class FrameElement
{
Hashtable /*IVariable->Rational*/! terms = new Hashtable /*IVariable->Rational*/ ();
///
/// Constructs an empty FrameElement. To add dimensions, call AddCoordinate after construction.
///
public FrameElement()
{
}
///
/// This method is to be thought of as being part of the FrameElement object's construction process.
/// Assumes "dimension" is not already in FrameElement.
///
///
///
public void AddCoordinate(IVariable! dimension, Rational value)
{
terms.Add(dimension, value);
}
[Pure]
public override string! ToString()
{
string s = null;
foreach (DictionaryEntry item in terms)
{
if (s == null)
{
s = "(";
}
else
{
s += ", ";
}
s += String.Format("<{0},{1}>", item.Key, (Rational) ((!)item.Value));
}
if (s == null)
{
s = "(";
}
return s + ")";
}
public IMutableSet /*IVariable!*/! GetDefinedDimensions()
{
HashSet /*IVariable!*/! dims = new HashSet /*IVariable!*/ (terms.Count);
foreach (IVariable! dim in terms.Keys)
{
dims.Add(dim);
}
System.Diagnostics.Debug.Assert(dims.Count == terms.Count);
return dims;
}
///
/// The getter returns the value at the given dimension, or 0 if that dimension is not defined.
///
public Rational this [IVariable! dimension]
{
get
{
object z = terms[dimension];
if (z == null)
{
return Rational.ZERO;
}
else
{
return (Rational)z;
}
}
set
{
terms[dimension] = value;
}
}
public FrameElement Rename(IVariable! oldName, IVariable! newName)
{
object /*Rational*/ z = terms[oldName];
if (z == null)
{
return this;
}
else
{
System.Diagnostics.Debug.Assert(z is Rational);
Hashtable /*IVariable->Rational*/ newTerms = (Hashtable! /*IVariable->Rational*/)terms.Clone();
newTerms.Remove(oldName);
newTerms.Add(newName, z);
FrameElement fe = new FrameElement();
fe.terms = newTerms;
return fe;
}
}
public FrameElement Clone()
{
FrameElement z = new FrameElement();
z.terms = (Hashtable /*IVariable->Rational*/)this.terms.Clone();
return z;
}
}
}