Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (88,34 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
    sollyaPrecChanged = False
597
    (sollyaPrecSo, sollyaPrecSa) = pobyso_get_prec_so_so_sa()
598
    if precSa is None:
599
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
600
        #print "Computed internal precision:", precSa
601
        if precSa < 192:
602
            precSa = 192
603
    if precSa != sollyaPrecSa:
604
        precSo = pobyso_constant_from_int_sa_so(precSa)
605
        pobyso_set_prec_so_so(precSo)
606
        sollya_lib_clear_obj(precSo)
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(sollyaPrecSo)
659
                sollya_lib_clear_obj(sollyaPrecSo)
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(sollyaPrecSo)
681
        sollya_lib_clear_obj(sollyaPrecSo)
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
    sollyaPrecChangedSa = False
713
    if sollyaPrecSa is None:
714
        sollyaPrecSa = pobyso_get_prec_so_sa()
715
    else:
716
        initialSollyaPrecSa = pobyso_get_prec_so_sa()
717
        if sollyaPrecSa != initialSollyaPrecSa:
718
            initialSollyaPrecSo = pobyso_get_prec_so()
719
            pobyso_set_prec_sa_so(sollyaPrecSa)
720
            sollyaPrecChangedSa = True
721
    ## Other initializations and data recovery.
722
    RRR = lowerBoundSa.parent()
723
    intervalShrinkConstFactorSa = RRR('0.9')
724
    absoluteErrorTypeSo = pobyso_absolute_so_so()
725
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
726
    currentUpperBoundSa = upperBoundSa
727
    currentLowerBoundSa = lowerBoundSa
728
    # What we want here is the polynomial without the variable change, 
729
    # since our actual variable will be x-intervalCenter defined over the 
730
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
731
    (polySo, intervalCenterSo, maxErrorSo) = \
732
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
733
                                                    currentRangeSo, 
734
                                                    absoluteErrorTypeSo)
735
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
736
    while maxErrorSa > approxAccurSa:
737
        print "++Approximation error:", maxErrorSa.n()
738
        sollya_lib_clear_obj(polySo)
739
        sollya_lib_clear_obj(intervalCenterSo)
740
        sollya_lib_clear_obj(maxErrorSo)
741
        # Very empirical shrinking factor.
742
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
743
        print "Shrink factor:", \
744
              shrinkFactorSa.n(), \
745
              intervalShrinkConstFactorSa
746
        print
747
        #errorRatioSa = approxAccurSa/maxErrorSa
748
        #print "Error ratio: ", errorRatioSa
749
        # Make sure interval shrinks.
750
        if shrinkFactorSa > intervalShrinkConstFactorSa:
751
            actualShrinkFactorSa = intervalShrinkConstFactorSa
752
            #print "Fixed"
753
        else:
754
            actualShrinkFactorSa = shrinkFactorSa
755
            #print "Computed",shrinkFactorSa,maxErrorSa
756
            #print shrinkFactorSa, maxErrorSa
757
        #print "Shrink factor", actualShrinkFactorSa
758
        currentUpperBoundSa = currentLowerBoundSa + \
759
                                (currentUpperBoundSa - currentLowerBoundSa) * \
760
                                actualShrinkFactorSa
761
        #print "Current upper bound:", currentUpperBoundSa
762
        sollya_lib_clear_obj(currentRangeSo)
763
        # Check what is left with the bounds.
764
        if currentUpperBoundSa <= currentLowerBoundSa or \
765
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
766
            sollya_lib_clear_obj(absoluteErrorTypeSo)
767
            print "Can't find an interval."
768
            print "Use either or both a higher polynomial degree or a higher",
769
            print "internal precision."
770
            print "Aborting!"
771
            if sollyaPrecChangedSa:
772
                pobyso_set_prec_so_so(initialSollyaPrecSo)
773
                sollya_lib_clear_obj(initialSollyaPrecSo)
774
            return None
775
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
776
                                                      currentUpperBoundSa)
777
        # print "New interval:",
778
        # pobyso_autoprint(currentRangeSo)
779
        #print "Second Taylor expansion call."
780
        (polySo, intervalCenterSo, maxErrorSo) = \
781
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
782
                                                        currentRangeSo, 
783
                                                        absoluteErrorTypeSo)
784
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
785
        #print "Max errorSo:",
786
        #pobyso_autoprint(maxErrorSo)
787
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
788
        #print "Max errorSa:", maxErrorSa
789
        #print "Sollya prec:",
790
        #pobyso_autoprint(sollya_lib_get_prec(None))
791
    # End while
792
    sollya_lib_clear_obj(absoluteErrorTypeSo)
793
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
794
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
795
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
796
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
797
    if debug:
798
        print "About to call polynomial rounding with:"
799
        print "polySo: ",           ; pobyso_autoprint(polySo)
800
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
801
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
802
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
803
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
804
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
805
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
806
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
807
    """
808
    # Naive rounding.
809
    (roundedPolySo, roundedPolyMaxErrSo) = \
810
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
811
                                                           functionSo,
812
                                                           intervalCenterSo,
813
                                                           currentRangeSo,
814
                                                           itpSo,
815
                                                           ftpSo,
816
                                                           maxPrecSo,
817
                                                           approxAccurSo)
818
    """
819
    # Proved rounding.
820
    (roundedPolySo, roundedPolyMaxErrSo) = \
821
        pobyso_round_coefficients_progressive_so_so(polySo, 
822
                                                    functionSo,
823
                                                    maxPrecSo,
824
                                                    currentRangeSo,
825
                                                    intervalCenterSo,
826
                                                    maxErrorSo,
827
                                                    approxAccurSo,
828
                                                    debug=False)
829
    #### Comment out the two next lines when polynomial rounding is activated.
830
    #roundedPolySo = sollya_lib_copy_obj(polySo)
831
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
832
    sollya_lib_clear_obj(polySo)
833
    sollya_lib_clear_obj(maxErrorSo)
834
    sollya_lib_clear_obj(itpSo)
835
    sollya_lib_clear_obj(ftpSo)
836
    sollya_lib_clear_obj(approxAccurSo)
837
    if sollyaPrecChangedSa:
838
        pobyso_set_prec_so_so(initialSollyaPrecSa)
839
        sollya_lib_clear_obj(initialSollyaPrecSa)
840
    if debug:
841
        print "1: ", ; pobyso_autoprint(roundedPolySo)
842
        print "2: ", ; pobyso_autoprint(currentRangeSo)
843
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
844
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
845
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
846
# End slz_compute_polynomial_and_interval_01
847

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

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

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

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

    
1320
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1321
    # Create a polynomial over the rationals.
1322
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1323
    return(ratPolynomialRing(polyOfFloat))
1324
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1325

    
1326
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1327
    """
1328
     Create a polynomial over the rationals where all denominators are
1329
     powers of two.
1330
    """
1331
    polyVariable = polyOfFloat.variables()[0] 
1332
    RPR = QQ[str(polyVariable)]
1333
    polyCoeffs      = polyOfFloat.coefficients()
1334
    #print polyCoeffs
1335
    polyExponents   = polyOfFloat.exponents()
1336
    #print polyExponents
1337
    polyDenomPtwoCoeffs = []
1338
    for coeff in polyCoeffs:
1339
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1340
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1341
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1342
    ratPoly = RPR(0)
1343
    #print type(ratPoly)
1344
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1345
    #  The coefficient becomes plainly wrong when exponent == 0.
1346
    #  No clue as to why.
1347
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1348
        ratPoly += coeff * RPR(polyVariable^exponent)
1349
    return ratPoly
1350
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1351

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

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

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

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

    
1787
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1788
                                                precision,
1789
                                                targetHardnessToRound,
1790
                                                variable1,
1791
                                                variable2):
1792
    """
1793
    Creates a new multivariate polynomial with integer coefficients for use
1794
     with the Coppersmith method.
1795
    A the same time it computes :
1796
    - 2^K (N);
1797
    - 2^k (bound on the second variable)
1798
    - lcm
1799

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

    
1851
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1852
                                          precision):
1853
    """
1854
    Makes a variable substitution into the input polynomial so that the output
1855
    polynomial can take integer arguments.
1856
    All variables of the input polynomial "have precision p". That is to say
1857
    that they are rationals with denominator == 2^(precision - 1): 
1858
    x = y/2^(precision - 1).
1859
    We "incorporate" these denominators into the coefficients with, 
1860
    respectively, the "right" power.
1861
    """
1862
    polynomialField = ratPolyOfRat.parent()
1863
    polynomialVariable = ratPolyOfRat.variables()[0]
1864
    #print "The polynomial field is:", polynomialField
1865
    return \
1866
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1867
                                   polynomialVariable/2^(precision-1)}))
1868
    
1869
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1870

    
1871
def slz_reduce_and_test_base(matrixToReduce,
1872
                             nAtAlpha,
1873
                             monomialsCountSqrt):
1874
    """
1875
    Reduces the basis, tests the Coppersmith condition and returns
1876
    a list of rows that comply with the condition.
1877
    """
1878
    ccComplientRowsList = []
1879
    reducedMatrix = matrixToReduce.LLL(None)
1880
    if not reducedMatrix.is_LLL_reduced():
1881
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1882
    else:
1883
        print "reducedMatrix is actually reduced."
1884
    print "N^alpha:", nAtAlpha.n()
1885
    rowIndex = 0
1886
    for row in reducedMatrix.rows():
1887
        l2Norm = row.norm(2)
1888
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1889
                monomialsCountSqrt.n(), ". Is vector OK?", 
1890
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1891
            ccComplientRowsList.append(row)
1892
            print "True"
1893
        else:
1894
            print "False"
1895
    # End for
1896
    return ccComplientRowsList
1897
# End slz_reduce_and_test_base
1898

    
1899
def slz_resultant(poly1, poly2, elimVar):
1900
    """
1901
    Compute the resultant for two polynomials for a given variable
1902
    and return the (poly1, poly2, resultant) if the resultant
1903
    is not 0.
1904
    Return () otherwise.
1905
    """
1906
    polynomialRing0    = poly1.parent()
1907
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1908
    if resultantInElimVar is None:
1909
        return None
1910
    if resultantInElimVar.is_zero():
1911
        return None
1912
    else:
1913
        return resultantInElimVar
1914
# End slz_resultant.
1915
#
1916
def slz_resultant_tuple(poly1, poly2, elimVar):
1917
    """
1918
    Compute the resultant for two polynomials for a given variable
1919
    and return the (poly1, poly2, resultant) if the resultant
1920
    is not 0.
1921
    Return () otherwise.
1922
    """
1923
    polynomialRing0    = poly1.parent()
1924
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1925
    if resultantInElimVar.is_zero():
1926
        return ()
1927
    else:
1928
        return (poly1, poly2, resultantInElimVar)
1929
# End slz_resultant_tuple.
1930
#
1931
print "\t...sageSLZ loaded"