Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (105,76 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
#
525
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
526
                                                                    alpha,
527
                                                                    N,
528
                                                                    iBound,
529
                                                                    tBound,
530
                                                                    debug = False):
531
    """
532
    For a given set of arguments (see below), compute a list
533
    of "reduced polynomials" that could be used to compute roots
534
    of the inputPolynomial.
535
    Print the volume of the initial basis as well.
536
    INPUT:
537
    
538
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
539
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
540
    - "N" -- the modulus;
541
    - "iBound" -- the bound on the first variable;
542
    - "tBound" -- the bound on the second variable.
543
    
544
    OUTPUT:
545
    
546
    A list of bivariate integer polynomial obtained using the Coppersmith
547
    algorithm. The polynomials correspond to the rows of the LLL-reduce
548
    reduced base that comply with the Coppersmith condition.
549
    """
550
    # Arguments check.
551
    if iBound == 0 or tBound == 0:
552
        return None
553
    # End arguments check.
554
    nAtAlpha = N^alpha
555
    if debug:
556
        print "N at alpha:", nAtAlpha
557
    ## Building polynomials for matrix.
558
    polyRing   = inputPolynomial.parent()
559
    # Whatever the 2 variables are actually called, we call them
560
    # 'i' and 't' in all the variable names.
561
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
562
    #print polyVars[0], type(polyVars[0])
563
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
564
                                              tVariable:tVariable * tBound})
565
##    polynomialsList = \
566
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
567
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
568
    polynomialsList = \
569
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
570
                                             alpha,
571
                                             N,
572
                                             iBound,
573
                                             tBound,
574
                                             0)
575
    #print "Polynomials list:", polynomialsList
576
    ## Building the proto matrix.
577
    knownMonomials = []
578
    protoMatrix    = []
579
    for poly in polynomialsList:
580
        spo_add_polynomial_coeffs_to_matrix_row(poly,
581
                                                knownMonomials,
582
                                                protoMatrix,
583
                                                0)
584
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
585
    matrixToReduceTranspose = matrixToReduce.transpose()
586
    squareMatrix = matrixToReduce * matrixToReduceTranspose
587
    squareMatDet = det(squareMatrix)
588
    latticeVolume = sqrt(squareMatDet)
589
    print "Lattice volume:", latticeVolume.n()
590
    print "Lattice volume / N:", (latticeVolume/N).n()
591
    #print matrixToReduce
592
    ## Reduction and checking.
593
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
594
    #  error message issued when previous code was used.
595
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
596
    reductionTimeStart = cputime() 
597
    reducedMatrix = matrixToReduce.LLL(fp=None)
598
    reductionTime = cputime(reductionTimeStart)
599
    print "Reduction time:", reductionTime
600
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
601
    if not isLLLReduced:
602
       return None
603
    #
604
    if debug:
605
        matrixFile = file('/tmp/reducedMatrix.txt', 'w')
606
        for row in reducedMatrix.rows():
607
            matrixFile.write(str(row) + "\n")
608
        matrixFile.close()
609
    #
610
    monomialsCount     = len(knownMonomials)
611
    monomialsCountSqrt = sqrt(monomialsCount)
612
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
613
    #print reducedMatrix
614
    ## Check the Coppersmith condition for each row and build the reduced 
615
    #  polynomials.
616
    ccVectorsCount = 0
617
    ccReducedPolynomialsList = []
618
    for row in reducedMatrix.rows():
619
        l2Norm = row.norm(2)
620
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
621
            #print (l2Norm * monomialsCountSqrt).n()
622
            #print l2Norm.n()
623
            ccVectorsCount +=1            
624
            ccReducedPolynomial = \
625
                slz_compute_reduced_polynomial(row,
626
                                               knownMonomials,
627
                                               iVariable,
628
                                               iBound,
629
                                               tVariable,
630
                                               tBound)
631
            if not ccReducedPolynomial is None:
632
                ccReducedPolynomialsList.append(ccReducedPolynomial)
633
        else:
634
            #print l2Norm.n() , ">", nAtAlpha
635
            pass
636
    if debug:
637
        print ccVectorsCount, "out of ", len(ccReducedPolynomialsList), 
638
        print "took Coppersmith text."
639
    if len(ccReducedPolynomialsList) < 2:
640
        print "Less than 2 Coppersmith condition compliant vectors."
641
        return ()
642
    if debug:
643
        print "Reduced and Coppersmith compliant polynomials list", ccReducedPolynomialsList
644
    return ccReducedPolynomialsList
645
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
646

    
647
def slz_compute_initial_lattice_matrix(inputPolynomial,
648
                                       alpha,
649
                                       N,
650
                                       iBound,
651
                                       tBound,
652
                                       debug = False):
653
    """
654
    For a given set of arguments (see below), compute the initial lattice
655
    that could be reduced. 
656
    INPUT:
657
    
658
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
659
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
660
    - "N" -- the modulus;
661
    - "iBound" -- the bound on the first variable;
662
    - "tBound" -- the bound on the second variable.
663
    
664
    OUTPUT:
665
    
666
    A list of bivariate integer polynomial obtained using the Coppersmith
667
    algorithm. The polynomials correspond to the rows of the LLL-reduce
668
    reduced base that comply with the Coppersmith condition.
669
    """
670
    # Arguments check.
671
    if iBound == 0 or tBound == 0:
672
        return None
673
    # End arguments check.
674
    nAtAlpha = N^alpha
675
    ## Building polynomials for matrix.
676
    polyRing   = inputPolynomial.parent()
677
    # Whatever the 2 variables are actually called, we call them
678
    # 'i' and 't' in all the variable names.
679
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
680
    #print polyVars[0], type(polyVars[0])
681
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
682
                                              tVariable:tVariable * tBound})
683
    polynomialsList = \
684
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
685
                                             alpha,
686
                                             N,
687
                                             iBound,
688
                                             tBound,
689
                                             0)
690
    #print "Polynomials list:", polynomialsList
691
    ## Building the proto matrix.
692
    knownMonomials = []
693
    protoMatrix    = []
694
    for poly in polynomialsList:
695
        spo_add_polynomial_coeffs_to_matrix_row(poly,
696
                                                knownMonomials,
697
                                                protoMatrix,
698
                                                0)
699
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
700
    if debug:
701
        print "Initial basis polynomials"
702
        for poly in polynomialsList:
703
            print poly
704
    return matrixToReduce
705
# End slz_compute_initial_lattice_matrix.
706

    
707
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
708
                                                 alpha,
709
                                                 N,
710
                                                 iBound,
711
                                                 tBound):
712
    """
713
    For a given set of arguments (see below), compute the polynomial modular 
714
    roots, if any.
715
    
716
    """
717
    # Arguments check.
718
    if iBound == 0 or tBound == 0:
719
        return set()
720
    # End arguments check.
721
    nAtAlpha = N^alpha
722
    ## Building polynomials for matrix.
723
    polyRing   = inputPolynomial.parent()
724
    # Whatever the 2 variables are actually called, we call them
725
    # 'i' and 't' in all the variable names.
726
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
727
    ccReducedPolynomialsList = \
728
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
729
                                                     alpha,
730
                                                     N,
731
                                                     iBound,
732
                                                     tBound)
733
    if len(ccReducedPolynomialsList) == 0:
734
        return set()   
735
    ## Create the valid (poly1 and poly2 are algebraically independent) 
736
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
737
    # Try to mix and match all the polynomial pairs built from the 
738
    # ccReducedPolynomialsList to obtain non zero resultants.
739
    resultantsInITuplesList = []
740
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
741
        for polyInnerIndex in xrange(polyOuterIndex+1, 
742
                                     len(ccReducedPolynomialsList)):
743
            # Compute the resultant in resultants in the
744
            # first variable (is it the optimal choice?).
745
            resultantInI = \
746
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
747
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
748
            #print "Resultant", resultantInI
749
            # Test algebraic independence.
750
            if not resultantInI.is_zero():
751
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
752
                                                 ccReducedPolynomialsList[polyInnerIndex],
753
                                                 resultantInI))
754
    # If no non zero resultant was found: we can't get no algebraically 
755
    # independent polynomials pair. Give up!
756
    if len(resultantsInITuplesList) == 0:
757
        return set()
758
    #print resultantsInITuplesList
759
    # Compute the roots.
760
    Zi = ZZ[str(iVariable)]
761
    Zt = ZZ[str(tVariable)]
762
    polynomialRootsSet = set()
763
    # First, solve in the second variable since resultants are in the first
764
    # variable.
765
    for resultantInITuple in resultantsInITuplesList:
766
        tRootsList = Zt(resultantInITuple[2]).roots()
767
        # For each tRoot, compute the corresponding iRoots and check 
768
        # them in the input polynomial.
769
        for tRoot in tRootsList:
770
            #print "tRoot:", tRoot
771
            # Roots returned by root() are (value, multiplicity) tuples.
772
            iRootsList = \
773
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
774
            print iRootsList
775
            # The iRootsList can be empty, hence the test.
776
            if len(iRootsList) != 0:
777
                for iRoot in iRootsList:
778
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
779
                    # polyEvalModN must be an integer.
780
                    if polyEvalModN == int(polyEvalModN):
781
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
782
    return polynomialRootsSet
783
# End slz_compute_integer_polynomial_modular_roots.
784
#
785
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
786
                                                 alpha,
787
                                                 N,
788
                                                 iBound,
789
                                                 tBound):
790
    """
791
    For a given set of arguments (see below), compute the polynomial modular 
792
    roots, if any.
793
    This version differs in the way resultants are computed.   
794
    """
795
    # Arguments check.
796
    if iBound == 0 or tBound == 0:
797
        return set()
798
    # End arguments check.
799
    nAtAlpha = N^alpha
800
    ## Building polynomials for matrix.
801
    polyRing   = inputPolynomial.parent()
802
    # Whatever the 2 variables are actually called, we call them
803
    # 'i' and 't' in all the variable names.
804
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
805
    #print polyVars[0], type(polyVars[0])
806
    ccReducedPolynomialsList = \
807
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
808
                                                     alpha,
809
                                                     N,
810
                                                     iBound,
811
                                                     tBound)
812
    if len(ccReducedPolynomialsList) == 0:
813
        return set()   
814
    ## Create the valid (poly1 and poly2 are algebraically independent) 
815
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
816
    # Try to mix and match all the polynomial pairs built from the 
817
    # ccReducedPolynomialsList to obtain non zero resultants.
818
    resultantsInTTuplesList = []
819
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
820
        for polyInnerIndex in xrange(polyOuterIndex+1, 
821
                                     len(ccReducedPolynomialsList)):
822
            # Compute the resultant in resultants in the
823
            # first variable (is it the optimal choice?).
824
            resultantInT = \
825
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
826
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
827
            #print "Resultant", resultantInT
828
            # Test algebraic independence.
829
            if not resultantInT.is_zero():
830
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
831
                                                 ccReducedPolynomialsList[polyInnerIndex],
832
                                                 resultantInT))
833
    # If no non zero resultant was found: we can't get no algebraically 
834
    # independent polynomials pair. Give up!
835
    if len(resultantsInTTuplesList) == 0:
836
        return set()
837
    #print resultantsInITuplesList
838
    # Compute the roots.
839
    Zi = ZZ[str(iVariable)]
840
    Zt = ZZ[str(tVariable)]
841
    polynomialRootsSet = set()
842
    # First, solve in the second variable since resultants are in the first
843
    # variable.
844
    for resultantInTTuple in resultantsInTTuplesList:
845
        iRootsList = Zi(resultantInTTuple[2]).roots()
846
        # For each iRoot, compute the corresponding tRoots and check 
847
        # them in the input polynomial.
848
        for iRoot in iRootsList:
849
            #print "iRoot:", iRoot
850
            # Roots returned by root() are (value, multiplicity) tuples.
851
            tRootsList = \
852
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
853
            print tRootsList
854
            # The tRootsList can be empty, hence the test.
855
            if len(tRootsList) != 0:
856
                for tRoot in tRootsList:
857
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
858
                    # polyEvalModN must be an integer.
859
                    if polyEvalModN == int(polyEvalModN):
860
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
861
    return polynomialRootsSet
862
# End slz_compute_integer_polynomial_modular_roots_2.
863
#
864
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
865
                                        upperBoundSa, approxAccurSa, 
866
                                        precSa=None, debug=False):
867
    """
868
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
869
    a polynomial that approximates the function on a an interval starting
870
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
871
    approximates with the expected precision.
872
    The interval upper bound is lowered until the expected approximation 
873
    precision is reached.
874
    The polynomial, the bounds, the center of the interval and the error
875
    are returned.
876
    Argument debug is not used.
877
    OUTPUT:
878
    A tuple made of 4 Sollya objects:
879
    - a polynomial;
880
    - an range (an interval, not in the sense of number given as an interval);
881
    - the center of the interval;
882
    - the maximum error in the approximation of the input functionSo by the
883
      output polynomial ; this error <= approxAccurSaS.
884
    
885
    """
886
    #print"In slz_compute_polynomial_and_interval..."
887
    ## Superficial argument check.
888
    if lowerBoundSa > upperBoundSa:
889
        return None
890
    ## Change Sollya precision, if requested.
891
    if precSa is None:
892
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
893
        #print "Computed internal precision:", precSa
894
        if precSa < 192:
895
            precSa = 192
896
    sollyaPrecChanged = False
897
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
898
    if precSa > initialSollyaPrecSa:
899
        if precSa <= 2:
900
            print inspect.stack()[0][3], ": precision change <=2 requested."
901
        pobyso_set_prec_sa_so(precSa)
902
        sollyaPrecChanged = True
903
    RRR = lowerBoundSa.parent()
904
    intervalShrinkConstFactorSa = RRR('0.9')
905
    absoluteErrorTypeSo = pobyso_absolute_so_so()
906
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
907
    currentUpperBoundSa = upperBoundSa
908
    currentLowerBoundSa = lowerBoundSa
909
    # What we want here is the polynomial without the variable change, 
910
    # since our actual variable will be x-intervalCenter defined over the 
911
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
912
    (polySo, intervalCenterSo, maxErrorSo) = \
913
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
914
                                                    currentRangeSo, 
915
                                                    absoluteErrorTypeSo)
916
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
917
    while maxErrorSa > approxAccurSa:
918
        print "++Approximation error:", maxErrorSa.n()
919
        sollya_lib_clear_obj(polySo)
920
        sollya_lib_clear_obj(intervalCenterSo)
921
        sollya_lib_clear_obj(maxErrorSo)
922
        # Very empirical shrinking factor.
923
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
924
        print "Shrink factor:", \
925
              shrinkFactorSa.n(), \
926
              intervalShrinkConstFactorSa
927
        print
928
        #errorRatioSa = approxAccurSa/maxErrorSa
929
        #print "Error ratio: ", errorRatioSa
930
        # Make sure interval shrinks.
931
        if shrinkFactorSa > intervalShrinkConstFactorSa:
932
            actualShrinkFactorSa = intervalShrinkConstFactorSa
933
            #print "Fixed"
934
        else:
935
            actualShrinkFactorSa = shrinkFactorSa
936
            #print "Computed",shrinkFactorSa,maxErrorSa
937
            #print shrinkFactorSa, maxErrorSa
938
        #print "Shrink factor", actualShrinkFactorSa
939
        currentUpperBoundSa = currentLowerBoundSa + \
940
                                (currentUpperBoundSa - currentLowerBoundSa) * \
941
                                actualShrinkFactorSa
942
        #print "Current upper bound:", currentUpperBoundSa
943
        sollya_lib_clear_obj(currentRangeSo)
944
        # Check what is left with the bounds.
945
        if currentUpperBoundSa <= currentLowerBoundSa or \
946
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
947
            sollya_lib_clear_obj(absoluteErrorTypeSo)
948
            print "Can't find an interval."
949
            print "Use either or both a higher polynomial degree or a higher",
950
            print "internal precision."
951
            print "Aborting!"
952
            if sollyaPrecChanged:
953
                pobyso_set_prec_so_so(initialSollyaPrecSo)
954
            sollya_lib_clear_obj(initialSollyaPrecSo)
955
            return None
956
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
957
                                                      currentUpperBoundSa)
958
        # print "New interval:",
959
        # pobyso_autoprint(currentRangeSo)
960
        #print "Second Taylor expansion call."
961
        (polySo, intervalCenterSo, maxErrorSo) = \
962
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
963
                                                        currentRangeSo, 
964
                                                        absoluteErrorTypeSo)
965
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
966
        #print "Max errorSo:",
967
        #pobyso_autoprint(maxErrorSo)
968
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
969
        #print "Max errorSa:", maxErrorSa
970
        #print "Sollya prec:",
971
        #pobyso_autoprint(sollya_lib_get_prec(None))
972
    # End while
973
    sollya_lib_clear_obj(absoluteErrorTypeSo)
974
    if sollyaPrecChanged:
975
        pobyso_set_prec_so_so(initialSollyaPrecSo)
976
    sollya_lib_clear_obj(initialSollyaPrecSo)
977
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
978
# End slz_compute_polynomial_and_interval
979

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

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

    
1300
def slz_compute_reduced_polynomial(matrixRow,
1301
                                    knownMonomials,
1302
                                    var1,
1303
                                    var1Bound,
1304
                                    var2,
1305
                                    var2Bound):
1306
    """
1307
    Compute a polynomial from a single reduced matrix row.
1308
    This function was introduced in order to avoid the computation of the
1309
    all the polynomials  from the full matrix (even those built from rows 
1310
    that do no verify the Coppersmith condition) as this may involves 
1311
    expensive operations over (large) integers.
1312
    """
1313
    ## Check arguments.
1314
    if len(knownMonomials) == 0:
1315
        return None
1316
    # varNounds can be zero since 0^0 returns 1.
1317
    if (var1Bound < 0) or (var2Bound < 0):
1318
        return None
1319
    ## Initialisations. 
1320
    polynomialRing = knownMonomials[0].parent() 
1321
    currentPolynomial = polynomialRing(0)
1322
    # TODO: use zip instead of indices.
1323
    for colIndex in xrange(0, len(knownMonomials)):
1324
        currentCoefficient = matrixRow[colIndex]
1325
        if currentCoefficient != 0:
1326
            #print "Current coefficient:", currentCoefficient
1327
            currentMonomial = knownMonomials[colIndex]
1328
            #print "Monomial as multivariate polynomial:", \
1329
            #currentMonomial, type(currentMonomial)
1330
            degreeInVar1 = currentMonomial.degree(var1)
1331
            #print "Degree in var1", var1, ":", degreeInVar1
1332
            degreeInVar2 = currentMonomial.degree(var2)
1333
            #print "Degree in var2", var2, ":", degreeInVar2
1334
            if degreeInVar1 > 0:
1335
                currentCoefficient = \
1336
                    currentCoefficient / (var1Bound^degreeInVar1)
1337
                #print "varBound1 in degree:", var1Bound^degreeInVar1
1338
                #print "Current coefficient(1)", currentCoefficient
1339
            if degreeInVar2 > 0:
1340
                currentCoefficient = \
1341
                    currentCoefficient / (var2Bound^degreeInVar2)
1342
                #print "Current coefficient(2)", currentCoefficient
1343
            #print "Current reduced monomial:", (currentCoefficient * \
1344
            #                                    currentMonomial)
1345
            currentPolynomial += (currentCoefficient * currentMonomial)
1346
            #print "Current polynomial:", currentPolynomial
1347
        # End if
1348
    # End for colIndex.
1349
    #print "Type of the current polynomial:", type(currentPolynomial)
1350
    return(currentPolynomial)
1351
# End slz_compute_reduced_polynomial
1352
#
1353
def slz_compute_reduced_polynomials(reducedMatrix,
1354
                                        knownMonomials,
1355
                                        var1,
1356
                                        var1Bound,
1357
                                        var2,
1358
                                        var2Bound):
1359
    """
1360
    Legacy function, use slz_compute_reduced_polynomials_list
1361
    """
1362
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1363
                                                knownMonomials,
1364
                                                var1,
1365
                                                var1Bound,
1366
                                                var2,
1367
                                                var2Bound)
1368
    )
1369
#
1370
def slz_compute_reduced_polynomials_list(reducedMatrix,
1371
                                         knownMonomials,
1372
                                         var1,
1373
                                         var1Bound,
1374
                                         var2,
1375
                                         var2Bound):
1376
    """
1377
    From a reduced matrix, holding the coefficients, from a monomials list,
1378
    from the bounds of each variable, compute the corresponding polynomials
1379
    scaled back by dividing by the "right" powers of the variables bounds.
1380
    
1381
    The elements in knownMonomials must be of the "right" polynomial type.
1382
    They set the polynomial type of the output polynomials in list.
1383
    @param reducedMatrix:  the reduced matrix as output from LLL;
1384
    @param kwnonMonomials: the ordered list of the monomials used to
1385
                           build the polynomials;
1386
    @param var1:           the first variable (of the "right" type);
1387
    @param var1Bound:      the first variable bound;
1388
    @param var2:           the second variable (of the "right" type);
1389
    @param var2Bound:      the second variable bound.
1390
    @return: a list of polynomials obtained with the reduced coefficients
1391
             and scaled down with the bounds
1392
    """
1393
    
1394
    # TODO: check input arguments.
1395
    reducedPolynomials = []
1396
    #print "type var1:", type(var1), " - type var2:", type(var2)
1397
    for matrixRow in reducedMatrix.rows():
1398
        currentPolynomial = 0
1399
        for colIndex in xrange(0, len(knownMonomials)):
1400
            currentCoefficient = matrixRow[colIndex]
1401
            if currentCoefficient != 0:
1402
                #print "Current coefficient:", currentCoefficient
1403
                currentMonomial = knownMonomials[colIndex]
1404
                parentRing = currentMonomial.parent()
1405
                #print "Monomial as multivariate polynomial:", \
1406
                #currentMonomial, type(currentMonomial)
1407
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1408
                #print "Degree in var", var1, ":", degreeInVar1
1409
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1410
                #print "Degree in var", var2, ":", degreeInVar2
1411
                if degreeInVar1 > 0:
1412
                    currentCoefficient /= var1Bound^degreeInVar1
1413
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1414
                    #print "Current coefficient(1)", currentCoefficient
1415
                if degreeInVar2 > 0:
1416
                    currentCoefficient /= var2Bound^degreeInVar2
1417
                    #print "Current coefficient(2)", currentCoefficient
1418
                #print "Current reduced monomial:", (currentCoefficient * \
1419
                #                                    currentMonomial)
1420
                currentPolynomial += (currentCoefficient * currentMonomial)
1421
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1422
                    #print "!!!! constant term !!!!"
1423
                #print "Current polynomial:", currentPolynomial
1424
            # End if
1425
        # End for colIndex.
1426
        #print "Type of the current polynomial:", type(currentPolynomial)
1427
        reducedPolynomials.append(currentPolynomial)
1428
    return reducedPolynomials 
1429
# End slz_compute_reduced_polynomials_list.
1430

    
1431
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1432
                                                   knownMonomials,
1433
                                                   var1,
1434
                                                   var1Bound,
1435
                                                   var2,
1436
                                                   var2Bound):
1437
    """
1438
    From a list of rows, holding the coefficients, from a monomials list,
1439
    from the bounds of each variable, compute the corresponding polynomials
1440
    scaled back by dividing by the "right" powers of the variables bounds.
1441
    
1442
    The elements in knownMonomials must be of the "right" polynomial type.
1443
    They set the polynomial type of the output polynomials in list.
1444
    @param rowsList:       the reduced matrix as output from LLL;
1445
    @param kwnonMonomials: the ordered list of the monomials used to
1446
                           build the polynomials;
1447
    @param var1:           the first variable (of the "right" type);
1448
    @param var1Bound:      the first variable bound;
1449
    @param var2:           the second variable (of the "right" type);
1450
    @param var2Bound:      the second variable bound.
1451
    @return: a list of polynomials obtained with the reduced coefficients
1452
             and scaled down with the bounds
1453
    """
1454
    
1455
    # TODO: check input arguments.
1456
    reducedPolynomials = []
1457
    #print "type var1:", type(var1), " - type var2:", type(var2)
1458
    for matrixRow in rowsList:
1459
        currentPolynomial = 0
1460
        for colIndex in xrange(0, len(knownMonomials)):
1461
            currentCoefficient = matrixRow[colIndex]
1462
            if currentCoefficient != 0:
1463
                #print "Current coefficient:", currentCoefficient
1464
                currentMonomial = knownMonomials[colIndex]
1465
                parentRing = currentMonomial.parent()
1466
                #print "Monomial as multivariate polynomial:", \
1467
                #currentMonomial, type(currentMonomial)
1468
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1469
                #print "Degree in var", var1, ":", degreeInVar1
1470
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1471
                #print "Degree in var", var2, ":", degreeInVar2
1472
                if degreeInVar1 > 0:
1473
                    currentCoefficient /= var1Bound^degreeInVar1
1474
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1475
                    #print "Current coefficient(1)", currentCoefficient
1476
                if degreeInVar2 > 0:
1477
                    currentCoefficient /= var2Bound^degreeInVar2
1478
                    #print "Current coefficient(2)", currentCoefficient
1479
                #print "Current reduced monomial:", (currentCoefficient * \
1480
                #                                    currentMonomial)
1481
                currentPolynomial += (currentCoefficient * currentMonomial)
1482
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1483
                    #print "!!!! constant term !!!!"
1484
                #print "Current polynomial:", currentPolynomial
1485
            # End if
1486
        # End for colIndex.
1487
        #print "Type of the current polynomial:", type(currentPolynomial)
1488
        reducedPolynomials.append(currentPolynomial)
1489
    return reducedPolynomials 
1490
# End slz_compute_reduced_polynomials_list_from_rows.
1491
#
1492
def slz_compute_scaled_function(functionSa,
1493
                                lowerBoundSa,
1494
                                upperBoundSa,
1495
                                floatingPointPrecSa,
1496
                                debug=False):
1497
    """
1498
    From a function, compute the scaled function whose domain
1499
    is included in [1, 2) and whose image is also included in [1,2).
1500
    Return a tuple: 
1501
    [0]: the scaled function
1502
    [1]: the scaled domain lower bound
1503
    [2]: the scaled domain upper bound
1504
    [3]: the scaled image lower bound
1505
    [4]: the scaled image upper bound
1506
    """
1507
    ## The variable can be called anything.
1508
    x = functionSa.variables()[0]
1509
    # Scalling the domain -> [1,2[.
1510
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1511
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1512
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1513
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1514
    if debug:
1515
        print "domainScalingExpression for argument :", \
1516
        invDomainScalingExpressionSa
1517
        print "function: ", functionSa
1518
    ff = functionSa.subs({x : domainScalingExpressionSa})
1519
    ## Bless expression as a function.
1520
    ff = ff.function(x)
1521
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1522
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1523
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1524
    scaledLowerBoundSa = \
1525
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1526
    scaledUpperBoundSa = \
1527
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1528
    if debug:
1529
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1530
              scaledUpperBoundSa
1531
    ## If both bounds are negative, swap them.
1532
    if lowerBoundSa < 0 and upperBoundSa < 0:
1533
        scaledLowerBoundSa, scaledUpperBoundSa = \
1534
        scaledUpperBoundSa,scaledLowerBoundSa
1535
    #
1536
    # Scalling the image -> [1,2[.
1537
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
1538
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
1539
    if flbSa <= fubSa: # Increasing
1540
        imageBinadeBottomSa = floor(flbSa.log2())
1541
    else: # Decreasing
1542
        imageBinadeBottomSa = floor(fubSa.log2())
1543
    if debug:
1544
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
1545
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
1546
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
1547
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
1548
    if debug:
1549
        print "imageScalingExpression for argument :", \
1550
              invImageScalingExpressionSa
1551
    iis = invImageScalingExpressionSa.function(x)
1552
    fff = iis.subs({x:ff})
1553
    if debug:
1554
        print "fff:", fff,
1555
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1556
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1557
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1558
# End slz_compute_scaled_function
1559

    
1560
def slz_fix_bounds_for_binades(lowerBound, 
1561
                               upperBound, 
1562
                               func = None, 
1563
                               domainDirection = -1,
1564
                               imageDirection  = -1):
1565
    """
1566
    Assuming the function is increasing or decreasing over the 
1567
    [lowerBound, upperBound] interval, return a lower bound lb and 
1568
    an upper bound ub such that:
1569
    - lb and ub belong to the same binade;
1570
    - func(lb) and func(ub) belong to the same binade.
1571
    domainDirection indicate how bounds move to fit into the same binade:
1572
    - a negative value move the upper bound down;
1573
    - a positive value move the lower bound up.
1574
    imageDirection indicate how bounds move in order to have their image
1575
    fit into the same binade, variation of the function is also condidered.
1576
    For an increasing function:
1577
    - negative value moves the upper bound down (and its image value as well);
1578
    - a positive values moves the lower bound up (and its image value as well);
1579
    For a decreasing function it is the other way round.
1580
    """
1581
    ## Arguments check
1582
    if lowerBound > upperBound:
1583
        return None
1584
    if lowerBound == upperBound:
1585
        return (lowerBound, upperBound)
1586
    if func is None:
1587
        return None
1588
    #
1589
    #varFunc = func.variables()[0]
1590
    lb = lowerBound
1591
    ub = upperBound
1592
    lbBinade = slz_compute_binade(lb) 
1593
    ubBinade = slz_compute_binade(ub)
1594
    ## Domain binade.
1595
    while lbBinade != ubBinade:
1596
        newIntervalWidth = (ub - lb) / 2
1597
        if domainDirection < 0:
1598
            ub = lb + newIntervalWidth
1599
            ubBinade = slz_compute_binade(ub)
1600
        else:
1601
            lb = lb + newIntervalWidth
1602
            lbBinade = slz_compute_binade(lb) 
1603
    #print "sfbfb: lower bound:", lb.str(truncate=False)
1604
    #print "sfbfb: upper bound:", ub.str(truncate=False)
1605
    ## At this point, both bounds belond to the same binade.
1606
    ## Image binade.
1607
    if lb == ub:
1608
        return (lb, ub)
1609
    lbImg = func(lb)
1610
    ubImg = func(ub)
1611
    funcIsInc = ((ubImg - lbImg) >= 0)
1612
    lbImgBinade = slz_compute_binade(lbImg)
1613
    ubImgBinade = slz_compute_binade(ubImg)
1614
    while lbImgBinade != ubImgBinade:
1615
        newIntervalWidth = (ub - lb) / 2
1616
        if imageDirection < 0:
1617
            if funcIsInc:
1618
                ub = lb + newIntervalWidth
1619
                ubImgBinade = slz_compute_binade(func(ub))
1620
                #print ubImgBinade
1621
            else:
1622
                lb = lb + newIntervalWidth
1623
                lbImgBinade = slz_compute_binade(func(lb))
1624
                #print lbImgBinade
1625
        else:
1626
            if funcIsInc:
1627
                lb = lb + newIntervalWidth
1628
                lbImgBinade = slz_compute_binade(func(lb))
1629
                #print lbImgBinade
1630
            else:
1631
                ub = lb + newIntervalWidth
1632
                ubImgBinade = slz_compute_binade(func(ub))
1633
                #print ubImgBinade
1634
    # End while lbImgBinade != ubImgBinade:
1635
    return (lb, ub)
1636
# End slz_fix_bounds_for_binades.
1637

    
1638
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1639
    # Create a polynomial over the rationals.
1640
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1641
    return(ratPolynomialRing(polyOfFloat))
1642
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1643

    
1644
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1645
    """
1646
     Create a polynomial over the rationals where all denominators are
1647
     powers of two.
1648
    """
1649
    polyVariable = polyOfFloat.variables()[0] 
1650
    RPR = QQ[str(polyVariable)]
1651
    polyCoeffs      = polyOfFloat.coefficients()
1652
    #print polyCoeffs
1653
    polyExponents   = polyOfFloat.exponents()
1654
    #print polyExponents
1655
    polyDenomPtwoCoeffs = []
1656
    for coeff in polyCoeffs:
1657
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1658
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1659
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1660
    ratPoly = RPR(0)
1661
    #print type(ratPoly)
1662
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1663
    #  The coefficient becomes plainly wrong when exponent == 0.
1664
    #  No clue as to why.
1665
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1666
        ratPoly += coeff * RPR(polyVariable^exponent)
1667
    return ratPoly
1668
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1669

    
1670
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1671
                                      lowerBoundSa, 
1672
                                      upperBoundSa, 
1673
                                      floatingPointPrecSa, 
1674
                                      internalSollyaPrecSa, 
1675
                                      approxAccurSa):
1676
    """
1677
    Under the assumption that:
1678
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1679
    - lowerBound and upperBound belong to the same binade.
1680
    from a:
1681
    - function;
1682
    - a degree
1683
    - a pair of bounds;
1684
    - the floating-point precision we work on;
1685
    - the internal Sollya precision;
1686
    - the requested approximation error
1687
    The initial interval is, possibly, splitted into smaller intervals.
1688
    It return a list of tuples, each made of:
1689
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1690
    - a second polynomial (with a changed variable f(x) = q(x))
1691
    - the approximation interval;
1692
    - the center, x0, of the interval;
1693
    - the corresponding approximation error.
1694
    TODO: fix endless looping for some parameters sets.
1695
    """
1696
    resultArray = []
1697
    # Set Sollya to the necessary internal precision.
1698
    sollyaPrecChangedSa = False
1699
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1700
    if internalSollyaPrecSa > initialSollyaPrecSa:
1701
        if internalSollyaPrecSa <= 2:
1702
            print inspect.stack()[0][3], ": precision change <=2 requested."
1703
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1704
        sollyaPrecChangedSa = True
1705
    #
1706
    x = functionSa.variables()[0] # Actual variable name can be anything.
1707
    # Scaled function: [1=,2] -> [1,2].
1708
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1709
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1710
                slz_compute_scaled_function(functionSa,                       \
1711
                                            lowerBoundSa,                     \
1712
                                            upperBoundSa,                     \
1713
                                            floatingPointPrecSa)
1714
    # In case bounds were in the negative real one may need to
1715
    # switch scaled bounds.
1716
    if scaledLowerBoundSa > scaledUpperBoundSa:
1717
        scaledLowerBoundSa, scaledUpperBoundSa = \
1718
            scaledUpperBoundSa, scaledLowerBoundSa
1719
        #print "Switching!"
1720
    print "Approximation accuracy: ", RR(approxAccurSa)
1721
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1722
    functionSo = \
1723
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1724
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1725
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1726
                                                  scaledUpperBoundSa)
1727

    
1728
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1729
                                              upperBoundSa.parent().precision()))
1730
    currentScaledLowerBoundSa = scaledLowerBoundSa
1731
    currentScaledUpperBoundSa = scaledUpperBoundSa
1732
    while True:
1733
        ## Compute the first Taylor expansion.
1734
        print "Computing a Taylor expansion..."
1735
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1736
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1737
                                        currentScaledLowerBoundSa, 
1738
                                        currentScaledUpperBoundSa,
1739
                                        approxAccurSa, internalSollyaPrecSa)
1740
        print "...done."
1741
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1742
        #  This value goes to the first variable: polySo. Other variables are
1743
        #  not assigned and should not be tested.
1744
        if polySo is None:
1745
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1746
            if precChangedSa:
1747
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1748
            sollya_lib_clear_obj(initialSollyaPrecSo)
1749
            sollya_lib_clear_obj(functionSo)
1750
            sollya_lib_clear_obj(degreeSo)
1751
            sollya_lib_clear_obj(scaledBoundsSo)
1752
            return None
1753
        ## Add to the result array.
1754
        ### Change variable stuff in Sollya x -> x0-x.
1755
        changeVarExpressionSo = \
1756
            sollya_lib_build_function_sub( \
1757
                              sollya_lib_build_function_free_variable(), 
1758
                              sollya_lib_copy_obj(intervalCenterSo))
1759
        polyVarChangedSo = \
1760
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1761
        #### Get rid of the variable change Sollya stuff. 
1762
        sollya_lib_clear_obj(changeVarExpressionSo)
1763
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1764
                            intervalCenterSo, maxErrorSo))
1765
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1766
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1767
        print "Computed approximation error:", errorSa.n(digits=10)
1768
        # If the error and interval are OK a the first try, just return.
1769
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1770
            (errorSa <= approxAccurSa):
1771
            if sollyaPrecChangedSa:
1772
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1773
            sollya_lib_clear_obj(initialSollyaPrecSo)
1774
            sollya_lib_clear_obj(functionSo)
1775
            sollya_lib_clear_obj(degreeSo)
1776
            sollya_lib_clear_obj(scaledBoundsSo)
1777
            #print "Approximation error:", errorSa
1778
            return resultArray
1779
        ## The returned interval upper bound does not reach the requested upper
1780
        #  upper bound: compute the next upper bound.
1781
        ## The following ratio is always >= 1. If errorSa, we may want to
1782
        #  enlarge the interval
1783
        currentErrorRatio = approxAccurSa / errorSa
1784
        ## --|--------------------------------------------------------------|--
1785
        #  --|--------------------|--------------------------------------------
1786
        #  --|----------------------------|------------------------------------
1787
        ## Starting point for the next upper bound.
1788
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1789
        # Compute the increment. 
1790
        newBoundsWidthSa = \
1791
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1792
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1793
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1794
        # Take into account the original interval upper bound.                     
1795
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1796
            currentScaledUpperBoundSa = scaledUpperBoundSa
1797
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1798
            print "Can't shrink the interval anymore!"
1799
            print "You should consider increasing the Sollya internal precision"
1800
            print "or the polynomial degree."
1801
            print "Giving up!"
1802
            if precChangedSa:
1803
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1804
            sollya_lib_clear_obj(initialSollyaPrecSo)
1805
            sollya_lib_clear_obj(functionSo)
1806
            sollya_lib_clear_obj(degreeSo)
1807
            sollya_lib_clear_obj(scaledBoundsSo)
1808
            return None
1809
        # Compute the other expansions.
1810
        # Test for insufficient precision.
1811
# End slz_get_intervals_and_polynomials
1812

    
1813
def slz_interval_scaling_expression(boundsInterval, expVar):
1814
    """
1815
    Compute the scaling expression to map an interval that spans at most
1816
    a single binade into [1, 2) and the inverse expression as well.
1817
    If the interval spans more than one binade, result is wrong since
1818
    scaling is only based on the lower bound.
1819
    Not very sure that the transformation makes sense for negative numbers.
1820
    """
1821
    # The "one of the bounds is 0" special case. Here we consider
1822
    # the interval as the subnormals binade.
1823
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1824
        # The upper bound is (or should be) positive.
1825
        if boundsInterval.endpoints()[0] == 0:
1826
            if boundsInterval.endpoints()[1] == 0:
1827
                return None
1828
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1829
            l2     = boundsInterval.endpoints()[1].abs().log2()
1830
            # The upper bound is a power of two
1831
            if binade == l2:
1832
                scalingCoeff    = 2^(-binade)
1833
                invScalingCoeff = 2^(binade)
1834
                scalingOffset   = 1
1835
                return \
1836
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1837
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1838
            else:
1839
                scalingCoeff    = 2^(-binade-1)
1840
                invScalingCoeff = 2^(binade+1)
1841
                scalingOffset   = 1
1842
                return((scalingCoeff * expVar + scalingOffset),\
1843
                    ((expVar - scalingOffset) * invScalingCoeff))
1844
        # The lower bound is (or should be) negative.
1845
        if boundsInterval.endpoints()[1] == 0:
1846
            if boundsInterval.endpoints()[0] == 0:
1847
                return None
1848
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1849
            l2     = boundsInterval.endpoints()[0].abs().log2()
1850
            # The upper bound is a power of two
1851
            if binade == l2:
1852
                scalingCoeff    = -2^(-binade)
1853
                invScalingCoeff = -2^(binade)
1854
                scalingOffset   = 1
1855
                return((scalingCoeff * expVar + scalingOffset),\
1856
                    ((expVar - scalingOffset) * invScalingCoeff))
1857
            else:
1858
                scalingCoeff    = -2^(-binade-1)
1859
                invScalingCoeff = -2^(binade+1)
1860
                scalingOffset   = 1
1861
                return((scalingCoeff * expVar + scalingOffset),\
1862
                   ((expVar - scalingOffset) * invScalingCoeff))
1863
    # General case
1864
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1865
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1866
    # We allow for a single exception in binade spanning is when the
1867
    # "outward bound" is a power of 2 and its binade is that of the
1868
    # "inner bound" + 1.
1869
    if boundsInterval.endpoints()[0] > 0:
1870
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1871
        if lbBinade != ubBinade:
1872
            print "Different binades."
1873
            if ubL2 != ubBinade:
1874
                print "Not a power of 2."
1875
                return None
1876
            elif abs(ubBinade - lbBinade) > 1:
1877
                print "Too large span:", abs(ubBinade - lbBinade)
1878
                return None
1879
    else:
1880
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1881
        if lbBinade != ubBinade:
1882
            print "Different binades."
1883
            if lbL2 != lbBinade:
1884
                print "Not a power of 2."
1885
                return None
1886
            elif abs(ubBinade - lbBinade) > 1:
1887
                print "Too large span:", abs(ubBinade - lbBinade)
1888
                return None
1889
    #print "Lower bound binade:", binade
1890
    if boundsInterval.endpoints()[0] > 0:
1891
        return \
1892
            ((2^(-lbBinade) * expVar).function(expVar),
1893
             (2^(lbBinade) * expVar).function(expVar))
1894
    else:
1895
        return \
1896
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1897
             (-(2^(ubBinade)) * expVar).function(expVar))
1898
"""
1899
    # Code sent to attic. Should be the base for a 
1900
    # "slz_interval_translate_expression" rather than scale.
1901
    # Extra control and special cases code  added in  
1902
    # slz_interval_scaling_expression could (should ?) be added to
1903
    # this new function.
1904
    # The scaling offset is only used for negative numbers.
1905
    # When the absolute value of the lower bound is < 0.
1906
    if abs(boundsInterval.endpoints()[0]) < 1:
1907
        if boundsInterval.endpoints()[0] >= 0:
1908
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1909
            invScalingCoeff = 1/scalingCoeff
1910
            return((scalingCoeff * expVar, 
1911
                    invScalingCoeff * expVar))
1912
        else:
1913
            scalingCoeff = \
1914
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1915
            scalingOffset = -3 * scalingCoeff
1916
            return((scalingCoeff * expVar + scalingOffset,
1917
                    1/scalingCoeff * expVar + 3))
1918
    else: 
1919
        if boundsInterval.endpoints()[0] >= 0:
1920
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1921
            scalingOffset = 0
1922
            return((scalingCoeff * expVar, 
1923
                    1/scalingCoeff * expVar))
1924
        else:
1925
            scalingCoeff = \
1926
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1927
            scalingOffset =  -3 * scalingCoeff
1928
            #scalingOffset = 0
1929
            return((scalingCoeff * expVar + scalingOffset,
1930
                    1/scalingCoeff * expVar + 3))
1931
"""
1932
# End slz_interval_scaling_expression
1933
   
1934
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1935
    """
1936
    Compute the Sage version of the Taylor polynomial and it's
1937
    companion data (interval, center...)
1938
    The input parameter is a five elements tuple:
1939
    - [0]: the polyomial (without variable change), as polynomial over a
1940
           real ring;
1941
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1942
           over a real ring;
1943
    - [2]: the interval (as Sollya range);
1944
    - [3]: the interval center;
1945
    - [4]: the approximation error. 
1946
    
1947
    The function returns a 5 elements tuple: formed with all the 
1948
    input elements converted into their Sollya counterpart.
1949
    """
1950
    #print "Sollya polynomial to convert:", 
1951
    #pobyso_autoprint(polyRangeCenterErrorSo)
1952
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
1953
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
1954
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
1955
    intervalSa = \
1956
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1957
    centerSa = \
1958
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1959
    errorSa = \
1960
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1961
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1962
    # End slz_interval_and_polynomial_to_sage
1963

    
1964
def slz_is_htrn(argument, 
1965
                function, 
1966
                targetAccuracy, 
1967
                targetRF = None, 
1968
                targetPlusOnePrecRF = None, 
1969
                quasiExactRF = None):
1970
    """
1971
      Check if an element (argument) of the domain of function (function)
1972
      yields a HTRN case (rounding to next) for the target precision 
1973
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1974
      
1975
      The strategy is: 
1976
      - compute the image at high (quasi-exact) precision;
1977
      - round it to nearest to precision;
1978
      - round it to exactly to (precision+1), the computed number has two
1979
        midpoint neighbors;
1980
      - check the distance between these neighbors and the quasi-exact value;
1981
        - if none of them is closer than the targetAccuracy, return False,
1982
        - else return true.
1983
      - Powers of two are a special case when comparing the midpoint in
1984
        the 0 direction..
1985
    """
1986
    ## Arguments filtering. 
1987
    ## TODO: filter the first argument for consistence.
1988
    if targetRF is None:
1989
        targetRF = argument.parent()
1990
    ## Ditto for the real field holding the midpoints.
1991
    if targetPlusOnePrecRF is None:
1992
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1993
    ## If no quasiExactField is provided, create a high accuracy RealField.
1994
    if quasiExactRF is None:
1995
        quasiExactRF = RealField(1536)
1996
    function                      = function.function(function.variables()[0])
1997
    exactArgument                 = quasiExactRF(argument)
1998
    quasiExactValue               = function(exactArgument)
1999
    roundedValue                  = targetRF(quasiExactValue)
2000
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
2001
    # Upper midpoint.
2002
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
2003
    # Lower midpoint.
2004
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
2005
    binade                        = slz_compute_binade(roundedValue)
2006
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
2007
    #print "Argument:", argument
2008
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
2009
    #print "Binade:", binade
2010
    #print "binadeCorrectedTargetAccuracy:", \
2011
    #binadeCorrectedTargetAccuracy.n(prec=100)
2012
    #print "binadeCorrectedTargetAccuracy:", \
2013
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
2014
    #print "Upper midpoint:", \
2015
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2016
    #print "Rounded value :", \
2017
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
2018
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
2019
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
2020
    #print "Lower midpoint:", \
2021
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2022
    ## Make quasiExactValue = 0 a special case to move it out of the way.
2023
    if quasiExactValue == 0:
2024
        return False
2025
    ## Begining of the general case : check with the midpoint of 
2026
    #  greatest absolute value.
2027
    if quasiExactValue > 0:
2028
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
2029
           binadeCorrectedTargetAccuracy:
2030
            #print "Too close to the upper midpoint: ", \
2031
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2032
            #print argument.n().str(base=16, \
2033
            #  no_sci=False)
2034
            #print "Lower midpoint:", \
2035
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2036
            #print "Value         :", \
2037
            # quasiExactValue.n(prec=200).str(base=2)
2038
            #print "Upper midpoint:", \
2039
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2040
            return True
2041
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
2042
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2043
           binadeCorrectedTargetAccuracy:
2044
            #print "Too close to the upper midpoint: ", \
2045
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2046
            #print argument.n().str(base=16, \
2047
            #  no_sci=False)
2048
            #print "Lower midpoint:", \
2049
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2050
            #print "Value         :", \
2051
            #  quasiExactValue.n(prec=200).str(base=2)
2052
            #print "Upper midpoint:", \
2053
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2054
            #print
2055
            return True
2056
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2057
    ## For the midpoint of smaller absolute value, 
2058
    #  split cases with the powers of 2.
2059
    if sno_abs_is_power_of_two(roundedValue):
2060
        if quasiExactValue > 0:        
2061
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
2062
               binadeCorrectedTargetAccuracy / 2:
2063
                #print "Lower midpoint:", \
2064
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2065
                #print "Value         :", \
2066
                #  quasiExactValue.n(prec=200).str(base=2)
2067
                #print "Upper midpoint:", \
2068
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2069
                #print
2070
                return True
2071
        else:
2072
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2073
              binadeCorrectedTargetAccuracy / 2:
2074
                #print "Lower midpoint:", \
2075
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2076
                #print "Value         :", 
2077
                #  quasiExactValue.n(prec=200).str(base=2)
2078
                #print "Upper midpoint:", 
2079
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2080
                #print
2081
                return True
2082
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2083
    else: ## Not a power of 2, regular comparison with the midpoint of 
2084
          #  smaller absolute value.
2085
        if quasiExactValue > 0:        
2086
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2087
              binadeCorrectedTargetAccuracy:
2088
                #print "Lower midpoint:", \
2089
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2090
                #print "Value         :", \
2091
                #  quasiExactValue.n(prec=200).str(base=2)
2092
                #print "Upper midpoint:", \
2093
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2094
                #print
2095
                return True
2096
        else: # quasiExactValue <= 0
2097
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2098
              binadeCorrectedTargetAccuracy:
2099
                #print "Lower midpoint:", \
2100
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2101
                #print "Value         :", \
2102
                #  quasiExactValue.n(prec=200).str(base=2)
2103
                #print "Upper midpoint:", \
2104
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2105
                #print
2106
                return True
2107
    #print "distance to the upper midpoint:", \
2108
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
2109
    #print "distance to the lower midpoint:", \
2110
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
2111
    return False
2112
# End slz_is_htrn
2113
#
2114
def slz_pm1():
2115
    """
2116
    Compute a uniform RV in {-1, 1} (not zero).
2117
    """
2118
    ## function getrandbits(N) generates a long int with N random bits.
2119
    return getrandbits(1) * 2-1
2120
# End slz_pm1.
2121
#
2122
def slz_random_proj_pm1(r, c):
2123
    """
2124
    r x c matrix with \pm 1 ({-1, 1}) coefficients
2125
    """
2126
    M = matrix(r, c)
2127
    for i in range(0, r):
2128
        for j in range (0, c):
2129
            M[i,j] = slz_pm1()
2130
    return M
2131
# End random_proj_pm1.
2132
#
2133
def slz_random_proj_uniform(n, r, c):
2134
    """
2135
    r x c integer matrix with uniform n-bit integer coefficients
2136
    """
2137
    M = matrix(r, c)
2138
    for i in range(0, r):
2139
        for j in range (0, c):
2140
            M[i,j] = slz_uniform(n)
2141
    return M       
2142
# End slz_random_proj_uniform.
2143
#
2144
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
2145
                                                precision,
2146
                                                targetHardnessToRound,
2147
                                                variable1,
2148
                                                variable2):
2149
    """
2150
    Creates a new multivariate polynomial with integer coefficients for use
2151
     with the Coppersmith method.
2152
    A the same time it computes :
2153
    - 2^K (N);
2154
    - 2^k (bound on the second variable)
2155
    - lcm
2156

    
2157
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
2158
                         variables.
2159
    :param precision: the precision of the floating-point coefficients.
2160
    :param targetHardnessToRound: the hardness to round we want to check.
2161
    :param variable1: the first variable of the polynomial (an expression).
2162
    :param variable2: the second variable of the polynomial (an expression).
2163
    
2164
    :returns: a 4 elements tuple:
2165
                - the polynomial;
2166
                - the modulus (N);
2167
                - the t bound;
2168
                - the lcm used to compute the integral coefficients and the 
2169
                  module.
2170
    """
2171
    # Create a new integer polynomial ring.
2172
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
2173
    # Coefficients are issued in the increasing power order.
2174
    ratPolyCoefficients = ratPolyOfInt.coefficients()
2175
    # Print the reversed list for debugging.
2176
    #print
2177
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
2178
    # Build the list of number we compute the lcm of.
2179
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
2180
    #print "Coefficient denominators:", coefficientDenominators
2181
    coefficientDenominators.append(2^precision)
2182
    coefficientDenominators.append(2^(targetHardnessToRound))
2183
    leastCommonMultiple = lcm(coefficientDenominators)
2184
    # Compute the expression corresponding to the new polynomial
2185
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
2186
    #print coefficientNumerators
2187
    polynomialExpression = 0
2188
    power = 0
2189
    # Iterate over two lists at the same time, stop when the shorter
2190
    # (is this case coefficientsNumerators) is 
2191
    # exhausted. Both lists are ordered in the order of increasing powers
2192
    # of variable1.
2193
    for numerator, denominator in \
2194
                        zip(coefficientNumerators, coefficientDenominators):
2195
        multiplicator = leastCommonMultiple / denominator 
2196
        newCoefficient = numerator * multiplicator
2197
        polynomialExpression += newCoefficient * variable1^power
2198
        power +=1
2199
    polynomialExpression += - variable2
2200
    return (IP(polynomialExpression),
2201
            leastCommonMultiple / 2^precision, # 2^K aka N.
2202
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
2203
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
2204
            leastCommonMultiple) # If we want to make test computations.
2205
        
2206
# End slz_rat_poly_of_int_to_poly_for_coppersmith
2207

    
2208
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
2209
                                          precision):
2210
    """
2211
    Makes a variable substitution into the input polynomial so that the output
2212
    polynomial can take integer arguments.
2213
    All variables of the input polynomial "have precision p". That is to say
2214
    that they are rationals with denominator == 2^(precision - 1): 
2215
    x = y/2^(precision - 1).
2216
    We "incorporate" these denominators into the coefficients with, 
2217
    respectively, the "right" power.
2218
    """
2219
    polynomialField = ratPolyOfRat.parent()
2220
    polynomialVariable = ratPolyOfRat.variables()[0]
2221
    #print "The polynomial field is:", polynomialField
2222
    return \
2223
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
2224
                                   polynomialVariable/2^(precision-1)}))
2225
    
2226
# End slz_rat_poly_of_rat_to_rat_poly_of_int
2227

    
2228
def slz_reduce_and_test_base(matrixToReduce,
2229
                             nAtAlpha,
2230
                             monomialsCountSqrt):
2231
    """
2232
    Reduces the basis, tests the Coppersmith condition and returns
2233
    a list of rows that comply with the condition.
2234
    """
2235
    ccComplientRowsList = []
2236
    reducedMatrix = matrixToReduce.LLL(None)
2237
    if not reducedMatrix.is_LLL_reduced():
2238
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
2239
    else:
2240
        print "reducedMatrix is actually reduced."
2241
    print "N^alpha:", nAtAlpha.n()
2242
    rowIndex = 0
2243
    for row in reducedMatrix.rows():
2244
        l2Norm = row.norm(2)
2245
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
2246
                monomialsCountSqrt.n(), ". Is vector OK?", 
2247
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
2248
            ccComplientRowsList.append(row)
2249
            print "True"
2250
        else:
2251
            print "False"
2252
    # End for
2253
    return ccComplientRowsList
2254
# End slz_reduce_and_test_base
2255

    
2256
def slz_reduce_lll_proj(matToReduce, n):
2257
    """
2258
    Compute the transformation matrix that realizes an LLL reduction on
2259
    the random uniform projected matrix.
2260
    Return both the initial matrix "reduced" by the transformation matrix and
2261
    the transformation matrix itself.
2262
    """
2263
    ## Compute the projected matrix.
2264
    """
2265
    # Random matrix elements {-2^(n-1),...,0,...,2^(n-1)-1}.
2266
    matProjector = slz_random_proj_uniform(n,
2267
                                           matToReduce.ncols(),
2268
                                           matToReduce.nrows())
2269
    """
2270
    # Random matrix elements in {-1,0,1}. 
2271
    matProjector = slz_random_proj_pm1(matToReduce.ncols(),
2272
                                       matToReduce.nrows())
2273
    matProjected = matToReduce * matProjector
2274
    ## Build the argument matrix for LLL in such a way that the transformation
2275
    #  matrix is also returned. This matrix is obtained at almost no extra
2276
    # cost. An identity matrix must be appended to 
2277
    #  the left of the initial matrix. The transformation matrix will
2278
    # will be recovered at the same location from the returned matrix .
2279
    idMat = identity_matrix(matProjected.nrows())
2280
    augmentedMatToReduce = idMat.augment(matProjected)
2281
    reducedProjMat = \
2282
        augmentedMatToReduce.LLL(algorithm='fpLLL:wrapper')
2283
    ## Recover the transformation matrix (the left part of the reduced matrix). 
2284
    #  We discard the reduced matrix itself.
2285
    transMat = reducedProjMat.submatrix(0,
2286
                                        0,
2287
                                        reducedProjMat.nrows(),
2288
                                        reducedProjMat.nrows())
2289
    ## Return the initial matrix "reduced" and the transformation matrix tuple.
2290
    return (transMat * matToReduce, transMat)
2291
# End  slz_reduce_lll_proj.
2292
#
2293
def slz_resultant(poly1, poly2, elimVar, debug = False):
2294
    """
2295
    Compute the resultant for two polynomials for a given variable
2296
    and return the (poly1, poly2, resultant) if the resultant
2297
    is not 0.
2298
    Return () otherwise.
2299
    """
2300
    polynomialRing0    = poly1.parent()
2301
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2302
    if resultantInElimVar is None:
2303
        if debug:
2304
            print poly1
2305
            print poly2
2306
            print "have GCD = ", poly1.gcd(poly2)
2307
        return None
2308
    if resultantInElimVar.is_zero():
2309
        if debug:
2310
            print poly1
2311
            print poly2
2312
            print "have GCD = ", poly1.gcd(poly2)
2313
        return None
2314
    else:
2315
        if debug:
2316
            print poly1
2317
            print poly2
2318
            print "have resultant in t = ", resultantInElimVar
2319
        return resultantInElimVar
2320
# End slz_resultant.
2321
#
2322
def slz_resultant_tuple(poly1, poly2, elimVar):
2323
    """
2324
    Compute the resultant for two polynomials for a given variable
2325
    and return the (poly1, poly2, resultant) if the resultant
2326
    is not 0.
2327
    Return () otherwise.
2328
    """
2329
    polynomialRing0    = poly1.parent()
2330
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2331
    if resultantInElimVar.is_zero():
2332
        return ()
2333
    else:
2334
        return (poly1, poly2, resultantInElimVar)
2335
# End slz_resultant_tuple.
2336
#
2337
def slz_uniform(n):
2338
    """
2339
    Compute a uniform RV in [-2^(n-1), 2^(n-1)-1] (zero is included).
2340
    """
2341
    ## function getrandbits(n) generates a long int with n random bits.
2342
    return getrandbits(n) - 2^(n-1)
2343
# End slz_uniform.
2344
#
2345
sys.stderr.write("\t...sageSLZ loaded\n")
2346