import copy


def nonuniform_mutation(random, candidate, args):
    """Return the mutants produced by nonuniform mutation on the candidates.

    The function performs nonuniform mutation as specified in
    (Michalewicz, "Genetic Algorithms + Data Structures = Evolution
    Programs," Springer, 1996). This function also makes use of the 
    bounder function as specified in the EC's ``evolve`` method.
    
    .. note::
    
       This function **requires** that *max_generations* be specified in 
       the *args* dictionary. Therefore, it is best to use this operator 
       in conjunction with the ``generation_termination`` terminator. 

    .. Arguments:
       random -- the random number generator object
       candidate -- the candidate solution
       args -- a dictionary of keyword arguments

    Required keyword arguments in args:
    
    - *max_generations* -- the maximum number of generations for which
      evolution should take place
    
    Optional keyword arguments in args:
    
    - *mutation_strength* -- the strength of the mutation, where higher
      values correspond to greater variation (default 1)
    
    """
    bounder = args['_ec'].bounder
    num_gens = args['_ec'].num_generations
    max_gens = args['max_generations']
    strength = args.setdefault('mutation_strength', 1)
    exponent = (1.0 - num_gens / float(max_gens)) ** strength
    mutant = copy.copy(candidate)
    for i, (c, lo, hi) in enumerate(zip(candidate, bounder.lower_bound, bounder.upper_bound)):
        if random.random() <= 0.5:
            new_value = c + (hi - c) * (1.0 - random.random() ** exponent)
        else:
            new_value = c - (c - lo) * (1.0 - random.random() ** exponent)
        mutant[i] = new_value
    return mutant