1 /*
2 Copyright (C) 2006, 2011, 2016 William Hart
3 Copyright (C) 2015 Nitin Kumar
4
5 This file is part of FLINT.
6
7 FLINT is free software: you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License (LGPL) as published
9 by the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version. See <http://www.gnu.org/licenses/>.
11 */
12
13 #include <ctype.h>
14 #include "qsieve.h"
15
16 #define HASH_MULT (2654435761U) /* hash function, taken from 'msieve' */
17 #define HASH(a) ((ulong)((((unsigned int) a) * HASH_MULT) >> (12)))
18
19 /******************************************************************************
20 *
21 * Some helper function, used for debugging
22 *
23 *****************************************************************************/
24
25 /*
26 Display a relation for debugging purposes
27 */
qsieve_display_relation(qs_t qs_inf,relation_t a)28 void qsieve_display_relation(qs_t qs_inf, relation_t a)
29 {
30 slong i;
31
32 flint_printf("%wu ", a.lp);
33
34 for (i = 0; i < qs_inf->small_primes; i++)
35 flint_printf("%wd ", a.small[i]);
36
37 flint_printf("%wd ", a.num_factors);
38
39 for (i = 0; i < a.num_factors; i++)
40 flint_printf("%wd %wu ", a.factor[i].ind, a.factor[i].exp);
41
42 fmpz_print(a.Y);
43 flint_printf("\n");
44 }
45
46 /*
47 Check a relation is valid (debugging)
48 */
qsieve_is_relation(qs_t qs_inf,relation_t a)49 int qsieve_is_relation(qs_t qs_inf, relation_t a)
50 {
51 slong i;
52 fmpz_t temp, temp2;
53 fmpz_init(temp);
54 fmpz_init_set_ui(temp2, 1);
55
56 for (i = 0; i < qs_inf->small_primes; i++)
57 {
58 fmpz_set_si(temp, qs_inf->factor_base[i].p);
59 fmpz_pow_ui(temp, temp, a.small[i]);
60 fmpz_mul(temp2, temp2, temp);
61 }
62
63 if (a.num_factors > qs_inf->max_factors)
64 {
65 return 0;
66 }
67
68 for (i = 0; i < a.num_factors; i++)
69 {
70 fmpz_set_ui(temp, qs_inf->factor_base[a.factor[i].ind].p);
71 fmpz_pow_ui(temp, temp, a.factor[i].exp);
72 fmpz_mul(temp2, temp2, temp);
73 }
74
75 fmpz_mul_ui(temp2, temp2, a.lp);
76 fmpz_pow_ui(temp, a.Y, UWORD(2));
77 fmpz_mod(temp, temp, qs_inf->kn);
78 fmpz_mod(temp2, temp2, qs_inf->kn);
79
80 if (fmpz_cmp(temp, temp2) != 0)
81 {
82 return 0;
83 }
84
85 fmpz_clear(temp);
86 fmpz_clear(temp2);
87
88 return 1;
89 }
90
91 /*
92 Write partial or full relation to file
93 */
qsieve_write_to_file(qs_t qs_inf,mp_limb_t prime,fmpz_t Y,qs_poly_t poly)94 void qsieve_write_to_file(qs_t qs_inf, mp_limb_t prime, fmpz_t Y, qs_poly_t poly)
95 {
96 slong i;
97 char * str = NULL;
98 slong num_factors = poly->num_factors;
99 slong * small = poly->small;
100 fac_t * factor = poly->factor;
101
102 flint_fprintf(qs_inf->siqs, "%X ", prime); /* write large prime */
103
104 for (i = 0; i < qs_inf->small_primes; i++) /* write small primes */
105 flint_fprintf(qs_inf->siqs, "%X ", small[i]);
106
107 flint_fprintf(qs_inf->siqs, "%X ", num_factors); /* write number of factors */
108
109 for (i = 0; i < num_factors; i++) /* write factor along with exponent */
110 flint_fprintf(qs_inf->siqs, "%wx %X ", factor[i].ind, factor[i].exp);
111
112 str = fmpz_get_str(str, 16, Y); /* converting value of 'Y' to hex */
113
114 flint_fprintf(qs_inf->siqs, "%s\n", str); /* write value of 'Y' */
115 flint_free(str);
116 }
117
118 /******************************************************************************
119 *
120 * Hash table
121 *
122 *****************************************************************************/
123
124 /*
125 Hash table used to keep count of large primes, idea is taken from msieve
126 Each new prime is filled at last unoccupied position in array and primes
127 which have same hash value are linked with each other keeping offset
128 */
129
130 /*
131 return a pointer to location of 'prime' in table if it exists else
132 create an entry for it and return pointer to that
133 */
qsieve_get_table_entry(qs_t qs_inf,mp_limb_t prime)134 hash_t * qsieve_get_table_entry(qs_t qs_inf, mp_limb_t prime)
135 {
136 mp_limb_t offset, first_offset;
137 hash_t * entry;
138 mp_limb_t * hash_table = qs_inf->hash_table;
139 hash_t * table = qs_inf->table;
140 slong table_size = qs_inf->table_size;
141
142 /* reallocate table if not large enough */
143 if (3*qs_inf->vertices/2 + 1 >= table_size)
144 {
145 table_size *= 1.4;
146 table = flint_realloc(table, table_size*sizeof(hash_t));
147 qs_inf->table_size = table_size;
148 qs_inf->table = table;
149 }
150
151 /* find first offset with that hash */
152 first_offset = HASH(prime);
153 offset = hash_table[first_offset];
154
155 /* check linked offsets to see if prime is there, return if so */
156 while (offset != 0)
157 {
158 entry = table + offset;
159 if (entry->prime == prime)
160 break;
161 offset = entry->next;
162 }
163
164 /* if we didn't find it, make a new entry in hash table and return it */
165 if (offset == 0)
166 {
167 qs_inf->vertices++;
168 entry = table + qs_inf->vertices;
169 entry->prime = prime;
170 entry->next = hash_table[first_offset];
171 entry->count = 0;
172 hash_table[first_offset] = qs_inf->vertices;
173 }
174
175 return entry;
176 }
177
178 /*
179 add prime to hashtable, increase size of table if neccessary
180 and increment count for the added prime
181 */
qsieve_add_to_hashtable(qs_t qs_inf,mp_limb_t prime)182 void qsieve_add_to_hashtable(qs_t qs_inf, mp_limb_t prime)
183 {
184 hash_t * entry;
185
186 entry = qsieve_get_table_entry(qs_inf, prime);
187 entry->count++;
188 }
189
190 /******************************************************************************
191 *
192 * Large prime functionality
193 *
194 *****************************************************************************/
195
196 /*
197 given a string representing a relation, parse it to
198 obtain relation
199 */
qsieve_parse_relation(qs_t qs_inf,char * str)200 relation_t qsieve_parse_relation(qs_t qs_inf, char * str)
201 {
202 slong i;
203 char * next;
204 relation_t rel;
205
206 rel.lp = UWORD(1);
207 rel.small = flint_malloc(qs_inf->small_primes * sizeof(slong));
208 rel.factor = flint_malloc(qs_inf->max_factors * sizeof(fac_t));
209
210 for (i = 0; i < qs_inf->small_primes; i++)
211 {
212 while (isspace(*str))
213 str++;
214
215 rel.small[i] = strtoul(str, &next, 16);
216 str = next;
217 }
218
219 while (isspace(*str))
220 str++;
221
222 rel.num_factors = strtoul(str, &next, 16);
223 rel.small_primes = qs_inf->small_primes;
224 str = next;
225
226 for (i = 0; i < rel.num_factors; i++)
227 {
228 while (isspace(*str))
229 str++;
230
231 rel.factor[i].ind = strtoul(str, &next, 16);
232 str = next;
233
234 while (isspace(*str))
235 str++;
236
237 rel.factor[i].exp = strtoul(str, &next, 16);
238 str = next;
239 }
240
241 while (isspace(*str))
242 str++;
243
244 fmpz_init(rel.Y);
245 fmpz_set_str(rel.Y, str, 16);
246
247 return rel;
248 }
249
250 /*
251 given two partials with same large prime, merge them to
252 obtain a full relation
253 */
qsieve_merge_relation(qs_t qs_inf,relation_t a,relation_t b)254 relation_t qsieve_merge_relation(qs_t qs_inf, relation_t a, relation_t b)
255 {
256 slong i = 0, j = 0, k = 0;
257 relation_t c;
258 fmpz_t temp;
259
260 c.lp = UWORD(1);
261 c.small = flint_malloc(qs_inf->small_primes * sizeof(slong));
262 c.factor = flint_malloc(qs_inf->max_factors * sizeof(fac_t));
263 fmpz_init(c.Y);
264
265 for (i = 0; i < qs_inf->small_primes; i++)
266 c.small[i] = (a.small[i] + b.small[i]);
267
268 i = 0;
269
270 while (i < a.num_factors && j < b.num_factors)
271 {
272 if (a.factor[i].ind == b.factor[j].ind)
273 {
274 c.factor[k].ind = a.factor[i].ind;
275 c.factor[k++].exp = a.factor[i++].exp + b.factor[j++].exp;
276 }
277 else if (a.factor[i].ind < b.factor[j].ind)
278 {
279 c.factor[k].ind = a.factor[i].ind;
280 c.factor[k++].exp = a.factor[i++].exp;
281 }
282 else
283 {
284 c.factor[k].ind = b.factor[j].ind;
285 c.factor[k++].exp = b.factor[j++].exp;
286 }
287
288 if (k >= qs_inf->max_factors)
289 {
290 flint_printf("more than max_factor !!\n");
291 abort();
292 }
293 }
294
295 while (i < a.num_factors)
296 {
297 c.factor[k].ind = a.factor[i].ind;
298 c.factor[k++].exp = a.factor[i++].exp;
299
300 if (k >= qs_inf->max_factors)
301 {
302 flint_printf("more than max_factor !!\n");
303 abort();
304 }
305 }
306
307 while (j < b.num_factors)
308 {
309 c.factor[k].ind = b.factor[j].ind;
310 c.factor[k++].exp = b.factor[j++].exp;
311
312 if (k >= qs_inf->max_factors)
313 {
314 flint_printf("more than max_factor !!\n");
315 abort();
316 }
317 }
318
319 c.num_factors = k;
320 c.small_primes = qs_inf->small_primes;
321
322 fmpz_init_set_ui(temp, a.lp);
323
324 if (fmpz_invmod(temp, temp, qs_inf->kn) == 0)
325 {
326 flint_printf("Inverse doesn't exist !!\n");
327 abort();
328 }
329
330 fmpz_mul(c.Y, a.Y, b.Y);
331 fmpz_mul(c.Y, c.Y, temp);
332 if (fmpz_cmp(qs_inf->kn, c.Y) <= 0)
333 fmpz_mod(c.Y, c.Y, qs_inf->kn);
334 fmpz_clear(temp);
335
336 return c;
337 }
338
339 /*
340 compare two relations in the following order,
341 large_prime, number of factors, factor, small_prime
342 */
qsieve_compare_relation(const void * a,const void * b)343 int qsieve_compare_relation(const void * a, const void * b)
344 {
345 slong i;
346 relation_t * r1 = (relation_t *) a;
347 relation_t * r2 = (relation_t *) b;
348
349 if (r1->lp > r2->lp)
350 return 1;
351
352 if (r1->lp < r2->lp)
353 return -1;
354
355 if (r1->num_factors > r2->num_factors)
356 return 1;
357
358 if (r1->num_factors < r2->num_factors)
359 return -1;
360
361 for (i = 0; i < r1->num_factors; i++)
362 {
363 if (r1->factor[i].ind > r2->factor[i].ind)
364 return 1;
365
366 if (r1->factor[i].ind < r2->factor[i].ind)
367 return -1;
368
369 if (r1->factor[i].exp > r2->factor[i].exp)
370 return 1;
371
372 if (r1->factor[i].exp < r2->factor[i].exp)
373 return -1;
374 }
375
376 for (i = 0; i < r1->small_primes; i++)
377 {
378 if (r1->small[i] > r2->small[i])
379 return 1;
380
381 if (r1->small[i] < r2->small[i])
382 return -1;
383 }
384
385 return 0;
386 }
387
388 /*
389 given a list of relations, remove duplicate relations from it
390 */
qsieve_remove_duplicates(relation_t * rel_list,slong num_relations)391 int qsieve_remove_duplicates(relation_t * rel_list, slong num_relations)
392 {
393 slong i, j;
394
395 if (num_relations < 2)
396 return 1;
397
398 qsort(rel_list, (size_t) num_relations, sizeof(relation_t), qsieve_compare_relation);
399
400 for (i = 1, j = 0; i < num_relations; i++)
401 {
402 if (qsieve_compare_relation(rel_list + j, rel_list + i) == 0)
403 {
404 rel_list[i].num_factors = 0;
405 flint_free(rel_list[i].small);
406 flint_free(rel_list[i].factor);
407 fmpz_clear(rel_list[i].Y);
408 } else
409 {
410 rel_list[++j] = rel_list[i];
411 }
412 }
413
414 j++;
415
416 #if QS_DEBUG
417 flint_printf("%wd duplicates out of %wd\n", num_relations - j, num_relations);
418 #endif
419
420 return j;
421 }
422
423 /*
424 give a list of relations, add those relations to matrix
425 */
qsieve_insert_relation(qs_t qs_inf,relation_t * rel_list,slong num_relations)426 void qsieve_insert_relation(qs_t qs_inf, relation_t * rel_list, slong num_relations)
427 {
428 slong i, j, num_factors, fac_num;
429 slong * small;
430 slong * curr_rel;
431 fac_t * factor;
432 la_col_t * matrix = qs_inf->matrix;
433
434 qs_inf->num_relations = 0;
435
436 for (j = 0; j < num_relations; j++)
437 {
438 small = rel_list[j].small;
439 num_factors = rel_list[j].num_factors;
440 factor = rel_list[j].factor;
441 curr_rel = qs_inf->curr_rel;
442 fac_num = 0;
443
444 clear_col(matrix + j);
445
446 for (i = 0; i < qs_inf->small_primes; i++)
447 {
448 if (small[i] & 1) insert_col_entry(matrix + j, i);
449
450 if (small[i])
451 {
452 curr_rel[2*fac_num + 1] = i;
453 curr_rel[2*fac_num + 2] = small[i];
454 fac_num++;
455 }
456 }
457
458 for (i = 0; i < num_factors; i++)
459 {
460 if (factor[i].exp & 1) insert_col_entry(matrix + j, factor[i].ind);
461 curr_rel[2*fac_num + 1] = factor[i].ind;
462 curr_rel[2*fac_num + 2] = factor[i].exp;
463 fac_num++;
464 }
465
466 curr_rel[0] = fac_num;
467
468 matrix[j].orig = qs_inf->num_relations;
469
470 fmpz_set(qs_inf->Y_arr + qs_inf->num_relations, rel_list[j].Y);
471
472 qs_inf->curr_rel += qs_inf->max_factors*2;
473 qs_inf->num_relations++;
474 }
475
476 qs_inf->columns = qs_inf->num_relations;
477 }
478
479 /*
480 process relations from the file
481 */
qsieve_process_relation(qs_t qs_inf)482 int qsieve_process_relation(qs_t qs_inf)
483 {
484 char buf[1024];
485 char * str;
486 slong i, num_relations = 0, num_relations2, full = 0;
487 slong rel_list_length;
488 slong rlist_length;
489 mp_limb_t prime;
490 hash_t * entry;
491 mp_limb_t * hash_table = qs_inf->hash_table;
492 slong rel_size = 50000;
493 relation_t * rel_list = (relation_t *) flint_malloc(rel_size * sizeof(relation_t));
494 relation_t * rlist;
495 int done = 0;
496
497 qs_inf->siqs = fopen(qs_inf->fname, "r");
498
499 #if QS_DEBUG & 64
500 printf("Getting relations\n");
501 #endif
502
503 while (fgets(buf, sizeof(buf), qs_inf->siqs) != NULL)
504 {
505 prime = strtoul(buf, &str, 16);
506 entry = qsieve_get_table_entry(qs_inf, prime);
507
508 if (num_relations == rel_size)
509 {
510 rel_list = (relation_t *) flint_realloc(rel_list, 2*rel_size * sizeof(relation_t));
511 rel_size *= 2;
512 }
513
514 if (prime == 1 || entry->count >= 2)
515 {
516 rel_list[num_relations] = qsieve_parse_relation(qs_inf, str);
517 rel_list[num_relations].lp = prime;
518 num_relations++;
519 }
520 }
521
522 fclose(qs_inf->siqs);
523
524 #if QS_DEBUG & 64
525 printf("Removing duplicates\n");
526 #endif
527
528 num_relations = qsieve_remove_duplicates(rel_list, num_relations);
529 rel_list_length = num_relations;
530
531 #if QS_DEBUG & 64
532 printf("Merging relations\n");
533 #endif
534
535 rlist = flint_malloc(num_relations * sizeof(relation_t));
536 memset(hash_table, 0, (1 << 20) * sizeof(mp_limb_t));
537 qs_inf->vertices = 0;
538
539 rlist_length = 0;
540 for (i = 0; i < num_relations; i++)
541 {
542 if (rel_list[i].lp == UWORD(1))
543 {
544 rlist[rlist_length++] = rel_list[i];
545 full++;
546 }
547 else
548 {
549 entry = qsieve_get_table_entry(qs_inf, rel_list[i].lp);
550
551 if (entry->count == 0) entry->count = i;
552 else
553 {
554 if (fmpz_fdiv_ui(qs_inf->kn, rel_list[i].lp) == 0)
555 {
556 qs_inf->small_factor = rel_list[i].lp;
557
558 done = -1;
559 goto cleanup;
560 }
561 rlist[rlist_length++] = qsieve_merge_relation(qs_inf, rel_list[i], rel_list[entry->count]);
562 }
563 }
564 }
565
566 num_relations = rlist_length;
567
568 #if QS_DEBUG & 64
569 printf("Sorting relations\n");
570 #endif
571
572 if (rlist_length < qs_inf->num_primes + qs_inf->ks_primes + qs_inf->extra_rels)
573 {
574 qs_inf->edges -= 100;
575 done = 0;
576 qs_inf->siqs = fopen(qs_inf->fname, "a");
577 } else
578 {
579 done = 1;
580 num_relations2 = qs_inf->num_primes + qs_inf->ks_primes + qs_inf->extra_rels;
581 qsort(rlist, (size_t) num_relations2, sizeof(relation_t), qsieve_compare_relation);
582 qsieve_insert_relation(qs_inf, rlist, num_relations2);
583 }
584
585 cleanup:
586
587 for (i = 0; i < rel_list_length; i++)
588 {
589 /* it looks like rlist stole our data if rel_list[i].lp == UWORD(1)) */
590 if (rel_list[i].lp != UWORD(1))
591 {
592 flint_free(rel_list[i].small);
593 flint_free(rel_list[i].factor);
594 fmpz_clear(rel_list[i].Y);
595 }
596 }
597 flint_free(rel_list);
598
599 for (i = 0; i < rlist_length; i++)
600 {
601 flint_free(rlist[i].small);
602 flint_free(rlist[i].factor);
603 fmpz_clear(rlist[i].Y);
604 }
605 flint_free(rlist);
606
607 return done;
608 }
609
610