removed run_identifier and sped up stale counting
[p2pool.git] / p2pool / util / math.py
1 from __future__ import absolute_import, division
2
3 import __builtin__
4 import math
5 import random
6
7 def median(x, use_float=True):
8     # there exist better algorithms...
9     y = sorted(x)
10     if not y:
11         raise ValueError('empty sequence!')
12     left = (len(y) - 1)//2
13     right = len(y)//2
14     sum = y[left] + y[right]
15     if use_float:
16         return sum/2
17     else:
18         return sum//2
19
20 def shuffled(x):
21     x = list(x)
22     random.shuffle(x)
23     return x
24
25 def shift_left(n, m):
26     # python: :(
27     if m >= 0:
28         return n << m
29     return n >> -m
30
31 def clip(x, (low, high)):
32     if x < low:
33         return low
34     elif x > high:
35         return high
36     else:
37         return x
38
39 def nth(i, n=0):
40     i = iter(i)
41     for _ in xrange(n):
42         i.next()
43     return i.next()
44
45 def geometric(p):
46     if p <= 0 or p > 1:
47         raise ValueError('p must be in the interval (0.0, 1.0]')
48     if p == 1:
49         return 1
50     return int(math.log1p(-random.random()) / math.log1p(-p)) + 1
51
52 def add_dicts(*dicts):
53     res = {}
54     for d in dicts:
55         for k, v in d.iteritems():
56             res[k] = res.get(k, 0) + v
57     return dict((k, v) for k, v in res.iteritems() if v)
58
59 def format(x):
60     prefixes = 'kMGTPEZY'
61     count = 0
62     while x >= 100000 and count < len(prefixes) - 2:
63         x = x//1000
64         count += 1
65     s = '' if count == 0 else prefixes[count - 1]
66     return '%i' % (x,) + s
67
68 def perfect_round(x):
69     a, b = divmod(x, 1)
70     a2 = int(a)
71     if random.random() >= b:
72         return a2
73     else:
74         return a2 + 1
75
76 def erf(x):
77     # save the sign of x
78     sign = 1
79     if x < 0:
80         sign = -1
81     x = abs(x)
82     
83     # constants
84     a1 =  0.254829592
85     a2 = -0.284496736
86     a3 =  1.421413741
87     a4 = -1.453152027
88     a5 =  1.061405429
89     p  =  0.3275911
90     
91     # A&S formula 7.1.26
92     t = 1.0/(1.0 + p*x)
93     y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*math.exp(-x*x)
94     return sign*y # erf(-x) = -erf(x)
95
96 def ierf(z, steps=10):
97     guess = 0
98     for i in xrange(steps):
99         d = 2*math.e**(-guess**2)/math.sqrt(math.pi)
100         guess = guess - (erf(guess) - z)/d
101     return guess
102
103 def binomial_conf_interval(x, n, conf=0.95):
104     # approximate - Wilson score interval
105     z = math.sqrt(2)*ierf(conf)
106     p = x/n
107     topa = p + z**2/2/n
108     topb = z * math.sqrt(p*(1-p)/n + z**2/4/n**2)
109     bottom = 1 + z**2/n
110     return (topa - topb)/bottom, (topa + topb)/bottom
111
112 def interval_to_center_radius((low, high)):
113     return (high+low)/2, (high-low)/2
114
115 def reversed(x):
116     try:
117         return __builtin__.reversed(x)
118     except TypeError:
119         return reversed(list(x))
120
121 class Object(object):
122     def __init__(self, **kwargs):
123         for k, v in kwargs.iteritems():
124             setattr(self, k, v)
125
126 def add_tuples(res, *tuples):
127     for t in tuples:
128         if len(t) != len(res):
129             print 'tuples must all be the same length'
130         res = tuple(a + b for a, b in zip(res, t))
131     return res
132
133 if __name__ == '__main__':
134     import random
135     a = 1
136     while True:
137         print a, format(a) + 'H/s'
138         a = a * random.randrange(2, 5)