Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (89,17 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
print "sageSLZ loading..."
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_with_lattice_volume(inputPolynomial,
253
                                                                    alpha,
254
                                                                    N,
255
                                                                    iBound,
256
                                                                    tBound):
257
    """
258
    For a given set of arguments (see below), compute a list
259
    of "reduced polynomials" that could be used to compute roots
260
    of the inputPolynomial.
261
    Print the volume of the initial basis as well.
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
##    polynomialsList = \
290
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
291
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
292
    polynomialsList = \
293
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
294
                                             alpha,
295
                                             N,
296
                                             iBound,
297
                                             tBound,
298
                                             0)
299
    #print "Polynomials list:", polynomialsList
300
    ## Building the proto matrix.
301
    knownMonomials = []
302
    protoMatrix    = []
303
    for poly in polynomialsList:
304
        spo_add_polynomial_coeffs_to_matrix_row(poly,
305
                                                knownMonomials,
306
                                                protoMatrix,
307
                                                0)
308
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
309
    matrixToReduceTranspose = matrixToReduce.transpose()
310
    squareMatrix = matrixToReduce * matrixToReduceTranspose
311
    squareMatDet = det(squareMatrix)
312
    latticeVolume = sqrt(squareMatDet)
313
    print "Lattice volume:", latticeVolume.n()
314
    print "Lattice volume / N:", (latticeVolume/N).n()
315
    #print matrixToReduce
316
    ## Reduction and checking.
317
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
318
    #  error message issued when previous code was used.
319
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
320
    reductionTimeStart = cputime() 
321
    reducedMatrix = matrixToReduce.LLL(fp=None)
322
    reductionTime = cputime(reductionTimeStart)
323
    print "Reduction time:", reductionTime
324
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
325
    if not isLLLReduced:
326
        return None
327
    monomialsCount     = len(knownMonomials)
328
    monomialsCountSqrt = sqrt(monomialsCount)
329
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
330
    #print reducedMatrix
331
    ## Check the Coppersmith condition for each row and build the reduced 
332
    #  polynomials.
333
    ccReducedPolynomialsList = []
334
    for row in reducedMatrix.rows():
335
        l2Norm = row.norm(2)
336
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
337
            #print (l2Norm * monomialsCountSqrt).n()
338
            #print l2Norm.n()
339
            ccReducedPolynomial = \
340
                slz_compute_reduced_polynomial(row,
341
                                               knownMonomials,
342
                                               iVariable,
343
                                               iBound,
344
                                               tVariable,
345
                                               tBound)
346
            if not ccReducedPolynomial is None:
347
                ccReducedPolynomialsList.append(ccReducedPolynomial)
348
        else:
349
            #print l2Norm.n() , ">", nAtAlpha
350
            pass
351
    if len(ccReducedPolynomialsList) < 2:
352
        print "Less than 2 Coppersmith condition compliant vectors."
353
        return ()
354
    #print ccReducedPolynomialsList
355
    return ccReducedPolynomialsList
356
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
357

    
358
def slz_compute_initial_lattice_matrix(inputPolynomial,
359
                                       alpha,
360
                                       N,
361
                                       iBound,
362
                                       tBound,
363
                                       debug = False):
364
    """
365
    For a given set of arguments (see below), compute the initial lattice
366
    that could be reduced. 
367
    INPUT:
368
    
369
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
370
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
371
    - "N" -- the modulus;
372
    - "iBound" -- the bound on the first variable;
373
    - "tBound" -- the bound on the second variable.
374
    
375
    OUTPUT:
376
    
377
    A list of bivariate integer polynomial obtained using the Coppersmith
378
    algorithm. The polynomials correspond to the rows of the LLL-reduce
379
    reduced base that comply with the Coppersmith condition.
380
    """
381
    # Arguments check.
382
    if iBound == 0 or tBound == 0:
383
        return None
384
    # End arguments check.
385
    nAtAlpha = N^alpha
386
    ## Building polynomials for matrix.
387
    polyRing   = inputPolynomial.parent()
388
    # Whatever the 2 variables are actually called, we call them
389
    # 'i' and 't' in all the variable names.
390
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
391
    #print polyVars[0], type(polyVars[0])
392
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
393
                                              tVariable:tVariable * tBound})
394
    polynomialsList = \
395
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
396
                                             alpha,
397
                                             N,
398
                                             iBound,
399
                                             tBound,
400
                                             0)
401
    #print "Polynomials list:", polynomialsList
402
    ## Building the proto matrix.
403
    knownMonomials = []
404
    protoMatrix    = []
405
    for poly in polynomialsList:
406
        spo_add_polynomial_coeffs_to_matrix_row(poly,
407
                                                knownMonomials,
408
                                                protoMatrix,
409
                                                0)
410
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
411
    if debug:
412
        print "Initial basis polynomials"
413
        for poly in polynomialsList:
414
            print poly
415
    return matrixToReduce
416
# End slz_compute_initial_lattice_matrix.
417

    
418
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
419
                                                 alpha,
420
                                                 N,
421
                                                 iBound,
422
                                                 tBound):
423
    """
424
    For a given set of arguments (see below), compute the polynomial modular 
425
    roots, if any.
426
    
427
    """
428
    # Arguments check.
429
    if iBound == 0 or tBound == 0:
430
        return set()
431
    # End arguments check.
432
    nAtAlpha = N^alpha
433
    ## Building polynomials for matrix.
434
    polyRing   = inputPolynomial.parent()
435
    # Whatever the 2 variables are actually called, we call them
436
    # 'i' and 't' in all the variable names.
437
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
438
    ccReducedPolynomialsList = \
439
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
440
                                                     alpha,
441
                                                     N,
442
                                                     iBound,
443
                                                     tBound)
444
    if len(ccReducedPolynomialsList) == 0:
445
        return set()   
446
    ## Create the valid (poly1 and poly2 are algebraically independent) 
447
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
448
    # Try to mix and match all the polynomial pairs built from the 
449
    # ccReducedPolynomialsList to obtain non zero resultants.
450
    resultantsInITuplesList = []
451
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
452
        for polyInnerIndex in xrange(polyOuterIndex+1, 
453
                                     len(ccReducedPolynomialsList)):
454
            # Compute the resultant in resultants in the
455
            # first variable (is it the optimal choice?).
456
            resultantInI = \
457
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
458
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
459
            #print "Resultant", resultantInI
460
            # Test algebraic independence.
461
            if not resultantInI.is_zero():
462
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
463
                                                 ccReducedPolynomialsList[polyInnerIndex],
464
                                                 resultantInI))
465
    # If no non zero resultant was found: we can't get no algebraically 
466
    # independent polynomials pair. Give up!
467
    if len(resultantsInITuplesList) == 0:
468
        return set()
469
    #print resultantsInITuplesList
470
    # Compute the roots.
471
    Zi = ZZ[str(iVariable)]
472
    Zt = ZZ[str(tVariable)]
473
    polynomialRootsSet = set()
474
    # First, solve in the second variable since resultants are in the first
475
    # variable.
476
    for resultantInITuple in resultantsInITuplesList:
477
        tRootsList = Zt(resultantInITuple[2]).roots()
478
        # For each tRoot, compute the corresponding iRoots and check 
479
        # them in the input polynomial.
480
        for tRoot in tRootsList:
481
            #print "tRoot:", tRoot
482
            # Roots returned by root() are (value, multiplicity) tuples.
483
            iRootsList = \
484
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
485
            print iRootsList
486
            # The iRootsList can be empty, hence the test.
487
            if len(iRootsList) != 0:
488
                for iRoot in iRootsList:
489
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
490
                    # polyEvalModN must be an integer.
491
                    if polyEvalModN == int(polyEvalModN):
492
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
493
    return polynomialRootsSet
494
# End slz_compute_integer_polynomial_modular_roots.
495
#
496
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
497
                                                 alpha,
498
                                                 N,
499
                                                 iBound,
500
                                                 tBound):
501
    """
502
    For a given set of arguments (see below), compute the polynomial modular 
503
    roots, if any.
504
    This version differs in the way resultants are computed.   
505
    """
506
    # Arguments check.
507
    if iBound == 0 or tBound == 0:
508
        return set()
509
    # End arguments check.
510
    nAtAlpha = N^alpha
511
    ## Building polynomials for matrix.
512
    polyRing   = inputPolynomial.parent()
513
    # Whatever the 2 variables are actually called, we call them
514
    # 'i' and 't' in all the variable names.
515
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
516
    #print polyVars[0], type(polyVars[0])
517
    ccReducedPolynomialsList = \
518
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
519
                                                     alpha,
520
                                                     N,
521
                                                     iBound,
522
                                                     tBound)
523
    if len(ccReducedPolynomialsList) == 0:
524
        return set()   
525
    ## Create the valid (poly1 and poly2 are algebraically independent) 
526
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
527
    # Try to mix and match all the polynomial pairs built from the 
528
    # ccReducedPolynomialsList to obtain non zero resultants.
529
    resultantsInTTuplesList = []
530
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
531
        for polyInnerIndex in xrange(polyOuterIndex+1, 
532
                                     len(ccReducedPolynomialsList)):
533
            # Compute the resultant in resultants in the
534
            # first variable (is it the optimal choice?).
535
            resultantInT = \
536
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
537
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
538
            #print "Resultant", resultantInT
539
            # Test algebraic independence.
540
            if not resultantInT.is_zero():
541
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
542
                                                 ccReducedPolynomialsList[polyInnerIndex],
543
                                                 resultantInT))
544
    # If no non zero resultant was found: we can't get no algebraically 
545
    # independent polynomials pair. Give up!
546
    if len(resultantsInTTuplesList) == 0:
547
        return set()
548
    #print resultantsInITuplesList
549
    # Compute the roots.
550
    Zi = ZZ[str(iVariable)]
551
    Zt = ZZ[str(tVariable)]
552
    polynomialRootsSet = set()
553
    # First, solve in the second variable since resultants are in the first
554
    # variable.
555
    for resultantInTTuple in resultantsInTTuplesList:
556
        iRootsList = Zi(resultantInTTuple[2]).roots()
557
        # For each iRoot, compute the corresponding tRoots and check 
558
        # them in the input polynomial.
559
        for iRoot in iRootsList:
560
            #print "iRoot:", iRoot
561
            # Roots returned by root() are (value, multiplicity) tuples.
562
            tRootsList = \
563
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
564
            print tRootsList
565
            # The tRootsList can be empty, hence the test.
566
            if len(tRootsList) != 0:
567
                for tRoot in tRootsList:
568
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
569
                    # polyEvalModN must be an integer.
570
                    if polyEvalModN == int(polyEvalModN):
571
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
572
    return polynomialRootsSet
573
# End slz_compute_integer_polynomial_modular_roots_2.
574
#
575
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
576
                                        upperBoundSa, approxAccurSa, 
577
                                        precSa=None):
578
    """
579
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
580
    a polynomial that approximates the function on a an interval starting
581
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
582
    approximates with the expected precision.
583
    The interval upper bound is lowered until the expected approximation 
584
    precision is reached.
585
    The polynomial, the bounds, the center of the interval and the error
586
    are returned.
587
    OUTPUT:
588
    A tuple made of 4 Sollya objects:
589
    - a polynomial;
590
    - an range (an interval, not in the sense of number given as an interval);
591
    - the center of the interval;
592
    - the maximum error in the approximation of the input functionSo by the
593
      output polynomial ; this error <= approxAccurSaS.
594
    
595
    """
596
    #print"In slz_compute_polynomial_and_interval..."
597
    ## Superficial argument check.
598
    if lowerBoundSa > upperBoundSa:
599
        return None
600
    ## Change Sollya precision, if requested.
601
    if precSa is None:
602
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
603
        #print "Computed internal precision:", precSa
604
        if precSa < 192:
605
            precSa = 192
606
    sollyaPrecChanged = False
607
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
608
    if precSa > initialSollyaPrecSa:
609
        if precSa <= 2:
610
            print inspect.stack()[0][3], ": precision change <=2 requested."
611
        pobyso_set_prec_sa_so(precSa)
612
        sollyaPrecChanged = True
613
    RRR = lowerBoundSa.parent()
614
    intervalShrinkConstFactorSa = RRR('0.9')
615
    absoluteErrorTypeSo = pobyso_absolute_so_so()
616
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
617
    currentUpperBoundSa = upperBoundSa
618
    currentLowerBoundSa = lowerBoundSa
619
    # What we want here is the polynomial without the variable change, 
620
    # since our actual variable will be x-intervalCenter defined over the 
621
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
622
    (polySo, intervalCenterSo, maxErrorSo) = \
623
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
624
                                                    currentRangeSo, 
625
                                                    absoluteErrorTypeSo)
626
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
627
    while maxErrorSa > approxAccurSa:
628
        print "++Approximation error:", maxErrorSa.n()
629
        sollya_lib_clear_obj(polySo)
630
        sollya_lib_clear_obj(intervalCenterSo)
631
        sollya_lib_clear_obj(maxErrorSo)
632
        # Very empirical shrinking factor.
633
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
634
        print "Shrink factor:", \
635
              shrinkFactorSa.n(), \
636
              intervalShrinkConstFactorSa
637
        print
638
        #errorRatioSa = approxAccurSa/maxErrorSa
639
        #print "Error ratio: ", errorRatioSa
640
        # Make sure interval shrinks.
641
        if shrinkFactorSa > intervalShrinkConstFactorSa:
642
            actualShrinkFactorSa = intervalShrinkConstFactorSa
643
            #print "Fixed"
644
        else:
645
            actualShrinkFactorSa = shrinkFactorSa
646
            #print "Computed",shrinkFactorSa,maxErrorSa
647
            #print shrinkFactorSa, maxErrorSa
648
        #print "Shrink factor", actualShrinkFactorSa
649
        currentUpperBoundSa = currentLowerBoundSa + \
650
                                (currentUpperBoundSa - currentLowerBoundSa) * \
651
                                actualShrinkFactorSa
652
        #print "Current upper bound:", currentUpperBoundSa
653
        sollya_lib_clear_obj(currentRangeSo)
654
        # Check what is left with the bounds.
655
        if currentUpperBoundSa <= currentLowerBoundSa or \
656
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
657
            sollya_lib_clear_obj(absoluteErrorTypeSo)
658
            print "Can't find an interval."
659
            print "Use either or both a higher polynomial degree or a higher",
660
            print "internal precision."
661
            print "Aborting!"
662
            if sollyaPrecChanged:
663
                pobyso_set_prec_so_so(initialSollyaPrecSo)
664
            sollya_lib_clear_obj(initialSollyaPrecSo)
665
            return None
666
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
667
                                                      currentUpperBoundSa)
668
        # print "New interval:",
669
        # pobyso_autoprint(currentRangeSo)
670
        #print "Second Taylor expansion call."
671
        (polySo, intervalCenterSo, maxErrorSo) = \
672
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
673
                                                        currentRangeSo, 
674
                                                        absoluteErrorTypeSo)
675
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
676
        #print "Max errorSo:",
677
        #pobyso_autoprint(maxErrorSo)
678
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
679
        #print "Max errorSa:", maxErrorSa
680
        #print "Sollya prec:",
681
        #pobyso_autoprint(sollya_lib_get_prec(None))
682
    # End while
683
    sollya_lib_clear_obj(absoluteErrorTypeSo)
684
    if sollyaPrecChanged:
685
        pobyso_set_prec_so_so(initialSollyaPrecSo)
686
    sollya_lib_clear_obj(initialSollyaPrecSo)
687
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
688
# End slz_compute_polynomial_and_interval
689

    
690
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
691
                                        upperBoundSa, approxAccurSa, 
692
                                        sollyaPrecSa=None, debug=False):
693
    """
694
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
695
    a polynomial that approximates the function on a an interval starting
696
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
697
    approximates with the expected precision.
698
    The interval upper bound is lowered until the expected approximation 
699
    precision is reached.
700
    The polynomial, the bounds, the center of the interval and the error
701
    are returned.
702
    OUTPUT:
703
    A tuple made of 4 Sollya objects:
704
    - a polynomial;
705
    - an range (an interval, not in the sense of number given as an interval);
706
    - the center of the interval;
707
    - the maximum error in the approximation of the input functionSo by the
708
      output polynomial ; this error <= approxAccurSaS.
709
    
710
    """
711
    #print"In slz_compute_polynomial_and_interval..."
712
    ## Superficial argument check.
713
    if lowerBoundSa > upperBoundSa:
714
        print inspect.stack()[0][3], ": lower bound is larger than upper bound. "
715
        return None
716
    ## Change Sollya precision, if requested.
717
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
718
    sollyaPrecChangedSa = False
719
    if sollyaPrecSa is None:
720
        sollyaPrecSa = initialSollyaPrecSa
721
    else:
722
        if sollyaPrecSa > initialSollyaPrecSa:
723
            if sollyaPrecSa <= 2:
724
                print inspect.stack()[0][3], ": precision change <= 2 requested."
725
            pobyso_set_prec_sa_so(sollyaPrecSa)
726
            sollyaPrecChangedSa = True
727
    ## Other initializations and data recovery.
728
    RRR = lowerBoundSa.parent()
729
    intervalShrinkConstFactorSa = RRR('0.9')
730
    absoluteErrorTypeSo = pobyso_absolute_so_so()
731
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
732
    currentUpperBoundSa = upperBoundSa
733
    currentLowerBoundSa = lowerBoundSa
734
    # What we want here is the polynomial without the variable change, 
735
    # since our actual variable will be x-intervalCenter defined over the 
736
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
737
    (polySo, intervalCenterSo, maxErrorSo) = \
738
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
739
                                                    currentRangeSo, 
740
                                                    absoluteErrorTypeSo)
741
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
742
    while maxErrorSa > approxAccurSa:
743
        print "++Approximation error:", maxErrorSa.n()
744
        sollya_lib_clear_obj(polySo)
745
        sollya_lib_clear_obj(intervalCenterSo)
746
        sollya_lib_clear_obj(maxErrorSo)
747
        # Very empirical shrinking factor.
748
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
749
        print "Shrink factor:", \
750
              shrinkFactorSa.n(), \
751
              intervalShrinkConstFactorSa
752
        print
753
        #errorRatioSa = approxAccurSa/maxErrorSa
754
        #print "Error ratio: ", errorRatioSa
755
        # Make sure interval shrinks.
756
        if shrinkFactorSa > intervalShrinkConstFactorSa:
757
            actualShrinkFactorSa = intervalShrinkConstFactorSa
758
            #print "Fixed"
759
        else:
760
            actualShrinkFactorSa = shrinkFactorSa
761
            #print "Computed",shrinkFactorSa,maxErrorSa
762
            #print shrinkFactorSa, maxErrorSa
763
        #print "Shrink factor", actualShrinkFactorSa
764
        currentUpperBoundSa = currentLowerBoundSa + \
765
                                (currentUpperBoundSa - currentLowerBoundSa) * \
766
                                actualShrinkFactorSa
767
        #print "Current upper bound:", currentUpperBoundSa
768
        sollya_lib_clear_obj(currentRangeSo)
769
        # Check what is left with the bounds.
770
        if currentUpperBoundSa <= currentLowerBoundSa or \
771
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
772
            sollya_lib_clear_obj(absoluteErrorTypeSo)
773
            print "Can't find an interval."
774
            print "Use either or both a higher polynomial degree or a higher",
775
            print "internal precision."
776
            print "Aborting!"
777
            if sollyaPrecChangedSa:
778
                pobyso_set_prec_so_so(initialSollyaPrecSo)
779
            sollya_lib_clear_obj(initialSollyaPrecSo)
780
            return None
781
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
782
                                                      currentUpperBoundSa)
783
        # print "New interval:",
784
        # pobyso_autoprint(currentRangeSo)
785
        #print "Second Taylor expansion call."
786
        (polySo, intervalCenterSo, maxErrorSo) = \
787
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
788
                                                        currentRangeSo, 
789
                                                        absoluteErrorTypeSo)
790
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
791
        #print "Max errorSo:",
792
        #pobyso_autoprint(maxErrorSo)
793
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
794
        #print "Max errorSa:", maxErrorSa
795
        #print "Sollya prec:",
796
        #pobyso_autoprint(sollya_lib_get_prec(None))
797
    # End while
798
    sollya_lib_clear_obj(absoluteErrorTypeSo)
799
    print inspect.stack()[0][3], "SollyaPrecSa:", sollyaPrecSa
800
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
801
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
802
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
803
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
804
    if debug:
805
        print "About to call polynomial rounding with:"
806
        print "polySo: ",           ; pobyso_autoprint(polySo)
807
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
808
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
809
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
810
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
811
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
812
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
813
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
814
    """
815
    # Naive rounding.
816
    (roundedPolySo, roundedPolyMaxErrSo) = \
817
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
818
                                                           functionSo,
819
                                                           intervalCenterSo,
820
                                                           currentRangeSo,
821
                                                           itpSo,
822
                                                           ftpSo,
823
                                                           maxPrecSo,
824
                                                           approxAccurSo)
825
    """
826
    # Proved rounding.
827
    (roundedPolySo, roundedPolyMaxErrSo) = \
828
        pobyso_round_coefficients_progressive_so_so(polySo, 
829
                                                    functionSo,
830
                                                    maxPrecSo,
831
                                                    currentRangeSo,
832
                                                    intervalCenterSo,
833
                                                    maxErrorSo,
834
                                                    approxAccurSo,
835
                                                    debug=False)
836
    #### Comment out the two next lines when polynomial rounding is activated.
837
    #roundedPolySo = sollya_lib_copy_obj(polySo)
838
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
839
    sollya_lib_clear_obj(polySo)
840
    sollya_lib_clear_obj(maxErrorSo)
841
    sollya_lib_clear_obj(itpSo)
842
    sollya_lib_clear_obj(ftpSo)
843
    sollya_lib_clear_obj(approxAccurSo)
844
    if sollyaPrecChangedSa:
845
        pobyso_set_prec_so_so(initialSollyaPrecSa)
846
    sollya_lib_clear_obj(initialSollyaPrecSa)
847
    if debug:
848
        print "1: ", ; pobyso_autoprint(roundedPolySo)
849
        print "2: ", ; pobyso_autoprint(currentRangeSo)
850
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
851
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
852
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
853
# End slz_compute_polynomial_and_interval_01
854

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

    
1006
def slz_compute_reduced_polynomial(matrixRow,
1007
                                    knownMonomials,
1008
                                    var1,
1009
                                    var1Bound,
1010
                                    var2,
1011
                                    var2Bound):
1012
    """
1013
    Compute a polynomial from a single reduced matrix row.
1014
    This function was introduced in order to avoid the computation of the
1015
    all the polynomials  from the full matrix (even those built from rows 
1016
    that do no verify the Coppersmith condition) as this may involves 
1017
    expensive operations over (large) integers.
1018
    """
1019
    ## Check arguments.
1020
    if len(knownMonomials) == 0:
1021
        return None
1022
    # varNounds can be zero since 0^0 returns 1.
1023
    if (var1Bound < 0) or (var2Bound < 0):
1024
        return None
1025
    ## Initialisations. 
1026
    polynomialRing = knownMonomials[0].parent() 
1027
    currentPolynomial = polynomialRing(0)
1028
    # TODO: use zip instead of indices.
1029
    for colIndex in xrange(0, len(knownMonomials)):
1030
        currentCoefficient = matrixRow[colIndex]
1031
        if currentCoefficient != 0:
1032
            #print "Current coefficient:", currentCoefficient
1033
            currentMonomial = knownMonomials[colIndex]
1034
            #print "Monomial as multivariate polynomial:", \
1035
            #currentMonomial, type(currentMonomial)
1036
            degreeInVar1 = currentMonomial.degree(var1)
1037
            #print "Degree in var1", var1, ":", degreeInVar1
1038
            degreeInVar2 = currentMonomial.degree(var2)
1039
            #print "Degree in var2", var2, ":", degreeInVar2
1040
            if degreeInVar1 > 0:
1041
                currentCoefficient = \
1042
                    currentCoefficient / (var1Bound^degreeInVar1)
1043
                #print "varBound1 in degree:", var1Bound^degreeInVar1
1044
                #print "Current coefficient(1)", currentCoefficient
1045
            if degreeInVar2 > 0:
1046
                currentCoefficient = \
1047
                    currentCoefficient / (var2Bound^degreeInVar2)
1048
                #print "Current coefficient(2)", currentCoefficient
1049
            #print "Current reduced monomial:", (currentCoefficient * \
1050
            #                                    currentMonomial)
1051
            currentPolynomial += (currentCoefficient * currentMonomial)
1052
            #print "Current polynomial:", currentPolynomial
1053
        # End if
1054
    # End for colIndex.
1055
    #print "Type of the current polynomial:", type(currentPolynomial)
1056
    return(currentPolynomial)
1057
# End slz_compute_reduced_polynomial
1058
#
1059
def slz_compute_reduced_polynomials(reducedMatrix,
1060
                                        knownMonomials,
1061
                                        var1,
1062
                                        var1Bound,
1063
                                        var2,
1064
                                        var2Bound):
1065
    """
1066
    Legacy function, use slz_compute_reduced_polynomials_list
1067
    """
1068
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1069
                                                knownMonomials,
1070
                                                var1,
1071
                                                var1Bound,
1072
                                                var2,
1073
                                                var2Bound)
1074
    )
1075
#
1076
def slz_compute_reduced_polynomials_list(reducedMatrix,
1077
                                         knownMonomials,
1078
                                         var1,
1079
                                         var1Bound,
1080
                                         var2,
1081
                                         var2Bound):
1082
    """
1083
    From a reduced matrix, holding the coefficients, from a monomials list,
1084
    from the bounds of each variable, compute the corresponding polynomials
1085
    scaled back by dividing by the "right" powers of the variables bounds.
1086
    
1087
    The elements in knownMonomials must be of the "right" polynomial type.
1088
    They set the polynomial type of the output polynomials in list.
1089
    @param reducedMatrix:  the reduced matrix as output from LLL;
1090
    @param kwnonMonomials: the ordered list of the monomials used to
1091
                           build the polynomials;
1092
    @param var1:           the first variable (of the "right" type);
1093
    @param var1Bound:      the first variable bound;
1094
    @param var2:           the second variable (of the "right" type);
1095
    @param var2Bound:      the second variable bound.
1096
    @return: a list of polynomials obtained with the reduced coefficients
1097
             and scaled down with the bounds
1098
    """
1099
    
1100
    # TODO: check input arguments.
1101
    reducedPolynomials = []
1102
    #print "type var1:", type(var1), " - type var2:", type(var2)
1103
    for matrixRow in reducedMatrix.rows():
1104
        currentPolynomial = 0
1105
        for colIndex in xrange(0, len(knownMonomials)):
1106
            currentCoefficient = matrixRow[colIndex]
1107
            if currentCoefficient != 0:
1108
                #print "Current coefficient:", currentCoefficient
1109
                currentMonomial = knownMonomials[colIndex]
1110
                parentRing = currentMonomial.parent()
1111
                #print "Monomial as multivariate polynomial:", \
1112
                #currentMonomial, type(currentMonomial)
1113
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1114
                #print "Degree in var", var1, ":", degreeInVar1
1115
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1116
                #print "Degree in var", var2, ":", degreeInVar2
1117
                if degreeInVar1 > 0:
1118
                    currentCoefficient /= var1Bound^degreeInVar1
1119
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1120
                    #print "Current coefficient(1)", currentCoefficient
1121
                if degreeInVar2 > 0:
1122
                    currentCoefficient /= var2Bound^degreeInVar2
1123
                    #print "Current coefficient(2)", currentCoefficient
1124
                #print "Current reduced monomial:", (currentCoefficient * \
1125
                #                                    currentMonomial)
1126
                currentPolynomial += (currentCoefficient * currentMonomial)
1127
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1128
                    #print "!!!! constant term !!!!"
1129
                #print "Current polynomial:", currentPolynomial
1130
            # End if
1131
        # End for colIndex.
1132
        #print "Type of the current polynomial:", type(currentPolynomial)
1133
        reducedPolynomials.append(currentPolynomial)
1134
    return reducedPolynomials 
1135
# End slz_compute_reduced_polynomials_list.
1136

    
1137
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1138
                                                   knownMonomials,
1139
                                                   var1,
1140
                                                   var1Bound,
1141
                                                   var2,
1142
                                                   var2Bound):
1143
    """
1144
    From a list of rows, holding the coefficients, from a monomials list,
1145
    from the bounds of each variable, compute the corresponding polynomials
1146
    scaled back by dividing by the "right" powers of the variables bounds.
1147
    
1148
    The elements in knownMonomials must be of the "right" polynomial type.
1149
    They set the polynomial type of the output polynomials in list.
1150
    @param rowsList:       the reduced matrix as output from LLL;
1151
    @param kwnonMonomials: the ordered list of the monomials used to
1152
                           build the polynomials;
1153
    @param var1:           the first variable (of the "right" type);
1154
    @param var1Bound:      the first variable bound;
1155
    @param var2:           the second variable (of the "right" type);
1156
    @param var2Bound:      the second variable bound.
1157
    @return: a list of polynomials obtained with the reduced coefficients
1158
             and scaled down with the bounds
1159
    """
1160
    
1161
    # TODO: check input arguments.
1162
    reducedPolynomials = []
1163
    #print "type var1:", type(var1), " - type var2:", type(var2)
1164
    for matrixRow in rowsList:
1165
        currentPolynomial = 0
1166
        for colIndex in xrange(0, len(knownMonomials)):
1167
            currentCoefficient = matrixRow[colIndex]
1168
            if currentCoefficient != 0:
1169
                #print "Current coefficient:", currentCoefficient
1170
                currentMonomial = knownMonomials[colIndex]
1171
                parentRing = currentMonomial.parent()
1172
                #print "Monomial as multivariate polynomial:", \
1173
                #currentMonomial, type(currentMonomial)
1174
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1175
                #print "Degree in var", var1, ":", degreeInVar1
1176
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1177
                #print "Degree in var", var2, ":", degreeInVar2
1178
                if degreeInVar1 > 0:
1179
                    currentCoefficient /= var1Bound^degreeInVar1
1180
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1181
                    #print "Current coefficient(1)", currentCoefficient
1182
                if degreeInVar2 > 0:
1183
                    currentCoefficient /= var2Bound^degreeInVar2
1184
                    #print "Current coefficient(2)", currentCoefficient
1185
                #print "Current reduced monomial:", (currentCoefficient * \
1186
                #                                    currentMonomial)
1187
                currentPolynomial += (currentCoefficient * currentMonomial)
1188
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1189
                    #print "!!!! constant term !!!!"
1190
                #print "Current polynomial:", currentPolynomial
1191
            # End if
1192
        # End for colIndex.
1193
        #print "Type of the current polynomial:", type(currentPolynomial)
1194
        reducedPolynomials.append(currentPolynomial)
1195
    return reducedPolynomials 
1196
# End slz_compute_reduced_polynomials_list_from_rows.
1197
#
1198
def slz_compute_scaled_function(functionSa,
1199
                                lowerBoundSa,
1200
                                upperBoundSa,
1201
                                floatingPointPrecSa,
1202
                                debug=False):
1203
    """
1204
    From a function, compute the scaled function whose domain
1205
    is included in [1, 2) and whose image is also included in [1,2).
1206
    Return a tuple: 
1207
    [0]: the scaled function
1208
    [1]: the scaled domain lower bound
1209
    [2]: the scaled domain upper bound
1210
    [3]: the scaled image lower bound
1211
    [4]: the scaled image upper bound
1212
    """
1213
    ## The variable can be called anything.
1214
    x = functionSa.variables()[0]
1215
    # Scalling the domain -> [1,2[.
1216
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1217
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1218
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1219
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1220
    if debug:
1221
        print "domainScalingExpression for argument :", \
1222
        invDomainScalingExpressionSa
1223
        print "function: ", functionSa
1224
    ff = functionSa.subs({x : domainScalingExpressionSa})
1225
    ## Bless expression as a function.
1226
    ff = ff.function(x)
1227
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1228
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1229
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1230
    scaledLowerBoundSa = \
1231
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1232
    scaledUpperBoundSa = \
1233
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1234
    if debug:
1235
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1236
              scaledUpperBoundSa
1237
    #
1238
    # Scalling the image -> [1,2[.
1239
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
1240
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
1241
    if flbSa <= fubSa: # Increasing
1242
        imageBinadeBottomSa = floor(flbSa.log2())
1243
    else: # Decreasing
1244
        imageBinadeBottomSa = floor(fubSa.log2())
1245
    if debug:
1246
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
1247
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
1248
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
1249
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
1250
    if debug:
1251
        print "imageScalingExpression for argument :", \
1252
              invImageScalingExpressionSa
1253
    iis = invImageScalingExpressionSa.function(x)
1254
    fff = iis.subs({x:ff})
1255
    if debug:
1256
        print "fff:", fff,
1257
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1258
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1259
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1260
# End slz_compute_scaled_function
1261

    
1262
def slz_fix_bounds_for_binades(lowerBound, 
1263
                               upperBound, 
1264
                               func = None, 
1265
                               domainDirection = -1,
1266
                               imageDirection  = -1):
1267
    """
1268
    Assuming the function is increasing or decreasing over the 
1269
    [lowerBound, upperBound] interval, return a lower bound lb and 
1270
    an upper bound ub such that:
1271
    - lb and ub belong to the same binade;
1272
    - func(lb) and func(ub) belong to the same binade.
1273
    domainDirection indicate how bounds move to fit into the same binade:
1274
    - a negative value move the upper bound down;
1275
    - a positive value move the lower bound up.
1276
    imageDirection indicate how bounds move in order to have their image
1277
    fit into the same binade, variation of the function is also condidered.
1278
    For an increasing function:
1279
    - negative value moves the upper bound down (and its image value as well);
1280
    - a positive values moves the lower bound up (and its image value as well);
1281
    For a decreasing function it is the other way round.
1282
    """
1283
    ## Arguments check
1284
    if lowerBound > upperBound:
1285
        return None
1286
    if lowerBound == upperBound:
1287
        return (lowerBound, upperBound)
1288
    if func is None:
1289
        return None
1290
    #
1291
    #varFunc = func.variables()[0]
1292
    lb = lowerBound
1293
    ub = upperBound
1294
    lbBinade = slz_compute_binade(lb) 
1295
    ubBinade = slz_compute_binade(ub)
1296
    ## Domain binade.
1297
    while lbBinade != ubBinade:
1298
        newIntervalWidth = (ub - lb) / 2
1299
        if domainDirection < 0:
1300
            ub = lb + newIntervalWidth
1301
            ubBinade = slz_compute_binade(ub)
1302
        else:
1303
            lb = lb + newIntervalWidth
1304
            lbBinade = slz_compute_binade(lb) 
1305
    ## Image binade.
1306
    if lb == ub:
1307
        return (lb, ub)
1308
    lbImg = func(lb)
1309
    ubImg = func(ub)
1310
    funcIsInc = (ubImg >= lbImg)
1311
    lbImgBinade = slz_compute_binade(lbImg)
1312
    ubImgBinade = slz_compute_binade(ubImg)
1313
    while lbImgBinade != ubImgBinade:
1314
        newIntervalWidth = (ub - lb) / 2
1315
        if imageDirection < 0:
1316
            if funcIsInc:
1317
                ub = lb + newIntervalWidth
1318
                ubImgBinade = slz_compute_binade(func(ub))
1319
                #print ubImgBinade
1320
            else:
1321
                lb = lb + newIntervalWidth
1322
                lbImgBinade = slz_compute_binade(func(lb))
1323
                #print lbImgBinade
1324
        else:
1325
            if funcIsInc:
1326
                lb = lb + newIntervalWidth
1327
                lbImgBinade = slz_compute_binade(func(lb))
1328
                #print lbImgBinade
1329
            else:
1330
                ub = lb + newIntervalWidth
1331
                ubImgBinade = slz_compute_binade(func(ub))
1332
                #print ubImgBinade
1333
    # End while lbImgBinade != ubImgBinade:
1334
    return (lb, ub)
1335
# End slz_fix_bounds_for_binades.
1336

    
1337
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1338
    # Create a polynomial over the rationals.
1339
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1340
    return(ratPolynomialRing(polyOfFloat))
1341
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1342

    
1343
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1344
    """
1345
     Create a polynomial over the rationals where all denominators are
1346
     powers of two.
1347
    """
1348
    polyVariable = polyOfFloat.variables()[0] 
1349
    RPR = QQ[str(polyVariable)]
1350
    polyCoeffs      = polyOfFloat.coefficients()
1351
    #print polyCoeffs
1352
    polyExponents   = polyOfFloat.exponents()
1353
    #print polyExponents
1354
    polyDenomPtwoCoeffs = []
1355
    for coeff in polyCoeffs:
1356
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1357
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1358
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1359
    ratPoly = RPR(0)
1360
    #print type(ratPoly)
1361
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1362
    #  The coefficient becomes plainly wrong when exponent == 0.
1363
    #  No clue as to why.
1364
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1365
        ratPoly += coeff * RPR(polyVariable^exponent)
1366
    return ratPoly
1367
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1368

    
1369
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1370
                                      lowerBoundSa, 
1371
                                      upperBoundSa, floatingPointPrecSa, 
1372
                                      internalSollyaPrecSa, approxPrecSa):
1373
    """
1374
    Under the assumption that:
1375
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1376
    - lowerBound and upperBound belong to the same binade.
1377
    from a:
1378
    - function;
1379
    - a degree
1380
    - a pair of bounds;
1381
    - the floating-point precision we work on;
1382
    - the internal Sollya precision;
1383
    - the requested approximation error
1384
    The initial interval is, possibly, splitted into smaller intervals.
1385
    It return a list of tuples, each made of:
1386
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1387
    - a second polynomial (with a changed variable f(x) = q(x))
1388
    - the approximation interval;
1389
    - the center, x0, of the interval;
1390
    - the corresponding approximation error.
1391
    TODO: fix endless looping for some parameters sets.
1392
    """
1393
    resultArray = []
1394
    # Set Sollya to the necessary internal precision.
1395
    sollyaPrecChangedSa = False
1396
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1397
    if internalSollyaPrecSa > currentSollyaPrecSa:
1398
        if internalSollyaPrecSa <= 2:
1399
            print inspect.stack()[0][3], ": precision change <=2 requested."
1400
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1401
        sollyaPrecChangedSa = True
1402
    #
1403
    x = functionSa.variables()[0] # Actual variable name can be anything.
1404
    # Scaled function: [1=,2] -> [1,2].
1405
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1406
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1407
                slz_compute_scaled_function(functionSa,                       \
1408
                                            lowerBoundSa,                     \
1409
                                            upperBoundSa,                     \
1410
                                            floatingPointPrecSa)
1411
    # In case bounds were in the negative real one may need to
1412
    # switch scaled bounds.
1413
    if scaledLowerBoundSa > scaledUpperBoundSa:
1414
        scaledLowerBoundSa, scaledUpperBoundSa = \
1415
            scaledUpperBoundSa, scaledLowerBoundSa
1416
        #print "Switching!"
1417
    print "Approximation accuracy: ", RR(approxAccurSa)
1418
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1419
    functionSo = \
1420
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1421
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1422
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1423
                                                  scaledUpperBoundSa)
1424

    
1425
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1426
                                              upperBoundSa.parent().precision()))
1427
    currentScaledLowerBoundSa = scaledLowerBoundSa
1428
    currentScaledUpperBoundSa = scaledUpperBoundSa
1429
    while True:
1430
        ## Compute the first Taylor expansion.
1431
        print "Computing a Taylor expansion..."
1432
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1433
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1434
                                        currentScaledLowerBoundSa, 
1435
                                        currentScaledUpperBoundSa,
1436
                                        approxAccurSa, internalSollyaPrecSa)
1437
        print "...done."
1438
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1439
        #  This value goes to the first variable: polySo. Other variables are
1440
        #  not assigned and should not be tested.
1441
        if polySo is None:
1442
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1443
            if precChangedSa:
1444
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1445
            sollya_lib_clear_obj(initialSollyaPrecSo)
1446
            sollya_lib_clear_obj(functionSo)
1447
            sollya_lib_clear_obj(degreeSo)
1448
            sollya_lib_clear_obj(scaledBoundsSo)
1449
            return None
1450
        ## Add to the result array.
1451
        ### Change variable stuff in Sollya x -> x0-x.
1452
        changeVarExpressionSo = \
1453
            sollya_lib_build_function_sub( \
1454
                              sollya_lib_build_function_free_variable(), 
1455
                              sollya_lib_copy_obj(intervalCenterSo))
1456
        polyVarChangedSo = \
1457
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1458
        #### Get rid of the variable change Sollya stuff. 
1459
        sollya_lib_clear_obj(changeVarExpressionSo)
1460
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1461
                            intervalCenterSo, maxErrorSo))
1462
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1463
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1464
        print "Computed approximation error:", errorSa.n(digits=10)
1465
        # If the error and interval are OK a the first try, just return.
1466
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1467
            (errorSa <= approxAccurSa):
1468
            if precChangedSa:
1469
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1470
            sollya_lib_clear_obj(initialSollyaPrecSo)
1471
            sollya_lib_clear_obj(functionSo)
1472
            sollya_lib_clear_obj(degreeSo)
1473
            sollya_lib_clear_obj(scaledBoundsSo)
1474
            #print "Approximation error:", errorSa
1475
            return resultArray
1476
        ## The returned interval upper bound does not reach the requested upper
1477
        #  upper bound: compute the next upper bound.
1478
        ## The following ratio is always >= 1. If errorSa, we may want to
1479
        #  enlarge the interval
1480
        currentErrorRatio = approxAccurSa / errorSa
1481
        ## --|--------------------------------------------------------------|--
1482
        #  --|--------------------|--------------------------------------------
1483
        #  --|----------------------------|------------------------------------
1484
        ## Starting point for the next upper bound.
1485
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1486
        # Compute the increment. 
1487
        newBoundsWidthSa = \
1488
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1489
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1490
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1491
        # Take into account the original interval upper bound.                     
1492
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1493
            currentScaledUpperBoundSa = scaledUpperBoundSa
1494
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1495
            print "Can't shrink the interval anymore!"
1496
            print "You should consider increasing the Sollya internal precision"
1497
            print "or the polynomial degree."
1498
            print "Giving up!"
1499
            if precChangedSa:
1500
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1501
            sollya_lib_clear_obj(initialSollyaPrecSo)
1502
            sollya_lib_clear_obj(functionSo)
1503
            sollya_lib_clear_obj(degreeSo)
1504
            sollya_lib_clear_obj(scaledBoundsSo)
1505
            return None
1506
        # Compute the other expansions.
1507
        # Test for insufficient precision.
1508
# End slz_get_intervals_and_polynomials
1509

    
1510
def slz_interval_scaling_expression(boundsInterval, expVar):
1511
    """
1512
    Compute the scaling expression to map an interval that spans at most
1513
    a single binade into [1, 2) and the inverse expression as well.
1514
    If the interval spans more than one binade, result is wrong since
1515
    scaling is only based on the lower bound.
1516
    Not very sure that the transformation makes sense for negative numbers.
1517
    """
1518
    # The "one of the bounds is 0" special case. Here we consider
1519
    # the interval as the subnormals binade.
1520
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1521
        # The upper bound is (or should be) positive.
1522
        if boundsInterval.endpoints()[0] == 0:
1523
            if boundsInterval.endpoints()[1] == 0:
1524
                return None
1525
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1526
            l2     = boundsInterval.endpoints()[1].abs().log2()
1527
            # The upper bound is a power of two
1528
            if binade == l2:
1529
                scalingCoeff    = 2^(-binade)
1530
                invScalingCoeff = 2^(binade)
1531
                scalingOffset   = 1
1532
                return \
1533
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1534
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1535
            else:
1536
                scalingCoeff    = 2^(-binade-1)
1537
                invScalingCoeff = 2^(binade+1)
1538
                scalingOffset   = 1
1539
                return((scalingCoeff * expVar + scalingOffset),\
1540
                    ((expVar - scalingOffset) * invScalingCoeff))
1541
        # The lower bound is (or should be) negative.
1542
        if boundsInterval.endpoints()[1] == 0:
1543
            if boundsInterval.endpoints()[0] == 0:
1544
                return None
1545
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1546
            l2     = boundsInterval.endpoints()[0].abs().log2()
1547
            # The upper bound is a power of two
1548
            if binade == l2:
1549
                scalingCoeff    = -2^(-binade)
1550
                invScalingCoeff = -2^(binade)
1551
                scalingOffset   = 1
1552
                return((scalingCoeff * expVar + scalingOffset),\
1553
                    ((expVar - scalingOffset) * invScalingCoeff))
1554
            else:
1555
                scalingCoeff    = -2^(-binade-1)
1556
                invScalingCoeff = -2^(binade+1)
1557
                scalingOffset   = 1
1558
                return((scalingCoeff * expVar + scalingOffset),\
1559
                   ((expVar - scalingOffset) * invScalingCoeff))
1560
    # General case
1561
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1562
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1563
    # We allow for a single exception in binade spanning is when the
1564
    # "outward bound" is a power of 2 and its binade is that of the
1565
    # "inner bound" + 1.
1566
    if boundsInterval.endpoints()[0] > 0:
1567
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1568
        if lbBinade != ubBinade:
1569
            print "Different binades."
1570
            if ubL2 != ubBinade:
1571
                print "Not a power of 2."
1572
                return None
1573
            elif abs(ubBinade - lbBinade) > 1:
1574
                print "Too large span:", abs(ubBinade - lbBinade)
1575
                return None
1576
    else:
1577
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1578
        if lbBinade != ubBinade:
1579
            print "Different binades."
1580
            if lbL2 != lbBinade:
1581
                print "Not a power of 2."
1582
                return None
1583
            elif abs(ubBinade - lbBinade) > 1:
1584
                print "Too large span:", abs(ubBinade - lbBinade)
1585
                return None
1586
    #print "Lower bound binade:", binade
1587
    if boundsInterval.endpoints()[0] > 0:
1588
        return \
1589
            ((2^(-lbBinade) * expVar).function(expVar),
1590
             (2^(lbBinade) * expVar).function(expVar))
1591
    else:
1592
        return \
1593
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1594
             (-(2^(ubBinade)) * expVar).function(expVar))
1595
"""
1596
    # Code sent to attic. Should be the base for a 
1597
    # "slz_interval_translate_expression" rather than scale.
1598
    # Extra control and special cases code  added in  
1599
    # slz_interval_scaling_expression could (should ?) be added to
1600
    # this new function.
1601
    # The scaling offset is only used for negative numbers.
1602
    # When the absolute value of the lower bound is < 0.
1603
    if abs(boundsInterval.endpoints()[0]) < 1:
1604
        if boundsInterval.endpoints()[0] >= 0:
1605
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1606
            invScalingCoeff = 1/scalingCoeff
1607
            return((scalingCoeff * expVar, 
1608
                    invScalingCoeff * expVar))
1609
        else:
1610
            scalingCoeff = \
1611
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1612
            scalingOffset = -3 * scalingCoeff
1613
            return((scalingCoeff * expVar + scalingOffset,
1614
                    1/scalingCoeff * expVar + 3))
1615
    else: 
1616
        if boundsInterval.endpoints()[0] >= 0:
1617
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1618
            scalingOffset = 0
1619
            return((scalingCoeff * expVar, 
1620
                    1/scalingCoeff * expVar))
1621
        else:
1622
            scalingCoeff = \
1623
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1624
            scalingOffset =  -3 * scalingCoeff
1625
            #scalingOffset = 0
1626
            return((scalingCoeff * expVar + scalingOffset,
1627
                    1/scalingCoeff * expVar + 3))
1628
"""
1629
# End slz_interval_scaling_expression
1630
   
1631
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1632
    """
1633
    Compute the Sage version of the Taylor polynomial and it's
1634
    companion data (interval, center...)
1635
    The input parameter is a five elements tuple:
1636
    - [0]: the polyomial (without variable change), as polynomial over a
1637
           real ring;
1638
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1639
           over a real ring;
1640
    - [2]: the interval (as Sollya range);
1641
    - [3]: the interval center;
1642
    - [4]: the approximation error. 
1643
    
1644
    The function returns a 5 elements tuple: formed with all the 
1645
    input elements converted into their Sollya counterpart.
1646
    """
1647
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
1648
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
1649
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
1650
    intervalSa = \
1651
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1652
    centerSa = \
1653
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1654
    errorSa = \
1655
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1656
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1657
    # End slz_interval_and_polynomial_to_sage
1658

    
1659
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1660
            targetPlusOnePrecRF = None, quasiExactRF = None):
1661
    """
1662
      Check if an element (argument) of the domain of function (function)
1663
      yields a HTRN case (rounding to next) for the target precision 
1664
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1665
      
1666
      The strategy is: 
1667
      - compute the image at high (quasi-exact) precision;
1668
      - round it to nearest to precision;
1669
      - round it to exactly to (precision+1), the computed number has two
1670
        midpoint neighbors;
1671
      - check the distance between these neighbors and the quasi-exact value;
1672
        - if none of them is closer than the targetAccuracy, return False,
1673
        - else return true.
1674
      - Powers of two are a special case when comparing the midpoint in
1675
        the 0 direction..
1676
    """
1677
    ## Arguments filtering. 
1678
    ## TODO: filter the first argument for consistence.
1679
    if targetRF is None:
1680
        targetRF = argument.parent()
1681
    ## Ditto for the real field holding the midpoints.
1682
    if targetPlusOnePrecRF is None:
1683
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1684
    ## If no quasiExactField is provided, create a high accuracy RealField.
1685
    if quasiExactRF is None:
1686
        quasiExactRF = RealField(1536)
1687
    function                      = function.function(function.variables()[0])
1688
    exactArgument                 = quasiExactRF(argument)
1689
    quasiExactValue               = function(exactArgument)
1690
    roundedValue                  = targetRF(quasiExactValue)
1691
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1692
    # Upper midpoint.
1693
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1694
    # Lower midpoint.
1695
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1696
    binade                        = slz_compute_binade(roundedValue)
1697
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1698
    #print "Argument:", argument
1699
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1700
    #print "Binade:", binade
1701
    #print "binadeCorrectedTargetAccuracy:", \
1702
    #binadeCorrectedTargetAccuracy.n(prec=100)
1703
    #print "binadeCorrectedTargetAccuracy:", \
1704
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1705
    #print "Upper midpoint:", \
1706
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1707
    #print "Rounded value :", \
1708
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1709
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1710
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1711
    #print "Lower midpoint:", \
1712
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1713
    ## Make quasiExactValue = 0 a special case to move it out of the way.
1714
    if quasiExactValue == 0:
1715
        return False
1716
    ## Begining of the general case : check with the midpoint of 
1717
    #  greatest absolute value.
1718
    if quasiExactValue > 0:
1719
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1720
           binadeCorrectedTargetAccuracy:
1721
            #print "Too close to the upper midpoint: ", \
1722
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1723
            #print argument.n().str(base=16, \
1724
            #  no_sci=False)
1725
            #print "Lower midpoint:", \
1726
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1727
            #print "Value         :", \
1728
            # quasiExactValue.n(prec=200).str(base=2)
1729
            #print "Upper midpoint:", \
1730
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1731
            return True
1732
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
1733
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1734
           binadeCorrectedTargetAccuracy:
1735
            #print "Too close to the upper midpoint: ", \
1736
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1737
            #print argument.n().str(base=16, \
1738
            #  no_sci=False)
1739
            #print "Lower midpoint:", \
1740
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1741
            #print "Value         :", \
1742
            #  quasiExactValue.n(prec=200).str(base=2)
1743
            #print "Upper midpoint:", \
1744
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1745
            #print
1746
            return True
1747
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1748
    ## For the midpoint of smaller absolute value, 
1749
    #  split cases with the powers of 2.
1750
    if sno_abs_is_power_of_two(roundedValue):
1751
        if quasiExactValue > 0:        
1752
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1753
               binadeCorrectedTargetAccuracy / 2:
1754
                #print "Lower midpoint:", \
1755
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1756
                #print "Value         :", \
1757
                #  quasiExactValue.n(prec=200).str(base=2)
1758
                #print "Upper midpoint:", \
1759
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1760
                #print
1761
                return True
1762
        else:
1763
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1764
              binadeCorrectedTargetAccuracy / 2:
1765
                #print "Lower midpoint:", \
1766
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1767
                #print "Value         :", 
1768
                #  quasiExactValue.n(prec=200).str(base=2)
1769
                #print "Upper midpoint:", 
1770
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1771
                #print
1772
                return True
1773
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1774
    else: ## Not a power of 2, regular comparison with the midpoint of 
1775
          #  smaller absolute value.
1776
        if quasiExactValue > 0:        
1777
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1778
              binadeCorrectedTargetAccuracy:
1779
                #print "Lower midpoint:", \
1780
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1781
                #print "Value         :", \
1782
                #  quasiExactValue.n(prec=200).str(base=2)
1783
                #print "Upper midpoint:", \
1784
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1785
                #print
1786
                return True
1787
        else: # quasiExactValue <= 0
1788
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1789
              binadeCorrectedTargetAccuracy:
1790
                #print "Lower midpoint:", \
1791
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1792
                #print "Value         :", \
1793
                #  quasiExactValue.n(prec=200).str(base=2)
1794
                #print "Upper midpoint:", \
1795
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1796
                #print
1797
                return True
1798
    #print "distance to the upper midpoint:", \
1799
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1800
    #print "distance to the lower midpoint:", \
1801
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1802
    return False
1803
# End slz_is_htrn
1804

    
1805
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1806
                                                precision,
1807
                                                targetHardnessToRound,
1808
                                                variable1,
1809
                                                variable2):
1810
    """
1811
    Creates a new multivariate polynomial with integer coefficients for use
1812
     with the Coppersmith method.
1813
    A the same time it computes :
1814
    - 2^K (N);
1815
    - 2^k (bound on the second variable)
1816
    - lcm
1817

    
1818
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1819
                         variables.
1820
    :param precision: the precision of the floating-point coefficients.
1821
    :param targetHardnessToRound: the hardness to round we want to check.
1822
    :param variable1: the first variable of the polynomial (an expression).
1823
    :param variable2: the second variable of the polynomial (an expression).
1824
    
1825
    :returns: a 4 elements tuple:
1826
                - the polynomial;
1827
                - the modulus (N);
1828
                - the t bound;
1829
                - the lcm used to compute the integral coefficients and the 
1830
                  module.
1831
    """
1832
    # Create a new integer polynomial ring.
1833
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1834
    # Coefficients are issued in the increasing power order.
1835
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1836
    # Print the reversed list for debugging.
1837
    #print
1838
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1839
    # Build the list of number we compute the lcm of.
1840
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1841
    #print "Coefficient denominators:", coefficientDenominators
1842
    coefficientDenominators.append(2^precision)
1843
    coefficientDenominators.append(2^(targetHardnessToRound))
1844
    leastCommonMultiple = lcm(coefficientDenominators)
1845
    # Compute the expression corresponding to the new polynomial
1846
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1847
    #print coefficientNumerators
1848
    polynomialExpression = 0
1849
    power = 0
1850
    # Iterate over two lists at the same time, stop when the shorter
1851
    # (is this case coefficientsNumerators) is 
1852
    # exhausted. Both lists are ordered in the order of increasing powers
1853
    # of variable1.
1854
    for numerator, denominator in \
1855
                        zip(coefficientNumerators, coefficientDenominators):
1856
        multiplicator = leastCommonMultiple / denominator 
1857
        newCoefficient = numerator * multiplicator
1858
        polynomialExpression += newCoefficient * variable1^power
1859
        power +=1
1860
    polynomialExpression += - variable2
1861
    return (IP(polynomialExpression),
1862
            leastCommonMultiple / 2^precision, # 2^K aka N.
1863
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1864
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1865
            leastCommonMultiple) # If we want to make test computations.
1866
        
1867
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1868

    
1869
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1870
                                          precision):
1871
    """
1872
    Makes a variable substitution into the input polynomial so that the output
1873
    polynomial can take integer arguments.
1874
    All variables of the input polynomial "have precision p". That is to say
1875
    that they are rationals with denominator == 2^(precision - 1): 
1876
    x = y/2^(precision - 1).
1877
    We "incorporate" these denominators into the coefficients with, 
1878
    respectively, the "right" power.
1879
    """
1880
    polynomialField = ratPolyOfRat.parent()
1881
    polynomialVariable = ratPolyOfRat.variables()[0]
1882
    #print "The polynomial field is:", polynomialField
1883
    return \
1884
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1885
                                   polynomialVariable/2^(precision-1)}))
1886
    
1887
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1888

    
1889
def slz_reduce_and_test_base(matrixToReduce,
1890
                             nAtAlpha,
1891
                             monomialsCountSqrt):
1892
    """
1893
    Reduces the basis, tests the Coppersmith condition and returns
1894
    a list of rows that comply with the condition.
1895
    """
1896
    ccComplientRowsList = []
1897
    reducedMatrix = matrixToReduce.LLL(None)
1898
    if not reducedMatrix.is_LLL_reduced():
1899
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1900
    else:
1901
        print "reducedMatrix is actually reduced."
1902
    print "N^alpha:", nAtAlpha.n()
1903
    rowIndex = 0
1904
    for row in reducedMatrix.rows():
1905
        l2Norm = row.norm(2)
1906
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1907
                monomialsCountSqrt.n(), ". Is vector OK?", 
1908
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1909
            ccComplientRowsList.append(row)
1910
            print "True"
1911
        else:
1912
            print "False"
1913
    # End for
1914
    return ccComplientRowsList
1915
# End slz_reduce_and_test_base
1916

    
1917
def slz_resultant(poly1, poly2, elimVar):
1918
    """
1919
    Compute the resultant for two polynomials for a given variable
1920
    and return the (poly1, poly2, resultant) if the resultant
1921
    is not 0.
1922
    Return () otherwise.
1923
    """
1924
    polynomialRing0    = poly1.parent()
1925
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1926
    if resultantInElimVar is None:
1927
        return None
1928
    if resultantInElimVar.is_zero():
1929
        return None
1930
    else:
1931
        return resultantInElimVar
1932
# End slz_resultant.
1933
#
1934
def slz_resultant_tuple(poly1, poly2, elimVar):
1935
    """
1936
    Compute the resultant for two polynomials for a given variable
1937
    and return the (poly1, poly2, resultant) if the resultant
1938
    is not 0.
1939
    Return () otherwise.
1940
    """
1941
    polynomialRing0    = poly1.parent()
1942
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1943
    if resultantInElimVar.is_zero():
1944
        return ()
1945
    else:
1946
        return (poly1, poly2, resultantInElimVar)
1947
# End slz_resultant_tuple.
1948
#
1949
print "\t...sageSLZ loaded"