1# This file is part of Hypothesis, which may be found at
2# https://github.com/HypothesisWorks/hypothesis/
3#
4# Copyright the Hypothesis Authors.
5# Individual contributors are listed in AUTHORS.rst and the git log.
6#
7# This Source Code Form is subject to the terms of the Mozilla Public License,
8# v. 2.0. If a copy of the MPL was not distributed with this file, You can
9# obtain one at https://mozilla.org/MPL/2.0/.
10
11from hypothesis.internal.conjecture.junkdrawer import find_integer
12from hypothesis.internal.conjecture.shrinking.common import Shrinker
13
14"""
15This module implements a shrinker for non-negative integers.
16"""
17
18
19class Integer(Shrinker):
20 """Attempts to find a smaller integer. Guaranteed things to try ``0``,
21
22 ``1``, ``initial - 1``, ``initial - 2``. Plenty of optimisations beyond
23 that but those are the guaranteed ones.
24 """
25
26 def short_circuit(self):
27 for i in range(2):
28 if self.consider(i):
29 return True
30 self.mask_high_bits()
31 if self.size > 8:
32 # see if we can squeeze the integer into a single byte.
33 self.consider(self.current >> (self.size - 8))
34 self.consider(self.current & 0xFF)
35 return self.current == 2
36
37 def check_invariants(self, value):
38 assert value >= 0
39
40 def left_is_better(self, left, right):
41 return left < right
42
43 def run_step(self):
44 self.shift_right()
45 self.shrink_by_multiples(2)
46 self.shrink_by_multiples(1)
47
48 def shift_right(self):
49 base = self.current
50 find_integer(lambda k: k <= self.size and self.consider(base >> k))
51
52 def mask_high_bits(self):
53 base = self.current
54 n = base.bit_length()
55
56 @find_integer
57 def try_mask(k):
58 if k >= n:
59 return False
60 mask = (1 << (n - k)) - 1
61 return self.consider(mask & base)
62
63 @property
64 def size(self):
65 return self.current.bit_length()
66
67 def shrink_by_multiples(self, k):
68 base = self.current
69
70 @find_integer
71 def shrunk(n):
72 attempt = base - n * k
73 return attempt >= 0 and self.consider(attempt)
74
75 return shrunk > 0