public static Result calculate(
    Map<String, Long> oldChecksums,
    Map<String, Long> newChecksums,
    Set<String> critical,
    Set<String> optional,
    boolean lookForMoved
  ) {
    Result result = new Result();
    result.commonFiles = collect(oldChecksums, newChecksums, critical, true);
    result.filesToDelete = withAllRemoved(oldChecksums, newChecksums);

    Map<String, Long> toUpdate = collect(oldChecksums, newChecksums, critical, false);
    Map<String, Long> toCreate = withAllRemoved(newChecksums, oldChecksums);

    result.filesToCreate = lookForMoved ? new LinkedHashMap<>() : toCreate;

    result.filesToUpdate = new LinkedHashMap<>();
    for (Map.Entry<String, Long> update : toUpdate.entrySet()) {
      if (Digester.isSymlink(update.getValue()) || Digester.isSymlink(newChecksums.get(update.getKey()))) {
        result.filesToDelete.put(update.getKey(), update.getValue());
        result.filesToCreate.put(update.getKey(), Digester.INVALID);
      }
      else {
        result.filesToUpdate.put(update.getKey(), new Update(update.getKey(), update.getValue(), false));
      }
    }

    if (lookForMoved) {
      Map<Long, List<String>> byContent = groupFilesByContent(result.filesToDelete);
      Map<String, List<String>> byName = groupFilesByName(result.filesToDelete);

      for (Map.Entry<String, Long> create : toCreate.entrySet()) {
        if (Digester.isFile(create.getValue())) {
          List<String> sameContent = byContent.get(create.getValue());
          String source = findBestCandidateForMove(sameContent, create.getKey(), optional);
          boolean move = true;

          if (source == null) {
            List<String> sameName = byName.get(new File(create.getKey()).getName());
            source = findBestCandidateForMove(sameName, create.getKey(), optional);
            move = false;
          }

          if (source != null && !critical.contains(source)) {
            result.filesToUpdate.put(create.getKey(), new Update(source, result.filesToDelete.get(source), move));
            continue;
          }
        }

        result.filesToCreate.put(create.getKey(), create.getValue());
      }
    }

    return result;
  }
public static Result calculate(
    Map<String, Long> oldChecksums,
    Map<String, Long> newChecksums,
    Set<String> critical,
    Set<String> optional,
    boolean lookForMoved
  )