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

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

r""" 

Fast conversion of Python objects to C long 

""" 

  

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

# Copyright (C) 2015 Vincent Delecroix <20100.delecroix@gmail.com> 

# Copyright (C) 2017 Jeroen Demeyer <J.Demeyer@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 libc.limits cimport LONG_MIN 

  

from cpython.object cimport Py_SIZE 

from cpython.int cimport PyInt_AS_LONG 

from cpython.long cimport PyLong_AsLong 

from cpython.number cimport PyNumber_Index, PyIndex_Check 

from cpython.longintrepr cimport PyLongObject, PyLong_SHIFT, digit 

  

from sage.libs.gmp.mpz cimport mpz_fits_slong_p, mpz_get_si 

from sage.rings.integer_fake cimport is_Integer, Integer_AS_MPZ 

  

  

cdef inline long pyobject_to_long(x) except? LONG_MIN: 

r""" 

Given a Python object ``x`` cast it quickly to a C long. 

  

A ``TypeError`` is raised if the input can not be converted to an integer or 

an ``OverflowError`` is raised if it does not fit into a C long. 

  

TESTS: 

  

We test indirectly that ``Integer.__pow__`` works:: 

  

sage: a = 10 

sage: a^10 

10000000000 

sage: a^(10r) 

10000000000 

sage: a^(10l) 

10000000000 

sage: a^(10/1) 

10000000000 

sage: a^(2**258) 

Traceback (most recent call last): 

... 

OverflowError: exponent must be at most 2147483647 # 32-bit 

OverflowError: exponent must be at most 9223372036854775807 # 64-bit 

  

See :trac:`22319`:: 

  

sage: a^pari(10) 

10000000000 

""" 

cdef long value 

cdef int err 

if integer_check_long_py(x, &value, &err): 

if err: 

raise OverflowError("Python int too large to convert to C long") 

return value 

if is_Integer(x): 

z = Integer_AS_MPZ(x) 

if mpz_fits_slong_p(z): 

return mpz_get_si(z) 

else: 

raise OverflowError("Sage Integer too large to convert to C long") 

  

return PyNumber_Index(x) 

  

  

# Error values for integer_check_long() 

cdef enum: 

ERR_TYPE = 1 

ERR_INDEX = 2 

ERR_OVERFLOW = 3 

  

  

cdef inline bint integer_check_long(x, long* value, int* err) except -1: 

""" 

Return whether ``x`` is some integer type. This is true for the 

Python types ``int`` and ``long``, for Sage Integers and for types 

implementing ``__index__``. 

  

If possible, compute the value of this integer as C long and store 

it in ``*value``. 

  

Errors are returned as an error indicator ``*err`` (without raising 

any Python exception). 

  

Possible errors when returning ``True``: 

  

- ``0``: ``x`` was successfully converted to a C long and its value 

is stored in ``*value``. 

  

- ``ERR_OVERFLOW``: ``x`` is an integer type but too large to store 

in a C long. 

  

Possible errors when returning ``False``: 

  

- ``ERR_TYPE``: ``x`` is not an integer type of any kind. 

  

- ``ERR_INDEX``: ``x`` implements ``__index__`` but a ``TypeError`` 

was raised calling ``__index__()``. 

  

- Other exceptions in ``__index__`` are simply propagated. This is 

the only way this function can raise an exception. 

  

EXAMPLES: 

  

We create a pure Python wrapper of this function:: 

  

sage: cython(''' 

....: from sage.arith.long cimport * 

....: from sage.rings.integer cimport smallInteger 

....: def check_long(x): 

....: cdef long value 

....: cdef int err 

....: cdef bint c = integer_check_long(x, &value, &err) 

....: if c: 

....: if err == 0: 

....: return value 

....: elif err == ERR_OVERFLOW: 

....: raise OverflowError("integer_check_long: overflow") 

....: elif err == ERR_TYPE: 

....: raise TypeError("integer_check_long: wrong type") 

....: elif err == ERR_INDEX: 

....: raise TypeError("integer_check_long: bad __index__") 

....: assert False 

....: from libc.limits cimport LONG_MIN, LONG_MAX 

....: def long_min(): 

....: return smallInteger(LONG_MIN) 

....: def long_max(): 

....: return smallInteger(LONG_MAX) 

....: ''') 

sage: import six 

sage: types = (ZZ, QQ) + six.integer_types 

sage: L = [1, 12345, 10^9, 2^30, long_max()//9, long_max()//3, long_max()] 

sage: L += [-x for x in L] + [0, long_min()] 

sage: for v in L: 

....: for t in (Integer,) + six.integer_types: 

....: assert check_long(t(v)) == v 

sage: check_long(2^100) 

Traceback (most recent call last): 

... 

OverflowError: integer_check_long: overflow 

sage: check_long(long_max() + 1) 

Traceback (most recent call last): 

... 

OverflowError: integer_check_long: overflow 

sage: check_long(long_min() - 1) 

Traceback (most recent call last): 

... 

OverflowError: integer_check_long: overflow 

sage: check_long("hello") 

Traceback (most recent call last): 

... 

TypeError: integer_check_long: wrong type 

sage: check_long(2/3) 

Traceback (most recent call last): 

... 

TypeError: integer_check_long: bad __index__ 

""" 

cdef int c = integer_check_long_py(x, value, err) 

if c: 

return c 

if is_Integer(x): 

z = Integer_AS_MPZ(x) 

if mpz_fits_slong_p(z): 

value[0] = mpz_get_si(z) 

err[0] = 0 

else: 

err[0] = ERR_OVERFLOW 

return 1 

elif PyIndex_Check(x): 

err[0] = ERR_INDEX 

try: 

x = PyNumber_Index(x) 

except TypeError: 

return 0 

return integer_check_long_py(x, value, err) 

else: 

err[0] = ERR_TYPE 

return 0 

  

  

cdef inline long dig(const digit* D, int n): 

# Convenient helper function for integer_check_long_py() 

return (<long>D[n]) << (n * PyLong_SHIFT) 

  

  

cdef inline bint integer_check_long_py(x, long* value, int* err): 

""" 

Part of ``integer_check_long`` in ``long.pxd``, checking only for 

Python objects of type ``int`` and ``long``. See that function for 

documentation and tests. 

""" 

if not isinstance(x, long): 

if isinstance(x, int): 

# This can happen only on Python 2 

value[0] = PyInt_AS_LONG(x) 

err[0] = 0 

return 1 

err[0] = ERR_TYPE 

return 0 

  

# x is a Python "long" (called "int" on Python 3) 

cdef const digit* D = (<PyLongObject*>x).ob_digit 

cdef Py_ssize_t size = Py_SIZE(x) 

  

# We assume that PyLong_SHIFT is 15 on a 32-bit system and 30 on a 

# 64-bit system. This is not guaranteed by Python, but it is the 

# default configuration. 

# 

# This way, we know that 1 and 2 digits certainly fit in a C long 

# and 4 or more digits never fit. For 3 digits, we need an explicit 

# overflow check. 

cdef int BITS_IN_LONG = 8 * sizeof(long) - 1 

if not (2 * PyLong_SHIFT <= BITS_IN_LONG < 4 * PyLong_SHIFT): 

raise AssertionError 

  

cdef long lead 

cdef long lead_3_overflow = (<long>1) << (BITS_IN_LONG - 2 * PyLong_SHIFT) 

if size == 0: 

value[0] = 0 

err[0] = 0 

elif size == 1: 

value[0] = dig(D, 0) 

err[0] = 0 

elif size == -1: 

value[0] = -dig(D, 0) 

err[0] = 0 

elif size == 2: 

value[0] = dig(D, 0) + dig(D, 1) 

err[0] = 0 

elif size == -2: 

value[0] = -(dig(D, 0) + dig(D, 1)) 

err[0] = 0 

elif size == 3: 

lead = D[2] 

if lead < lead_3_overflow: 

value[0] = dig(D, 0) + dig(D, 1) + dig(D, 2) 

err[0] = 0 

else: 

err[0] = ERR_OVERFLOW 

elif size == -3: 

lead = D[2] 

if lead < lead_3_overflow: 

value[0] = -(dig(D, 0) + dig(D, 1) + dig(D, 2)) 

err[0] = 0 

elif D[0] == 0 and D[1] == 0 and lead == lead_3_overflow: 

# Special case for LONG_MIN 

value[0] = (<long>-1) << BITS_IN_LONG 

err[0] = 0 

else: 

err[0] = ERR_OVERFLOW 

else: 

# 4 digits or more: guaranteed overflow 

err[0] = ERR_OVERFLOW 

return 1