docs: def block_similarity(self, block_a, block_b):
          """
          :param block_a: The first block address.
          :param block_b: The second block address.
          :returns:       The similarity of the basic blocks, normalized for the base address of the block and function
                          call addresses.
          """
  
          # handle sim procedure blocks
          if self._project_a.is_hooked(block_a) and self._project_b.is_hooked(block_b):
              if self._project_a._sim_procedures[block_a] == self._project_b._sim_procedures[block_b]:
                  return 1.0
              else:
                  return 0.0
  
          try:
              block_a = NormalizedBlock(block_a, self._function_a)
          except (SimMemoryError, SimEngineError):
              block_a = None
  
          try:
              block_b = NormalizedBlock(block_b, self._function_b)
          except (SimMemoryError, SimEngineError):
              block_b = None
  
          # if both were None then they are assumed to be the same, if only one was the same they are assumed to differ
          if block_a is None and block_b is None:
              return 1.0
          elif block_a is None or block_b is None:
              return 0.0
  
          # get all elements for computing similarity
          tags_a = [s.tag for s in block_a.statements]
          tags_b = [s.tag for s in block_b.statements]
          consts_a = [c.value for c in block_a.all_constants]
          consts_b = [c.value for c in block_b.all_constants]
          all_registers_a = [s.offset for s in block_a.statements if hasattr(s, "offset")]
          all_registers_b = [s.offset for s in block_b.statements if hasattr(s, "offset")]
          jumpkind_a = block_a.jumpkind
          jumpkind_b = block_b.jumpkind
  
          # compute total distance
          total_dist = 0
          total_dist += _levenshtein_distance(tags_a, tags_b)
          total_dist += _levenshtein_distance(block_a.operations, block_b.operations)
          total_dist += _levenshtein_distance(all_registers_a, all_registers_b)
          acceptable_differences = self._get_acceptable_constant_differences(block_a, block_b)
          total_dist += _normalized_levenshtein_distance(consts_a, consts_b, acceptable_differences)
          total_dist += 0 if jumpkind_a == jumpkind_b else 1
  
          # compute similarity
          num_values = max(len(tags_a), len(tags_b))
          num_values += max(len(consts_a), len(consts_b))
          num_values += max(len(block_a.operations), len(block_b.operations))
          num_values += 1  # jumpkind
          similarity = 1 - (float(total_dist) / num_values)
  
          return similarity