nodes: 0, module; 1, function_definition; 2, function_name:key_periods; 3, parameters; 4, block; 5, identifier:ciphertext; 6, identifier:max_key_period; 7, expression_statement; 8, if_statement; 9, expression_statement; 10, for_statement; 11, return_statement; 12, comment:"""Rank all key periods for ``ciphertext`` up to and including ``max_key_period``
  
      Example:
          >>> key_periods(ciphertext, 30)
          [2, 4, 8, 3, ...]
  
      Args:
          ciphertext (str): The text to analyze
          max_key_period (int): The maximum period the key could be
  
      Returns:
          Sorted list of keys
  
      Raises:
          ValueError: If max_key_period is less than or equal to 0
      """; 13, comparison_operator:max_key_period <= 0; 14, block; 15, assignment; 16, identifier:period; 17, call; 18, block; 19, list_comprehension; 20, identifier:max_key_period; 21, integer:0; 22, raise_statement; 23, identifier:key_scores; 24, list; 25, identifier:range; 26, argument_list; 27, expression_statement; 28, expression_statement; 29, subscript; 30, for_in_clause; 31, call; 32, integer:1; 33, binary_operator:min(max_key_period, len(ciphertext)) + 1; 34, assignment; 35, call; 36, identifier:p; 37, integer:0; 38, identifier:p; 39, call; 40, identifier:ValueError; 41, argument_list; 42, call; 43, integer:1; 44, identifier:score; 45, call; 46, attribute; 47, argument_list; 48, identifier:sorted; 49, argument_list; 50, string:"max_key_period must be a positive integer"; 51, identifier:min; 52, argument_list; 53, identifier:abs; 54, argument_list; 55, identifier:key_scores; 56, identifier:append; 57, tuple; 58, identifier:key_scores; 59, keyword_argument; 60, identifier:max_key_period; 61, call; 62, binary_operator:ENGLISH_IC - index_of_coincidence(*split_columns(ciphertext, period)); 63, identifier:period; 64, identifier:score; 65, identifier:key; 66, lambda; 67, identifier:len; 68, argument_list; 69, identifier:ENGLISH_IC; 70, call; 71, lambda_parameters; 72, subscript; 73, identifier:ciphertext; 74, identifier:index_of_coincidence; 75, argument_list; 76, identifier:x; 77, identifier:x; 78, integer:1; 79, list_splat; 80, call; 81, identifier:split_columns; 82, argument_list; 83, identifier:ciphertext; 84, identifier:period
code: def key_periods(ciphertext, max_key_period):
      """Rank all key periods for ``ciphertext`` up to and including ``max_key_period``
  
      Example:
          >>> key_periods(ciphertext, 30)
          [2, 4, 8, 3, ...]
  
      Args:
          ciphertext (str): The text to analyze
          max_key_period (int): The maximum period the key could be
  
      Returns:
          Sorted list of keys
  
      Raises:
          ValueError: If max_key_period is less than or equal to 0
      """
      if max_key_period <= 0:
          raise ValueError("max_key_period must be a positive integer")
  
      key_scores = []
      for period in range(1, min(max_key_period, len(ciphertext)) + 1):
          score = abs(ENGLISH_IC - index_of_coincidence(*split_columns(ciphertext, period)))
          key_scores.append((period, score))
  
      return [p[0] for p in sorted(key_scores, key=lambda x: x[1])]