Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (104,8 ko)

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

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

    
7
Examples:
8

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

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

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

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

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

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

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

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

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

    
1622
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1623
    # Create a polynomial over the rationals.
1624
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1625
    return(ratPolynomialRing(polyOfFloat))
1626
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1627

    
1628
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1629
    """
1630
     Create a polynomial over the rationals where all denominators are
1631
     powers of two.
1632
    """
1633
    polyVariable = polyOfFloat.variables()[0] 
1634
    RPR = QQ[str(polyVariable)]
1635
    polyCoeffs      = polyOfFloat.coefficients()
1636
    #print polyCoeffs
1637
    polyExponents   = polyOfFloat.exponents()
1638
    #print polyExponents
1639
    polyDenomPtwoCoeffs = []
1640
    for coeff in polyCoeffs:
1641
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1642
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1643
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1644
    ratPoly = RPR(0)
1645
    #print type(ratPoly)
1646
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1647
    #  The coefficient becomes plainly wrong when exponent == 0.
1648
    #  No clue as to why.
1649
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1650
        ratPoly += coeff * RPR(polyVariable^exponent)
1651
    return ratPoly
1652
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1653

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

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

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

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

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

    
2186
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
2187
                                          precision):
2188
    """
2189
    Makes a variable substitution into the input polynomial so that the output
2190
    polynomial can take integer arguments.
2191
    All variables of the input polynomial "have precision p". That is to say
2192
    that they are rationals with denominator == 2^(precision - 1): 
2193
    x = y/2^(precision - 1).
2194
    We "incorporate" these denominators into the coefficients with, 
2195
    respectively, the "right" power.
2196
    """
2197
    polynomialField = ratPolyOfRat.parent()
2198
    polynomialVariable = ratPolyOfRat.variables()[0]
2199
    #print "The polynomial field is:", polynomialField
2200
    return \
2201
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
2202
                                   polynomialVariable/2^(precision-1)}))
2203
    
2204
# End slz_rat_poly_of_rat_to_rat_poly_of_int
2205

    
2206
def slz_reduce_and_test_base(matrixToReduce,
2207
                             nAtAlpha,
2208
                             monomialsCountSqrt):
2209
    """
2210
    Reduces the basis, tests the Coppersmith condition and returns
2211
    a list of rows that comply with the condition.
2212
    """
2213
    ccComplientRowsList = []
2214
    reducedMatrix = matrixToReduce.LLL(None)
2215
    if not reducedMatrix.is_LLL_reduced():
2216
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
2217
    else:
2218
        print "reducedMatrix is actually reduced."
2219
    print "N^alpha:", nAtAlpha.n()
2220
    rowIndex = 0
2221
    for row in reducedMatrix.rows():
2222
        l2Norm = row.norm(2)
2223
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
2224
                monomialsCountSqrt.n(), ". Is vector OK?", 
2225
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
2226
            ccComplientRowsList.append(row)
2227
            print "True"
2228
        else:
2229
            print "False"
2230
    # End for
2231
    return ccComplientRowsList
2232
# End slz_reduce_and_test_base
2233

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