@Override
  public boolean differentiate(DifferentiateContext context, Iterable<Node<?, ?>> nodesBefore, Iterable<Node<?, ?>> nodesAfter) {
    Delta delta = context.getDelta();
    if (!delta.isSourceOnly()) {
      return true;
    }

    Graph graph = context.getGraph();
    Set<NodeSource> baseSources = delta.getBaseSources();
    Utils present = new Utils(context.getGraph(), context.getParams().belongsToCurrentCompilationChunk());
    Set<Usage> affectedUsages = new HashSet<>();
    Set<JvmNodeReferenceID> baseNodes = new HashSet<>();

    for (JvmClass cls : flat(map(baseSources, s -> graph.getNodes(s, JvmClass.class)))) {

      if (!cls.isPrivate()) {
        for (ReferenceID id : present.withAllSubclasses(cls.getReferenceID())) {
          if (id instanceof JvmNodeReferenceID) {
            // potentially changed types can be used in kotlin's type inference in super classes
            affectedUsages.add(new ClassUsage(((JvmNodeReferenceID)id)));
          }
        }
      }

      KmDeclarationContainer container = KJvmUtils.getDeclarationContainer(cls);
      if (container != null) {
        if (container instanceof KmClass) {
          baseNodes.add(cls.getReferenceID());
        }
        
        List<KmTypeAlias> typeAliases = container.getTypeAliases();
        if (!typeAliases.isEmpty()) {
          String ownerName = KJvmUtils.getKotlinName(cls);
          if (ownerName != null) {
            for (KmTypeAlias alias : typeAliases) {
              // there can be dependencies in the kotlin super classes on potentially changed type aliases
              affectedUsages.add(new LookupNameUsage(ownerName, alias.getName()));
            }
          }
        }
      }
    }

    Set<NodeSource> affectedSources = new HashSet<>();
    if (!affectedUsages.isEmpty()) {
      BooleanFunction<NodeSource> unmodifiedKtSources = s -> !baseSources.contains(s) && !isEmpty(filter(graph.getNodes(s, JvmClass.class), KJvmUtils::isKotlinNode));

      Iterable<NodeSource> supertypeSources = unique(flat(map(unique(filter(flat(map(baseNodes, present::allSupertypes)), id -> !baseNodes.contains(id))), present::getNodeSources)));

      for (NodeSource src : filter(supertypeSources, unmodifiedKtSources)) {
        if (!isEmpty(filter(flat(map(graph.getNodes(src), Node::getUsages)), affectedUsages::contains))) {
          LOG.debug("Parent Kotlin class in a class hierarchy is not marked for compilation, while it may be using a potentially changed type alias or has compiler-inferred types based on potentially changed types. Affecting  " + src);
          affectedSources.add(src);
        }
      }
    }

    // sealed classes check should be done on both base and affected sources
    Set<JvmNodeReferenceID> affectedSealedClasses = new HashSet<>();
    for (JvmClass cls : flat(map(flat(baseSources, affectedSources), s -> graph.getNodes(s, JvmClass.class)))) {
      KmDeclarationContainer container = KJvmUtils.getDeclarationContainer(cls);
      if (container instanceof KmClass) {
        Iterable<ReferenceID> candidates = unique(flat(map(KJvmUtils.withAllSubclassesIfSealed(present, cls.getReferenceID()), present::allDirectSupertypes)));
        for (JvmClass sealedCls : filter(flat(map(candidates, id -> present.getNodes(id, JvmClass.class))), KJvmUtils::isSealed)) {
          affectedSealedClasses.add(sealedCls.getReferenceID());
        }
      }
    }

    for (NodeSource src : affectedSources) {
      context.affectNodeSource(src);
    }
    if (!affectedSealedClasses.isEmpty()) {
      for (NodeSource src : filter(unique(flat(map(flat(map(affectedSealedClasses, id -> KJvmUtils.withAllSubclassesIfSealed(present, id))), present::getNodeSources))), s -> !baseSources.contains(s))) {
        LOG.debug("Sealed class or subclass of a sealed class is about to be recompiled => sealed classes should be always compiled together with all its subclasses. Affecting  " + src);
        context.affectNodeSource(src);
      }
    }

    return true;
  }
@Override
  public boolean differentiate(DifferentiateContext context, Iterable<Node<?, ?>> nodesBefore, Iterable<Node<?, ?>> nodesAfter)