/*
   * Let the "tail formula" of the exit path be the path formula for all edges of the
   * exit path except the first. We find the set of all terms in the tail formula in which
   * all program variables appear with SSA index 1 (meaning this regards their state upon exit
   * of the loop, and prior to any subsequent modification), and we form a fresh-parameter
   * linear combination of these terms for the LHS of our template. The RHS is another
   * fresh parameter, and the relation is the one passed as argument.
   *
   * The idea is to try this method after the exitHeadNegationMethod. If that one failed,
   * then probably we didn't enter the error state simply because of the edge along which
   * we exited the loop; instead, it might have been because something which is actually
   * true of the program variables on loop exit was supposed not to be. For example, we might
   * have reached the error state because of a failed 'assert' statement, which simply asserts
   * something about the variables involved in the loop. Our hope is to build as invariant the
   * very statement that is asserted.
   */
  private Template exitTailIndexOneFreeCombMethod(InfixReln relation) {
    // Get top-level terms.
    Set<TemplateTerm> terms = exitFormulaTail.getTopLevelTerms();

    // Keep just those that have max index 1.
    Set<TemplateTerm> indexone = new HashSet<>();
    int n;
    for (TemplateTerm t : terms) {
      n = t.getMaxIndex();
      if (n == 1) {
        indexone.add(t);
      }
    }

    // Combine these for LHS.
    TemplateSum LHS = TemplateSum.freshParamLinComb(type, indexone);

    // Make RHS parameter.
    TemplateTerm RHS = makeRHSParameter();

    // Build template formula as constraint.
    TemplateFormula formula = new TemplateConstraint(LHS, relation, RHS);
    formula.unindex();

    // Make nonzero parameter clause.
    // Namely, we simply ask that not all of the top-level parameters on
    // the LHS be zero.
    Set<TemplateVariable> topLevelLHSparams = LHS.getTopLevelParameters();
    TemplateFormula nzpc = makeBasicParamClause(topLevelLHSparams);

    Template choice = new Template(formula, nzpc);
    return choice;
  }
  private Template topLevelTermFormsMethod(InfixReln relation) {
    Template choice = null;

    // Get all forms.
    Set<TermForm> forms = entryFormula.getTopLevelTermForms();
    forms.addAll(loopFormula.getTopLevelTermForms());
    forms.addAll(exitFormula.getTopLevelTermForms());

    // Convert to terms, and sum up for LHS.
    Vector<TemplateTerm> terms = new Vector<>();
    for (TermForm f : forms) {
      terms.add(f.getTemplate());
    }
    TemplateSum LHS = new TemplateSum(FormulaType.RationalType, terms);

    // Make RHS parameter.
    TemplateTerm RHS = makeRHSParameter();

    // Build template formula as constraint.
    TemplateFormula formula = new TemplateConstraint(LHS, relation, RHS);

    // Make nonzero parameter clause.
    // Namely, we simply ask that not all of the top-level parameters on
    // the LHS be zero.
    Set<TemplateVariable> topLevelLHSparams = LHS.getTopLevelParameters();
    TemplateFormula nzpc = makeBasicParamClause(topLevelLHSparams);

    choice = new Template(formula, nzpc);
    return choice;
  }
  private Template loopVarsAndHeadFreeCombMethod(InfixReln relation) {
    Template choice = null;
    // Get all loop head and loop tail variables.
    Set<TemplateTerm> headVars = getUnindexedVarsAsTerms(loopFormulaHead);
    Set<TemplateTerm> tailVars = getUnindexedVarsAsTerms(loopFormulaTail);

    // Declare a list of terms for each conjunct.
    List<TemplateTerm> headTerms = new Vector<>();
    List<TemplateTerm> tailTerms = new Vector<>();

    // Add the loop vars.
    headTerms.addAll(headVars);
    tailTerms.addAll(tailVars);

    // Create a fresh parameter linear combination of the terms for each conjunct.
    TemplateSum headLHS = TemplateSum.freshParamLinComb(type, headTerms);
    TemplateSum tailLHS = TemplateSum.freshParamLinComb(type, tailTerms);

    // Make RHS parameters.
    TemplateVariable param = TemplateTerm.getNextFreshParameter(type);
    TemplateTerm headRHS = new TemplateTerm(type);
    headRHS.setParameter(param);
    param = TemplateTerm.getNextFreshParameter(type);
    TemplateTerm tailRHS = new TemplateTerm(type);
    tailRHS.setParameter(param);

    // Build the conjuncts as constraints.
    TemplateFormula headFormula = new TemplateConstraint(headLHS, InfixReln.LEQ, headRHS);
    TemplateFormula tailFormula = new TemplateConstraint(tailLHS, InfixReln.EQUAL, tailRHS);

    // Form the conjunction.
    TemplateFormula formula =
        TemplateConjunction.conjoin((TemplateBoolean) headFormula, (TemplateBoolean) tailFormula);

    // Make nonzero parameter clause.
    // Namely, we simply ask that not all of the top-level parameters on
    // the LHS be zero.
    Set<TemplateVariable> topLevelLHSparams = headLHS.getTopLevelParameters();
    topLevelLHSparams.addAll(tailLHS.getTopLevelParameters());
    TemplateFormula nzpc = makeBasicParamClause(topLevelLHSparams);

    choice = new Template(formula, nzpc);
    return choice;
  }
  private Template loopVarsFreeCombMethod(InfixReln relation) {
    Template choice = null;
    // Get all loop variables.
    Set<TemplateTerm> loopVars = getUnindexedVarsAsTerms(loopFormula);

    // Get all distinct UIF names occurring at the top level in the loop formula,
    // and their arities.
    Map<String, Integer> uifNA = getTopLevelUIFnamesAndArities(loopFormula);

    // Declare the list of terms for the template.
    List<TemplateTerm> templateTerms = new Vector<>();

    // First add one term for each UIF name.
    int arity;
    TemplateUIF uif;
    for (String name : uifNA.keySet()) {
      arity = uifNA.get(name).intValue();
      TemplateSum[] args = new TemplateSum[arity];
      // each arg is a fresh parameter linear combination over loopVars
      for (int i = 0; i < arity; i++) {
        args[i] = TemplateSum.freshParamLinComb(type, loopVars);
      }
      // create the uif
      uif = new TemplateUIF(name, type, new TemplateSumList(args));
      // put it in a term
      templateTerms.add(new TemplateTerm(uif));
    }

    // Now add the loop vars themselves.
    templateTerms.addAll(loopVars);

    // Finally create a fresh parameter linear combination of all the terms.
    TemplateSum LHS = TemplateSum.freshParamLinComb(type, templateTerms);

    // Make RHS parameter.
    TemplateVariable param = TemplateTerm.getNextFreshParameter(type);
    TemplateTerm RHS = new TemplateTerm(type);
    RHS.setParameter(param);

    // Build template formula as constraint.
    TemplateFormula formula = new TemplateConstraint(LHS, relation, RHS);

    // Make nonzero parameter clause.
    // Namely, we simply ask that not all of the top-level parameters on
    // the LHS be zero.
    Set<TemplateVariable> topLevelLHSparams = LHS.getTopLevelParameters();
    TemplateFormula nzpc = makeBasicParamClause(topLevelLHSparams);

    choice = new Template(formula, nzpc);
    return choice;
  }