text: """Arbitrary-precision integer primitive ops.
  
  These mostly operate on (usually) unboxed integers that use a tagged pointer
  representation (CPyTagged) and correspond to the Python 'int' type.
  
  See also the documentation for mypyc.rtypes.int_rprimitive.
  
  Use mypyc.ir.ops.IntOp for operations on fixed-width/C integers.
  """
  
  from typing import Dict, NamedTuple
  from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ComparisonOp
  from mypyc.ir.rtypes import (
      int_rprimitive, bool_rprimitive, float_rprimitive, object_rprimitive,
      str_rprimitive, bit_rprimitive, RType
  )
  from mypyc.primitives.registry import (
      load_address_op, c_unary_op, CFunctionDescription, function_op, binary_op, custom_op
  )
  
  # These int constructors produce object_rprimitives that then need to be unboxed
  # I guess unboxing ourselves would save a check and branch though?
  
  # Get the type object for 'builtins.int'.
  # For ordinary calls to int() we use a load_address to the type
  load_address_op(
      name='builtins.int',
      type=object_rprimitive,
      src='PyLong_Type')
  
  # Convert from a float to int. We could do a bit better directly.
  function_op(
      name='builtins.int',
      arg_types=[float_rprimitive],
      return_type=object_rprimitive,
      c_function_name='CPyLong_FromFloat',
      error_kind=ERR_MAGIC)
  
  # int(string)
  function_op(
      name='builtins.int',
      arg_types=[str_rprimitive],
      return_type=object_rprimitive,
      c_function_name='CPyLong_FromStr',
      error_kind=ERR_MAGIC)
  
  # int(string, base)
  function_op(
      name='builtins.int',
      arg_types=[str_rprimitive, int_rprimitive],
      return_type=object_rprimitive,
      c_function_name='CPyLong_FromStrWithBase',
      error_kind=ERR_MAGIC)
  
  # str(n) on ints
  function_op(
      name='builtins.str',
      arg_types=[int_rprimitive],
      return_type=str_rprimitive,
      c_function_name='CPyTagged_Str',
      error_kind=ERR_MAGIC,
      priority=2)
  
  # We need a specialization for str on bools also since the int one is wrong...
  function_op(
      name='builtins.str',
      arg_types=[bool_rprimitive],
      return_type=str_rprimitive,
      c_function_name='CPyBool_Str',
      error_kind=ERR_MAGIC,
      priority=3)
  
  
  def int_binary_op(name: str, c_function_name: str,
                    return_type: RType = int_rprimitive,
                    error_kind: int = ERR_NEVER) -> None:
      binary_op(name=name,
                arg_types=[int_rprimitive, int_rprimitive],
                return_type=return_type,
                c_function_name=c_function_name,
                error_kind=error_kind)
  
  
  # Binary, unary and augmented assignment operations that operate on CPyTagged ints
  # are implemented as C functions.
  
  int_binary_op('+', 'CPyTagged_Add')
  int_binary_op('-', 'CPyTagged_Subtract')
  int_binary_op('*', 'CPyTagged_Multiply')
  int_binary_op('&', 'CPyTagged_And')
  int_binary_op('|', 'CPyTagged_Or')
  int_binary_op('^', 'CPyTagged_Xor')
  # Divide and remainder we honestly propagate errors from because they
  # can raise ZeroDivisionError
  int_binary_op('//', 'CPyTagged_FloorDivide', error_kind=ERR_MAGIC)
  int_binary_op('%', 'CPyTagged_Remainder', error_kind=ERR_MAGIC)
  # Negative shift counts raise an exception
  int_binary_op('>>', 'CPyTagged_Rshift', error_kind=ERR_MAGIC)
  int_binary_op('<<', 'CPyTagged_Lshift', error_kind=ERR_MAGIC)
  
  # This should work because assignment operators are parsed differently
  # and the code in irbuild that handles it does the assignment
  # regardless of whether or not the operator works in place anyway.
  int_binary_op('+=', 'CPyTagged_Add')
  int_binary_op('-=', 'CPyTagged_Subtract')
  int_binary_op('*=', 'CPyTagged_Multiply')
  int_binary_op('&=', 'CPyTagged_And')
  int_binary_op('|=', 'CPyTagged_Or')
  int_binary_op('^=', 'CPyTagged_Xor')
  int_binary_op('//=', 'CPyTagged_FloorDivide', error_kind=ERR_MAGIC)
  int_binary_op('%=', 'CPyTagged_Remainder', error_kind=ERR_MAGIC)
  int_binary_op('>>=', 'CPyTagged_Rshift', error_kind=ERR_MAGIC)
  int_binary_op('<<=', 'CPyTagged_Lshift', error_kind=ERR_MAGIC)
  
  
  def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription:
      return c_unary_op(name=name,
                        arg_type=int_rprimitive,
                        return_type=int_rprimitive,
                        c_function_name=c_function_name,
                        error_kind=ERR_NEVER)
  
  
  int_neg_op = int_unary_op('-', 'CPyTagged_Negate')
  int_invert_op = int_unary_op('~', 'CPyTagged_Invert')
  
  # Primitives related to integer comparison operations:
  
  # Description for building int comparison ops
  #
  # Fields:
  #   binary_op_variant: identify which IntOp to use when operands are short integers
  #   c_func_description: the C function to call when operands are tagged integers
  #   c_func_negated: whether to negate the C function call's result
  #   c_func_swap_operands: whether to swap lhs and rhs when call the function
  IntComparisonOpDescription = NamedTuple(
      'IntComparisonOpDescription',  [('binary_op_variant', int),
                                      ('c_func_description', CFunctionDescription),
                                      ('c_func_negated', bool),
                                      ('c_func_swap_operands', bool)])
  
  # Equals operation on two boxed tagged integers
  int_equal_ = custom_op(
      arg_types=[int_rprimitive, int_rprimitive],
      return_type=bit_rprimitive,
      c_function_name='CPyTagged_IsEq_',
      error_kind=ERR_NEVER)
  
  # Less than operation on two boxed tagged integers
  int_less_than_ = custom_op(
      arg_types=[int_rprimitive, int_rprimitive],
      return_type=bit_rprimitive,
      c_function_name='CPyTagged_IsLt_',
      error_kind=ERR_NEVER)
  
  # Provide mapping from textual op to short int's op variant and boxed int's description.
  # Note that these are not complete implementations and require extra IR.
  int_comparison_op_mapping = {
      '==': IntComparisonOpDescription(ComparisonOp.EQ, int_equal_, False, False),
      '!=': IntComparisonOpDescription(ComparisonOp.NEQ, int_equal_, True, False),
      '<': IntComparisonOpDescription(ComparisonOp.SLT, int_less_than_, False, False),
      '<=': IntComparisonOpDescription(ComparisonOp.SLE, int_less_than_, True, True),
      '>': IntComparisonOpDescription(ComparisonOp.SGT, int_less_than_, False, True),
      '>=': IntComparisonOpDescription(ComparisonOp.SGE, int_less_than_, True, False),
  }  # type: Dict[str, IntComparisonOpDescription]
  