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