### annotate env/lib/python3.9/site-packages/boltons/mathutils.py @ 0:4f3585e2f14bdraftdefaulttip

author shellac Mon, 22 Mar 2021 18:12:50 +0000
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
shellac
parents:
diff changeset
1 """This module provides useful math functions on top of Python's
shellac
parents:
diff changeset
2 built-in :mod:`math` module.
shellac
parents:
diff changeset
3 """
shellac
parents:
diff changeset
4 from __future__ import division
shellac
parents:
diff changeset
5
shellac
parents:
diff changeset
6 from math import ceil as _ceil, floor as _floor
shellac
parents:
diff changeset
7 import bisect
shellac
parents:
diff changeset
8 import binascii
shellac
parents:
diff changeset
9
shellac
parents:
diff changeset
10
shellac
parents:
diff changeset
11 def clamp(x, lower=float('-inf'), upper=float('inf')):
shellac
parents:
diff changeset
12 """Limit a value to a given range.
shellac
parents:
diff changeset
13
shellac
parents:
diff changeset
14 Args:
shellac
parents:
diff changeset
15 x (int or float): Number to be clamped.
shellac
parents:
diff changeset
16 lower (int or float): Minimum value for x.
shellac
parents:
diff changeset
17 upper (int or float): Maximum value for x.
shellac
parents:
diff changeset
18
shellac
parents:
diff changeset
19 The returned value is guaranteed to be between *lower* and
shellac
parents:
diff changeset
20 *upper*. Integers, floats, and other comparable types can be
shellac
parents:
diff changeset
21 mixed.
shellac
parents:
diff changeset
22
shellac
parents:
diff changeset
23 >>> clamp(1.0, 0, 5)
shellac
parents:
diff changeset
24 1.0
shellac
parents:
diff changeset
25 >>> clamp(-1.0, 0, 5)
shellac
parents:
diff changeset
26 0
shellac
parents:
diff changeset
27 >>> clamp(101.0, 0, 5)
shellac
parents:
diff changeset
28 5
shellac
parents:
diff changeset
29 >>> clamp(123, upper=5)
shellac
parents:
diff changeset
30 5
shellac
parents:
diff changeset
31
shellac
parents:
diff changeset
32 Similar to `numpy's clip`_ function.
shellac
parents:
diff changeset
33
shellac
parents:
diff changeset
34 .. _numpy's clip: http://docs.scipy.org/doc/numpy/reference/generated/numpy.clip.html
shellac
parents:
diff changeset
35
shellac
parents:
diff changeset
36 """
shellac
parents:
diff changeset
37 if upper < lower:
shellac
parents:
diff changeset
38 raise ValueError('expected upper bound (%r) >= lower bound (%r)'
shellac
parents:
diff changeset
39 % (upper, lower))
shellac
parents:
diff changeset
40 return min(max(x, lower), upper)
shellac
parents:
diff changeset
41
shellac
parents:
diff changeset
42
shellac
parents:
diff changeset
43 def ceil(x, options=None):
shellac
parents:
diff changeset
44 """Return the ceiling of *x*. If *options* is set, return the smallest
shellac
parents:
diff changeset
45 integer or float from *options* that is greater than or equal to
shellac
parents:
diff changeset
46 *x*.
shellac
parents:
diff changeset
47
shellac
parents:
diff changeset
48 Args:
shellac
parents:
diff changeset
49 x (int or float): Number to be tested.
shellac
parents:
diff changeset
50 options (iterable): Optional iterable of arbitrary numbers
shellac
parents:
diff changeset
51 (ints or floats).
shellac
parents:
diff changeset
52
shellac
parents:
diff changeset
53 >>> VALID_CABLE_CSA = [1.5, 2.5, 4, 6, 10, 25, 35, 50]
shellac
parents:
diff changeset
54 >>> ceil(3.5, options=VALID_CABLE_CSA)
shellac
parents:
diff changeset
55 4
shellac
parents:
diff changeset
56 >>> ceil(4, options=VALID_CABLE_CSA)
shellac
parents:
diff changeset
57 4
shellac
parents:
diff changeset
58 """
shellac
parents:
diff changeset
59 if options is None:
shellac
parents:
diff changeset
60 return _ceil(x)
shellac
parents:
diff changeset
61 options = sorted(options)
shellac
parents:
diff changeset
62 i = bisect.bisect_left(options, x)
shellac
parents:
diff changeset
63 if i == len(options):
shellac
parents:
diff changeset
64 raise ValueError("no ceil options greater than or equal to: %r" % x)
shellac
parents:
diff changeset
65 return options[i]
shellac
parents:
diff changeset
66
shellac
parents:
diff changeset
67
shellac
parents:
diff changeset
68 def floor(x, options=None):
shellac
parents:
diff changeset
69 """Return the floor of *x*. If *options* is set, return the largest
shellac
parents:
diff changeset
70 integer or float from *options* that is less than or equal to
shellac
parents:
diff changeset
71 *x*.
shellac
parents:
diff changeset
72
shellac
parents:
diff changeset
73 Args:
shellac
parents:
diff changeset
74 x (int or float): Number to be tested.
shellac
parents:
diff changeset
75 options (iterable): Optional iterable of arbitrary numbers
shellac
parents:
diff changeset
76 (ints or floats).
shellac
parents:
diff changeset
77
shellac
parents:
diff changeset
78 >>> VALID_CABLE_CSA = [1.5, 2.5, 4, 6, 10, 25, 35, 50]
shellac
parents:
diff changeset
79 >>> floor(3.5, options=VALID_CABLE_CSA)
shellac
parents:
diff changeset
80 2.5
shellac
parents:
diff changeset
81 >>> floor(2.5, options=VALID_CABLE_CSA)
shellac
parents:
diff changeset
82 2.5
shellac
parents:
diff changeset
83
shellac
parents:
diff changeset
84 """
shellac
parents:
diff changeset
85 if options is None:
shellac
parents:
diff changeset
86 return _floor(x)
shellac
parents:
diff changeset
87 options = sorted(options)
shellac
parents:
diff changeset
88
shellac
parents:
diff changeset
89 i = bisect.bisect_right(options, x)
shellac
parents:
diff changeset
90 if not i:
shellac
parents:
diff changeset
91 raise ValueError("no floor options less than or equal to: %r" % x)
shellac
parents:
diff changeset
92 return options[i - 1]
shellac
parents:
diff changeset
93
shellac
parents:
diff changeset
94
shellac
parents:
diff changeset
95 try:
shellac
parents:
diff changeset
96 _int_types = (int, long)
shellac
parents:
diff changeset
97 bytes = str
shellac
parents:
diff changeset
98 except NameError:
shellac
parents:
diff changeset
99 # py3 has no long
shellac
parents:
diff changeset
100 _int_types = (int,)
shellac
parents:
diff changeset
101 unicode = str
shellac
parents:
diff changeset
102
shellac
parents:
diff changeset
103
shellac
parents:
diff changeset
104 class Bits(object):
shellac
parents:
diff changeset
105 '''
shellac
parents:
diff changeset
106 An immutable bit-string or bit-array object.
shellac
parents:
diff changeset
shellac
parents:
diff changeset
108 as well as bitwise masking and shifting operators.
shellac
parents:
diff changeset
109 Bits also make it easy to convert between many
shellac
parents:
diff changeset
110 different useful representations:
shellac
parents:
diff changeset
111
shellac
parents:
diff changeset
112 * bytes -- good for serializing raw binary data
shellac
parents:
diff changeset
113 * int -- good for incrementing (e.g. to try all possible values)
shellac
parents:
diff changeset
114 * list of bools -- good for iterating over or treating as flags
shellac
parents:
diff changeset
115 * hex/bin string -- good for human readability
shellac
parents:
diff changeset
116
shellac
parents:
diff changeset
117 '''
shellac
parents:
diff changeset
118 __slots__ = ('val', 'len')
shellac
parents:
diff changeset
119
shellac
parents:
diff changeset
120 def __init__(self, val=0, len_=None):
shellac
parents:
diff changeset
121 if type(val) not in _int_types:
shellac
parents:
diff changeset
122 if type(val) is list:
shellac
parents:
diff changeset
123 val = ''.join(['1' if e else '0' for e in val])
shellac
parents:
diff changeset
124 if type(val) is bytes:
shellac
parents:
diff changeset
125 val = val.decode('ascii')
shellac
parents:
diff changeset
126 if type(val) is unicode:
shellac
parents:
diff changeset
127 if len_ is None:
shellac
parents:
diff changeset
128 len_ = len(val)
shellac
parents:
diff changeset
129 if val.startswith('0x'):
shellac
parents:
diff changeset
130 len_ = (len_ - 2) * 4
shellac
parents:
diff changeset
131 if val.startswith('0x'):
shellac
parents:
diff changeset
132 val = int(val, 16)
shellac
parents:
diff changeset
133 else:
shellac
parents:
diff changeset
134 if val:
shellac
parents:
diff changeset
135 val = int(val, 2)
shellac
parents:
diff changeset
136 else:
shellac
parents:
diff changeset
137 val = 0
shellac
parents:
diff changeset
138 if type(val) not in _int_types:
shellac
parents:
diff changeset
139 raise TypeError('initialized with bad type: {0}'.format(type(val).__name__))
shellac
parents:
diff changeset
140 if val < 0:
shellac
parents:
diff changeset
141 raise ValueError('Bits cannot represent negative values')
shellac
parents:
diff changeset
142 if len_ is None:
shellac
parents:
diff changeset
143 len_ = len('{0:b}'.format(val))
shellac
parents:
diff changeset
144 if val > 2 ** len_:
shellac
parents:
diff changeset
145 raise ValueError('value {0} cannot be represented with {1} bits'.format(val, len_))
shellac
parents:
diff changeset
146 self.val = val # data is stored internally as integer
shellac
parents:
diff changeset
147 self.len = len_
shellac
parents:
diff changeset
148
shellac
parents:
diff changeset
149 def __getitem__(self, k):
shellac
parents:
diff changeset
150 if type(k) is slice:
shellac
parents:
diff changeset
151 return Bits(self.as_bin()[k])
shellac
parents:
diff changeset
152 if type(k) is int:
shellac
parents:
diff changeset
153 if k >= self.len:
shellac
parents:
diff changeset
154 raise IndexError(k)
shellac
parents:
diff changeset
155 return bool((1 << (self.len - k - 1)) & self.val)
shellac
parents:
diff changeset
156 raise TypeError(type(k))
shellac
parents:
diff changeset
157
shellac
parents:
diff changeset
158 def __len__(self):
shellac
parents:
diff changeset
159 return self.len
shellac
parents:
diff changeset
160
shellac
parents:
diff changeset
161 def __eq__(self, other):
shellac
parents:
diff changeset
162 if type(self) is not type(other):
shellac
parents:
diff changeset
163 return NotImplemented
shellac
parents:
diff changeset
164 return self.val == other.val and self.len == other.len
shellac
parents:
diff changeset
165
shellac
parents:
diff changeset
166 def __or__(self, other):
shellac
parents:
diff changeset
167 if type(self) is not type(other):
shellac
parents:
diff changeset
168 return NotImplemented
shellac
parents:
diff changeset
169 return Bits(self.val | other.val, max(self.len, other.len))
shellac
parents:
diff changeset
170
shellac
parents:
diff changeset
171 def __and__(self, other):
shellac
parents:
diff changeset
172 if type(self) is not type(other):
shellac
parents:
diff changeset
173 return NotImplemented
shellac
parents:
diff changeset
174 return Bits(self.val & other.val, max(self.len, other.len))
shellac
parents:
diff changeset
175
shellac
parents:
diff changeset
176 def __lshift__(self, other):
shellac
parents:
diff changeset
177 return Bits(self.val << other, self.len + other)
shellac
parents:
diff changeset
178
shellac
parents:
diff changeset
179 def __rshift__(self, other):
shellac
parents:
diff changeset
180 return Bits(self.val >> other, self.len - other)
shellac
parents:
diff changeset
181
shellac
parents:
diff changeset
182 def __hash__(self):
shellac
parents:
diff changeset
183 return hash(self.val)
shellac
parents:
diff changeset
184
shellac
parents:
diff changeset
185 def as_list(self):
shellac
parents:
diff changeset
186 return [c == '1' for c in '{0:b}'.format(self.val)]
shellac
parents:
diff changeset
187
shellac
parents:
diff changeset
188 def as_bin(self):
shellac
parents:
diff changeset
189 return '{{0:0{0}b}}'.format(self.len).format(self.val)
shellac
parents:
diff changeset
190
shellac
parents:
diff changeset
191 def as_hex(self):
shellac
parents:
diff changeset
192 # make template to pad out to number of bytes necessary to represent bits
shellac
parents:
diff changeset
193 tmpl = '%0{0}X'.format(2 * (self.len // 8 + ((self.len % 8) != 0)))
shellac
parents:
diff changeset
194 ret = tmpl % self.val
shellac
parents:
diff changeset
195 return ret
shellac
parents:
diff changeset
196
shellac
parents:
diff changeset
197 def as_int(self):
shellac
parents:
diff changeset
198 return self.val
shellac
parents:
diff changeset
199
shellac
parents:
diff changeset
200 def as_bytes(self):
shellac
parents:
diff changeset
201 return binascii.unhexlify(self.as_hex())
shellac
parents:
diff changeset
202
shellac
parents:
diff changeset
203 @classmethod
shellac
parents:
diff changeset
204 def from_list(cls, list_):
shellac
parents:
diff changeset
205 return cls(list_)
shellac
parents:
diff changeset
206
shellac
parents:
diff changeset
207 @classmethod
shellac
parents:
diff changeset
208 def from_bin(cls, bin):
shellac
parents:
diff changeset
209 return cls(bin)
shellac
parents:
diff changeset
210
shellac
parents:
diff changeset
211 @classmethod
shellac
parents:
diff changeset
212 def from_hex(cls, hex):
shellac
parents:
diff changeset
213 if isinstance(hex, bytes):
shellac
parents:
diff changeset
214 hex = hex.decode('ascii')
shellac
parents:
diff changeset
215 if not hex.startswith('0x'):
shellac
parents:
diff changeset
216 hex = '0x' + hex
shellac
parents:
diff changeset
217 return cls(hex)
shellac
parents:
diff changeset
218
shellac
parents:
diff changeset
219 @classmethod
shellac
parents:
diff changeset
220 def from_int(cls, int_, len_=None):
shellac
parents:
diff changeset
221 return cls(int_, len_)
shellac
parents:
diff changeset
222
shellac
parents:
diff changeset
223 @classmethod
shellac
parents:
diff changeset
224 def from_bytes(cls, bytes_):
shellac
parents:
diff changeset
225 return cls.from_hex(binascii.hexlify(bytes_))
shellac
parents:
diff changeset
226
shellac
parents:
diff changeset
227 def __repr__(self):