code: def ndto2d(x, axis=-1):
      
  
      # Convert int axis into a tuple
      if isinstance(axis, int):
          axis = (axis,)
      # Handle negative axis indices
      axis = tuple([k if k >= 0 else x.ndim + k for k in axis])
      # Complement of axis set on full set of axes of input v
      caxis = tuple(set(range(x.ndim)) - set(axis))
      # Permute axes of x (generalised transpose) so that axes over
      # which operation is to be applied are all at the end
      prm = caxis + axis
      xt = np.transpose(x, axes=prm)
      xts = xt.shape
      # Reshape into a 2D array with the axes specified by the axis
      # parameter flattened into an index along rows, and the remaining
      # axes flattened into an index aalong the columns
      xtr = xt.reshape((np.product(xts[0:len(caxis)]), -1))
      # Return reshaped array and a tuple containing the information
      # necessary to undo the entire operation
      return xtr, (xts, prm)
docs: Convert a multi-dimensional array into a 2d array, with the axes
      specified by the `axis` parameter flattened into an index along
      rows, and the remaining axes flattened into an index along the
      columns. This operation can not be properly achieved by a simple
      reshape operation since a reshape would shuffle element order if
      the axes to be grouped together were not consecutive: this is
      avoided by first permuting the axes so that the grouped axes are
      consecutive.
  
  
      Parameters
      ----------
      x : array_like
        Multi-dimensional input array
      axis : int or tuple of ints, optional (default -1)
        Axes of `x` to be grouped together to form the rows of the output
        2d array.
  
      Returns
      -------
      xtr : ndarray
        2D output array
      rsi : tuple
        A tuple containing the details of transformation applied in the
        conversion to 2D