mirror of https://github.com/mahmoud/boltons.git
Ceil/floor function re-implementation using bisect. Corrections and updates to unittests and documentation.
This commit is contained in:
parent
b175ab7a21
commit
b6f61b8d8b
|
@ -1,15 +1,18 @@
|
|||
"""This module provides useful math functions on top of Python's built-in :mod:`math` module.
|
||||
"""
|
||||
import bisect
|
||||
|
||||
|
||||
def ceil_from_iter(src, x, allow_equal=True):
|
||||
def ceil_from_iter(src, x, allow_equal=True, sorted_src=False):
|
||||
"""
|
||||
Return the ceiling of *x*, the smallest integer or float from *src* that is greater than [or equal to] *x*.
|
||||
|
||||
Args:
|
||||
src (iterable): Iterable of arbitrary numbers (ints or floats).
|
||||
x (int or float): Number to be tested.
|
||||
src (iterable): Iterable of arbitrary numbers (ints or floats).
|
||||
x (int or float): Number to be tested.
|
||||
allow_equal (bool): Defaults to ``True``. Allows equality to be ignored if set to ``False``.
|
||||
sorted_src (bool): Defaults to ``False``. Allows potential performance increase for large *src* iterable if set
|
||||
to ``False``, as long as *src* is pre-sorted.
|
||||
|
||||
>>> VALID_CABLE_CSA = [1.5, 2.5, 4, 6, 10, 25, 35, 50]
|
||||
>>> ceil_from_iter(VALID_CABLE_CSA, 3.5)
|
||||
|
@ -20,17 +23,31 @@ def ceil_from_iter(src, x, allow_equal=True):
|
|||
6
|
||||
"""
|
||||
|
||||
return min(filter(lambda y: y >= x if allow_equal else y > x, src))
|
||||
if not sorted_src:
|
||||
src = sorted(src)
|
||||
|
||||
if allow_equal:
|
||||
i = bisect.bisect_left(src, x)
|
||||
if i != len(src):
|
||||
return src[i]
|
||||
raise ValueError("No ceiling value in source iterable greater than or equal to:", x)
|
||||
else:
|
||||
i = bisect.bisect_right(src, x)
|
||||
if i != len(src):
|
||||
return src[i]
|
||||
raise ValueError("No ceiling value in source iterable greater than:", x)
|
||||
|
||||
|
||||
def floor_from_iter(src, x, allow_equal=True):
|
||||
def floor_from_iter(src, x, allow_equal=True, sorted_src=False):
|
||||
"""
|
||||
Return the floor of *x*, the largest integer or float from *src* that is less than [or equal to] *x*.
|
||||
|
||||
Args:
|
||||
src (iterable): Iterable of arbitrary numbers (ints or floats).
|
||||
x (int or float): Number to be tested.
|
||||
src (iterable): Iterable of arbitrary numbers (ints or floats).
|
||||
x (int or float): Number to be tested.
|
||||
allow_equal (bool): Defaults to ``True``. Allows equality to be ignored if set to ``False``.
|
||||
sorted_src (bool): Defaults to ``False``. Allows potential performance increase for large *src* iterable if set
|
||||
to ``False``, as long as *src* is pre-sorted.
|
||||
|
||||
>>> VALID_CABLE_CSA = [1.5, 2.5, 4, 6, 10, 25, 35, 50]
|
||||
>>> floor_from_iter(VALID_CABLE_CSA, 3.5)
|
||||
|
@ -41,4 +58,16 @@ def floor_from_iter(src, x, allow_equal=True):
|
|||
1.5
|
||||
"""
|
||||
|
||||
return max(filter(lambda y: y <= x if allow_equal else y < x, src))
|
||||
if not sorted_src:
|
||||
src = sorted(src)
|
||||
|
||||
if allow_equal:
|
||||
i = bisect.bisect_right(src, x)
|
||||
if i:
|
||||
return src[i-1]
|
||||
raise ValueError("No floor value in source iterable less than or equal to:", x)
|
||||
else:
|
||||
i = bisect.bisect_left(src, x)
|
||||
if i:
|
||||
return src[i-1]
|
||||
raise ValueError("No floor value in source iterable less than:", x)
|
|
@ -8,4 +8,11 @@ Alternative Ceiling/Floor Functions
|
|||
|
||||
.. autofunction:: boltons.mathutils.ceil_from_iter
|
||||
|
||||
.. autofunction:: boltons.mathutils.floor_from_iter
|
||||
.. autofunction:: boltons.mathutils.floor_from_iter
|
||||
|
||||
Note: :func:`ceil_from_iter` and :func:`floor_from_iter` functions are based on `this example`_ using from the
|
||||
:mod:`bisect` module in the standard library. Refer to this `StackOverflow Answer`_ for further information regarding
|
||||
the performance impact of this approach.
|
||||
|
||||
.. _this example: https://docs.python.org/3/library/bisect.html#searching-sorted-lists
|
||||
.. _StackOverflow Answer: http://stackoverflow.com/a/12141511/811740
|
|
@ -10,14 +10,15 @@ class TestCeilAndFloor(unittest.TestCase):
|
|||
self.BIG_LIST_SORTED = sorted(self.BIG_LIST)
|
||||
self.OUT_OF_RANGE_LOWER = 60
|
||||
self.OUT_OF_RANGE_UPPER = 2500
|
||||
self.VALID_LOWER = self.BIG_LIST_SORTED[3]
|
||||
self.VALID_UPPER = self.BIG_LIST_SORTED[-3]
|
||||
self.VAILD_BETWEEN = 248.5
|
||||
self.VALID_LOWER = 247
|
||||
self.VALID_UPPER = 2314
|
||||
self.VALID_BETWEEN = 248.5
|
||||
|
||||
# Tests for boltons.mathutils.ceil_from_iter()
|
||||
def test_ceil_from_iter_default(self):
|
||||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_LOWER), self.VALID_LOWER)
|
||||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_UPPER), self.VALID_UPPER)
|
||||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_BETWEEN), 250)
|
||||
|
||||
def test_ceil_from_iter_default_allow_equal_false(self):
|
||||
self.assertNotEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_LOWER, allow_equal=False), self.VALID_LOWER)
|
||||
|
@ -30,18 +31,18 @@ class TestCeilAndFloor(unittest.TestCase):
|
|||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_UPPER),
|
||||
bmu.ceil_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER))
|
||||
|
||||
def test_ceil_from_iter_unsorted_failure(self):
|
||||
self.assertNotEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_LOWER),
|
||||
bmu.ceil_from_iter(self.BIG_LIST_SORTED, self.VALID_LOWER, sorted_src=True))
|
||||
def test_ceil_from_iter_sorted_src_true(self):
|
||||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_LOWER),
|
||||
bmu.ceil_from_iter(self.BIG_LIST_SORTED, self.VALID_LOWER, sorted_src=True))
|
||||
|
||||
self.assertNotEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_UPPER),
|
||||
bmu.ceil_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER, sorted_src=True))
|
||||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST, self.VALID_UPPER),
|
||||
bmu.ceil_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER, sorted_src=True))
|
||||
|
||||
def test_ceil_from_iter_sorted(self):
|
||||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST_SORTED, self.VALID_LOWER), self.VALID_LOWER)
|
||||
self.assertEqual(bmu.ceil_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER), self.VALID_UPPER)
|
||||
|
||||
def test_ceil_from_iter_exception_out_of_range_lower(self):
|
||||
def test_ceil_from_iter_out_of_range_lower(self):
|
||||
expected = min(self.BIG_LIST)
|
||||
actual = bmu.ceil_from_iter(self.BIG_LIST, self.OUT_OF_RANGE_LOWER)
|
||||
self.assertEqual(expected, actual)
|
||||
|
@ -49,10 +50,14 @@ class TestCeilAndFloor(unittest.TestCase):
|
|||
def test_ceil_from_iter_exception_out_of_range_upper(self):
|
||||
self.assertRaises(ValueError, bmu.ceil_from_iter, self.BIG_LIST, self.OUT_OF_RANGE_UPPER)
|
||||
|
||||
def test_ceil_from_iter_exception_out_of_range_upper_allow_equal_false(self):
|
||||
self.assertRaises(ValueError, bmu.ceil_from_iter, self.BIG_LIST, self.OUT_OF_RANGE_UPPER, allow_equal=False)
|
||||
|
||||
# Tests for boltons.mathutils.floor_from_iter()
|
||||
def test_floor_from_iter_default(self):
|
||||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_LOWER), self.VALID_LOWER)
|
||||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_UPPER), self.VALID_UPPER)
|
||||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_BETWEEN), 247)
|
||||
|
||||
def test_floor_from_iter_default_allow_equal_false(self):
|
||||
self.assertNotEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_LOWER, allow_equal=False), self.VALID_LOWER)
|
||||
|
@ -65,24 +70,27 @@ class TestCeilAndFloor(unittest.TestCase):
|
|||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_UPPER),
|
||||
bmu.floor_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER))
|
||||
|
||||
def test_floor_from_iter_unsorted_failure(self):
|
||||
self.assertNotEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_LOWER),
|
||||
bmu.floor_from_iter(self.BIG_LIST_SORTED, self.VALID_LOWER, sorted_src=True))
|
||||
def test_floor_from_iter_sorted_src_true(self):
|
||||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_LOWER),
|
||||
bmu.floor_from_iter(self.BIG_LIST_SORTED, self.VALID_LOWER, sorted_src=True))
|
||||
|
||||
self.assertNotEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_UPPER),
|
||||
bmu.floor_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER, sorted_src=True))
|
||||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST, self.VALID_UPPER),
|
||||
bmu.floor_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER, sorted_src=True))
|
||||
|
||||
def test_floor_from_iter_sorted(self):
|
||||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST_SORTED, self.VALID_LOWER), self.VALID_LOWER)
|
||||
self.assertEqual(bmu.floor_from_iter(self.BIG_LIST_SORTED, self.VALID_UPPER), self.VALID_UPPER)
|
||||
|
||||
def test_floor_from_iter_exception_out_of_range_lower(self):
|
||||
self.assertRaises(ValueError, bmu.floor_from_iter, self.BIG_LIST, self.OUT_OF_RANGE_LOWER)
|
||||
|
||||
def test_floor_from_iter_exception_out_of_range_upper(self):
|
||||
def test_floor_from_iter_out_of_range_upper(self):
|
||||
expected = max(self.BIG_LIST)
|
||||
actual = bmu.floor_from_iter(self.BIG_LIST, self.OUT_OF_RANGE_UPPER)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_floor_from_iter_exception_out_of_range_lower(self):
|
||||
self.assertRaises(ValueError, bmu.floor_from_iter, self.BIG_LIST, self.OUT_OF_RANGE_LOWER)
|
||||
|
||||
def test_floor_from_iter_exception_out_of_range_lower_allow_equal_false(self):
|
||||
self.assertRaises(ValueError, bmu.floor_from_iter, self.BIG_LIST, self.OUT_OF_RANGE_LOWER, allow_equal=False)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Loading…
Reference in New Issue