Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (112,79 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_proj(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
    INPUT:
385
    
386
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
387
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
388
    - "N" -- the modulus;
389
    - "iBound" -- the bound on the first variable;
390
    - "tBound" -- the bound on the second variable.
391
    
392
    OUTPUT:
393
    
394
    A list of bivariate integer polynomial obtained using the Coppersmith
395
    algorithm. The polynomials correspond to the rows of the LLL-reduce
396
    reduced base that comply with the Coppersmith condition.
397
    """
398
    #@par Changes from runSLZ-113.sage
399
    # LLL reduction is not performed on the matrix itself but rather on the
400
    # product of the matrix with a uniform random matrix.
401
    # The reduced matrix obtained is discarded but the transformation matrix 
402
    # obtained is used to multiply the original matrix in order to reduced it.
403
    # If a sufficient level of reduction is obtained, we stop here. If not
404
    # the product matrix obtained above is LLL reduced. But as it has been
405
    # pre-reduced at the above step, reduction is supposed to be much faster.
406
    #
407
    # Arguments check.
408
    if iBound == 0 or tBound == 0:
409
        return None
410
    # End arguments check.
411
    nAtAlpha = N^alpha
412
    ## Building polynomials for matrix.
413
    polyRing   = inputPolynomial.parent()
414
    # Whatever the 2 variables are actually called, we call them
415
    # 'i' and 't' in all the variable names.
416
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
417
    #print polyVars[0], type(polyVars[0])
418
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
419
                                              tVariable:tVariable * tBound})
420
    if debug:
421
        polynomialsList = \
422
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
423
                                                 alpha,
424
                                                 N,
425
                                                 iBound,
426
                                                 tBound,
427
                                                 20)
428
    else:
429
        polynomialsList = \
430
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
431
                                                 alpha,
432
                                                 N,
433
                                                 iBound,
434
                                                 tBound,
435
                                                 0)
436
    #print "Polynomials list:", polynomialsList
437
    ## Building the proto matrix.
438
    knownMonomials = []
439
    protoMatrix    = []
440
    if debug:
441
        for poly in polynomialsList:
442
            spo_add_polynomial_coeffs_to_matrix_row(poly,
443
                                                    knownMonomials,
444
                                                    protoMatrix,
445
                                                    20)
446
    else:
447
        for poly in polynomialsList:
448
            spo_add_polynomial_coeffs_to_matrix_row(poly,
449
                                                    knownMonomials,
450
                                                    protoMatrix,
451
                                                    0)
452
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
453
    #print matrixToReduce
454
    ## Reduction and checking.
455
    ### Reduction with projection
456
    (reducedMatrixStep1, reductionMatrixStep1) = \
457
         slz_reduce_lll_proj(matrixToReduce,16)
458
    #print "Reduced matrix:"
459
    #print reducedMatrixStep1
460
    #for row in reducedMatrix.rows():
461
    #    print row
462
    monomialsCount     = len(knownMonomials)
463
    monomialsCountSqrt = sqrt(monomialsCount)
464
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
465
    #print reducedMatrix
466
    ## Check the Coppersmith condition for each row and build the reduced 
467
    #  polynomials.
468
    ccReducedPolynomialsList = []
469
    for row in reducedMatrixStep1.rows():
470
        l2Norm = row.norm(2)
471
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
472
            #print (l2Norm * monomialsCountSqrt).n()
473
            #print l2Norm.n()
474
            ccReducedPolynomial = \
475
                slz_compute_reduced_polynomial(row,
476
                                               knownMonomials,
477
                                               iVariable,
478
                                               iBound,
479
                                               tVariable,
480
                                               tBound)
481
            if not ccReducedPolynomial is None:
482
                ccReducedPolynomialsList.append(ccReducedPolynomial)
483
        else:
484
            #print l2Norm.n() , ">", nAtAlpha
485
            pass
486
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
487
        print "Less than 2 Coppersmith condition compliant vectors."
488
        print "Extra reduction starting..."
489
        reducedMatrix = reducedMatrixStep1.LLL(algorithm='fpLLL:wrapper')
490
        ### If uncommented, the following statement avoids performing
491
        #   an actual LLL reduction. This allows for demonstrating
492
        #   the behavior of our pseudo-reduction alone.
493
        #return ()
494
    else:
495
        print "First step of reduction afforded enough vectors"
496
        return ccReducedPolynomialsList
497
    #print ccReducedPolynomialsList
498
    ## Check again the Coppersmith condition for each row and build the reduced 
499
    #  polynomials.
500
    ccReducedPolynomialsList = []
501
    for row in reducedMatrix.rows():
502
        l2Norm = row.norm(2)
503
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
504
            #print (l2Norm * monomialsCountSqrt).n()
505
            #print l2Norm.n()
506
            ccReducedPolynomial = \
507
                slz_compute_reduced_polynomial(row,
508
                                               knownMonomials,
509
                                               iVariable,
510
                                               iBound,
511
                                               tVariable,
512
                                               tBound)
513
            if not ccReducedPolynomial is None:
514
                ccReducedPolynomialsList.append(ccReducedPolynomial)
515
        else:
516
            #print l2Norm.n() , ">", nAtAlpha
517
            pass
518
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
519
        print "Less than 2 Coppersmith condition compliant vectors after extra reduction."
520
        return ()
521
    else:
522
        return ccReducedPolynomialsList
523
# End slz_compute_coppersmith_reduced_polynomials_proj
524
def slz_compute_weak_coppersmith_reduced_polynomials_proj(inputPolynomial,
525
                                                          alpha,
526
                                                          N,
527
                                                          iBound,
528
                                                          tBound,
529
                                                          debug = False):
530
    """
531
    For a given set of arguments (see below), compute a list
532
    of "reduced polynomials" that could be used to compute roots
533
    of the inputPolynomial.
534
    INPUT:
535
    
536
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
537
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
538
    - "N" -- the modulus;
539
    - "iBound" -- the bound on the first variable;
540
    - "tBound" -- the bound on the second variable.
541
    
542
    OUTPUT:
543
    
544
    A list of bivariate integer polynomial obtained using the Coppersmith
545
    algorithm. The polynomials correspond to the rows of the LLL-reduce
546
    reduced base that comply with the weak version of Coppersmith condition.
547
    """
548
    #@par Changes from runSLZ-113.sage
549
    # LLL reduction is not performed on the matrix itself but rather on the
550
    # product of the matrix with a uniform random matrix.
551
    # The reduced matrix obtained is discarded but the transformation matrix 
552
    # obtained is used to multiply the original matrix in order to reduced it.
553
    # If a sufficient level of reduction is obtained, we stop here. If not
554
    # the product matrix obtained above is LLL reduced. But as it has been
555
    # pre-reduced at the above step, reduction is supposed to be much faster.
556
    #
557
    # Arguments check.
558
    if iBound == 0 or tBound == 0:
559
        return None
560
    # End arguments check.
561
    nAtAlpha = N^alpha
562
    ## Building polynomials for matrix.
563
    polyRing   = inputPolynomial.parent()
564
    # Whatever the 2 variables are actually called, we call them
565
    # 'i' and 't' in all the variable names.
566
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
567
    #print polyVars[0], type(polyVars[0])
568
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
569
                                              tVariable:tVariable * tBound})
570
    if debug:
571
        polynomialsList = \
572
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
573
                                                 alpha,
574
                                                 N,
575
                                                 iBound,
576
                                                 tBound,
577
                                                 20)
578
    else:
579
        polynomialsList = \
580
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
581
                                                 alpha,
582
                                                 N,
583
                                                 iBound,
584
                                                 tBound,
585
                                                 0)
586
    #print "Polynomials list:", polynomialsList
587
    ## Building the proto matrix.
588
    knownMonomials = []
589
    protoMatrix    = []
590
    if debug:
591
        for poly in polynomialsList:
592
            spo_add_polynomial_coeffs_to_matrix_row(poly,
593
                                                    knownMonomials,
594
                                                    protoMatrix,
595
                                                    20)
596
    else:
597
        for poly in polynomialsList:
598
            spo_add_polynomial_coeffs_to_matrix_row(poly,
599
                                                    knownMonomials,
600
                                                    protoMatrix,
601
                                                    0)
602
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
603
    #print matrixToReduce
604
    ## Reduction and checking.
605
    ### Reduction with projection
606
    (reducedMatrixStep1, reductionMatrixStep1) = \
607
         slz_reduce_lll_proj(matrixToReduce,16)
608
    #print "Reduced matrix:"
609
    #print reducedMatrixStep1
610
    #for row in reducedMatrix.rows():
611
    #    print row
612
    monomialsCount     = len(knownMonomials)
613
    monomialsCountSqrt = sqrt(monomialsCount)
614
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
615
    #print reducedMatrix
616
    ## Check the Coppersmith condition for each row and build the reduced 
617
    #  polynomials.
618
    ccReducedPolynomialsList = []
619
    for row in reducedMatrixStep1.rows():
620
        l1Norm = row.norm(1)
621
        l2Norm = row.norm(2)
622
        if l2Norm * monomialsCountSqrt < l1Norm:
623
            print "l1norm is smaller than l2norm*sqrt(w)."
624
        else:
625
            print "l1norm is NOT smaller than l2norm*sqrt(w)."
626
            print (l2Norm * monomialsCountSqrt).n(), 'vs ', l1Norm.n()
627
        if l1Norm < nAtAlpha:
628
            #print l2Norm.n()
629
            ccReducedPolynomial = \
630
                slz_compute_reduced_polynomial(row,
631
                                               knownMonomials,
632
                                               iVariable,
633
                                               iBound,
634
                                               tVariable,
635
                                               tBound)
636
            if not ccReducedPolynomial is None:
637
                ccReducedPolynomialsList.append(ccReducedPolynomial)
638
        else:
639
            #print l2Norm.n() , ">", nAtAlpha
640
            pass
641
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
642
        print "Less than 2 Coppersmith condition compliant vectors."
643
        print "Extra reduction starting..."
644
        reducedMatrix = reducedMatrixStep1.LLL(algorithm='fpLLL:wrapper')
645
        ### If uncommented, the following statement avoids performing
646
        #   an actual LLL reduction. This allows for demonstrating
647
        #   the behavior of our pseudo-reduction alone.
648
        #return ()
649
    else:
650
        print "First step of reduction afforded enough vectors"
651
        return ccReducedPolynomialsList
652
    #print ccReducedPolynomialsList
653
    ## Check again the Coppersmith condition for each row and build the reduced 
654
    #  polynomials.
655
    ccReducedPolynomialsList = []
656
    for row in reducedMatrix.rows():
657
        l2Norm = row.norm(2)
658
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
659
            #print (l2Norm * monomialsCountSqrt).n()
660
            #print l2Norm.n()
661
            ccReducedPolynomial = \
662
                slz_compute_reduced_polynomial(row,
663
                                               knownMonomials,
664
                                               iVariable,
665
                                               iBound,
666
                                               tVariable,
667
                                               tBound)
668
            if not ccReducedPolynomial is None:
669
                ccReducedPolynomialsList.append(ccReducedPolynomial)
670
        else:
671
            #print l2Norm.n() , ">", nAtAlpha
672
            pass
673
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
674
        print "Less than 2 Coppersmith condition compliant vectors after extra reduction."
675
        return ()
676
    else:
677
        return ccReducedPolynomialsList
678
# End slz_compute_coppersmith_reduced_polynomials_proj
679
#
680
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
681
                                                                    alpha,
682
                                                                    N,
683
                                                                    iBound,
684
                                                                    tBound,
685
                                                                    debug = False):
686
    """
687
    For a given set of arguments (see below), compute a list
688
    of "reduced polynomials" that could be used to compute roots
689
    of the inputPolynomial.
690
    Print the volume of the initial basis as well.
691
    INPUT:
692
    
693
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
694
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
695
    - "N" -- the modulus;
696
    - "iBound" -- the bound on the first variable;
697
    - "tBound" -- the bound on the second variable.
698
    
699
    OUTPUT:
700
    
701
    A list of bivariate integer polynomial obtained using the Coppersmith
702
    algorithm. The polynomials correspond to the rows of the LLL-reduce
703
    reduced base that comply with the Coppersmith condition.
704
    """
705
    # Arguments check.
706
    if iBound == 0 or tBound == 0:
707
        return None
708
    # End arguments check.
709
    nAtAlpha = N^alpha
710
    if debug:
711
        print "N at alpha:", nAtAlpha
712
    ## Building polynomials for matrix.
713
    polyRing   = inputPolynomial.parent()
714
    # Whatever the 2 variables are actually called, we call them
715
    # 'i' and 't' in all the variable names.
716
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
717
    #print polyVars[0], type(polyVars[0])
718
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
719
                                              tVariable:tVariable * tBound})
720
##    polynomialsList = \
721
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
722
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
723
    polynomialsList = \
724
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
725
                                             alpha,
726
                                             N,
727
                                             iBound,
728
                                             tBound,
729
                                             0)
730
    #print "Polynomials list:", polynomialsList
731
    ## Building the proto matrix.
732
    knownMonomials = []
733
    protoMatrix    = []
734
    for poly in polynomialsList:
735
        spo_add_polynomial_coeffs_to_matrix_row(poly,
736
                                                knownMonomials,
737
                                                protoMatrix,
738
                                                0)
739
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
740
    matrixToReduceTranspose = matrixToReduce.transpose()
741
    squareMatrix = matrixToReduce * matrixToReduceTranspose
742
    squareMatDet = det(squareMatrix)
743
    latticeVolume = sqrt(squareMatDet)
744
    print "Lattice volume:", latticeVolume.n()
745
    print "Lattice volume / N:", (latticeVolume/N).n()
746
    #print matrixToReduce
747
    ## Reduction and checking.
748
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
749
    #  error message issued when previous code was used.
750
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
751
    reductionTimeStart = cputime() 
752
    reducedMatrix = matrixToReduce.LLL(fp=None)
753
    reductionTime = cputime(reductionTimeStart)
754
    print "Reduction time:", reductionTime
755
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
756
    if not isLLLReduced:
757
       return None
758
    #
759
    if debug:
760
        matrixFile = file('/tmp/reducedMatrix.txt', 'w')
761
        for row in reducedMatrix.rows():
762
            matrixFile.write(str(row) + "\n")
763
        matrixFile.close()
764
    #
765
    monomialsCount     = len(knownMonomials)
766
    monomialsCountSqrt = sqrt(monomialsCount)
767
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
768
    #print reducedMatrix
769
    ## Check the Coppersmith condition for each row and build the reduced 
770
    #  polynomials.
771
    ccVectorsCount = 0
772
    ccReducedPolynomialsList = []
773
    for row in reducedMatrix.rows():
774
        l2Norm = row.norm(2)
775
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
776
            #print (l2Norm * monomialsCountSqrt).n()
777
            #print l2Norm.n()
778
            ccVectorsCount +=1            
779
            ccReducedPolynomial = \
780
                slz_compute_reduced_polynomial(row,
781
                                               knownMonomials,
782
                                               iVariable,
783
                                               iBound,
784
                                               tVariable,
785
                                               tBound)
786
            if not ccReducedPolynomial is None:
787
                ccReducedPolynomialsList.append(ccReducedPolynomial)
788
        else:
789
            #print l2Norm.n() , ">", nAtAlpha
790
            pass
791
    if debug:
792
        print ccVectorsCount, "out of ", len(ccReducedPolynomialsList), 
793
        print "took Coppersmith text."
794
    if len(ccReducedPolynomialsList) < 2:
795
        print "Less than 2 Coppersmith condition compliant vectors."
796
        return ()
797
    if debug:
798
        print "Reduced and Coppersmith compliant polynomials list", ccReducedPolynomialsList
799
    return ccReducedPolynomialsList
800
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
801

    
802
def slz_compute_initial_lattice_matrix(inputPolynomial,
803
                                       alpha,
804
                                       N,
805
                                       iBound,
806
                                       tBound,
807
                                       debug = False):
808
    """
809
    For a given set of arguments (see below), compute the initial lattice
810
    that could be reduced. 
811
    INPUT:
812
    
813
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
814
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
815
    - "N" -- the modulus;
816
    - "iBound" -- the bound on the first variable;
817
    - "tBound" -- the bound on the second variable.
818
    
819
    OUTPUT:
820
    
821
    A list of bivariate integer polynomial obtained using the Coppersmith
822
    algorithm. The polynomials correspond to the rows of the LLL-reduce
823
    reduced base that comply with the Coppersmith condition.
824
    """
825
    # Arguments check.
826
    if iBound == 0 or tBound == 0:
827
        return None
828
    # End arguments check.
829
    nAtAlpha = N^alpha
830
    ## Building polynomials for matrix.
831
    polyRing   = inputPolynomial.parent()
832
    # Whatever the 2 variables are actually called, we call them
833
    # 'i' and 't' in all the variable names.
834
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
835
    #print polyVars[0], type(polyVars[0])
836
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
837
                                              tVariable:tVariable * tBound})
838
    polynomialsList = \
839
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
840
                                             alpha,
841
                                             N,
842
                                             iBound,
843
                                             tBound,
844
                                             0)
845
    #print "Polynomials list:", polynomialsList
846
    ## Building the proto matrix.
847
    knownMonomials = []
848
    protoMatrix    = []
849
    for poly in polynomialsList:
850
        spo_add_polynomial_coeffs_to_matrix_row(poly,
851
                                                knownMonomials,
852
                                                protoMatrix,
853
                                                0)
854
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
855
    if debug:
856
        print "Initial basis polynomials"
857
        for poly in polynomialsList:
858
            print poly
859
    return matrixToReduce
860
# End slz_compute_initial_lattice_matrix.
861

    
862
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
863
                                                 alpha,
864
                                                 N,
865
                                                 iBound,
866
                                                 tBound):
867
    """
868
    For a given set of arguments (see below), compute the polynomial modular 
869
    roots, if any.
870
    
871
    """
872
    # Arguments check.
873
    if iBound == 0 or tBound == 0:
874
        return set()
875
    # End arguments check.
876
    nAtAlpha = N^alpha
877
    ## Building polynomials for matrix.
878
    polyRing   = inputPolynomial.parent()
879
    # Whatever the 2 variables are actually called, we call them
880
    # 'i' and 't' in all the variable names.
881
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
882
    ccReducedPolynomialsList = \
883
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
884
                                                     alpha,
885
                                                     N,
886
                                                     iBound,
887
                                                     tBound)
888
    if len(ccReducedPolynomialsList) == 0:
889
        return set()   
890
    ## Create the valid (poly1 and poly2 are algebraically independent) 
891
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
892
    # Try to mix and match all the polynomial pairs built from the 
893
    # ccReducedPolynomialsList to obtain non zero resultants.
894
    resultantsInITuplesList = []
895
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
896
        for polyInnerIndex in xrange(polyOuterIndex+1, 
897
                                     len(ccReducedPolynomialsList)):
898
            # Compute the resultant in resultants in the
899
            # first variable (is it the optimal choice?).
900
            resultantInI = \
901
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
902
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
903
            #print "Resultant", resultantInI
904
            # Test algebraic independence.
905
            if not resultantInI.is_zero():
906
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
907
                                                 ccReducedPolynomialsList[polyInnerIndex],
908
                                                 resultantInI))
909
    # If no non zero resultant was found: we can't get no algebraically 
910
    # independent polynomials pair. Give up!
911
    if len(resultantsInITuplesList) == 0:
912
        return set()
913
    #print resultantsInITuplesList
914
    # Compute the roots.
915
    Zi = ZZ[str(iVariable)]
916
    Zt = ZZ[str(tVariable)]
917
    polynomialRootsSet = set()
918
    # First, solve in the second variable since resultants are in the first
919
    # variable.
920
    for resultantInITuple in resultantsInITuplesList:
921
        tRootsList = Zt(resultantInITuple[2]).roots()
922
        # For each tRoot, compute the corresponding iRoots and check 
923
        # them in the input polynomial.
924
        for tRoot in tRootsList:
925
            #print "tRoot:", tRoot
926
            # Roots returned by root() are (value, multiplicity) tuples.
927
            iRootsList = \
928
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
929
            print iRootsList
930
            # The iRootsList can be empty, hence the test.
931
            if len(iRootsList) != 0:
932
                for iRoot in iRootsList:
933
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
934
                    # polyEvalModN must be an integer.
935
                    if polyEvalModN == int(polyEvalModN):
936
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
937
    return polynomialRootsSet
938
# End slz_compute_integer_polynomial_modular_roots.
939
#
940
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
941
                                                 alpha,
942
                                                 N,
943
                                                 iBound,
944
                                                 tBound):
945
    """
946
    For a given set of arguments (see below), compute the polynomial modular 
947
    roots, if any.
948
    This version differs in the way resultants are computed.   
949
    """
950
    # Arguments check.
951
    if iBound == 0 or tBound == 0:
952
        return set()
953
    # End arguments check.
954
    nAtAlpha = N^alpha
955
    ## Building polynomials for matrix.
956
    polyRing   = inputPolynomial.parent()
957
    # Whatever the 2 variables are actually called, we call them
958
    # 'i' and 't' in all the variable names.
959
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
960
    #print polyVars[0], type(polyVars[0])
961
    ccReducedPolynomialsList = \
962
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
963
                                                     alpha,
964
                                                     N,
965
                                                     iBound,
966
                                                     tBound)
967
    if len(ccReducedPolynomialsList) == 0:
968
        return set()   
969
    ## Create the valid (poly1 and poly2 are algebraically independent) 
970
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
971
    # Try to mix and match all the polynomial pairs built from the 
972
    # ccReducedPolynomialsList to obtain non zero resultants.
973
    resultantsInTTuplesList = []
974
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
975
        for polyInnerIndex in xrange(polyOuterIndex+1, 
976
                                     len(ccReducedPolynomialsList)):
977
            # Compute the resultant in resultants in the
978
            # first variable (is it the optimal choice?).
979
            resultantInT = \
980
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
981
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
982
            #print "Resultant", resultantInT
983
            # Test algebraic independence.
984
            if not resultantInT.is_zero():
985
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
986
                                                 ccReducedPolynomialsList[polyInnerIndex],
987
                                                 resultantInT))
988
    # If no non zero resultant was found: we can't get no algebraically 
989
    # independent polynomials pair. Give up!
990
    if len(resultantsInTTuplesList) == 0:
991
        return set()
992
    #print resultantsInITuplesList
993
    # Compute the roots.
994
    Zi = ZZ[str(iVariable)]
995
    Zt = ZZ[str(tVariable)]
996
    polynomialRootsSet = set()
997
    # First, solve in the second variable since resultants are in the first
998
    # variable.
999
    for resultantInTTuple in resultantsInTTuplesList:
1000
        iRootsList = Zi(resultantInTTuple[2]).roots()
1001
        # For each iRoot, compute the corresponding tRoots and check 
1002
        # them in the input polynomial.
1003
        for iRoot in iRootsList:
1004
            #print "iRoot:", iRoot
1005
            # Roots returned by root() are (value, multiplicity) tuples.
1006
            tRootsList = \
1007
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
1008
            print tRootsList
1009
            # The tRootsList can be empty, hence the test.
1010
            if len(tRootsList) != 0:
1011
                for tRoot in tRootsList:
1012
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
1013
                    # polyEvalModN must be an integer.
1014
                    if polyEvalModN == int(polyEvalModN):
1015
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
1016
    return polynomialRootsSet
1017
# End slz_compute_integer_polynomial_modular_roots_2.
1018
#
1019
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
1020
                                        upperBoundSa, approxAccurSa, 
1021
                                        precSa=None, debug=False):
1022
    """
1023
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
1024
    a polynomial that approximates the function on a an interval starting
1025
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1026
    approximates with the expected precision.
1027
    The interval upper bound is lowered until the expected approximation 
1028
    precision is reached.
1029
    The polynomial, the bounds, the center of the interval and the error
1030
    are returned.
1031
    Argument debug is not used.
1032
    OUTPUT:
1033
    A tuple made of 4 Sollya objects:
1034
    - a polynomial;
1035
    - an range (an interval, not in the sense of number given as an interval);
1036
    - the center of the interval;
1037
    - the maximum error in the approximation of the input functionSo by the
1038
      output polynomial ; this error <= approxAccurSaS.
1039
    
1040
    """
1041
    #print"In slz_compute_polynomial_and_interval..."
1042
    ## Superficial argument check.
1043
    if lowerBoundSa > upperBoundSa:
1044
        return None
1045
    ## Change Sollya precision, if requested.
1046
    if precSa is None:
1047
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
1048
        #print "Computed internal precision:", precSa
1049
        if precSa < 192:
1050
            precSa = 192
1051
    sollyaPrecChanged = False
1052
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1053
    if precSa > initialSollyaPrecSa:
1054
        if precSa <= 2:
1055
            print inspect.stack()[0][3], ": precision change <=2 requested."
1056
        pobyso_set_prec_sa_so(precSa)
1057
        sollyaPrecChanged = True
1058
    RRR = lowerBoundSa.parent()
1059
    intervalShrinkConstFactorSa = RRR('0.9')
1060
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1061
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1062
    currentUpperBoundSa = upperBoundSa
1063
    currentLowerBoundSa = lowerBoundSa
1064
    # What we want here is the polynomial without the variable change, 
1065
    # since our actual variable will be x-intervalCenter defined over the 
1066
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
1067
    (polySo, intervalCenterSo, maxErrorSo) = \
1068
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1069
                                                    currentRangeSo, 
1070
                                                    absoluteErrorTypeSo)
1071
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1072
    while maxErrorSa > approxAccurSa:
1073
        print "++Approximation error:", maxErrorSa.n()
1074
        sollya_lib_clear_obj(polySo)
1075
        sollya_lib_clear_obj(intervalCenterSo)
1076
        sollya_lib_clear_obj(maxErrorSo)
1077
        # Very empirical shrinking factor.
1078
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1079
        print "Shrink factor:", \
1080
              shrinkFactorSa.n(), \
1081
              intervalShrinkConstFactorSa
1082
        print
1083
        #errorRatioSa = approxAccurSa/maxErrorSa
1084
        #print "Error ratio: ", errorRatioSa
1085
        # Make sure interval shrinks.
1086
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1087
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1088
            #print "Fixed"
1089
        else:
1090
            actualShrinkFactorSa = shrinkFactorSa
1091
            #print "Computed",shrinkFactorSa,maxErrorSa
1092
            #print shrinkFactorSa, maxErrorSa
1093
        #print "Shrink factor", actualShrinkFactorSa
1094
        currentUpperBoundSa = currentLowerBoundSa + \
1095
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1096
                                actualShrinkFactorSa
1097
        #print "Current upper bound:", currentUpperBoundSa
1098
        sollya_lib_clear_obj(currentRangeSo)
1099
        # Check what is left with the bounds.
1100
        if currentUpperBoundSa <= currentLowerBoundSa or \
1101
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1102
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1103
            print "Can't find an interval."
1104
            print "Use either or both a higher polynomial degree or a higher",
1105
            print "internal precision."
1106
            print "Aborting!"
1107
            if sollyaPrecChanged:
1108
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1109
            sollya_lib_clear_obj(initialSollyaPrecSo)
1110
            return None
1111
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1112
                                                      currentUpperBoundSa)
1113
        # print "New interval:",
1114
        # pobyso_autoprint(currentRangeSo)
1115
        #print "Second Taylor expansion call."
1116
        (polySo, intervalCenterSo, maxErrorSo) = \
1117
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1118
                                                        currentRangeSo, 
1119
                                                        absoluteErrorTypeSo)
1120
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1121
        #print "Max errorSo:",
1122
        #pobyso_autoprint(maxErrorSo)
1123
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1124
        #print "Max errorSa:", maxErrorSa
1125
        #print "Sollya prec:",
1126
        #pobyso_autoprint(sollya_lib_get_prec(None))
1127
    # End while
1128
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1129
    if sollyaPrecChanged:
1130
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1131
    sollya_lib_clear_obj(initialSollyaPrecSo)
1132
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
1133
# End slz_compute_polynomial_and_interval
1134

    
1135
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
1136
                                        upperBoundSa, approxAccurSa, 
1137
                                        sollyaPrecSa=None, debug=False):
1138
    """
1139
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
1140
    a polynomial that approximates the function on a an interval starting
1141
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1142
    approximates with the expected precision.
1143
    The interval upper bound is lowered until the expected approximation 
1144
    precision is reached.
1145
    The polynomial, the bounds, the center of the interval and the error
1146
    are returned.
1147
    OUTPUT:
1148
    A tuple made of 4 Sollya objects:
1149
    - a polynomial;
1150
    - an range (an interval, not in the sense of number given as an interval);
1151
    - the center of the interval;
1152
    - the maximum error in the approximation of the input functionSo by the
1153
      output polynomial ; this error <= approxAccurSaS.
1154
      
1155
    Changes from version 0 (no number):
1156
    - make use of debug argument;
1157
    - polynomial coefficients are "shaved".
1158
    
1159
    """
1160
    #print"In slz_compute_polynomial_and_interval..."
1161
    ## Superficial argument check.
1162
    if lowerBoundSa > upperBoundSa:
1163
        print inspect.stack()[0][3], ": lower bound is larger than upper bound. "
1164
        return None
1165
    ## Change Sollya precision, if requested.
1166
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1167
    sollyaPrecChangedSa = False
1168
    if sollyaPrecSa is None:
1169
        sollyaPrecSa = initialSollyaPrecSa
1170
    else:
1171
        if sollyaPrecSa > initialSollyaPrecSa:
1172
            if sollyaPrecSa <= 2:
1173
                print inspect.stack()[0][3], ": precision change <= 2 requested."
1174
            pobyso_set_prec_sa_so(sollyaPrecSa)
1175
            sollyaPrecChangedSa = True
1176
    ## Other initializations and data recovery.
1177
    RRR = lowerBoundSa.parent()
1178
    intervalShrinkConstFactorSa = RRR('0.9')
1179
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1180
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1181
    currentUpperBoundSa = upperBoundSa
1182
    currentLowerBoundSa = lowerBoundSa
1183
    # What we want here is the polynomial without the variable change, 
1184
    # since our actual variable will be x-intervalCenter defined over the 
1185
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
1186
    (polySo, intervalCenterSo, maxErrorSo) = \
1187
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1188
                                                    currentRangeSo, 
1189
                                                    absoluteErrorTypeSo)
1190
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1191
    while maxErrorSa > approxAccurSa:
1192
        print "++Approximation error:", maxErrorSa.n()
1193
        sollya_lib_clear_obj(polySo)
1194
        sollya_lib_clear_obj(intervalCenterSo)
1195
        sollya_lib_clear_obj(maxErrorSo)
1196
        # Very empirical shrinking factor.
1197
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1198
        print "Shrink factor:", \
1199
              shrinkFactorSa.n(), \
1200
              intervalShrinkConstFactorSa
1201
        print
1202
        #errorRatioSa = approxAccurSa/maxErrorSa
1203
        #print "Error ratio: ", errorRatioSa
1204
        # Make sure interval shrinks.
1205
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1206
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1207
            #print "Fixed"
1208
        else:
1209
            actualShrinkFactorSa = shrinkFactorSa
1210
            #print "Computed",shrinkFactorSa,maxErrorSa
1211
            #print shrinkFactorSa, maxErrorSa
1212
        #print "Shrink factor", actualShrinkFactorSa
1213
        currentUpperBoundSa = currentLowerBoundSa + \
1214
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1215
                                actualShrinkFactorSa
1216
        #print "Current upper bound:", currentUpperBoundSa
1217
        sollya_lib_clear_obj(currentRangeSo)
1218
        # Check what is left with the bounds.
1219
        if currentUpperBoundSa <= currentLowerBoundSa or \
1220
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1221
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1222
            print "Can't find an interval."
1223
            print "Use either or both a higher polynomial degree or a higher",
1224
            print "internal precision."
1225
            print "Aborting!"
1226
            if sollyaPrecChangedSa:
1227
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1228
            sollya_lib_clear_obj(initialSollyaPrecSo)
1229
            return None
1230
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1231
                                                      currentUpperBoundSa)
1232
        # print "New interval:",
1233
        # pobyso_autoprint(currentRangeSo)
1234
        #print "Second Taylor expansion call."
1235
        (polySo, intervalCenterSo, maxErrorSo) = \
1236
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1237
                                                        currentRangeSo, 
1238
                                                        absoluteErrorTypeSo)
1239
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1240
        #print "Max errorSo:",
1241
        #pobyso_autoprint(maxErrorSo)
1242
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1243
        #print "Max errorSa:", maxErrorSa
1244
        #print "Sollya prec:",
1245
        #pobyso_autoprint(sollya_lib_get_prec(None))
1246
    # End while
1247
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1248
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1249
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1250
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1251
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1252
    if debug:
1253
        print inspect.stack()[0][3], "SollyaPrecSa:", sollyaPrecSa
1254
        print "About to call polynomial rounding with:"
1255
        print "polySo: ",           ; pobyso_autoprint(polySo)
1256
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
1257
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1258
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1259
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
1260
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1261
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1262
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1263
    """
1264
    # Naive rounding.
1265
    (roundedPolySo, roundedPolyMaxErrSo) = \
1266
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
1267
                                                           functionSo,
1268
                                                           intervalCenterSo,
1269
                                                           currentRangeSo,
1270
                                                           itpSo,
1271
                                                           ftpSo,
1272
                                                           maxPrecSo,
1273
                                                           approxAccurSo)
1274
    """
1275
    # Proved rounding.
1276
    (roundedPolySo, roundedPolyMaxErrSo) = \
1277
        pobyso_round_coefficients_progressive_so_so(polySo, 
1278
                                                    functionSo,
1279
                                                    maxPrecSo,
1280
                                                    currentRangeSo,
1281
                                                    intervalCenterSo,
1282
                                                    maxErrorSo,
1283
                                                    approxAccurSo,
1284
                                                    debug=False)
1285
    #### Comment out the two next lines when polynomial rounding is activated.
1286
    #roundedPolySo = sollya_lib_copy_obj(polySo)
1287
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
1288
    sollya_lib_clear_obj(polySo)
1289
    sollya_lib_clear_obj(maxErrorSo)
1290
    sollya_lib_clear_obj(itpSo)
1291
    sollya_lib_clear_obj(ftpSo)
1292
    sollya_lib_clear_obj(approxAccurSo)
1293
    if sollyaPrecChangedSa:
1294
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1295
    sollya_lib_clear_obj(initialSollyaPrecSo)
1296
    if debug:
1297
        print "1: ", ; pobyso_autoprint(roundedPolySo)
1298
        print "2: ", ; pobyso_autoprint(currentRangeSo)
1299
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
1300
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1301
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1302
# End slz_compute_polynomial_and_interval_01
1303

    
1304
def slz_compute_polynomial_and_interval_02(functionSo, degreeSo, lowerBoundSa, 
1305
                                        upperBoundSa, approxAccurSa, 
1306
                                        sollyaPrecSa=None, debug=True ):
1307
    """
1308
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
1309
    a polynomial that approximates the function on a an interval starting
1310
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1311
    approximates with the expected precision.
1312
    The interval upper bound is lowered until the expected approximation 
1313
    precision is reached.
1314
    The polynomial, the bounds, the center of the interval and the error
1315
    are returned.
1316
    OUTPUT:
1317
    A tuple made of 4 Sollya objects:
1318
    - a polynomial;
1319
    - an range (an interval, not in the sense of number given as an interval);
1320
    - the center of the interval;
1321
    - the maximum error in the approximation of the input functionSo by the
1322
      output polynomial ; this error <= approxAccurSaS.
1323
    Changes fom v 01:
1324
        extra verbose.
1325
    """
1326
    print"In slz_compute_polynomial_and_interval..."
1327
    ## Superficial argument check.
1328
    if lowerBoundSa > upperBoundSa:
1329
        return None
1330
    ## Change Sollya precision, if requested.
1331
    sollyaPrecChanged = False
1332
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1333
    #print "Initial Sollya prec:", initialSollyaPrecSa, type(initialSollyaPrecSa)
1334
    if sollyaPrecSa is None:
1335
        sollyaPrecSa = initialSollyaPrecSa
1336
    else:
1337
        if sollyaPrecSa <= 2:
1338
            print inspect.stack()[0][3], ": precision change <=2 requested."
1339
        pobyso_set_prec_sa_so(sollyaPrecSa)
1340
        sollyaPrecChanged = True
1341
    RRR = lowerBoundSa.parent()
1342
    intervalShrinkConstFactorSa = RRR('0.9')
1343
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1344
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1345
    currentUpperBoundSa = upperBoundSa
1346
    currentLowerBoundSa = lowerBoundSa
1347
    #pobyso_autoprint(functionSo)
1348
    #pobyso_autoprint(degreeSo)
1349
    #pobyso_autoprint(currentRangeSo)
1350
    #pobyso_autoprint(absoluteErrorTypeSo)
1351
    ## What we want here is the polynomial without the variable change, 
1352
    #  since our actual variable will be x-intervalCenter defined over the 
1353
    #  domain [lowerBound-intervalCenter , upperBound-intervalCenter].
1354
    (polySo, intervalCenterSo, maxErrorSo) = \
1355
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1356
                                                    currentRangeSo, 
1357
                                                    absoluteErrorTypeSo)
1358
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1359
    print "...after Taylor expansion."
1360
    while maxErrorSa > approxAccurSa:
1361
        print "++Approximation error:", maxErrorSa.n()
1362
        sollya_lib_clear_obj(polySo)
1363
        sollya_lib_clear_obj(intervalCenterSo)
1364
        sollya_lib_clear_obj(maxErrorSo)
1365
        # Very empirical shrinking factor.
1366
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1367
        print "Shrink factor:", \
1368
              shrinkFactorSa.n(), \
1369
              intervalShrinkConstFactorSa
1370
        print
1371
        #errorRatioSa = approxAccurSa/maxErrorSa
1372
        #print "Error ratio: ", errorRatioSa
1373
        # Make sure interval shrinks.
1374
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1375
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1376
            #print "Fixed"
1377
        else:
1378
            actualShrinkFactorSa = shrinkFactorSa
1379
            #print "Computed",shrinkFactorSa,maxErrorSa
1380
            #print shrinkFactorSa, maxErrorSa
1381
        #print "Shrink factor", actualShrinkFactorSa
1382
        currentUpperBoundSa = currentLowerBoundSa + \
1383
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1384
                                actualShrinkFactorSa
1385
        #print "Current upper bound:", currentUpperBoundSa
1386
        sollya_lib_clear_obj(currentRangeSo)
1387
        # Check what is left with the bounds.
1388
        if currentUpperBoundSa <= currentLowerBoundSa or \
1389
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1390
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1391
            print "Can't find an interval."
1392
            print "Use either or both a higher polynomial degree or a higher",
1393
            print "internal precision."
1394
            print "Aborting!"
1395
            if sollyaPrecChanged:
1396
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1397
            sollya_lib_clear_obj(initialSollyaPrecSo)
1398
            return None
1399
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1400
                                                      currentUpperBoundSa)
1401
        # print "New interval:",
1402
        # pobyso_autoprint(currentRangeSo)
1403
        #print "Second Taylor expansion call."
1404
        (polySo, intervalCenterSo, maxErrorSo) = \
1405
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1406
                                                        currentRangeSo, 
1407
                                                        absoluteErrorTypeSo)
1408
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1409
        #print "Max errorSo:",
1410
        #pobyso_autoprint(maxErrorSo)
1411
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1412
        #print "Max errorSa:", maxErrorSa
1413
        #print "Sollya prec:",
1414
        #pobyso_autoprint(sollya_lib_get_prec(None))
1415
    # End while
1416
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1417
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1418
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1419
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1420
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1421
    print "About to call polynomial rounding with:"
1422
    print "polySo: ",           ; pobyso_autoprint(polySo)
1423
    print "functionSo: ",       ; pobyso_autoprint(functionSo)
1424
    print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1425
    print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1426
    print "itpSo: ",            ; pobyso_autoprint(itpSo)
1427
    print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1428
    print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1429
    print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1430
    (roundedPolySo, roundedPolyMaxErrSo) = \
1431
    pobyso_round_coefficients_progressive_so_so(polySo,
1432
                                                functionSo,
1433
                                                maxPrecSo,
1434
                                                currentRangeSo,
1435
                                                intervalCenterSo,
1436
                                                maxErrorSo,
1437
                                                approxAccurSo,
1438
                                                debug = True)
1439
    
1440
    sollya_lib_clear_obj(polySo)
1441
    sollya_lib_clear_obj(maxErrorSo)
1442
    sollya_lib_clear_obj(itpSo)
1443
    sollya_lib_clear_obj(ftpSo)
1444
    sollya_lib_clear_obj(approxAccurSo)
1445
    if sollyaPrecChanged:
1446
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1447
    sollya_lib_clear_obj(initialSollyaPrecSo)
1448
    print "1: ", ; pobyso_autoprint(roundedPolySo)
1449
    print "2: ", ; pobyso_autoprint(currentRangeSo)
1450
    print "3: ", ; pobyso_autoprint(intervalCenterSo)
1451
    print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1452
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1453
# End slz_compute_polynomial_and_interval_02
1454

    
1455
def slz_compute_reduced_polynomial(matrixRow,
1456
                                    knownMonomials,
1457
                                    var1,
1458
                                    var1Bound,
1459
                                    var2,
1460
                                    var2Bound):
1461
    """
1462
    Compute a polynomial from a single reduced matrix row.
1463
    This function was introduced in order to avoid the computation of the
1464
    all the polynomials  from the full matrix (even those built from rows 
1465
    that do no verify the Coppersmith condition) as this may involves 
1466
    expensive operations over (large) integers.
1467
    """
1468
    ## Check arguments.
1469
    if len(knownMonomials) == 0:
1470
        return None
1471
    # varNounds can be zero since 0^0 returns 1.
1472
    if (var1Bound < 0) or (var2Bound < 0):
1473
        return None
1474
    ## Initialisations. 
1475
    polynomialRing = knownMonomials[0].parent() 
1476
    currentPolynomial = polynomialRing(0)
1477
    # TODO: use zip instead of indices.
1478
    for colIndex in xrange(0, len(knownMonomials)):
1479
        currentCoefficient = matrixRow[colIndex]
1480
        if currentCoefficient != 0:
1481
            #print "Current coefficient:", currentCoefficient
1482
            currentMonomial = knownMonomials[colIndex]
1483
            #print "Monomial as multivariate polynomial:", \
1484
            #currentMonomial, type(currentMonomial)
1485
            degreeInVar1 = currentMonomial.degree(var1)
1486
            #print "Degree in var1", var1, ":", degreeInVar1
1487
            degreeInVar2 = currentMonomial.degree(var2)
1488
            #print "Degree in var2", var2, ":", degreeInVar2
1489
            if degreeInVar1 > 0:
1490
                currentCoefficient = \
1491
                    currentCoefficient / (var1Bound^degreeInVar1)
1492
                #print "varBound1 in degree:", var1Bound^degreeInVar1
1493
                #print "Current coefficient(1)", currentCoefficient
1494
            if degreeInVar2 > 0:
1495
                currentCoefficient = \
1496
                    currentCoefficient / (var2Bound^degreeInVar2)
1497
                #print "Current coefficient(2)", currentCoefficient
1498
            #print "Current reduced monomial:", (currentCoefficient * \
1499
            #                                    currentMonomial)
1500
            currentPolynomial += (currentCoefficient * currentMonomial)
1501
            #print "Current polynomial:", currentPolynomial
1502
        # End if
1503
    # End for colIndex.
1504
    #print "Type of the current polynomial:", type(currentPolynomial)
1505
    return(currentPolynomial)
1506
# End slz_compute_reduced_polynomial
1507
#
1508
def slz_compute_reduced_polynomials(reducedMatrix,
1509
                                        knownMonomials,
1510
                                        var1,
1511
                                        var1Bound,
1512
                                        var2,
1513
                                        var2Bound):
1514
    """
1515
    Legacy function, use slz_compute_reduced_polynomials_list
1516
    """
1517
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1518
                                                knownMonomials,
1519
                                                var1,
1520
                                                var1Bound,
1521
                                                var2,
1522
                                                var2Bound)
1523
    )
1524
#
1525
def slz_compute_reduced_polynomials_list(reducedMatrix,
1526
                                         knownMonomials,
1527
                                         var1,
1528
                                         var1Bound,
1529
                                         var2,
1530
                                         var2Bound):
1531
    """
1532
    From a reduced matrix, holding the coefficients, from a monomials list,
1533
    from the bounds of each variable, compute the corresponding polynomials
1534
    scaled back by dividing by the "right" powers of the variables bounds.
1535
    
1536
    The elements in knownMonomials must be of the "right" polynomial type.
1537
    They set the polynomial type of the output polynomials in list.
1538
    @param reducedMatrix:  the reduced matrix as output from LLL;
1539
    @param kwnonMonomials: the ordered list of the monomials used to
1540
                           build the polynomials;
1541
    @param var1:           the first variable (of the "right" type);
1542
    @param var1Bound:      the first variable bound;
1543
    @param var2:           the second variable (of the "right" type);
1544
    @param var2Bound:      the second variable bound.
1545
    @return: a list of polynomials obtained with the reduced coefficients
1546
             and scaled down with the bounds
1547
    """
1548
    
1549
    # TODO: check input arguments.
1550
    reducedPolynomials = []
1551
    #print "type var1:", type(var1), " - type var2:", type(var2)
1552
    for matrixRow in reducedMatrix.rows():
1553
        currentPolynomial = 0
1554
        for colIndex in xrange(0, len(knownMonomials)):
1555
            currentCoefficient = matrixRow[colIndex]
1556
            if currentCoefficient != 0:
1557
                #print "Current coefficient:", currentCoefficient
1558
                currentMonomial = knownMonomials[colIndex]
1559
                parentRing = currentMonomial.parent()
1560
                #print "Monomial as multivariate polynomial:", \
1561
                #currentMonomial, type(currentMonomial)
1562
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1563
                #print "Degree in var", var1, ":", degreeInVar1
1564
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1565
                #print "Degree in var", var2, ":", degreeInVar2
1566
                if degreeInVar1 > 0:
1567
                    currentCoefficient /= var1Bound^degreeInVar1
1568
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1569
                    #print "Current coefficient(1)", currentCoefficient
1570
                if degreeInVar2 > 0:
1571
                    currentCoefficient /= var2Bound^degreeInVar2
1572
                    #print "Current coefficient(2)", currentCoefficient
1573
                #print "Current reduced monomial:", (currentCoefficient * \
1574
                #                                    currentMonomial)
1575
                currentPolynomial += (currentCoefficient * currentMonomial)
1576
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1577
                    #print "!!!! constant term !!!!"
1578
                #print "Current polynomial:", currentPolynomial
1579
            # End if
1580
        # End for colIndex.
1581
        #print "Type of the current polynomial:", type(currentPolynomial)
1582
        reducedPolynomials.append(currentPolynomial)
1583
    return reducedPolynomials 
1584
# End slz_compute_reduced_polynomials_list.
1585

    
1586
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1587
                                                   knownMonomials,
1588
                                                   var1,
1589
                                                   var1Bound,
1590
                                                   var2,
1591
                                                   var2Bound):
1592
    """
1593
    From a list of rows, holding the coefficients, from a monomials list,
1594
    from the bounds of each variable, compute the corresponding polynomials
1595
    scaled back by dividing by the "right" powers of the variables bounds.
1596
    
1597
    The elements in knownMonomials must be of the "right" polynomial type.
1598
    They set the polynomial type of the output polynomials in list.
1599
    @param rowsList:       the reduced matrix as output from LLL;
1600
    @param kwnonMonomials: the ordered list of the monomials used to
1601
                           build the polynomials;
1602
    @param var1:           the first variable (of the "right" type);
1603
    @param var1Bound:      the first variable bound;
1604
    @param var2:           the second variable (of the "right" type);
1605
    @param var2Bound:      the second variable bound.
1606
    @return: a list of polynomials obtained with the reduced coefficients
1607
             and scaled down with the bounds
1608
    """
1609
    
1610
    # TODO: check input arguments.
1611
    reducedPolynomials = []
1612
    #print "type var1:", type(var1), " - type var2:", type(var2)
1613
    for matrixRow in rowsList:
1614
        currentPolynomial = 0
1615
        for colIndex in xrange(0, len(knownMonomials)):
1616
            currentCoefficient = matrixRow[colIndex]
1617
            if currentCoefficient != 0:
1618
                #print "Current coefficient:", currentCoefficient
1619
                currentMonomial = knownMonomials[colIndex]
1620
                parentRing = currentMonomial.parent()
1621
                #print "Monomial as multivariate polynomial:", \
1622
                #currentMonomial, type(currentMonomial)
1623
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1624
                #print "Degree in var", var1, ":", degreeInVar1
1625
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1626
                #print "Degree in var", var2, ":", degreeInVar2
1627
                if degreeInVar1 > 0:
1628
                    currentCoefficient /= var1Bound^degreeInVar1
1629
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1630
                    #print "Current coefficient(1)", currentCoefficient
1631
                if degreeInVar2 > 0:
1632
                    currentCoefficient /= var2Bound^degreeInVar2
1633
                    #print "Current coefficient(2)", currentCoefficient
1634
                #print "Current reduced monomial:", (currentCoefficient * \
1635
                #                                    currentMonomial)
1636
                currentPolynomial += (currentCoefficient * currentMonomial)
1637
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1638
                    #print "!!!! constant term !!!!"
1639
                #print "Current polynomial:", currentPolynomial
1640
            # End if
1641
        # End for colIndex.
1642
        #print "Type of the current polynomial:", type(currentPolynomial)
1643
        reducedPolynomials.append(currentPolynomial)
1644
    return reducedPolynomials 
1645
# End slz_compute_reduced_polynomials_list_from_rows.
1646
#
1647
def slz_compute_scaled_function(functionSa,
1648
                                lowerBoundSa,
1649
                                upperBoundSa,
1650
                                floatingPointPrecSa,
1651
                                debug=False):
1652
    """
1653
    From a function, compute the scaled function whose domain
1654
    is included in [1, 2) and whose image is also included in [1,2).
1655
    Return a tuple: 
1656
    [0]: the scaled function
1657
    [1]: the scaled domain lower bound
1658
    [2]: the scaled domain upper bound
1659
    [3]: the scaled image lower bound
1660
    [4]: the scaled image upper bound
1661
    """
1662
    ## The variable can be called anything.
1663
    x = functionSa.variables()[0]
1664
    # Scalling the domain -> [1,2[.
1665
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1666
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1667
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1668
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1669
    if debug:
1670
        print "domainScalingExpression for argument :", \
1671
        invDomainScalingExpressionSa
1672
        print "function: ", functionSa
1673
    ff = functionSa.subs({x : domainScalingExpressionSa})
1674
    ## Bless expression as a function.
1675
    ff = ff.function(x)
1676
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1677
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1678
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1679
    scaledLowerBoundSa = \
1680
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1681
    scaledUpperBoundSa = \
1682
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1683
    if debug:
1684
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1685
              scaledUpperBoundSa
1686
    ## If both bounds are negative, swap them.
1687
    if lowerBoundSa < 0 and upperBoundSa < 0:
1688
        scaledLowerBoundSa, scaledUpperBoundSa = \
1689
        scaledUpperBoundSa,scaledLowerBoundSa
1690
    #
1691
    # Scalling the image -> [1,2[.
1692
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
1693
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
1694
    if flbSa <= fubSa: # Increasing
1695
        imageBinadeBottomSa = floor(flbSa.log2())
1696
    else: # Decreasing
1697
        imageBinadeBottomSa = floor(fubSa.log2())
1698
    if debug:
1699
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
1700
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
1701
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
1702
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
1703
    if debug:
1704
        print "imageScalingExpression for argument :", \
1705
              invImageScalingExpressionSa
1706
    iis = invImageScalingExpressionSa.function(x)
1707
    fff = iis.subs({x:ff})
1708
    if debug:
1709
        print "fff:", fff,
1710
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1711
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1712
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1713
# End slz_compute_scaled_function
1714

    
1715
def slz_fix_bounds_for_binades(lowerBound, 
1716
                               upperBound, 
1717
                               func = None, 
1718
                               domainDirection = -1,
1719
                               imageDirection  = -1):
1720
    """
1721
    Assuming the function is increasing or decreasing over the 
1722
    [lowerBound, upperBound] interval, return a lower bound lb and 
1723
    an upper bound ub such that:
1724
    - lb and ub belong to the same binade;
1725
    - func(lb) and func(ub) belong to the same binade.
1726
    domainDirection indicate how bounds move to fit into the same binade:
1727
    - a negative value move the upper bound down;
1728
    - a positive value move the lower bound up.
1729
    imageDirection indicate how bounds move in order to have their image
1730
    fit into the same binade, variation of the function is also condidered.
1731
    For an increasing function:
1732
    - negative value moves the upper bound down (and its image value as well);
1733
    - a positive values moves the lower bound up (and its image value as well);
1734
    For a decreasing function it is the other way round.
1735
    """
1736
    ## Arguments check
1737
    if lowerBound > upperBound:
1738
        return None
1739
    if lowerBound == upperBound:
1740
        return (lowerBound, upperBound)
1741
    if func is None:
1742
        return None
1743
    #
1744
    #varFunc = func.variables()[0]
1745
    lb = lowerBound
1746
    ub = upperBound
1747
    lbBinade = slz_compute_binade(lb) 
1748
    ubBinade = slz_compute_binade(ub)
1749
    ## Domain binade.
1750
    while lbBinade != ubBinade:
1751
        newIntervalWidth = (ub - lb) / 2
1752
        if domainDirection < 0:
1753
            ub = lb + newIntervalWidth
1754
            ubBinade = slz_compute_binade(ub)
1755
        else:
1756
            lb = lb + newIntervalWidth
1757
            lbBinade = slz_compute_binade(lb) 
1758
    #print "sfbfb: lower bound:", lb.str(truncate=False)
1759
    #print "sfbfb: upper bound:", ub.str(truncate=False)
1760
    ## At this point, both bounds belond to the same binade.
1761
    ## Image binade.
1762
    if lb == ub:
1763
        return (lb, ub)
1764
    lbImg = func(lb)
1765
    ubImg = func(ub)
1766
    funcIsInc = ((ubImg - lbImg) >= 0)
1767
    lbImgBinade = slz_compute_binade(lbImg)
1768
    ubImgBinade = slz_compute_binade(ubImg)
1769
    while lbImgBinade != ubImgBinade:
1770
        newIntervalWidth = (ub - lb) / 2
1771
        if imageDirection < 0:
1772
            if funcIsInc:
1773
                ub = lb + newIntervalWidth
1774
                ubImgBinade = slz_compute_binade(func(ub))
1775
                #print ubImgBinade
1776
            else:
1777
                lb = lb + newIntervalWidth
1778
                lbImgBinade = slz_compute_binade(func(lb))
1779
                #print lbImgBinade
1780
        else:
1781
            if funcIsInc:
1782
                lb = lb + newIntervalWidth
1783
                lbImgBinade = slz_compute_binade(func(lb))
1784
                #print lbImgBinade
1785
            else:
1786
                ub = lb + newIntervalWidth
1787
                ubImgBinade = slz_compute_binade(func(ub))
1788
                #print ubImgBinade
1789
    # End while lbImgBinade != ubImgBinade:
1790
    return (lb, ub)
1791
# End slz_fix_bounds_for_binades.
1792

    
1793
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1794
    # Create a polynomial over the rationals.
1795
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1796
    return(ratPolynomialRing(polyOfFloat))
1797
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1798

    
1799
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1800
    """
1801
     Create a polynomial over the rationals where all denominators are
1802
     powers of two.
1803
    """
1804
    polyVariable = polyOfFloat.variables()[0] 
1805
    RPR = QQ[str(polyVariable)]
1806
    polyCoeffs      = polyOfFloat.coefficients()
1807
    #print polyCoeffs
1808
    polyExponents   = polyOfFloat.exponents()
1809
    #print polyExponents
1810
    polyDenomPtwoCoeffs = []
1811
    for coeff in polyCoeffs:
1812
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1813
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1814
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1815
    ratPoly = RPR(0)
1816
    #print type(ratPoly)
1817
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1818
    #  The coefficient becomes plainly wrong when exponent == 0.
1819
    #  No clue as to why.
1820
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1821
        ratPoly += coeff * RPR(polyVariable^exponent)
1822
    return ratPoly
1823
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1824

    
1825
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1826
                                      lowerBoundSa, 
1827
                                      upperBoundSa, 
1828
                                      floatingPointPrecSa, 
1829
                                      internalSollyaPrecSa, 
1830
                                      approxAccurSa):
1831
    """
1832
    Under the assumption that:
1833
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1834
    - lowerBound and upperBound belong to the same binade.
1835
    from a:
1836
    - function;
1837
    - a degree
1838
    - a pair of bounds;
1839
    - the floating-point precision we work on;
1840
    - the internal Sollya precision;
1841
    - the requested approximation error
1842
    The initial interval is, possibly, splitted into smaller intervals.
1843
    It return a list of tuples, each made of:
1844
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1845
    - a second polynomial (with a changed variable f(x) = q(x))
1846
    - the approximation interval;
1847
    - the center, x0, of the interval;
1848
    - the corresponding approximation error.
1849
    TODO: fix endless looping for some parameters sets.
1850
    """
1851
    resultArray = []
1852
    # Set Sollya to the necessary internal precision.
1853
    sollyaPrecChangedSa = False
1854
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1855
    if internalSollyaPrecSa > initialSollyaPrecSa:
1856
        if internalSollyaPrecSa <= 2:
1857
            print inspect.stack()[0][3], ": precision change <=2 requested."
1858
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1859
        sollyaPrecChangedSa = True
1860
    #
1861
    x = functionSa.variables()[0] # Actual variable name can be anything.
1862
    # Scaled function: [1=,2] -> [1,2].
1863
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1864
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1865
                slz_compute_scaled_function(functionSa,                       \
1866
                                            lowerBoundSa,                     \
1867
                                            upperBoundSa,                     \
1868
                                            floatingPointPrecSa)
1869
    # In case bounds were in the negative real one may need to
1870
    # switch scaled bounds.
1871
    if scaledLowerBoundSa > scaledUpperBoundSa:
1872
        scaledLowerBoundSa, scaledUpperBoundSa = \
1873
            scaledUpperBoundSa, scaledLowerBoundSa
1874
        #print "Switching!"
1875
    print "Approximation accuracy: ", RR(approxAccurSa)
1876
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1877
    functionSo = \
1878
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1879
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1880
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1881
                                                  scaledUpperBoundSa)
1882

    
1883
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1884
                                              upperBoundSa.parent().precision()))
1885
    currentScaledLowerBoundSa = scaledLowerBoundSa
1886
    currentScaledUpperBoundSa = scaledUpperBoundSa
1887
    while True:
1888
        ## Compute the first Taylor expansion.
1889
        print "Computing a Taylor expansion..."
1890
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1891
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1892
                                        currentScaledLowerBoundSa, 
1893
                                        currentScaledUpperBoundSa,
1894
                                        approxAccurSa, internalSollyaPrecSa)
1895
        print "...done."
1896
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1897
        #  This value goes to the first variable: polySo. Other variables are
1898
        #  not assigned and should not be tested.
1899
        if polySo is None:
1900
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1901
            if precChangedSa:
1902
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1903
            sollya_lib_clear_obj(initialSollyaPrecSo)
1904
            sollya_lib_clear_obj(functionSo)
1905
            sollya_lib_clear_obj(degreeSo)
1906
            sollya_lib_clear_obj(scaledBoundsSo)
1907
            return None
1908
        ## Add to the result array.
1909
        ### Change variable stuff in Sollya x -> x0-x.
1910
        changeVarExpressionSo = \
1911
            sollya_lib_build_function_sub( \
1912
                              sollya_lib_build_function_free_variable(), 
1913
                              sollya_lib_copy_obj(intervalCenterSo))
1914
        polyVarChangedSo = \
1915
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1916
        #### Get rid of the variable change Sollya stuff. 
1917
        sollya_lib_clear_obj(changeVarExpressionSo)
1918
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1919
                            intervalCenterSo, maxErrorSo))
1920
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1921
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1922
        print "Computed approximation error:", errorSa.n(digits=10)
1923
        # If the error and interval are OK a the first try, just return.
1924
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1925
            (errorSa <= approxAccurSa):
1926
            if sollyaPrecChangedSa:
1927
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1928
            sollya_lib_clear_obj(initialSollyaPrecSo)
1929
            sollya_lib_clear_obj(functionSo)
1930
            sollya_lib_clear_obj(degreeSo)
1931
            sollya_lib_clear_obj(scaledBoundsSo)
1932
            #print "Approximation error:", errorSa
1933
            return resultArray
1934
        ## The returned interval upper bound does not reach the requested upper
1935
        #  upper bound: compute the next upper bound.
1936
        ## The following ratio is always >= 1. If errorSa, we may want to
1937
        #  enlarge the interval
1938
        currentErrorRatio = approxAccurSa / errorSa
1939
        ## --|--------------------------------------------------------------|--
1940
        #  --|--------------------|--------------------------------------------
1941
        #  --|----------------------------|------------------------------------
1942
        ## Starting point for the next upper bound.
1943
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1944
        # Compute the increment. 
1945
        newBoundsWidthSa = \
1946
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1947
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1948
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1949
        # Take into account the original interval upper bound.                     
1950
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1951
            currentScaledUpperBoundSa = scaledUpperBoundSa
1952
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1953
            print "Can't shrink the interval anymore!"
1954
            print "You should consider increasing the Sollya internal precision"
1955
            print "or the polynomial degree."
1956
            print "Giving up!"
1957
            if precChangedSa:
1958
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1959
            sollya_lib_clear_obj(initialSollyaPrecSo)
1960
            sollya_lib_clear_obj(functionSo)
1961
            sollya_lib_clear_obj(degreeSo)
1962
            sollya_lib_clear_obj(scaledBoundsSo)
1963
            return None
1964
        # Compute the other expansions.
1965
        # Test for insufficient precision.
1966
# End slz_get_intervals_and_polynomials
1967

    
1968
def slz_interval_scaling_expression(boundsInterval, expVar):
1969
    """
1970
    Compute the scaling expression to map an interval that spans at most
1971
    a single binade into [1, 2) and the inverse expression as well.
1972
    If the interval spans more than one binade, result is wrong since
1973
    scaling is only based on the lower bound.
1974
    Not very sure that the transformation makes sense for negative numbers.
1975
    """
1976
    # The "one of the bounds is 0" special case. Here we consider
1977
    # the interval as the subnormals binade.
1978
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1979
        # The upper bound is (or should be) positive.
1980
        if boundsInterval.endpoints()[0] == 0:
1981
            if boundsInterval.endpoints()[1] == 0:
1982
                return None
1983
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1984
            l2     = boundsInterval.endpoints()[1].abs().log2()
1985
            # The upper bound is a power of two
1986
            if binade == l2:
1987
                scalingCoeff    = 2^(-binade)
1988
                invScalingCoeff = 2^(binade)
1989
                scalingOffset   = 1
1990
                return \
1991
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1992
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1993
            else:
1994
                scalingCoeff    = 2^(-binade-1)
1995
                invScalingCoeff = 2^(binade+1)
1996
                scalingOffset   = 1
1997
                return((scalingCoeff * expVar + scalingOffset),\
1998
                    ((expVar - scalingOffset) * invScalingCoeff))
1999
        # The lower bound is (or should be) negative.
2000
        if boundsInterval.endpoints()[1] == 0:
2001
            if boundsInterval.endpoints()[0] == 0:
2002
                return None
2003
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
2004
            l2     = boundsInterval.endpoints()[0].abs().log2()
2005
            # The upper bound is a power of two
2006
            if binade == l2:
2007
                scalingCoeff    = -2^(-binade)
2008
                invScalingCoeff = -2^(binade)
2009
                scalingOffset   = 1
2010
                return((scalingCoeff * expVar + scalingOffset),\
2011
                    ((expVar - scalingOffset) * invScalingCoeff))
2012
            else:
2013
                scalingCoeff    = -2^(-binade-1)
2014
                invScalingCoeff = -2^(binade+1)
2015
                scalingOffset   = 1
2016
                return((scalingCoeff * expVar + scalingOffset),\
2017
                   ((expVar - scalingOffset) * invScalingCoeff))
2018
    # General case
2019
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
2020
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
2021
    # We allow for a single exception in binade spanning is when the
2022
    # "outward bound" is a power of 2 and its binade is that of the
2023
    # "inner bound" + 1.
2024
    if boundsInterval.endpoints()[0] > 0:
2025
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
2026
        if lbBinade != ubBinade:
2027
            print "Different binades."
2028
            if ubL2 != ubBinade:
2029
                print "Not a power of 2."
2030
                return None
2031
            elif abs(ubBinade - lbBinade) > 1:
2032
                print "Too large span:", abs(ubBinade - lbBinade)
2033
                return None
2034
    else:
2035
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
2036
        if lbBinade != ubBinade:
2037
            print "Different binades."
2038
            if lbL2 != lbBinade:
2039
                print "Not a power of 2."
2040
                return None
2041
            elif abs(ubBinade - lbBinade) > 1:
2042
                print "Too large span:", abs(ubBinade - lbBinade)
2043
                return None
2044
    #print "Lower bound binade:", binade
2045
    if boundsInterval.endpoints()[0] > 0:
2046
        return \
2047
            ((2^(-lbBinade) * expVar).function(expVar),
2048
             (2^(lbBinade) * expVar).function(expVar))
2049
    else:
2050
        return \
2051
            ((-(2^(-ubBinade)) * expVar).function(expVar),
2052
             (-(2^(ubBinade)) * expVar).function(expVar))
2053
"""
2054
    # Code sent to attic. Should be the base for a 
2055
    # "slz_interval_translate_expression" rather than scale.
2056
    # Extra control and special cases code  added in  
2057
    # slz_interval_scaling_expression could (should ?) be added to
2058
    # this new function.
2059
    # The scaling offset is only used for negative numbers.
2060
    # When the absolute value of the lower bound is < 0.
2061
    if abs(boundsInterval.endpoints()[0]) < 1:
2062
        if boundsInterval.endpoints()[0] >= 0:
2063
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
2064
            invScalingCoeff = 1/scalingCoeff
2065
            return((scalingCoeff * expVar, 
2066
                    invScalingCoeff * expVar))
2067
        else:
2068
            scalingCoeff = \
2069
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
2070
            scalingOffset = -3 * scalingCoeff
2071
            return((scalingCoeff * expVar + scalingOffset,
2072
                    1/scalingCoeff * expVar + 3))
2073
    else: 
2074
        if boundsInterval.endpoints()[0] >= 0:
2075
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
2076
            scalingOffset = 0
2077
            return((scalingCoeff * expVar, 
2078
                    1/scalingCoeff * expVar))
2079
        else:
2080
            scalingCoeff = \
2081
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
2082
            scalingOffset =  -3 * scalingCoeff
2083
            #scalingOffset = 0
2084
            return((scalingCoeff * expVar + scalingOffset,
2085
                    1/scalingCoeff * expVar + 3))
2086
"""
2087
# End slz_interval_scaling_expression
2088
   
2089
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
2090
    """
2091
    Compute the Sage version of the Taylor polynomial and it's
2092
    companion data (interval, center...)
2093
    The input parameter is a five elements tuple:
2094
    - [0]: the polyomial (without variable change), as polynomial over a
2095
           real ring;
2096
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
2097
           over a real ring;
2098
    - [2]: the interval (as Sollya range);
2099
    - [3]: the interval center;
2100
    - [4]: the approximation error. 
2101
    
2102
    The function returns a 5 elements tuple: formed with all the 
2103
    input elements converted into their Sollya counterpart.
2104
    """
2105
    #print "Sollya polynomial to convert:", 
2106
    #pobyso_autoprint(polyRangeCenterErrorSo)
2107
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
2108
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
2109
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
2110
    intervalSa = \
2111
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
2112
    centerSa = \
2113
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
2114
    errorSa = \
2115
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
2116
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
2117
    # End slz_interval_and_polynomial_to_sage
2118

    
2119
def slz_is_htrn(argument, 
2120
                function, 
2121
                targetAccuracy, 
2122
                targetRF = None, 
2123
                targetPlusOnePrecRF = None, 
2124
                quasiExactRF = None):
2125
    """
2126
      Check if an element (argument) of the domain of function (function)
2127
      yields a HTRN case (rounding to next) for the target precision 
2128
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
2129
      
2130
      The strategy is: 
2131
      - compute the image at high (quasi-exact) precision;
2132
      - round it to nearest to precision;
2133
      - round it to exactly to (precision+1), the computed number has two
2134
        midpoint neighbors;
2135
      - check the distance between these neighbors and the quasi-exact value;
2136
        - if none of them is closer than the targetAccuracy, return False,
2137
        - else return true.
2138
      - Powers of two are a special case when comparing the midpoint in
2139
        the 0 direction..
2140
    """
2141
    ## Arguments filtering. 
2142
    ## TODO: filter the first argument for consistence.
2143
    if targetRF is None:
2144
        targetRF = argument.parent()
2145
    ## Ditto for the real field holding the midpoints.
2146
    if targetPlusOnePrecRF is None:
2147
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
2148
    ## If no quasiExactField is provided, create a high accuracy RealField.
2149
    if quasiExactRF is None:
2150
        quasiExactRF = RealField(1536)
2151
    function                      = function.function(function.variables()[0])
2152
    exactArgument                 = quasiExactRF(argument)
2153
    quasiExactValue               = function(exactArgument)
2154
    roundedValue                  = targetRF(quasiExactValue)
2155
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
2156
    # Upper midpoint.
2157
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
2158
    # Lower midpoint.
2159
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
2160
    binade                        = slz_compute_binade(roundedValue)
2161
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
2162
    #print "Argument:", argument
2163
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
2164
    #print "Binade:", binade
2165
    #print "binadeCorrectedTargetAccuracy:", \
2166
    #binadeCorrectedTargetAccuracy.n(prec=100)
2167
    #print "binadeCorrectedTargetAccuracy:", \
2168
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
2169
    #print "Upper midpoint:", \
2170
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2171
    #print "Rounded value :", \
2172
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
2173
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
2174
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
2175
    #print "Lower midpoint:", \
2176
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2177
    ## Make quasiExactValue = 0 a special case to move it out of the way.
2178
    if quasiExactValue == 0:
2179
        return False
2180
    ## Begining of the general case : check with the midpoint of 
2181
    #  greatest absolute value.
2182
    if quasiExactValue > 0:
2183
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
2184
           binadeCorrectedTargetAccuracy:
2185
            #print "Too close to the upper midpoint: ", \
2186
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2187
            #print argument.n().str(base=16, \
2188
            #  no_sci=False)
2189
            #print "Lower midpoint:", \
2190
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2191
            #print "Value         :", \
2192
            # quasiExactValue.n(prec=200).str(base=2)
2193
            #print "Upper midpoint:", \
2194
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2195
            return True
2196
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
2197
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2198
           binadeCorrectedTargetAccuracy:
2199
            #print "Too close to the upper midpoint: ", \
2200
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2201
            #print argument.n().str(base=16, \
2202
            #  no_sci=False)
2203
            #print "Lower midpoint:", \
2204
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2205
            #print "Value         :", \
2206
            #  quasiExactValue.n(prec=200).str(base=2)
2207
            #print "Upper midpoint:", \
2208
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2209
            #print
2210
            return True
2211
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2212
    ## For the midpoint of smaller absolute value, 
2213
    #  split cases with the powers of 2.
2214
    if sno_abs_is_power_of_two(roundedValue):
2215
        if quasiExactValue > 0:        
2216
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
2217
               binadeCorrectedTargetAccuracy / 2:
2218
                #print "Lower midpoint:", \
2219
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2220
                #print "Value         :", \
2221
                #  quasiExactValue.n(prec=200).str(base=2)
2222
                #print "Upper midpoint:", \
2223
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2224
                #print
2225
                return True
2226
        else:
2227
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2228
              binadeCorrectedTargetAccuracy / 2:
2229
                #print "Lower midpoint:", \
2230
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2231
                #print "Value         :", 
2232
                #  quasiExactValue.n(prec=200).str(base=2)
2233
                #print "Upper midpoint:", 
2234
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2235
                #print
2236
                return True
2237
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2238
    else: ## Not a power of 2, regular comparison with the midpoint of 
2239
          #  smaller absolute value.
2240
        if quasiExactValue > 0:        
2241
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2242
              binadeCorrectedTargetAccuracy:
2243
                #print "Lower midpoint:", \
2244
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2245
                #print "Value         :", \
2246
                #  quasiExactValue.n(prec=200).str(base=2)
2247
                #print "Upper midpoint:", \
2248
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2249
                #print
2250
                return True
2251
        else: # quasiExactValue <= 0
2252
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2253
              binadeCorrectedTargetAccuracy:
2254
                #print "Lower midpoint:", \
2255
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2256
                #print "Value         :", \
2257
                #  quasiExactValue.n(prec=200).str(base=2)
2258
                #print "Upper midpoint:", \
2259
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2260
                #print
2261
                return True
2262
    #print "distance to the upper midpoint:", \
2263
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
2264
    #print "distance to the lower midpoint:", \
2265
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
2266
    return False
2267
# End slz_is_htrn
2268
#
2269
def slz_pm1():
2270
    """
2271
    Compute a uniform RV in {-1, 1} (not zero).
2272
    """
2273
    ## function getrandbits(N) generates a long int with N random bits.
2274
    return getrandbits(1) * 2-1
2275
# End slz_pm1.
2276
#
2277
def slz_random_proj_pm1(r, c):
2278
    """
2279
    r x c matrix with \pm 1 ({-1, 1}) coefficients
2280
    """
2281
    M = matrix(r, c)
2282
    for i in range(0, r):
2283
        for j in range (0, c):
2284
            M[i,j] = slz_pm1()
2285
    return M
2286
# End random_proj_pm1.
2287
#
2288
def slz_random_proj_uniform(n, r, c):
2289
    """
2290
    r x c integer matrix with uniform n-bit integer coefficients
2291
    """
2292
    M = matrix(r, c)
2293
    for i in range(0, r):
2294
        for j in range (0, c):
2295
            M[i,j] = slz_uniform(n)
2296
    return M       
2297
# End slz_random_proj_uniform.
2298
#
2299
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
2300
                                                precision,
2301
                                                targetHardnessToRound,
2302
                                                variable1,
2303
                                                variable2):
2304
    """
2305
    Creates a new multivariate polynomial with integer coefficients for use
2306
     with the Coppersmith method.
2307
    A the same time it computes :
2308
    - 2^K (N);
2309
    - 2^k (bound on the second variable)
2310
    - lcm
2311

    
2312
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
2313
                         variables.
2314
    :param precision: the precision of the floating-point coefficients.
2315
    :param targetHardnessToRound: the hardness to round we want to check.
2316
    :param variable1: the first variable of the polynomial (an expression).
2317
    :param variable2: the second variable of the polynomial (an expression).
2318
    
2319
    :returns: a 4 elements tuple:
2320
                - the polynomial;
2321
                - the modulus (N);
2322
                - the t bound;
2323
                - the lcm used to compute the integral coefficients and the 
2324
                  module.
2325
    """
2326
    # Create a new integer polynomial ring.
2327
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
2328
    # Coefficients are issued in the increasing power order.
2329
    ratPolyCoefficients = ratPolyOfInt.coefficients()
2330
    # Print the reversed list for debugging.
2331
    #print
2332
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
2333
    # Build the list of number we compute the lcm of.
2334
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
2335
    #print "Coefficient denominators:", coefficientDenominators
2336
    coefficientDenominators.append(2^precision)
2337
    coefficientDenominators.append(2^(targetHardnessToRound))
2338
    leastCommonMultiple = lcm(coefficientDenominators)
2339
    # Compute the expression corresponding to the new polynomial
2340
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
2341
    #print coefficientNumerators
2342
    polynomialExpression = 0
2343
    power = 0
2344
    # Iterate over two lists at the same time, stop when the shorter
2345
    # (is this case coefficientsNumerators) is 
2346
    # exhausted. Both lists are ordered in the order of increasing powers
2347
    # of variable1.
2348
    for numerator, denominator in \
2349
                        zip(coefficientNumerators, coefficientDenominators):
2350
        multiplicator = leastCommonMultiple / denominator 
2351
        newCoefficient = numerator * multiplicator
2352
        polynomialExpression += newCoefficient * variable1^power
2353
        power +=1
2354
    polynomialExpression += - variable2
2355
    return (IP(polynomialExpression),
2356
            leastCommonMultiple / 2^precision, # 2^K aka N.
2357
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
2358
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
2359
            leastCommonMultiple) # If we want to make test computations.
2360
        
2361
# End slz_rat_poly_of_int_to_poly_for_coppersmith
2362

    
2363
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
2364
                                          precision):
2365
    """
2366
    Makes a variable substitution into the input polynomial so that the output
2367
    polynomial can take integer arguments.
2368
    All variables of the input polynomial "have precision p". That is to say
2369
    that they are rationals with denominator == 2^(precision - 1): 
2370
    x = y/2^(precision - 1).
2371
    We "incorporate" these denominators into the coefficients with, 
2372
    respectively, the "right" power.
2373
    """
2374
    polynomialField = ratPolyOfRat.parent()
2375
    polynomialVariable = ratPolyOfRat.variables()[0]
2376
    #print "The polynomial field is:", polynomialField
2377
    return \
2378
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
2379
                                   polynomialVariable/2^(precision-1)}))
2380
    
2381
# End slz_rat_poly_of_rat_to_rat_poly_of_int
2382

    
2383
def slz_reduce_and_test_base(matrixToReduce,
2384
                             nAtAlpha,
2385
                             monomialsCountSqrt):
2386
    """
2387
    Reduces the basis, tests the Coppersmith condition and returns
2388
    a list of rows that comply with the condition.
2389
    """
2390
    ccComplientRowsList = []
2391
    reducedMatrix = matrixToReduce.LLL(None)
2392
    if not reducedMatrix.is_LLL_reduced():
2393
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
2394
    else:
2395
        print "reducedMatrix is actually reduced."
2396
    print "N^alpha:", nAtAlpha.n()
2397
    rowIndex = 0
2398
    for row in reducedMatrix.rows():
2399
        l2Norm = row.norm(2)
2400
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
2401
                monomialsCountSqrt.n(), ". Is vector OK?", 
2402
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
2403
            ccComplientRowsList.append(row)
2404
            print "True"
2405
        else:
2406
            print "False"
2407
    # End for
2408
    return ccComplientRowsList
2409
# End slz_reduce_and_test_base
2410

    
2411
def slz_reduce_lll_proj(matToReduce, n):
2412
    """
2413
    Compute the transformation matrix that realizes an LLL reduction on
2414
    the random uniform projected matrix.
2415
    Return both the initial matrix "reduced" by the transformation matrix and
2416
    the transformation matrix itself.
2417
    """
2418
    ## Compute the projected matrix.
2419
    """
2420
    # Random matrix elements {-2^(n-1),...,0,...,2^(n-1)-1}.
2421
    matProjector = slz_random_proj_uniform(n,
2422
                                           matToReduce.ncols(),
2423
                                           matToReduce.nrows())
2424
    """
2425
    # Random matrix elements in {-1,0,1}. 
2426
    matProjector = slz_random_proj_pm1(matToReduce.ncols(),
2427
                                       matToReduce.nrows())
2428
    matProjected = matToReduce * matProjector
2429
    ## Build the argument matrix for LLL in such a way that the transformation
2430
    #  matrix is also returned. This matrix is obtained at almost no extra
2431
    # cost. An identity matrix must be appended to 
2432
    #  the left of the initial matrix. The transformation matrix will
2433
    # will be recovered at the same location from the returned matrix .
2434
    idMat = identity_matrix(matProjected.nrows())
2435
    augmentedMatToReduce = idMat.augment(matProjected)
2436
    reducedProjMat = \
2437
        augmentedMatToReduce.LLL(algorithm='fpLLL:wrapper')
2438
    ## Recover the transformation matrix (the left part of the reduced matrix). 
2439
    #  We discard the reduced matrix itself.
2440
    transMat = reducedProjMat.submatrix(0,
2441
                                        0,
2442
                                        reducedProjMat.nrows(),
2443
                                        reducedProjMat.nrows())
2444
    ## Return the initial matrix "reduced" and the transformation matrix tuple.
2445
    return (transMat * matToReduce, transMat)
2446
# End  slz_reduce_lll_proj.
2447
#
2448
def slz_resultant(poly1, poly2, elimVar, debug = False):
2449
    """
2450
    Compute the resultant for two polynomials for a given variable
2451
    and return the (poly1, poly2, resultant) if the resultant
2452
    is not 0.
2453
    Return () otherwise.
2454
    """
2455
    polynomialRing0    = poly1.parent()
2456
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2457
    if resultantInElimVar is None:
2458
        if debug:
2459
            print poly1
2460
            print poly2
2461
            print "have GCD = ", poly1.gcd(poly2)
2462
        return None
2463
    if resultantInElimVar.is_zero():
2464
        if debug:
2465
            print poly1
2466
            print poly2
2467
            print "have GCD = ", poly1.gcd(poly2)
2468
        return None
2469
    else:
2470
        if debug:
2471
            print poly1
2472
            print poly2
2473
            print "have resultant in t = ", resultantInElimVar
2474
        return resultantInElimVar
2475
# End slz_resultant.
2476
#
2477
def slz_resultant_tuple(poly1, poly2, elimVar):
2478
    """
2479
    Compute the resultant for two polynomials for a given variable
2480
    and return the (poly1, poly2, resultant) if the resultant
2481
    is not 0.
2482
    Return () otherwise.
2483
    """
2484
    polynomialRing0    = poly1.parent()
2485
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2486
    if resultantInElimVar.is_zero():
2487
        return ()
2488
    else:
2489
        return (poly1, poly2, resultantInElimVar)
2490
# End slz_resultant_tuple.
2491
#
2492
def slz_uniform(n):
2493
    """
2494
    Compute a uniform RV in [-2^(n-1), 2^(n-1)-1] (zero is included).
2495
    """
2496
    ## function getrandbits(n) generates a long int with n random bits.
2497
    return getrandbits(n) - 2^(n-1)
2498
# End slz_uniform.
2499
#
2500
sys.stderr.write("\t...sageSLZ loaded\n")
2501