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