private boolean compareClassInstances()

in base/src/main/java/org/arend/core/expr/visitor/CompareVisitor.java [1853:1991]


  private boolean compareClassInstances(Expression expr1, ClassCallExpression classCall1, Expression expr2, ClassCallExpression classCall2, Expression type) {
    if (expr1 instanceof ArrayExpression array1 && expr2 instanceof ArrayExpression array2 && array1.getElements().size() == array2.getElements().size()) return false;
    if (classCall1.getDefinition() == Prelude.DEP_ARRAY && classCall2.getDefinition() == Prelude.DEP_ARRAY) {
      Expression length1 = classCall1.getImplementationHere(Prelude.ARRAY_LENGTH, expr1);
      Expression length2 = classCall2.getImplementationHere(Prelude.ARRAY_LENGTH, expr1);
      if (length1 != null && length2 != null) {
        length1 = length1.normalize(NormalizationMode.WHNF);
        length2 = length2.normalize(NormalizationMode.WHNF);
        if (length1 instanceof IntegerExpression && ((IntegerExpression) length1).isZero() && length2 instanceof IntegerExpression && ((IntegerExpression) length2).isZero()) {
          Expression elemsType1 = classCall1.getImplementationHere(Prelude.ARRAY_ELEMENTS_TYPE, expr1);
          if (elemsType1 == null) elemsType1 = FieldCallExpression.make(Prelude.ARRAY_ELEMENTS_TYPE, expr1);
          Expression elemsType2 = classCall2.getImplementationHere(Prelude.ARRAY_ELEMENTS_TYPE, expr2);
          if (elemsType2 == null) elemsType2 = FieldCallExpression.make(Prelude.ARRAY_ELEMENTS_TYPE, expr2);
          if (!compare(elemsType1, elemsType2, null, false)) {
            myResult = null;
            return false;
          }
          return true;
        } else {
          if (!classCall1.isImplemented(Prelude.ARRAY_AT) && !(expr1 instanceof ArrayExpression) && !classCall2.isImplemented(Prelude.ARRAY_AT) && !(expr2 instanceof ArrayExpression)) {
            return false;
          }
          var pair1 = getSucs(length1);
          var pair2 = getSucs(length2);
          BigInteger m = pair1.proj2.min(pair2.proj2);
          if (!m.equals(BigInteger.ZERO)) {
            for (BigInteger i = BigInteger.ZERO; i.compareTo(m) < 0; i = i.add(BigInteger.ONE)) {
              IntegerExpression index = new BigIntegerExpression(i);
              if (!normalizedCompare(FunCallExpression.make(Prelude.ARRAY_INDEX, classCall1.getLevels(), Arrays.asList(expr1, index)).normalize(NormalizationMode.WHNF),
                                     FunCallExpression.make(Prelude.ARRAY_INDEX, classCall2.getLevels(), Arrays.asList(expr2, index)).normalize(NormalizationMode.WHNF), null, true) ||
                  !compare(dropElements(expr1, pair1.proj1, classCall1, m), dropElements(expr2, pair2.proj1, classCall2, m), new ClassCallExpression(Prelude.DEP_ARRAY, classCall1.getLevels()), true)) {
                myResult = null;
                return false;
              }
            }
            return true;
          }
        }
      }
    }

    Set<? extends ClassField> fields = null;
    if (type != null) {
      ClassCallExpression classCall = type.cast(ClassCallExpression.class);
      if (classCall != null) {
        Set<ClassField> fieldsCopy = new LinkedHashSet<>();
        for (ClassField field : classCall.getDefinition().getNotImplementedFields()) {
          if (!classCall.isImplementedHere(field)) {
            fieldsCopy.add(field);
          }
        }
        fields = fieldsCopy;
      }
    }
    if (fields == null) {
      if (classCall1.getDefinition() == classCall2.getDefinition()) {
        fields = classCall1.getDefinition().getAllFields();
      } else {
        Set<ClassField> fields1 = new LinkedHashSet<>();
        for (ClassField field : classCall1.getDefinition().getNotImplementedFields()) {
          if (classCall2.getDefinition().containsField(field)) {
            fields1.add(field);
          }
        }
        for (ClassField field : classCall1.getDefinition().getImplementedFields()) {
          if (classCall2.getDefinition().containsField(field)) {
            fields1.add(field);
          }
        }
        fields = fields1;
      }
    }

    for (ClassField field : fields) {
      if (!field.isProperty() && !classCall1.isImplemented(field) && !classCall2.isImplemented(field)) {
        return false;
      }
    }

    for (ClassField field : fields) {
      if (field.isProperty()) {
        continue;
      }

      AbsExpression absImpl1 = classCall1.getAbsImplementation(field);
      AbsExpression absImpl2 = classCall2.getAbsImplementation(field);
      if (absImpl1 == null || absImpl2 == null) {
        Expression impl1 = absImpl1 == null ? FieldCallExpression.make(field, expr1) : classCall1.getImplementation(field, expr1);
        Expression impl2 = absImpl2 == null ? FieldCallExpression.make(field, expr2) : classCall2.getImplementation(field, expr2);
        if (!compare(impl1, impl2, field.getType(classCall1.getLevels(field.getParentClass())).applyExpression(expr1), true)) {
          myResult = null;
          return false;
        }
      } else {
        Binding prevBinding = mySubstitution.put(classCall2.getThisBinding(), classCall1.getThisBinding());
        Expression impl1 = classCall1.getAbsImplementationHere(field);
        Expression impl2 = classCall2.getAbsImplementationHere(field);
        if (impl1 == null) {
          impl1 = Objects.requireNonNull(classCall1.getDefinition().getImplementation(field)).apply(expr1, LevelSubstitution.EMPTY);
        }
        if (impl2 == null) {
          impl2 = Objects.requireNonNull(classCall2.getDefinition().getImplementation(field)).apply(expr2, LevelSubstitution.EMPTY);
        }
        boolean ok = compare(impl1, impl2, field.getType(classCall1.getLevels(field.getParentClass())).applyExpression(expr1), true);
        if (prevBinding == null) {
          mySubstitution.remove(classCall2.getThisBinding());
        } else {
          mySubstitution.put(classCall2.getThisBinding(), prevBinding);
        }
        if (!ok) {
          if (expr1 instanceof NewExpression && expr2 instanceof NewExpression) {
            if (myResult == null) {
              initResult(expr1, expr2);
            } else {
              if (classCall1.isImplementedHere(field)) {
                Map<ClassField, Expression> impls = new LinkedHashMap<>(classCall1.getImplementedHere());
                impls.put(field, myResult.wholeExpr1);
                myResult.wholeExpr1 = new NewExpression(null, new ClassCallExpression(classCall1.getDefinition(), classCall1.getLevels(), impls, classCall1.getSort(), classCall1.getUniverseKind()));
              } else {
                myResult.wholeExpr1 = expr1;
              }
              if (classCall2.isImplementedHere(field)) {
                Map<ClassField, Expression> impls = new LinkedHashMap<>(classCall2.getImplementedHere());
                impls.put(field, myResult.wholeExpr2);
                myResult.wholeExpr2 = new NewExpression(null, new ClassCallExpression(classCall2.getDefinition(), classCall2.getLevels(), impls, classCall2.getSort(), classCall2.getUniverseKind()));
              } else {
                myResult.wholeExpr2 = expr2;
              }
            }
          } else {
            myResult = null;
          }
          return false;
        }
      }
    }

    return true;
  }