"""Number-sequence generators for use by SeqGen class"""
#import psyco
#psyco.full()

from itertools import combinations as _combs

def _partitions(cardinal):
    "Produce number partitions"
    if  cardinal == 0:
        yield []
        return
    for part in _partitions(cardinal-1):
        yield [1] + part
        if part and (len(part) < 2 or part[1] > part[0]):
            yield [part[0] + 1] + part[1:]

def _reverse(seq, start, end):
    """seq = seq[:start] + reversed(seq[start:end]) + seq[end:]"""
    end -= 1
    if end <= start:
        return
    while True:
        seq[start], seq[end] = seq[end], seq[start]
        if start == end or start+1 == end:
            return
        start += 1
        end -= 1

def _next_permutation(seq):
    "Like C++ std::next_permutation() but implemented as generator."
    first = 0
    last = len(seq)
    yield seq
    if last == 1:
        raise StopIteration
    while True:
        next0 = last - 1
        while True:
            # Step 1.
            next1 = next0
            next0 -= 1
            if seq[next0] < seq[next1]:
                # Step 2.
                mid = last - 1
                while seq[next0] >= seq[mid]:
                    mid -= 1
                seq[next0], seq[mid] = seq[mid], seq[next0]
                # Step 3.
                _reverse(seq, next1, last)
                # Change to yield references to get rid of
                # (at worst) |seq|! copy operations.
                yield seq
                break
            if next0 == first:
                raise StopIteration
    raise StopIteration

def full(cardinal, flag=None):
    "Get all permutations"
    for part in _partitions(cardinal):
        for perm in _next_permutation(part):
            seq = [0]
            for i in perm[:-1]:
                seq.append(seq[-1] + i)
            yield seq
            #If this result fails a test based on its parent partition,
            #this flag is set and we move on to the next partition
            if flag:
                break

def combs(cardinal):
    "Yield all combinations of a range"
    yield [0]
    cardrange = list(range(1, cardinal))
    for num in cardrange:
        for comb in _combs(cardrange, num):
            yield [0] + list(comb)

def randgen(cardinal):
    """Random pcsets"""
    from random import randint
    while 1:
        seq = []
        for _ in range(randint(1, cardinal)):
            num = randint(0, cardinal - 1)
            while num in seq:
                num = randint(0, cardinal - 1)
            seq.append(num)
        yield seq
