/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package abs.frontend.typechecker.locationtypes.infer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import abs.frontend.typechecker.ext.AdaptDirection;
import abs.frontend.typechecker.locationtypes.LocationType;
import static abs.frontend.typechecker.locationtypes.LocationType.*;
import static abs.frontend.typechecker.locationtypes.infer.MultiListIterable.fromIterable;
public abstract class Constraint {
public final static Integer MUST_HAVE = 2147483647;
public final static Integer NICE_TO_HAVE = 1;
public final static Integer SHOULD_HAVE = 1337;
protected void prependAll(Integer i, List<List<Integer>> l) {
for (List<Integer> subl : l) {
subl.add(0, i);
}
}
protected List<LocationType> commonTypes(List<LocationType> lt1, List<LocationType> lt2) {
List<LocationType> result = new ArrayList<LocationType>(lt1);
result.retainAll(lt2);
return result;
}
protected List<LocationType> diffTypes(List<LocationType> lt1, List<LocationType> lt2) {
List<LocationType> result = new ArrayList<LocationType>(lt1);
result.removeAll(commonTypes(lt1, lt2));
return result;
}
public abstract List<List<Integer>> generateSat(Environment e);
public abstract void variables(Set<LocationTypeVariable> vars);
private static List<Integer> generate(LocationTypeVariable v, Environment e, LocationType... types) {
return generate(v,e,Arrays.asList(types));
}
private static List<Integer> generate(LocationTypeVariable v, Environment e, List<LocationType> types) {
List<Integer> values = new ArrayList<Integer>();
for (LocationType t : types) {
values.add(e.get(v, t));
}
return values;
}
private static class SubConstraint extends Constraint {
LocationTypeVariable tv1, tv2;
public SubConstraint(LocationTypeVariable tv1, LocationTypeVariable tv2) {
this.tv1 = tv1;
this.tv2 = tv2;
}
@Override
public String toString() {
return tv1+ " <: " + tv2;
}
@Override
public List<List<Integer>> generateSat(Environment e) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> values;
values = new ArrayList<Integer>();
values.add(- e.get(tv1, BOTTOM));
values.addAll(generate(tv2, e, ALLVISTYPES));
result.add(values);
values = new ArrayList<Integer>();
values.add(- e.get(tv1, NEAR));
values.addAll(generate(tv2, e, NEAR, SOMEWHERE));
result.add(values);
values = new ArrayList<Integer>();
values.add(- e.get(tv1, FAR));
values.addAll(generate(tv2, e, FAR, SOMEWHERE));
result.add(values);
values = new ArrayList<Integer>();
values.add(- e.get(tv1, SOMEWHERE));
values.addAll(generate(tv2, e, SOMEWHERE));
result.add(values);
for (LocationType pft : tv1.parametricFarTypes()) {
if (tv2.parametricFarTypes().contains(pft)) {
values = new ArrayList<Integer>();
values.add(- e.get(tv1, pft));
values.addAll(generate(tv2, e, pft, FAR, SOMEWHERE));
result.add(values);
} else {
values = new ArrayList<Integer>();
values.add(- e.get(tv1, pft));
values.addAll(generate(tv2, e, FAR, SOMEWHERE));
result.add(values);
}
}
prependAll(SHOULD_HAVE, result);
return result;
}
@Override
public void variables(Set<LocationTypeVariable> vars) {
vars.add(tv1);
vars.add(tv2);
}
}
private static class FarConstraint extends Constraint {
LocationTypeVariable tv1, tv2;
public FarConstraint(LocationTypeVariable tv1, LocationTypeVariable tv2) {
/*if (tv2.parametricFarTypes().size() > 0) {
throw new IllegalArgumentException("tv2 should not contain parametric Far Types");
}*/
this.tv1 = tv1;
this.tv2 = tv2;
}
@Override
public String toString() {
return tv1+ " <:_FAR " + tv2;
}
@Override
public List<List<Integer>> generateSat(Environment e) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> values;
values = new ArrayList<Integer>();
values.add(- e.get(tv1, BOTTOM));
values.addAll(generate(tv2, e, tv2.allTypes()));
result.add(values);
values = new ArrayList<Integer>();
values.add(- e.get(tv1, NEAR));
values.addAll(generate(tv2, e, FAR, SOMEWHERE));
result.add(values);
values = new ArrayList<Integer>();
values.add(- e.get(tv1, FAR));
values.addAll(generate(tv2, e, FAR, SOMEWHERE));
result.add(values);
values = new ArrayList<Integer>();
values.add(- e.get(tv1, SOMEWHERE));
values.addAll(generate(tv2, e, FAR, SOMEWHERE));
result.add(values);
for (LocationType pft : tv1.parametricFarTypes()) {
if (tv2.parametricFarTypes().contains(pft)) {
values = new ArrayList<Integer>();
values.add(- e.get(tv1, pft));
values.addAll(generate(tv2, e, pft, FAR, SOMEWHERE));
result.add(values);
} else {
values = new ArrayList<Integer>();
values.add(- e.get(tv1, pft));
values.addAll(generate(tv2, e, FAR, SOMEWHERE));
result.add(values);
}
}
prependAll(SHOULD_HAVE, result);
return result;
}
@Override
public void variables(Set<LocationTypeVariable> vars) {
vars.add(tv1);
vars.add(tv2);
}
}
public static class DeclConstraint extends Constraint {
LocationTypeVariable tv;
public DeclConstraint(LocationTypeVariable tv) {
this.tv = tv;
}
@Override
public List<List<Integer>> generateSat(Environment e) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> values = new ArrayList<Integer>();
// vt must have at least a type
for (LocationType it : tv.allTypes()) {
values.add(e.get(tv, it));
}
result.add(values);
// vt must have maximally one type
for (LocationType it1 : tv.allTypes()) {
for (LocationType it2 : tv.allTypes()) {
if (!it1.equals(it2)) {
// !tv = it1 or !tv = it2 === ! (tv = it1 && tv = it2)
result.add(new CL(e).not(tv).is(it1).orNot(tv).is(it2).getValues());
}
}
}
prependAll(MUST_HAVE, result);
return result;
}
@Override
public String toString() {
return tv + " def= " + tv.allTypes();
}
@Override
public void variables(Set<LocationTypeVariable> vars) {
vars.add(tv);
}
}
private static class CL {
private ArrayList<Integer> values = new ArrayList<Integer>();
private Environment e;
public CL(Environment e) {
this.e = e;
}
class Predicate {
LocationTypeVariable tv;
private boolean not;
Predicate(LocationTypeVariable v, boolean not) {
tv = v;
this.not = not;
}
CL is(LocationType t) {
if (not) {
return orNot(tv,t);
} else {
return or(tv,t);
}
}
}
public Predicate or(LocationTypeVariable tv) {
return new Predicate(tv,false);
}
public Predicate then(LocationTypeVariable tv) {
return new Predicate(tv,false);
}
public Predicate orNot(LocationTypeVariable tv) {
return new Predicate(tv,true);
}
public Predicate not(LocationTypeVariable tv) {
return orNot(tv);
}
public Predicate if_(LocationTypeVariable tv) {
return orNot(tv);
}
public Predicate andIf(LocationTypeVariable tv) {
return orNot(tv);
}
public CL not(LocationTypeVariable tv, LocationType t) {
return orNot(tv,t);
}
public CL orNot(LocationTypeVariable tv, LocationType t) {
values.add(- e.get(tv, t));
return this;
}
public CL or(LocationTypeVariable tv, LocationType t) {
values.add(e.get(tv, t));
return this;
}
public CL if_(LocationTypeVariable tv, LocationType t) {
return not(tv, t);
}
public CL andIf(LocationTypeVariable tv, LocationType t) {
return not(tv, t);
}
public CL then(LocationTypeVariable tv, LocationType t) {
return or(tv, t);
}
public ArrayList<Integer> getValues() {
return values;
}
}
private static class AdaptConstraint extends Constraint {
LocationTypeVariable resultTv, tv, adaptToTv;
AdaptDirection dir;
public AdaptConstraint(LocationTypeVariable resultTv, LocationTypeVariable tv, AdaptDirection dir, LocationTypeVariable adaptToTv) {
if (resultTv == null || tv == null || adaptToTv == null)
throw new IllegalArgumentException("some variable is null: "+resultTv+", "+tv+", "+adaptToTv);
if (dir == null) {
throw new IllegalArgumentException("direction is null");
}
/*if (!Arrays.equals(adaptToTv.parametricFarTypes(), resultTv.parametricFarTypes())) {
throw new IllegalArgumentException("Parametric Far Types for adaptToTv and resultTv should be equal");
}*/
/*
if (adaptToTv.parametricFarTypes().length > 0 && !Arrays.equals(adaptToTv.parametricFarTypes(), resultTv.parametricFarTypes())) {
throw new IllegalArgumentException("Parametric Far Types for adaptToTv and resultTv should be equal");
}*/
this.resultTv = resultTv;
this.tv = tv;
this.adaptToTv = adaptToTv;
this.dir = dir;
}
@Override
public String toString() {
return tv + " >>_" + dir + " " + adaptToTv + " = " + resultTv;
}
@Override
public List<List<Integer>> generateSat(Environment e) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
// if adaptToTv = NEAR
result.add(new CL(e).if_(adaptToTv).is(NEAR).andIf(tv).is(BOTTOM).then(resultTv).is(BOTTOM).getValues());
result.add(new CL(e).if_(adaptToTv).is(NEAR).andIf(tv).is(NEAR).then(resultTv).is(NEAR).getValues());
result.add(new CL(e).if_(adaptToTv).is(NEAR).andIf(tv).is(FAR).then(resultTv).is(FAR).getValues());
result.add(new CL(e).if_(adaptToTv).is(NEAR).andIf(tv).is(SOMEWHERE).then(resultTv).is(SOMEWHERE).getValues());
for (LocationType t : commonTypes(tv.parametricFarTypes(), resultTv.parametricFarTypes())) {
result.add(new CL(e).if_(adaptToTv).is(NEAR).andIf(tv).is(t).then(resultTv).is(t).getValues());
}
for (LocationType t : diffTypes(tv.parametricFarTypes(), resultTv.parametricFarTypes())) {
result.add(new CL(e).if_(adaptToTv).is(NEAR).andIf(tv).is(t).then(resultTv).is(FAR).getValues());
}
// if adaptToTv = FAR
result.add(new CL(e).if_(adaptToTv).is(FAR).andIf(tv).is(BOTTOM).then(resultTv).is(BOTTOM).getValues());
result.add(new CL(e).if_(adaptToTv).is(FAR).andIf(tv).is(NEAR).then(resultTv).is(FAR).getValues());
result.add(new CL(e).if_(adaptToTv).is(FAR).andIf(tv).is(FAR).then(resultTv).is(SOMEWHERE).getValues());
result.add(new CL(e).if_(adaptToTv).is(FAR).andIf(tv).is(SOMEWHERE).then(resultTv).is(SOMEWHERE).getValues());
for (LocationType t : tv.parametricFarTypes()) {
result.add(new CL(e).if_(adaptToTv).is(FAR).andIf(tv).is(t).then(resultTv).is(SOMEWHERE).getValues());
}
// if adaptToTv = FAR(i)
for (LocationType t : adaptToTv.parametricFarTypes()) {
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(BOTTOM).then(resultTv).is(BOTTOM).getValues());
if (dir.isFrom())
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(NEAR).then(resultTv).is(t).getValues());
else
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(NEAR).then(resultTv).is(FAR).getValues());
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(FAR).then(resultTv).is(SOMEWHERE).getValues());
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(SOMEWHERE).then(resultTv).is(SOMEWHERE).getValues());
}
//List<LocationType> commonAdaptTvAndTv = commonTypes(adaptToTv.parametricFarTypes(), tv.parametricFarTypes());
for (LocationType t : adaptToTv.parametricFarTypes()) {
for (LocationType t2 : tv.parametricFarTypes()) {
if (t != t2) {
if (dir.isFrom()) {
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(t2).then(resultTv).is(SOMEWHERE).getValues());
} else {
if (resultTv.parametricFarTypes().contains(t2)) {
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(t2).then(resultTv).is(t2).getValues());
} else {
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(t2).then(resultTv).is(FAR).getValues());
}
}
} else {
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(t2).then(resultTv).is(SOMEWHERE).getValues());
}
}
}
/*for (LocationType t : diffTypes(adaptToTv.parametricFarTypes(), tv.parametricFarTypes())) {
for (LocationType t2 : diffTypes(tv.parametricFarTypes(), adaptToTv.parametricFarTypes())) {
result.add(new CL(e).if_(adaptToTv).is(t).andIf(tv).is(t2).then(resultTv).is(SOMEWHERE).getValues());
}
}*/
// if adaptToTv = SOMEWHERE
for (LocationType t : tv.allTypes()) {
if (t != BOTTOM)
result.add(new CL(e).if_(adaptToTv).is(SOMEWHERE).andIf(tv).is(t).then(resultTv).is(SOMEWHERE).getValues());
}
result.add(new CL(e).if_(adaptToTv).is(SOMEWHERE).andIf(tv).is(BOTTOM).then(resultTv).is(BOTTOM).getValues());
prependAll(MUST_HAVE, result);
// adaptToTv should always be unequal to BOTTOM
//result.addAll(Constraint.neqConstraint(adaptToTv, LocationTypeVariable.ALWAYS_BOTTOM, SHOULD_HAVE).generateSat(e));
return result;
}
@Override
public void variables(Set<LocationTypeVariable> vars) {
vars.add(resultTv);
vars.add(tv);
vars.add(adaptToTv);
}
}
private static class EqConstraint extends Constraint {
LocationTypeVariable tv1, tv2;
Integer importance;
public EqConstraint(LocationTypeVariable tv1, LocationTypeVariable tv2, Integer importance) {
/*if (!Arrays.equals(tv1.parametricFarTypes(), tv2.parametricFarTypes())) {
throw new IllegalArgumentException("Parametric Far Types for tv1 and tv2 should be equal");
}*/
this.tv1 = tv1;
this.tv2 = tv2;
this.importance = importance;
}
@Override
public List<List<Integer>> generateSat(Environment e) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> values;
//boolean b = tv1.allTypes().length > tv2.allTypes().length;
for (LocationType t : commonTypes(tv1.allTypes(), tv2.allTypes())) {
values = new ArrayList<Integer>();
values.add(- e.get(tv1, t));
values.add(e.get(tv2, t));
result.add(values);
}
for (LocationType lt : diffTypes(tv1.parametricFarTypes(), tv2.parametricFarTypes())) {
values = new ArrayList<Integer>();
values.add(- e.get(tv1, lt));
result.add(values);
}
for (LocationType lt : diffTypes(tv2.parametricFarTypes(), tv1.parametricFarTypes())) {
values = new ArrayList<Integer>();
values.add(- e.get(tv2, lt));
result.add(values);
}
prependAll(importance, result);
return result;
}
@Override
public String toString() {
return tv1 + " = " + tv2;
}
@Override
public void variables(Set<LocationTypeVariable> vars) {
vars.add(tv1);
vars.add(tv2);
}
}
private static class ConstConstraint extends Constraint {
LocationTypeVariable tv;
Iterable<LocationType> t;
Integer importance;
public ConstConstraint(LocationTypeVariable tv, Iterable<LocationType> list, Integer importance) {
this.tv = tv;
this.t = list;
this.importance = importance;
}
@Override
public List<List<Integer>> generateSat(Environment e) {
if (!tv.allTypes().containsAll(fromIterable(t))) {
throw new IllegalArgumentException("t should be a subset of types in tv");
}
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> values;
values = new ArrayList<Integer>();
for (LocationType lt : t) {
values.add(e.get(tv, lt));
}
result.add(values);
/*for (LocationType t2 : LocationType.ALLTYPES) {
if (!t.equals(t2)) {
values = new ArrayList<Integer>();
values.add(- e.get(tv, t2));
result.add(values);
}
}*/
prependAll(importance, result);
return result;
}
@Override
public String toString() {
return tv + " := " + t;
}
@Override
public void variables(Set<LocationTypeVariable> vars) {
vars.add(tv);
}
}
// tv1 < tv2
public static Constraint subConstraint(LocationTypeVariable tv1, LocationTypeVariable tv2) {
return new SubConstraint(tv1, tv2);
}
// tv1 = tv2
public static Constraint eqConstraint(LocationTypeVariable tv1, LocationTypeVariable tv2, Integer importance) {
return new EqConstraint(tv1, tv2, importance);
}
// tv := t
public static Constraint constConstraint(LocationTypeVariable tv, LocationType t, Integer importance) {
return new ConstConstraint(tv, Arrays.asList(t), importance);
}
/*public static Constraint constConstraint(LocationTypeVariable tv, LocationType[] tl, Integer importance) {
return new ConstConstraint(tv, Arrays.asList(tl), importance);
}*/
public static Constraint constConstraint(LocationTypeVariable tv, Iterable<LocationType> tl, Integer importance) {
return new ConstConstraint(tv, tl, importance);
}
// tv1 = tv2 |> tv3
public static Constraint adaptConstraint(LocationTypeVariable resultTv, LocationTypeVariable tv, AdaptDirection dir, LocationTypeVariable adaptTo) {
return new AdaptConstraint(resultTv, tv, dir, adaptTo);
}
public static Constraint declConstraint(LocationTypeVariable tv) {
return new DeclConstraint(tv);
}
// tv1 <_far tv2
public static Constraint farConstraint(LocationTypeVariable tv1, LocationTypeVariable tv2) {
return new FarConstraint(tv1, tv2);
}
}