private Context joinDescendants(Context ancestor, Context[] descendants) {
		// Santity check parameters, as they maybe null. This happens in case of
		// branches which terminate (e.g. via return or break).
		descendants = removeNull(descendants);
		if (descendants.length == 0) {
			// In this case, the are actually no active descendants. Hence, the
			// resulting context is null to indicate no branches escape this
			// meet point.
			return null;
		} else if (descendants.length == 1) {
			// If there's only one, then we don't need to join it.
			return descendants[0];
		}
		//
		LocalEnvironment joinedEnvironment = joinEnvironments(descendants);
		//
		AssumptionSet[] descendentAssumptions = new AssumptionSet[descendants.length];
		for (int i = 0; i != descendants.length; ++i) {
			Context ithContext = descendants[i];
			LocalEnvironment ithEnvironment = ithContext.environment;
			AssumptionSet ithAssumptions = ithContext.assumptions;
			descendentAssumptions[i] = updateVariableVersions(ithAssumptions, ithEnvironment, joinedEnvironment);
		}
		//
		AssumptionSet joinedAssumptions = ancestor.assumptions.joinDescendants(descendentAssumptions);
		//
		return new Context(ancestor.wyalFile, joinedAssumptions, joinedEnvironment, ancestor.initialEnvironment,
				ancestor.enclosingLoop, ancestor.verificationConditions);
	}
private Context joinDescendants ( Context ancestor , Context [ ] descendants ) { descendants = removeNull ( descendants ) ; if ( descendants . length == 0 ) { return null ; } else if ( descendants . length == 1 ) { return descendants [ 0 ] ; } LocalEnvironment joinedEnvironment = joinEnvironments ( descendants ) ; AssumptionSet [ ] descendentAssumptions = new AssumptionSet [ descendants . length ] ; for ( int i = 0 ; i != descendants . length ; ++ i ) { Context ithContext = descendants [ i ] ; LocalEnvironment ithEnvironment = ithContext . environment ; AssumptionSet ithAssumptions = ithContext . assumptions ; descendentAssumptions [ i ] = updateVariableVersions ( ithAssumptions , ithEnvironment , joinedEnvironment ) ; } AssumptionSet joinedAssumptions = ancestor . assumptions . joinDescendants ( descendentAssumptions ) ; return new Context ( ancestor . wyalFile , joinedAssumptions , joinedEnvironment , ancestor . initialEnvironment , ancestor . enclosingLoop , ancestor . verificationConditions ) ; }
Join one or more descendant context's together. To understand this, consider
the following snippet, annotated with context information:

<pre>
// Context: y >= 0
if x >= 0:
x = x + 1
// Context: y >= 0 && x >= 0 && x$1 == x + 1
else:
x = -x
// Context: y >= 0 && x < 0 && x$2 == x + 1
//
Context: ?
</pre>

At this point, we have two goals in combining the contextual information back
together. Firstly, we want to factor out the parts common to both (e.g.
<code>y >= 0</code> above). Secondly, we need to determine the appropriate
version for variables modified on one or both branches (e.g. <code>x</code>
above). Thus, the joined context for the above would be:

<pre>
y >= 0 && ((x >= 0 && x$1 == x + 1 && x$3 == x$1) || (x < 0 && x$2 == -x && x$3 == x$2))
</pre>

In the resulting environment, the current version of <code>x</code> would
then be <code>x$3</code>. To determine affected variables we simplify
identify any variable with a different version between at least two
context's.

@param ancestor
Distinguished context for join, which is an ancestor of those
context's being joined.
@param descendants
Descendant context's being joined. Again, these maybe null for
branches which terminate (e.g. via return).
@return