Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

""" 

Various functions to deal with conversion mpz <-> Python int/long 

  

For doctests, see :class:`Integer`. 

  

AUTHORS: 

  

- Gonzalo Tornaria (2006): initial version 

  

- David Harvey (2007-08-18): added ``mpz_get_pyintlong`` function 

(:trac:`440`) 

  

- Jeroen Demeyer (2015-02-24): moved from c_lib, rewritten using 

``mpz_export`` and ``mpz_import`` (:trac:`17853`) 

""" 

  

#***************************************************************************** 

# Copyright (C) 2015 Jeroen Demeyer <jdemeyer@cage.ugent.be> 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 2 of the License, or 

# (at your option) any later version. 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

  

  

from cpython.object cimport Py_SIZE 

from cpython.int cimport PyInt_FromLong 

from cpython.long cimport PyLong_FromLong 

from cpython.longintrepr cimport _PyLong_New, PyLongObject, digit, PyLong_SHIFT 

from .mpz cimport * 

  

cdef extern from *: 

Py_ssize_t* Py_SIZE_PTR "&Py_SIZE"(object) 

int hash_bits """ 

#ifdef _PyHASH_BITS 

_PyHASH_BITS /* Python 3 */ 

#else 

(8 * sizeof(void*)) /* Python 2 */ 

#endif 

""" 

int limb_bits "(8 * sizeof(mp_limb_t))" 

  

  

# Unused bits in every PyLong digit 

cdef size_t PyLong_nails = 8*sizeof(digit) - PyLong_SHIFT 

  

  

cdef mpz_get_pylong_large(mpz_srcptr z): 

""" 

Convert a non-zero ``mpz`` to a Python ``long``. 

""" 

cdef size_t nbits = mpz_sizeinbase(z, 2) 

cdef size_t pylong_size = (nbits + PyLong_SHIFT - 1) // PyLong_SHIFT 

L = _PyLong_New(pylong_size) 

mpz_export((<PyLongObject*>L).ob_digit, NULL, 

-1, sizeof(digit), 0, PyLong_nails, z) 

if mpz_sgn(z) < 0: 

# Set correct size (use a pointer to hack around Cython's 

# non-support for lvalues). 

sizeptr = Py_SIZE_PTR(L) 

sizeptr[0] = -pylong_size 

return L 

  

  

cdef mpz_get_pylong(mpz_srcptr z): 

""" 

Convert an ``mpz`` to a Python ``long``. 

""" 

if mpz_fits_slong_p(z): 

return PyLong_FromLong(mpz_get_si(z)) 

return mpz_get_pylong_large(z) 

  

  

cdef mpz_get_pyintlong(mpz_srcptr z): 

""" 

Convert an ``mpz`` to a Python ``int`` if possible, or a ``long`` 

if the value is too large. 

""" 

if mpz_fits_slong_p(z): 

return PyInt_FromLong(mpz_get_si(z)) 

return mpz_get_pylong_large(z) 

  

  

cdef int mpz_set_pylong(mpz_ptr z, L) except -1: 

""" 

Convert a Python ``long`` `L` to an ``mpz``. 

""" 

cdef Py_ssize_t pylong_size = Py_SIZE(L) 

if pylong_size < 0: 

pylong_size = -pylong_size 

mpz_import(z, pylong_size, -1, sizeof(digit), 0, PyLong_nails, 

(<PyLongObject*>L).ob_digit) 

if Py_SIZE(L) < 0: 

mpz_neg(z, z) 

  

  

cdef Py_hash_t mpz_pythonhash(mpz_srcptr z): 

""" 

Hash an ``mpz``, where the hash value is the same as the hash value 

of the corresponding Python ``int`` or ``long``, except that we do 

not replace -1 by -2 (the Cython wrapper for ``__hash__`` does that). 

""" 

if mpz_sgn(z) == 0: 

return 0 

  

# The hash value equals z % m where m = 2 ^ hash_bits - 1. 

# 

# Safely compute 2 ^ hash_bits - 1 without overflow 

cdef mp_limb_t modulus = (((<mp_limb_t>(1) << (hash_bits - 1)) - 1) * 2) + 1 

  

cdef mp_limb_t h = 0 

cdef mp_limb_t x, y 

cdef size_t i, n 

cdef unsigned int r 

n = mpz_size(z) 

for i in range(n): 

x = mpz_getlimbn(z, i) 

  

# Computing modulo 2 ^ hash_bits - 1 means that the bit at 

# position j is really moved to position (j % hash_bits). 

# We need to shift every bit of x left by (limb_bits * i) 

# and then put it in the right position to account for 

# the modulo operation. Store the result in y. 

if limb_bits == hash_bits: 

y = x 

else: 

r = (limb_bits * i) % hash_bits 

y = (x << r) & modulus 

y += (x >> (hash_bits - r)) & modulus 

# Only do this shift if we don't shift more than the size of the 

# type 

if r > 2 * hash_bits - limb_bits: 

y += (x >> (2 * hash_bits - r)) 

  

# Safely compute h = (h + y) % modulus knowing that h < modulus 

# and y <= modulus 

if h < modulus - y: 

h = h + y 

else: 

h = h - (modulus - y) 

  

# Special case for Python 2 

if limb_bits == hash_bits and h == 0: 

h = -1 

  

if mpz_sgn(z) < 0: 

return -h 

return h 

  

  

assert hash_bits <= limb_bits <= 2 * hash_bits