public ConcreteExpression solve()

in meta/src/main/java/org/arend/lib/meta/equation/MonoidSolver.java [251:410]


  public ConcreteExpression solve(@Nullable ConcreteExpression hint, @NotNull TypedExpression leftExpr, @NotNull TypedExpression rightExpr, @NotNull ErrorReporter errorReporter) {
    CompiledTerm term1 = lastTerm == leftExpr ? lastCompiled : compileTerm(leftExpr.getExpression());
    CompiledTerm term2 = compileTerm(rightExpr.getExpression());
    lastTerm = rightExpr;
    lastCompiled = term2;

    // TODO: get rid of this shit with flags
    boolean oldCommutative = isCommutative;
    boolean commutative = false;
    if (isCommutative && !term1.nf.equals(term2.nf)) {
      commutative = true;
      term1.nf = CountingSort.sort(term1.nf);
      term2.nf = CountingSort.sort(term2.nf);
    }
    isCommutative = commutative;

    boolean oldSemilattice = isSemilattice;
    boolean semilattice = false;
    if (isSemilattice && commutative && !term1.nf.equals(term2.nf)) {
      semilattice = true;
      term1.nf = removeDuplicates(term1.nf);
      term2.nf = removeDuplicates(term2.nf);
    }
    isSemilattice = semilattice;

    ConcreteExpression lastArgument;
    if (!term1.nf.equals(term2.nf)) {
      if (!useHypotheses) {
        errorReporter.report(new MonoidSolverError(isMultiplicative, term1.nf, term2.nf, values.getValues(), Collections.emptyList(), Collections.emptyList(),  Collections.emptyList(), hint != null ? hint : refExpr));
        return null;
      }

      List<RuleExt> rules = new ArrayList<>();
      if (contextRules == null) {
        contextRules = new HashMap<>();
      }
      ContextHelper helper = new ContextHelper(hint);
      for (CoreBinding binding : helper.getContextBindings(typechecker)) {
        rules.addAll(contextRules.computeIfAbsent(binding, k -> {
          List<RuleExt> ctxList = new ArrayList<>();
          typeToRule(null, binding, false, ctxList);
          return ctxList;
        }));
      }
      for (CoreBinding binding : helper.getAdditionalBindings(typechecker)) {
        typeToRule(null, binding, true, rules);
      }

      if (isCommutative) {
        ComMonoidWPSolver solver = new ComMonoidWPSolver();
        var equalities = new ArrayList<Equality>();
        for (RuleExt rule : rules) {
          if (rule.direction == Direction.FORWARD || rule.direction == Direction.UNKNOWN) {
            equalities.add(new Equality(rule.binding != null ? factory.ref(rule.binding) : factory.core(rule.expression), rule.lhsTerm, rule.rhsTerm, rule.lhs, rule.rhs));
          } else {
            equalities.add(new Equality(rule.binding != null ? factory.ref(rule.binding) : factory.core(rule.expression), rule.rhsTerm, rule.lhsTerm, rule.rhs, rule.lhs));
          }
        }
        isCommutative = oldCommutative;
        isSemilattice = oldSemilattice;
        ConcreteExpression result = solver.solve(term1, term2, equalities);
        if (result == null) {
          errorReporter.report(new MonoidSolverError(isMultiplicative, term1.nf, term2.nf, values.getValues(), rules, Collections.emptyList(), Collections.emptyList(), hint != null ? hint : refExpr));
        }
        return result;
      }

      List<Step> trace1 = new ArrayList<>();
      List<Step> trace2 = new ArrayList<>();
      List<Integer> newNf1 = applyRules(term1.nf, rules, trace1);
      List<Integer> newNf2 = applyRules(term2.nf, rules, trace2);
      if (!newNf1.equals(newNf2)) {
        isCommutative = oldCommutative;
        isSemilattice = oldSemilattice;
        errorReporter.report(new MonoidSolverError(isMultiplicative, term1.nf, term2.nf, values.getValues(), rules, trace1, trace2, hint != null ? hint : refExpr));
        return null;
      }

      while (!trace1.isEmpty() && !trace2.isEmpty()) {
        if (trace1.get(trace1.size() - 1).equals(trace2.get(trace2.size() - 1))) {
          trace1.remove(trace1.size() - 1);
          trace2.remove(trace2.size() - 1);
        } else {
          break;
        }
      }

      for (Step step : trace1) {
        step.rule.count++;
      }
      for (Step step : trace2) {
        step.rule.count++;
      }
      for (RuleExt rule : rules) {
        if (rule.count > 0) {
          if (rule.rnfTerm == null) {
            rule.rnfTerm = computeNFTerm(rule.rhs);
          }
          if (rule.cExpr != null) {
            continue;
          }

          ConcreteExpression cExpr = rule.binding != null ? factory.ref(rule.binding) : factory.core(null, rule.expression);
          if (rule.direction == Direction.BACKWARD) {
            cExpr = factory.app(factory.ref(meta.ext.inv.getRef()), true, singletonList(cExpr));
          }
          if (!isNF(rule.lhsTerm) || !isNF(rule.rhsTerm)) {
            cExpr = factory.appBuilder(factory.ref(meta.termsEqConv.getRef()))
              .app(factory.ref(dataRef), false)
              .app(rule.lhsTerm)
              .app(rule.rhsTerm)
              .app(cExpr)
              .build();
          }
          if (rule.count > 1 && !(cExpr instanceof ConcreteReferenceExpression) || rule.binding == null) {
            ArendRef letClause = factory.local("rule" + letClauses.size());
            letClauses.add(factory.letClause(letClause, Collections.emptyList(), null, cExpr));
            rule.cExpr = factory.ref(letClause);
          } else {
            rule.cExpr = cExpr;
          }
        }
      }

      ConcreteExpression expr1 = trace1.isEmpty() ? null : traceToExpr(term1.nf, trace1, dataRef, factory);
      ConcreteExpression expr2 = trace2.isEmpty() ? null : factory.app(factory.ref(meta.ext.inv.getRef()), true, singletonList(traceToExpr(term2.nf, trace2, dataRef, factory)));
      if (expr1 == null && expr2 == null) {
        lastArgument = factory.ref(meta.ext.prelude.getIdp().getRef());
      } else if (expr2 == null) {
        lastArgument = expr1;
      } else if (expr1 == null) {
        lastArgument = expr2;
      } else {
        lastArgument = factory.appBuilder(factory.ref(meta.ext.concat.getRef())).app(expr1).app(expr2).build();
      }
    } else {
      lastArgument = factory.ref(meta.ext.prelude.getIdp().getRef());
    }

    ConcreteAppBuilder builder = factory.appBuilder(factory.ref((isCat ? meta.catTermsEq : semilattice ? meta.semilatticeTermsEq : commutative ? meta.commTermsEq : meta.termsEq).getRef()))
      .app(factory.ref(dataRef), false);
    if (isCat) {
      CoreExpression type = leftExpr.getType().normalize(NormalizationMode.WHNF);
      if (type instanceof CoreAppExpression) {
        CoreExpression fun = ((CoreAppExpression) type).getFunction().normalize(NormalizationMode.WHNF);
        if (fun instanceof CoreAppExpression) {
          int dom = obValues.addValue(((CoreAppExpression) fun).getArgument());
          int cod = obValues.addValue(((CoreAppExpression) type).getArgument());
          builder.app(factory.number(dom), false).app(factory.number(cod), false);
        }
      }
    }
    isCommutative = oldCommutative;
    isSemilattice = oldSemilattice;
    return builder
      .app(term1.concrete)
      .app(term2.concrete)
      .app(lastArgument)
      .build();
  }