import copy


def uniform_crossover(random, mom, dad, args):
    """Return the offspring of the uniform crossover on the candidate.
    Based on two candidates (parents) build 2 children:
    - elements present in both parents will be present in both children;
    - both children have at least one element;
    - elements present in only one parent have equal probability to be
    present in child 1 or child 2 (after each child has at least one element).

    Args:
        random : the random number generator object
        mom : the first parent candidate
        dad : the second parent candidate
        args : a dictionary of keyword arguments

    Returns:
        Return the offspring of the candidates given as argument.

    Notes:
        Optional keyword arguments in args:
        - *crossover_rate* -- the rate at which crossover is performed (default 1.0)
    """
    crossRate = args.setdefault("crossover_rate", 1.0)
    children = []
    if random.random() > crossRate or (len(mom) == 1 and len(dad) == 1):
        children.append(mom)
        children.append(dad)
        return children

    maxSize = args["candidate_max_size"]
    intersection = mom & dad
    otherElems = list((mom | dad).difference(intersection))
    child1 = copy.copy(intersection)
    child2 = copy.copy(intersection)

    while len(otherElems) > 0:
        elemPosition = random.randint(0, len(otherElems) - 1) if len(otherElems) > 1 else 0
        if len(child1) == maxSize or len(child2) == 0:
            child2.add(otherElems[elemPosition])
        elif len(child2) == maxSize or len(child1) == 0:
            child1.add(otherElems[elemPosition])
        else:
            r = random.random()
            if r <= 0.5:
                child1.add(otherElems[elemPosition])
            else:
                child2.add(otherElems[elemPosition])

        otherElems.pop(elemPosition)

    children.append(child1)
    children.append(child2)
    return children