docs: def mxarray_to_ndarray(libmx, pm):
      """Convert MATLAB object `pm` to numpy equivalent."""
  
      ndims = libmx.mxGetNumberOfDimensions(pm)
      dims = libmx.mxGetDimensions(pm)
      numelems = libmx.mxGetNumberOfElements(pm)
      elem_size = libmx.mxGetElementSize(pm)
      class_name = libmx.mxGetClassName(pm)
      is_numeric = libmx.mxIsNumeric(pm)
      is_complex = libmx.mxIsComplex(pm)
      data = libmx.mxGetData(pm)
      imag_data = libmx.mxGetImagData(pm)
  
  
      if is_numeric:
          datasize = numelems*elem_size
  
          real_buffer = ctypes.create_string_buffer(datasize)
          ctypes.memmove(real_buffer, data, datasize)
          pyarray = np.ndarray(
              buffer=real_buffer,
              shape=dims[:ndims],
              dtype=class_name,
              order='F'
          )
  
          if is_complex:
              imag_buffer = ctypes.create_string_buffer(datasize)
              ctypes.memmove(imag_buffer, imag_data, datasize)
              pyarray_imag = np.ndarray(
                  buffer=imag_buffer,
                  shape=dims[:ndims],
                  dtype=class_name,
                  order='F'
              )
  
              pyarray = pyarray + pyarray_imag * 1j
  
          out = pyarray.squeeze()
  
          if out.ndim == 0:
              out, = np.atleast_1d(out)
  
  
      elif class_name == 'char':
          datasize = numelems + 1
  
          pystring = ctypes.create_string_buffer(datasize+1)
          libmx.mxGetString(pm, pystring, datasize)
  
          out = pystring.value
  
  
      elif class_name == 'logical':
          datasize = numelems*elem_size
  
          buf = ctypes.create_string_buffer(datasize)
          ctypes.memmove(buf, data, datasize)
  
          pyarray = np.ndarray(
              buffer=buf,
              shape=dims[:ndims],
              dtype='bool',
              order='F'
          )
  
          out = pyarray.squeeze()
          if out.ndim == 0:
              out, = np.atleast_1d(out)
  
  
      elif class_name == 'cell':
          out = np.empty(numelems, dtype='O')
          for i in range(numelems):
              cell = libmx.mxGetCell(pm, i)
  
              if bool(cell):
                  out[i] = mxarray_to_ndarray(libmx, cell)
              else:
                  ### uninitialized cell
                  out[i] = None
  
          out = out.reshape(dims[:ndims], order='F')
          out = out.squeeze()
  
  
      elif class_name == 'struct':
          field_num = libmx.mxGetNumberOfFields(pm)
  
          ### Get all field names
          field_names = []
          for i in range(field_num):
              field_name = libmx.mxGetFieldNameByNumber(pm, i)
              field_names.append(field_name)
  
          ### Get all fields
          records = []            # [(x0, y0, z0), (x1, y1, z1), ... (xN, yN, zN)]
          for i in range(numelems):
              record = []
              for field_name in field_names:
                  field = libmx.mxGetField(pm, i, field_name)
  
                  if bool(field):
                      el = mxarray_to_ndarray(libmx, field)
                  else:
                      ### uninitialized cell
                      el = None
  
                  record.append(el)
              records.append(record)
  
          ### Set the dtypes right (if there is any ndarray, we want dtype=object)
          arrays = zip(*records)  # [(x0, x1, ... xN), (y0, y1, ... yN), (z0, z1, ... zN)]
          new_arrays = []
  
          ## This loop is necessary, because np.rec.fromarrays() cannot
          ## handle a list of arrays of the same size well
          for arr in arrays:
              contains_ndarray = np.any([isinstance(el, np.ndarray) for el in arr])
  
              if contains_ndarray:
                  newarr = np.empty(len(arr), dtype='O')
                  for i,a in enumerate(arr):
                      newarr[i] = a
              else:
                  newarr = np.array(arr)
  
              new_arrays.append(newarr)
  
          if new_arrays:
              out = np.rec.fromarrays(new_arrays, names=field_names)
              out = out.reshape(dims[:ndims], order='F')
              out = out.squeeze()
          else:
              out = np.array([])
  
  
      else:
          raise NotImplementedError('{}-arrays are not supported'.format(class_name))
  
  
      return out