import random, string
from collections import Counter, defaultdict
import operator
from itertools import groupby, chain
from timeit import timeit


def rndstr(len):
    return ''.join(random.choice(string.letters) for i in xrange(len))

def rnditem():
    return (rndstr(20), random.randint(1, 1000))

a=[("13.5",100)]
b=[("14.5",100), ("15.5", 100)]
c=[("15.5",100), ("16.5", 100)]
input=[a,b,c]


def kos(input):
    return reduce(operator.add, (Counter(dict(x)) for x in input))

def kos2(input):
    def u(a,b):
        a.update(b)
        return a
    return reduce(u, (Counter(dict(x)) for x in input))

def initial(input):
    output=defaultdict(int)
    for d in input:
        for item in d:
           output[item[0]]+=item[1]
    return dict(output)

def BigYellowCactus(input):
    input = sorted(chain(*input), key=lambda x: x[0])
    output = {}
    for k, g in groupby(input, key=lambda x: x[0]):
        output[k] = sum(x[1] for x in g)
    return output

def merge_with(dicts, fn=lambda x, y: x + y):
    res = dict(dicts[0])
    for dic in dicts[1:]:
        for key, val in dic:
            try:
                res[key] = fn(res[key], val)
	    except KeyError:
        	res[key] = val
    return res

astynax = merge_with

def astynax2(input):
    res = defaultdict(int)
    for k, v in chain(*input):
        res[k] += v
    return res

dim = 80
input = [[rnditem() for i in xrange(dim)] for j in xrange(dim)]

results = [(k.func_name, timeit(lambda: k(input), number=3)) for k in (
    kos, kos2, initial, BigYellowCactus, astynax, astynax2)]
results.sort(key=operator.itemgetter(1))
print '\n'.join(str.format('{:20}{}', *x) for x in results)
