code: public UnixPath normalize() {
      List<String> parts = new ArrayList<>();
      boolean mutated = false;
      int resultLength = 0;
      int mark = 0;
      int index;
      do {
        index = path.indexOf(SEPARATOR, mark);
        String part = path.substring(mark, index == -1 ? path.length() : index + 1);
        switch (part) {
          case CURRENT_DIR:
          case CURRENT_DIR + SEPARATOR:
            mutated = true;
            break;
          case PARENT_DIR:
          case PARENT_DIR + SEPARATOR:
            mutated = true;
            if (!parts.isEmpty()) {
              resultLength -= parts.remove(parts.size() - 1).length();
            }
            break;
          default:
            if (index != mark || index == 0) {
              parts.add(part);
              resultLength = part.length();
            } else {
              mutated = true;
            }
        }
        mark = index + 1;
      } while (index != -1);
      if (!mutated) {
        return this;
      }
      StringBuilder result = new StringBuilder(resultLength);
      for (String part : parts) {
        result.append(part);
      }
      return new UnixPath(permitEmptyComponents, result.toString());
    }
docs: Returns path without extra separators or {@code .} and {@code ..}, preserving trailing slash.
  
  @see java.nio.file.Path#normalize()