forked from libtom/libtommath
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbn_mp_compute_signed_factored_factorials.c
243 lines (230 loc) · 6.82 KB
/
bn_mp_compute_signed_factored_factorials.c
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
#include <tommath.h>
#ifdef BN_MP_COMPUTE_SIGNED_FACTORED_FACTORIALS_C
static long local_highbit(unsigned long n)
{
long r=0;
while (n >>= 1) {
r++;
}
return r;
}
/*
To compute the resulting polynomials we need an approach different from
mp_compute_factored_factorials() because we have to be able to handle negative
exponents, too.
*/
int mp_compute_signed_factored_factorials(long *f, unsigned long f_length,
mp_int *c, mp_int *r)
{
unsigned long start=0,i,count=0,neg_count = 0,k=0;
long shift = 0;
long bit=0,neg_bit=0,hb=0,neg_hb=0,*p=NULL;
mp_int temp;
int e;
if (f[0] == 2) {
shift = f[1];
if (f_length == 2) {
if (shift > 0) {
if ((e = mp_set_int(c,1)) != MP_OKAY) {
return e;
}
if ((e = mp_mul_2d(c,(int)f[1],c)) != MP_OKAY) {
return e;
}
}
return MP_OKAY;
}
}
if (shift) {
start+=2;
k=2;
}
/*
One simple thing to handle negative exponents is to keep them in a second
array with absolute values of the exponents and divide at the end.
Another alternative might be to return both arrays and let the user decide.
No matter which way, we have to check and if there is at least one, divide.
This is expensive and should be done inline instead but for now...
*/
for (k=start; k<f_length; k+=2) {
if (f[k+1] < 0) {
/* count how many to save some memory */
count++;
}
/* Only primes up to 2^DIGIT_BIT because of mp_mul_d() */
if (f[k] >= 1L<<DIGIT_BIT) {
return MP_VAL;
}
}
/* all are negative */
//if(count && count == f_length/2){
/* if the alternative with two outputs has been chosen just skip the
computation of the polynomial with all positive exponents and set
that return to 1 (one)
*/
/* goto: negative_exponents; */
/* The other alternative would compute 1/x that gives always 0 in integer
divisions if x>1. But it would need exactly one exponent with zero which
has been filtered out already. It is debatable now if that was a good
decision.*/
//return MP_OKAY;
//}
if (count) {
p = malloc(sizeof(long)* (count * 2) +1);
if (p == NULL) {
return MP_MEM;
}
}
for (k=start; k<f_length; k+=2) {
/* won't branch if count == 0 */
if (f[k+1] < 0) {
p[neg_count] = f[k];
p[neg_count+1] = abs(f[k+1]);
neg_count +=2 ;
/* And while we are already here ... */
neg_hb = local_highbit(abs(f[k+1]));
if (neg_bit < neg_hb)neg_bit = neg_hb;
/* This allows for some optimization, mainly w.r.t memory. Not done yet */
f[k] = 1;
/* Set to zero to let the main loop skip it */
f[k+1] = 0;
} else {
hb = local_highbit(f[k+1]);
if (bit < hb)bit = hb;
}
}
/* DIGIT_BIT can be as small as 8 */
if (bit >(long) DIGIT_BIT || neg_bit >(long) DIGIT_BIT) {
return MP_VAL;
}
/* Anything times zero is zero, so set the result to one in advance */
if ((e = mp_set_int(c,1)) != MP_OKAY) {
return e;
}
/* Everything is in place, so let's start by computing with the positive
exponents first */
/* The other function mp_compute_factored_factorials() has a branch
but the cases where it makes sense are quite rare. Feel free to add it
yourself */
for (; bit>=0; bit--) {
if ((e = mp_sqr(c, c)) != MP_OKAY) {
return e;
}
for (i=start; i<f_length; i+=2) {
if ((f[i+1] & (1LU << (unsigned long)bit)) != 0) {
/* This is problematic if DIGIT_BIT is too small. checked above */
if ((e = mp_mul_d(c,(mp_digit)f[i], c)) != MP_OKAY) {
return e;
}
}
}
}
/* Compute c * 2^n if n!=0 */
if (shift && shift > 0) {
if (shift < 1L<<DIGIT_BIT) {
/* The choice of types is a bit questionable. Sometimes. */
if ((e = mp_mul_2d(c, (mp_digit) shift, c)) != MP_OKAY) {
return e;
}
} else {
long multiplicator = 0;
another_round:
while (shift > 1L<<DIGIT_BIT) {
shift >>= 1;
multiplicator++;
}
if ((e = mp_mul_2d(c, (mp_digit) shift, c)) != MP_OKAY) {
return e;
}
if (multiplicator< DIGIT_BIT) {
if ((e = mp_mul_2d(c, (mp_digit)(1<<multiplicator), c)) != MP_OKAY) {
return e;
}
} else {
shift = 1<<multiplicator;
multiplicator = 0;
goto another_round;
}
}
}
/* None are negative*/
if (count == 0) {
/* Clean up */
/* Nothing to clean up */
return MP_OKAY;
}
/* Compute with the negative eponents */
if ((e = mp_init(&temp)) != MP_OKAY) {
return e;
}
if ((e = mp_set_int(&temp,1)) != MP_OKAY) {
return e;
}
for (; neg_bit>=0; neg_bit--) {
if ((e = mp_sqr(&temp, &temp)) != MP_OKAY) {
return e;
}
/* The exponents of 2 hve been stripped already so we start at zero.
"count" counts only the occurences, the size itself is twice as large.
*/
for (i=0; i<count*2; i+=2) {
if ((p[i+1] & (1LU << (unsigned long)neg_bit)) != 0) {
if ((e = mp_mul_d(&temp,(mp_digit)p[i], &temp)) != MP_OKAY) {
return e;
}
}
}
}
/* Compute c * 2^n if n!=0 for negative n*/
if (shift && shift < 0) {
/* check for overflow */
if (shift == LONG_MIN) {
return MP_VAL;
}
shift = -shift;
if (shift < 1L<<DIGIT_BIT) {
/* The choice of types is a bit questionable. Sometimes. */
if ((e = mp_mul_2d(&temp, (mp_digit) shift, &temp)) != MP_OKAY) {
return e;
}
} else {
long multiplicator = 0;
and_another_round:
while (shift > 1L<<DIGIT_BIT) {
shift >>= 1;
multiplicator++;
}
if ((e = mp_mul_2d(&temp, (mp_digit) shift, &temp)) != MP_OKAY) {
return e;
}
if (multiplicator< DIGIT_BIT) {
if ((e = mp_mul_2d(&temp, (mp_digit)(1<<multiplicator), &temp)) != MP_OKAY) {
return e;
}
} else {
shift = 1<<multiplicator;
multiplicator = 0;
goto and_another_round;
}
}
}
/*
Another alternative would be to check for "r == NULL" which wouldn't be
such a strictly compile-time-only decision
*/
#ifdef BN_MP_DO_LAST_DIVISION
if ((e = mp_div(c,&temp,c,r)) != MP_OKAY) {
return e;
}
#else
if (r != NULL) {
if ((e = mp_copy(&temp,r)) != MP_OKAY) {
return e;
}
}
#endif
free(p);
mp_clear(&temp);
return MP_OKAY;
}
#endif