public SatisfiabilityObligation(
      AImplicitFunctionDefinition func, IPOContextStack ctxt, IPogAssistantFactory af)
      throws AnalysisException {
    super(func, POType.FUNC_SATISFIABILITY, ctxt, func.getLocation(), af);

    /** f: A * B -> R [pre ...] post ... [pre_f(a, b) =>] exists r:R & post_f(a, b, r) */
    List<PExp> arglist = new Vector<PExp>();

    for (APatternListTypePair pltp : func.getParamPatterns()) {
      for (PPattern pattern : pltp.getPatterns()) {
        arglist.add(patternToExp(pattern));
      }
    }

    AApplyExp preApply = null;

    if (func.getPredef() != null) {
      preApply =
          getApplyExp(
              getVarExp(func.getPredef().getName().clone(), func.getPredef().clone()), arglist);
      preApply.setType(new ABooleanBasicType());
      preApply.getRoot().setType(func.getPredef().getType().clone());
    }

    AExistsExp existsExp = new AExistsExp();
    existsExp.setType(new ABooleanBasicType());
    List<PExp> postArglist = new Vector<PExp>(arglist);

    if (func.getResult().getPattern() instanceof AIdentifierPattern) {
      AIdentifierPattern ip = (AIdentifierPattern) func.getResult().getPattern().clone();
      postArglist.add(patternToExp(func.getResult().getPattern()));
      existsExp.setBindList(
          getMultipleTypeBindList(func.getResult().getType().clone(), ip.getName()));
    } else {
      throw new RuntimeException("Expecting identifier pattern in function result");
    }

    AApplyExp postApply =
        getApplyExp(getVarExp(func.getPostdef().getName(), func.getPostdef()), postArglist);
    postApply.setType(new ABooleanBasicType());
    postApply.getRoot().setType(func.getPostdef().getType().clone());
    existsExp.setPredicate(postApply);

    if (preApply != null) {
      AImpliesBooleanBinaryExp implies =
          AstExpressionFactory.newAImpliesBooleanBinaryExp(preApply, existsExp);
      stitch = implies;
      valuetree.setPredicate(ctxt.getPredWithContext(implies));
    } else {
      stitch = existsExp;
      valuetree.setPredicate(ctxt.getPredWithContext(existsExp));
    }

    // valuetree.setContext(ctxt.getContextNodeList());
  }
  PExp buildPredicate(AImplicitOperationDefinition op, PDefinition stateDefinition)
      throws AnalysisException {
    List<PExp> arglist = new Vector<PExp>();

    for (APatternListTypePair pltp : op.getParameterPatterns()) {
      for (PPattern pattern : pltp.getPatterns()) {
        arglist.add(patternToExp(pattern.clone()));
      }
    }

    if (stateDefinition != null) {
      stateInPre(arglist, stateDefinition);
    }
    AApplyExp preApply = null;

    if (op.getPredef() != null) {
      preApply = getApplyExp(getVarExp(op.getPredef().getName().clone(), op.getPredef()), arglist);
      preApply.getRoot().setType(op.getPredef().getType().clone());
      preApply.setType(new ABooleanBasicType());
    }

    PExp mainExp;

    // Operation Has a Result. Add it in the post condition.
    if (op.getResult() != null) {

      AExistsExp existsExp = new AExistsExp();
      existsExp.setType(new ABooleanBasicType());
      List<PExp> postArglist = new Vector<PExp>(arglist);

      if (op.getResult().getPattern() instanceof AIdentifierPattern) {
        AIdentifierPattern ip = (AIdentifierPattern) op.getResult().getPattern();
        postArglist.add(patternToExp(op.getResult().getPattern().clone()));

        if (stateDefinition != null) {

          if (stateDefinition instanceof AStateDefinition) {
            AVariableExp varExp = getVarExp(OLD_STATE_ARG);
            varExp.setType(((AStateDefinition) stateDefinition).getRecordType().clone());
            postArglist.add(varExp);
            AVariableExp varExp2 = getVarExp(NEW_STATE_ARG);
            varExp2.setType(((AStateDefinition) stateDefinition).getRecordType().clone());
            postArglist.add(varExp2);
          } else {
            AVariableExp varExp = getVarExp(OLD_SELF_ARG);
            postArglist.add(varExp);
            varExp.setType(stateDefinition.getType().clone());
            AVariableExp varExp2 = getVarExp(NEW_SELF_ARG);
            postArglist.add(varExp2);
            varExp2.setType(stateDefinition.getType().clone());
          }
        }

        existsExp.setBindList(
            getMultipleTypeBindList(op.getResult().getType().clone(), ip.getName().clone()));
      } else {
        throw new RuntimeException("Expecting single identifier pattern in operation result");
      }

      AApplyExp postApply = getApplyExp(getVarExp(op.getPostdef().getName()), postArglist);
      postApply.getRoot().setType(op.getPostdef().getType().clone());
      postApply.setType(new ABooleanBasicType());
      existsExp.setPredicate(postApply);
      mainExp = existsExp;
    }

    // No Result. Just add new state to post condition
    else {

      AExistsExp exists_exp = new AExistsExp();
      exists_exp.setType(new ABooleanBasicType());
      List<PExp> postArglist = new Vector<PExp>(arglist);

      List<PMultipleBind> exists_binds = new LinkedList<PMultipleBind>();
      if (stateDefinition != null) {
        stateInPost(exists_binds, postArglist, stateDefinition);
      }
      exists_exp.setBindList(exists_binds);
      AApplyExp postApply =
          getApplyExp(getVarExp(op.getPostdef().getName()), new Vector<PExp>(postArglist));
      postApply.setType(new ABooleanBasicType());
      postApply.getRoot().setType(op.getPostdef().getType().clone());
      exists_exp.setPredicate(postApply);
      mainExp = exists_exp;
    }

    if (preApply != null) {
      return AstExpressionFactory.newAImpliesBooleanBinaryExp(preApply, mainExp);
    } else {
      return mainExp;
    }
  }