import random
import time
import sys
import itertools

# --------------------------------------------------------------------------------
#    U T I L I T I E S

def printf(format, *args):
    sys.stdout.write(format % args)

printLim = 100 # if array len is bigger than printLim it will not be printed

# --------------------------------------------------------------------------------
#    S I M P L E    A R R A Y   G E N E R A T O R S

def generate( arrSize ):
    random.seed(1234321)
    t1 = time.time()
    zz = list(range(arrSize)) # create list [0,1,2, ..., n-1 ]
    random.shuffle(zz)
    t2 = time.time()
    #printf("time shuffling %f\n", t2-t1  )
    if arrSize <= printLim: print(zz)
    return zz

def randomFill( arrSize, maxVal ):
    random.seed(1234321)
    t1 = time.time()
    zz = [0] * arrSize
    for i in range( arrSize ):
        zz[i] = random.randint(0, maxVal)
    random.shuffle(zz)
    t2 = time.time()
    #printf("time shuffling %f\n", t2-t1  )
    if arrSize <= printLim:
        print(zz)
    return zz


# --------------------------------------------------------------------------------
#    F I N D    T U P L E S   W I T H   S P E C I F I C  P R O P E R T I E S

def quintuplesWithSum( a, Sum ):
    t1 = time.time()
    noOfChecks = 0
    num = 0
    for i in range( 0, len(a)-4 ):
        for j in range( i+1, len(a)-3 ):
            for k in range( j+1, len(a)-2 ):
                for m in range( k+1, len(a)-1 ):
                    for n in range( m+1, len(a) ):
                        noOfChecks += 1
                        if a[i] + a[j] + a[k] + a[m] + a[n] == Sum:
                            num += 1
                            printf( "%d.  %d+%d+%d+%d+%d = %d\n", \
                                num, a[i], a[j], a[k], a[m], a[n], Sum )
    t2 = time.time()
    printf( "Found %d quintuples.\n", num )
    printf( "Total checks performed: %d \n", noOfChecks )
    print( "time:", t2-t1 )


def quadruplesWithSum( a, Sum ):
    t1 = time.time()
    num = 0
    for i in range( 0, len(a)-3 ):
        for j in range( i+1, len(a)-2 ):
            for k in range( j+1, len(a)-1 ):
                for m in range( k+1, len(a) ):
                    if a[i] + a[j] + a[k] + a[m] == Sum:
                        num += 1
                        printf( "%d.  %d+%d+%d+%d = %d\n", \
                            num, a[i], a[j], a[k], a[m], Sum )
    t2 = time.time()
    printf( "Found %d quadruples.\n", num )
    print( "time:", t2-t1 )


def triplesWithSum( arr, Sum ):
    t1 = time.time()
    num = 0
    for i in range( 0, len(arr)-2 ):
        for j in range( i+1, len(arr)-1 ):
            for k in range( j+1, len(arr) ):
                if arr[i] + arr[j] + arr[k] == Sum:
                    num += 1
                    #printf( "%d.  %d+%d+%d = %d\n", \
                    #       num, arr[i], arr[j], arr[k], Sum )
    t2 = time.time()
    printf( "Found %d triples.\n", num )
    print( "time:", t2-t1 )

def pairsWithSum( arr, Sum ):
    t1 = time.time()
    num = 0
    for i in range( 0, len(arr)-1 ):
        for j in range( i+1, len(arr) ):
                if arr[i] + arr[j] == Sum:
                    num += 1
                    #printf( "%d.  %d+%d = %d\n", \
                    # num, arr[i], arr[j],  Sum )
    t2 = time.time()
    printf( "Found %d pairs.\n", num )
    print( "time:", t2-t1 )


# a general function, accepts any tuple size
def tuplesWithSum( tupleSize, arr, Sum ):
    t1 = time.time()
    tuplesList = list( itertools.combinations( arr, tupleSize ) )
    t2 = time.time()
    howMany = 0
    for item in tuplesList:
        if sum(item) == Sum:
           howMany += 1
    t3 = time.time()
    print( 'Using itertools module:' )
    printf( "Found %d  %d-tuples.\n", howMany, tupleSize )
    print( "time - list creation  :", t2-t1 )
    print( "time - list processing:", t3-t2 )



# --------------------------------------------------------------------------------
#    A R E A   F O R   E X P E R I M E N T S
# --------------------------------------------------------------------------------
# modify the size of arrays and check how long does it take to process the array
# - to check all tuples -
#  depending on the size of the array and the complexity of the task
# - number of nested loops


if False:
    arrSize = 10
    printf( "arr size %d\n", arrSize )
    array = generate( arrSize )
    #array = [2,1,4,3,7,5,6,9,8]
    pairsWithSum( array, 10 )

if True:
    arrSize = 800
    printf( "arr size %d\n", arrSize )
    array = generate( arrSize )
    triplesWithSum( array, 22 )
    tuplesWithSum( 3, array, 22 )


if False:
    arrSize = 20
    array = generate( arrSize )
    quadruplesWithSum( array, 9 )

if False:
    arrSize = 200
    array = randomFill( arrSize, 200 )
    triplesWithSum( array, 7 )

if False:
    arrSize = 200
    array = randomFill( arrSize, 60 )
    quadruplesWithSum( array, 9 )

if False:
    arrSize = 70
    array = randomFill( arrSize, 100 )
    quintuplesWithSum( array, 480 )




