text: import struct
  
  import six
  from builtins import range
  from past.builtins import map
  
  from .base_primitive import BasePrimitive
  from .. import helpers
  from ..constants import LITTLE_ENDIAN
  
  
  def binary_string_to_int(binary):
      """
      Convert a binary string to a decimal number.
  
      @type  binary: str
      @param binary: Binary string
  
      @rtype:  int
      @return: Converted bit string
      """
  
      return int(binary, 2)
  
  
  def int_to_binary_string(number, bit_width):
      """
      Convert a number to a binary string.
  
      @type  number:    int
      @param number:    (Optional, def=self._value) Number to convert
      @type  bit_width: int
      @param bit_width: (Optional, def=self.width) Width of bit string
  
      @rtype:  str
      @return: Bit string
      """
      return "".join(map(lambda x: str((number >> x) & 1), range(bit_width - 1, -1, -1)))
  
  
  class BitField(BasePrimitive):
      def __init__(
          self,
          value,
          width,
          max_num=None,
          endian=LITTLE_ENDIAN,
          output_format="binary",
          signed=False,
          full_range=False,
          fuzzable=True,
          name=None,
      ):
          """
          The bit field primitive represents a number of variable length and is used to define all other integer types.
  
          @type  value:         int
          @param value:         Default integer value
          @type  width:         int
          @param width:         Width of bit fields
          @type  max_num:       int
          @param max_num:       Maximum number to iterate up to
          @type  endian:        chr
          @param endian:        (Optional, def=LITTLE_ENDIAN) Endianess of the bit field (LITTLE_ENDIAN: <, BIG_ENDIAN: >)
          @type  output_format: str
          @param output_format: (Optional, def=binary) Output format, "binary" or "ascii"
          @type  signed:        bool
          @param signed:        (Optional, def=False) Make size signed vs. unsigned (applicable only with format="ascii")
          @type  full_range:    bool
          @param full_range:    (Optional, def=False) If enabled the field mutates through *all* possible values.
          @type  fuzzable:      bool
          @param fuzzable:      (Optional, def=True) Enable/disable fuzzing of this primitive
          @type  name:          str
          @param name:          (Optional, def=None) Specifying a name gives you direct access to a primitive
          """
  
          super(BitField, self).__init__()
  
          assert isinstance(value, (six.integer_types, list, tuple)), "value must be an integer, list, or tuple!"
          assert isinstance(width, six.integer_types), "width must be an integer!"
  
          self._value = self._original_value = value
          self.width = width
          self.max_num = max_num
          self.endian = endian
          self.format = output_format
          self.signed = signed
          self.full_range = full_range
          self._fuzzable = fuzzable
          self._name = name
          self.cyclic_index = 0  # when cycling through non-mutating values
  
          if not self.max_num:
              self.max_num = binary_string_to_int("1" + "0" * width)
  
          assert isinstance(self.max_num, six.integer_types), "max_num must be an integer!"
  
          if self.full_range:
              # add all possible values.
              for i in range(0, self.max_num):
                  self._fuzz_library.append(i)
          else:
              if isinstance(value, (list, tuple)):
                  # Use the supplied values as the fuzz library.
                  for val in iter(value):
                      self._fuzz_library.append(val)
  
                  # Use the first value of the supplied values as the default value if it exists, 0 else.
                  val = 0 if len(value) == 0 else value[0]
                  self._value = self._original_value = val
              else:
                  # try only "smart" values.
                  self.add_integer_boundaries(0)
                  self.add_integer_boundaries(self.max_num // 2)
                  self.add_integer_boundaries(self.max_num // 3)
                  self.add_integer_boundaries(self.max_num // 4)
                  self.add_integer_boundaries(self.max_num // 8)
                  self.add_integer_boundaries(self.max_num // 16)
                  self.add_integer_boundaries(self.max_num // 32)
                  self.add_integer_boundaries(self.max_num)
  
              # TODO: Add injectable arbitrary bit fields
  
      @property
      def name(self):
          return self._name
  
      def add_integer_boundaries(self, integer):
          """
          Add the supplied integer and border cases to the integer fuzz heuristics library.
  
          @type  integer: int
          @param integer: int to append to fuzz heuristics
          """
          for i in range(-10, 10):
              case = integer + i
              # ensure the border case falls within the valid range for this field.
              if 0 <= case < self.max_num:
                  if case not in self._fuzz_library:
                      self._fuzz_library.append(case)
  
      def _render(self, value):
          temp = self.render_int(
              value, output_format=self.format, bit_width=self.width, endian=self.endian, signed=self.signed
          )
          return helpers.str_to_bytes(temp)
  
      @staticmethod
      def render_int(value, output_format, bit_width, endian, signed):
          """
          Convert value to a bit or byte string.
  
          Args:
              value (int): Value to convert to a byte string.
              output_format (str): "binary" or "ascii"
              bit_width (int): Width of output in bits.
              endian: BIG_ENDIAN or LITTLE_ENDIAN
              signed (bool):
  
          Returns:
              str: value converted to a byte string
          """
          if output_format == "binary":
              bit_stream = ""
              rendered = b""
  
              # pad the bit stream to the next byte boundary.
              if bit_width % 8 == 0:
                  bit_stream += int_to_binary_string(value, bit_width)
              else:
                  bit_stream = "0" * (8 - (bit_width % 8))
                  bit_stream += int_to_binary_string(value, bit_width)
  
              # convert the bit stream from a string of bits into raw bytes.
              for i in range(len(bit_stream) // 8):
                  chunk_min = 8 * i
                  chunk_max = chunk_min + 8
                  chunk = bit_stream[chunk_min:chunk_max]
                  rendered += struct.pack("B", binary_string_to_int(chunk))
  
              # if necessary, convert the endianness of the raw bytes.
              if endian == LITTLE_ENDIAN:
                  # reverse the bytes
                  rendered = rendered[::-1]
  
              _rendered = rendered
          else:
              # Otherwise we have ascii/something else
              # if the sign flag is raised and we are dealing with a signed integer (first bit is 1).
              if signed and int_to_binary_string(value, bit_width)[0] == "1":
                  max_num = binary_string_to_int("1" + "0" * (bit_width - 1))
                  # chop off the sign bit.
                  val = value & binary_string_to_int("1" * (bit_width - 1))
  
                  # account for the fact that the negative scale works backwards.
                  val = max_num - val - 1
  
                  # toss in the negative sign.
                  _rendered = "%d" % ~val
  
              # unsigned integer or positive signed integer.
              else:
                  _rendered = "%d" % value
          return _rendered
  
      def __len__(self):
          return len(self._render(self._value))
  
      def __bool__(self):
          """
          Make sure instances evaluate to True even if __len__ is zero.
  
          :return: True
          """
          return True
  