Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/c_lib/src/mpn_pylong.c
4049 views
1
/* mpn <-> pylong conversion and "pythonhash" for mpn
2
*
3
* Author: Gonzalo TornarĂ­a <[email protected]>
4
* Date: March 2006
5
* License: GPL v2 or later
6
*
7
* the code to change the base to 2^SHIFT is based on the function
8
* mpn_get_str from GNU MP, but the new bugs are mine
9
*
10
* this is free software: if it breaks, you get to keep all the pieces
11
*/
12
13
#include "mpn_pylong.h"
14
15
/* This code assumes that SHIFT < GMP_NUMB_BITS */
16
#if SHIFT >= GMP_NUMB_BITS
17
#error "Python limb larger than GMP limb !!!"
18
#endif
19
20
/* Use these "portable" (I hope) sizebits functions
21
* We could implement this in terms of count_leading_zeros from GMP,
22
* but it is not exported !
23
*/
24
static const
25
unsigned char
26
__sizebits_tab[128] =
27
{
28
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
29
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
30
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
31
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
32
};
33
34
#if GMP_LIMB_BITS > 64
35
#error "word size > 64 unsupported"
36
#endif
37
38
static inline
39
unsigned long
40
mpn_sizebits(mp_ptr up, mp_size_t un) {
41
unsigned long cnt;
42
mp_limb_t x;
43
if (un==0) return 0;
44
cnt = (un - 1) * GMP_NUMB_BITS;
45
x = up[un - 1];
46
#if GMP_LIMB_BITS > 32
47
if ((x >> 32) != 0) { x >>= 32; cnt += 32; }
48
#endif
49
#if GMP_LIMB_BITS > 16
50
if ((x >> 16) != 0) { x >>= 16; cnt += 16; }
51
#endif
52
#if GMP_LIMB_BITS > 8
53
if ((x >> 8) != 0) { x >>= 8; cnt += 8; }
54
#endif
55
return cnt + ((x & 0x80) ? 8 : __sizebits_tab[x]);
56
}
57
58
static inline
59
unsigned long
60
pylong_sizebits(digit *digits, py_size_t size) {
61
unsigned long cnt;
62
digit x;
63
if (size==0) return 0;
64
cnt = (size - 1) * SHIFT;
65
x = digits[size - 1];
66
#if SHIFT > 32
67
if ((x >> 32) != 0) { x >>= 32; cnt += 32; }
68
#endif
69
#if SHIFT > 16
70
if ((x >> 16) != 0) { x >>= 16; cnt += 16; }
71
#endif
72
#if SHIFT > 8
73
if ((x >> 8) != 0) { x >>= 8; cnt += 8; }
74
#endif
75
return cnt + ((x & 0x80) ? 8 : __sizebits_tab[x]);
76
}
77
78
79
/* mpn -> pylong conversion */
80
81
int
82
mpn_pylong_size (mp_ptr up, mp_size_t un)
83
{
84
return (mpn_sizebits(up, un) + SHIFT - 1) / SHIFT;
85
}
86
87
/* this is based from GMP code in mpn/get_str.c */
88
89
/* Assume digits points to a chunk of size size
90
* where size >= mpn_pylong_size(up, un)
91
*/
92
void
93
mpn_get_pylong (digit *digits, py_size_t size, mp_ptr up, mp_size_t un)
94
{
95
mp_limb_t n1, n0;
96
mp_size_t i;
97
int bit_pos;
98
/* point past the allocated chunk */
99
digit * s = digits + size;
100
101
/* input length 0 is special ! */
102
if (un == 0) {
103
while (size) digits[--size]=0;
104
return;
105
}
106
107
i = un - 1;
108
n1 = up[i];
109
bit_pos = size * SHIFT - i * GMP_NUMB_BITS;
110
111
for (;;)
112
{
113
bit_pos -= SHIFT;
114
while (bit_pos >= 0)
115
{
116
*--s = (n1 >> bit_pos) & MASK;
117
bit_pos -= SHIFT;
118
}
119
if (i == 0)
120
break;
121
n0 = (n1 << -bit_pos) & MASK;
122
n1 = up[--i];
123
bit_pos += GMP_NUMB_BITS;
124
*--s = n0 | (n1 >> bit_pos);
125
}
126
}
127
128
/* pylong -> mpn conversion */
129
130
mp_size_t
131
mpn_size_from_pylong (digit *digits, py_size_t size)
132
{
133
return (pylong_sizebits(digits, size) + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS;
134
}
135
136
void
137
mpn_set_pylong (mp_ptr up, mp_size_t un, digit *digits, py_size_t size)
138
{
139
mp_limb_t n1, d;
140
mp_size_t i;
141
int bit_pos;
142
/* point past the allocated chunk */
143
digit * s = digits + size;
144
145
/* input length 0 is special ! */
146
if (size == 0) {
147
while (un) up[--un]=0;
148
return;
149
}
150
151
i = un - 1;
152
n1 = 0;
153
bit_pos = size * SHIFT - i * GMP_NUMB_BITS;
154
155
for (;;)
156
{
157
bit_pos -= SHIFT;
158
while (bit_pos >= 0)
159
{
160
d = (mp_limb_t) *--s;
161
n1 |= (d << bit_pos) & GMP_NUMB_MASK;
162
bit_pos -= SHIFT;
163
}
164
if (i == 0)
165
break;
166
d = (mp_limb_t) *--s;
167
/* add some high bits of d; maybe none if bit_pos=-SHIFT */
168
up[i--] = n1 | (d & MASK) >> -bit_pos;
169
bit_pos += GMP_NUMB_BITS;
170
n1 = (d << bit_pos) & GMP_NUMB_MASK;
171
}
172
up[0] = n1;
173
}
174
175
176
/************************************************************/
177
178
/* Hashing functions */
179
180
/* This is a bad hash...
181
* If we decide to give up pylong compatibility, we should research to
182
* find a decent (but fast) hash
183
*
184
* Some pointers to start:
185
* <http://www.isthe.com/chongo/tech/comp/fnv/>
186
* <http://www.azillionmonkeys.com/qed/hash.html>
187
* <http://burtleburtle.net/bob/hash/doobs.html>
188
*/
189
/*
190
* for an mpz, this number has to be multiplied by the sign
191
* also remember to catch -1 and map it to -2 !
192
*/
193
long
194
mpn_pythonhash (mp_ptr up, mp_size_t un)
195
{
196
/* Simply add all limbs */
197
mp_limb_t h = 0;
198
mp_limb_t h0;
199
mp_size_t i;
200
for (i = 0; i < un; i++)
201
{
202
h0 = h;
203
h += up[i];
204
/* Add 1 on overflow */
205
if (h < h0) h++;
206
}
207
return h;
208
}
209
210
211