Statistiques
| Révision :

root / pobysoPythonSage / src / sageSLZ / sageSLZ.sage @ 246

Historique | Voir | Annoter | Télécharger (95,57 ko)

1
r"""
2
Sage core functions needed for the implementation of SLZ.
3

    
4
AUTHORS:
5
- S.T. (2013-08): initial version
6

    
7
Examples:
8

    
9
TODO::
10
"""
11
sys.stderr.write("sageSLZ loading...\n")
12
#
13
import inspect
14
#
15
def slz_compute_binade(number):
16
    """"
17
    For a given number, compute the "binade" that is integer m such that
18
    2^m <= number < 2^(m+1). If number == 0 return None.
19
    """
20
    # Checking the parameter.
21
    # The exception construction is used to detect if number is a RealNumber
22
    # since not all numbers have
23
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
24
    try:
25
        classTree = [number.__class__] + number.mro()
26
        # If the number is not a RealNumber (or offspring thereof) try
27
        # to transform it.
28
        if not sage.rings.real_mpfr.RealNumber in classTree:
29
            numberAsRR = RR(number)
30
        else:
31
            numberAsRR = number
32
    except AttributeError:
33
        return None
34
    # Zero special case.
35
    if numberAsRR == 0:
36
        return RR(-infinity)
37
    else:
38
        realField           = numberAsRR.parent()
39
        numberLog2          = numberAsRR.abs().log2()
40
        floorNumberLog2     = floor(numberLog2)
41
        ## Do not get caught by rounding of log2() both ways.
42
        ## When numberLog2 is an integer, compare numberAsRR
43
        #  with 2^numberLog2.
44
        if floorNumberLog2 == numberLog2:
45
            if numberAsRR.abs() < realField(2^floorNumberLog2):
46
                return floorNumberLog2 - 1
47
            else:
48
                return floorNumberLog2
49
        else:
50
            return floorNumberLog2
51
# End slz_compute_binade
52

    
53
#
54
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
55
    """
56
    For given "real number", compute the bounds of the binade it belongs to.
57
    
58
    NOTE::
59
        When number >= 2^(emax+1), we return the "fake" binade 
60
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
61
        with interval [-infinity, -2^(emax+1)]. We want to distinguish
62
        this case from that of "really" invalid arguments.
63
        
64
    """
65
    # Check the parameters.
66
    # RealNumbers or RealNumber offspring only.
67
    # The exception construction is necessary since not all objects have
68
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
69
    try:
70
        classTree = [number.__class__] + number.mro()
71
        if not sage.rings.real_mpfr.RealNumber in classTree:
72
            return None
73
    except AttributeError:
74
        return None
75
    # Non zero negative integers only for emin.
76
    if emin >= 0 or int(emin) != emin:
77
        return None
78
    # Non zero positive integers only for emax.
79
    if emax <= 0 or int(emax) != emax:
80
        return None
81
    precision = number.precision()
82
    RF  = RealField(precision)
83
    if number == 0:
84
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
85
    # A more precise RealField is needed to avoid unwanted rounding effects
86
    # when computing number.log2().
87
    RRF = RealField(max(2048, 2 * precision))
88
    # number = 0 special case, the binade bounds are 
89
    # [0, 2^emin - 2^(emin-precision)]
90
    # Begin general case
91
    l2 = RRF(number).abs().log2()
92
    # Another special one: beyond largest representable -> "Fake" binade.
93
    if l2 >= emax + 1:
94
        if number > 0:
95
            return (RF(2^(emax+1)), RF(+infinity) )
96
        else:
97
            return (RF(-infinity), -RF(2^(emax+1)))
98
    # Regular case cont'd.
99
    offset = int(l2)
100
    # number.abs() >= 1.
101
    if l2 >= 0:
102
        if number >= 0:
103
            lb = RF(2^offset)
104
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
105
        else: #number < 0
106
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
107
            ub = -RF(2^offset)
108
    else: # log2 < 0, number.abs() < 1.
109
        if l2 < emin: # Denormal
110
           # print "Denormal:", l2
111
            if number >= 0:
112
                lb = RF(0)
113
                ub = RF(2^(emin)) - RF(2^(emin-precision))
114
            else: # number <= 0
115
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
116
                ub = RF(0)
117
        elif l2 > emin: # Normal number other than +/-2^emin.
118
            if number >= 0:
119
                if int(l2) == l2:
120
                    lb = RF(2^(offset))
121
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
122
                else:
123
                    lb = RF(2^(offset-1))
124
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
125
            else: # number < 0
126
                if int(l2) == l2: # Binade limit.
127
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
128
                    ub = -RF(2^(offset))
129
                else:
130
                    lb = -RF(2^(offset) - 2^(-precision+offset))
131
                    ub = -RF(2^(offset-1))
132
        else: # l2== emin, number == +/-2^emin
133
            if number >= 0:
134
                lb = RF(2^(offset))
135
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
136
            else: # number < 0
137
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
138
                ub = -RF(2^(offset))
139
    return (lb, ub)
140
# End slz_compute_binade_bounds
141
#
142
def slz_compute_coppersmith_reduced_polynomials(inputPolynomial,
143
                                                 alpha,
144
                                                 N,
145
                                                 iBound,
146
                                                 tBound,
147
                                                 debug = False):
148
    """
149
    For a given set of arguments (see below), compute a list
150
    of "reduced polynomials" that could be used to compute roots
151
    of the inputPolynomial.
152
    INPUT:
153
    
154
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
155
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
156
    - "N" -- the modulus;
157
    - "iBound" -- the bound on the first variable;
158
    - "tBound" -- the bound on the second variable.
159
    
160
    OUTPUT:
161
    
162
    A list of bivariate integer polynomial obtained using the Coppersmith
163
    algorithm. The polynomials correspond to the rows of the LLL-reduce
164
    reduced base that comply with the Coppersmith condition.
165
    """
166
    # Arguments check.
167
    if iBound == 0 or tBound == 0:
168
        return None
169
    # End arguments check.
170
    nAtAlpha = N^alpha
171
    ## Building polynomials for matrix.
172
    polyRing   = inputPolynomial.parent()
173
    # Whatever the 2 variables are actually called, we call them
174
    # 'i' and 't' in all the variable names.
175
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
176
    #print polyVars[0], type(polyVars[0])
177
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
178
                                              tVariable:tVariable * tBound})
179
    if debug:
180
        polynomialsList = \
181
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
182
                                                 alpha,
183
                                                 N,
184
                                                 iBound,
185
                                                 tBound,
186
                                                 20)
187
    else:
188
        polynomialsList = \
189
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
190
                                                 alpha,
191
                                                 N,
192
                                                 iBound,
193
                                                 tBound,
194
                                                 0)
195
    #print "Polynomials list:", polynomialsList
196
    ## Building the proto matrix.
197
    knownMonomials = []
198
    protoMatrix    = []
199
    if debug:
200
        for poly in polynomialsList:
201
            spo_add_polynomial_coeffs_to_matrix_row(poly,
202
                                                    knownMonomials,
203
                                                    protoMatrix,
204
                                                    20)
205
    else:
206
        for poly in polynomialsList:
207
            spo_add_polynomial_coeffs_to_matrix_row(poly,
208
                                                    knownMonomials,
209
                                                    protoMatrix,
210
                                                    0)
211
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
212
    #print matrixToReduce
213
    ## Reduction and checking.
214
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
215
    #  error message issued when previous code was used.
216
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
217
    reducedMatrix = matrixToReduce.LLL(fp=None)
218
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
219
    if not isLLLReduced:
220
        return None
221
    monomialsCount     = len(knownMonomials)
222
    monomialsCountSqrt = sqrt(monomialsCount)
223
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
224
    #print reducedMatrix
225
    ## Check the Coppersmith condition for each row and build the reduced 
226
    #  polynomials.
227
    ccReducedPolynomialsList = []
228
    for row in reducedMatrix.rows():
229
        l2Norm = row.norm(2)
230
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
231
            #print (l2Norm * monomialsCountSqrt).n()
232
            #print l2Norm.n()
233
            ccReducedPolynomial = \
234
                slz_compute_reduced_polynomial(row,
235
                                               knownMonomials,
236
                                               iVariable,
237
                                               iBound,
238
                                               tVariable,
239
                                               tBound)
240
            if not ccReducedPolynomial is None:
241
                ccReducedPolynomialsList.append(ccReducedPolynomial)
242
        else:
243
            #print l2Norm.n() , ">", nAtAlpha
244
            pass
245
    if len(ccReducedPolynomialsList) < 2:
246
        print "Less than 2 Coppersmith condition compliant vectors."
247
        return ()
248
    #print ccReducedPolynomialsList
249
    return ccReducedPolynomialsList
250
# End slz_compute_coppersmith_reduced_polynomials
251
#
252
def slz_compute_coppersmith_reduced_polynomials_gram(inputPolynomial,
253
                                                     alpha,
254
                                                     N,
255
                                                     iBound,
256
                                                     tBound,
257
                                                     debug = False):
258
    """
259
    For a given set of arguments (see below), compute a list
260
    of "reduced polynomials" that could be used to compute roots
261
    of the inputPolynomial.
262
    INPUT:
263
    
264
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
265
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
266
    - "N" -- the modulus;
267
    - "iBound" -- the bound on the first variable;
268
    - "tBound" -- the bound on the second variable.
269
    
270
    OUTPUT:
271
    
272
    A list of bivariate integer polynomial obtained using the Coppersmith
273
    algorithm. The polynomials correspond to the rows of the LLL-reduce
274
    reduced base that comply with the Coppersmith condition.
275
    """
276
    # Arguments check.
277
    if iBound == 0 or tBound == 0:
278
        return None
279
    # End arguments check.
280
    nAtAlpha = N^alpha
281
    ## Building polynomials for matrix.
282
    polyRing   = inputPolynomial.parent()
283
    # Whatever the 2 variables are actually called, we call them
284
    # 'i' and 't' in all the variable names.
285
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
286
    #print polyVars[0], type(polyVars[0])
287
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
288
                                              tVariable:tVariable * tBound})
289
    if debug:
290
        polynomialsList = \
291
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
292
                                                 alpha,
293
                                                 N,
294
                                                 iBound,
295
                                                 tBound,
296
                                                 20)
297
    else:
298
        polynomialsList = \
299
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
300
                                                 alpha,
301
                                                 N,
302
                                                 iBound,
303
                                                 tBound,
304
                                                 0)
305
    #print "Polynomials list:", polynomialsList
306
    ## Building the proto matrix.
307
    knownMonomials = []
308
    protoMatrix    = []
309
    if debug:
310
        for poly in polynomialsList:
311
            spo_add_polynomial_coeffs_to_matrix_row(poly,
312
                                                    knownMonomials,
313
                                                    protoMatrix,
314
                                                    20)
315
    else:
316
        for poly in polynomialsList:
317
            spo_add_polynomial_coeffs_to_matrix_row(poly,
318
                                                    knownMonomials,
319
                                                    protoMatrix,
320
                                                    0)
321
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
322
    #print matrixToReduce
323
    ## Reduction and checking.
324
    ### In this variant we use the Pari LLL_gram reduction function.
325
    #   It works with the Gram matrix instead of the plain matrix.
326
    matrixToReduceTransposed = matrixToReduce.transpose() 
327
    matrixToReduceGram = matrixToReduce * matrixToReduceTransposed
328
    ### LLL_gram returns a unimodular transformation matrix such that:
329
    #   umt.transpose() * matrixToReduce * umt is reduced..
330
    umt = matrixToReduceGram.LLL_gram()
331
    #print "Unimodular transformation matrix:"
332
    #for row in umt.rows():
333
    #    print row
334
    ### The computed transformation matrix is transposed and applied to the
335
    #   left side of matrixToReduce. 
336
    reducedMatrix = umt.transpose() * matrixToReduce
337
    #print "Reduced matrix:"
338
    #for row in reducedMatrix.rows():
339
    #    print row
340
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
341
    #if not isLLLReduced:
342
    #    return None
343
    monomialsCount     = len(knownMonomials)
344
    monomialsCountSqrt = sqrt(monomialsCount)
345
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
346
    #print reducedMatrix
347
    ## Check the Coppersmith condition for each row and build the reduced 
348
    #  polynomials.
349
    ccReducedPolynomialsList = []
350
    for row in reducedMatrix.rows():
351
        l2Norm = row.norm(2)
352
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
353
            #print (l2Norm * monomialsCountSqrt).n()
354
            #print l2Norm.n()
355
            ccReducedPolynomial = \
356
                slz_compute_reduced_polynomial(row,
357
                                               knownMonomials,
358
                                               iVariable,
359
                                               iBound,
360
                                               tVariable,
361
                                               tBound)
362
            if not ccReducedPolynomial is None:
363
                ccReducedPolynomialsList.append(ccReducedPolynomial)
364
        else:
365
            #print l2Norm.n() , ">", nAtAlpha
366
            pass
367
    if len(ccReducedPolynomialsList) < 2:
368
        print "Less than 2 Coppersmith condition compliant vectors."
369
        return ()
370
    #print ccReducedPolynomialsList
371
    return ccReducedPolynomialsList
372
# End slz_compute_coppersmith_reduced_polynomials_gram
373
#
374
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
375
                                                                    alpha,
376
                                                                    N,
377
                                                                    iBound,
378
                                                                    tBound,
379
                                                                    debug = False):
380
    """
381
    For a given set of arguments (see below), compute a list
382
    of "reduced polynomials" that could be used to compute roots
383
    of the inputPolynomial.
384
    Print the volume of the initial basis as well.
385
    INPUT:
386
    
387
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
388
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
389
    - "N" -- the modulus;
390
    - "iBound" -- the bound on the first variable;
391
    - "tBound" -- the bound on the second variable.
392
    
393
    OUTPUT:
394
    
395
    A list of bivariate integer polynomial obtained using the Coppersmith
396
    algorithm. The polynomials correspond to the rows of the LLL-reduce
397
    reduced base that comply with the Coppersmith condition.
398
    """
399
    # Arguments check.
400
    if iBound == 0 or tBound == 0:
401
        return None
402
    # End arguments check.
403
    nAtAlpha = N^alpha
404
    if debug:
405
        print "N at alpha:", nAtAlpha
406
    ## Building polynomials for matrix.
407
    polyRing   = inputPolynomial.parent()
408
    # Whatever the 2 variables are actually called, we call them
409
    # 'i' and 't' in all the variable names.
410
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
411
    #print polyVars[0], type(polyVars[0])
412
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
413
                                              tVariable:tVariable * tBound})
414
##    polynomialsList = \
415
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
416
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
417
    polynomialsList = \
418
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
419
                                             alpha,
420
                                             N,
421
                                             iBound,
422
                                             tBound,
423
                                             0)
424
    #print "Polynomials list:", polynomialsList
425
    ## Building the proto matrix.
426
    knownMonomials = []
427
    protoMatrix    = []
428
    for poly in polynomialsList:
429
        spo_add_polynomial_coeffs_to_matrix_row(poly,
430
                                                knownMonomials,
431
                                                protoMatrix,
432
                                                0)
433
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
434
    matrixToReduceTranspose = matrixToReduce.transpose()
435
    squareMatrix = matrixToReduce * matrixToReduceTranspose
436
    squareMatDet = det(squareMatrix)
437
    latticeVolume = sqrt(squareMatDet)
438
    print "Lattice volume:", latticeVolume.n()
439
    print "Lattice volume / N:", (latticeVolume/N).n()
440
    #print matrixToReduce
441
    ## Reduction and checking.
442
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
443
    #  error message issued when previous code was used.
444
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
445
    reductionTimeStart = cputime() 
446
    reducedMatrix = matrixToReduce.LLL(fp=None)
447
    reductionTime = cputime(reductionTimeStart)
448
    print "Reduction time:", reductionTime
449
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
450
    if not isLLLReduced:
451
       return None
452
    #
453
    if debug:
454
        matrixFile = file('/tmp/reducedMatrix.txt', 'w')
455
        for row in reducedMatrix.rows():
456
            matrixFile.write(str(row) + "\n")
457
        matrixFile.close()
458
    #
459
    monomialsCount     = len(knownMonomials)
460
    monomialsCountSqrt = sqrt(monomialsCount)
461
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
462
    #print reducedMatrix
463
    ## Check the Coppersmith condition for each row and build the reduced 
464
    #  polynomials.
465
    ccVectorsCount = 0
466
    ccReducedPolynomialsList = []
467
    for row in reducedMatrix.rows():
468
        l2Norm = row.norm(2)
469
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
470
            #print (l2Norm * monomialsCountSqrt).n()
471
            #print l2Norm.n()
472
            ccVectorsCount +=1            
473
            ccReducedPolynomial = \
474
                slz_compute_reduced_polynomial(row,
475
                                               knownMonomials,
476
                                               iVariable,
477
                                               iBound,
478
                                               tVariable,
479
                                               tBound)
480
            if not ccReducedPolynomial is None:
481
                ccReducedPolynomialsList.append(ccReducedPolynomial)
482
        else:
483
            #print l2Norm.n() , ">", nAtAlpha
484
            pass
485
    if debug:
486
        print ccVectorsCount, "out of ", len(ccReducedPolynomialsList), 
487
        print "took Coppersmith text."
488
    if len(ccReducedPolynomialsList) < 2:
489
        print "Less than 2 Coppersmith condition compliant vectors."
490
        return ()
491
    if debug:
492
        print "Reduced and Coppersmith compliant polynomials list", ccReducedPolynomialsList
493
    return ccReducedPolynomialsList
494
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
495

    
496
def slz_compute_initial_lattice_matrix(inputPolynomial,
497
                                       alpha,
498
                                       N,
499
                                       iBound,
500
                                       tBound,
501
                                       debug = False):
502
    """
503
    For a given set of arguments (see below), compute the initial lattice
504
    that could be reduced. 
505
    INPUT:
506
    
507
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
508
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
509
    - "N" -- the modulus;
510
    - "iBound" -- the bound on the first variable;
511
    - "tBound" -- the bound on the second variable.
512
    
513
    OUTPUT:
514
    
515
    A list of bivariate integer polynomial obtained using the Coppersmith
516
    algorithm. The polynomials correspond to the rows of the LLL-reduce
517
    reduced base that comply with the Coppersmith condition.
518
    """
519
    # Arguments check.
520
    if iBound == 0 or tBound == 0:
521
        return None
522
    # End arguments check.
523
    nAtAlpha = N^alpha
524
    ## Building polynomials for matrix.
525
    polyRing   = inputPolynomial.parent()
526
    # Whatever the 2 variables are actually called, we call them
527
    # 'i' and 't' in all the variable names.
528
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
529
    #print polyVars[0], type(polyVars[0])
530
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
531
                                              tVariable:tVariable * tBound})
532
    polynomialsList = \
533
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
534
                                             alpha,
535
                                             N,
536
                                             iBound,
537
                                             tBound,
538
                                             0)
539
    #print "Polynomials list:", polynomialsList
540
    ## Building the proto matrix.
541
    knownMonomials = []
542
    protoMatrix    = []
543
    for poly in polynomialsList:
544
        spo_add_polynomial_coeffs_to_matrix_row(poly,
545
                                                knownMonomials,
546
                                                protoMatrix,
547
                                                0)
548
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
549
    if debug:
550
        print "Initial basis polynomials"
551
        for poly in polynomialsList:
552
            print poly
553
    return matrixToReduce
554
# End slz_compute_initial_lattice_matrix.
555

    
556
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
557
                                                 alpha,
558
                                                 N,
559
                                                 iBound,
560
                                                 tBound):
561
    """
562
    For a given set of arguments (see below), compute the polynomial modular 
563
    roots, if any.
564
    
565
    """
566
    # Arguments check.
567
    if iBound == 0 or tBound == 0:
568
        return set()
569
    # End arguments check.
570
    nAtAlpha = N^alpha
571
    ## Building polynomials for matrix.
572
    polyRing   = inputPolynomial.parent()
573
    # Whatever the 2 variables are actually called, we call them
574
    # 'i' and 't' in all the variable names.
575
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
576
    ccReducedPolynomialsList = \
577
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
578
                                                     alpha,
579
                                                     N,
580
                                                     iBound,
581
                                                     tBound)
582
    if len(ccReducedPolynomialsList) == 0:
583
        return set()   
584
    ## Create the valid (poly1 and poly2 are algebraically independent) 
585
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
586
    # Try to mix and match all the polynomial pairs built from the 
587
    # ccReducedPolynomialsList to obtain non zero resultants.
588
    resultantsInITuplesList = []
589
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
590
        for polyInnerIndex in xrange(polyOuterIndex+1, 
591
                                     len(ccReducedPolynomialsList)):
592
            # Compute the resultant in resultants in the
593
            # first variable (is it the optimal choice?).
594
            resultantInI = \
595
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
596
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
597
            #print "Resultant", resultantInI
598
            # Test algebraic independence.
599
            if not resultantInI.is_zero():
600
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
601
                                                 ccReducedPolynomialsList[polyInnerIndex],
602
                                                 resultantInI))
603
    # If no non zero resultant was found: we can't get no algebraically 
604
    # independent polynomials pair. Give up!
605
    if len(resultantsInITuplesList) == 0:
606
        return set()
607
    #print resultantsInITuplesList
608
    # Compute the roots.
609
    Zi = ZZ[str(iVariable)]
610
    Zt = ZZ[str(tVariable)]
611
    polynomialRootsSet = set()
612
    # First, solve in the second variable since resultants are in the first
613
    # variable.
614
    for resultantInITuple in resultantsInITuplesList:
615
        tRootsList = Zt(resultantInITuple[2]).roots()
616
        # For each tRoot, compute the corresponding iRoots and check 
617
        # them in the input polynomial.
618
        for tRoot in tRootsList:
619
            #print "tRoot:", tRoot
620
            # Roots returned by root() are (value, multiplicity) tuples.
621
            iRootsList = \
622
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
623
            print iRootsList
624
            # The iRootsList can be empty, hence the test.
625
            if len(iRootsList) != 0:
626
                for iRoot in iRootsList:
627
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
628
                    # polyEvalModN must be an integer.
629
                    if polyEvalModN == int(polyEvalModN):
630
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
631
    return polynomialRootsSet
632
# End slz_compute_integer_polynomial_modular_roots.
633
#
634
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
635
                                                 alpha,
636
                                                 N,
637
                                                 iBound,
638
                                                 tBound):
639
    """
640
    For a given set of arguments (see below), compute the polynomial modular 
641
    roots, if any.
642
    This version differs in the way resultants are computed.   
643
    """
644
    # Arguments check.
645
    if iBound == 0 or tBound == 0:
646
        return set()
647
    # End arguments check.
648
    nAtAlpha = N^alpha
649
    ## Building polynomials for matrix.
650
    polyRing   = inputPolynomial.parent()
651
    # Whatever the 2 variables are actually called, we call them
652
    # 'i' and 't' in all the variable names.
653
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
654
    #print polyVars[0], type(polyVars[0])
655
    ccReducedPolynomialsList = \
656
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
657
                                                     alpha,
658
                                                     N,
659
                                                     iBound,
660
                                                     tBound)
661
    if len(ccReducedPolynomialsList) == 0:
662
        return set()   
663
    ## Create the valid (poly1 and poly2 are algebraically independent) 
664
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
665
    # Try to mix and match all the polynomial pairs built from the 
666
    # ccReducedPolynomialsList to obtain non zero resultants.
667
    resultantsInTTuplesList = []
668
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
669
        for polyInnerIndex in xrange(polyOuterIndex+1, 
670
                                     len(ccReducedPolynomialsList)):
671
            # Compute the resultant in resultants in the
672
            # first variable (is it the optimal choice?).
673
            resultantInT = \
674
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
675
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
676
            #print "Resultant", resultantInT
677
            # Test algebraic independence.
678
            if not resultantInT.is_zero():
679
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
680
                                                 ccReducedPolynomialsList[polyInnerIndex],
681
                                                 resultantInT))
682
    # If no non zero resultant was found: we can't get no algebraically 
683
    # independent polynomials pair. Give up!
684
    if len(resultantsInTTuplesList) == 0:
685
        return set()
686
    #print resultantsInITuplesList
687
    # Compute the roots.
688
    Zi = ZZ[str(iVariable)]
689
    Zt = ZZ[str(tVariable)]
690
    polynomialRootsSet = set()
691
    # First, solve in the second variable since resultants are in the first
692
    # variable.
693
    for resultantInTTuple in resultantsInTTuplesList:
694
        iRootsList = Zi(resultantInTTuple[2]).roots()
695
        # For each iRoot, compute the corresponding tRoots and check 
696
        # them in the input polynomial.
697
        for iRoot in iRootsList:
698
            #print "iRoot:", iRoot
699
            # Roots returned by root() are (value, multiplicity) tuples.
700
            tRootsList = \
701
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
702
            print tRootsList
703
            # The tRootsList can be empty, hence the test.
704
            if len(tRootsList) != 0:
705
                for tRoot in tRootsList:
706
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
707
                    # polyEvalModN must be an integer.
708
                    if polyEvalModN == int(polyEvalModN):
709
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
710
    return polynomialRootsSet
711
# End slz_compute_integer_polynomial_modular_roots_2.
712
#
713
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
714
                                        upperBoundSa, approxAccurSa, 
715
                                        precSa=None):
716
    """
717
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
718
    a polynomial that approximates the function on a an interval starting
719
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
720
    approximates with the expected precision.
721
    The interval upper bound is lowered until the expected approximation 
722
    precision is reached.
723
    The polynomial, the bounds, the center of the interval and the error
724
    are returned.
725
    OUTPUT:
726
    A tuple made of 4 Sollya objects:
727
    - a polynomial;
728
    - an range (an interval, not in the sense of number given as an interval);
729
    - the center of the interval;
730
    - the maximum error in the approximation of the input functionSo by the
731
      output polynomial ; this error <= approxAccurSaS.
732
    
733
    """
734
    #print"In slz_compute_polynomial_and_interval..."
735
    ## Superficial argument check.
736
    if lowerBoundSa > upperBoundSa:
737
        return None
738
    ## Change Sollya precision, if requested.
739
    if precSa is None:
740
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
741
        #print "Computed internal precision:", precSa
742
        if precSa < 192:
743
            precSa = 192
744
    sollyaPrecChanged = False
745
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
746
    if precSa > initialSollyaPrecSa:
747
        if precSa <= 2:
748
            print inspect.stack()[0][3], ": precision change <=2 requested."
749
        pobyso_set_prec_sa_so(precSa)
750
        sollyaPrecChanged = True
751
    RRR = lowerBoundSa.parent()
752
    intervalShrinkConstFactorSa = RRR('0.9')
753
    absoluteErrorTypeSo = pobyso_absolute_so_so()
754
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
755
    currentUpperBoundSa = upperBoundSa
756
    currentLowerBoundSa = lowerBoundSa
757
    # What we want here is the polynomial without the variable change, 
758
    # since our actual variable will be x-intervalCenter defined over the 
759
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
760
    (polySo, intervalCenterSo, maxErrorSo) = \
761
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
762
                                                    currentRangeSo, 
763
                                                    absoluteErrorTypeSo)
764
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
765
    while maxErrorSa > approxAccurSa:
766
        print "++Approximation error:", maxErrorSa.n()
767
        sollya_lib_clear_obj(polySo)
768
        sollya_lib_clear_obj(intervalCenterSo)
769
        sollya_lib_clear_obj(maxErrorSo)
770
        # Very empirical shrinking factor.
771
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
772
        print "Shrink factor:", \
773
              shrinkFactorSa.n(), \
774
              intervalShrinkConstFactorSa
775
        print
776
        #errorRatioSa = approxAccurSa/maxErrorSa
777
        #print "Error ratio: ", errorRatioSa
778
        # Make sure interval shrinks.
779
        if shrinkFactorSa > intervalShrinkConstFactorSa:
780
            actualShrinkFactorSa = intervalShrinkConstFactorSa
781
            #print "Fixed"
782
        else:
783
            actualShrinkFactorSa = shrinkFactorSa
784
            #print "Computed",shrinkFactorSa,maxErrorSa
785
            #print shrinkFactorSa, maxErrorSa
786
        #print "Shrink factor", actualShrinkFactorSa
787
        currentUpperBoundSa = currentLowerBoundSa + \
788
                                (currentUpperBoundSa - currentLowerBoundSa) * \
789
                                actualShrinkFactorSa
790
        #print "Current upper bound:", currentUpperBoundSa
791
        sollya_lib_clear_obj(currentRangeSo)
792
        # Check what is left with the bounds.
793
        if currentUpperBoundSa <= currentLowerBoundSa or \
794
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
795
            sollya_lib_clear_obj(absoluteErrorTypeSo)
796
            print "Can't find an interval."
797
            print "Use either or both a higher polynomial degree or a higher",
798
            print "internal precision."
799
            print "Aborting!"
800
            if sollyaPrecChanged:
801
                pobyso_set_prec_so_so(initialSollyaPrecSo)
802
            sollya_lib_clear_obj(initialSollyaPrecSo)
803
            return None
804
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
805
                                                      currentUpperBoundSa)
806
        # print "New interval:",
807
        # pobyso_autoprint(currentRangeSo)
808
        #print "Second Taylor expansion call."
809
        (polySo, intervalCenterSo, maxErrorSo) = \
810
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
811
                                                        currentRangeSo, 
812
                                                        absoluteErrorTypeSo)
813
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
814
        #print "Max errorSo:",
815
        #pobyso_autoprint(maxErrorSo)
816
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
817
        #print "Max errorSa:", maxErrorSa
818
        #print "Sollya prec:",
819
        #pobyso_autoprint(sollya_lib_get_prec(None))
820
    # End while
821
    sollya_lib_clear_obj(absoluteErrorTypeSo)
822
    if sollyaPrecChanged:
823
        pobyso_set_prec_so_so(initialSollyaPrecSo)
824
    sollya_lib_clear_obj(initialSollyaPrecSo)
825
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
826
# End slz_compute_polynomial_and_interval
827

    
828
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
829
                                        upperBoundSa, approxAccurSa, 
830
                                        sollyaPrecSa=None, debug=False):
831
    """
832
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
833
    a polynomial that approximates the function on a an interval starting
834
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
835
    approximates with the expected precision.
836
    The interval upper bound is lowered until the expected approximation 
837
    precision is reached.
838
    The polynomial, the bounds, the center of the interval and the error
839
    are returned.
840
    OUTPUT:
841
    A tuple made of 4 Sollya objects:
842
    - a polynomial;
843
    - an range (an interval, not in the sense of number given as an interval);
844
    - the center of the interval;
845
    - the maximum error in the approximation of the input functionSo by the
846
      output polynomial ; this error <= approxAccurSaS.
847
    
848
    """
849
    #print"In slz_compute_polynomial_and_interval..."
850
    ## Superficial argument check.
851
    if lowerBoundSa > upperBoundSa:
852
        print inspect.stack()[0][3], ": lower bound is larger than upper bound. "
853
        return None
854
    ## Change Sollya precision, if requested.
855
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
856
    sollyaPrecChangedSa = False
857
    if sollyaPrecSa is None:
858
        sollyaPrecSa = initialSollyaPrecSa
859
    else:
860
        if sollyaPrecSa > initialSollyaPrecSa:
861
            if sollyaPrecSa <= 2:
862
                print inspect.stack()[0][3], ": precision change <= 2 requested."
863
            pobyso_set_prec_sa_so(sollyaPrecSa)
864
            sollyaPrecChangedSa = True
865
    ## Other initializations and data recovery.
866
    RRR = lowerBoundSa.parent()
867
    intervalShrinkConstFactorSa = RRR('0.9')
868
    absoluteErrorTypeSo = pobyso_absolute_so_so()
869
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
870
    currentUpperBoundSa = upperBoundSa
871
    currentLowerBoundSa = lowerBoundSa
872
    # What we want here is the polynomial without the variable change, 
873
    # since our actual variable will be x-intervalCenter defined over the 
874
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
875
    (polySo, intervalCenterSo, maxErrorSo) = \
876
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
877
                                                    currentRangeSo, 
878
                                                    absoluteErrorTypeSo)
879
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
880
    while maxErrorSa > approxAccurSa:
881
        print "++Approximation error:", maxErrorSa.n()
882
        sollya_lib_clear_obj(polySo)
883
        sollya_lib_clear_obj(intervalCenterSo)
884
        sollya_lib_clear_obj(maxErrorSo)
885
        # Very empirical shrinking factor.
886
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
887
        print "Shrink factor:", \
888
              shrinkFactorSa.n(), \
889
              intervalShrinkConstFactorSa
890
        print
891
        #errorRatioSa = approxAccurSa/maxErrorSa
892
        #print "Error ratio: ", errorRatioSa
893
        # Make sure interval shrinks.
894
        if shrinkFactorSa > intervalShrinkConstFactorSa:
895
            actualShrinkFactorSa = intervalShrinkConstFactorSa
896
            #print "Fixed"
897
        else:
898
            actualShrinkFactorSa = shrinkFactorSa
899
            #print "Computed",shrinkFactorSa,maxErrorSa
900
            #print shrinkFactorSa, maxErrorSa
901
        #print "Shrink factor", actualShrinkFactorSa
902
        currentUpperBoundSa = currentLowerBoundSa + \
903
                                (currentUpperBoundSa - currentLowerBoundSa) * \
904
                                actualShrinkFactorSa
905
        #print "Current upper bound:", currentUpperBoundSa
906
        sollya_lib_clear_obj(currentRangeSo)
907
        # Check what is left with the bounds.
908
        if currentUpperBoundSa <= currentLowerBoundSa or \
909
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
910
            sollya_lib_clear_obj(absoluteErrorTypeSo)
911
            print "Can't find an interval."
912
            print "Use either or both a higher polynomial degree or a higher",
913
            print "internal precision."
914
            print "Aborting!"
915
            if sollyaPrecChangedSa:
916
                pobyso_set_prec_so_so(initialSollyaPrecSo)
917
            sollya_lib_clear_obj(initialSollyaPrecSo)
918
            return None
919
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
920
                                                      currentUpperBoundSa)
921
        # print "New interval:",
922
        # pobyso_autoprint(currentRangeSo)
923
        #print "Second Taylor expansion call."
924
        (polySo, intervalCenterSo, maxErrorSo) = \
925
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
926
                                                        currentRangeSo, 
927
                                                        absoluteErrorTypeSo)
928
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
929
        #print "Max errorSo:",
930
        #pobyso_autoprint(maxErrorSo)
931
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
932
        #print "Max errorSa:", maxErrorSa
933
        #print "Sollya prec:",
934
        #pobyso_autoprint(sollya_lib_get_prec(None))
935
    # End while
936
    sollya_lib_clear_obj(absoluteErrorTypeSo)
937
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
938
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
939
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
940
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
941
    if debug:
942
        print inspect.stack()[0][3], "SollyaPrecSa:", sollyaPrecSa
943
        print "About to call polynomial rounding with:"
944
        print "polySo: ",           ; pobyso_autoprint(polySo)
945
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
946
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
947
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
948
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
949
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
950
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
951
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
952
    """
953
    # Naive rounding.
954
    (roundedPolySo, roundedPolyMaxErrSo) = \
955
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
956
                                                           functionSo,
957
                                                           intervalCenterSo,
958
                                                           currentRangeSo,
959
                                                           itpSo,
960
                                                           ftpSo,
961
                                                           maxPrecSo,
962
                                                           approxAccurSo)
963
    """
964
    # Proved rounding.
965
    (roundedPolySo, roundedPolyMaxErrSo) = \
966
        pobyso_round_coefficients_progressive_so_so(polySo, 
967
                                                    functionSo,
968
                                                    maxPrecSo,
969
                                                    currentRangeSo,
970
                                                    intervalCenterSo,
971
                                                    maxErrorSo,
972
                                                    approxAccurSo,
973
                                                    debug=False)
974
    #### Comment out the two next lines when polynomial rounding is activated.
975
    #roundedPolySo = sollya_lib_copy_obj(polySo)
976
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
977
    sollya_lib_clear_obj(polySo)
978
    sollya_lib_clear_obj(maxErrorSo)
979
    sollya_lib_clear_obj(itpSo)
980
    sollya_lib_clear_obj(ftpSo)
981
    sollya_lib_clear_obj(approxAccurSo)
982
    if sollyaPrecChangedSa:
983
        pobyso_set_prec_so_so(initialSollyaPrecSo)
984
    sollya_lib_clear_obj(initialSollyaPrecSo)
985
    if debug:
986
        print "1: ", ; pobyso_autoprint(roundedPolySo)
987
        print "2: ", ; pobyso_autoprint(currentRangeSo)
988
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
989
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
990
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
991
# End slz_compute_polynomial_and_interval_01
992

    
993
def slz_compute_polynomial_and_interval_02(functionSo, degreeSo, lowerBoundSa, 
994
                                        upperBoundSa, approxAccurSa, 
995
                                        sollyaPrecSa=None, debug=True ):
996
    """
997
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
998
    a polynomial that approximates the function on a an interval starting
999
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1000
    approximates with the expected precision.
1001
    The interval upper bound is lowered until the expected approximation 
1002
    precision is reached.
1003
    The polynomial, the bounds, the center of the interval and the error
1004
    are returned.
1005
    OUTPUT:
1006
    A tuple made of 4 Sollya objects:
1007
    - a polynomial;
1008
    - an range (an interval, not in the sense of number given as an interval);
1009
    - the center of the interval;
1010
    - the maximum error in the approximation of the input functionSo by the
1011
      output polynomial ; this error <= approxAccurSaS.
1012
    Changes fom v 01:
1013
        extra verbose.
1014
    """
1015
    print"In slz_compute_polynomial_and_interval..."
1016
    ## Superficial argument check.
1017
    if lowerBoundSa > upperBoundSa:
1018
        return None
1019
    ## Change Sollya precision, if requested.
1020
    sollyaPrecChanged = False
1021
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1022
    #print "Initial Sollya prec:", initialSollyaPrecSa, type(initialSollyaPrecSa)
1023
    if sollyaPrecSa is None:
1024
        sollyaPrecSa = initialSollyaPrecSa
1025
    else:
1026
        if sollyaPrecSa <= 2:
1027
            print inspect.stack()[0][3], ": precision change <=2 requested."
1028
        pobyso_set_prec_sa_so(sollyaPrecSa)
1029
        sollyaPrecChanged = True
1030
    RRR = lowerBoundSa.parent()
1031
    intervalShrinkConstFactorSa = RRR('0.9')
1032
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1033
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1034
    currentUpperBoundSa = upperBoundSa
1035
    currentLowerBoundSa = lowerBoundSa
1036
    #pobyso_autoprint(functionSo)
1037
    #pobyso_autoprint(degreeSo)
1038
    #pobyso_autoprint(currentRangeSo)
1039
    #pobyso_autoprint(absoluteErrorTypeSo)
1040
    ## What we want here is the polynomial without the variable change, 
1041
    #  since our actual variable will be x-intervalCenter defined over the 
1042
    #  domain [lowerBound-intervalCenter , upperBound-intervalCenter].
1043
    (polySo, intervalCenterSo, maxErrorSo) = \
1044
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1045
                                                    currentRangeSo, 
1046
                                                    absoluteErrorTypeSo)
1047
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1048
    print "...after Taylor expansion."
1049
    while maxErrorSa > approxAccurSa:
1050
        print "++Approximation error:", maxErrorSa.n()
1051
        sollya_lib_clear_obj(polySo)
1052
        sollya_lib_clear_obj(intervalCenterSo)
1053
        sollya_lib_clear_obj(maxErrorSo)
1054
        # Very empirical shrinking factor.
1055
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1056
        print "Shrink factor:", \
1057
              shrinkFactorSa.n(), \
1058
              intervalShrinkConstFactorSa
1059
        print
1060
        #errorRatioSa = approxAccurSa/maxErrorSa
1061
        #print "Error ratio: ", errorRatioSa
1062
        # Make sure interval shrinks.
1063
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1064
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1065
            #print "Fixed"
1066
        else:
1067
            actualShrinkFactorSa = shrinkFactorSa
1068
            #print "Computed",shrinkFactorSa,maxErrorSa
1069
            #print shrinkFactorSa, maxErrorSa
1070
        #print "Shrink factor", actualShrinkFactorSa
1071
        currentUpperBoundSa = currentLowerBoundSa + \
1072
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1073
                                actualShrinkFactorSa
1074
        #print "Current upper bound:", currentUpperBoundSa
1075
        sollya_lib_clear_obj(currentRangeSo)
1076
        # Check what is left with the bounds.
1077
        if currentUpperBoundSa <= currentLowerBoundSa or \
1078
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1079
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1080
            print "Can't find an interval."
1081
            print "Use either or both a higher polynomial degree or a higher",
1082
            print "internal precision."
1083
            print "Aborting!"
1084
            if sollyaPrecChanged:
1085
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1086
            sollya_lib_clear_obj(initialSollyaPrecSo)
1087
            return None
1088
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1089
                                                      currentUpperBoundSa)
1090
        # print "New interval:",
1091
        # pobyso_autoprint(currentRangeSo)
1092
        #print "Second Taylor expansion call."
1093
        (polySo, intervalCenterSo, maxErrorSo) = \
1094
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1095
                                                        currentRangeSo, 
1096
                                                        absoluteErrorTypeSo)
1097
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1098
        #print "Max errorSo:",
1099
        #pobyso_autoprint(maxErrorSo)
1100
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1101
        #print "Max errorSa:", maxErrorSa
1102
        #print "Sollya prec:",
1103
        #pobyso_autoprint(sollya_lib_get_prec(None))
1104
    # End while
1105
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1106
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1107
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1108
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1109
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1110
    print "About to call polynomial rounding with:"
1111
    print "polySo: ",           ; pobyso_autoprint(polySo)
1112
    print "functionSo: ",       ; pobyso_autoprint(functionSo)
1113
    print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1114
    print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1115
    print "itpSo: ",            ; pobyso_autoprint(itpSo)
1116
    print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1117
    print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1118
    print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1119
    (roundedPolySo, roundedPolyMaxErrSo) = \
1120
    pobyso_round_coefficients_progressive_so_so(polySo,
1121
                                                functionSo,
1122
                                                maxPrecSo,
1123
                                                currentRangeSo,
1124
                                                intervalCenterSo,
1125
                                                maxErrorSo,
1126
                                                approxAccurSo,
1127
                                                debug = True)
1128
    
1129
    sollya_lib_clear_obj(polySo)
1130
    sollya_lib_clear_obj(maxErrorSo)
1131
    sollya_lib_clear_obj(itpSo)
1132
    sollya_lib_clear_obj(ftpSo)
1133
    sollya_lib_clear_obj(approxAccurSo)
1134
    if sollyaPrecChanged:
1135
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1136
    sollya_lib_clear_obj(initialSollyaPrecSo)
1137
    print "1: ", ; pobyso_autoprint(roundedPolySo)
1138
    print "2: ", ; pobyso_autoprint(currentRangeSo)
1139
    print "3: ", ; pobyso_autoprint(intervalCenterSo)
1140
    print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1141
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1142
# End slz_compute_polynomial_and_interval_02
1143

    
1144
def slz_compute_reduced_polynomial(matrixRow,
1145
                                    knownMonomials,
1146
                                    var1,
1147
                                    var1Bound,
1148
                                    var2,
1149
                                    var2Bound):
1150
    """
1151
    Compute a polynomial from a single reduced matrix row.
1152
    This function was introduced in order to avoid the computation of the
1153
    all the polynomials  from the full matrix (even those built from rows 
1154
    that do no verify the Coppersmith condition) as this may involves 
1155
    expensive operations over (large) integers.
1156
    """
1157
    ## Check arguments.
1158
    if len(knownMonomials) == 0:
1159
        return None
1160
    # varNounds can be zero since 0^0 returns 1.
1161
    if (var1Bound < 0) or (var2Bound < 0):
1162
        return None
1163
    ## Initialisations. 
1164
    polynomialRing = knownMonomials[0].parent() 
1165
    currentPolynomial = polynomialRing(0)
1166
    # TODO: use zip instead of indices.
1167
    for colIndex in xrange(0, len(knownMonomials)):
1168
        currentCoefficient = matrixRow[colIndex]
1169
        if currentCoefficient != 0:
1170
            #print "Current coefficient:", currentCoefficient
1171
            currentMonomial = knownMonomials[colIndex]
1172
            #print "Monomial as multivariate polynomial:", \
1173
            #currentMonomial, type(currentMonomial)
1174
            degreeInVar1 = currentMonomial.degree(var1)
1175
            #print "Degree in var1", var1, ":", degreeInVar1
1176
            degreeInVar2 = currentMonomial.degree(var2)
1177
            #print "Degree in var2", var2, ":", degreeInVar2
1178
            if degreeInVar1 > 0:
1179
                currentCoefficient = \
1180
                    currentCoefficient / (var1Bound^degreeInVar1)
1181
                #print "varBound1 in degree:", var1Bound^degreeInVar1
1182
                #print "Current coefficient(1)", currentCoefficient
1183
            if degreeInVar2 > 0:
1184
                currentCoefficient = \
1185
                    currentCoefficient / (var2Bound^degreeInVar2)
1186
                #print "Current coefficient(2)", currentCoefficient
1187
            #print "Current reduced monomial:", (currentCoefficient * \
1188
            #                                    currentMonomial)
1189
            currentPolynomial += (currentCoefficient * currentMonomial)
1190
            #print "Current polynomial:", currentPolynomial
1191
        # End if
1192
    # End for colIndex.
1193
    #print "Type of the current polynomial:", type(currentPolynomial)
1194
    return(currentPolynomial)
1195
# End slz_compute_reduced_polynomial
1196
#
1197
def slz_compute_reduced_polynomials(reducedMatrix,
1198
                                        knownMonomials,
1199
                                        var1,
1200
                                        var1Bound,
1201
                                        var2,
1202
                                        var2Bound):
1203
    """
1204
    Legacy function, use slz_compute_reduced_polynomials_list
1205
    """
1206
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1207
                                                knownMonomials,
1208
                                                var1,
1209
                                                var1Bound,
1210
                                                var2,
1211
                                                var2Bound)
1212
    )
1213
#
1214
def slz_compute_reduced_polynomials_list(reducedMatrix,
1215
                                         knownMonomials,
1216
                                         var1,
1217
                                         var1Bound,
1218
                                         var2,
1219
                                         var2Bound):
1220
    """
1221
    From a reduced matrix, holding the coefficients, from a monomials list,
1222
    from the bounds of each variable, compute the corresponding polynomials
1223
    scaled back by dividing by the "right" powers of the variables bounds.
1224
    
1225
    The elements in knownMonomials must be of the "right" polynomial type.
1226
    They set the polynomial type of the output polynomials in list.
1227
    @param reducedMatrix:  the reduced matrix as output from LLL;
1228
    @param kwnonMonomials: the ordered list of the monomials used to
1229
                           build the polynomials;
1230
    @param var1:           the first variable (of the "right" type);
1231
    @param var1Bound:      the first variable bound;
1232
    @param var2:           the second variable (of the "right" type);
1233
    @param var2Bound:      the second variable bound.
1234
    @return: a list of polynomials obtained with the reduced coefficients
1235
             and scaled down with the bounds
1236
    """
1237
    
1238
    # TODO: check input arguments.
1239
    reducedPolynomials = []
1240
    #print "type var1:", type(var1), " - type var2:", type(var2)
1241
    for matrixRow in reducedMatrix.rows():
1242
        currentPolynomial = 0
1243
        for colIndex in xrange(0, len(knownMonomials)):
1244
            currentCoefficient = matrixRow[colIndex]
1245
            if currentCoefficient != 0:
1246
                #print "Current coefficient:", currentCoefficient
1247
                currentMonomial = knownMonomials[colIndex]
1248
                parentRing = currentMonomial.parent()
1249
                #print "Monomial as multivariate polynomial:", \
1250
                #currentMonomial, type(currentMonomial)
1251
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1252
                #print "Degree in var", var1, ":", degreeInVar1
1253
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1254
                #print "Degree in var", var2, ":", degreeInVar2
1255
                if degreeInVar1 > 0:
1256
                    currentCoefficient /= var1Bound^degreeInVar1
1257
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1258
                    #print "Current coefficient(1)", currentCoefficient
1259
                if degreeInVar2 > 0:
1260
                    currentCoefficient /= var2Bound^degreeInVar2
1261
                    #print "Current coefficient(2)", currentCoefficient
1262
                #print "Current reduced monomial:", (currentCoefficient * \
1263
                #                                    currentMonomial)
1264
                currentPolynomial += (currentCoefficient * currentMonomial)
1265
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1266
                    #print "!!!! constant term !!!!"
1267
                #print "Current polynomial:", currentPolynomial
1268
            # End if
1269
        # End for colIndex.
1270
        #print "Type of the current polynomial:", type(currentPolynomial)
1271
        reducedPolynomials.append(currentPolynomial)
1272
    return reducedPolynomials 
1273
# End slz_compute_reduced_polynomials_list.
1274

    
1275
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1276
                                                   knownMonomials,
1277
                                                   var1,
1278
                                                   var1Bound,
1279
                                                   var2,
1280
                                                   var2Bound):
1281
    """
1282
    From a list of rows, holding the coefficients, from a monomials list,
1283
    from the bounds of each variable, compute the corresponding polynomials
1284
    scaled back by dividing by the "right" powers of the variables bounds.
1285
    
1286
    The elements in knownMonomials must be of the "right" polynomial type.
1287
    They set the polynomial type of the output polynomials in list.
1288
    @param rowsList:       the reduced matrix as output from LLL;
1289
    @param kwnonMonomials: the ordered list of the monomials used to
1290
                           build the polynomials;
1291
    @param var1:           the first variable (of the "right" type);
1292
    @param var1Bound:      the first variable bound;
1293
    @param var2:           the second variable (of the "right" type);
1294
    @param var2Bound:      the second variable bound.
1295
    @return: a list of polynomials obtained with the reduced coefficients
1296
             and scaled down with the bounds
1297
    """
1298
    
1299
    # TODO: check input arguments.
1300
    reducedPolynomials = []
1301
    #print "type var1:", type(var1), " - type var2:", type(var2)
1302
    for matrixRow in rowsList:
1303
        currentPolynomial = 0
1304
        for colIndex in xrange(0, len(knownMonomials)):
1305
            currentCoefficient = matrixRow[colIndex]
1306
            if currentCoefficient != 0:
1307
                #print "Current coefficient:", currentCoefficient
1308
                currentMonomial = knownMonomials[colIndex]
1309
                parentRing = currentMonomial.parent()
1310
                #print "Monomial as multivariate polynomial:", \
1311
                #currentMonomial, type(currentMonomial)
1312
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1313
                #print "Degree in var", var1, ":", degreeInVar1
1314
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1315
                #print "Degree in var", var2, ":", degreeInVar2
1316
                if degreeInVar1 > 0:
1317
                    currentCoefficient /= var1Bound^degreeInVar1
1318
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1319
                    #print "Current coefficient(1)", currentCoefficient
1320
                if degreeInVar2 > 0:
1321
                    currentCoefficient /= var2Bound^degreeInVar2
1322
                    #print "Current coefficient(2)", currentCoefficient
1323
                #print "Current reduced monomial:", (currentCoefficient * \
1324
                #                                    currentMonomial)
1325
                currentPolynomial += (currentCoefficient * currentMonomial)
1326
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1327
                    #print "!!!! constant term !!!!"
1328
                #print "Current polynomial:", currentPolynomial
1329
            # End if
1330
        # End for colIndex.
1331
        #print "Type of the current polynomial:", type(currentPolynomial)
1332
        reducedPolynomials.append(currentPolynomial)
1333
    return reducedPolynomials 
1334
# End slz_compute_reduced_polynomials_list_from_rows.
1335
#
1336
def slz_compute_scaled_function(functionSa,
1337
                                lowerBoundSa,
1338
                                upperBoundSa,
1339
                                floatingPointPrecSa,
1340
                                debug=False):
1341
    """
1342
    From a function, compute the scaled function whose domain
1343
    is included in [1, 2) and whose image is also included in [1,2).
1344
    Return a tuple: 
1345
    [0]: the scaled function
1346
    [1]: the scaled domain lower bound
1347
    [2]: the scaled domain upper bound
1348
    [3]: the scaled image lower bound
1349
    [4]: the scaled image upper bound
1350
    """
1351
    ## The variable can be called anything.
1352
    x = functionSa.variables()[0]
1353
    # Scalling the domain -> [1,2[.
1354
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1355
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1356
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1357
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1358
    if debug:
1359
        print "domainScalingExpression for argument :", \
1360
        invDomainScalingExpressionSa
1361
        print "function: ", functionSa
1362
    ff = functionSa.subs({x : domainScalingExpressionSa})
1363
    ## Bless expression as a function.
1364
    ff = ff.function(x)
1365
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1366
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1367
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1368
    scaledLowerBoundSa = \
1369
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1370
    scaledUpperBoundSa = \
1371
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1372
    if debug:
1373
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1374
              scaledUpperBoundSa
1375
    #
1376
    # Scalling the image -> [1,2[.
1377
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
1378
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
1379
    if flbSa <= fubSa: # Increasing
1380
        imageBinadeBottomSa = floor(flbSa.log2())
1381
    else: # Decreasing
1382
        imageBinadeBottomSa = floor(fubSa.log2())
1383
    if debug:
1384
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
1385
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
1386
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
1387
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
1388
    if debug:
1389
        print "imageScalingExpression for argument :", \
1390
              invImageScalingExpressionSa
1391
    iis = invImageScalingExpressionSa.function(x)
1392
    fff = iis.subs({x:ff})
1393
    if debug:
1394
        print "fff:", fff,
1395
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1396
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1397
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1398
# End slz_compute_scaled_function
1399

    
1400
def slz_fix_bounds_for_binades(lowerBound, 
1401
                               upperBound, 
1402
                               func = None, 
1403
                               domainDirection = -1,
1404
                               imageDirection  = -1):
1405
    """
1406
    Assuming the function is increasing or decreasing over the 
1407
    [lowerBound, upperBound] interval, return a lower bound lb and 
1408
    an upper bound ub such that:
1409
    - lb and ub belong to the same binade;
1410
    - func(lb) and func(ub) belong to the same binade.
1411
    domainDirection indicate how bounds move to fit into the same binade:
1412
    - a negative value move the upper bound down;
1413
    - a positive value move the lower bound up.
1414
    imageDirection indicate how bounds move in order to have their image
1415
    fit into the same binade, variation of the function is also condidered.
1416
    For an increasing function:
1417
    - negative value moves the upper bound down (and its image value as well);
1418
    - a positive values moves the lower bound up (and its image value as well);
1419
    For a decreasing function it is the other way round.
1420
    """
1421
    ## Arguments check
1422
    if lowerBound > upperBound:
1423
        return None
1424
    if lowerBound == upperBound:
1425
        return (lowerBound, upperBound)
1426
    if func is None:
1427
        return None
1428
    #
1429
    #varFunc = func.variables()[0]
1430
    lb = lowerBound
1431
    ub = upperBound
1432
    lbBinade = slz_compute_binade(lb) 
1433
    ubBinade = slz_compute_binade(ub)
1434
    ## Domain binade.
1435
    while lbBinade != ubBinade:
1436
        newIntervalWidth = (ub - lb) / 2
1437
        if domainDirection < 0:
1438
            ub = lb + newIntervalWidth
1439
            ubBinade = slz_compute_binade(ub)
1440
        else:
1441
            lb = lb + newIntervalWidth
1442
            lbBinade = slz_compute_binade(lb) 
1443
    ## Image binade.
1444
    if lb == ub:
1445
        return (lb, ub)
1446
    lbImg = func(lb)
1447
    ubImg = func(ub)
1448
    funcIsInc = (ubImg >= lbImg)
1449
    lbImgBinade = slz_compute_binade(lbImg)
1450
    ubImgBinade = slz_compute_binade(ubImg)
1451
    while lbImgBinade != ubImgBinade:
1452
        newIntervalWidth = (ub - lb) / 2
1453
        if imageDirection < 0:
1454
            if funcIsInc:
1455
                ub = lb + newIntervalWidth
1456
                ubImgBinade = slz_compute_binade(func(ub))
1457
                #print ubImgBinade
1458
            else:
1459
                lb = lb + newIntervalWidth
1460
                lbImgBinade = slz_compute_binade(func(lb))
1461
                #print lbImgBinade
1462
        else:
1463
            if funcIsInc:
1464
                lb = lb + newIntervalWidth
1465
                lbImgBinade = slz_compute_binade(func(lb))
1466
                #print lbImgBinade
1467
            else:
1468
                ub = lb + newIntervalWidth
1469
                ubImgBinade = slz_compute_binade(func(ub))
1470
                #print ubImgBinade
1471
    # End while lbImgBinade != ubImgBinade:
1472
    return (lb, ub)
1473
# End slz_fix_bounds_for_binades.
1474

    
1475
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1476
    # Create a polynomial over the rationals.
1477
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1478
    return(ratPolynomialRing(polyOfFloat))
1479
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1480

    
1481
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1482
    """
1483
     Create a polynomial over the rationals where all denominators are
1484
     powers of two.
1485
    """
1486
    polyVariable = polyOfFloat.variables()[0] 
1487
    RPR = QQ[str(polyVariable)]
1488
    polyCoeffs      = polyOfFloat.coefficients()
1489
    #print polyCoeffs
1490
    polyExponents   = polyOfFloat.exponents()
1491
    #print polyExponents
1492
    polyDenomPtwoCoeffs = []
1493
    for coeff in polyCoeffs:
1494
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1495
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1496
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1497
    ratPoly = RPR(0)
1498
    #print type(ratPoly)
1499
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1500
    #  The coefficient becomes plainly wrong when exponent == 0.
1501
    #  No clue as to why.
1502
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1503
        ratPoly += coeff * RPR(polyVariable^exponent)
1504
    return ratPoly
1505
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1506

    
1507
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1508
                                      lowerBoundSa, 
1509
                                      upperBoundSa, floatingPointPrecSa, 
1510
                                      internalSollyaPrecSa, approxPrecSa):
1511
    """
1512
    Under the assumption that:
1513
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1514
    - lowerBound and upperBound belong to the same binade.
1515
    from a:
1516
    - function;
1517
    - a degree
1518
    - a pair of bounds;
1519
    - the floating-point precision we work on;
1520
    - the internal Sollya precision;
1521
    - the requested approximation error
1522
    The initial interval is, possibly, splitted into smaller intervals.
1523
    It return a list of tuples, each made of:
1524
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1525
    - a second polynomial (with a changed variable f(x) = q(x))
1526
    - the approximation interval;
1527
    - the center, x0, of the interval;
1528
    - the corresponding approximation error.
1529
    TODO: fix endless looping for some parameters sets.
1530
    """
1531
    resultArray = []
1532
    # Set Sollya to the necessary internal precision.
1533
    sollyaPrecChangedSa = False
1534
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1535
    if internalSollyaPrecSa > currentSollyaPrecSa:
1536
        if internalSollyaPrecSa <= 2:
1537
            print inspect.stack()[0][3], ": precision change <=2 requested."
1538
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1539
        sollyaPrecChangedSa = True
1540
    #
1541
    x = functionSa.variables()[0] # Actual variable name can be anything.
1542
    # Scaled function: [1=,2] -> [1,2].
1543
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1544
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1545
                slz_compute_scaled_function(functionSa,                       \
1546
                                            lowerBoundSa,                     \
1547
                                            upperBoundSa,                     \
1548
                                            floatingPointPrecSa)
1549
    # In case bounds were in the negative real one may need to
1550
    # switch scaled bounds.
1551
    if scaledLowerBoundSa > scaledUpperBoundSa:
1552
        scaledLowerBoundSa, scaledUpperBoundSa = \
1553
            scaledUpperBoundSa, scaledLowerBoundSa
1554
        #print "Switching!"
1555
    print "Approximation accuracy: ", RR(approxAccurSa)
1556
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1557
    functionSo = \
1558
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1559
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1560
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1561
                                                  scaledUpperBoundSa)
1562

    
1563
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1564
                                              upperBoundSa.parent().precision()))
1565
    currentScaledLowerBoundSa = scaledLowerBoundSa
1566
    currentScaledUpperBoundSa = scaledUpperBoundSa
1567
    while True:
1568
        ## Compute the first Taylor expansion.
1569
        print "Computing a Taylor expansion..."
1570
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1571
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1572
                                        currentScaledLowerBoundSa, 
1573
                                        currentScaledUpperBoundSa,
1574
                                        approxAccurSa, internalSollyaPrecSa)
1575
        print "...done."
1576
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1577
        #  This value goes to the first variable: polySo. Other variables are
1578
        #  not assigned and should not be tested.
1579
        if polySo is None:
1580
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1581
            if precChangedSa:
1582
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1583
            sollya_lib_clear_obj(initialSollyaPrecSo)
1584
            sollya_lib_clear_obj(functionSo)
1585
            sollya_lib_clear_obj(degreeSo)
1586
            sollya_lib_clear_obj(scaledBoundsSo)
1587
            return None
1588
        ## Add to the result array.
1589
        ### Change variable stuff in Sollya x -> x0-x.
1590
        changeVarExpressionSo = \
1591
            sollya_lib_build_function_sub( \
1592
                              sollya_lib_build_function_free_variable(), 
1593
                              sollya_lib_copy_obj(intervalCenterSo))
1594
        polyVarChangedSo = \
1595
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1596
        #### Get rid of the variable change Sollya stuff. 
1597
        sollya_lib_clear_obj(changeVarExpressionSo)
1598
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1599
                            intervalCenterSo, maxErrorSo))
1600
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1601
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1602
        print "Computed approximation error:", errorSa.n(digits=10)
1603
        # If the error and interval are OK a the first try, just return.
1604
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1605
            (errorSa <= approxAccurSa):
1606
            if precChangedSa:
1607
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1608
            sollya_lib_clear_obj(initialSollyaPrecSo)
1609
            sollya_lib_clear_obj(functionSo)
1610
            sollya_lib_clear_obj(degreeSo)
1611
            sollya_lib_clear_obj(scaledBoundsSo)
1612
            #print "Approximation error:", errorSa
1613
            return resultArray
1614
        ## The returned interval upper bound does not reach the requested upper
1615
        #  upper bound: compute the next upper bound.
1616
        ## The following ratio is always >= 1. If errorSa, we may want to
1617
        #  enlarge the interval
1618
        currentErrorRatio = approxAccurSa / errorSa
1619
        ## --|--------------------------------------------------------------|--
1620
        #  --|--------------------|--------------------------------------------
1621
        #  --|----------------------------|------------------------------------
1622
        ## Starting point for the next upper bound.
1623
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1624
        # Compute the increment. 
1625
        newBoundsWidthSa = \
1626
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1627
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1628
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1629
        # Take into account the original interval upper bound.                     
1630
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1631
            currentScaledUpperBoundSa = scaledUpperBoundSa
1632
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1633
            print "Can't shrink the interval anymore!"
1634
            print "You should consider increasing the Sollya internal precision"
1635
            print "or the polynomial degree."
1636
            print "Giving up!"
1637
            if precChangedSa:
1638
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1639
            sollya_lib_clear_obj(initialSollyaPrecSo)
1640
            sollya_lib_clear_obj(functionSo)
1641
            sollya_lib_clear_obj(degreeSo)
1642
            sollya_lib_clear_obj(scaledBoundsSo)
1643
            return None
1644
        # Compute the other expansions.
1645
        # Test for insufficient precision.
1646
# End slz_get_intervals_and_polynomials
1647

    
1648
def slz_interval_scaling_expression(boundsInterval, expVar):
1649
    """
1650
    Compute the scaling expression to map an interval that spans at most
1651
    a single binade into [1, 2) and the inverse expression as well.
1652
    If the interval spans more than one binade, result is wrong since
1653
    scaling is only based on the lower bound.
1654
    Not very sure that the transformation makes sense for negative numbers.
1655
    """
1656
    # The "one of the bounds is 0" special case. Here we consider
1657
    # the interval as the subnormals binade.
1658
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1659
        # The upper bound is (or should be) positive.
1660
        if boundsInterval.endpoints()[0] == 0:
1661
            if boundsInterval.endpoints()[1] == 0:
1662
                return None
1663
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1664
            l2     = boundsInterval.endpoints()[1].abs().log2()
1665
            # The upper bound is a power of two
1666
            if binade == l2:
1667
                scalingCoeff    = 2^(-binade)
1668
                invScalingCoeff = 2^(binade)
1669
                scalingOffset   = 1
1670
                return \
1671
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1672
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1673
            else:
1674
                scalingCoeff    = 2^(-binade-1)
1675
                invScalingCoeff = 2^(binade+1)
1676
                scalingOffset   = 1
1677
                return((scalingCoeff * expVar + scalingOffset),\
1678
                    ((expVar - scalingOffset) * invScalingCoeff))
1679
        # The lower bound is (or should be) negative.
1680
        if boundsInterval.endpoints()[1] == 0:
1681
            if boundsInterval.endpoints()[0] == 0:
1682
                return None
1683
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1684
            l2     = boundsInterval.endpoints()[0].abs().log2()
1685
            # The upper bound is a power of two
1686
            if binade == l2:
1687
                scalingCoeff    = -2^(-binade)
1688
                invScalingCoeff = -2^(binade)
1689
                scalingOffset   = 1
1690
                return((scalingCoeff * expVar + scalingOffset),\
1691
                    ((expVar - scalingOffset) * invScalingCoeff))
1692
            else:
1693
                scalingCoeff    = -2^(-binade-1)
1694
                invScalingCoeff = -2^(binade+1)
1695
                scalingOffset   = 1
1696
                return((scalingCoeff * expVar + scalingOffset),\
1697
                   ((expVar - scalingOffset) * invScalingCoeff))
1698
    # General case
1699
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1700
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1701
    # We allow for a single exception in binade spanning is when the
1702
    # "outward bound" is a power of 2 and its binade is that of the
1703
    # "inner bound" + 1.
1704
    if boundsInterval.endpoints()[0] > 0:
1705
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1706
        if lbBinade != ubBinade:
1707
            print "Different binades."
1708
            if ubL2 != ubBinade:
1709
                print "Not a power of 2."
1710
                return None
1711
            elif abs(ubBinade - lbBinade) > 1:
1712
                print "Too large span:", abs(ubBinade - lbBinade)
1713
                return None
1714
    else:
1715
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1716
        if lbBinade != ubBinade:
1717
            print "Different binades."
1718
            if lbL2 != lbBinade:
1719
                print "Not a power of 2."
1720
                return None
1721
            elif abs(ubBinade - lbBinade) > 1:
1722
                print "Too large span:", abs(ubBinade - lbBinade)
1723
                return None
1724
    #print "Lower bound binade:", binade
1725
    if boundsInterval.endpoints()[0] > 0:
1726
        return \
1727
            ((2^(-lbBinade) * expVar).function(expVar),
1728
             (2^(lbBinade) * expVar).function(expVar))
1729
    else:
1730
        return \
1731
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1732
             (-(2^(ubBinade)) * expVar).function(expVar))
1733
"""
1734
    # Code sent to attic. Should be the base for a 
1735
    # "slz_interval_translate_expression" rather than scale.
1736
    # Extra control and special cases code  added in  
1737
    # slz_interval_scaling_expression could (should ?) be added to
1738
    # this new function.
1739
    # The scaling offset is only used for negative numbers.
1740
    # When the absolute value of the lower bound is < 0.
1741
    if abs(boundsInterval.endpoints()[0]) < 1:
1742
        if boundsInterval.endpoints()[0] >= 0:
1743
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1744
            invScalingCoeff = 1/scalingCoeff
1745
            return((scalingCoeff * expVar, 
1746
                    invScalingCoeff * expVar))
1747
        else:
1748
            scalingCoeff = \
1749
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1750
            scalingOffset = -3 * scalingCoeff
1751
            return((scalingCoeff * expVar + scalingOffset,
1752
                    1/scalingCoeff * expVar + 3))
1753
    else: 
1754
        if boundsInterval.endpoints()[0] >= 0:
1755
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1756
            scalingOffset = 0
1757
            return((scalingCoeff * expVar, 
1758
                    1/scalingCoeff * expVar))
1759
        else:
1760
            scalingCoeff = \
1761
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1762
            scalingOffset =  -3 * scalingCoeff
1763
            #scalingOffset = 0
1764
            return((scalingCoeff * expVar + scalingOffset,
1765
                    1/scalingCoeff * expVar + 3))
1766
"""
1767
# End slz_interval_scaling_expression
1768
   
1769
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1770
    """
1771
    Compute the Sage version of the Taylor polynomial and it's
1772
    companion data (interval, center...)
1773
    The input parameter is a five elements tuple:
1774
    - [0]: the polyomial (without variable change), as polynomial over a
1775
           real ring;
1776
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1777
           over a real ring;
1778
    - [2]: the interval (as Sollya range);
1779
    - [3]: the interval center;
1780
    - [4]: the approximation error. 
1781
    
1782
    The function returns a 5 elements tuple: formed with all the 
1783
    input elements converted into their Sollya counterpart.
1784
    """
1785
    #print "Sollya polynomial to convert:", 
1786
    #pobyso_autoprint(polyRangeCenterErrorSo)
1787
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
1788
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
1789
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
1790
    intervalSa = \
1791
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1792
    centerSa = \
1793
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1794
    errorSa = \
1795
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1796
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1797
    # End slz_interval_and_polynomial_to_sage
1798

    
1799
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1800
            targetPlusOnePrecRF = None, quasiExactRF = None):
1801
    """
1802
      Check if an element (argument) of the domain of function (function)
1803
      yields a HTRN case (rounding to next) for the target precision 
1804
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1805
      
1806
      The strategy is: 
1807
      - compute the image at high (quasi-exact) precision;
1808
      - round it to nearest to precision;
1809
      - round it to exactly to (precision+1), the computed number has two
1810
        midpoint neighbors;
1811
      - check the distance between these neighbors and the quasi-exact value;
1812
        - if none of them is closer than the targetAccuracy, return False,
1813
        - else return true.
1814
      - Powers of two are a special case when comparing the midpoint in
1815
        the 0 direction..
1816
    """
1817
    ## Arguments filtering. 
1818
    ## TODO: filter the first argument for consistence.
1819
    if targetRF is None:
1820
        targetRF = argument.parent()
1821
    ## Ditto for the real field holding the midpoints.
1822
    if targetPlusOnePrecRF is None:
1823
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1824
    ## If no quasiExactField is provided, create a high accuracy RealField.
1825
    if quasiExactRF is None:
1826
        quasiExactRF = RealField(1536)
1827
    function                      = function.function(function.variables()[0])
1828
    exactArgument                 = quasiExactRF(argument)
1829
    quasiExactValue               = function(exactArgument)
1830
    roundedValue                  = targetRF(quasiExactValue)
1831
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1832
    # Upper midpoint.
1833
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1834
    # Lower midpoint.
1835
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1836
    binade                        = slz_compute_binade(roundedValue)
1837
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1838
    #print "Argument:", argument
1839
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1840
    #print "Binade:", binade
1841
    #print "binadeCorrectedTargetAccuracy:", \
1842
    #binadeCorrectedTargetAccuracy.n(prec=100)
1843
    #print "binadeCorrectedTargetAccuracy:", \
1844
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1845
    #print "Upper midpoint:", \
1846
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1847
    #print "Rounded value :", \
1848
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1849
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1850
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1851
    #print "Lower midpoint:", \
1852
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1853
    ## Make quasiExactValue = 0 a special case to move it out of the way.
1854
    if quasiExactValue == 0:
1855
        return False
1856
    ## Begining of the general case : check with the midpoint of 
1857
    #  greatest absolute value.
1858
    if quasiExactValue > 0:
1859
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1860
           binadeCorrectedTargetAccuracy:
1861
            #print "Too close to the upper midpoint: ", \
1862
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1863
            #print argument.n().str(base=16, \
1864
            #  no_sci=False)
1865
            #print "Lower midpoint:", \
1866
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1867
            #print "Value         :", \
1868
            # quasiExactValue.n(prec=200).str(base=2)
1869
            #print "Upper midpoint:", \
1870
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1871
            return True
1872
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
1873
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1874
           binadeCorrectedTargetAccuracy:
1875
            #print "Too close to the upper midpoint: ", \
1876
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1877
            #print argument.n().str(base=16, \
1878
            #  no_sci=False)
1879
            #print "Lower midpoint:", \
1880
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1881
            #print "Value         :", \
1882
            #  quasiExactValue.n(prec=200).str(base=2)
1883
            #print "Upper midpoint:", \
1884
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1885
            #print
1886
            return True
1887
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1888
    ## For the midpoint of smaller absolute value, 
1889
    #  split cases with the powers of 2.
1890
    if sno_abs_is_power_of_two(roundedValue):
1891
        if quasiExactValue > 0:        
1892
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1893
               binadeCorrectedTargetAccuracy / 2:
1894
                #print "Lower midpoint:", \
1895
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1896
                #print "Value         :", \
1897
                #  quasiExactValue.n(prec=200).str(base=2)
1898
                #print "Upper midpoint:", \
1899
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1900
                #print
1901
                return True
1902
        else:
1903
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1904
              binadeCorrectedTargetAccuracy / 2:
1905
                #print "Lower midpoint:", \
1906
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1907
                #print "Value         :", 
1908
                #  quasiExactValue.n(prec=200).str(base=2)
1909
                #print "Upper midpoint:", 
1910
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1911
                #print
1912
                return True
1913
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1914
    else: ## Not a power of 2, regular comparison with the midpoint of 
1915
          #  smaller absolute value.
1916
        if quasiExactValue > 0:        
1917
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1918
              binadeCorrectedTargetAccuracy:
1919
                #print "Lower midpoint:", \
1920
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1921
                #print "Value         :", \
1922
                #  quasiExactValue.n(prec=200).str(base=2)
1923
                #print "Upper midpoint:", \
1924
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1925
                #print
1926
                return True
1927
        else: # quasiExactValue <= 0
1928
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1929
              binadeCorrectedTargetAccuracy:
1930
                #print "Lower midpoint:", \
1931
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1932
                #print "Value         :", \
1933
                #  quasiExactValue.n(prec=200).str(base=2)
1934
                #print "Upper midpoint:", \
1935
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1936
                #print
1937
                return True
1938
    #print "distance to the upper midpoint:", \
1939
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1940
    #print "distance to the lower midpoint:", \
1941
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1942
    return False
1943
# End slz_is_htrn
1944

    
1945
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1946
                                                precision,
1947
                                                targetHardnessToRound,
1948
                                                variable1,
1949
                                                variable2):
1950
    """
1951
    Creates a new multivariate polynomial with integer coefficients for use
1952
     with the Coppersmith method.
1953
    A the same time it computes :
1954
    - 2^K (N);
1955
    - 2^k (bound on the second variable)
1956
    - lcm
1957

    
1958
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1959
                         variables.
1960
    :param precision: the precision of the floating-point coefficients.
1961
    :param targetHardnessToRound: the hardness to round we want to check.
1962
    :param variable1: the first variable of the polynomial (an expression).
1963
    :param variable2: the second variable of the polynomial (an expression).
1964
    
1965
    :returns: a 4 elements tuple:
1966
                - the polynomial;
1967
                - the modulus (N);
1968
                - the t bound;
1969
                - the lcm used to compute the integral coefficients and the 
1970
                  module.
1971
    """
1972
    # Create a new integer polynomial ring.
1973
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1974
    # Coefficients are issued in the increasing power order.
1975
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1976
    # Print the reversed list for debugging.
1977
    #print
1978
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1979
    # Build the list of number we compute the lcm of.
1980
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1981
    #print "Coefficient denominators:", coefficientDenominators
1982
    coefficientDenominators.append(2^precision)
1983
    coefficientDenominators.append(2^(targetHardnessToRound))
1984
    leastCommonMultiple = lcm(coefficientDenominators)
1985
    # Compute the expression corresponding to the new polynomial
1986
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1987
    #print coefficientNumerators
1988
    polynomialExpression = 0
1989
    power = 0
1990
    # Iterate over two lists at the same time, stop when the shorter
1991
    # (is this case coefficientsNumerators) is 
1992
    # exhausted. Both lists are ordered in the order of increasing powers
1993
    # of variable1.
1994
    for numerator, denominator in \
1995
                        zip(coefficientNumerators, coefficientDenominators):
1996
        multiplicator = leastCommonMultiple / denominator 
1997
        newCoefficient = numerator * multiplicator
1998
        polynomialExpression += newCoefficient * variable1^power
1999
        power +=1
2000
    polynomialExpression += - variable2
2001
    return (IP(polynomialExpression),
2002
            leastCommonMultiple / 2^precision, # 2^K aka N.
2003
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
2004
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
2005
            leastCommonMultiple) # If we want to make test computations.
2006
        
2007
# End slz_rat_poly_of_int_to_poly_for_coppersmith
2008

    
2009
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
2010
                                          precision):
2011
    """
2012
    Makes a variable substitution into the input polynomial so that the output
2013
    polynomial can take integer arguments.
2014
    All variables of the input polynomial "have precision p". That is to say
2015
    that they are rationals with denominator == 2^(precision - 1): 
2016
    x = y/2^(precision - 1).
2017
    We "incorporate" these denominators into the coefficients with, 
2018
    respectively, the "right" power.
2019
    """
2020
    polynomialField = ratPolyOfRat.parent()
2021
    polynomialVariable = ratPolyOfRat.variables()[0]
2022
    #print "The polynomial field is:", polynomialField
2023
    return \
2024
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
2025
                                   polynomialVariable/2^(precision-1)}))
2026
    
2027
# End slz_rat_poly_of_rat_to_rat_poly_of_int
2028

    
2029
def slz_reduce_and_test_base(matrixToReduce,
2030
                             nAtAlpha,
2031
                             monomialsCountSqrt):
2032
    """
2033
    Reduces the basis, tests the Coppersmith condition and returns
2034
    a list of rows that comply with the condition.
2035
    """
2036
    ccComplientRowsList = []
2037
    reducedMatrix = matrixToReduce.LLL(None)
2038
    if not reducedMatrix.is_LLL_reduced():
2039
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
2040
    else:
2041
        print "reducedMatrix is actually reduced."
2042
    print "N^alpha:", nAtAlpha.n()
2043
    rowIndex = 0
2044
    for row in reducedMatrix.rows():
2045
        l2Norm = row.norm(2)
2046
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
2047
                monomialsCountSqrt.n(), ". Is vector OK?", 
2048
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
2049
            ccComplientRowsList.append(row)
2050
            print "True"
2051
        else:
2052
            print "False"
2053
    # End for
2054
    return ccComplientRowsList
2055
# End slz_reduce_and_test_base
2056

    
2057
def slz_resultant(poly1, poly2, elimVar, debug = False):
2058
    """
2059
    Compute the resultant for two polynomials for a given variable
2060
    and return the (poly1, poly2, resultant) if the resultant
2061
    is not 0.
2062
    Return () otherwise.
2063
    """
2064
    polynomialRing0    = poly1.parent()
2065
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2066
    if resultantInElimVar is None:
2067
        if debug:
2068
            print poly1
2069
            print poly2
2070
            print "have GCD = ", poly1.gcd(poly2)
2071
        return None
2072
    if resultantInElimVar.is_zero():
2073
        if debug:
2074
            print poly1
2075
            print poly2
2076
            print "have GCD = ", poly1.gcd(poly2)
2077
        return None
2078
    else:
2079
        if debug:
2080
            print poly1
2081
            print poly2
2082
            print "have resultant in t = ", resultantInElimVar
2083
        return resultantInElimVar
2084
# End slz_resultant.
2085
#
2086
def slz_resultant_tuple(poly1, poly2, elimVar):
2087
    """
2088
    Compute the resultant for two polynomials for a given variable
2089
    and return the (poly1, poly2, resultant) if the resultant
2090
    is not 0.
2091
    Return () otherwise.
2092
    """
2093
    polynomialRing0    = poly1.parent()
2094
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2095
    if resultantInElimVar.is_zero():
2096
        return ()
2097
    else:
2098
        return (poly1, poly2, resultantInElimVar)
2099
# End slz_resultant_tuple.
2100
#
2101
sys.stderr.write("\t...sageSLZ loaded\n")
2102