docs: public int[] unifyVariables(int[] uniqueVars, HeadedSyntacticCategory other, int[] assignedVars) {
      Preconditions.checkArgument(other.isUnifiableWith(this), "Not unifiable: " + this + " and " + other);
      int[] otherSemanticVariables = other.semanticVariables;
      Preconditions.checkArgument(otherSemanticVariables.length == semanticVariables.length);
  
      int[] mapping = new int[uniqueVars.length];
      Arrays.fill(mapping, -1);
  
      for (int i = 0; i < semanticVariables.length; i++) {
        int curVar = semanticVariables[i];
        if (curVar != -1) {
          int curVarIndex = Ints.indexOf(uniqueVars, curVar);
          int existingMapping = mapping[curVarIndex];
          int curMapping = otherSemanticVariables[i];
          if (curMapping == -1) {
            continue;
          } else if (existingMapping == -1) {
            mapping[curVarIndex] = curMapping;
          } else if (curMapping != existingMapping) {
            return null;
          }
        }
      }
  
      // Check that mapping contains no duplicate elements.
      int[] sortedMapping = ArrayUtils.copyOf(mapping, mapping.length);
      Arrays.sort(sortedMapping);
      for (int i = 1; i < sortedMapping.length; i++) {
        if (sortedMapping[i] != -1 && sortedMapping[i - 1] == sortedMapping[i]) {
          return null;
        }
      }
  
      int maxAssigned = assignedVars.length != 0 ? Ints.max(assignedVars) : 0;
      int maxUnique = other.getUniqueVariables().length != 0 ? Ints.max(other.getUniqueVariables()) : 0;
      int nextVar = Math.max(maxAssigned, maxUnique) + 1;
      for (int i = 0; i < mapping.length; i++) {
        if (mapping[i] == -1) {
          mapping[i] = nextVar;
          nextVar++;
        }
      }
  
      return mapping;
    }