Statistiques
| Révision :

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

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

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

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

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

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

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

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

    
1332
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1333
    # Create a polynomial over the rationals.
1334
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1335
    return(ratPolynomialRing(polyOfFloat))
1336
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1337

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

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

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

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

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

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

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

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

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

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