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, 4, 85, 29), 'Crypto.Random.random.shuffle', 'rand.shuffle', ({(85, 17, 85, 28): 'all_amounts'}, {}), '(all_amounts)', True, 'import Crypto.Random.random as rand\n'), ((8, 12, 8, 30), 'Crypto.Random.random.randint', 'rand.randint', ({(8, 25, 8, 26): '1', (8, 28, 8, 29): 'i'}, {}), '(1, i)', True, 'import Crypto.Random.random as rand\n'), ((63, 14, 63, 27), 'math.log10', 'math.log10', ({(63, 25, 63, 26): 'x'}, {}), '(x)', False, 'import math\n'), ((94, 22, 94, 60), 'itertools.combinations', 'itertools.combinations', ({(94, 45, 94, 56): 'all_amounts', (94, 58, 94, 59): 'L'}, {}), '(all_amounts, L)', False, 'import itertools\n'), ((27, 21, 27, 39), 'Crypto.Random.random.randint', 'rand.randint', ({(27, 34, 27, 35): '(0)', (27, 37, 27, 38): 'n'}, {}), '(0, n)', True, 'import Crypto.Random.random as rand\n'), ((77, 19, 77, 44), 'math.log', 'math.log', ({(77, 28, 77, 43): '(2 ** Partitions)'}, {}), '(2 ** Partitions)', False, 'import math\n'), ((77, 47, 77, 59), 'math.log', 'math.log', ({(77, 56, 77, 58): '(10)'}, {}), '(10)', False, 'import math\n')]