docs: @Override
    public SparseTensor relabelDimensions(int[] newDimensions) {
      Preconditions.checkArgument(newDimensions.length == numDimensions());
      if (Ordering.natural().isOrdered(Ints.asList(newDimensions))) {
        // If the new dimension labels are in sorted order, then we
        // don't have to re-sort the outcome and value arrays. This is a big
        // efficiency win if it happens. Note that keyNums and values
        // are (treated as) immutable, and hence we don't need to copy them.
        return new SparseTensor(newDimensions, getDimensionSizes(), keyNums, values);
      }
  
      int[] sortedDims = ArrayUtils.copyOf(newDimensions, newDimensions.length);
      Arrays.sort(sortedDims);
  
      // Figure out the mapping from the new, sorted dimension indices
      // to
      // the current indices of the outcome table.
      Map<Integer, Integer> currentDimInds = Maps.newHashMap();
      for (int i = 0; i < newDimensions.length; i++) {
        currentDimInds.put(newDimensions[i], i);
      }
  
      int[] sortedSizes = new int[newDimensions.length];
      long[] sortedIndexOffsets = new long[newDimensions.length];
      int[] newOrder = new int[sortedDims.length];
      int[] dimensionSizes = getDimensionSizes();
      long curIndexOffset = 1;
      for (int i = sortedDims.length - 1; i >= 0; i--) {
        newOrder[currentDimInds.get(sortedDims[i])] = i;
        sortedSizes[i] = dimensionSizes[currentDimInds.get(sortedDims[i])];
        sortedIndexOffsets[i] = curIndexOffset;
        curIndexOffset *= sortedSizes[i];
      }
  
      double[] resultValues = ArrayUtils.copyOf(values, values.length);
      // Map each key of this into a key of the relabeled tensor.
      long[] resultKeyInts = transformKeyNums(keyNums, indexOffsets, sortedIndexOffsets, newOrder);
  
      ArrayUtils.sortKeyValuePairs(resultKeyInts, resultValues, 0, values.length);
      return new SparseTensor(sortedDims, sortedSizes, resultKeyInts, resultValues);
    }