docs: def part(x, *args):
      '''
      part(x, ii, jj...) is equivalent to x[ii, jj...] if x is a sparse matrix or numpy array and is
        equivalent to np.asarray(x)[ii][:, jj][...] if x is not. If only one argument is passed and
        it is a tuple, then it is passed like x[ii] alone.
  
      The part function is comparible with slices (though the must be entered using the slice(...)
      rather than the : syntax) and Ellipsis.
      '''
      n = len(args)
      sl = slice(None)
      if sps.issparse(x):
          if n == 1: return x[args[0]]
          elif n > 2: raise ValueError('Too many indices for sparse matrix')
          (ii,jj) = args
          if   ii is Ellipsis: ii = sl
          elif jj is Ellipsis: jj = sl
          ni = pimms.is_number(ii)
          nj = pimms.is_number(jj)
          if   ni and nj: return x[ii,jj]
          elif ni:        return x[ii,jj].toarray()[0]
          elif nj:        return x[ii,jj].toarray()[:,0]
          else:           return x[ii][:,jj]
      else:
          x = np.asarray(x)
          if n == 1: return x[args[0]]
          i0 = []
          for (k,arg) in enumerate(args):
              if arg is Ellipsis:
                  # special case...
                  #if Ellipsis in args[ii+1:]: raise ValueError('only one ellipsis allowed per part')
                  left = n - k - 1
                  i0 = [sl for _ in range(len(x.shape) - left)]
              else:
                  x = x[tuple(i0 + [arg])]
                  if not pimms.is_number(arg): i0.append(sl)
          return x