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