# your code goes here
import time

def rotate(l, n):
    return l[n:] + l[:n]

def numGroupCombs(n, nGrps, grpSize):
    result = 1

    for i in range(n, nGrps, -1):
        result *= i

    result = int(result)
    myDiv = 1

    for i in range(2, grpSize + 1):
        myDiv *= i

    result /= myDiv**nGrps
    return int(result)
    
def ComboGroups(v, nGrps, grpSize):

    if not isinstance(v, list):
        z = list(v)
    else:
        z = v.copy()

    for i in range(numGroupCombs(len(z), nGrps, grpSize)):
        yield z.copy()

        idx1 = (nGrps - 1) * grpSize - 1
        idx2 = len(z) - 1;
        last1 = (nGrps - 2) * grpSize + 1

        while (idx2 > idx1 and z[idx2] > z[idx1]):
            idx2 -= 1

        if (idx2 + 1) < len(z):
            if z[idx2 + 1] > z[idx1]:
                z[idx1], z[idx2 + 1] = z[idx2 + 1], z[idx1]
        else:
            while idx1 > 0:
                tipPnt = z[idx2]

                while (idx1 > last1 and tipPnt < z[idx1]):
                    idx1 -= 1

                if tipPnt > z[idx1]:
                    idx3 = idx1 + 1
                    z[idx3:] = sorted(z[idx3:])
                    xtr = last1 + grpSize - idx3

                    while z[idx3] < z[idx1]:
                        idx3 += 1

                    z[idx3], z[idx1] = z[idx1], z[idx3]
                    z[(idx1 + 1):(idx3 + xtr)] = rotate(z[(idx1 + 1):(idx3 + xtr)], idx3 - idx1)
                    break
                else:
                    idx1 -= 2
                    idx2 -= grpSize
                    last1 -= grpSize
                    
def example(z, nGrps, verbose = True):
    grpSize = int(len(z) / nGrps)

    if verbose:
        for a in ComboGroups(z, nGrps, grpSize):
            print([a[i:i + grpSize] for i in range(0, len(a), grpSize)])
    else:
        start = time.time()

        for a in ComboGroups(z, nGrps, grpSize):
            b = a

        end = time.time()
        print(end - start)
        
example(list(range(1, 9)), 2)
example(list(range(1, 19)), 3, False)