source: trunk/Compiler/ModelicaFrontEnd/src/jastadd/flattening/Connections.jrag @ 12934

Last change on this file since 12934 was 12934, checked in by Jesper Mattsson, 6 months ago

Reverted r12897 due to some test models failing. (#5752)

File size: 120.6 KB
Line 
1/*
2    Copyright (C) 2009 Modelon AB
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, version 3 of the License.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*/
16
17import java.util.Arrays;
18import java.util.Iterator;
19import java.util.LinkedHashSet;
20import java.util.ArrayList;
21import java.util.Collection;
22import java.util.Map;
23import java.util.HashMap;
24import java.util.Queue;
25import java.util.Set;
26import java.util.ArrayDeque;
27import java.util.PriorityQueue;
28
29import org.jmodelica.util.Enumerator;
30import org.jmodelica.util.collections.GrowableSetIterable;
31
32aspect Connections {
33
34        /* Machinery to manage connection sets */
35
36        public ConnectionSetManager FClass.connectionSetManager = new ConnectionSetManager();
37       
38        public ConnectionSetManager FClass.getConnectionSetManager() {
39                return connectionSetManager;
40        }
41
42    public void FClass.genConnectionEquations(Flattener f) {
43        beginStep("genConnectionEquations()");
44        for (ConnectionSet set : connectionSetManager.getConnectionSetList()) 
45            set.generateEquations(f);
46        connectionSetManager.generateEqualityConstraints(f);
47        connectionSetManager.disconnectFromInstanceTree();
48        endStep("genConnectionEquations()");
49    }
50
51    public void ASTNode.enableStreamsRewrite() {
52        for (ASTNode n : this)
53            n.enableStreamsRewrite();
54    }
55
56    public void FStreamBuiltIn.enableStreamsRewrite() {
57        super.enableStreamsRewrite();
58        rewriteStreams = true;
59        is$Final = false;
60    }
61
62    protected boolean FStreamBuiltIn.rewriteStreams = false;
63
64    public class FClass {
65        public class enableStreamsRewrite extends Transformation {
66            // Depends on enableIfEquationElimination being done
67            public void perform() {
68                enableStreamsRewrite();
69                change();
70            }
71        }
72    }
73
74    /**
75     * Rewrite the inSteam operator to expanded form.
76     */
77    rewrite FInStream {
78        when (rewriteStreams) to FExp expandInStreamExp();
79    }
80
81    protected FExp FInStream.expandInStreamExp() {
82        // Always create expression from the connection set where the variable is an inside connector.
83        String name = getFExp().asCommonAccess().name();
84        ConnectionSet cs = myFClass().getConnectionSetManager().getStreamConnectionSet(name, false);
85        return cs.expandInStreamExp(name);
86    }
87
88    protected FExp FDerStream.expandInStreamExp() {
89        // Note: this handles both enableStreamsRewrite and enableExpandedInStreamRewrite
90        if (getFExp().needsLaterInStreamRewrite()) {
91            return new FDerStream(getFExp().treeCopy());
92        } else {
93            return getFExp().diff(FExp.TIME);
94        }
95    }
96
97    syn boolean FExp.needsLaterInStreamRewrite() = false;
98    eq FExInStream.needsLaterInStreamRewrite()   = true;
99    eq FDerStream.needsLaterInStreamRewrite()    = true;
100
101    public class FClass {
102        public class enableExpandedInStreamRewrite extends Transformation {
103            // Depends on variabilityPropagationIfSet and the following aliasEliminationIfSet being done
104            public void perform() {
105                enableExpandedInStreamRewrite();
106                change();
107            }
108        }
109    }
110   
111    public void ASTNode.enableExpandedInStreamRewrite() {
112        for (ASTNode n : this)
113            n.enableExpandedInStreamRewrite();
114    }
115   
116    public void FDerStream.enableExpandedInStreamRewrite() {
117        super.enableExpandedInStreamRewrite();
118        rewriteStreams = true;
119        is$Final = false;
120    }
121
122    public void FExInStream.enableExpandedInStreamRewrite() {
123        super.enableExpandedInStreamRewrite();
124       
125        // We don't need to add anything for < 2 contributors
126        FExp stream = getDefault().treeCopy();
127        int n = 0;
128        for (InStreamPart cont : contributors()) {
129            stream = cont.stream.treeCopy();
130            n++;
131            if (n > 1) {
132                break;
133            }
134        }
135        if (n < 2) {
136            replaceMe(stream);
137            return;
138        }
139       
140        FExp eps   = getEps();
141        FExp alpha = generateAlpha(eps, generateS());
142       
143        // Generate replacement expression
144        ArrayList<FExp> nominatorTerms   = new ArrayList<FExp>();
145        ArrayList<FExp> denominatorTerms = new ArrayList<FExp>();
146        for (InStreamPart cont : contributors()) {
147            stream = cont.stream.treeCopy();
148            FExp flow = cont.flow.treeCopy();
149            if (!cont.outside) {
150                flow = new FNegExp(flow);
151            }
152            FExp posMax = positiveMax(eps, flow, alpha);
153            nominatorTerms.add(new FMulExp(posMax.treeCopyNoTransform(), stream));
154            denominatorTerms.add(posMax);
155        }
156        replaceMe(new FDivExp(
157                FExp.createBalancedBinaryTree(new FAddExp(), nominatorTerms),
158                FExp.createBalancedBinaryTree(new FAddExp(), denominatorTerms)));
159    }
160
161    /**
162     * Generate and add variable with varType, add an equation for the variable with rhs <code>exp</code>.
163     */
164    private FExp FExInStream.addEquation(FlatVariableMap.GeneratedVarType varType, FExp exp) {
165        String name = calcGeneratedVarName(varType);
166        TypePrefixVariability v = variability().combine(Variability.FIXEDPARAMETER);
167        myFClass().addFVariable(type().createTempFVariable(new FAccessString(name), variability()));
168        myFClass().equationList(v).add(new FEquation(new FAccessExp(name), exp));
169        return new FAccessExp(name);
170    }
171   
172    /**
173     * Get (no transform) equation list matching the variability <code>v</code>.
174     */
175    public List<FAbstractEquation> FClass.equationList(TypePrefixVariability v) {
176        if (v.knownParameterOrLess()) {
177            throw new UnsupportedOperationException();
178        } else if (v.fixedParameterVariability()) {
179            return getParameterEquationsNoTransform();
180        } else if (v.initialParameterVariability()) {
181            return getFInitialEquationsNoTransform();
182        } else {
183            return getFAbstractEquationsNoTransform();
184        }
185    }
186
187    /**
188     * Add variable and equation for s_i, return reference to s_i
189     */
190    private FExp FExInStream.generateS() {
191        ArrayList<FExp> terms = new ArrayList<FExp>();
192        for (InStreamPart cont : contributors()) {
193            FExp flow = cont.flow.treeCopy();
194            if (!cont.outside) {
195                flow = new FNegExp(flow);
196            }
197            terms.add(new FMaxExp(flow, new Opt<FExp>(new FIntegerLitExp(0))));
198           
199        }
200        return addEquation(FlatVariableMap.GeneratedVarType.STREAM_S,
201                FExp.createBalancedBinaryTree(new FAddExp(), terms));
202    }
203
204    /**
205     * Add variable and equation for alpha_i, return reference to alpha_i
206     */
207    private FExp FExInStream.generateAlpha(FExp eps, FExp s) {
208        FExp sDivEps = new FDivExp(s.treeCopyNoTransform(), eps.treeCopyNoTransform());
209        FExp alpha = new FSmoothExp(
210                new FIntegerLitExp(1), 
211                new FIfExp(
212                        new FGtExp(
213                                s.treeCopyNoTransform(),
214                                eps.treeCopyNoTransform()),
215                        new FIntegerLitExp(1),
216                        new FIfExp(
217                                new FGtExp(
218                                        s.treeCopyNoTransform(),
219                                        new FIntegerLitExp(0)),
220                                new FMulExp(
221                                        sDivEps.treeCopyNoTransform(), 
222                                        new FMulExp(
223                                                sDivEps.treeCopyNoTransform(),
224                                                new FSubExp(
225                                                        new FIntegerLitExp(3),
226                                                        new FMulExp(
227                                                                new FIntegerLitExp(2),
228                                                                s.treeCopyNoTransform())))),
229                                new FIntegerLitExp(0))));
230        return addEquation(FlatVariableMap.GeneratedVarType.STREAM_ALPHA, alpha);
231    }
232
233    /**
234     * Add variable and equation for positiveMax_i, return reference to positiveMax_i
235     */
236    private FExp FExInStream.positiveMax(FExp eps, FExp flow, FExp alpha) {
237        FExp exp = new FAddExp(
238                new FMulExp(
239                        alpha.treeCopyNoTransform(),
240                        new FMaxExp(
241                                flow,
242                                new Opt<FExp>(new FIntegerLitExp(0)))),
243                new FMulExp(
244                        new FSubExp(
245                                new FIntegerLitExp(1),
246                                alpha.treeCopyNoTransform()),
247                        eps.treeCopyNoTransform()));
248        return addEquation(FlatVariableMap.GeneratedVarType.STREAM_POSMAX, exp);
249    }
250
251
252    syn int FExInStream.numVars() = getNumVar() / 3;
253
254    syn FExp FExInStream.flowExp(int i) = getVar(i * 3);
255
256    syn FExp FExInStream.streamExp(int i) = getVar(i * 3 + 1);
257
258    syn FExp FExInStream.isOutsideExp(int i) = getVar(i * 3 + 2);
259
260    syn boolean FExInStream.isOutside(int i) = isOutsideExp(i).ceval().booleanValue();
261
262    public class FExInStream {
263        /**
264         * An iterable over all stream/flow pairs that contribute to this inStream().
265         */
266        public Iterable<InStreamPart> contributors() {
267            return new ContributorIterable();
268        }
269       
270        public class InStreamPart {
271            public final FExp flow;
272            public final FExp stream;
273            public final boolean outside;
274
275            public InStreamPart(int i) {
276                flow = flowExp(i);
277                stream = streamExp(i);
278                outside = isOutside(i);
279            }
280
281            public boolean contributes() {
282                if (outside) {
283                    return flow.maxRealValue() > 0;
284                } else {
285                    return flow.minRealValue() < 0;
286                }
287            }
288        }
289
290        private class ContributorIterable implements Iterable<InStreamPart> {
291            private int n = numVars();
292           
293            public Iterator<InStreamPart> iterator() {
294                return new ContributorIterator();
295            }
296
297            private class ContributorIterator implements Iterator<InStreamPart> {
298                private int i;
299                private InStreamPart next;
300
301                public ContributorIterator() {
302                    i = -1;
303                    step();
304                }
305
306                public boolean hasNext() {
307                    return i < n;
308                }
309
310                public InStreamPart next() {
311                    InStreamPart res = next;
312                    step();
313                    return res;
314                }
315               
316                public void remove() {
317                    throw new UnsupportedOperationException();
318                }
319
320                private void step() {
321                    do {
322                        i++;
323                        next = (i < n) ? new InStreamPart(i) : null;
324                    } while (i < n && !next.contributes());
325                }
326            }
327        }
328    }
329
330    /**
331     * Check if this expression is multiplied with the given variable.
332     */
333    inh boolean FExp.isMultipliedWith(FVariable v);
334    eq BaseNode.getChild().isMultipliedWith(FVariable v)          = false;
335    eq FDotMulExp.getRight().isMultipliedWith(FVariable v)        = getLeft().isMultiplicationOf(v) || isMultipliedWith(v);
336    eq FDotMulExp.getLeft().isMultipliedWith(FVariable v)         = getRight().isMultiplicationOf(v) || isMultipliedWith(v);
337    eq FSemiLinearExp.getPosSlope().isMultipliedWith(FVariable v) = getX().isMultiplicationOf(v) || isMultipliedWith(v);
338    eq FSemiLinearExp.getNegSlope().isMultipliedWith(FVariable v) = getX().isMultiplicationOf(v) || isMultipliedWith(v);
339
340    /**
341     * Check if this expression is an access to v or a multiplication where one of the
342     * multiplicands is an access to v.
343     */
344    syn boolean FExp.isMultiplicationOf(FVariable v) = false;
345    eq FAccessExp.isMultiplicationOf(FVariable v)    = v == myFV();
346    eq FNegExp.isMultiplicationOf(FVariable v)       = getFExp().isMultiplicationOf(v);
347    eq FDotMulExp.isMultiplicationOf(FVariable v)    = 
348        getLeft().isMultiplicationOf(v) || getRight().isMultiplicationOf(v);
349
350    rewrite FActualStream {
351        when (rewriteStreams) to FExp {
352            // Get the stream variable name prefix
353            FAccess varName = getFExp().asFAccessExp().getFAccess();
354            FAccess prefix = varName.copyPrefix();
355            ArrayList<ConnectionSetEntry> cses = 
356                myFClass().getConnectionSetManager().getFlowVariables(prefix.name());
357            ConnectionSetEntry cse = null;
358            if (cses.size() == 1) {
359                cse = cses.get(0);
360            } else if (cses.size() == 2) {
361                cse = cses.get(0);
362                // Check that this is an inside and an outside entry for the same name
363                ConnectionSetEntry cse2 = cses.get(1);
364                if (cse.isInside() == cse2.isInside() || !cse.name().equals(cse2.name()))
365                    cse = null;
366            }
367            if (cse != null) {
368                // Get the name of the single flow variable of the connector, then get the variables.
369                FAccess flowVarName = cse.getFAccess();
370                FRealVariable var = (FRealVariable) varName.myFV();
371                FRealVariable flowVar = (FRealVariable) varName.lookupFV(cse.name());
372               
373                // Decide what parts are valid
374                boolean constant = flowVar.isConstant();
375                double min = constant ? flowVar.ceval().realValue() : flowVar.minAttribute();
376                double max = constant ? min : flowVar.maxAttribute();
377                boolean multFlow = isMultipliedWith(flowVar);
378                boolean badMinMax = min > max;
379                boolean thenValid = badMinMax || max > 0.0;
380                boolean elseValid = badMinMax || min < 0.0 || (min == 0.0 && !(thenValid && multFlow));
381               
382                // Generate the expression
383                FExp res = null, elseExp = null;
384                FInStream thenExp = null;
385                if (thenValid) {
386                    res = thenExp = new FInStream(var.createAccessExp());
387                    thenExp.rewriteStreams = true; // Enable further rewrite of the inStream operator.
388                }
389                if (elseValid) 
390                    res = elseExp = var.createAccessExp();
391                if (thenValid && elseValid) {
392                    FExp guard = new FGtExp(flowVar.createAccessExp(), new FRealLitExp(0.0));
393                    res = new FIfExp(guard, thenExp, elseExp);
394                    if (multFlow)
395                        res = new FSmoothExp(0, res);
396                }
397                return res;
398            }
399            // There should already have been an error message in this case, so we should never come here.
400            throw new UnsupportedOperationException("Rewriting actualStream() for '" + varName.name() + 
401                    "': found " + cses.size() + " matching flow variables");
402        }
403    }
404
405       
406        public abstract class ConnectionSet extends TreeSet<ConnectionSetEntry> {
407               
408                protected ConnectionSet(ConnectionSetEntry e) {
409                        add(e);
410                }
411               
412                public static ConnectionSet create(ConnectionSetEntry e) {
413                        if (e.isFlow())
414                                return new FlowConnectionSet(e);
415                        else if (e.isStream())
416                                return new StreamConnectionSet(e);
417                        else 
418                                return new PotentialConnectionSet(e);
419                }
420               
421                public void disconnectFromInstanceTree() {
422                        for (ConnectionSetEntry e : this)
423                                e.disconnectFromInstanceTree();
424                }
425               
426                public int getNumInside() {
427                        int nInside = 0;
428                        for (ConnectionSetEntry cse : this) {
429                                if (cse.isInside()) {
430                                        nInside++;
431                                }
432                        }
433                        return nInside;
434                }
435               
436                public int getNumOutside() {
437                        int nOutside = 0;
438                        for (ConnectionSetEntry cse : this) {
439                                if (cse.isOutside()) {
440                                        nOutside++;
441                                }
442                        }
443                        return nOutside;
444                }
445
446                public ConnectionSetEntry getConnectionSetEntry(String name, boolean outside) {
447                        for (ConnectionSetEntry cse : this) {
448                                if (cse.equals(name, outside)) {
449                                        return cse;
450                                }
451                        }
452                        return null;
453                }
454               
455                /**
456                 * Check if a rewrite expression can be generated for an inStream() on a variable in this set.
457                 *
458                 * Default implementation returns false, since inStream() only applies to stream connecton sets.
459                 */
460                public boolean canCreateInStreamExp() {
461                        return false;
462                }
463               
464                /**
465                 * Create rewrite expression for an inStream() on a variable in this set.
466                 *
467                 * Only valid for stream connecton sets.
468                 *
469                 * @param name  the name of the variable to generate name for
470                 */
471                public FExp expandInStreamExp(String name) {
472                        throw new UnsupportedOperationException();
473                }
474               
475                /**
476                 * Generate equations for this set.
477                 *
478                 * @param eqns  equation list to add equations to
479                 */
480                public abstract void generateEquations(Flattener f);
481               
482                /**
483                 * Return all ConnectionSetEntrys corresponding to flow variables based
484                 * on a name prefix. This method is useful when generating expressions
485                 * for actualStream operators when the name of the flow variable in a
486                 * stream connector is needed.
487                 */
488                public ArrayList<ConnectionSetEntry> getFlowVariables(String prefix) {
489                        return new ArrayList<ConnectionSetEntry>(0);
490                }
491               
492                public int numStreamVariables() {
493                        int n_stream_vars = 0;
494                        for (ConnectionSetEntry e : this) {
495                                if (e.isStream()) {
496                                        n_stream_vars++;
497                                }
498                        }
499                        return n_stream_vars;
500                }
501               
502                public String toString() {
503                        StringBuffer str = new StringBuffer();
504                       
505                        str.append("Connection set (");
506                        str.append(typeString());
507                        str.append("): {");
508                        String set_str = super.toString();
509                        str.append(set_str.substring(1, set_str.length() - 1));
510                        str.append("}\n");
511                        return str.toString();
512                }
513               
514                protected abstract String typeString();
515               
516        }
517       
518        public class FlowConnectionSet extends ConnectionSet {
519               
520                public FlowConnectionSet(ConnectionSetEntry e) {
521                        super(e);
522                }
523               
524                public String typeString() {
525                        return "flow";
526                }
527               
528                public ArrayList<ConnectionSetEntry> getFlowVariables(String prefix) {
529                        ArrayList<ConnectionSetEntry> cses = new ArrayList<ConnectionSetEntry>();
530                        for (ConnectionSetEntry cse : this) 
531                                if (cse.prefix().equals(prefix)) 
532                                        cses.add(cse);
533                        return cses;
534                }
535
536        public void generateEquations(Flattener f) {
537            ConnectionSetEntry fst = first();
538            if (size() == 1 && fst.isInside() && fst.getVar().isTopLevelInputOutput()) {
539                return;
540            }
541            FExp e = null;
542            for (ConnectionSetEntry cse : this) 
543                e = cse.buildFlow(f, e);
544            FExp zero = fst.createZeroExp(f);
545            f.addNormalEquation(new FEquation(e, zero));
546        }
547
548        }
549
550    public class StreamConnectionSet extends ConnectionSet {
551
552        private double nominal = Double.MAX_VALUE;
553
554        public StreamConnectionSet(ConnectionSetEntry e) {
555            super(e);
556        }
557
558        public String typeString() {
559            return "stream";
560        }
561
562        public void generateEquations(Flattener f) {
563            for (ConnectionSetEntry e : this) {
564                nominal = Math.min(e.flowNominal(), nominal);
565            }
566            for (ConnectionSetEntry e : this) {
567                if (e.isOutside()) {
568                    FExp left = e.createFAccessExp();
569                    FExp right = expandInStreamExp(e, null, e.createZeroExp(f));
570                    f.addNormalEquation(new FEquation(left, right));
571                }
572            }
573        }
574
575        /**
576         * Create rewrite expression for an inStream() on a variable in this set.
577         *
578         * @param name  the name of the variable to generate name for
579         */
580        public FExp expandInStreamExp(String name) {
581            ConnectionSetEntry e = getConnectionSetEntry(name, false);
582            FExp exp = expandInStreamExp(e, name, new FAccessExp(name));
583            exp.enableStreamsRewrite();
584            return exp;
585        }
586
587        /**
588         * Create an expression for the inStream() of the given entry.
589         *
590         * Used for inStream() for inside entries and for additional equations for outside entries.
591         *
592         * @param cse   the entry to generate the expression for
593         * @param name  the name of the scalar var to generate the stream access for,
594         *              of null for the entire array (in scalar case they are equivalent)
595         * @param def   expression to use when there are no contributing streams
596         */
597        private FExp expandInStreamExp(ConnectionSetEntry cse, String name, FExp def) {
598            FExp stream = def;
599            List<FExp> vars = new List<FExp>();
600            for (ConnectionSetEntry e : this) {
601                if (cse != e) {
602                    stream = (name == null) ? e.createFAccessExp() : e.createCommonAccessExp(name, cse);
603                    if (e.isOutside()) 
604                        stream = new FInStream(stream);
605                    vars.add(e.createFlowCommonAccessExp());
606                    vars.add(stream);
607                    vars.add(FBooleanLitExp.create(e.isOutside()));
608                }
609            }
610           
611            if (vars.getNumChildNoTransform() < 6) {
612                return stream;
613            } else {
614                return new FExInStream(def, new FMulExp(new FInStreamEpsExp(), new FRealLitExp(nominal)), vars);
615            }
616        }
617    }
618
619    /**
620     * Find the flow variable corresponding to this stream variable.
621     *
622     * Only valid for stream variables.
623     */
624    inh InstComponentDecl InstComponentDecl.myFlowVar();
625    eq InstComponentDecl.getChild().myFlowVar() = isConnector() ? findFlowVar() : null;
626    eq InstClassDecl.getChild().myFlowVar()     = isConnector() ? findFlowVar() : null;
627    eq InstRoot.getChild().myFlowVar()          = null;
628    eq Root.getChild().myFlowVar()              = null;
629
630    /**
631     * Find the first flow variable in this node, if any.
632     */
633    syn InstComponentDecl InstNode.findFlowVar() {
634        for (InstComponentDecl icd : allInstComponentDecls())
635            if (icd.isFlow())
636                return icd;
637        return null;
638    }
639
640    public class PotentialConnectionSet extends ConnectionSet {
641       
642        public PotentialConnectionSet(ConnectionSetEntry e) {
643            super(e);
644        }
645
646        public String typeString() {
647            return "potential";
648        }
649
650        public void generateEquations(Flattener f) {
651            if (size() == 1) {
652                // Cell that is not connected to in array in expandable connector
653                f.addNormalEquation(first().createZeroEquation(f));
654            } else {
655                FExp e1 = null;
656                for (ConnectionSetEntry cse : this) {
657                    FExp e2 = cse.createFAccessExp();
658                    if (e1 != null) {
659                        f.addNormalEquation(new FEquation(e1, e2));
660                    }
661                    e1 = e2;
662                }
663            }
664        }
665       
666    }
667
668
669        public class ConnectionSetManager {
670       
671                private ArrayList<ConnectionSet> list = new ArrayList<ConnectionSet>();
672                private ArrayList<EqualityConstraintConnection> equalityConstraints = new ArrayList<EqualityConstraintConnection>();
673                private Map<ConnectionSetEntry, ConnectionSet> cseMap = new HashMap<ConnectionSetEntry, ConnectionSet>();
674                private Map<String, CSENameMapEntry> cseStreamMap = new HashMap<String, CSENameMapEntry>();
675                private OverconstrainedConnectionGraph graph = null;
676                private ExpandableConnectorSets expandable = null;
677               
678        private static class CSENameMapEntry {
679            private ConnectionSetEntry outsideCSE = null;
680            private ConnectionSet outside = null;
681            private ConnectionSetEntry insideCSE = null;
682            private ConnectionSet inside = null;
683        }
684
685        private void updateCSEMapEntry(ConnectionSetEntry cse, ConnectionSet newValue) {
686            cseMap.put(cse, newValue);
687            if (cse.isStream()) {
688                for (String name : cse.names()) {
689                    CSENameMapEntry entry = cseStreamMap.get(name);
690                    if (entry == null) {
691                        entry = new CSENameMapEntry();
692                        cseStreamMap.put(name, entry);
693                    }
694                    if (cse.isOutside()) {
695                        entry.outsideCSE = cse;
696                        entry.outside = newValue;
697                    } else {
698                        entry.insideCSE = cse;
699                        entry.inside = newValue;
700                    }
701                }
702            }
703        }
704               
705                public ArrayList<ConnectionSet> getConnectionSetList() {
706                        return list;
707                }
708               
709                public OverconstrainedConnectionGraph getGraph() {
710                        if (graph == null) 
711                                graph = new OverconstrainedConnectionGraph();
712                        return graph;
713                }
714       
715       public ExpandableConnectorSets getExpandable() {
716           if (expandable == null) 
717               expandable = new ExpandableConnectorSets();
718           return expandable;
719       }
720       
721      public boolean isExpandableConnectorsDone() {
722          return expandable != null && expandable.isExpansionDone();
723      }
724
725        public void buildOverconstrainedConnectionTrees() {
726            if (graph != null) 
727                graph.buildTrees(this);
728        }
729       
730        public void elaborateExpandableConnectors() {
731            if (expandable != null) {
732                expandable.elaborate(this);
733                expandable = null;
734            }
735        }
736
737        public void addFlowVar(InstComponentDecl var, boolean outside, FAccess name) {
738            addVar(ConnectionSetEntry.create(var, outside, name));
739        }
740
741        public void addExpandableArrayMember(InstComponentDecl var, FAccess name) {
742            // If var is present as inner, don't add it as outer
743            if (getConnectionSet(ConnectionSetEntry.create(var, false, name)) == null)
744                addVar(ConnectionSetEntry.create(var, true, name));
745        }
746
747                /**
748                 * Add vars to connection sets.
749                 *
750                 * Filters out parameters and constants.
751                 */
752                public void addVars(ConnectionSetEntry cse1, ConnectionSetEntry cse2) {
753
754//                      log.debug("ConnectionSetManager.addVars");
755               
756//                  System.out.println(namePrefix1.name()+" . "+var1.name() + " outside: " + outside1);
757//                  System.out.println(namePrefix2.name()+" . "+var2.name() + " outside: " + outside2);
758               
759                        // Don't add parameters or constants to connection set
760                        // TODO: Add them, but generate asserts instead of equations
761                        if (cse1.getVar().variability().parameterOrLess() || cse2.getVar().variability().parameterOrLess()) 
762                                return;
763
764                        ConnectionSet setA = getConnectionSet(cse1);
765                        ConnectionSet setB = getConnectionSet(cse2);
766                       
767                        if (setA != null && setB != null) {
768                                if (setA != setB)
769                                        merge(setA, setB);
770                        } else if (setA != null && setB == null) {
771                                add(setA, cse2);
772                        } else if (setA == null && setB != null) {
773                                add(setB, cse1);
774                        } else if (setA == null && setB == null) {
775                                join(cse1, cse2);
776                        }
777                }
778               
779                public void addEqualityConstraint(ConnectionSetEntry cse1, ConnectionSetEntry cse2, FAccess prefix) {
780                        equalityConstraints.add(new EqualityConstraintConnection(cse1, cse2, prefix));
781                }
782       
783        private void addVar(ConnectionSetEntry entry) {
784            if (getConnectionSet(entry) == null) {
785                ConnectionSet set = ConnectionSet.create(entry);
786                list.add(set);
787                updateCSEMapEntry(entry, set);
788            }
789        }
790               
791                private void join(ConnectionSetEntry entryA, ConnectionSetEntry entryB) {
792                        ConnectionSet set = ConnectionSet.create(entryA);
793                        set.add(entryB);
794                        list.add(set);
795                        updateCSEMapEntry(entryA, set);
796                        updateCSEMapEntry(entryB, set);
797                }
798               
799                private void add(ConnectionSet set, ConnectionSetEntry entry) {
800                        set.add(entry);
801                        updateCSEMapEntry(entry, set);
802                }
803               
804                private void merge(ConnectionSet setA, ConnectionSet setB) {
805                        if (setA.size() < setB.size()) {
806                                ConnectionSet tmp = setA;
807                                setA = setB;
808                                setB = tmp;
809                        }
810                        for (ConnectionSetEntry entry : setB)
811                            updateCSEMapEntry(entry, setA);
812                        setA.addAll(setB);
813                        list.remove(setB);
814                }
815               
816                public ConnectionSet getConnectionSet(ConnectionSetEntry cse) {
817                        return cseMap.get(cse);
818                }
819
820                public ConnectionSet getStreamConnectionSet(String name, boolean outside) {
821            CSENameMapEntry entry = cseStreamMap.get(name);
822            if (entry == null)
823                return null;
824            if (outside)
825                return entry.outside;
826            else
827                return entry.inside;
828                }
829
830                public ArrayList<ConnectionSetEntry> getFlowVariables(String prefix) {
831                        ArrayList<ConnectionSetEntry> cses = new ArrayList<ConnectionSetEntry>();
832                        for (ConnectionSet set : list) {
833                                cses.addAll(set.getFlowVariables(prefix));             
834                        }
835                        return cses;
836                }
837               
838                public void disconnectFromInstanceTree() {
839                        for (ConnectionSet set : list)
840                                set.disconnectFromInstanceTree();
841            if (graph != null) 
842                graph.disconnectFromInstanceTree();
843                        equalityConstraints = null;
844                }
845               
846                public void generateEqualityConstraints(Flattener f) {
847                        for (EqualityConstraintConnection ecc : equalityConstraints)
848                                ecc.generate(f);
849                }
850               
851                public String printConnectionSets() {
852               
853                        StringBuffer str = new StringBuffer();
854                       
855                        str.append("Connection sets: " + list.size() + " sets\n");
856                       
857                        // Print connection sets
858                        for(ConnectionSet set : list) {
859                                str.append(set);
860                        }
861
862                        return str.toString();
863                }
864        }
865
866
867    public class ConnectionSetEntry implements Comparable<ConnectionSetEntry> {
868
869        private InstComponentDecl cd;
870        private boolean outside;
871        private FAccess access;
872        private String prefix;
873        private String str;
874        private String[] scalarNames;
875
876        public static ConnectionSetEntry create(InstComponentDecl cd, boolean outside, FAccess access) {
877            if (cd.isFlow()) 
878                return new FlowConnectionSetEntry(cd, outside, access);
879            if (cd.isStream()) 
880                return new StreamConnectionSetEntry(cd, outside, access);
881            return new ConnectionSetEntry(cd, outside, access);
882        }
883
884        private ConnectionSetEntry(InstComponentDecl cd, boolean outside, FAccess access) {
885            //log.debug("Created ConnectionSetEntry: " + cd.name());
886            this.cd = cd;
887            this.outside = outside;
888            this.access = access;
889            prefix = null;
890        }
891
892        public void disconnectFromInstanceTree() {
893            cd = null;
894        }
895
896        public boolean isOutside() {
897            return outside;
898        }
899
900        public boolean isInside() {
901            return !outside;
902        }
903
904        public InstComponentDecl getVar() {
905            return cd;
906        }
907
908        public boolean isFlow() {
909            return false;
910        }
911
912        public boolean isStream() {
913            return false;
914        }
915
916        public String name() {
917           return access.name();
918        }
919       
920        public String[] names() {
921            if (scalarNames != null)
922                return scalarNames;
923            else
924                return new String[] {name()};
925        }
926
927        public String prefix() {
928            if (prefix == null) 
929                prefix = access.copyPrefix().name();
930            return prefix;
931        }
932
933        public boolean equals(String name, boolean outside) {
934            if (outside != this.outside)
935                return false;
936            if (scalarNames != null) {
937                for (String scalarName : scalarNames)
938                    if (name.equals(scalarName))
939                        return true;
940                return false;
941            } else {
942                return name.equals(name());
943            }
944        }
945
946        public String toString() {
947            if (str == null)
948                str = access + (outside ? " (o)" : " (i)");
949            return str;
950        }
951
952        public FAccess getFAccess() {
953            return access;
954        }
955
956        public FAccessExp createFAccessExp() {
957            return new FAccessExp(access.treeCopy());
958        }
959
960        /**
961         * Create an FAccessExp that is an access to a variable in this entry, that is connected to
962         * the variable named <code>name</code> in <code>other</code>.
963         */
964        public FAccessExp createCommonAccessExp(String name, ConnectionSetEntry other) {
965            if (other == this)
966                return new FAccessExp(name); 
967            if (scalarNames == null || other.scalarNames == null)
968                return createFAccessExp();
969            int i;
970            for (i = 0; i < other.scalarNames.length && !other.scalarNames[i].equals(name); i++);
971            return (i < scalarNames.length) ? new FAccessExp(scalarNames[i]) : createFAccessExp();
972        }
973
974        /**
975         * Create a zero expression suitable for this entry.
976         */
977        public FExp createZeroExp(Flattener f) {
978            FType t = cd.type();
979            if (access.hasFArraySubscripts() && access.getFArraySubscripts().accessNdims() == 0) {
980                t = t.scalarType();
981            }
982            return t.flattenZeroLiteral(f);
983        }
984
985        /**
986         * Create an equation setting the variable of this entry to zero.
987         */
988        public FAbstractEquation createZeroEquation(Flattener f) {
989            return new FEquation(createFAccessExp(), createZeroExp(f));
990        }
991
992        /**
993         * Create an access to the var of this entry and add or subtract it to/from e.
994         *
995         * Used to build up flow equations.
996         */
997        public FExp buildFlow(Flattener f, FExp e) {
998            return createFAccessExp().appendSum(f, e, outside, cd.type());
999        }
1000
1001        public int hashCode() {
1002            return toString().hashCode();
1003        }
1004
1005        /**
1006         * Create a new connection set entry referring to a specific cell of the variable this refers to.
1007         *
1008         * Assumes that the variable is an array and that <code>i</code> is suitable.
1009         */
1010        public ConnectionSetEntry specifyCell(Index i) {
1011            InstComponentDecl cell = cd.findCell(i, 0);
1012            return ConnectionSetEntry.create(cell, outside, cell.getFAccess(i));
1013        }
1014
1015        public boolean equals(Object o) {
1016            return o instanceof ConnectionSetEntry && toString().equals(o.toString());
1017        }
1018   
1019        public int compareTo(ConnectionSetEntry cse) {
1020            return toString().compareTo(cse.toString());
1021        }
1022
1023        /**
1024         * Create an access expression to the associated flow variable.
1025         *
1026         * Only valid for stream connection entries.
1027         */
1028        public FExp createFlowCommonAccessExp() {
1029            throw new UnsupportedOperationException("Only supported for stream connection sets");
1030        }
1031
1032        /**
1033         * Get the epsilon to use when calculating flows, based on the associated flow variable's nominal value.
1034         *
1035         * Only valid for stream connection entries.
1036         */
1037         public double flowNominal() {
1038             throw new UnsupportedOperationException("Only supported for stream connection sets");
1039        }
1040
1041         /**
1042          * Check if this entry should contribute when calculating streams.
1043          *
1044          * Only valid for stream connection entries.
1045          */
1046          public boolean contributesToStream() {
1047              throw new UnsupportedOperationException("Only supported for stream connection sets");
1048        }
1049
1050
1051        private static class FlowConnectionSetEntry extends ConnectionSetEntry {
1052
1053            public FlowConnectionSetEntry(InstComponentDecl cd, boolean outside, FAccess access) {
1054                super(cd, outside, access);
1055            }
1056
1057            public boolean isFlow() {
1058                return true;
1059            }
1060
1061        }
1062
1063        private static class StreamConnectionSetEntry extends ConnectionSetEntry {
1064
1065            private FExp flowExp;
1066            private double nominal = 1;
1067            private boolean contribute = true;
1068
1069            public StreamConnectionSetEntry(InstComponentDecl cd, boolean outside, FAccess access) {
1070                super(cd, outside, access);
1071                InstComponentDecl flow = cd.myFlowVar();
1072                flowExp = new FAccessExp(flow.getFAccess().treeCopy());
1073                try {
1074                    CValue cval = flow.nominalAttributeCValue();
1075                    if (cval.hasRealValue()) {
1076                        nominal = Math.abs(cval.realValue());
1077                    }
1078                } catch (ConstantEvaluationException e) {}
1079                try {
1080                    CValue cval = isOutside() ? flow.maxAttributeCValue() : flow.minAttributeCValue();
1081                    if (cval.hasRealValue()) {
1082                        double val = cval.realValue();
1083                        contribute = isOutside() ? (val > 0) : (val < 0);
1084                    }
1085                } catch (ConstantEvaluationException e) {}
1086            }
1087
1088            public boolean isStream() {
1089                return true;
1090            }
1091
1092            public FExp createFlowCommonAccessExp() {
1093                return flowExp.fullCopy();
1094            }
1095
1096            public double flowNominal() {
1097                return nominal;
1098            }
1099
1100            public boolean contributesToStream() {
1101                return contribute;
1102            }
1103
1104        }
1105
1106    }
1107
1108
1109    /**
1110     * Connect the variables referenced in a connect or branch statement in the connection set manager.
1111     *
1112     * @param right   the other access
1113     * @param prefix  the prefix to use when flattening names
1114     * @param csm     the connection set manager to add variables to
1115     * @param source  the originating connect or branch statement
1116     */
1117    public void InstAccess.connectTo(InstAccess right, FAccess prefix, ConnectionSetManager csm, ConnectionEdge source) {
1118       
1119        boolean leftOutside  = isOutsideConnector();
1120        boolean rightOutside = right.isOutsideConnector();
1121        InstComponentDecl leftComp  = lookupEvaluatingIndices();
1122        InstComponentDecl rightComp = right.lookupEvaluatingIndices();
1123       
1124        boolean handleExpandable = !csm.isExpandableConnectorsDone();
1125        if (handleExpandable && (isExpandableConnectorPart() || right.isExpandableConnectorPart())) {
1126            if (isExpandableConnectorPart())
1127                csm.getExpandable().addIntroducingConnection(this, right, prefix, source);
1128            if (right.isExpandableConnectorPart())
1129                csm.getExpandable().addIntroducingConnection(right, this, prefix, source);
1130        } else if (handleExpandable && leftComp.isExpandableConnector()) {
1131            csm.getExpandable().addSpanningConnection(leftComp, rightComp, prefix, source);
1132        } else if (isArray() && !leftComp.isOverconstrainedConnection(rightComp)) {
1133            Indices leftInd = indices();
1134            Indices rightInd = right.indices();
1135            Iterator<Index> leftIter = leftInd.iterator();
1136            Iterator<Index> rightIter = rightInd.iterator();
1137            while (leftIter.hasNext() && rightIter.hasNext()) {
1138                Index leftI = leftInd.translate(leftIter.next());
1139                Index rightI = rightInd.translate(rightIter.next());
1140                InstComponentDecl leftCompCell = leftComp.findCell(leftI, leftI.ndims() - leftComp.ndims());
1141                InstComponentDecl rightCompCell = rightComp.findCell(rightI, rightI.ndims() - rightComp.ndims());
1142                FAccess leftCellName  = leftCompCell.getFAccess(leftI);
1143                FAccess rightCellName = rightCompCell.getFAccess(rightI);
1144                ConnectionSetEntry leftEntry = ConnectionSetEntry.create(leftCompCell, leftOutside, leftCellName);
1145                ConnectionSetEntry rightEntry = ConnectionSetEntry.create(rightCompCell, rightOutside, rightCellName);
1146                leftCompCell.connectTo(leftEntry, rightEntry, prefix, csm, source, true);
1147            }
1148        } else {
1149            FAccess leftName  = leftComp.getFAccess(index());
1150            FAccess rightName = rightComp.getFAccess(right.index());
1151            ConnectionSetEntry leftEntry = ConnectionSetEntry.create(leftComp, leftOutside, leftName);
1152            ConnectionSetEntry rightEntry = ConnectionSetEntry.create(rightComp, rightOutside, rightName);
1153            leftComp.connectTo(leftEntry, rightEntry, prefix, csm, source, true);
1154        }
1155    }
1156
1157    /**
1158     * Helper method for constructing FAccesss for connection sets
1159     */
1160    public FAccess InstComponentDecl.getFAccess(Index i) {
1161        FAccess res = getFAccess();
1162        if (isArray()) {
1163            if (i.ndims() > 0) {
1164                res = res.copyAndAddFas(i.subIndex(i.ndims() - ndims()).createFArraySubscripts());
1165            } else {
1166                res = res.copyAndAddFas(expandedSubscripts());
1167            }
1168        }
1169        return res;
1170    }
1171   
1172    syn Index InstAccess.index() = hasFArraySubscripts() ?
1173            getFArraySubscripts().createIndex() :
1174            Index.NULL;
1175
1176    /**
1177     * Connect the variables referenced in a connect or branch statement in the connection set manager.
1178     *
1179     * @param left        describes current part of the left access
1180     * @param right       describes current part of the right access
1181     * @param prefix      the prefix to use when flattening names
1182     * @param csm         the connection set manager to add variables to
1183     * @param source      the originating connect or branch statement
1184     * @param buildGraph  if true, pass instances of overconstrained types to overconstrained connection graph
1185     */
1186    public void InstComponentDecl.connectTo(ConnectionSetEntry left, ConnectionSetEntry right, 
1187            FAccess prefix, ConnectionSetManager csm, ConnectionEdge source, boolean buildGraph) {
1188        if (!isDisabled() && !right.getVar().isDisabled()) {
1189            if (buildGraph && isOverconstrainedConnection(right.getVar())) {
1190                csm.getGraph().addConnection(left, right, prefix, source);
1191            } else if (isArray()) {
1192                for (Index i : indices()) {
1193                    ConnectionSetEntry leftEntry = left.specifyCell(i);
1194                    ConnectionSetEntry rightEntry = right.specifyCell(i);
1195                    leftEntry.getVar().connectTo(leftEntry, rightEntry, prefix, csm, source, buildGraph);
1196                }
1197            } else if (isOperatorRecord()) {
1198                connectToAsPrimitive(left, right, prefix, csm, source, buildGraph);
1199            } else {
1200                SortedSet<InstComponentDecl> rightChildren = right.getVar().containedInstComponents();
1201                for (InstComponentDecl leftComp : containedInstComponents()) {
1202                    SortedSet<InstComponentDecl> rightTail = rightChildren.tailSet(leftComp);
1203                    if (rightTail.size() > 0) {
1204                        InstComponentDecl rightComp = rightTail.first();
1205                        FAccess leftName  = leftComp.getFAccess(Index.NULL);
1206                        FAccess rightName = rightComp.getFAccess(Index.NULL);
1207                        ConnectionSetEntry leftEntry = ConnectionSetEntry.create(leftComp, left.isOutside(), leftName);
1208                        ConnectionSetEntry rightEntry = ConnectionSetEntry.create(rightComp, right.isOutside(), rightName);
1209                        leftComp.connectTo(leftEntry, rightEntry, prefix, csm, source, buildGraph);
1210                    }
1211                }
1212            }
1213            csm.countCardinality(left.getFAccess().scalarName());
1214            csm.countCardinality(right.getFAccess().scalarName());
1215        }
1216    }
1217
1218    public void InstPrimitive.connectTo(ConnectionSetEntry left, ConnectionSetEntry right, 
1219            FAccess prefix, ConnectionSetManager csm, ConnectionEdge source, boolean buildGraph) {
1220        if (!isDisabled() && !right.getVar().isDisabled())
1221            connectToAsPrimitive(left, right, prefix, csm, source, buildGraph);
1222    }
1223
1224    public void InstComponentDecl.connectToAsPrimitive(ConnectionSetEntry left, ConnectionSetEntry right, 
1225            FAccess prefix, ConnectionSetManager csm, ConnectionEdge source, boolean buildGraph) {
1226        if (left.equals(right)) {
1227            source.warning("Ignored connection from connector to itself");
1228        } else {
1229            if (buildGraph && isOverconstrainedConnection(right.getVar())) {
1230                csm.getGraph().addConnection(left, right, prefix, source);
1231            } else {
1232                // In the special case of members of expandable connectors, we need each cell separately.
1233                if (isArray() && (isExpandableConnectorMember() || right.getVar().isExpandableConnectorMember()) && 
1234                        !left.getFAccess().hasScalarSubscripts()) 
1235                    for (Index i : indices())
1236                        csm.addVars(left.specifyCell(i), right.specifyCell(i));
1237                else
1238                    csm.addVars(left, right);
1239            }
1240            csm.countCardinality(left.getFAccess().scalarName());
1241            csm.countCardinality(right.getFAccess().scalarName());
1242        }
1243    }
1244
1245    syn boolean FAccess.hasScalarSubscripts() {
1246        FArraySubscripts fas = getFArraySubscripts();
1247        if (fas == null)
1248            return false;
1249        for (Subscript s : fas.subscripts())
1250            if (s.ndims() > 0)
1251                return false;
1252        return true;
1253    }
1254
1255    syn boolean InstComponentDecl.isOverconstrainedConnection(InstComponentDecl right) =
1256            isOverconstrainedType() && right.isOverconstrainedType();
1257
1258    /**
1259     * Retrieve the specific cell for a given index from an array component.
1260     *
1261     * What dimension of i that refers to the children of this component is given by j.
1262     */
1263    syn InstComponentDecl InstComponentDecl.findCell(Index i, int j) =
1264         ((j > 0) ? findCellUp(i, j, this) : this).findCellDown(i, j);
1265
1266    inh InstComponentDecl InstComponentDecl.findCellUp(Index i, int j, InstComponentDecl src);
1267    eq InstComponentDecl.getChild().findCellUp(Index i, int j, InstComponentDecl src) {
1268        if (j > 0) {
1269            if (isArray()) {
1270                return findCellUp(i, j - 1, this).getInstComponentDecl(i.get(j - 1) - 1);
1271            } else {
1272                return findCellUp(i, j, this).memberInstComponent(src.name()).target();
1273            }
1274        }
1275        return src;
1276    }
1277    eq InstClassDecl.getChild().findCellUp(Index i, int j, InstComponentDecl src) = src;
1278    eq Root.getChild().findCellUp(Index i, int j, InstComponentDecl src) {
1279        throw new UnsupportedOperationException();
1280    }
1281
1282    syn InstComponentDecl InstComponentDecl.findCellDown(Index i, int j) = 
1283        (j < i.ndims()) ? getInstComponentDecl(i.get(j) - 1).findCellDown(i, j + 1) : this;
1284    eq InstPrimitive.findCellDown(Index i, int j)                        = this;
1285
1286        syn boolean InstAccess.isOutsideConnector() = getFirstInstAccess().myInstComponentDecl().isConnector();
1287       
1288       
1289        /**
1290         * Traverse tree and build connection sets and overconstranined connection graph from relevant constructs.
1291         *
1292         * @param prefix   the prefix to use when flattening names
1293         * @param csm      the connection set manager to add variables to
1294         * @param connect  if false, then we are in a dead branch, and shouldn't change sets or graph
1295         */
1296        public void ASTNode.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1297                for (ASTNode n : this) 
1298                        n.buildConnectionSets(prefix, csm, connect);
1299        }
1300       
1301        public void InstNode.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1302                prefix = getFAccess();
1303                for (FAbstractEquation ae : getFAbstractEquations()) 
1304                   ae.buildConnectionSets(prefix, csm, connect);
1305                getInstComponentDeclList().buildConnectionSets(prefix, csm, connect);
1306                getInstExtendsList().buildConnectionSets(prefix, csm, connect);
1307        }
1308
1309    public void InstClassDecl.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1310        super.buildConnectionSets(prefix, csm, connect);
1311        getInstGeneratedInners().buildConnectionSets(prefix, csm, connect);
1312    }
1313
1314        public void InstComponentDecl.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1315                if (useInFlattening()) 
1316                        super.buildConnectionSets(prefix, csm, connect);
1317        }
1318
1319    public void InstAssignable.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1320        if (useInFlattening() && hasBindingFExp())
1321            getBindingFExp().buildConnectionSets(prefix, csm, connect);
1322        if (isOperatorRecord())
1323            buildConnectionSetsAsPrimitive(prefix, csm, connect);
1324        else
1325            super.buildConnectionSets(prefix, csm, connect);
1326    }
1327
1328    public void InstPrimitive.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1329        buildConnectionSetsAsPrimitive(prefix, csm, connect);
1330        super.buildConnectionSets(prefix, csm, connect);
1331    }
1332
1333    public void InstComponentDecl.buildConnectionSetsAsPrimitive(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1334        if (useInFlattening() && (isFlow() || isStream())) {
1335            FAccess access = getFAccess(Index.NULL);
1336            csm.addFlowVar(this, false, access);
1337            if (inExpandableConnector()) {
1338                csm.addFlowVar(this, true, access);
1339            }
1340        }
1341    }
1342
1343    public void FIfEquation.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1344        boolean all = true;
1345        boolean test = false;
1346        try {
1347            CValue cval = getTest().ceval();
1348            if (cval.hasBooleanValue()) {
1349                test = cval.booleanValue();
1350                all = false;
1351            }
1352        } catch (ConstantEvaluationException e) {
1353        }
1354        getTest().buildConnectionSets(prefix, csm, connect);
1355        for (FAbstractEquation equ : getFAbstractEquations())
1356            equ.buildConnectionSets(prefix, csm, (all || test) && connect);
1357        if (hasElse())
1358            getElse().buildConnectionSets(prefix, csm, (all || !test) && connect);
1359    }
1360
1361        public void InstForClauseE.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1362                Indices indices = Indices.create(getInstForIndexs());
1363                for (Index i : indices) {
1364                        int j = 0;
1365                        int[] ii = indices.translate(i).index();
1366                        for (InstForIndex fi : getInstForIndexs()) {
1367                                fi.getInstPrimitive().setLocalCachedEvaluationValue(new CValueInteger(ii[j]));
1368                                j++;
1369                        }
1370                        for (FAbstractEquation equ : getFAbstractEquations())
1371                            equ.buildConnectionSets(prefix, csm, connect);
1372                        getFAbstractEquations().flushAllRecursive();
1373                }
1374                for (InstForIndex fi : getInstForIndexs()) {
1375                        fi.getInstPrimitive().setLocalCachedEvaluationValue(null);
1376                        fi.buildConnectionSets(prefix, csm, connect);
1377                }
1378        }
1379
1380    public void FConnectClause.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1381        if (!isDisabled() && connect) {
1382            InstAccess left  = getConnector1();
1383            InstAccess right = getConnector2();
1384            left.connectTo(right, prefix, csm, this);
1385        }
1386    }
1387
1388    syn boolean FConnectClause.isDisabled() = getConnector1().isDisabled() || getConnector2().isDisabled();
1389
1390}
1391
1392aspect OverconstrainedConnections {
1393       
1394        public void FConnBranch.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1395                if (connect) {
1396            InstAccess left  = getA().asInstAccess();
1397            InstAccess right = getB().asInstAccess();
1398                        left.connectTo(right, prefix, csm, this);
1399                }
1400        }
1401       
1402        public void FConnRoot.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1403                if (connect)
1404                        csm.getGraph().addRoot(variableA());
1405        }
1406       
1407        public void FConnPotentialRoot.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1408                if (connect) {
1409                        int prio = hasPriority() ? getPriority().ceval().intValue() : 0;
1410                        csm.getGraph().addPotentialRoot(variableA(), prio);
1411                }
1412        }
1413       
1414        public void FConnBoolOp.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
1415                connectionGraph = csm.getGraph();
1416        }
1417       
1418        /**
1419         * Get the variable referenced by child A.
1420         *
1421     * Assumes A is an InstAccessExp.
1422         */
1423        syn InstComponentDecl FConnectionsOp.variableA() = 
1424            getA().asInstAccess().myInstComponentDecl();
1425       
1426       
1427        /**
1428         * Represents a broken branch in the overconstrained connection graph.
1429         *
1430         * Generates a call to the relevant equalityConstraint() function.
1431         */
1432        public class EqualityConstraintConnection {
1433               
1434                private ConnectionSetEntry cse1;
1435                private ConnectionSetEntry cse2;
1436                private FAccess prefix;
1437               
1438                public EqualityConstraintConnection(ConnectionSetEntry cse1, ConnectionSetEntry cse2, FAccess prefix) {
1439                        this.cse1 = cse1;
1440                        this.cse2 = cse2;
1441                        this.prefix = prefix;
1442                }
1443               
1444                /**
1445                 * Generate the equalityConstraint() call.
1446                 */
1447                public void generate(Flattener f) {
1448                        InstClassDecl func = cse1.getVar().equalityConstraint();
1449                        func.flattenFunction(f);
1450                        Size s = func.myOutputs().get(0).size();
1451                        FFunctionCall right = new FFunctionCall();
1452                        right.setName(new FAccessString(func.qualifiedName()));
1453            right.addArg(cse1.createFAccessExp());
1454            right.addArg(cse2.createFAccessExp());
1455            right.setFType(func.functionType().flatten(f, null, true));
1456                        FAbstractEquation eqn;
1457                        if (!s.isUnknown() && s.numElements() == 0) 
1458                                eqn = new FFunctionCallEquation(new List<FFunctionCallLeft>(), right);
1459                        else
1460                                eqn = new FEquation(s.createZeroFExp(), right);
1461                        f.addNormalEquation(eqn);
1462                }
1463               
1464        }
1465       
1466        /**
1467         * Check if an assignable is of an overconstrained connection type.
1468         */
1469        syn boolean InstComponentDecl.isOverconstrainedType() = equalityConstraint() != null;
1470       
1471        /**
1472         * Name of equality constraint function.
1473         */
1474        public static final String InstAssignable.EQUALITY_CONSTRAINT_NAME = "equalityConstraint";
1475
1476    /**
1477     * Get the equality constraint function for this type, if any.
1478     */
1479    syn InstClassDecl InstComponentDecl.equalityConstraint()  = null;
1480    syn lazy InstClassDecl InstAssignable.equalityConstraint() {
1481        InstLookupResult<InstClassDecl> res = memberInstClass(EQUALITY_CONSTRAINT_NAME);
1482        if (!res.successful())
1483            res = myInstClass().memberInstClass(EQUALITY_CONSTRAINT_NAME);
1484        if (res.successful()) {
1485            InstClassDecl icd = res.target().actualInstClass();
1486            if (icd.isEqualityConstraint(type()))
1487                return icd;
1488        }
1489        return null;
1490    }
1491
1492        /**
1493         * Check if this is an equality constraint function for the given type.
1494         */
1495        syn boolean InstClassDecl.isEqualityConstraint(FType type) {
1496                ArrayList<InstComponentDecl> inputs = myInputs();
1497                ArrayList<InstComponentDecl> outputs = myOutputs();
1498                return isFunction() && 
1499                                inputs.size() == 2 && 
1500                                inputs.get(0).type().typeCompatible(type, true) && 
1501                                inputs.get(1).type().typeCompatible(type, true) && 
1502                                outputs.size() == 1 && 
1503                                outputs.get(0).ndims() == 1 && 
1504                                outputs.get(0).type().isReal();
1505        }
1506       
1507        /**
1508         * The source node for an edge in the overconstrained connection graph.
1509         */
1510        public interface ConnectionEdge {
1511               
1512                /**
1513                 * Does this node represent a breakable branch in the overconstrained connection graph?
1514                 */
1515                public boolean isBreakable();
1516       
1517        /**
1518         * Report a warning.
1519         */
1520        public void warning(String msg);
1521       
1522        /**
1523         * Report an error.
1524         */
1525        public void error(String msg);
1526               
1527        }
1528       
1529        FConnectClause implements ConnectionEdge;
1530        FConnBranch implements ConnectionEdge;
1531       
1532        syn boolean FConnectClause.isBreakable() = true;
1533        syn boolean FConnBranch.isBreakable()    = false;
1534       
1535        /**
1536         * Describes a system of overconstrained connenctions.
1537         *
1538         * Will break loops in the graph to form spanning trees.
1539         */
1540        public class OverconstrainedConnectionGraph {
1541               
1542                /**
1543                 * The calculated values for operators that depend on the graph for a specific component.
1544                 */
1545                public static class OpsResult {
1546                       
1547                        /** The result for the Connections.isRoot() operator. */
1548                        public final boolean isRoot;
1549                        /** The result for the Connections.rooted() operator. */
1550                        public final boolean rooted;
1551                       
1552                        public OpsResult(boolean isRoot, boolean rooted) {
1553                                this.isRoot = isRoot;
1554                                this.rooted = rooted;
1555                        }
1556                       
1557                }
1558               
1559                private Collection<Edge> edges = new ArrayList<Edge>();
1560                private Collection<Node> nodes = new ArrayList<Node>();
1561                private Map<String,Node> nodeMap = new HashMap<String,Node>();
1562                private Map<String,OpsResult> opsMap;
1563                static String UNROOTED_ERR_MSG  = "Set of unrooted connectors in overconstrained connection graph:";
1564                static String MULTIROOT_ERR_MSG = "Multiple definite roots in unbreakable subgraph in overconstrained connection graph";
1565                static String LOOP_ERR_MSG      = "Unbreakable loop in overconstrained connection graph";
1566                static String MULTIDEF_ERR_MSG  = "Multiple root definitions for single connector in overconstrained connection graph";
1567                static String ERR_SEP = "\n    ";
1568               
1569                /**
1570                 * Add a connection to the graph.
1571                 *
1572                 * @param left    describes one end of connection
1573                 * @param right   describes other end of connection
1574                 * @param prefix  the prefix to use when flattening names
1575                 * @param source  the originating connect or branch statement
1576                 */
1577                public void addConnection(ConnectionSetEntry left, ConnectionSetEntry right, FAccess prefix, ConnectionEdge source) {
1578                        Node leftNode = getNode(left.name(), left.getVar());
1579                        Node rightNode = getNode(right.name(), right.getVar());
1580                        edges.add(new Edge(source, leftNode, rightNode, left, right, prefix));
1581                }
1582               
1583                /**
1584                 * Mark a component as a root in the graph.
1585                 */
1586                public void addRoot(InstComponentDecl var) {
1587                        getNode(var).setRoot();
1588                }
1589               
1590                /**
1591                 * Mark a component as a potential root with the given priority in the graph.
1592                 */
1593                public void addPotentialRoot(InstComponentDecl var, int priority) {
1594                        getNode(var).setPotentialRoot(priority);
1595                }
1596
1597        /**
1598         * Convert graph to a set of spanning trees.
1599         *
1600         * See Modelica Language Specification 3.3, section 9.4.2.
1601         */
1602        public void buildTrees(ConnectionSetManager csm) {
1603            breakPureConnectLoops();
1604            Queue<Node> roots = collectRoots();
1605            java.util.List<Node> selected = new ArrayList<Node>(roots.size());
1606            if (selectRoots(roots, selected))
1607                return;
1608           
1609            buildTrees(csm, selected);
1610            unrootedErrors();
1611            buildOpsMap();
1612        }
1613
1614        /**
1615         * Break loops comprised entirely of connect edges.
1616         */
1617        private void breakPureConnectLoops() {
1618            for (Node n : nodes) {
1619                n.breakPureConnectLoops();
1620            }
1621        }
1622
1623        /**
1624         * Collect roots sorted by priority.
1625         */
1626        private Queue<Node> collectRoots() {
1627            Queue<Node> roots = new PriorityQueue<Node>();
1628            for (Node n : nodes)
1629                if (n.canBeRoot())
1630                    roots.add(n);
1631            return roots;
1632        }
1633
1634        /**
1635         * Select a root for each unbreakable sub-graph, and generate any root-related errors.
1636         *
1637         * @param roots     list of roots sorted in priority order
1638         * @param selected  list where the selected roots will be added
1639         * @return  true if any errors were generated
1640         */
1641        private boolean selectRoots(Queue<Node> roots, java.util.List<Node> selected) {
1642            clearVisited();
1643            boolean foundError = false;
1644            java.util.List<Node> otherRoots = new ArrayList<Node>();
1645            for (Node root = roots.poll(); root != null; root = roots.poll()) {
1646                if (!root.visited) {
1647                    boolean loop = root.markSelectedRoot(root, otherRoots);
1648                    selected.add(root);
1649                   
1650                    // Generate errors for unbreakable loops.
1651                    if (loop) {
1652                        genError(LOOP_ERR_MSG, root, "Selected root: ");
1653                        foundError = true;
1654                    }
1655                    // Generate errors for multiple roots.
1656                    if (otherRoots.size() > 0) {
1657                        genError(MULTIROOT_ERR_MSG, root, "Selected root: ", otherRoots, "Other root: ");
1658                        otherRoots = new ArrayList<Node>();
1659                        foundError = true;
1660                    }
1661                }
1662            }
1663            return foundError;
1664        }
1665
1666        /**
1667         * Build spanning trees from graph, starting from selected roots.
1668         */
1669        private void buildTrees(ConnectionSetManager csm, java.util.List<Node> selected) {
1670            clearVisited();
1671            for (Node sel : selected) {
1672                if (!sel.visited) {
1673                    Queue<Node> work = new ArrayDeque<Node>();
1674                    work.add(sel);
1675                    sel.level = 0;
1676                    sel.visited = true;
1677                    for (Node n = work.poll(); n != null; n = work.poll()) {
1678                        n.buildTree(work, csm);
1679                    }
1680                }
1681            }
1682        }
1683
1684        /**
1685         * Generate errors for subgraphs without roots.
1686         */
1687        private void unrootedErrors() {
1688            for (Node n : nodes) {
1689                if (!n.visited) {
1690                    ArrayList<Node> subGraph = new ArrayList<Node>();
1691                    n.subGraph(subGraph);
1692                    genError(UNROOTED_ERR_MSG, n, subGraph, null);
1693                }
1694            }
1695        }
1696
1697        /**
1698         * Build lookup map for operators that are dependent on the result.
1699         */
1700        private void buildOpsMap() {
1701            opsMap = new HashMap<String,OpsResult>();
1702            for (Node n : nodes) 
1703                opsMap.put(n.flatName(), n.opsResult());
1704        }
1705
1706        /**
1707         * Clear visited flags.
1708         */
1709        private void clearVisited() {
1710            for (Node n : nodes)
1711                n.visited = false;
1712            for (Edge e : edges)
1713                e.visited = false;
1714        }
1715
1716        /**
1717         * Generate error message.
1718         */
1719        private void genError(String msg, Node node, 
1720                java.util.List<Node> nodeList, String listMsg) {
1721            genError(msg, node, null, nodeList, listMsg);
1722        }
1723
1724        /**
1725         * Generate error message.
1726         */
1727        private void genError(String msg, Node node, String nodeMsg) {
1728            genError(msg, node, nodeMsg, null, null);
1729        }
1730
1731        /**
1732         * Generate error message.
1733         */
1734        private void genError(String msg, Node node, String nodeMsg, 
1735                java.util.List<Node> nodeList, String listMsg) {
1736            StringBuilder errorMsg = new StringBuilder();
1737            errorMsg.append(msg);
1738            if (nodeMsg != null) {
1739                errorMsg.append(ERR_SEP);
1740                errorMsg.append(nodeMsg);
1741                errorMsg.append(node.flatName());
1742            }
1743            if (nodeList != null) {
1744                for (Node n : nodeList) {
1745                    errorMsg.append(ERR_SEP);
1746                    if (listMsg != null) {
1747                        errorMsg.append(listMsg);
1748                    }
1749                    errorMsg.append(n.flatName());
1750                }
1751            }
1752            node.var.error(errorMsg.toString());
1753        }
1754
1755                /**
1756                 * Check if conversion to spanning trees is finished.
1757                 */
1758                public boolean builtTreesDone() {
1759                        return opsMap != null;
1760                }
1761               
1762                /**
1763                 * Find the result for operators that depend on the graph for the given access.
1764                 */
1765                public OpsResult ops(FExp e) {
1766                        OpsResult res = null;
1767            CommonAccess u = e.asCommonAccess();
1768                        if (u.isInstAccess()) {
1769                InstComponentDecl var = u.asInstAccess().myInstComponentDecl();
1770                                Node n = getNode(var);
1771                                res = (n == null) ? null : n.opsResult();
1772                        } else {
1773                res = opsMap.get(u.name());
1774                        }
1775                        return (res == null) ? new OpsResult(false, false) : res;
1776                }
1777               
1778                /**
1779                 * Remove all references to the instance tree, and discard information that is no longer needed.
1780                 */
1781                public void disconnectFromInstanceTree() {
1782                        edges = null;
1783                        nodes = null;
1784                        nodeMap = null;
1785                }
1786
1787        /**
1788         * Find, or if neccessary create, the node for a given component.
1789         */
1790        private Node getNode(InstComponentDecl var) {
1791            return getNode(var.getFAccess().name(), var);
1792        }
1793
1794        /**
1795         * Find, or if neccessary create, the node for a given component accessed by the specified name.
1796         */
1797        private Node getNode(String name, InstComponentDecl var) {
1798            Node res = nodeMap.get(name);
1799            if (res == null) {
1800                res = new Node(var, name);
1801                nodeMap.put(name, res);
1802                nodes.add(res);
1803            }
1804            return res;
1805        }
1806
1807                /**
1808                 * An edge in the graph.
1809                 */
1810                private static class Edge {
1811                       
1812                        /** Visited flag used in traversals. */
1813                        public boolean visited;
1814
1815                        private ConnectionEdge source;
1816                        private Node n1;
1817                        private Node n2;
1818                        private ConnectionSetEntry cse1;
1819                        private ConnectionSetEntry cse2;
1820                        private FAccess prefix;
1821                        private boolean broken;
1822                        private boolean ignore = false;
1823                       
1824                        /**
1825                         * Create a new edge.
1826                         *
1827                         * The edge is undirected, use of "first" and "second" below is purely to tell them apart.
1828                         *
1829                         * @param source  the originating connect or branch statement
1830                         * @param n1      first end of the edge
1831                         * @param n2      second end of the edge
1832                         * @param cse1    describes first end of edge in terms of the connection set manager
1833                         * @param cse2    describes second end of edge in terms of the connection set manager
1834                         * @param prefix  the prefix to use when flattening names
1835                         */
1836                        public Edge(ConnectionEdge source, Node n1, Node n2, ConnectionSetEntry cse1, ConnectionSetEntry cse2, FAccess prefix) {
1837                                this.source = source;
1838                                this.n1 = n1;
1839                                this.n2 = n2;
1840                                n1.addEdge(this);
1841                                n2.addEdge(this);
1842                                broken = false;
1843                                if (isBreakable()) {
1844                                        this.cse1 = cse1;
1845                                        this.cse2 = cse2;
1846                                        this.prefix = prefix;
1847                                }
1848                        }
1849                       
1850                        /**
1851                         * Check if this is a breakable edge (a connect() statement).
1852                         */
1853                        public boolean isBreakable() {
1854                                return source.isBreakable();
1855                        }
1856           
1857            public String toString() {
1858                return n1 + " - " + n2;
1859            }
1860                       
1861                        /**
1862                         * Get the other end of the edge.
1863                         *
1864                         * Assumes that n is one of the ends.
1865                         */
1866                        public Node other(Node n) {
1867                                return (n == n1) ? n2 : n1;
1868                        }
1869                       
1870                        /**
1871                         * Checks if the given node is the edge corresponding to the left argument of a
1872                         * connect or branch.
1873                         */
1874                        public boolean isLeft(Node n) {
1875                                return n == n1;
1876                        }
1877                       
1878                        /**
1879                         * Break this edge.
1880                         *
1881                         * @throws UnsupportedOperationException  if the edge isn't breakable
1882                         */
1883                        public void breakEdge() {
1884                                if (!isBreakable())
1885                                        throw new UnsupportedOperationException();
1886                                broken = true;
1887                        }
1888                       
1889                        /**
1890                         * Check if edge is broken.
1891                         */
1892                        public boolean isBroken() {
1893                                return broken;
1894                        }
1895                       
1896                        /**
1897                         * Add edge to connection sets, either as normal connections or as equalityConstraint() calls.
1898                         */
1899                        public void connect(ConnectionSetManager csm) {
1900                                if (isBreakable()) {
1901                                        if (isBroken())
1902                                                csm.addEqualityConstraint(cse1, cse2, prefix);
1903                                        else
1904                                                cse1.getVar().connectTo(cse1, cse2, prefix, csm, source, false);
1905                                }
1906                        }
1907
1908            /**
1909             * Checks if this edge should be ignored during computations
1910             */
1911            public boolean isIgnored() {
1912                return ignore;
1913            }
1914
1915            /**
1916             * Marks this edge as ignored and will not be considered during computations
1917             */
1918            public void setAsIgnored() {
1919                ignore = true;
1920            }
1921
1922                }
1923
1924        /**
1925         * A node in the graph.
1926         */
1927        private static class Node implements Comparable<Node> {
1928
1929            /** Visited flag used in traversals. */
1930            public boolean visited = false;
1931            /** Depth of this node from root of tree. Depth 0 is the root. */
1932            public int level = -1;
1933
1934            private static final int DEFINITE_ROOT = -1;
1935            private static final int NOT_ROOT      = Integer.MAX_VALUE;
1936
1937            private InstComponentDecl var;
1938            private String name;
1939            private int rootPriority;
1940            private Collection<Edge> edges;
1941            private Node selectedRoot;
1942
1943            public Node(InstComponentDecl var, String name) {
1944                this.var = var;
1945                this.name = name;
1946                rootPriority = NOT_ROOT;
1947                edges = new ArrayList<Edge>();
1948                selectedRoot = null;
1949            }
1950
1951            public int compareTo(Node other) {
1952                return rootPriority - other.rootPriority;
1953            }
1954
1955            public String toString() {
1956                return name;
1957            }
1958
1959            public void addEdge(Edge e) {
1960                edges.add(e);
1961            }
1962
1963            /**
1964             * Check if this node is declared either root or potential root.
1965             */
1966            public boolean canBeRoot() {
1967                return rootPriority != NOT_ROOT;
1968            }
1969
1970            /**
1971             * Check if this node was selected as a root during conversion to spanning trees.
1972             *
1973             * Returns false before conversion.
1974             */
1975            public boolean isSelectedRoot() {
1976                return level == 0;
1977            }
1978
1979            /**
1980             * The flattened name of the node, as a String.
1981             */
1982            public String flatName() {
1983                return name;
1984            }
1985
1986            /**
1987             * Mark this node as a potential root with the given priority.
1988             */
1989            public void setPotentialRoot(int priority) {
1990                if (rootPriority < NOT_ROOT && rootPriority != priority) {
1991                    // Error - Multiple root definitions of a connector
1992                    StringBuilder errorMsg = new StringBuilder();
1993                    errorMsg.append(MULTIDEF_ERR_MSG);
1994                    errorMsg.append(ERR_SEP);
1995                    errorMsg.append("Connector: ");
1996                    errorMsg.append(this.flatName());
1997                    var.error(errorMsg.toString());
1998                }
1999                if (rootPriority > priority)
2000                    rootPriority = priority;
2001            }
2002
2003            /**
2004             * Mark this node as a definite root.
2005             */
2006            public void setRoot() {
2007                setPotentialRoot(DEFINITE_ROOT);
2008            }
2009
2010            /**
2011             * Calculate the results of isRoot() and rooted() when applied to this node.
2012             */
2013            public OpsResult opsResult() {
2014                Node other = null;
2015                for (Edge e : edges)
2016                    if (!e.isIgnored() && e.isLeft(this) && !e.isBreakable())
2017                        other = e.other(this);
2018                boolean rooted = other != null && level < other.level;
2019                return new OpsResult(isSelectedRoot(), rooted);
2020            }
2021
2022            /**
2023             * Traversal method for building the spanning trees.
2024             */
2025            public void buildTree(Queue<Node> work, ConnectionSetManager csm) {
2026                for (Edge e : edges) 
2027                    if (!e.isIgnored() && !e.visited) 
2028                        buildTreeVisit(e, work, csm);
2029            }
2030
2031            /**
2032             * Traversal method for decending into unbreakable subgraphs when building the spanning trees.
2033             */
2034            private void buildTreeForUnbreakable(Queue<Node> work, ConnectionSetManager csm) {
2035                for (Edge e : edges) 
2036                    if (!e.isIgnored() && !e.visited && !e.isBreakable()) 
2037                        buildTreeVisit(e, work, csm);
2038            }
2039
2040            /**
2041             * Visit a specific edge during traversal for building the spanning trees.
2042             */
2043            private void buildTreeVisit(Edge e, Queue<Node> work, ConnectionSetManager csm) {
2044                e.visited = true;
2045                Node n = e.other(this);
2046                if (n.visited) {
2047                    e.breakEdge();
2048                } else if (n.selectedRoot != selectedRoot) {
2049                    if (n.selectedRoot != null && n.selectedRoot.rootPriority == DEFINITE_ROOT)
2050                        e.breakEdge();
2051                    else
2052                        n.selectedRoot = selectedRoot;
2053                }
2054                n.visited = true;
2055                e.connect(csm);
2056                if (!e.isBroken()) {
2057                    n.level = level + 1;
2058                    work.add(n);
2059                    n.buildTreeForUnbreakable(work, csm);
2060                }
2061            }
2062
2063            /**
2064             * Find and break loops consisting only of connect edges.
2065             */
2066            public void breakPureConnectLoops() {
2067                if (!visited) {
2068                    visited = true;
2069                    for (Edge e : edges) {
2070                        if (!e.isIgnored() && !e.visited && e.isBreakable()) {
2071                            e.visited = true;
2072                            Node n = e.other(this);
2073                            if (n.visited) {
2074                                e.setAsIgnored();
2075                            } else {
2076                                n.breakPureConnectLoops();
2077                            }
2078                        }
2079                    }
2080                }
2081            }
2082
2083            /**
2084             * Set the selected root of this subgraph.
2085             * @param root        the selected root node
2086             * @param otherRoots  a collection to add any redundant roots to
2087             * @return true if unbreakable loop found
2088             */
2089            public boolean markSelectedRoot(Node root, Collection<Node> otherRoots) {
2090                boolean loop = false;
2091                visited = true;
2092                selectedRoot = root;
2093                if (root != this && rootPriority == DEFINITE_ROOT) {
2094                    // Error - more than one definite root in unbreakable subgraph
2095                    otherRoots.add(this);
2096                }
2097               
2098                for (Edge e : edges) {
2099                    if (!e.isIgnored() && !e.visited && !e.isBreakable()) {
2100                        e.visited = true;
2101                        Node n = e.other(this);
2102                        if (n.visited) {
2103                            // Error - unbreakable loop
2104                            loop = true;
2105                        }
2106                        loop = loop || n.markSelectedRoot(root, otherRoots);
2107                    }
2108                }
2109                return loop;
2110            }
2111
2112            /**
2113             * Gathers the subgraph which n is in.
2114             */
2115            public void subGraph(ArrayList<Node> sg) {
2116                if (visited)
2117                    return;
2118                visited = true;
2119                sg.add(this);
2120                for (Edge e : edges) {
2121                    if (!e.isIgnored()) {
2122                        e.other(this).subGraph(sg);
2123                    }
2124                }
2125            }
2126
2127        }
2128
2129    }
2130
2131}
2132
2133aspect ExpandableConnectors {
2134   
2135    public class ExpandableConnectorSets {
2136       
2137        private Map<InstComponentDecl,ExpandableSet> map;
2138        private Set<ExpandableSet> sets;
2139        private java.util.List<Connection> connections;
2140        private java.util.List<InstAccess> uses;
2141        private boolean expansionDone;
2142
2143       
2144        public ExpandableConnectorSets() {
2145            map = new LinkedHashMap<>();
2146            sets = new LinkedHashSet<>();
2147            connections = new ArrayList<>();
2148            uses = new ArrayList<>();
2149            expansionDone = false;
2150        }
2151       
2152        /**
2153         * Calculate the components present in each expandable connector and apply connections
2154         * involving them.
2155         */
2156        public void elaborate(ConnectionSetManager csm) {
2157            boolean ok = true;
2158            for (ExpandableSet set : sets()) 
2159                set.addNested();
2160            for (ExpandableSet set : sets()) 
2161                if (!set.expand())
2162                    ok = false;
2163           
2164            if (ok) {
2165                expansionDone = true;
2166                for (Connection conn : connections) {
2167                    conn.connect(csm);
2168                }
2169                for (InstComponentDecl icd : map.keySet()) {
2170                    icd.buildConnectionSets(null, csm, true);
2171                    icd.getInstComponentDecls().collectErrors(ErrorCheckType.GENERATED);
2172                }
2173                for (InstAccess use : uses) {
2174                    use.flushAllRecursiveClearFinal();
2175                    if (use.myInstComponentDecl().isUnknown()) 
2176                        use.error("Using member of expandable connector is only allowed if the member is connected to in the connection set");
2177                }
2178            }
2179        }
2180       
2181        /**
2182         * Add an expandable connector to sets.
2183         */
2184        public void addConnector(InstComponentDecl conn) {
2185            setFor(conn);
2186        }
2187       
2188        /**
2189         * Add a use of a member of an expandable connector.
2190         */
2191        public void addUse(InstAccess use) {
2192            uses.add(use);
2193        }
2194
2195        /**
2196         * Add information about a connection introducing a new component to an expandable connector.
2197         */
2198        public void addIntroducingConnection(InstAccess unknown, InstAccess known,
2199                FAccess prefix, ConnectionEdge source) {
2200            InstComponentDecl expandable = unknown.findReferencedExpandableConnector();
2201            InstAccess unknownPart = unknown.findExpandableMemberPart(expandable);
2202            ExpandableSet set = setFor(expandable);
2203            set.addMember(known, unknown, unknownPart);
2204           
2205            connections.add(new Connection(prefix, source));
2206            // TODO: support adding nestled expandable connectors? see Modelica:#428
2207       }
2208
2209        /**
2210         * Add information about a connection between two expandable connectors.
2211         */
2212        public void addSpanningConnection(InstComponentDecl leftComp, InstComponentDecl rightComp,
2213                FAccess prefix, ConnectionEdge source) {
2214            connectExpandableConnectors(leftComp, rightComp);
2215            connections.add(new Connection(prefix, source));
2216        }
2217
2218        /**
2219         * Check if elaboration is already done.
2220         */
2221        public boolean isExpansionDone() {
2222            return expansionDone;
2223        }
2224       
2225        private void connectExpandableConnectors(InstComponentDecl leftComp, InstComponentDecl rightComp) {
2226            ExpandableSet left = setFor(leftComp);
2227            ExpandableSet right = setFor(rightComp);
2228            left.merge(right);
2229           
2230            SortedSet<InstComponentDecl> rightChildren = rightComp.containedInstComponents();
2231            for (InstComponentDecl leftChild : leftComp.containedInstComponents()) {
2232                if (leftChild.isExpandableConnector() && rightChildren.contains(leftChild)) {
2233                    InstComponentDecl rightChild = rightChildren.tailSet(leftChild).first();
2234                    if (rightChild.isExpandableConnector())
2235                        connectExpandableConnectors(leftChild, rightChild);
2236                }
2237            }
2238        }
2239       
2240        /**
2241         * Add a new binding to the map and the list of sets to process.
2242         */
2243        private void bind(InstComponentDecl conn, ExpandableSet set) {
2244            map.put(conn, set);
2245            sets.add(set);
2246        }
2247       
2248        private ExpandableSet setFor(InstComponentDecl comp) {
2249            InstComponentDecl org = comp.duplicateOriginal();
2250            ExpandableSet set = map.get(org);
2251            if (set == null) {
2252                set = new ExpandableSet(org);
2253            }
2254            if (org != comp) {
2255                set.addConnector(comp);
2256            }
2257            return set;
2258        }
2259
2260        private Iterable<ExpandableSet> sets() {
2261            return new GrowableSetIterable(sets);
2262        }
2263
2264        private class Connection {
2265           
2266            public final FAccess prefix;
2267            public final FConnectClause source;
2268            private EvaluataionValueCache values;
2269           
2270            public Connection(FAccess prefix, ConnectionEdge source) {
2271                this.prefix = prefix;
2272                this.source = (FConnectClause) source;
2273                values = new EvaluataionValueCache(this.source);
2274            }
2275           
2276            public void connect(ConnectionSetManager csm) {
2277                source.flushAllRecursiveClearFinal(); // Depends on FConnectClause not resetting is$Final
2278                values.apply();
2279                source.buildConnectionSets(prefix, csm, true);
2280                values.reset();
2281            }
2282           
2283            public String toString() {
2284                return source.toString();
2285            }
2286           
2287        }
2288       
2289        private class ExpandableSet {
2290           
2291            private Set<InstComponentDecl> connectors;
2292            private Set<ExpandableSet> parentSets;
2293            private Map<String,ConnectorMember> members;
2294            private Set<ConnectorMember> owningMembers;
2295            private ExpandableSet replacement = null;
2296            private boolean expanded;
2297            private boolean nestedDone;
2298            private boolean isok;
2299           
2300            public ExpandableSet() {
2301                connectors = new LinkedHashSet<InstComponentDecl>();
2302                parentSets = new HashSet<ExpandableSet>();
2303                members = new HashMap<String,ConnectorMember>();
2304                owningMembers = new HashSet<ConnectorMember>();
2305                expanded = false;
2306                nestedDone = false;
2307                isok = true;
2308            }
2309           
2310            public ExpandableSet(InstComponentDecl connector) {
2311                this();
2312                addConnector(connector);
2313            }
2314           
2315            public ExpandableSet(ConnectorMember member) {
2316                this();
2317                owningMembers.add(member);
2318            }
2319           
2320            /**
2321             * Calculate the components present in each expandable connector, if not already done.
2322             *
2323             * @return  <code>false</code> if any errors were found
2324             */
2325            public boolean expand() {
2326                if (!expanded) {
2327                    expanded = true;
2328                    for (ExpandableSet parentSet : parentSets()) {
2329                        parentSet.expand();
2330                    }
2331                   
2332                    ConnectorMember[] sorted = members.values().toArray(new ConnectorMember[members.size()]);
2333                    Arrays.sort(sorted);
2334                    for (ConnectorMember member : sorted) {
2335                        isok = member.check() && isok;
2336                    }
2337                    if (isok) { 
2338                        for (InstComponentDecl conn : connectors()) {
2339                            expandConnector(conn, sorted);
2340                        }
2341                    }
2342                }
2343                return isok;
2344            }
2345
2346            private Iterable<InstComponentDecl> connectors() {
2347                return new GrowableSetIterable(connectors);
2348            }
2349
2350            /**
2351             * Add all nested expandable connector to their parent's sets.
2352             */
2353            public void addNested() {
2354                if (!nestedDone && !members.isEmpty()) {
2355                    nestedDone = true;
2356                    for (InstComponentDecl conn : connectors) {
2357                        InstComponentDecl p = conn.findExpandableAncestor();
2358                        if (p != null) {
2359                            InstComponentDecl c = conn.ancestorChild(p, conn);
2360                            ExpandableSet set = setFor(p);
2361                            set.addDeclared(c.name());
2362                            set.addNested();
2363                        }
2364                    }
2365                }
2366            }
2367           
2368            /**
2369             * Merge two sets of expandable connectors, keeping this one.
2370             */
2371            public void merge(ExpandableSet other) {
2372                if (other == null || other == this)
2373                    return;
2374               
2375                for (InstComponentDecl conn : other.connectors) {
2376                    addConnector(conn);
2377                }
2378                other.replaceWith(this);
2379               
2380                parentSets().addAll(other.parentSets());
2381               
2382                for (ConnectorMember member : other.members.values()) {
2383                    ConnectorMember local = members.get(member.name);
2384                    if (local == null) {
2385                        local = new ConnectorMember(member);
2386                        local.addParentSet(this);
2387                        members.put(local.name, local);
2388                    } else {
2389                        local.merge(member);
2390                    }
2391                    local.removeParentSet(other);
2392                }
2393               
2394                for (ConnectorMember member : other.owningMembers) {
2395                    owningMembers.add(member);
2396                    member.replaceSet(this);
2397                }
2398            }
2399           
2400            private ExpandableSet actual() {
2401                if (replacement == null) {
2402                    return this;
2403                } else {
2404                    replacement = replacement.actual();
2405                    return replacement;
2406                }
2407            }
2408           
2409            private void replaceWith(ExpandableSet other) {
2410                replacement = other.actual();
2411                // Don't process this set
2412                expanded = true;
2413                nestedDone = true;
2414            }
2415           
2416            /**
2417             * Add a member to the connectors of the set, possibly as expandable connector members
2418             * that members are recursively added to.
2419             */
2420            public void addMember(InstAccess source, InstAccess unknown, InstAccess unknownPart) {
2421                String name = unknownPart.name();
2422                ConnectorMember member = members.get(name);
2423                if (unknownPart == unknown.getLastInstAccess()) {
2424                    FArraySubscripts fas = unknownPart.hasFArraySubscripts() ? unknownPart.getFArraySubscripts() : null;
2425                    if (member == null) {
2426                        member = new ConnectorMember(name, source, fas);
2427                        members.put(name, member);
2428                    } else {
2429                        member.addConnection(source, fas);
2430                    }
2431                } else {
2432                    // TODO: probably need more to support arrays in this case
2433                    if (member == null) {
2434                        member = new ConnectorMember(name, unknown);
2435                        members.put(name, member);
2436                    }
2437                    member.set().addMember(source, unknown, unknownPart.getNextInstAccess());
2438                }
2439                member.addParentSet(actual());
2440            }
2441           
2442            /**
2443             * Add a declared member to the connectors of the set.
2444             *
2445             * This is only used to include nested expandable connectors.
2446             */
2447            public void addDeclared(String name) {
2448                ConnectorMember member = members.get(name);
2449                if (member == null) 
2450                    members.put(name, new ConnectorMember(name));
2451            }
2452           
2453            /**
2454             * Add a duplicate version of the expandable connector.
2455             *
2456             * All encountered duplicate versions are expanded.
2457             */
2458            public void addConnector(InstComponentDecl connector) {
2459                connectors.add(connector);
2460                bind(connector, this);
2461            }
2462           
2463            /**
2464             * Add another set as a parent to this one.
2465             *
2466             * All parent sets will be expanded before this one.
2467             */
2468            public void addParentSet(ExpandableSet parentSet) {
2469                parentSets.add(parentSet);
2470            }
2471
2472            /**
2473             * Remove another set from list of parents.
2474             */
2475            public void removeParentSet(ExpandableSet parentSet) {
2476                parentSets.remove(parentSet);
2477            }
2478           
2479            /**
2480             * Get the list of sets that need to be expanded before this one,
2481             * updated for replacements.
2482             */
2483            private Set<ExpandableSet> parentSets() {
2484                boolean replace = false;
2485                for (ExpandableSet parent : parentSets) {
2486                    replace |= parent.replacement != null;
2487                }
2488                if (replace) {
2489                    Set<ExpandableSet> replacement = new HashSet<>();
2490                    for (ExpandableSet parent : parentSets) {
2491                        replacement.add(parent.actual());
2492                    }
2493                    parentSets = replacement;
2494                }
2495                return parentSets;
2496            }
2497           
2498            /**
2499             * Create a list with the components each connector in this set should have.
2500             *
2501             * @param members  the members of this set, sorted by name
2502             */
2503            public void expandConnector(InstComponentDecl parent, ConnectorMember[] members) {
2504                List<InstComponentDecl> memberList = new List<InstComponentDecl>();
2505                memberList.setParent(parent); // Make sure inherited attributes work at once
2506                InstComponentDecl[] templates = new InstComponentDecl[members.length];
2507                int i = 0;
2508                for (ConnectorMember member : members) {
2509                    memberList.add(member.createInstComponent(parent));
2510                    templates[i++] = member.template(parent);
2511                }
2512                parent.expandConnector(memberList, templates);
2513            }
2514           
2515            /**
2516             * Check if the given expandable set describes a set of connectors that will contain
2517             * one of the connectors described by this set (directly or indirectly).
2518             */
2519            private boolean isOrAncestor(ExpandableSet other) {
2520                return other == this || isOrAncestorHelper(other, new HashSet<ExpandableSet>());
2521            }
2522           
2523            private boolean isOrAncestorHelper(ExpandableSet other, Set<ExpandableSet> visited) {
2524                /* If we have already been here, we either have a loop among the ancestors,
2525                 * or a diamond pattern in the graph. Neither case means that there is a loop
2526                 * involving other.
2527                 */
2528                if (visited.add(this)) {
2529                    for (ExpandableSet parent : parentSets) {
2530                        if (parent == other || parent.isOrAncestorHelper(other, visited)) {
2531                            return true;
2532                        }
2533                    }
2534                }
2535                return false;
2536            }
2537           
2538            public String toString() {
2539                StringBuilder buf = new StringBuilder("Connectors:");
2540                for (InstComponentDecl c : connectors) {
2541                    buf.append("\n  ");
2542                    buf.append(c.qualifiedName());
2543                }
2544                buf.append("\nMembers:");
2545                for (ConnectorMember m : members.values()) {
2546                    buf.append("\n  ");
2547                    buf.append(m.name);
2548                }
2549                return buf.toString();
2550            }
2551           
2552            private class ConnectorMember implements Comparable<ConnectorMember> {
2553               
2554                public final String name;
2555                private ExpandableSet set;
2556                private java.util.List<Source> sources;
2557                private java.util.List<InstAccess> nestledAccesses;
2558                private InstComponentDecl template;
2559                private Map<InstComponentDecl,InstComponentDecl> templateMap;
2560                private FArraySubscripts subscripts;
2561                private boolean error;
2562                private boolean calculated;
2563               
2564                public ConnectorMember(String name) {
2565                    this.name = name;
2566                    sources = new ArrayList<Source>(4);
2567                    nestledAccesses = new ArrayList<InstAccess>(2);
2568                    template = null;
2569                    templateMap = null;
2570                    subscripts = null;
2571                    calculated = false;
2572                    error = false;
2573                }
2574               
2575                public ConnectorMember(String name, InstAccess target, FArraySubscripts fas) {
2576                    this(name);
2577                    addConnection(target, fas);
2578                }
2579               
2580                public ConnectorMember(String name, InstAccess unknown) {
2581                    this(name);
2582                    nestledAccesses.add(unknown);
2583                }
2584               
2585                public ConnectorMember(ConnectorMember other) {
2586                    this(other.name);
2587                    merge(other);
2588                    nestledAccesses = other.nestledAccesses;
2589                }
2590               
2591                public int compareTo(ConnectorMember other) {
2592                    return name.compareTo(other.name);
2593                }
2594               
2595                public void addConnection(InstAccess target, FArraySubscripts fas) {
2596                    sources.add(new Source(target, fas));
2597                    InstComponentDecl targetVar = target.myInstComponentDecl();
2598                    ExpandableSet otherSet = map.get(targetVar);
2599                    if (otherSet == null && targetVar.isExpandableConnector()) {
2600                        if (set == null) {
2601                            set = setFor(targetVar);
2602                        } else {
2603                            set().addConnector(targetVar);
2604                        }
2605                    } else {
2606                        if (set == null) {
2607                            set = otherSet;
2608                        } else {
2609                            set().merge(otherSet);
2610                        }
2611                    }
2612                }
2613               
2614                public void merge(ConnectorMember other) {
2615                    if (set == null) {
2616                        set = other.set;
2617                    } else if (other.set != null) {
2618                        set().merge(other.set());
2619                    }
2620                    for (Source s : other.sources) {
2621                        sources.add(new Source(s));
2622                    }
2623                }
2624               
2625                public ExpandableSet set() {
2626                    if (set == null) {
2627                        set = new ExpandableSet(this);
2628                    }
2629                    set = set.actual();
2630                    return set;
2631                }
2632               
2633                public void replaceSet(ExpandableSet newSet) {
2634                    set = newSet;
2635                }
2636               
2637                public void addParentSet(ExpandableSet parentSet) {
2638                    if (set != null) {
2639                        set().addParentSet(parentSet);
2640                    }
2641                }
2642               
2643                public void removeParentSet(ExpandableSet parentSet) {
2644                    if (set != null) {
2645                        set().removeParentSet(parentSet);
2646                    }
2647                }
2648               
2649                /**
2650                 * Check if all connections to this member are consistent.
2651                 *
2652                 * @return  <code>false</code> if any errors were found
2653                 */
2654                public boolean check() {
2655                    if (!calculated)
2656                        calculateVariable();
2657                    return !error;
2658                }
2659               
2660                public InstComponentDecl createInstComponent(InstComponentDecl parent) {
2661                    InstComponentDecl tmpl = template(parent);
2662                    InstComponentDecl res;
2663                    if (tmpl.name().equals(name) && tmpl.isChildOf(parent)) {
2664                        if (subscripts() != null) {
2665                            tmpl.setLocalFArraySubscripts(subscripts());
2666                        }
2667                        res = tmpl;
2668                    } else {
2669                        SrcComponentDecl cd = tmpl.getSrcComponentDecl();
2670                        res = tmpl.myInstClass().newInstComponentDeclCopy(
2671                                name, subscripts(), cd, cd.getClassName());
2672                    }
2673                    if (tmpl.isExpandableConnector()) {
2674                        ExpandableSet set2 = setFor(tmpl);
2675                        set2.addConnector(res);
2676                        if (set != null) {
2677                            set().merge(set2);
2678                        }
2679                    }
2680                    return res;
2681                    // TODO: handle input/output (or is that really needed?)
2682                }
2683               
2684                private InstComponentDecl template(InstComponentDecl parent) {
2685                    if (!calculated) {
2686                        calculateVariable();
2687                    }
2688                    if (templateMap != null) {
2689                        InstComponentDecl declared = templateMap.get(parent);
2690                        if (declared != null) {
2691                            return declared;
2692                        }
2693                    }
2694                    return template;
2695                }
2696               
2697                private FArraySubscripts subscripts() {
2698                    if (!calculated)
2699                        calculateVariable();
2700                    return subscripts;
2701                }
2702               
2703                private void calculateVariable() {
2704                    error = false;
2705                   
2706                    // Check for recursive structure first.
2707                    if (set != null) {
2708                        for (Source src : sources) {
2709                            if (!src.checkRecursion(ExpandableSet.this)) {
2710                                error = true;
2711                            }
2712                        }
2713                        if (error) {
2714                            return;  // We risk infinite recursion if we continue
2715                        }
2716                    }
2717                   
2718                    // Collect instances of this member from declarations
2719                    ArrayList<InstComponentDecl> declared = new ArrayList<InstComponentDecl>();
2720                    Map<InstComponentDecl,InstComponentDecl> declaredMap = new HashMap<>();
2721                    for (InstComponentDecl conn : connectors) {
2722                        InstLookupResult<InstComponentDecl> res = conn.memberInstComponent(name);
2723                        if (res.successful()) {
2724                            InstComponentDecl decl = res.target();
2725                            declared.add(decl);
2726                            declaredMap.put(conn, decl);
2727                        }
2728                    }
2729                   
2730                    // Pick a variable to use as template
2731                    ASTNode templateErrorNode = null;
2732                    if (sources.isEmpty() && declared.isEmpty()) {
2733                        error = true;
2734                        for (InstAccess nestledAccess : nestledAccesses)
2735                            nestledAccess.compliance("Nested expandable connectors where some of the intermediate expandable connectors are neither connected to or declared are not supported");
2736                    } else if (declared.isEmpty()) {
2737                        template = sources.get(0).template();
2738                        templateErrorNode = sources.get(0).errorNode();
2739                    } else {
2740                        template = declared.get(0);
2741                        templateErrorNode = template;
2742                        templateMap = declaredMap;
2743                    }
2744                   
2745                    // Check types against template
2746                    if (template != null && !template.checkAsExpandableMemberTemplate(templateErrorNode))
2747                        error = true;
2748                    for (Source src : sources)
2749                        src.checkType(template);
2750                    for (InstComponentDecl decl : declared) {
2751                        if (!decl.connectableTypes(template)) {
2752                            error = true;
2753                            decl.error("Type of declared member of expandable connector does not match declarations in other expandable connectors in same connection set");
2754                        }
2755                    }
2756                   
2757                    // Calculate size
2758                    SummedSize ss = new SummedSize();
2759                    for (InstComponentDecl decl : declared)
2760                        ss.updateFromDeclaration(decl);
2761                    for (Source src : sources)
2762                        src.collectSize(ss);
2763                    Size s = ss.size;
2764                    if (template != null && template.ndims() > template.localNdims()) {
2765                        s = s.contract(0, template.ndims() - template.localNdims());
2766                    }
2767                   
2768                    // Create subscripts for size
2769                    if (s != Size.SCALAR && s != null) {
2770                        subscripts = s.createFArrayExpSubscripts();
2771                    }
2772                   
2773                    // Check against connections to non-existing members of non-expandable connectors
2774                    if (!sources.isEmpty() && set != null && set().connectors.isEmpty() && !set().members.isEmpty()) 
2775                        for (ConnectorMember m : set().members.values())
2776                            error = !m.checkNonExisting(template) || error;
2777                   
2778                    // Check array index types. If there is a declaration they are checked in FExpSubscript.typeCheckAsIndex;
2779                    if (declared.isEmpty()) {
2780                        Source tmpl = null;
2781                        for (Source src : sources) {
2782                            if (tmpl == null)
2783                                tmpl = src;
2784                            if (!src.checkIndexType(tmpl))
2785                                break;
2786                        }
2787                    }
2788                    calculated = true;
2789                }
2790               
2791                private boolean checkNonExisting(InstComponentDecl parentTemplate) {
2792                    boolean res = true;
2793                    InstComponentDecl myTemplate = (parentTemplate != null) ? parentTemplate.memberInstComponent(name).targetOrNull() : null;
2794                    if (myTemplate == null) {
2795                        for (Source s : sources)
2796                            s.errorNode().error("Can not connect to non-existing member of non-expandable connector in expandable connector");
2797                        res = false;
2798                    }
2799                    if (set != null) 
2800                        for (ConnectorMember m : set().members.values())
2801                            res = m.checkNonExisting(myTemplate) && res;
2802                    return res;
2803                }
2804               
2805                public String toString() {
2806                    return name + ": " + sources.toString();
2807                }
2808               
2809                private class Source {
2810                   
2811                    private InstAccess target;
2812                    private FArraySubscripts fas;
2813                    private boolean errorReported = false;
2814                   
2815                    private Size s;
2816                    private boolean[] fixed;
2817                   
2818                    public Source(InstAccess target, FArraySubscripts fas) {
2819                        this.target = target;
2820                        this.fas = fas;
2821                        calculateSize();
2822                    }
2823                   
2824                    public Source(Source other) {
2825                        this.target = other.target;
2826                        this.fas = other.fas;
2827                        this.s      = other.s;
2828                        this.fixed  = other.fixed;
2829                    }
2830                   
2831                    private void calculateSize() {
2832                        // Calculate local size
2833                        Size ts = target.size();
2834                        if (fas != null) {
2835                            MutableSize ms = new MutableSize(fas.ndims());
2836                            fixed = new boolean[ms.ndims()];
2837                            int tspos = 0;
2838                            for (int i = 0; i < fixed.length; i++) {
2839                                Subscript sub = fas.subscript(i);
2840                                int len = (tspos < ts.ndims()) ? ts.get(tspos) : 1;
2841                                if (!sub.calculateExpandableConnectorSize(ms, i, len)) 
2842                                    localError();
2843                                fixed[i] = sub.isColon();
2844                                if (sub.ndims() > 0) 
2845                                    tspos++;
2846                            }
2847                            if (tspos != ts.ndims())
2848                                localError();
2849                            s = ms;
2850                        } else {
2851                            s = (ts == Size.SCALAR) ? ts : ts.mutableClone();
2852                            fixed = new boolean[s.ndims()];
2853                            Arrays.fill(fixed, true);
2854                        }
2855                    }
2856                   
2857                    public void collectSize(SummedSize ss) {
2858                        if (errorReported)
2859                            return;
2860                       
2861                        // Merge with other sizes in set
2862                        if (!ss.merge(s, fixed))
2863                            mismatchError();
2864                   }
2865                   
2866                    public void checkType(InstComponentDecl template) {
2867                        if (!target.myInstComponentDecl().connectableTypes(template))
2868                            error("Type of component introduced to external connector does not match other connections to same name in connection set or component declared in connector");
2869                    }
2870                   
2871                    public boolean checkIndexType(Source other) {
2872                        if (fas == null || other.fas == null)
2873                            return true;
2874                        for (int dim = 0; dim < Math.min(fas.ndims(), other.fas.ndims()); dim++) {
2875                            if (!fas.subscript(dim).type().scalarType().typeCompatible(
2876                                    other.fas.subscript(dim).type().scalarType())) {
2877                                error("Array index type of component introduced to external connector does not match other"
2878                                        + " connections to same name in connection set");
2879                                return false;
2880                            }
2881                        }
2882                        return true;
2883                    }
2884                   
2885                    public boolean checkRecursion(ExpandableSet parent) {
2886                        ExpandableSet ancestor = map.get(target.myInstComponentDecl());
2887                        if (ancestor != null && parent.isOrAncestor(ancestor)) {
2888                            error("Connect introduces a copy of " + target + 
2889                                  " into a connector that is (possibly indirectly) connected back to " + target + 
2890                                  ". This creates an infinite recursive structure, and is not allowed:\n    " + 
2891                                  errorNode());
2892                            return false;
2893                        }
2894                        return true;
2895                    }
2896                   
2897                    private void mismatchError() {
2898                        error("Size introduced for external connector member does not match other connections to same name in connection set or component declared in connector");
2899                    }
2900                   
2901                    private void localError() {
2902                        error("Can not match size of connector to access introducing member in external connector");
2903                    }
2904                   
2905                    private void error(String err) {
2906                        error = true;
2907                        if (!errorReported) {
2908                            errorReported = true;
2909                            errorNode().error(err);
2910                        }
2911                    }
2912                   
2913                    public InstComponentDecl template() {
2914                        return target.myInstComponentElement();
2915                    }
2916                   
2917                    public ASTNode errorNode() {
2918                        return target.getParent();
2919                    }
2920                   
2921                    public String toString() {
2922                        return target.toString() + ((fas != null) ? (" (" + fas + ")") : "");
2923                    }
2924                   
2925                }
2926               
2927                private class SummedSize {
2928                   
2929                    public Size size = null;
2930                    public boolean[] fixed = null;
2931                   
2932                    public void updateFromDeclaration(InstComponentDecl icd) {
2933                        if (!mergeKnownFixed(icd.size())) {
2934                            error = true;
2935                            icd.error("Size of declared member of expandable connector does not match declarations in other expandable connectors in same connection set");
2936                        }
2937                    }
2938   
2939                    private boolean mergeKnownFixed(Size s) {
2940                        if (s != Size.SCALAR)
2941                            s = s.mutableClone();
2942                        boolean[] sFixed = new boolean[s.ndims()];
2943                        for (int i = 0; i < sFixed.length; i++)
2944                            sFixed[i] = s.hasValue(i);
2945                        return merge(s, sFixed);
2946                    }
2947   
2948                    public boolean merge(Size s, boolean[] sFixed) {
2949                        if (size == null) {
2950                            size = s;
2951                            fixed = sFixed;
2952                        } else if (sFixed.length != fixed.length) {
2953                            return false;
2954                        } else {
2955                            for (int i = 0; i < fixed.length; i++) {
2956                                int diff = s.get(i) - size.get(i);
2957                                if (sFixed[i]) {
2958                                    if (diff == 0) {
2959                                        fixed[i] = true;
2960                                    } else if (diff < 0) {
2961                                        return false;
2962                                    }
2963                                } 
2964                                if (diff > 0) {
2965                                    if (fixed[i]) {
2966                                        return false;
2967                                    } else {
2968                                        fixed[i] = sFixed[i];
2969                                        ((MutableSize) size).set(i, s, i);
2970                                    }
2971                                }
2972                            }
2973                        }
2974                        return true;
2975                    }
2976                   
2977                }
2978        }
2979
2980        }
2981       
2982    }
2983   
2984    public void InstComponentDecl.expandConnector(List<InstComponentDecl> members, InstComponentDecl[] templates) {
2985        throw new UnsupportedOperationException();
2986    }
2987   
2988    public void InstExpandableConnectorDecl.expandConnector(List<InstComponentDecl> members, InstComponentDecl[] templates) {
2989        expandedMembers = members;
2990        this.templates = templates;
2991        flushAll();
2992    }
2993   
2994    public void InstReplacingExpandableConnectorDecl.expandConnector(List<InstComponentDecl> members, InstComponentDecl[] templates) {
2995        expandedMembers = members;
2996        this.templates = templates;
2997        flushAll();
2998    }
2999   
3000    public void InstArrayExpandableConnector.expandConnector(List<InstComponentDecl> members, InstComponentDecl[] templates) {
3001        expandedMembers = members;
3002        this.templates = templates;
3003        flushAll();
3004    }
3005   
3006   
3007    /**
3008     * Find the closest ancestor component that is an expandable connector, if any.
3009     */
3010    inh InstComponentDecl InstComponentDecl.findExpandableAncestor();
3011    eq InstComponentDecl.getChild().findExpandableAncestor() = 
3012        (isExpandableConnector() && !isArray()) ? this : findExpandableAncestor();
3013    eq InstClassDecl.getChild().findExpandableAncestor()     = null;
3014    eq InstRoot.getChild().findExpandableAncestor()          = null;
3015    eq Root.getChild().findExpandableAncestor()              = null;
3016   
3017    /**
3018     * Find the ancestor component that is a direct child of the given component.
3019     *
3020     * @param a  the ancestor to find child of
3021     * @param c  the child being considered, always use <code>this</code>
3022     */
3023    inh InstComponentDecl InstComponentDecl.ancestorChild(InstComponentDecl a, InstComponentDecl c);
3024    eq InstComponentDecl.getChild().ancestorChild(InstComponentDecl a, InstComponentDecl c) = 
3025        (this == a) ? c : ancestorChild(a, this);
3026    eq InstClassDecl.getChild().ancestorChild(InstComponentDecl a, InstComponentDecl c)     = null;
3027    eq InstRoot.getChild().ancestorChild(InstComponentDecl a, InstComponentDecl c)          = null;
3028    eq Root.getChild().ancestorChild(InstComponentDecl a, InstComponentDecl c)              = null;
3029   
3030
3031    private static final BinaryOperation<CValue> FExpSubscript.INT_MAX_OP = new BinaryOperation<CValue>() {
3032        public CValue op(CValue a, CValue b) { 
3033            return (a.intValue() >= b.intValue()) ? a : b;
3034        }
3035    };
3036   
3037    public abstract boolean FSubscript.calculateExpandableConnectorSize(MutableSize s, int i, int len);
3038   
3039    public boolean FExpSubscript.calculateExpandableConnectorSize(MutableSize s, int i, int len) {
3040        CValue val = ceval();
3041        if (ndims() > 0) 
3042            val = val.reduce(INT_MAX_OP, new CValueInteger(1));
3043        s.set(i, val.intValue());
3044        return ndims() == 0 || size().get(0) == len;
3045    }
3046   
3047    public boolean FIntegerSubscript.calculateExpandableConnectorSize(MutableSize s, int i, int len) {
3048        s.set(i, getValue());
3049        return true;
3050    }
3051   
3052    public boolean IntegerSubscript.calculateExpandableConnectorSize(MutableSize s, int i, int len) {
3053        s.set(i, value);
3054        return true;
3055    }
3056   
3057    public boolean FColonSubscript.calculateExpandableConnectorSize(MutableSize s, int i, int len) {
3058        s.set(i, len);
3059        return true;
3060    }
3061   
3062    /**
3063     * Check that this component would be allowed as a member of an expandable connector.
3064     *
3065     * @param errorNode  node to report any errors found on
3066     * @return  <code>true</code> if the component is OK
3067     */
3068    public boolean InstComponentDecl.checkAsExpandableMemberTemplate(ASTNode errorNode) {
3069        for (InstComponentDecl child : getInstComponentDecls()) {
3070            if (child.isExpandableConnector()) {
3071                errorNode.compliance("Expandable connectors containing a non-expandable connector component, that in turn contains an expandable connector, is not supported");
3072                return false;
3073            } else if (!child.checkAsExpandableMemberTemplate(errorNode))
3074                return false;
3075        }
3076        return true;
3077    }
3078   
3079    public boolean InstExpandableConnectorDecl.checkAsExpandableMemberTemplate(ASTNode errorNode) {
3080        return true;
3081    }
3082   
3083    public boolean InstReplacingExpandableConnectorDecl.checkAsExpandableMemberTemplate(ASTNode errorNode) {
3084        return true;
3085    }
3086   
3087    public void InstAccess.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
3088        if (isExpandableConnectorPart())
3089            csm.getExpandable().addUse(this);
3090    }
3091   
3092    public void InstExpandableConnectorDecl.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
3093        buildConnectionSetsForExpandableConnector(prefix, csm, connect);
3094    }
3095   
3096    public void InstReplacingExpandableConnectorDecl.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
3097        buildConnectionSetsForExpandableConnector(prefix, csm, connect);
3098    }
3099   
3100    public void InstArrayExpandableConnector.buildConnectionSets(FAccess prefix, ConnectionSetManager csm, boolean connect) {
3101        buildConnectionSetsForExpandableConnector(prefix, csm, connect);
3102    }
3103   
3104    public void InstComponentDecl.buildConnectionSetsForExpandableConnector(FAccess prefix, ConnectionSetManager csm, boolean connect) {
3105        if (useInFlattening()) {
3106            if (!isArray()) {
3107                if (csm.isExpandableConnectorsDone()) {
3108                    for (InstComponentDecl icd : getInstComponentDecls()) {
3109                        if (icd.isArray()) {
3110                            icd.addExpandableArrayMembersTopLevel(csm);
3111                        }
3112                    }
3113                } else {
3114                    csm.getExpandable().addConnector(this);
3115                }
3116            }
3117            super.buildConnectionSets(prefix, csm, connect);
3118        }
3119    }
3120   
3121    /**
3122     * Add all variables to connection set manager, to get "= 0" equations if they are not connected.
3123     */
3124    public void InstComponentDecl.addExpandableArrayMembersTopLevel(ConnectionSetManager csm) {
3125        for (InstComponentDecl icd : allInstComponentDecls()) {
3126            icd.addExpandableArrayMembers(csm);
3127        }
3128    }
3129   
3130    public void InstPrimitive.addExpandableArrayMembersTopLevel(ConnectionSetManager csm) {
3131        if (!variability().parameterOrLess()) {
3132            for (Index i : indices()) {
3133                csm.addExpandableArrayMember(this, getFAccess(i));
3134            }
3135        }
3136    }
3137   
3138    /**
3139     * Add all variables to connection set manager, to get "= 0" equations if they are not connected.
3140     */
3141    public void InstComponentDecl.addExpandableArrayMembers(ConnectionSetManager csm) {
3142        for (InstComponentDecl icd : allInstComponentDecls()) {
3143            icd.addExpandableArrayMembers(csm);
3144        }
3145    }
3146   
3147    public void InstPrimitive.addExpandableArrayMembers(ConnectionSetManager csm) {
3148        if (!variability().parameterOrLess()) {
3149            csm.addExpandableArrayMember(this, getFAccess(Index.NULL));
3150        }
3151    }
3152   
3153   
3154    syn boolean InstAccess.isExpandableConnectorPart() = false;
3155    eq InstDot.isExpandableConnectorPart() {
3156        InstComponentDecl conn = findReferencedExpandableConnector();
3157        return conn != null && conn != getLastInstAccess().findReferencedExpandableConnector();
3158    }
3159   
3160    syn InstComponentDecl InstAccess.findReferencedExpandableConnector() = null;
3161    eq InstComponentAccess.findReferencedExpandableConnector() {
3162        InstComponentDecl icd = myInstComponentDecl();
3163        return icd.isExpandableConnector() ? icd : null;
3164    }
3165    eq InstComponentArrayAccess.findReferencedExpandableConnector() {
3166        InstComponentDecl icd = lookupArrayElement(myInstComponentDecl());
3167        return (icd != null && icd.isExpandableConnector()) ? icd : null;
3168    }
3169    eq InstDot.findReferencedExpandableConnector() {
3170        for (int i = getNumInstAccess() - 1; i >= 0; i--) {
3171            InstComponentDecl conn = getInstAccess(i).findReferencedExpandableConnector();
3172            if (conn != null)
3173                return conn;
3174        }
3175        return null;
3176    }
3177   
3178    syn InstAccess InstAccess.findExpandableMemberPart(InstComponentDecl expandable) = null;
3179    eq InstDot.findExpandableMemberPart(InstComponentDecl expandable) {
3180        for (int i = getNumInstAccess() - 2; i >= 0; i--) 
3181            if (getInstAccess(i).findReferencedExpandableConnector() != null)
3182                return getInstAccess(i + 1);
3183        return null;
3184    }
3185   
3186    // We need to make sure lookups work properly after flush
3187    public void InstAmbiguousAccess.flushAllRecursiveClearFinal() {
3188        super.flushAllRecursiveClearFinal();
3189        rewritten = false;
3190        is$Final = false;
3191    }
3192
3193    public void InstAmbiguousArrayAccess.flushAllRecursiveClearFinal() {
3194        super.flushAllRecursiveClearFinal();
3195        rewritten = false;
3196        is$Final = false;
3197    }
3198
3199}
3200
3201aspect Cardinality {
3202   
3203    private Map<String,Enumerator> ConnectionSetManager.cardinality = null;
3204   
3205    public void ConnectionSetManager.countCardinality(String name) {
3206        if (cardinality == null)
3207            cardinality = new HashMap<String,Enumerator>();
3208        Enumerator e = cardinality.get(name);
3209        if (e == null)
3210            cardinality.put(name, new Enumerator(1));
3211        else
3212            e.next();
3213    }
3214   
3215    public int ConnectionSetManager.getCardinality(String name) {
3216        if (cardinality == null)
3217            return 0;
3218        Enumerator e = cardinality.get(name);
3219        return (e == null) ? 0 : e.peek();
3220    }
3221
3222    // Can only be calculated after flattening - give dummy value before that to prevent error messages about structural parameters for cardinality()
3223    syn int FExp.cardinalityValue() {
3224        throw new UnsupportedOperationException();
3225    }
3226    eq InstAccessExp.cardinalityValue() = 1;
3227    eq FAccessExp.cardinalityValue()    = getFAccess().cardinalityValue();
3228
3229    syn int FAccess.cardinalityValue() = myFClass().getConnectionSetManager().getCardinality(scalarName());
3230
3231}
Note: See TracBrowser for help on using the repository browser.