Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (103,6 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
    (reducedMatrix1, reductionMatrix1) = \
457
         slz_reduce_lll_proj(matrixToReduce, 2^16)
458
    #print "Reduced matrix:"
459
    matrixIsReducedEnough = reducedMatrix1.is_LLL_reduced()
460
    #
461
    if not matrixIsReducedEnough:
462
        reducedMatrix = reducedMatrix1.LLL(algorithm='fpLLL:wrapper')
463
        isLLLReduced  = reducedMatrix.is_LLL_reduced()
464
    else:
465
        reducedMatrix = reducedMatrix1
466
        isLLLReduced  = matrixIsReducedEnough
467
    #for row in reducedMatrix.rows():
468
    #    print row
469
    if not isLLLReduced:
470
        return None
471
    monomialsCount     = len(knownMonomials)
472
    monomialsCountSqrt = sqrt(monomialsCount)
473
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
474
    #print reducedMatrix
475
    ## Check the Coppersmith condition for each row and build the reduced 
476
    #  polynomials.
477
    ccReducedPolynomialsList = []
478
    for row in reducedMatrix.rows():
479
        l2Norm = row.norm(2)
480
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
481
            #print (l2Norm * monomialsCountSqrt).n()
482
            #print l2Norm.n()
483
            ccReducedPolynomial = \
484
                slz_compute_reduced_polynomial(row,
485
                                               knownMonomials,
486
                                               iVariable,
487
                                               iBound,
488
                                               tVariable,
489
                                               tBound)
490
            if not ccReducedPolynomial is None:
491
                ccReducedPolynomialsList.append(ccReducedPolynomial)
492
        else:
493
            #print l2Norm.n() , ">", nAtAlpha
494
            pass
495
    if len(ccReducedPolynomialsList) < 2:
496
        print "Less than 2 Coppersmith condition compliant vectors."
497
        return ()
498
    #print ccReducedPolynomialsList
499
    return ccReducedPolynomialsList
500
# End slz_compute_coppersmith_reduced_polynomials_proj
501
#
502
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
503
                                                                    alpha,
504
                                                                    N,
505
                                                                    iBound,
506
                                                                    tBound,
507
                                                                    debug = False):
508
    """
509
    For a given set of arguments (see below), compute a list
510
    of "reduced polynomials" that could be used to compute roots
511
    of the inputPolynomial.
512
    Print the volume of the initial basis as well.
513
    INPUT:
514
    
515
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
516
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
517
    - "N" -- the modulus;
518
    - "iBound" -- the bound on the first variable;
519
    - "tBound" -- the bound on the second variable.
520
    
521
    OUTPUT:
522
    
523
    A list of bivariate integer polynomial obtained using the Coppersmith
524
    algorithm. The polynomials correspond to the rows of the LLL-reduce
525
    reduced base that comply with the Coppersmith condition.
526
    """
527
    # Arguments check.
528
    if iBound == 0 or tBound == 0:
529
        return None
530
    # End arguments check.
531
    nAtAlpha = N^alpha
532
    if debug:
533
        print "N at alpha:", nAtAlpha
534
    ## Building polynomials for matrix.
535
    polyRing   = inputPolynomial.parent()
536
    # Whatever the 2 variables are actually called, we call them
537
    # 'i' and 't' in all the variable names.
538
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
539
    #print polyVars[0], type(polyVars[0])
540
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
541
                                              tVariable:tVariable * tBound})
542
##    polynomialsList = \
543
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
544
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
545
    polynomialsList = \
546
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
547
                                             alpha,
548
                                             N,
549
                                             iBound,
550
                                             tBound,
551
                                             0)
552
    #print "Polynomials list:", polynomialsList
553
    ## Building the proto matrix.
554
    knownMonomials = []
555
    protoMatrix    = []
556
    for poly in polynomialsList:
557
        spo_add_polynomial_coeffs_to_matrix_row(poly,
558
                                                knownMonomials,
559
                                                protoMatrix,
560
                                                0)
561
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
562
    matrixToReduceTranspose = matrixToReduce.transpose()
563
    squareMatrix = matrixToReduce * matrixToReduceTranspose
564
    squareMatDet = det(squareMatrix)
565
    latticeVolume = sqrt(squareMatDet)
566
    print "Lattice volume:", latticeVolume.n()
567
    print "Lattice volume / N:", (latticeVolume/N).n()
568
    #print matrixToReduce
569
    ## Reduction and checking.
570
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
571
    #  error message issued when previous code was used.
572
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
573
    reductionTimeStart = cputime() 
574
    reducedMatrix = matrixToReduce.LLL(fp=None)
575
    reductionTime = cputime(reductionTimeStart)
576
    print "Reduction time:", reductionTime
577
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
578
    if not isLLLReduced:
579
       return None
580
    #
581
    if debug:
582
        matrixFile = file('/tmp/reducedMatrix.txt', 'w')
583
        for row in reducedMatrix.rows():
584
            matrixFile.write(str(row) + "\n")
585
        matrixFile.close()
586
    #
587
    monomialsCount     = len(knownMonomials)
588
    monomialsCountSqrt = sqrt(monomialsCount)
589
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
590
    #print reducedMatrix
591
    ## Check the Coppersmith condition for each row and build the reduced 
592
    #  polynomials.
593
    ccVectorsCount = 0
594
    ccReducedPolynomialsList = []
595
    for row in reducedMatrix.rows():
596
        l2Norm = row.norm(2)
597
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
598
            #print (l2Norm * monomialsCountSqrt).n()
599
            #print l2Norm.n()
600
            ccVectorsCount +=1            
601
            ccReducedPolynomial = \
602
                slz_compute_reduced_polynomial(row,
603
                                               knownMonomials,
604
                                               iVariable,
605
                                               iBound,
606
                                               tVariable,
607
                                               tBound)
608
            if not ccReducedPolynomial is None:
609
                ccReducedPolynomialsList.append(ccReducedPolynomial)
610
        else:
611
            #print l2Norm.n() , ">", nAtAlpha
612
            pass
613
    if debug:
614
        print ccVectorsCount, "out of ", len(ccReducedPolynomialsList), 
615
        print "took Coppersmith text."
616
    if len(ccReducedPolynomialsList) < 2:
617
        print "Less than 2 Coppersmith condition compliant vectors."
618
        return ()
619
    if debug:
620
        print "Reduced and Coppersmith compliant polynomials list", ccReducedPolynomialsList
621
    return ccReducedPolynomialsList
622
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
623

    
624
def slz_compute_initial_lattice_matrix(inputPolynomial,
625
                                       alpha,
626
                                       N,
627
                                       iBound,
628
                                       tBound,
629
                                       debug = False):
630
    """
631
    For a given set of arguments (see below), compute the initial lattice
632
    that could be reduced. 
633
    INPUT:
634
    
635
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
636
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
637
    - "N" -- the modulus;
638
    - "iBound" -- the bound on the first variable;
639
    - "tBound" -- the bound on the second variable.
640
    
641
    OUTPUT:
642
    
643
    A list of bivariate integer polynomial obtained using the Coppersmith
644
    algorithm. The polynomials correspond to the rows of the LLL-reduce
645
    reduced base that comply with the Coppersmith condition.
646
    """
647
    # Arguments check.
648
    if iBound == 0 or tBound == 0:
649
        return None
650
    # End arguments check.
651
    nAtAlpha = N^alpha
652
    ## Building polynomials for matrix.
653
    polyRing   = inputPolynomial.parent()
654
    # Whatever the 2 variables are actually called, we call them
655
    # 'i' and 't' in all the variable names.
656
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
657
    #print polyVars[0], type(polyVars[0])
658
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
659
                                              tVariable:tVariable * tBound})
660
    polynomialsList = \
661
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
662
                                             alpha,
663
                                             N,
664
                                             iBound,
665
                                             tBound,
666
                                             0)
667
    #print "Polynomials list:", polynomialsList
668
    ## Building the proto matrix.
669
    knownMonomials = []
670
    protoMatrix    = []
671
    for poly in polynomialsList:
672
        spo_add_polynomial_coeffs_to_matrix_row(poly,
673
                                                knownMonomials,
674
                                                protoMatrix,
675
                                                0)
676
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
677
    if debug:
678
        print "Initial basis polynomials"
679
        for poly in polynomialsList:
680
            print poly
681
    return matrixToReduce
682
# End slz_compute_initial_lattice_matrix.
683

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

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

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

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

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

    
1528
def slz_fix_bounds_for_binades(lowerBound, 
1529
                               upperBound, 
1530
                               func = None, 
1531
                               domainDirection = -1,
1532
                               imageDirection  = -1):
1533
    """
1534
    Assuming the function is increasing or decreasing over the 
1535
    [lowerBound, upperBound] interval, return a lower bound lb and 
1536
    an upper bound ub such that:
1537
    - lb and ub belong to the same binade;
1538
    - func(lb) and func(ub) belong to the same binade.
1539
    domainDirection indicate how bounds move to fit into the same binade:
1540
    - a negative value move the upper bound down;
1541
    - a positive value move the lower bound up.
1542
    imageDirection indicate how bounds move in order to have their image
1543
    fit into the same binade, variation of the function is also condidered.
1544
    For an increasing function:
1545
    - negative value moves the upper bound down (and its image value as well);
1546
    - a positive values moves the lower bound up (and its image value as well);
1547
    For a decreasing function it is the other way round.
1548
    """
1549
    ## Arguments check
1550
    if lowerBound > upperBound:
1551
        return None
1552
    if lowerBound == upperBound:
1553
        return (lowerBound, upperBound)
1554
    if func is None:
1555
        return None
1556
    #
1557
    #varFunc = func.variables()[0]
1558
    lb = lowerBound
1559
    ub = upperBound
1560
    lbBinade = slz_compute_binade(lb) 
1561
    ubBinade = slz_compute_binade(ub)
1562
    ## Domain binade.
1563
    while lbBinade != ubBinade:
1564
        newIntervalWidth = (ub - lb) / 2
1565
        if domainDirection < 0:
1566
            ub = lb + newIntervalWidth
1567
            ubBinade = slz_compute_binade(ub)
1568
        else:
1569
            lb = lb + newIntervalWidth
1570
            lbBinade = slz_compute_binade(lb) 
1571
    ## Image binade.
1572
    if lb == ub:
1573
        return (lb, ub)
1574
    lbImg = func(lb)
1575
    ubImg = func(ub)
1576
    funcIsInc = (ubImg >= lbImg)
1577
    lbImgBinade = slz_compute_binade(lbImg)
1578
    ubImgBinade = slz_compute_binade(ubImg)
1579
    while lbImgBinade != ubImgBinade:
1580
        newIntervalWidth = (ub - lb) / 2
1581
        if imageDirection < 0:
1582
            if funcIsInc:
1583
                ub = lb + newIntervalWidth
1584
                ubImgBinade = slz_compute_binade(func(ub))
1585
                #print ubImgBinade
1586
            else:
1587
                lb = lb + newIntervalWidth
1588
                lbImgBinade = slz_compute_binade(func(lb))
1589
                #print lbImgBinade
1590
        else:
1591
            if funcIsInc:
1592
                lb = lb + newIntervalWidth
1593
                lbImgBinade = slz_compute_binade(func(lb))
1594
                #print lbImgBinade
1595
            else:
1596
                ub = lb + newIntervalWidth
1597
                ubImgBinade = slz_compute_binade(func(ub))
1598
                #print ubImgBinade
1599
    # End while lbImgBinade != ubImgBinade:
1600
    return (lb, ub)
1601
# End slz_fix_bounds_for_binades.
1602

    
1603
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1604
    # Create a polynomial over the rationals.
1605
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1606
    return(ratPolynomialRing(polyOfFloat))
1607
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1608

    
1609
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1610
    """
1611
     Create a polynomial over the rationals where all denominators are
1612
     powers of two.
1613
    """
1614
    polyVariable = polyOfFloat.variables()[0] 
1615
    RPR = QQ[str(polyVariable)]
1616
    polyCoeffs      = polyOfFloat.coefficients()
1617
    #print polyCoeffs
1618
    polyExponents   = polyOfFloat.exponents()
1619
    #print polyExponents
1620
    polyDenomPtwoCoeffs = []
1621
    for coeff in polyCoeffs:
1622
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1623
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1624
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1625
    ratPoly = RPR(0)
1626
    #print type(ratPoly)
1627
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1628
    #  The coefficient becomes plainly wrong when exponent == 0.
1629
    #  No clue as to why.
1630
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1631
        ratPoly += coeff * RPR(polyVariable^exponent)
1632
    return ratPoly
1633
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1634

    
1635
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1636
                                      lowerBoundSa, 
1637
                                      upperBoundSa, floatingPointPrecSa, 
1638
                                      internalSollyaPrecSa, approxPrecSa):
1639
    """
1640
    Under the assumption that:
1641
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1642
    - lowerBound and upperBound belong to the same binade.
1643
    from a:
1644
    - function;
1645
    - a degree
1646
    - a pair of bounds;
1647
    - the floating-point precision we work on;
1648
    - the internal Sollya precision;
1649
    - the requested approximation error
1650
    The initial interval is, possibly, splitted into smaller intervals.
1651
    It return a list of tuples, each made of:
1652
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1653
    - a second polynomial (with a changed variable f(x) = q(x))
1654
    - the approximation interval;
1655
    - the center, x0, of the interval;
1656
    - the corresponding approximation error.
1657
    TODO: fix endless looping for some parameters sets.
1658
    """
1659
    resultArray = []
1660
    # Set Sollya to the necessary internal precision.
1661
    sollyaPrecChangedSa = False
1662
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1663
    if internalSollyaPrecSa > currentSollyaPrecSa:
1664
        if internalSollyaPrecSa <= 2:
1665
            print inspect.stack()[0][3], ": precision change <=2 requested."
1666
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1667
        sollyaPrecChangedSa = True
1668
    #
1669
    x = functionSa.variables()[0] # Actual variable name can be anything.
1670
    # Scaled function: [1=,2] -> [1,2].
1671
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1672
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1673
                slz_compute_scaled_function(functionSa,                       \
1674
                                            lowerBoundSa,                     \
1675
                                            upperBoundSa,                     \
1676
                                            floatingPointPrecSa)
1677
    # In case bounds were in the negative real one may need to
1678
    # switch scaled bounds.
1679
    if scaledLowerBoundSa > scaledUpperBoundSa:
1680
        scaledLowerBoundSa, scaledUpperBoundSa = \
1681
            scaledUpperBoundSa, scaledLowerBoundSa
1682
        #print "Switching!"
1683
    print "Approximation accuracy: ", RR(approxAccurSa)
1684
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1685
    functionSo = \
1686
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1687
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1688
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1689
                                                  scaledUpperBoundSa)
1690

    
1691
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1692
                                              upperBoundSa.parent().precision()))
1693
    currentScaledLowerBoundSa = scaledLowerBoundSa
1694
    currentScaledUpperBoundSa = scaledUpperBoundSa
1695
    while True:
1696
        ## Compute the first Taylor expansion.
1697
        print "Computing a Taylor expansion..."
1698
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1699
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1700
                                        currentScaledLowerBoundSa, 
1701
                                        currentScaledUpperBoundSa,
1702
                                        approxAccurSa, internalSollyaPrecSa)
1703
        print "...done."
1704
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1705
        #  This value goes to the first variable: polySo. Other variables are
1706
        #  not assigned and should not be tested.
1707
        if polySo is None:
1708
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1709
            if precChangedSa:
1710
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1711
            sollya_lib_clear_obj(initialSollyaPrecSo)
1712
            sollya_lib_clear_obj(functionSo)
1713
            sollya_lib_clear_obj(degreeSo)
1714
            sollya_lib_clear_obj(scaledBoundsSo)
1715
            return None
1716
        ## Add to the result array.
1717
        ### Change variable stuff in Sollya x -> x0-x.
1718
        changeVarExpressionSo = \
1719
            sollya_lib_build_function_sub( \
1720
                              sollya_lib_build_function_free_variable(), 
1721
                              sollya_lib_copy_obj(intervalCenterSo))
1722
        polyVarChangedSo = \
1723
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1724
        #### Get rid of the variable change Sollya stuff. 
1725
        sollya_lib_clear_obj(changeVarExpressionSo)
1726
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1727
                            intervalCenterSo, maxErrorSo))
1728
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1729
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1730
        print "Computed approximation error:", errorSa.n(digits=10)
1731
        # If the error and interval are OK a the first try, just return.
1732
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1733
            (errorSa <= approxAccurSa):
1734
            if precChangedSa:
1735
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1736
            sollya_lib_clear_obj(initialSollyaPrecSo)
1737
            sollya_lib_clear_obj(functionSo)
1738
            sollya_lib_clear_obj(degreeSo)
1739
            sollya_lib_clear_obj(scaledBoundsSo)
1740
            #print "Approximation error:", errorSa
1741
            return resultArray
1742
        ## The returned interval upper bound does not reach the requested upper
1743
        #  upper bound: compute the next upper bound.
1744
        ## The following ratio is always >= 1. If errorSa, we may want to
1745
        #  enlarge the interval
1746
        currentErrorRatio = approxAccurSa / errorSa
1747
        ## --|--------------------------------------------------------------|--
1748
        #  --|--------------------|--------------------------------------------
1749
        #  --|----------------------------|------------------------------------
1750
        ## Starting point for the next upper bound.
1751
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1752
        # Compute the increment. 
1753
        newBoundsWidthSa = \
1754
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1755
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1756
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1757
        # Take into account the original interval upper bound.                     
1758
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1759
            currentScaledUpperBoundSa = scaledUpperBoundSa
1760
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1761
            print "Can't shrink the interval anymore!"
1762
            print "You should consider increasing the Sollya internal precision"
1763
            print "or the polynomial degree."
1764
            print "Giving up!"
1765
            if precChangedSa:
1766
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1767
            sollya_lib_clear_obj(initialSollyaPrecSo)
1768
            sollya_lib_clear_obj(functionSo)
1769
            sollya_lib_clear_obj(degreeSo)
1770
            sollya_lib_clear_obj(scaledBoundsSo)
1771
            return None
1772
        # Compute the other expansions.
1773
        # Test for insufficient precision.
1774
# End slz_get_intervals_and_polynomials
1775

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

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

    
2116
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
2117
                         variables.
2118
    :param precision: the precision of the floating-point coefficients.
2119
    :param targetHardnessToRound: the hardness to round we want to check.
2120
    :param variable1: the first variable of the polynomial (an expression).
2121
    :param variable2: the second variable of the polynomial (an expression).
2122
    
2123
    :returns: a 4 elements tuple:
2124
                - the polynomial;
2125
                - the modulus (N);
2126
                - the t bound;
2127
                - the lcm used to compute the integral coefficients and the 
2128
                  module.
2129
    """
2130
    # Create a new integer polynomial ring.
2131
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
2132
    # Coefficients are issued in the increasing power order.
2133
    ratPolyCoefficients = ratPolyOfInt.coefficients()
2134
    # Print the reversed list for debugging.
2135
    #print
2136
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
2137
    # Build the list of number we compute the lcm of.
2138
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
2139
    #print "Coefficient denominators:", coefficientDenominators
2140
    coefficientDenominators.append(2^precision)
2141
    coefficientDenominators.append(2^(targetHardnessToRound))
2142
    leastCommonMultiple = lcm(coefficientDenominators)
2143
    # Compute the expression corresponding to the new polynomial
2144
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
2145
    #print coefficientNumerators
2146
    polynomialExpression = 0
2147
    power = 0
2148
    # Iterate over two lists at the same time, stop when the shorter
2149
    # (is this case coefficientsNumerators) is 
2150
    # exhausted. Both lists are ordered in the order of increasing powers
2151
    # of variable1.
2152
    for numerator, denominator in \
2153
                        zip(coefficientNumerators, coefficientDenominators):
2154
        multiplicator = leastCommonMultiple / denominator 
2155
        newCoefficient = numerator * multiplicator
2156
        polynomialExpression += newCoefficient * variable1^power
2157
        power +=1
2158
    polynomialExpression += - variable2
2159
    return (IP(polynomialExpression),
2160
            leastCommonMultiple / 2^precision, # 2^K aka N.
2161
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
2162
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
2163
            leastCommonMultiple) # If we want to make test computations.
2164
        
2165
# End slz_rat_poly_of_int_to_poly_for_coppersmith
2166

    
2167
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
2168
                                          precision):
2169
    """
2170
    Makes a variable substitution into the input polynomial so that the output
2171
    polynomial can take integer arguments.
2172
    All variables of the input polynomial "have precision p". That is to say
2173
    that they are rationals with denominator == 2^(precision - 1): 
2174
    x = y/2^(precision - 1).
2175
    We "incorporate" these denominators into the coefficients with, 
2176
    respectively, the "right" power.
2177
    """
2178
    polynomialField = ratPolyOfRat.parent()
2179
    polynomialVariable = ratPolyOfRat.variables()[0]
2180
    #print "The polynomial field is:", polynomialField
2181
    return \
2182
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
2183
                                   polynomialVariable/2^(precision-1)}))
2184
    
2185
# End slz_rat_poly_of_rat_to_rat_poly_of_int
2186

    
2187
def slz_reduce_and_test_base(matrixToReduce,
2188
                             nAtAlpha,
2189
                             monomialsCountSqrt):
2190
    """
2191
    Reduces the basis, tests the Coppersmith condition and returns
2192
    a list of rows that comply with the condition.
2193
    """
2194
    ccComplientRowsList = []
2195
    reducedMatrix = matrixToReduce.LLL(None)
2196
    if not reducedMatrix.is_LLL_reduced():
2197
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
2198
    else:
2199
        print "reducedMatrix is actually reduced."
2200
    print "N^alpha:", nAtAlpha.n()
2201
    rowIndex = 0
2202
    for row in reducedMatrix.rows():
2203
        l2Norm = row.norm(2)
2204
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
2205
                monomialsCountSqrt.n(), ". Is vector OK?", 
2206
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
2207
            ccComplientRowsList.append(row)
2208
            print "True"
2209
        else:
2210
            print "False"
2211
    # End for
2212
    return ccComplientRowsList
2213
# End slz_reduce_and_test_base
2214

    
2215
def slz_reduce_lll_proj(matToReduce, n):
2216
    """
2217
    Compute the transformation matrix that realizes an LLL reduction on
2218
    the random uniform projected matrix.
2219
    Return both the initial matrix "reduced" by the transformation matrix and
2220
    the transformation matrix itself.
2221
    """
2222
    ## Compute the projected matrix.
2223
    matProjector = slz_random_proj_uniform(n,
2224
                                           matToReduce.ncols(),
2225
                                           matToReduce.nrows())
2226
    matProjected = matToReduce * matProjector
2227
    ## Build the argument matrix for LLL in such a way that the transformation
2228
    #  matrix is also returned. This matrix is obtained at almost no extra
2229
    # cost. An identity matrix must be appended to 
2230
    #  the left of the initial matrix. The transformation matrix will
2231
    # will be recovered at the same location from the returned matrix .
2232
    idMat = identity_matrix(matProjected.nrows())
2233
    augmentedMatToReduce = idMat.augment(matProjected)
2234
    reducedProjMat = \
2235
        augmentedMatToReduce.LLL(algorithm='fpLLL:wrapper')
2236
    ## Recover the transformation matrix (the left part of the reduced matrix). 
2237
    #  We discard the reduced matrix itself.
2238
    transMat = reducedProjMat.submatrix(0,
2239
                                        0,
2240
                                        reducedProjMat.nrows(),
2241
                                        reducedProjMat.nrows())
2242
    ## Return the initial matrix "reduced" and the transformation matrix tuple.
2243
    return (transMat * matToReduce, transMat)
2244
# End  slz_reduce_lll_proj.
2245
#
2246
def slz_resultant(poly1, poly2, elimVar, debug = False):
2247
    """
2248
    Compute the resultant for two polynomials for a given variable
2249
    and return the (poly1, poly2, resultant) if the resultant
2250
    is not 0.
2251
    Return () otherwise.
2252
    """
2253
    polynomialRing0    = poly1.parent()
2254
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2255
    if resultantInElimVar is None:
2256
        if debug:
2257
            print poly1
2258
            print poly2
2259
            print "have GCD = ", poly1.gcd(poly2)
2260
        return None
2261
    if resultantInElimVar.is_zero():
2262
        if debug:
2263
            print poly1
2264
            print poly2
2265
            print "have GCD = ", poly1.gcd(poly2)
2266
        return None
2267
    else:
2268
        if debug:
2269
            print poly1
2270
            print poly2
2271
            print "have resultant in t = ", resultantInElimVar
2272
        return resultantInElimVar
2273
# End slz_resultant.
2274
#
2275
def slz_resultant_tuple(poly1, poly2, elimVar):
2276
    """
2277
    Compute the resultant for two polynomials for a given variable
2278
    and return the (poly1, poly2, resultant) if the resultant
2279
    is not 0.
2280
    Return () otherwise.
2281
    """
2282
    polynomialRing0    = poly1.parent()
2283
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2284
    if resultantInElimVar.is_zero():
2285
        return ()
2286
    else:
2287
        return (poly1, poly2, resultantInElimVar)
2288
# End slz_resultant_tuple.
2289
#
2290
def slz_uniform(n):
2291
    """
2292
    Compute a uniform RV in {-1, 1}.
2293
    """
2294
    ## function getrandbits(n) generates a long int with n random bits.
2295
    return getrandbits(n) - 2^(n-1)
2296
# End slz_uniform.
2297
#
2298
sys.stderr.write("\t...sageSLZ loaded\n")
2299