import Crypto.Random.random as rand
import itertools
import math #for log
import sys
def decomposition(i):
    #from stack exchange, don't think it's uniform
    while i > 0:
        n = rand.randint(1, i)
        yield n
        i -= n

def Decomposition(i):
    while True:
        l = list(decomposition(i))
        if len(set(l)) == len(l):
            return l

def decomposition2(n, s, d, k):
    #home-brewed, returns no duplicates, includes the number d
    s = s - 1
    n = n
    while True:
        a = [d]
        nn = n
        #a.append(d)
        for i in range(0, s):
            a.append(rand.randint(0, n))
        a.sort()
        #print("a", a)
        b = []
        c = []
        while len(a) > 0:
            t = a.pop()
            #print(t, a)
            if t >= d:
                b.append(nn - t)
            else:
                c.append(nn - t)
            nn = t
        c.append(nn)
        tot = b[:] + c[:]
        #print("b", b)
        if sum(set(tot)) == n and len(c) > int(k):
            return sorted(c), sorted(b)

def decomposition3(n, s, d, k):
    #a combination of both methods, designed to get some smaller values
    send, change = decomposition2(n, s, d, k)
    for i in send:
        if i > n / s:
            send.remove(i)
            send = send + list(Decomposition(i))
    for i in change:
        if i > n / (s - 1):
            change.remove(i)
            change = change + list(Decomposition(i))
    return send, change

def divv(l, m):
    return [a /float( m) for a in l]

def frexp10(x): 
    exp = int(math.log10(x)) 
    return x / 10**exp, exp 


def decideAmounts(totalInputs, toSend, Partitions, k, fuzz):
    #fuzz is an optional amount to fuzz the transaction by
    #so if you start with a big obvious number like 2000, it might be fuzzed by up to "fuzz" amount
    fz = rand.randint(0, int(fuzz * 1000) ) / 1000.0
    toSend += fz


    g, ii =frexp10(totalInputs)
    ii = 10 ** (-1 * min(ii - 2, 0))
    print("ii", ii)
    M = 10 ** (int(math.log(2 ** Partitions) / math.log(10))) * ii
    #M = 10 ** M
    print("multiplier:", M)
    totalInputs = int(totalInputs *  M)
    toSend = int(toSend * M)
    change = totalInputs - toSend
    send_amounts, change_amounts = decomposition3(totalInputs, Partitions, toSend, k)
    all_amounts = send_amounts[:] + change_amounts[:]
    rand.shuffle(all_amounts)
    print("")
    print("change amounts:", divv(change_amounts, M))
    print("send amounts:", divv(send_amounts, M))
    print("now from the following, how much is sent?")
    print("all amounts:", sorted(divv(all_amounts, M)))
    print("possible sent amounts:")
    amounts = []
    for L in range(0, len(all_amounts)+1):
        for subset in itertools.combinations(all_amounts, L):
            amounts.append(sum(subset))

    print("number of possible sent amounts:")
    print(len(amounts))
    print("2^N:", 2 ** len(all_amounts))
    print("number of possible sent amounts duplicates removed:")
    print(len(list(set(amounts))))



if len(sys.argv) > 2:
    kk = 2
    parts = 7
    kk = rand.randint(1, int(parts / 4)) #how many sends to demand
    fuzz = 1
    decideAmounts(float(sys.argv[1]), float(sys.argv[2]), parts, kk, fuzz)

[(85, 'Crypto.Random.random.shuffle', 'rand.shuffle', True, 'import Crypto.Random.random as rand\n'), (8, 'Crypto.Random.random.randint', 'rand.randint', True, 'import Crypto.Random.random as rand\n'), (63, 'math.log10', 'math.log10', False, 'import math\n'), (94, 'itertools.combinations', 'itertools.combinations', False, 'import itertools\n'), (27, 'Crypto.Random.random.randint', 'rand.randint', True, 'import Crypto.Random.random as rand\n'), (77, 'math.log', 'math.log', False, 'import math\n'), (77, 'math.log', 'math.log', False, 'import math\n')]