From 456fa49fa8e8acd43fb97fbaa4ab2eead3039be4 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Tue, 25 Feb 2020 09:54:25 -0500 Subject: [PATCH] Remove the float imprecision from the implementation The new direct positive-path Hypothesis-based test wants to pass in huge numbers and the float-based implementation does not do well for these inputs. Happily, we can do better. --- src/_zkapauthorizer/storage_common.py | 13 +++------- .../tests/test_storage_protocol.py | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/_zkapauthorizer/storage_common.py b/src/_zkapauthorizer/storage_common.py index 78b8dc5..9bf9435 100644 --- a/src/_zkapauthorizer/storage_common.py +++ b/src/_zkapauthorizer/storage_common.py @@ -24,10 +24,6 @@ from base64 import ( b64encode, ) -from math import ( - ceil, -) - def _message_maker(label): def make_message(storage_index): return u"{label} {storage_index}".format( @@ -59,17 +55,16 @@ def required_passes(bytes_per_pass, share_sizes): :return int: The number of passes required to cover the storage cost. """ - result = int( - ceil( - sum(share_sizes, 0) / bytes_per_pass, - ), - ) if not isinstance(share_sizes, list): raise TypeError( "Share sizes must be a list of integers, got {!r} instead".format( share_sizes, ), ) + result, b = divmod(sum(share_sizes, 0), bytes_per_pass) + if b: + result += 1 + # print("required_passes({}, {}) == {}".format(bytes_per_pass, share_sizes, result)) return result diff --git a/src/_zkapauthorizer/tests/test_storage_protocol.py b/src/_zkapauthorizer/tests/test_storage_protocol.py index b0c83d7..d595e7c 100644 --- a/src/_zkapauthorizer/tests/test_storage_protocol.py +++ b/src/_zkapauthorizer/tests/test_storage_protocol.py @@ -51,6 +51,7 @@ from hypothesis import ( ) from hypothesis.strategies import ( sets, + lists, tuples, integers, ) @@ -167,6 +168,30 @@ class RequiredPassesTests(TestCase): raises(TypeError), ) + @given( + bytes_per_pass=integers(min_value=1), + expected_per_share=lists(integers(min_value=1), min_size=1), + ) + def test_minimum_result(self, bytes_per_pass, expected_per_share): + """ + ``required_passes`` returns an integer giving the fewest passes required + to pay for the storage represented by the given share sizes. + """ + actual = required_passes( + bytes_per_pass, + list( + passes * bytes_per_pass + for passes + in expected_per_share + ), + ) + self.assertThat( + actual, + Equals(sum(expected_per_share)), + ) + + + class ShareTests(TestCase): """ Tests for interaction with shares. -- GitLab