code: def pop_symbols(
      compressed_message: CrayCompressedMessage,
      cfs: int,
      cdf_low: int,
      cdf_high: int,
      precision: int,
      allow_empty_pops: bool = False,
  ) -> CrayCompressedMessage:
      """
      Pops information corresponding to single symbol from the top of the stack.
  
      Args:
          compressed_message: Input stack to have data popped from.
          cfs: The CDF symbol value.
          cdf_low: The low CDF value, e.g., ``cdf[symbol]``.
          cdf_high: The high CDF, e.g., ``cdf[symbol+1]``.
          precision: Operating precision for pop operations.
  
      Returns:
          Compressed message with information content popped from top of stack.
      """
      frequencies = cdf_high - cdf_low
      head = jnp.array(
          frequencies * (compressed_message[0] >> precision)
          + cfs
          - cdf_low,
          dtype=HEAD_DTYPE,
      )
      tail = compressed_message[1]
  
      # Why range over three you may ask?
      # The answer is to vectorize. In the pop function normally this is done
      # with a while loop that would renormalize the range in steps of 8 bits.
      # Since we're using 32 bits for the head the increments can come at levels
      # of 8, 16, or 24, depending on the starting value of the head.
  
      for _ in range(3):
          indices = jnp.less(head, HEAD_MIN)
          tail, new_head = tail_pop(tail, indices, allow_empty_pops)
          head = jnp.where(
              indices, head << TAIL_PRECISION | new_head, head
          )
  
      return CrayCompressedMessage(head.astype(HEAD_DTYPE), tail)
  
imports: from typing import NamedTuple, Sequence, Tuple
  import jax.lax
  import jax.numpy
  from jax.numpy import ndarray
  from jax.random import PRNGKey, randint
label_desc: Use image node_id 6 for calling a global function with example usage: pop_symbols(compressed_message, cfs, cdf_low, cdf_high, precision, allow_empty_pops) and returns: CrayCompressedMessage