text: """
  A place for code to be called from core C-code.
  
  Some things are more easily handled Python.
  
  """
  from __future__ import division, absolute_import, print_function
  
  import re
  import sys
  
  from numpy.compat import basestring
  from .multiarray import dtype, array, ndarray
  try:
      import ctypes
  except ImportError:
      ctypes = None
  from .numerictypes import object_
  
  if (sys.byteorder == 'little'):
      _nbo = b'<'
  else:
      _nbo = b'>'
  
  def _makenames_list(adict, align):
      allfields = []
      fnames = list(adict.keys())
      for fname in fnames:
          obj = adict[fname]
          n = len(obj)
          if not isinstance(obj, tuple) or n not in [2, 3]:
              raise ValueError("entry not a 2- or 3- tuple")
          if (n > 2) and (obj[2] == fname):
              continue
          num = int(obj[1])
          if (num < 0):
              raise ValueError("invalid offset.")
          format = dtype(obj[0], align=align)
          if (n > 2):
              title = obj[2]
          else:
              title = None
          allfields.append((fname, format, num, title))
      # sort by offsets
      allfields.sort(key=lambda x: x[2])
      names = [x[0] for x in allfields]
      formats = [x[1] for x in allfields]
      offsets = [x[2] for x in allfields]
      titles = [x[3] for x in allfields]
  
      return names, formats, offsets, titles
  
  # Called in PyArray_DescrConverter function when
  #  a dictionary without "names" and "formats"
  #  fields is used as a data-type descriptor.
  def _usefields(adict, align):
      try:
          names = adict[-1]
      except KeyError:
          names = None
      if names is None:
          names, formats, offsets, titles = _makenames_list(adict, align)
      else:
          formats = []
          offsets = []
          titles = []
          for name in names:
              res = adict[name]
              formats.append(res[0])
              offsets.append(res[1])
              if (len(res) > 2):
                  titles.append(res[2])
              else:
                  titles.append(None)
  
      return dtype({"names": names,
                    "formats": formats,
                    "offsets": offsets,
                    "titles": titles}, align)
  
  
  # construct an array_protocol descriptor list
  #  from the fields attribute of a descriptor
  # This calls itself recursively but should eventually hit
  #  a descriptor that has no fields and then return
  #  a simple typestring
  
  def _array_descr(descriptor):
      fields = descriptor.fields
      if fields is None:
          subdtype = descriptor.subdtype
          if subdtype is None:
              if descriptor.metadata is None:
                  return descriptor.str
              else:
                  new = descriptor.metadata.copy()
                  if new:
                      return (descriptor.str, new)
                  else:
                      return descriptor.str
          else:
              return (_array_descr(subdtype[0]), subdtype[1])
  
      names = descriptor.names
      ordered_fields = [fields[x] + (x,) for x in names]
      result = []
      offset = 0
      for field in ordered_fields:
          if field[1] > offset:
              num = field[1] - offset
              result.append(('', '|V%d' % num))
              offset += num
          elif field[1] < offset:
              raise ValueError(
                  "dtype.descr is not defined for types with overlapping or "
                  "out-of-order fields")
          if len(field) > 3:
              name = (field[2], field[3])
          else:
              name = field[2]
          if field[0].subdtype:
              tup = (name, _array_descr(field[0].subdtype[0]),
                     field[0].subdtype[1])
          else:
              tup = (name, _array_descr(field[0]))
          offset += field[0].itemsize
          result.append(tup)
  
      if descriptor.itemsize > offset:
          num = descriptor.itemsize - offset
          result.append(('', '|V%d' % num))
  
      return result
  
  # Build a new array from the information in a pickle.
  # Note that the name numpy.core._internal._reconstruct is embedded in
  # pickles of ndarrays made with NumPy before release 1.0
  # so don't remove the name here, or you'll
  # break backward compatibility.
  def _reconstruct(subtype, shape, dtype):
      return ndarray.__new__(subtype, shape, dtype)
  
  
  # format_re was originally from numarray by J. Todd Miller
  
  format_re = re.compile(br'(?P<order1>[<>|=]?)'
                         br'(?P<repeats> *[(]?[ ,0-9L]*[)]? *)'
                         br'(?P<order2>[<>|=]?)'
                         br'(?P<dtype>[A-Za-z0-9.?]*(?:\[[a-zA-Z0-9,.]+\])?)')
  sep_re = re.compile(br'\s*,\s*')
  space_re = re.compile(br'\s+$')
  
  # astr is a string (perhaps comma separated)
  
  _convorder = {b'=': _nbo}
  
  def _commastring(astr):
      startindex = 0
      result = []
      while startindex < len(astr):
          mo = format_re.match(astr, pos=startindex)
          try:
              (order1, repeats, order2, dtype) = mo.groups()
          except (TypeError, AttributeError):
              raise ValueError('format number %d of "%s" is not recognized' %
                                              (len(result)+1, astr))
          startindex = mo.end()
          # Separator or ending padding
          if startindex < len(astr):
              if space_re.match(astr, pos=startindex):
                  startindex = len(astr)
              else:
                  mo = sep_re.match(astr, pos=startindex)
                  if not mo:
                      raise ValueError(
                          'format number %d of "%s" is not recognized' %
                          (len(result)+1, astr))
                  startindex = mo.end()
  
          if order2 == b'':
              order = order1
          elif order1 == b'':
              order = order2
          else:
              order1 = _convorder.get(order1, order1)
              order2 = _convorder.get(order2, order2)
              if (order1 != order2):
                  raise ValueError(
                      'inconsistent byte-order specification %s and %s' %
                      (order1, order2))
              order = order1
  
          if order in [b'|', b'=', _nbo]:
              order = b''
          dtype = order + dtype
          if (repeats == b''):
              newitem = dtype
          else:
              newitem = (dtype, eval(repeats))
          result.append(newitem)
  
      return result
  
  class dummy_ctype(object):
      def __init__(self, cls):
          self._cls = cls
      def __mul__(self, other):
          return self
      def __call__(self, *other):
          return self._cls(other)
      def __eq__(self, other):
          return self._cls == other._cls
      def __ne__(self, other):
          return self._cls != other._cls
  
  def _getintp_ctype():
      val = _getintp_ctype.cache
      if val is not None:
          return val
      if ctypes is None:
          import numpy as np
          val = dummy_ctype(np.intp)
      else:
          char = dtype('p').char
          if (char == 'i'):
              val = ctypes.c_int
          elif char == 'l':
              val = ctypes.c_long
          elif char == 'q':
              val = ctypes.c_longlong
          else:
              val = ctypes.c_long
      _getintp_ctype.cache = val
      return val
  _getintp_ctype.cache = None
  
  # Used for .ctypes attribute of ndarray
  
  class _missing_ctypes(object):
      def cast(self, num, obj):
          return num
  
      def c_void_p(self, num):
          return num
  
  class _ctypes(object):
      def __init__(self, array, ptr=None):
          if ctypes:
              self._ctypes = ctypes
          else:
              self._ctypes = _missing_ctypes()
          self._arr = array
          self._data = ptr
          if self._arr.ndim == 0:
              self._zerod = True
          else:
              self._zerod = False
  
      def data_as(self, obj):
          return self._ctypes.cast(self._data, obj)
  
      def shape_as(self, obj):
          if self._zerod:
              return None
          return (obj*self._arr.ndim)(*self._arr.shape)
  
      def strides_as(self, obj):
          if self._zerod:
              return None
          return (obj*self._arr.ndim)(*self._arr.strides)
  
      def get_data(self):
          return self._data
  
      def get_shape(self):
          return self.shape_as(_getintp_ctype())
  
      def get_strides(self):
          return self.strides_as(_getintp_ctype())
  
      def get_as_parameter(self):
          return self._ctypes.c_void_p(self._data)
  
      data = property(get_data, None, doc="c-types data")
      shape = property(get_shape, None, doc="c-types shape")
      strides = property(get_strides, None, doc="c-types strides")
      _as_parameter_ = property(get_as_parameter, None, doc="_as parameter_")
  
  
  def _newnames(datatype, order):
      """
      Given a datatype and an order object, return a new names tuple, with the
      order indicated
      """
      oldnames = datatype.names
      nameslist = list(oldnames)
      if isinstance(order, str):
          order = [order]
      seen = set()
      if isinstance(order, (list, tuple)):
          for name in order:
              try:
                  nameslist.remove(name)
              except ValueError:
                  if name in seen:
                      raise ValueError("duplicate field name: %s" % (name,))
                  else:
                      raise ValueError("unknown field name: %s" % (name,))
              seen.add(name)
          return tuple(list(order) + nameslist)
      raise ValueError("unsupported order value: %s" % (order,))
  
  def _copy_fields(ary):
      """Return copy of structured array with padding between fields removed.
  
      Parameters
      ----------
      ary : ndarray
         Structured array from which to remove padding bytes
  
      Returns
      -------
      ary_copy : ndarray
         Copy of ary with padding bytes removed
      """
      dt = ary.dtype
      copy_dtype = {'names': dt.names,
                    'formats': [dt.fields[name][0] for name in dt.names]}
      return array(ary, dtype=copy_dtype, copy=True)
  
  def _getfield_is_safe(oldtype, newtype, offset):
      """ Checks safety of getfield for object arrays.
  
      As in _view_is_safe, we need to check that memory containing objects is not
      reinterpreted as a non-object datatype and vice versa.
  
      Parameters
      ----------
      oldtype : data-type
          Data type of the original ndarray.
      newtype : data-type
          Data type of the field being accessed by ndarray.getfield
      offset : int
          Offset of the field being accessed by ndarray.getfield
  
      Raises
      ------
      TypeError
          If the field access is invalid
  
      """
      if newtype.hasobject or oldtype.hasobject:
          if offset == 0 and newtype == oldtype:
              return
          if oldtype.names:
              for name in oldtype.names:
                  if (oldtype.fields[name][1] == offset and
                          oldtype.fields[name][0] == newtype):
                      return
          raise TypeError("Cannot get/set field of an object array")
      return
  
  def _view_is_safe(oldtype, newtype):
      """ Checks safety of a view involving object arrays, for example when
      doing::
  
          np.zeros(10, dtype=oldtype).view(newtype)
  
      Parameters
      ----------
      oldtype : data-type
          Data type of original ndarray
      newtype : data-type
          Data type of the view
  
      Raises
      ------
      TypeError
          If the new type is incompatible with the old type.
  
      """
  
      # if the types are equivalent, there is no problem.
      # for example: dtype((np.record, 'i4,i4')) == dtype((np.void, 'i4,i4'))
      if oldtype == newtype:
          return
  
      if newtype.hasobject or oldtype.hasobject:
          raise TypeError("Cannot change data-type for object array.")
      return
  
  # Given a string containing a PEP 3118 format specifier,
  # construct a NumPy dtype
  
  _pep3118_native_map = {
      '?': '?',
      'c': 'S1',
      'b': 'b',
      'B': 'B',
      'h': 'h',
      'H': 'H',
      'i': 'i',
      'I': 'I',
      'l': 'l',
      'L': 'L',
      'q': 'q',
      'Q': 'Q',
      'e': 'e',
      'f': 'f',
      'd': 'd',
      'g': 'g',
      'Zf': 'F',
      'Zd': 'D',
      'Zg': 'G',
      's': 'S',
      'w': 'U',
      'O': 'O',
      'x': 'V',  # padding
  }
  _pep3118_native_typechars = ''.join(_pep3118_native_map.keys())
  
  _pep3118_standard_map = {
      '?': '?',
      'c': 'S1',
      'b': 'b',
      'B': 'B',
      'h': 'i2',
      'H': 'u2',
      'i': 'i4',
      'I': 'u4',
      'l': 'i4',
      'L': 'u4',
      'q': 'i8',
      'Q': 'u8',
      'e': 'f2',
      'f': 'f',
      'd': 'd',
      'Zf': 'F',
      'Zd': 'D',
      's': 'S',
      'w': 'U',
      'O': 'O',
      'x': 'V',  # padding
  }
  _pep3118_standard_typechars = ''.join(_pep3118_standard_map.keys())
  
  def _dtype_from_pep3118(spec):
  
      class Stream(object):
          def __init__(self, s):
              self.s = s
              self.byteorder = '@'
  
          def advance(self, n):
              res = self.s[:n]
              self.s = self.s[n:]
              return res
  
          def consume(self, c):
              if self.s[:len(c)] == c:
                  self.advance(len(c))
                  return True
              return False
  
          def consume_until(self, c):
              if callable(c):
                  i = 0
                  while i < len(self.s) and not c(self.s[i]):
                      i = i + 1
                  return self.advance(i)
              else:
                  i = self.s.index(c)
                  res = self.advance(i)
                  self.advance(len(c))
                  return res
  
          @property
          def next(self):
              return self.s[0]
  
          def __bool__(self):
              return bool(self.s)
          __nonzero__ = __bool__
  
      stream = Stream(spec)
  
      dtype, align = __dtype_from_pep3118(stream, is_subdtype=False)
      return dtype
  
  def __dtype_from_pep3118(stream, is_subdtype):
      field_spec = dict(
          names=[],
          formats=[],
          offsets=[],
          itemsize=0
      )
      offset = 0
      common_alignment = 1
      is_padding = False
  
      # Parse spec
      while stream:
          value = None
  
          # End of structure, bail out to upper level
          if stream.consume('}'):
              break
  
          # Sub-arrays (1)
          shape = None
          if stream.consume('('):
              shape = stream.consume_until(')')
              shape = tuple(map(int, shape.split(',')))
  
          # Byte order
          if stream.next in ('@', '=', '<', '>', '^', '!'):
              byteorder = stream.advance(1)
              if byteorder == '!':
                  byteorder = '>'
              stream.byteorder = byteorder
  
          # Byte order characters also control native vs. standard type sizes
          if stream.byteorder in ('@', '^'):
              type_map = _pep3118_native_map
              type_map_chars = _pep3118_native_typechars
          else:
              type_map = _pep3118_standard_map
              type_map_chars = _pep3118_standard_typechars
  
          # Item sizes
          itemsize_str = stream.consume_until(lambda c: not c.isdigit())
          if itemsize_str:
              itemsize = int(itemsize_str)
          else:
              itemsize = 1
  
          # Data types
          is_padding = False
  
          if stream.consume('T{'):
              value, align = __dtype_from_pep3118(
                  stream, is_subdtype=True)
          elif stream.next in type_map_chars:
              if stream.next == 'Z':
                  typechar = stream.advance(2)
              else:
                  typechar = stream.advance(1)
  
              is_padding = (typechar == 'x')
              dtypechar = type_map[typechar]
              if dtypechar in 'USV':
                  dtypechar += '%d' % itemsize
                  itemsize = 1
              numpy_byteorder = {'@': '=', '^': '='}.get(
                  stream.byteorder, stream.byteorder)
              value = dtype(numpy_byteorder + dtypechar)
              align = value.alignment
          else:
              raise ValueError("Unknown PEP 3118 data type specifier %r" % stream.s)
  
          #
          # Native alignment may require padding
          #
          # Here we assume that the presence of a '@' character implicitly implies
          # that the start of the array is *already* aligned.
          #
          extra_offset = 0
          if stream.byteorder == '@':
              start_padding = (-offset) % align
              intra_padding = (-value.itemsize) % align
  
              offset += start_padding
  
              if intra_padding != 0:
                  if itemsize > 1 or (shape is not None and _prod(shape) > 1):
                      # Inject internal padding to the end of the sub-item
                      value = _add_trailing_padding(value, intra_padding)
                  else:
                      # We can postpone the injection of internal padding,
                      # as the item appears at most once
                      extra_offset += intra_padding
  
              # Update common alignment
              common_alignment = _lcm(align, common_alignment)
  
          # Convert itemsize to sub-array
          if itemsize != 1:
              value = dtype((value, (itemsize,)))
  
          # Sub-arrays (2)
          if shape is not None:
              value = dtype((value, shape))
  
          # Field name
          if stream.consume(':'):
              name = stream.consume_until(':')
          else:
              name = None
  
          if not (is_padding and name is None):
              if name is not None and name in field_spec['names']:
                  raise RuntimeError("Duplicate field name '%s' in PEP3118 format"
                                     % name)
              field_spec['names'].append(name)
              field_spec['formats'].append(value)
              field_spec['offsets'].append(offset)
  
          offset += value.itemsize
          offset += extra_offset
  
          field_spec['itemsize'] = offset
  
      # extra final padding for aligned types
      if stream.byteorder == '@':
          field_spec['itemsize'] += (-offset) % common_alignment
  
      # Check if this was a simple 1-item type, and unwrap it
      if (field_spec['names'] == [None]
              and field_spec['offsets'][0] == 0
              and field_spec['itemsize'] == field_spec['formats'][0].itemsize
              and not is_subdtype):
          ret = field_spec['formats'][0]
      else:
          _fix_names(field_spec)
          ret = dtype(field_spec)
  
      # Finished
      return ret, common_alignment
  
  def _fix_names(field_spec):
      """ Replace names which are None with the next unused f%d name """
      names = field_spec['names']
      for i, name in enumerate(names):
          if name is not None:
              continue
  
          j = 0
          while True:
              name = 'f{}'.format(j)
              if name not in names:
                  break
              j = j + 1
          names[i] = name
  
  def _add_trailing_padding(value, padding):
      """Inject the specified number of padding bytes at the end of a dtype"""
      if value.fields is None:
          field_spec = dict(
              names=['f0'],
              formats=[value],
              offsets=[0],
              itemsize=value.itemsize
          )
      else:
          fields = value.fields
          names = value.names
          field_spec = dict(
              names=names,
              formats=[fields[name][0] for name in names],
              offsets=[fields[name][1] for name in names],
              itemsize=value.itemsize
          )
  
      field_spec['itemsize'] += padding
      return dtype(field_spec)
  
  def _prod(a):
      p = 1
      for x in a:
          p *= x
      return p
  
  def _gcd(a, b):
      """Calculate the greatest common divisor of a and b"""
      while b:
          a, b = b, a % b
      return a
  
  def _lcm(a, b):
      return a // _gcd(a, b) * b
  
  # Exception used in shares_memory()
  class TooHardError(RuntimeError):
      pass
  
  class AxisError(ValueError, IndexError):
      """ Axis supplied was invalid. """
      def __init__(self, axis, ndim=None, msg_prefix=None):
          # single-argument form just delegates to base class
          if ndim is None and msg_prefix is None:
              msg = axis
  
          # do the string formatting here, to save work in the C code
          else:
              msg = ("axis {} is out of bounds for array of dimension {}"
                     .format(axis, ndim))
              if msg_prefix is not None:
                  msg = "{}: {}".format(msg_prefix, msg)
  
          super(AxisError, self).__init__(msg)
  
  
  def array_ufunc_errmsg_formatter(dummy, ufunc, method, *inputs, **kwargs):
      """ Format the error message for when __array_ufunc__ gives up. """
      args_string = ', '.join(['{!r}'.format(arg) for arg in inputs] +
                              ['{}={!r}'.format(k, v)
                               for k, v in kwargs.items()])
      args = inputs + kwargs.get('out', ())
      types_string = ', '.join(repr(type(arg).__name__) for arg in args)
      return ('operand type(s) all returned NotImplemented from '
              '__array_ufunc__({!r}, {!r}, {}): {}'
              .format(ufunc, method, args_string, types_string))
  
  
  def _ufunc_doc_signature_formatter(ufunc):
      """
      Builds a signature string which resembles PEP 457
  
      This is used to construct the first line of the docstring
      """
  
      # input arguments are simple
      if ufunc.nin == 1:
          in_args = 'x'
      else:
          in_args = ', '.join('x{}'.format(i+1) for i in range(ufunc.nin))
  
      # output arguments are both keyword or positional
      if ufunc.nout == 0:
          out_args = ', /, out=()'
      elif ufunc.nout == 1:
          out_args = ', /, out=None'
      else:
          out_args = '[, {positional}], / [, out={default}]'.format(
              positional=', '.join(
                  'out{}'.format(i+1) for i in range(ufunc.nout)),
              default=repr((None,)*ufunc.nout)
          )
  
      # keyword only args depend on whether this is a gufunc
      kwargs = (
          ", casting='same_kind'"
          ", order='K'"
          ", dtype=None"
          ", subok=True"
          "[, signature"
          ", extobj]"
      )
      if ufunc.signature is None:
          kwargs = ", where=True" + kwargs
  
      # join all the parts together
      return '{name}({in_args}{out_args}, *{kwargs})'.format(
          name=ufunc.__name__,
          in_args=in_args,
          out_args=out_args,
          kwargs=kwargs
      )
  
  
  def _is_from_ctypes(obj):
      # determine if an object comes from ctypes, in order to work around
      # a bug in the buffer protocol for those objects, bpo-10746
      try:
          # ctypes class are new-style, so have an __mro__. This probably fails
          # for ctypes classes with multiple inheritance.
          ctype_base = type(obj).__mro__[-2]
          # right now, they're part of the _ctypes module
          return 'ctypes' in ctype_base.__module__
      except Exception:
          return False
  