001package org.clafer.ast.analysis;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.HashMap;
006import java.util.HashSet;
007import java.util.List;
008import java.util.Map;
009import java.util.Set;
010import org.clafer.ast.AstAbstractClafer;
011import org.clafer.ast.AstBoolExpr;
012import org.clafer.ast.AstClafer;
013import org.clafer.ast.AstConcreteClafer;
014import org.clafer.ast.AstConstraint;
015import org.clafer.ast.AstExpr;
016import org.clafer.ast.AstModel;
017import org.clafer.ast.AstRef;
018import org.clafer.ast.AstSetExpr;
019import org.clafer.ast.AstUtil;
020import org.clafer.ast.Card;
021import org.clafer.collection.Pair;
022import org.clafer.common.Util;
023import org.clafer.ir.IrDomain;
024import org.clafer.objective.Objective;
025import org.clafer.scope.Scopable;
026import org.clafer.scope.Scope;
027
028/**
029 *
030 * @author jimmy
031 */
032public class Analysis {
033
034    private final AstModel model;
035    private Scope scope;
036    private Map<Objective, AstSetExpr> objectiveExprs;
037    private final List<AstClafer> clafers;
038    private final List<AstAbstractClafer> abstractClafers;
039    private final List<AstConcreteClafer> concreteClafers;
040    private final List<Set<AstClafer>> clafersInParentAndSubOrder;
041    private List<AstConstraint> constraints;
042    private Set<AstConstraint> hardConstraints;
043    private Map<AstConstraint, AstBoolExpr> constraintExprs;
044    private Map<AstConcreteClafer, Card> cardMap;
045    private Map<AstClafer, Card> globalCardMap;
046    private Map<AstClafer, Format> formatMap;
047    private Map<AstAbstractClafer, Offsets> offsetMap;
048    private Map<AstClafer, PartialSolution> partialSolutionMap;
049    private Map<AstRef, IrDomain[]> partialIntsMap;
050    private Map<AstClafer, AstConcreteClafer[]> breakableChildrenMap;
051    private Map<AstRef, int[]> breakableRefsMap;
052    private Map<AstClafer, AstRef[]> breakableTargetsMap;
053    private Map<AstExpr, Type> typeMap;
054
055    Analysis(AstModel model, Scope scope) {
056        this(model, scope, Collections.<Objective>emptyList());
057    }
058
059    Analysis(AstModel model, Scope scope, List<Objective> objectives) {
060        this(model, scope, objectives,
061                AstUtil.getAbstractClafersInSubOrder(model),
062                AstUtil.getConcreteClafers(model),
063                AstUtil.getClafersInParentAndSubOrder(model));
064    }
065
066    Analysis(AstModel model, Scope scope,
067            List<Objective> objectives,
068            List<AstAbstractClafer> abstractClafers,
069            List<AstConcreteClafer> concreteClafers,
070            List<Set<AstClafer>> clafersInParentAndSubOrder) {
071        this.model = model;
072        this.scope = scope;
073        this.objectiveExprs = new HashMap<>();
074        for (Objective objective : objectives) {
075            this.objectiveExprs.put(objective, objective.getExpr());
076        }
077        this.clafers = append(abstractClafers, concreteClafers);
078        this.abstractClafers = abstractClafers;
079        this.concreteClafers = concreteClafers;
080        this.clafersInParentAndSubOrder = clafersInParentAndSubOrder;
081        this.constraints = AstUtil.getNestedConstraints(model);
082        this.hardConstraints = new HashSet<>(constraints.size());
083        this.constraintExprs = new HashMap<>(constraints.size());
084        for (AstConstraint constraint : constraints) {
085            if (constraint.isHard()) {
086                hardConstraints.add(constraint);
087            }
088            constraintExprs.put(constraint, constraint.getExpr());
089        }
090        this.cardMap = buildCardMap(clafers);
091    }
092
093    private static Map<AstConcreteClafer, Card> buildCardMap(List<AstClafer> clafers) {
094        Map<AstConcreteClafer, Card> cardMap = new HashMap<>();
095        for (AstClafer clafer : clafers) {
096            if (clafer instanceof AstConcreteClafer) {
097                AstConcreteClafer concreteClafer = (AstConcreteClafer) clafer;
098                cardMap.put(concreteClafer, concreteClafer.getCard());
099            }
100        }
101        return cardMap;
102    }
103
104    private static List<AstClafer> append(List<? extends AstClafer> abstractClafers, List<? extends AstConcreteClafer> concreteClafers) {
105        List<AstClafer> clafers = new ArrayList<>(abstractClafers.size() + concreteClafers.size());
106        clafers.addAll(abstractClafers);
107        clafers.addAll(concreteClafers);
108        return clafers;
109    }
110
111    public static Analysis analyze(AstModel model, Scopable scope, Analyzer... analyzers) {
112        return analyze(model, scope, Collections.<Objective>emptyList(), analyzers);
113    }
114
115    public static Analysis analyze(AstModel model, Scopable scope, List<Objective> objectives, Analyzer... analyzers) {
116        Analysis analysis = new Analysis(model, scope.toScope(), objectives);
117        for (Analyzer analyzer : analyzers) {
118            analysis = analyzer.analyze(analysis);
119        }
120        return analysis;
121    }
122
123    private <T> T notNull(String analysisName, T t) {
124        if (t == null) {
125            throw new AnalysisException(analysisName + " not yet analyzed.");
126        }
127        return t;
128    }
129
130    private <T> T notNull(String key, String analysisName, T t) {
131        if (t == null) {
132            throw new AnalysisException(analysisName + " for " + key + " not yet analyzed.");
133        }
134        return t;
135    }
136
137    private <T> T notNull(AstClafer key, String analysisName, T t) {
138        return notNull(key.getName(), analysisName, t);
139    }
140
141    /**
142     * Returns the original model. Analyzers are forbidden to alter the original
143     * model.
144     *
145     * @return the original model
146     */
147    public AstModel getModel() {
148        return model;
149    }
150
151    public int getScope(AstClafer clafer) {
152        return getScope().getScope(clafer);
153    }
154
155    public Scope getScope() {
156        return notNull("Scope", scope);
157    }
158
159    public Analysis setScope(Scope scope) {
160        this.scope = scope;
161        return this;
162    }
163
164    public AstSetExpr getExpr(Objective objective) {
165        return objectiveExprs.get(objective);
166    }
167
168    public Map<Objective, AstSetExpr> getObjectiveExprs() {
169        return Collections.unmodifiableMap(objectiveExprs);
170    }
171
172    public Analysis setObjectiveExprs(Map<Objective, AstSetExpr> objectiveExprs) {
173        this.objectiveExprs = objectiveExprs;
174        return this;
175    }
176
177    /**
178     * @return the Clafers in no specific order
179     */
180    public List<AstClafer> getClafers() {
181        return Collections.unmodifiableList(clafers);
182    }
183
184    /**
185     * @return the abstract Clafers in sub-order
186     */
187    public List<AstAbstractClafer> getAbstractClafers() {
188        return Collections.unmodifiableList(abstractClafers);
189    }
190
191    /**
192     * @return the concrete Clafers in parent-order
193     */
194    public List<AstConcreteClafer> getConcreteClafers() {
195        return Collections.unmodifiableList(concreteClafers);
196    }
197
198    /**
199     * @return the Clafers in parent-order and sub-order
200     */
201    public List<Set<AstClafer>> getClafersInParentAndSubOrder() {
202        return clafersInParentAndSubOrder;
203    }
204
205    public List<AstConstraint> getConstraints() {
206        return Collections.unmodifiableList(constraints);
207    }
208
209    public Analysis setConstraints(List<AstConstraint> constraints) {
210        this.constraints = constraints;
211        return this;
212    }
213
214    public boolean isHard(AstConstraint constraint) {
215        return hardConstraints.contains(constraint);
216    }
217
218    public boolean isSoft(AstConstraint constraint) {
219        return !isHard(constraint);
220    }
221
222    public Set<AstConstraint> getHardConstraints() {
223        return hardConstraints;
224    }
225
226    public Analysis setHardConstraints(Set<AstConstraint> hardConstraints) {
227        this.hardConstraints = hardConstraints;
228        return this;
229    }
230
231    public AstBoolExpr getExpr(AstConstraint constraint) {
232        return constraintExprs.get(constraint);
233    }
234
235    public Map<AstConstraint, AstBoolExpr> getConstraintExprs() {
236        return constraintExprs;
237    }
238
239    public Analysis setConstraintExprs(Map<AstConstraint, AstBoolExpr> constraintExprs) {
240        this.constraintExprs = constraintExprs;
241        return this;
242    }
243
244    public Card getCard(AstConcreteClafer clafer) {
245        return notNull(clafer, "Card", getCardMap().get(clafer));
246    }
247
248    public Map<AstConcreteClafer, Card> getCardMap() {
249        return notNull("Card", cardMap);
250    }
251
252    public Analysis setCardMap(Map<AstConcreteClafer, Card> cardMap) {
253        this.cardMap = cardMap;
254        return this;
255    }
256
257    public Card getGlobalCard(AstClafer clafer) {
258        return notNull(clafer, "GlobalCard", getGlobalCardMap().get(clafer));
259    }
260
261    public Map<AstClafer, Card> getGlobalCardMap() {
262        return notNull("Global card", globalCardMap);
263    }
264
265    public Analysis setGlobalCardMap(Map<AstClafer, Card> globalCardMap) {
266        this.globalCardMap = globalCardMap;
267        return this;
268    }
269
270    public Format getFormat(AstClafer clafer) {
271        return notNull(clafer, "Format", getFormatMap().get(clafer));
272    }
273
274    public Map<AstClafer, Format> getFormatMap() {
275        return notNull("Format", formatMap);
276    }
277
278    public Analysis setFormatMap(Map<AstClafer, Format> formatMap) {
279        this.formatMap = formatMap;
280        return this;
281    }
282
283    public Pair<AstConcreteClafer, Integer> getConcreteId(AstClafer clafer, int id) {
284        AstClafer sub = clafer;
285        int curId = id;
286        while (sub instanceof AstAbstractClafer) {
287            Offsets offset = getOffsets((AstAbstractClafer) sub);
288            sub = offset.getClafer(curId);
289            curId -= offset.getOffset(sub);
290        }
291        return new Pair<>((AstConcreteClafer) sub, curId);
292    }
293
294    public Pair<AstClafer, Integer> getSubId(AstAbstractClafer clafer, int id) {
295        Offsets offsets = getOffsets(clafer);
296        AstClafer sub = offsets.getClafer(id);
297        return new Pair<>(sub, id - offsets.getOffset(sub));
298    }
299
300    public Pair<AstAbstractClafer, Integer> getSuperId(AstClafer clafer, int id) {
301        assert clafer.hasSuperClafer();
302        int offset = getOffsets(clafer.getSuperClafer()).getOffset(clafer);
303        return new Pair<>(clafer.getSuperClafer(), id + offset);
304    }
305
306    public List<Pair<AstAbstractClafer, Integer>> getSuperIds(AstClafer clafer, int id) {
307        List<Pair<AstAbstractClafer, Integer>> superIds = new ArrayList<>();
308        AstClafer sup = clafer;
309        int curId = id;
310        while (sup.hasSuperClafer()) {
311            Pair<AstAbstractClafer, Integer> superId = getSuperId(sup, curId);
312            superIds.add(superId);
313            sup = superId.getFst();
314            curId = superId.getSnd().intValue();
315        }
316        return superIds;
317    }
318
319    public List<Pair<AstClafer, Integer>> getHierarcyOffsets(AstClafer clafer) {
320        return getHierarcyIds(clafer, 0);
321    }
322
323    public List<Pair<AstClafer, Integer>> getHierarcyIds(AstClafer clafer, int id) {
324        List<Pair<AstClafer, Integer>> superIds = new ArrayList<>();
325        superIds.add(new Pair<>(clafer, id));
326        AstClafer sup = clafer;
327        int curId = id;
328        while (sup.hasSuperClafer()) {
329            Pair<AstAbstractClafer, Integer> superId = getSuperId(sup, curId);
330            superIds.add(new Pair<AstClafer, Integer>(superId));
331            sup = superId.getFst();
332            curId = superId.getSnd().intValue();
333        }
334        return superIds;
335    }
336
337    public Pair<AstRef, Integer> getInheritedRefId(AstClafer clafer) {
338        AstClafer sup = clafer;
339        int curId = 0;
340        do {
341            if (sup.hasRef()) {
342                return new Pair<>(sup.getRef(), curId);
343            }
344            if (sup.hasSuperClafer()) {
345                curId += getOffsets(sup.getSuperClafer()).getOffset(sup);
346            }
347            sup = sup.getSuperClafer();
348        } while (sup != null);
349        return null;
350    }
351
352    public Offsets getOffsets(AstAbstractClafer clafer) {
353        return notNull(clafer, "Offset", getOffsetMap().get(clafer));
354    }
355
356    public Map<AstAbstractClafer, Offsets> getOffsetMap() {
357        return notNull("Offset", offsetMap);
358    }
359
360    public Analysis setOffsetMap(Map<AstAbstractClafer, Offsets> offsetMap) {
361        this.offsetMap = offsetMap;
362        return this;
363    }
364
365    public PartialSolution getPartialSolution(AstClafer clafer) {
366        return notNull(clafer, "Partial solution", getPartialSolutionMap().get(clafer));
367    }
368
369    public Map<AstClafer, PartialSolution> getPartialSolutionMap() {
370        return notNull("Partial solution", partialSolutionMap);
371    }
372
373    public Analysis setPartialSolutionMap(Map<AstClafer, PartialSolution> partialSolutionMap) {
374        this.partialSolutionMap = partialSolutionMap;
375        return this;
376    }
377
378    public IrDomain[] getPartialInts(AstRef ref) {
379        return notNull(ref.getSourceType(), "Partial integer", getPartialIntsMap().get(ref));
380    }
381
382    public Map<AstRef, IrDomain[]> getPartialIntsMap() {
383        return notNull("Partial integer", partialIntsMap);
384    }
385
386    public Analysis setPartialIntsMap(Map<AstRef, IrDomain[]> partialIntsMap) {
387        this.partialIntsMap = partialIntsMap;
388        return this;
389    }
390
391    public boolean hasInteritedBreakableChildren(AstClafer clafer) {
392        AstClafer sup = clafer;
393        do {
394            if (hasBreakableChildren(sup)) {
395                return true;
396            }
397            sup = sup.getSuperClafer();
398        } while (sup != null);
399        return false;
400    }
401
402    public boolean hasBreakableChildren(AstClafer clafer) {
403        return getBreakableChildren(clafer).length > 0;
404    }
405
406    public AstConcreteClafer[] getBreakableChildren(AstClafer clafer) {
407        return notNull("Breakable children", getBreakableChildrenMap().get(clafer));
408    }
409
410    public Map<AstClafer, AstConcreteClafer[]> getBreakableChildrenMap() {
411        return notNull("Breakable children", breakableChildrenMap);
412    }
413
414    public Analysis setBreakableChildrenMap(Map<AstClafer, AstConcreteClafer[]> breakableChildren) {
415        this.breakableChildrenMap = breakableChildren;
416        return this;
417    }
418
419    public boolean isBreakableRef(AstRef ref) {
420        return getBreakableRefsMap().containsKey(ref);
421    }
422
423    public boolean isBreakableRefId(AstRef ref, int id) {
424        int[] breakbleIDs = getBreakableRefsMap().get(ref);
425        if (breakbleIDs == null) {
426            return false;
427        }
428        return Util.in(id, breakbleIDs);
429    }
430
431    public Map<AstRef, int[]> getBreakableRefsMap() {
432        return notNull("Breakable ref", breakableRefsMap);
433    }
434
435    public Analysis setBreakableRefsMap(Map<AstRef, int[]> breakableRefs) {
436        this.breakableRefsMap = breakableRefs;
437        return this;
438    }
439
440    public boolean isInheritedBreakableTarget(AstClafer clafer) {
441        AstClafer sup = clafer;
442        do {
443            if (isBreakableTarget(sup)) {
444                return true;
445            }
446            sup = sup.getSuperClafer();
447        } while (sup != null);
448        return false;
449    }
450
451    public boolean isBreakableTarget(AstClafer clafer) {
452        return getBreakableTargetsMap().containsKey(clafer);
453    }
454
455    public AstRef[] getBreakableTarget(AstClafer clafer) {
456        AstRef[] ref = getBreakableTargetsMap().get(clafer);
457        return ref == null ? new AstRef[0] : ref;
458    }
459
460    public Map<AstClafer, AstRef[]> getBreakableTargetsMap() {
461        return notNull("Breakable target", breakableTargetsMap);
462    }
463
464    public Analysis setBreakableTargetsMap(Map<AstClafer, AstRef[]> breakableTargetsMap) {
465        this.breakableTargetsMap = breakableTargetsMap;
466        return this;
467    }
468
469    public Type getType(AstExpr expr) {
470        return notNull(expr.toString(), "Type", getTypeMap().get(expr));
471    }
472
473    public AstClafer getCommonSupertype(AstExpr expr) {
474        return notNull(expr.toString(), "Type", getTypeMap().get(expr)).getCommonSuperType();
475    }
476
477    public Map<AstExpr, Type> getTypeMap() {
478        return notNull("Type", typeMap);
479    }
480
481    public Analysis setTypeMap(Map<AstExpr, Type> typeMap) {
482        this.typeMap = typeMap;
483        return this;
484    }
485}