Viewing file: common.py (4.57 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.
"""Common functionality shared by several modules."""
import typing
class NotRelativePrimeError(ValueError): def __init__(self, a: int, b: int, d: int, msg: str = "") -> None: super().__init__(msg or "%d and %d are not relatively prime, divider=%i" % (a, b, d)) self.a = a self.b = b self.d = d
def bit_size(num: int) -> int: """ Number of bits needed to represent a integer excluding any prefix 0 bits.
Usage::
>>> bit_size(1023) 10 >>> bit_size(1024) 11 >>> bit_size(1025) 11
:param num: Integer value. If num is 0, returns 0. Only the absolute value of the number is considered. Therefore, signed integers will be abs(num) before the number's bit length is determined. :returns: Returns the number of bits in the integer. """
try: return num.bit_length() except AttributeError as ex: raise TypeError("bit_size(num) only supports integers, not %r" % type(num)) from ex
def byte_size(number: int) -> int: """ Returns the number of bytes required to hold a specific long number.
The number of bytes is rounded up.
Usage::
>>> byte_size(1 << 1023) 128 >>> byte_size((1 << 1024) - 1) 128 >>> byte_size(1 << 1024) 129
:param number: An unsigned integer :returns: The number of bytes required to hold a specific long number. """ if number == 0: return 1 return ceil_div(bit_size(number), 8)
def ceil_div(num: int, div: int) -> int: """ Returns the ceiling function of a division between `num` and `div`.
Usage::
>>> ceil_div(100, 7) 15 >>> ceil_div(100, 10) 10 >>> ceil_div(1, 4) 1
:param num: Division's numerator, a number :param div: Division's divisor, a number
:return: Rounded up result of the division between the parameters. """ quanta, mod = divmod(num, div) if mod: quanta += 1 return quanta
def extended_gcd(a: int, b: int) -> typing.Tuple[int, int, int]: """Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb""" # r = gcd(a,b) i = multiplicitive inverse of a mod b # or j = multiplicitive inverse of b mod a # Neg return values for i or j are made positive mod b or a respectively # Iterateive Version is faster and uses much less stack space x = 0 y = 1 lx = 1 ly = 0 oa = a # Remember original a/b to remove ob = b # negative values from return results while b != 0: q = a // b (a, b) = (b, a % b) (x, lx) = ((lx - (q * x)), x) (y, ly) = ((ly - (q * y)), y) if lx < 0: lx += ob # If neg wrap modulo original b if ly < 0: ly += oa # If neg wrap modulo original a return a, lx, ly # Return only positive values
def inverse(x: int, n: int) -> int: """Returns the inverse of x % n under multiplication, a.k.a x^-1 (mod n)
>>> inverse(7, 4) 3 >>> (inverse(143, 4) * 143) % 4 1 """
(divider, inv, _) = extended_gcd(x, n)
if divider != 1: raise NotRelativePrimeError(x, n, divider)
return inv
def crt(a_values: typing.Iterable[int], modulo_values: typing.Iterable[int]) -> int: """Chinese Remainder Theorem.
Calculates x such that x = a[i] (mod m[i]) for each i.
:param a_values: the a-values of the above equation :param modulo_values: the m-values of the above equation :returns: x such that x = a[i] (mod m[i]) for each i
>>> crt([2, 3], [3, 5]) 8
>>> crt([2, 3, 2], [3, 5, 7]) 23
>>> crt([2, 3, 0], [7, 11, 15]) 135 """
m = 1 x = 0
for modulo in modulo_values: m *= modulo
for (m_i, a_i) in zip(modulo_values, a_values): M_i = m // m_i inv = inverse(M_i, m_i)
x = (x + a_i * M_i * inv) % m
return x
if __name__ == "__main__": import doctest
doctest.testmod()
|