nodes: 0, module; 1, function_definition; 2, function_name:weighted_rand; 3, parameters; 4, block; 5, identifier:weights; 6, default_parameter; 7, expression_statement; 8, comment:# If just one weight is passed, simply return the weight's name; 9, if_statement; 10, comment:# Is there a way to do this more efficiently? Maybe even require that; 11, comment:# ``weights`` already be sorted?; 12, expression_statement; 13, expression_statement; 14, expression_statement; 15, expression_statement; 16, expression_statement; 17, comment:# Roll random numbers until a valid one is found; 18, expression_statement; 19, while_statement; 20, identifier:round_result; 21, False; 22, comment:"""
      Generate a non-uniform random value based on a list of weight tuples.
  
      Treats weights as coordinates for a probability distribution curve and
      rolls accordingly. Constructs a piece-wise linear curve according to
      coordinates given in ``weights`` and rolls random values in the
      curve's bounding box until a value is found under the curve
  
      Weight tuples should be of the form: (outcome, strength).
  
      Args:
          weights: (list): the list of weights where each weight
              is a tuple of form ``(float, float)`` corresponding to
              ``(outcome, strength)``.
              Weights with strength ``0`` or less will have no chance to be
              rolled. The list must be sorted in increasing order of outcomes.
          round_result (bool): Whether or not to round the resulting value
              to the nearest integer.
  
      Returns:
          float: A weighted random number
  
          int: A weighted random number rounded to the nearest ``int``
  
      Example:
          >>> weighted_rand([(-3, 4), (0, 10), (5, 1)])          # doctest: +SKIP
          -0.650612268193731
          >>> weighted_rand([(-3, 4), (0, 10), (5, 1)])          # doctest: +SKIP
          -2
      """; 23, comparison_operator:len(weights) == 1; 24, block; 25, assignment; 26, assignment; 27, assignment; 28, assignment; 29, assignment; 30, assignment; 31, comparison_operator:attempt_count < 500000; 32, comment:# Get sample point; 33, block; 34, else_clause; 35, call; 36, integer:1; 37, return_statement; 38, identifier:weights; 39, call; 40, identifier:x_min; 41, subscript; 42, identifier:x_max; 43, subscript; 44, identifier:y_min; 45, integer:0; 46, identifier:y_max; 47, call; 48, identifier:attempt_count; 49, integer:0; 50, identifier:attempt_count; 51, integer:500000; 52, expression_statement; 53, if_statement; 54, expression_statement; 55, block; 56, identifier:len; 57, argument_list; 58, subscript; 59, identifier:sorted; 60, argument_list; 61, subscript; 62, integer:0; 63, subscript; 64, integer:0; 65, identifier:max; 66, argument_list; 67, assignment; 68, call; 69, comment:# The sample point is under the curve; 70, block; 71, augmented_assignment; 72, expression_statement; 73, return_statement; 74, identifier:weights; 75, subscript; 76, integer:0; 77, identifier:weights; 78, keyword_argument; 79, identifier:weights; 80, integer:0; 81, identifier:weights; 82, unary_operator; 83, list_comprehension; 84, identifier:sample; 85, tuple; 86, identifier:_point_under_curve; 87, argument_list; 88, if_statement; 89, identifier:attempt_count; 90, integer:1; 91, call; 92, subscript; 93, identifier:weights; 94, integer:0; 95, identifier:key; 96, lambda; 97, integer:1; 98, subscript; 99, for_in_clause; 100, call; 101, call; 102, identifier:weights; 103, identifier:sample; 104, identifier:round_result; 105, block; 106, else_clause; 107, attribute; 108, argument_list; 109, call; 110, integer:0; 111, lambda_parameters; 112, subscript; 113, identifier:point; 114, integer:1; 115, identifier:point; 116, identifier:weights; 117, attribute; 118, argument_list; 119, attribute; 120, argument_list; 121, return_statement; 122, block; 123, identifier:warnings; 124, identifier:warn; 125, concatenated_string; 126, attribute; 127, argument_list; 128, identifier:w; 129, identifier:w; 130, integer:0; 131, identifier:random; 132, identifier:uniform; 133, identifier:x_min; 134, identifier:x_max; 135, identifier:random; 136, identifier:uniform; 137, identifier:y_min; 138, identifier:y_max; 139, call; 140, return_statement; 141, string; 142, string; 143, string; 144, identifier:random; 145, identifier:choice; 146, identifier:weights; 147, identifier:int; 148, argument_list; 149, subscript; 150, string_content:Point not being found in weighted_rand() after 500000; 151, string_content:attempts, defaulting to a random weight point.; 152, string_content:If this happens often, it is probably a bug.; 153, call; 154, identifier:sample; 155, integer:0; 156, identifier:round; 157, argument_list; 158, subscript; 159, identifier:sample; 160, integer:0
code: def weighted_rand(weights, round_result=False):
      """
      Generate a non-uniform random value based on a list of weight tuples.
  
      Treats weights as coordinates for a probability distribution curve and
      rolls accordingly. Constructs a piece-wise linear curve according to
      coordinates given in ``weights`` and rolls random values in the
      curve's bounding box until a value is found under the curve
  
      Weight tuples should be of the form: (outcome, strength).
  
      Args:
          weights: (list): the list of weights where each weight
              is a tuple of form ``(float, float)`` corresponding to
              ``(outcome, strength)``.
              Weights with strength ``0`` or less will have no chance to be
              rolled. The list must be sorted in increasing order of outcomes.
          round_result (bool): Whether or not to round the resulting value
              to the nearest integer.
  
      Returns:
          float: A weighted random number
  
          int: A weighted random number rounded to the nearest ``int``
  
      Example:
          >>> weighted_rand([(-3, 4), (0, 10), (5, 1)])          # doctest: +SKIP
          -0.650612268193731
          >>> weighted_rand([(-3, 4), (0, 10), (5, 1)])          # doctest: +SKIP
          -2
      """
      # If just one weight is passed, simply return the weight's name
      if len(weights) == 1:
          return weights[0][0]
  
      # Is there a way to do this more efficiently? Maybe even require that
      # ``weights`` already be sorted?
      weights = sorted(weights, key=lambda w: w[0])
  
      x_min = weights[0][0]
      x_max = weights[-1][0]
      y_min = 0
      y_max = max([point[1] for point in weights])
  
      # Roll random numbers until a valid one is found
      attempt_count = 0
      while attempt_count < 500000:
          # Get sample point
          sample = (random.uniform(x_min, x_max), random.uniform(y_min, y_max))
          if _point_under_curve(weights, sample):
              # The sample point is under the curve
              if round_result:
                  return int(round(sample[0]))
              else:
                  return sample[0]
          attempt_count += 1
      else:
          warnings.warn(
               'Point not being found in weighted_rand() after 500000 '
               'attempts, defaulting to a random weight point. '
               'If this happens often, it is probably a bug.')
          return random.choice(weights)[0]