Statistiques
| Révision :

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

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

    
51
#
52
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
53
    """
54
    For given "real number", compute the bounds of the binade it belongs to.
55
    
56
    NOTE::
57
        When number >= 2^(emax+1), we return the "fake" binade 
58
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
59
        with interval [-infinity, -2^(emax+1)]. We want to distinguish
60
        this case from that of "really" invalid arguments.
61
        
62
    """
63
    # Check the parameters.
64
    # RealNumbers or RealNumber offspring only.
65
    # The exception construction is necessary since not all objects have
66
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
67
    try:
68
        classTree = [number.__class__] + number.mro()
69
        if not sage.rings.real_mpfr.RealNumber in classTree:
70
            return None
71
    except AttributeError:
72
        return None
73
    # Non zero negative integers only for emin.
74
    if emin >= 0 or int(emin) != emin:
75
        return None
76
    # Non zero positive integers only for emax.
77
    if emax <= 0 or int(emax) != emax:
78
        return None
79
    precision = number.precision()
80
    RF  = RealField(precision)
81
    if number == 0:
82
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
83
    # A more precise RealField is needed to avoid unwanted rounding effects
84
    # when computing number.log2().
85
    RRF = RealField(max(2048, 2 * precision))
86
    # number = 0 special case, the binade bounds are 
87
    # [0, 2^emin - 2^(emin-precision)]
88
    # Begin general case
89
    l2 = RRF(number).abs().log2()
90
    # Another special one: beyond largest representable -> "Fake" binade.
91
    if l2 >= emax + 1:
92
        if number > 0:
93
            return (RF(2^(emax+1)), RF(+infinity) )
94
        else:
95
            return (RF(-infinity), -RF(2^(emax+1)))
96
    # Regular case cont'd.
97
    offset = int(l2)
98
    # number.abs() >= 1.
99
    if l2 >= 0:
100
        if number >= 0:
101
            lb = RF(2^offset)
102
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
103
        else: #number < 0
104
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
105
            ub = -RF(2^offset)
106
    else: # log2 < 0, number.abs() < 1.
107
        if l2 < emin: # Denormal
108
           # print "Denormal:", l2
109
            if number >= 0:
110
                lb = RF(0)
111
                ub = RF(2^(emin)) - RF(2^(emin-precision))
112
            else: # number <= 0
113
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
114
                ub = RF(0)
115
        elif l2 > emin: # Normal number other than +/-2^emin.
116
            if number >= 0:
117
                if int(l2) == l2:
118
                    lb = RF(2^(offset))
119
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
120
                else:
121
                    lb = RF(2^(offset-1))
122
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
123
            else: # number < 0
124
                if int(l2) == l2: # Binade limit.
125
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
126
                    ub = -RF(2^(offset))
127
                else:
128
                    lb = -RF(2^(offset) - 2^(-precision+offset))
129
                    ub = -RF(2^(offset-1))
130
        else: # l2== emin, number == +/-2^emin
131
            if number >= 0:
132
                lb = RF(2^(offset))
133
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
134
            else: # number < 0
135
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
136
                ub = -RF(2^(offset))
137
    return (lb, ub)
138
# End slz_compute_binade_bounds
139
#
140
def slz_compute_coppersmith_reduced_polynomials(inputPolynomial,
141
                                                 alpha,
142
                                                 N,
143
                                                 iBound,
144
                                                 tBound):
145
    """
146
    For a given set of arguments (see below), compute a list
147
    of "reduced polynomials" that could be used to compute roots
148
    of the inputPolynomial.
149
    INPUT:
150
    
151
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
152
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
153
    - "N" -- the modulus;
154
    - "iBound" -- the bound on the first variable;
155
    - "tBound" -- the bound on the second variable.
156
    
157
    OUTPUT:
158
    
159
    A list of bivariate integer polynomial obtained using the Coppersmith
160
    algorithm. The polynomials correspond to the rows of the LLL-reduce
161
    reduced base that comply with the Coppersmith condition.
162
    """
163
    # Arguments check.
164
    if iBound == 0 or tBound == 0:
165
        return None
166
    # End arguments check.
167
    nAtAlpha = N^alpha
168
    ## Building polynomials for matrix.
169
    polyRing   = inputPolynomial.parent()
170
    # Whatever the 2 variables are actually called, we call them
171
    # 'i' and 't' in all the variable names.
172
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
173
    #print polyVars[0], type(polyVars[0])
174
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
175
                                              tVariable:tVariable * tBound})
176
    polynomialsList = \
177
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
178
                                             alpha,
179
                                             N,
180
                                             iBound,
181
                                             tBound,
182
                                             0)
183
    #print "Polynomials list:", polynomialsList
184
    ## Building the proto matrix.
185
    knownMonomials = []
186
    protoMatrix    = []
187
    for poly in polynomialsList:
188
        spo_add_polynomial_coeffs_to_matrix_row(poly,
189
                                                knownMonomials,
190
                                                protoMatrix,
191
                                                0)
192
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
193
    #print matrixToReduce
194
    ## Reduction and checking.
195
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
196
    #  error message issued when previous code was used.
197
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
198
    reducedMatrix = matrixToReduce.LLL(fp=None)
199
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
200
    if not isLLLReduced:
201
        return None
202
    monomialsCount     = len(knownMonomials)
203
    monomialsCountSqrt = sqrt(monomialsCount)
204
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
205
    #print reducedMatrix
206
    ## Check the Coppersmith condition for each row and build the reduced 
207
    #  polynomials.
208
    ccReducedPolynomialsList = []
209
    for row in reducedMatrix.rows():
210
        l2Norm = row.norm(2)
211
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
212
            #print (l2Norm * monomialsCountSqrt).n()
213
            #print l2Norm.n()
214
            ccReducedPolynomial = \
215
                slz_compute_reduced_polynomial(row,
216
                                               knownMonomials,
217
                                               iVariable,
218
                                               iBound,
219
                                               tVariable,
220
                                               tBound)
221
            if not ccReducedPolynomial is None:
222
                ccReducedPolynomialsList.append(ccReducedPolynomial)
223
        else:
224
            #print l2Norm.n() , ">", nAtAlpha
225
            pass
226
    if len(ccReducedPolynomialsList) < 2:
227
        print "Less than 2 Coppersmith condition compliant vectors."
228
        return ()
229
    #print ccReducedPolynomialsList
230
    return ccReducedPolynomialsList
231
# End slz_compute_coppersmith_reduced_polynomials
232

    
233
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
234
                                                                    alpha,
235
                                                                    N,
236
                                                                    iBound,
237
                                                                    tBound):
238
    """
239
    For a given set of arguments (see below), compute a list
240
    of "reduced polynomials" that could be used to compute roots
241
    of the inputPolynomial.
242
    Print the volume of the initial basis as well.
243
    INPUT:
244
    
245
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
246
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
247
    - "N" -- the modulus;
248
    - "iBound" -- the bound on the first variable;
249
    - "tBound" -- the bound on the second variable.
250
    
251
    OUTPUT:
252
    
253
    A list of bivariate integer polynomial obtained using the Coppersmith
254
    algorithm. The polynomials correspond to the rows of the LLL-reduce
255
    reduced base that comply with the Coppersmith condition.
256
    """
257
    # Arguments check.
258
    if iBound == 0 or tBound == 0:
259
        return None
260
    # End arguments check.
261
    nAtAlpha = N^alpha
262
    ## Building polynomials for matrix.
263
    polyRing   = inputPolynomial.parent()
264
    # Whatever the 2 variables are actually called, we call them
265
    # 'i' and 't' in all the variable names.
266
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
267
    #print polyVars[0], type(polyVars[0])
268
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
269
                                              tVariable:tVariable * tBound})
270
##    polynomialsList = \
271
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
272
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
273
    polynomialsList = \
274
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
275
                                             alpha,
276
                                             N,
277
                                             iBound,
278
                                             tBound,
279
                                             0)
280
    #print "Polynomials list:", polynomialsList
281
    ## Building the proto matrix.
282
    knownMonomials = []
283
    protoMatrix    = []
284
    for poly in polynomialsList:
285
        spo_add_polynomial_coeffs_to_matrix_row(poly,
286
                                                knownMonomials,
287
                                                protoMatrix,
288
                                                0)
289
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
290
    matrixToReduceTranspose = matrixToReduce.transpose()
291
    squareMatrix = matrixToReduce * matrixToReduceTranspose
292
    squareMatDet = det(squareMatrix)
293
    latticeVolume = sqrt(squareMatDet)
294
    print "Lattice volume:", latticeVolume.n()
295
    print "Lattice volume / N:", (latticeVolume/N).n()
296
    #print matrixToReduce
297
    ## Reduction and checking.
298
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
299
    #  error message issued when previous code was used.
300
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
301
    reductionTimeStart = cputime() 
302
    reducedMatrix = matrixToReduce.LLL(fp=None)
303
    reductionTime = cputime(reductionTimeStart)
304
    print "Reduction time:", reductionTime
305
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
306
    if not isLLLReduced:
307
        return None
308
    monomialsCount     = len(knownMonomials)
309
    monomialsCountSqrt = sqrt(monomialsCount)
310
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
311
    #print reducedMatrix
312
    ## Check the Coppersmith condition for each row and build the reduced 
313
    #  polynomials.
314
    ccReducedPolynomialsList = []
315
    for row in reducedMatrix.rows():
316
        l2Norm = row.norm(2)
317
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
318
            #print (l2Norm * monomialsCountSqrt).n()
319
            #print l2Norm.n()
320
            ccReducedPolynomial = \
321
                slz_compute_reduced_polynomial(row,
322
                                               knownMonomials,
323
                                               iVariable,
324
                                               iBound,
325
                                               tVariable,
326
                                               tBound)
327
            if not ccReducedPolynomial is None:
328
                ccReducedPolynomialsList.append(ccReducedPolynomial)
329
        else:
330
            #print l2Norm.n() , ">", nAtAlpha
331
            pass
332
    if len(ccReducedPolynomialsList) < 2:
333
        print "Less than 2 Coppersmith condition compliant vectors."
334
        return ()
335
    #print ccReducedPolynomialsList
336
    return ccReducedPolynomialsList
337
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
338

    
339
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
340
                                                 alpha,
341
                                                 N,
342
                                                 iBound,
343
                                                 tBound):
344
    """
345
    For a given set of arguments (see below), compute the polynomial modular 
346
    roots, if any.
347
    
348
    """
349
    # Arguments check.
350
    if iBound == 0 or tBound == 0:
351
        return set()
352
    # End arguments check.
353
    nAtAlpha = N^alpha
354
    ## Building polynomials for matrix.
355
    polyRing   = inputPolynomial.parent()
356
    # Whatever the 2 variables are actually called, we call them
357
    # 'i' and 't' in all the variable names.
358
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
359
    ccReducedPolynomialsList = \
360
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
361
                                                     alpha,
362
                                                     N,
363
                                                     iBound,
364
                                                     tBound)
365
    if len(ccReducedPolynomialsList) == 0:
366
        return set()   
367
    ## Create the valid (poly1 and poly2 are algebraically independent) 
368
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
369
    # Try to mix and match all the polynomial pairs built from the 
370
    # ccReducedPolynomialsList to obtain non zero resultants.
371
    resultantsInITuplesList = []
372
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
373
        for polyInnerIndex in xrange(polyOuterIndex+1, 
374
                                     len(ccReducedPolynomialsList)):
375
            # Compute the resultant in resultants in the
376
            # first variable (is it the optimal choice?).
377
            resultantInI = \
378
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
379
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
380
            #print "Resultant", resultantInI
381
            # Test algebraic independence.
382
            if not resultantInI.is_zero():
383
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
384
                                                 ccReducedPolynomialsList[polyInnerIndex],
385
                                                 resultantInI))
386
    # If no non zero resultant was found: we can't get no algebraically 
387
    # independent polynomials pair. Give up!
388
    if len(resultantsInITuplesList) == 0:
389
        return set()
390
    #print resultantsInITuplesList
391
    # Compute the roots.
392
    Zi = ZZ[str(iVariable)]
393
    Zt = ZZ[str(tVariable)]
394
    polynomialRootsSet = set()
395
    # First, solve in the second variable since resultants are in the first
396
    # variable.
397
    for resultantInITuple in resultantsInITuplesList:
398
        tRootsList = Zt(resultantInITuple[2]).roots()
399
        # For each tRoot, compute the corresponding iRoots and check 
400
        # them in the input polynomial.
401
        for tRoot in tRootsList:
402
            #print "tRoot:", tRoot
403
            # Roots returned by root() are (value, multiplicity) tuples.
404
            iRootsList = \
405
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
406
            print iRootsList
407
            # The iRootsList can be empty, hence the test.
408
            if len(iRootsList) != 0:
409
                for iRoot in iRootsList:
410
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
411
                    # polyEvalModN must be an integer.
412
                    if polyEvalModN == int(polyEvalModN):
413
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
414
    return polynomialRootsSet
415
# End slz_compute_integer_polynomial_modular_roots.
416
#
417
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
418
                                                 alpha,
419
                                                 N,
420
                                                 iBound,
421
                                                 tBound):
422
    """
423
    For a given set of arguments (see below), compute the polynomial modular 
424
    roots, if any.
425
    This version differs in the way resultants are computed.   
426
    """
427
    # Arguments check.
428
    if iBound == 0 or tBound == 0:
429
        return set()
430
    # End arguments check.
431
    nAtAlpha = N^alpha
432
    ## Building polynomials for matrix.
433
    polyRing   = inputPolynomial.parent()
434
    # Whatever the 2 variables are actually called, we call them
435
    # 'i' and 't' in all the variable names.
436
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
437
    #print polyVars[0], type(polyVars[0])
438
    ccReducedPolynomialsList = \
439
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
440
                                                     alpha,
441
                                                     N,
442
                                                     iBound,
443
                                                     tBound)
444
    if len(ccReducedPolynomialsList) == 0:
445
        return set()   
446
    ## Create the valid (poly1 and poly2 are algebraically independent) 
447
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
448
    # Try to mix and match all the polynomial pairs built from the 
449
    # ccReducedPolynomialsList to obtain non zero resultants.
450
    resultantsInTTuplesList = []
451
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
452
        for polyInnerIndex in xrange(polyOuterIndex+1, 
453
                                     len(ccReducedPolynomialsList)):
454
            # Compute the resultant in resultants in the
455
            # first variable (is it the optimal choice?).
456
            resultantInT = \
457
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
458
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
459
            #print "Resultant", resultantInT
460
            # Test algebraic independence.
461
            if not resultantInT.is_zero():
462
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
463
                                                 ccReducedPolynomialsList[polyInnerIndex],
464
                                                 resultantInT))
465
    # If no non zero resultant was found: we can't get no algebraically 
466
    # independent polynomials pair. Give up!
467
    if len(resultantsInTTuplesList) == 0:
468
        return set()
469
    #print resultantsInITuplesList
470
    # Compute the roots.
471
    Zi = ZZ[str(iVariable)]
472
    Zt = ZZ[str(tVariable)]
473
    polynomialRootsSet = set()
474
    # First, solve in the second variable since resultants are in the first
475
    # variable.
476
    for resultantInTTuple in resultantsInTTuplesList:
477
        iRootsList = Zi(resultantInTTuple[2]).roots()
478
        # For each iRoot, compute the corresponding tRoots and check 
479
        # them in the input polynomial.
480
        for iRoot in iRootsList:
481
            #print "iRoot:", iRoot
482
            # Roots returned by root() are (value, multiplicity) tuples.
483
            tRootsList = \
484
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
485
            print tRootsList
486
            # The tRootsList can be empty, hence the test.
487
            if len(tRootsList) != 0:
488
                for tRoot in tRootsList:
489
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
490
                    # polyEvalModN must be an integer.
491
                    if polyEvalModN == int(polyEvalModN):
492
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
493
    return polynomialRootsSet
494
# End slz_compute_integer_polynomial_modular_roots_2.
495
#
496
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
497
                                        upperBoundSa, approxAccurSa, 
498
                                        precSa=None):
499
    """
500
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
501
    a polynomial that approximates the function on a an interval starting
502
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
503
    approximates with the expected precision.
504
    The interval upper bound is lowered until the expected approximation 
505
    precision is reached.
506
    The polynomial, the bounds, the center of the interval and the error
507
    are returned.
508
    OUTPUT:
509
    A tuple made of 4 Sollya objects:
510
    - a polynomial;
511
    - an range (an interval, not in the sense of number given as an interval);
512
    - the center of the interval;
513
    - the maximum error in the approximation of the input functionSo by the
514
      output polynomial ; this error <= approxAccurSaS.
515
    
516
    """
517
    #print"In slz_compute_polynomial_and_interval..."
518
    ## Superficial argument check.
519
    if lowerBoundSa > upperBoundSa:
520
        return None
521
    ## Change Sollya precision, if requested.
522
    sollyaPrecChanged = False
523
    (sollyaPrecSo, sollyaPrecSa) = pobyso_get_prec_so_so_sa()
524
    if precSa is None:
525
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
526
        #print "Computed internal precision:", precSa
527
        if precSa < 192:
528
            precSa = 192
529
    if precSa != sollyaPrecSa:
530
        precSo = pobyso_constant_from_int_sa_so(precSa)
531
        pobyso_set_prec_so_so(precSo)
532
        sollya_lib_clear_obj(precSo)
533
        sollyaPrecChanged = True
534
    RRR = lowerBoundSa.parent()
535
    intervalShrinkConstFactorSa = RRR('0.9')
536
    absoluteErrorTypeSo = pobyso_absolute_so_so()
537
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
538
    currentUpperBoundSa = upperBoundSa
539
    currentLowerBoundSa = lowerBoundSa
540
    # What we want here is the polynomial without the variable change, 
541
    # since our actual variable will be x-intervalCenter defined over the 
542
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
543
    (polySo, intervalCenterSo, maxErrorSo) = \
544
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
545
                                                    currentRangeSo, 
546
                                                    absoluteErrorTypeSo)
547
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
548
    while maxErrorSa > approxAccurSa:
549
        print "++Approximation error:", maxErrorSa.n()
550
        sollya_lib_clear_obj(polySo)
551
        sollya_lib_clear_obj(intervalCenterSo)
552
        sollya_lib_clear_obj(maxErrorSo)
553
        # Very empirical shrinking factor.
554
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
555
        print "Shrink factor:", \
556
              shrinkFactorSa.n(), \
557
              intervalShrinkConstFactorSa
558
        print
559
        #errorRatioSa = approxAccurSa/maxErrorSa
560
        #print "Error ratio: ", errorRatioSa
561
        # Make sure interval shrinks.
562
        if shrinkFactorSa > intervalShrinkConstFactorSa:
563
            actualShrinkFactorSa = intervalShrinkConstFactorSa
564
            #print "Fixed"
565
        else:
566
            actualShrinkFactorSa = shrinkFactorSa
567
            #print "Computed",shrinkFactorSa,maxErrorSa
568
            #print shrinkFactorSa, maxErrorSa
569
        #print "Shrink factor", actualShrinkFactorSa
570
        currentUpperBoundSa = currentLowerBoundSa + \
571
                                (currentUpperBoundSa - currentLowerBoundSa) * \
572
                                actualShrinkFactorSa
573
        #print "Current upper bound:", currentUpperBoundSa
574
        sollya_lib_clear_obj(currentRangeSo)
575
        # Check what is left with the bounds.
576
        if currentUpperBoundSa <= currentLowerBoundSa or \
577
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
578
            sollya_lib_clear_obj(absoluteErrorTypeSo)
579
            print "Can't find an interval."
580
            print "Use either or both a higher polynomial degree or a higher",
581
            print "internal precision."
582
            print "Aborting!"
583
            if sollyaPrecChanged:
584
                pobyso_set_prec_so_so(sollyaPrecSo)
585
                sollya_lib_clear_obj(sollyaPrecSo)
586
            return None
587
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
588
                                                      currentUpperBoundSa)
589
        # print "New interval:",
590
        # pobyso_autoprint(currentRangeSo)
591
        #print "Second Taylor expansion call."
592
        (polySo, intervalCenterSo, maxErrorSo) = \
593
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
594
                                                        currentRangeSo, 
595
                                                        absoluteErrorTypeSo)
596
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
597
        #print "Max errorSo:",
598
        #pobyso_autoprint(maxErrorSo)
599
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
600
        #print "Max errorSa:", maxErrorSa
601
        #print "Sollya prec:",
602
        #pobyso_autoprint(sollya_lib_get_prec(None))
603
    # End while
604
    sollya_lib_clear_obj(absoluteErrorTypeSo)
605
    if sollyaPrecChanged:
606
        pobyso_set_prec_so_so(sollyaPrecSo)
607
        sollya_lib_clear_obj(sollyaPrecSo)
608
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
609
# End slz_compute_polynomial_and_interval
610

    
611
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
612
                                        upperBoundSa, approxAccurSa, 
613
                                        sollyaPrecSa=None):
614
    """
615
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
616
    a polynomial that approximates the function on a an interval starting
617
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
618
    approximates with the expected precision.
619
    The interval upper bound is lowered until the expected approximation 
620
    precision is reached.
621
    The polynomial, the bounds, the center of the interval and the error
622
    are returned.
623
    OUTPUT:
624
    A tuple made of 4 Sollya objects:
625
    - a polynomial;
626
    - an range (an interval, not in the sense of number given as an interval);
627
    - the center of the interval;
628
    - the maximum error in the approximation of the input functionSo by the
629
      output polynomial ; this error <= approxAccurSaS.
630
    
631
    """
632
    print"In slz_compute_polynomial_and_interval..."
633
    ## Superficial argument check.
634
    if lowerBoundSa > upperBoundSa:
635
        return None
636
    ## Change Sollya precision, if requested.
637
    if not sollyaPrecSa is None:
638
        sollyaPrecSo = pobyso_get_prec_so()
639
        pobyso_set_prec_sa_so(sollyaPrecSa)
640
    else:
641
        sollyaPrecSa = pobyso_get_prec_so_sa()
642
        sollyaPrecSo = None
643
    RRR = lowerBoundSa.parent()
644
    intervalShrinkConstFactorSa = RRR('0.9')
645
    absoluteErrorTypeSo = pobyso_absolute_so_so()
646
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
647
    currentUpperBoundSa = upperBoundSa
648
    currentLowerBoundSa = lowerBoundSa
649
    # What we want here is the polynomial without the variable change, 
650
    # since our actual variable will be x-intervalCenter defined over the 
651
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
652
    (polySo, intervalCenterSo, maxErrorSo) = \
653
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
654
                                                    currentRangeSo, 
655
                                                    absoluteErrorTypeSo)
656
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
657
    while maxErrorSa > approxAccurSa:
658
        print "++Approximation error:", maxErrorSa.n()
659
        sollya_lib_clear_obj(polySo)
660
        sollya_lib_clear_obj(intervalCenterSo)
661
        sollya_lib_clear_obj(maxErrorSo)
662
        # Very empirical shrinking factor.
663
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
664
        print "Shrink factor:", \
665
              shrinkFactorSa.n(), \
666
              intervalShrinkConstFactorSa
667
        print
668
        #errorRatioSa = approxAccurSa/maxErrorSa
669
        #print "Error ratio: ", errorRatioSa
670
        # Make sure interval shrinks.
671
        if shrinkFactorSa > intervalShrinkConstFactorSa:
672
            actualShrinkFactorSa = intervalShrinkConstFactorSa
673
            #print "Fixed"
674
        else:
675
            actualShrinkFactorSa = shrinkFactorSa
676
            #print "Computed",shrinkFactorSa,maxErrorSa
677
            #print shrinkFactorSa, maxErrorSa
678
        #print "Shrink factor", actualShrinkFactorSa
679
        currentUpperBoundSa = currentLowerBoundSa + \
680
                                (currentUpperBoundSa - currentLowerBoundSa) * \
681
                                actualShrinkFactorSa
682
        #print "Current upper bound:", currentUpperBoundSa
683
        sollya_lib_clear_obj(currentRangeSo)
684
        # Check what is left with the bounds.
685
        if currentUpperBoundSa <= currentLowerBoundSa or \
686
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
687
            sollya_lib_clear_obj(absoluteErrorTypeSo)
688
            print "Can't find an interval."
689
            print "Use either or both a higher polynomial degree or a higher",
690
            print "internal precision."
691
            print "Aborting!"
692
            if not sollyaPrecSo is None:
693
                pobyso_set_prec_so_so(sollyaPrecSo)
694
                sollya_lib_clear_obj(sollyaPrecSo)
695
            return None
696
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
697
                                                      currentUpperBoundSa)
698
        # print "New interval:",
699
        # pobyso_autoprint(currentRangeSo)
700
        #print "Second Taylor expansion call."
701
        (polySo, intervalCenterSo, maxErrorSo) = \
702
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
703
                                                        currentRangeSo, 
704
                                                        absoluteErrorTypeSo)
705
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
706
        #print "Max errorSo:",
707
        #pobyso_autoprint(maxErrorSo)
708
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
709
        #print "Max errorSa:", maxErrorSa
710
        #print "Sollya prec:",
711
        #pobyso_autoprint(sollya_lib_get_prec(None))
712
    # End while
713
    sollya_lib_clear_obj(absoluteErrorTypeSo)
714
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
715
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
716
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
717
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
718
    print "About to call polynomial rounding with:"
719
    print "polySo: ",           ; pobyso_autoprint(polySo)
720
    print "functionSo: ",       ; pobyso_autoprint(functionSo)
721
    print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
722
    print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
723
    print "itpSo: ",            ; pobyso_autoprint(itpSo)
724
    print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
725
    print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
726
    print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
727
    (roundedPolySo, roundedPolyMaxErrSo) = \
728
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
729
                                                           functionSo,
730
                                                           intervalCenterSo,
731
                                                           currentRangeSo,
732
                                                           itpSo,
733
                                                           ftpSo,
734
                                                           maxPrecSo,
735
                                                           approxAccurSo)
736
    
737
    sollya_lib_clear_obj(polySo)
738
    sollya_lib_clear_obj(maxErrorSo)
739
    sollya_lib_clear_obj(itpSo)
740
    sollya_lib_clear_obj(ftpSo)
741
    sollya_lib_clear_obj(approxAccurSo)
742
    if not sollyaPrecSo is None:
743
        pobyso_set_prec_so_so(sollyaPrecSo)
744
        sollya_lib_clear_obj(sollyaPrecSo)
745
    print "1: ", ; pobyso_autoprint(roundedPolySo)
746
    print "2: ", ; pobyso_autoprint(currentRangeSo)
747
    print "3: ", ; pobyso_autoprint(intervalCenterSo)
748
    print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
749
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
750
# End slz_compute_polynomial_and_interval_01
751

    
752
def slz_compute_reduced_polynomial(matrixRow,
753
                                    knownMonomials,
754
                                    var1,
755
                                    var1Bound,
756
                                    var2,
757
                                    var2Bound):
758
    """
759
    Compute a polynomial from a single reduced matrix row.
760
    This function was introduced in order to avoid the computation of the
761
    all the polynomials  from the full matrix (even those built from rows 
762
    that do no verify the Coppersmith condition) as this may involves 
763
    expensive operations over (large) integers.
764
    """
765
    ## Check arguments.
766
    if len(knownMonomials) == 0:
767
        return None
768
    # varNounds can be zero since 0^0 returns 1.
769
    if (var1Bound < 0) or (var2Bound < 0):
770
        return None
771
    ## Initialisations. 
772
    polynomialRing = knownMonomials[0].parent() 
773
    currentPolynomial = polynomialRing(0)
774
    # TODO: use zip instead of indices.
775
    for colIndex in xrange(0, len(knownMonomials)):
776
        currentCoefficient = matrixRow[colIndex]
777
        if currentCoefficient != 0:
778
            #print "Current coefficient:", currentCoefficient
779
            currentMonomial = knownMonomials[colIndex]
780
            #print "Monomial as multivariate polynomial:", \
781
            #currentMonomial, type(currentMonomial)
782
            degreeInVar1 = currentMonomial.degree(var1)
783
            #print "Degree in var1", var1, ":", degreeInVar1
784
            degreeInVar2 = currentMonomial.degree(var2)
785
            #print "Degree in var2", var2, ":", degreeInVar2
786
            if degreeInVar1 > 0:
787
                currentCoefficient = \
788
                    currentCoefficient / (var1Bound^degreeInVar1)
789
                #print "varBound1 in degree:", var1Bound^degreeInVar1
790
                #print "Current coefficient(1)", currentCoefficient
791
            if degreeInVar2 > 0:
792
                currentCoefficient = \
793
                    currentCoefficient / (var2Bound^degreeInVar2)
794
                #print "Current coefficient(2)", currentCoefficient
795
            #print "Current reduced monomial:", (currentCoefficient * \
796
            #                                    currentMonomial)
797
            currentPolynomial += (currentCoefficient * currentMonomial)
798
            #print "Current polynomial:", currentPolynomial
799
        # End if
800
    # End for colIndex.
801
    #print "Type of the current polynomial:", type(currentPolynomial)
802
    return(currentPolynomial)
803
# End slz_compute_reduced_polynomial
804
#
805
def slz_compute_reduced_polynomials(reducedMatrix,
806
                                        knownMonomials,
807
                                        var1,
808
                                        var1Bound,
809
                                        var2,
810
                                        var2Bound):
811
    """
812
    Legacy function, use slz_compute_reduced_polynomials_list
813
    """
814
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
815
                                                knownMonomials,
816
                                                var1,
817
                                                var1Bound,
818
                                                var2,
819
                                                var2Bound)
820
    )
821
#
822
def slz_compute_reduced_polynomials_list(reducedMatrix,
823
                                         knownMonomials,
824
                                         var1,
825
                                         var1Bound,
826
                                         var2,
827
                                         var2Bound):
828
    """
829
    From a reduced matrix, holding the coefficients, from a monomials list,
830
    from the bounds of each variable, compute the corresponding polynomials
831
    scaled back by dividing by the "right" powers of the variables bounds.
832
    
833
    The elements in knownMonomials must be of the "right" polynomial type.
834
    They set the polynomial type of the output polynomials in list.
835
    @param reducedMatrix:  the reduced matrix as output from LLL;
836
    @param kwnonMonomials: the ordered list of the monomials used to
837
                           build the polynomials;
838
    @param var1:           the first variable (of the "right" type);
839
    @param var1Bound:      the first variable bound;
840
    @param var2:           the second variable (of the "right" type);
841
    @param var2Bound:      the second variable bound.
842
    @return: a list of polynomials obtained with the reduced coefficients
843
             and scaled down with the bounds
844
    """
845
    
846
    # TODO: check input arguments.
847
    reducedPolynomials = []
848
    #print "type var1:", type(var1), " - type var2:", type(var2)
849
    for matrixRow in reducedMatrix.rows():
850
        currentPolynomial = 0
851
        for colIndex in xrange(0, len(knownMonomials)):
852
            currentCoefficient = matrixRow[colIndex]
853
            if currentCoefficient != 0:
854
                #print "Current coefficient:", currentCoefficient
855
                currentMonomial = knownMonomials[colIndex]
856
                parentRing = currentMonomial.parent()
857
                #print "Monomial as multivariate polynomial:", \
858
                #currentMonomial, type(currentMonomial)
859
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
860
                #print "Degree in var", var1, ":", degreeInVar1
861
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
862
                #print "Degree in var", var2, ":", degreeInVar2
863
                if degreeInVar1 > 0:
864
                    currentCoefficient /= var1Bound^degreeInVar1
865
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
866
                    #print "Current coefficient(1)", currentCoefficient
867
                if degreeInVar2 > 0:
868
                    currentCoefficient /= var2Bound^degreeInVar2
869
                    #print "Current coefficient(2)", currentCoefficient
870
                #print "Current reduced monomial:", (currentCoefficient * \
871
                #                                    currentMonomial)
872
                currentPolynomial += (currentCoefficient * currentMonomial)
873
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
874
                    #print "!!!! constant term !!!!"
875
                #print "Current polynomial:", currentPolynomial
876
            # End if
877
        # End for colIndex.
878
        #print "Type of the current polynomial:", type(currentPolynomial)
879
        reducedPolynomials.append(currentPolynomial)
880
    return reducedPolynomials 
881
# End slz_compute_reduced_polynomials_list.
882

    
883
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
884
                                                   knownMonomials,
885
                                                   var1,
886
                                                   var1Bound,
887
                                                   var2,
888
                                                   var2Bound):
889
    """
890
    From a list of rows, holding the coefficients, from a monomials list,
891
    from the bounds of each variable, compute the corresponding polynomials
892
    scaled back by dividing by the "right" powers of the variables bounds.
893
    
894
    The elements in knownMonomials must be of the "right" polynomial type.
895
    They set the polynomial type of the output polynomials in list.
896
    @param rowsList:       the reduced matrix as output from LLL;
897
    @param kwnonMonomials: the ordered list of the monomials used to
898
                           build the polynomials;
899
    @param var1:           the first variable (of the "right" type);
900
    @param var1Bound:      the first variable bound;
901
    @param var2:           the second variable (of the "right" type);
902
    @param var2Bound:      the second variable bound.
903
    @return: a list of polynomials obtained with the reduced coefficients
904
             and scaled down with the bounds
905
    """
906
    
907
    # TODO: check input arguments.
908
    reducedPolynomials = []
909
    #print "type var1:", type(var1), " - type var2:", type(var2)
910
    for matrixRow in rowsList:
911
        currentPolynomial = 0
912
        for colIndex in xrange(0, len(knownMonomials)):
913
            currentCoefficient = matrixRow[colIndex]
914
            if currentCoefficient != 0:
915
                #print "Current coefficient:", currentCoefficient
916
                currentMonomial = knownMonomials[colIndex]
917
                parentRing = currentMonomial.parent()
918
                #print "Monomial as multivariate polynomial:", \
919
                #currentMonomial, type(currentMonomial)
920
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
921
                #print "Degree in var", var1, ":", degreeInVar1
922
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
923
                #print "Degree in var", var2, ":", degreeInVar2
924
                if degreeInVar1 > 0:
925
                    currentCoefficient /= var1Bound^degreeInVar1
926
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
927
                    #print "Current coefficient(1)", currentCoefficient
928
                if degreeInVar2 > 0:
929
                    currentCoefficient /= var2Bound^degreeInVar2
930
                    #print "Current coefficient(2)", currentCoefficient
931
                #print "Current reduced monomial:", (currentCoefficient * \
932
                #                                    currentMonomial)
933
                currentPolynomial += (currentCoefficient * currentMonomial)
934
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
935
                    #print "!!!! constant term !!!!"
936
                #print "Current polynomial:", currentPolynomial
937
            # End if
938
        # End for colIndex.
939
        #print "Type of the current polynomial:", type(currentPolynomial)
940
        reducedPolynomials.append(currentPolynomial)
941
    return reducedPolynomials 
942
# End slz_compute_reduced_polynomials_list_from_rows.
943
#
944
def slz_compute_scaled_function(functionSa,
945
                                lowerBoundSa,
946
                                upperBoundSa,
947
                                floatingPointPrecSa,
948
                                debug=False):
949
    """
950
    From a function, compute the scaled function whose domain
951
    is included in [1, 2) and whose image is also included in [1,2).
952
    Return a tuple: 
953
    [0]: the scaled function
954
    [1]: the scaled domain lower bound
955
    [2]: the scaled domain upper bound
956
    [3]: the scaled image lower bound
957
    [4]: the scaled image upper bound
958
    """
959
    ## The variable can be called anything.
960
    x = functionSa.variables()[0]
961
    # Scalling the domain -> [1,2[.
962
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
963
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
964
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
965
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
966
    if debug:
967
        print "domainScalingExpression for argument :", \
968
        invDomainScalingExpressionSa
969
        print "function: ", functionSa
970
    ff = functionSa.subs({x : domainScalingExpressionSa})
971
    ## Bless expression as a function.
972
    ff = ff.function(x)
973
    #ff = f.subs_expr(x==domainScalingExpressionSa)
974
    #domainScalingFunction(x) = invDomainScalingExpressionSa
975
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
976
    scaledLowerBoundSa = \
977
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
978
    scaledUpperBoundSa = \
979
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
980
    if debug:
981
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
982
              scaledUpperBoundSa
983
    #
984
    # Scalling the image -> [1,2[.
985
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
986
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
987
    if flbSa <= fubSa: # Increasing
988
        imageBinadeBottomSa = floor(flbSa.log2())
989
    else: # Decreasing
990
        imageBinadeBottomSa = floor(fubSa.log2())
991
    if debug:
992
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
993
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
994
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
995
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
996
    if debug:
997
        print "imageScalingExpression for argument :", \
998
              invImageScalingExpressionSa
999
    iis = invImageScalingExpressionSa.function(x)
1000
    fff = iis.subs({x:ff})
1001
    if debug:
1002
        print "fff:", fff,
1003
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1004
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1005
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1006
# End slz_compute_scaled_function
1007

    
1008
def slz_fix_bounds_for_binades(lowerBound, 
1009
                               upperBound, 
1010
                               func = None, 
1011
                               domainDirection = -1,
1012
                               imageDirection  = -1):
1013
    """
1014
    Assuming the function is increasing or decreasing over the 
1015
    [lowerBound, upperBound] interval, return a lower bound lb and 
1016
    an upper bound ub such that:
1017
    - lb and ub belong to the same binade;
1018
    - func(lb) and func(ub) belong to the same binade.
1019
    domainDirection indicate how bounds move to fit into the same binade:
1020
    - a negative value move the upper bound down;
1021
    - a positive value move the lower bound up.
1022
    imageDirection indicate how bounds move in order to have their image
1023
    fit into the same binade, variation of the function is also condidered.
1024
    For an increasing function:
1025
    - negative value moves the upper bound down (and its image value as well);
1026
    - a positive values moves the lower bound up (and its image value as well);
1027
    For a decreasing function it is the other way round.
1028
    """
1029
    ## Arguments check
1030
    if lowerBound > upperBound:
1031
        return None
1032
    if lowerBound == upperBound:
1033
        return (lowerBound, upperBound)
1034
    if func is None:
1035
        return None
1036
    #
1037
    #varFunc = func.variables()[0]
1038
    lb = lowerBound
1039
    ub = upperBound
1040
    lbBinade = slz_compute_binade(lb) 
1041
    ubBinade = slz_compute_binade(ub)
1042
    ## Domain binade.
1043
    while lbBinade != ubBinade:
1044
        newIntervalWidth = (ub - lb) / 2
1045
        if domainDirection < 0:
1046
            ub = lb + newIntervalWidth
1047
            ubBinade = slz_compute_binade(ub)
1048
        else:
1049
            lb = lb + newIntervalWidth
1050
            lbBinade = slz_compute_binade(lb) 
1051
    ## Image binade.
1052
    if lb == ub:
1053
        return (lb, ub)
1054
    lbImg = func(lb)
1055
    ubImg = func(ub)
1056
    funcIsInc = (ubImg >= lbImg)
1057
    lbImgBinade = slz_compute_binade(lbImg)
1058
    ubImgBinade = slz_compute_binade(ubImg)
1059
    while lbImgBinade != ubImgBinade:
1060
        newIntervalWidth = (ub - lb) / 2
1061
        if imageDirection < 0:
1062
            if funcIsInc:
1063
                ub = lb + newIntervalWidth
1064
                ubImgBinade = slz_compute_binade(func(ub))
1065
                #print ubImgBinade
1066
            else:
1067
                lb = lb + newIntervalWidth
1068
                lbImgBinade = slz_compute_binade(func(lb))
1069
                #print lbImgBinade
1070
        else:
1071
            if funcIsInc:
1072
                lb = lb + newIntervalWidth
1073
                lbImgBinade = slz_compute_binade(func(lb))
1074
                #print lbImgBinade
1075
            else:
1076
                ub = lb + newIntervalWidth
1077
                ubImgBinade = slz_compute_binade(func(ub))
1078
                #print ubImgBinade
1079
    # End while lbImgBinade != ubImgBinade:
1080
    return (lb, ub)
1081
# End slz_fix_bounds_for_binades.
1082

    
1083
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1084
    # Create a polynomial over the rationals.
1085
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1086
    return(ratPolynomialRing(polyOfFloat))
1087
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1088

    
1089
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1090
    """
1091
     Create a polynomial over the rationals where all denominators are
1092
     powers of two.
1093
    """
1094
    polyVariable = polyOfFloat.variables()[0] 
1095
    RPR = QQ[str(polyVariable)]
1096
    polyCoeffs      = polyOfFloat.coefficients()
1097
    #print polyCoeffs
1098
    polyExponents   = polyOfFloat.exponents()
1099
    #print polyExponents
1100
    polyDenomPtwoCoeffs = []
1101
    for coeff in polyCoeffs:
1102
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1103
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1104
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1105
    ratPoly = RPR(0)
1106
    #print type(ratPoly)
1107
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1108
    #  The coefficient becomes plainly wrong when exponent == 0.
1109
    #  No clue as to why.
1110
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1111
        ratPoly += coeff * RPR(polyVariable^exponent)
1112
    return ratPoly
1113
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1114

    
1115
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1116
                                      lowerBoundSa, 
1117
                                      upperBoundSa, floatingPointPrecSa, 
1118
                                      internalSollyaPrecSa, approxPrecSa):
1119
    """
1120
    Under the assumption that:
1121
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1122
    - lowerBound and upperBound belong to the same binade.
1123
    from a:
1124
    - function;
1125
    - a degree
1126
    - a pair of bounds;
1127
    - the floating-point precision we work on;
1128
    - the internal Sollya precision;
1129
    - the requested approximation error
1130
    The initial interval is, possibly, splitted into smaller intervals.
1131
    It return a list of tuples, each made of:
1132
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1133
    - a second polynomial (with a changed variable f(x) = q(x))
1134
    - the approximation interval;
1135
    - the center, x0, of the interval;
1136
    - the corresponding approximation error.
1137
    TODO: fix endless looping for some parameters sets.
1138
    """
1139
    resultArray = []
1140
    # Set Sollya to the necessary internal precision.
1141
    precChangedSa = False
1142
    currentSollyaPrecSo = pobyso_get_prec_so()
1143
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
1144
    if internalSollyaPrecSa > currentSollyaPrecSa:
1145
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1146
        precChangedSa = True
1147
    #
1148
    x = functionSa.variables()[0] # Actual variable name can be anything.
1149
    # Scaled function: [1=,2] -> [1,2].
1150
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1151
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1152
                slz_compute_scaled_function(functionSa,                       \
1153
                                            lowerBoundSa,                     \
1154
                                            upperBoundSa,                     \
1155
                                            floatingPointPrecSa)
1156
    # In case bounds were in the negative real one may need to
1157
    # switch scaled bounds.
1158
    if scaledLowerBoundSa > scaledUpperBoundSa:
1159
        scaledLowerBoundSa, scaledUpperBoundSa = \
1160
            scaledUpperBoundSa, scaledLowerBoundSa
1161
        #print "Switching!"
1162
    print "Approximation accuracy: ", RR(approxAccurSa)
1163
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1164
    functionSo = \
1165
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1166
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1167
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1168
                                                  scaledUpperBoundSa)
1169

    
1170
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1171
                                              upperBoundSa.parent().precision()))
1172
    currentScaledLowerBoundSa = scaledLowerBoundSa
1173
    currentScaledUpperBoundSa = scaledUpperBoundSa
1174
    while True:
1175
        ## Compute the first Taylor expansion.
1176
        print "Computing a Taylor expansion..."
1177
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1178
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1179
                                        currentScaledLowerBoundSa, 
1180
                                        currentScaledUpperBoundSa,
1181
                                        approxAccurSa, internalSollyaPrecSa)
1182
        print "...done."
1183
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1184
        #  This value goes to the first variable: polySo. Other variables are
1185
        #  not assigned and should not be tested.
1186
        if polySo is None:
1187
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1188
            if precChangedSa:
1189
                pobyso_set_prec_so_so(currentSollyaPrecSo)
1190
                sollya_lib_clear_obj(currentSollyaPrecSo)
1191
            sollya_lib_clear_obj(functionSo)
1192
            sollya_lib_clear_obj(degreeSo)
1193
            sollya_lib_clear_obj(scaledBoundsSo)
1194
            return None
1195
        ## Add to the result array.
1196
        ### Change variable stuff in Sollya x -> x0-x.
1197
        changeVarExpressionSo = \
1198
            sollya_lib_build_function_sub( \
1199
                              sollya_lib_build_function_free_variable(), 
1200
                              sollya_lib_copy_obj(intervalCenterSo))
1201
        polyVarChangedSo = \
1202
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1203
        #### Get rid of the variable change Sollya stuff. 
1204
        sollya_lib_clear_obj(changeVarExpressionSo)
1205
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1206
                            intervalCenterSo, maxErrorSo))
1207
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1208
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1209
        print "Computed approximation error:", errorSa.n(digits=10)
1210
        # If the error and interval are OK a the first try, just return.
1211
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1212
            (errorSa <= approxAccurSa):
1213
            if precChangedSa:
1214
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1215
            sollya_lib_clear_obj(currentSollyaPrecSo)
1216
            sollya_lib_clear_obj(functionSo)
1217
            sollya_lib_clear_obj(degreeSo)
1218
            sollya_lib_clear_obj(scaledBoundsSo)
1219
            #print "Approximation error:", errorSa
1220
            return resultArray
1221
        ## The returned interval upper bound does not reach the requested upper
1222
        #  upper bound: compute the next upper bound.
1223
        ## The following ratio is always >= 1. If errorSa, we may want to
1224
        #  enlarge the interval
1225
        currentErrorRatio = approxAccurSa / errorSa
1226
        ## --|--------------------------------------------------------------|--
1227
        #  --|--------------------|--------------------------------------------
1228
        #  --|----------------------------|------------------------------------
1229
        ## Starting point for the next upper bound.
1230
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1231
        # Compute the increment. 
1232
        newBoundsWidthSa = \
1233
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1234
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1235
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1236
        # Take into account the original interval upper bound.                     
1237
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1238
            currentScaledUpperBoundSa = scaledUpperBoundSa
1239
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1240
            print "Can't shrink the interval anymore!"
1241
            print "You should consider increasing the Sollya internal precision"
1242
            print "or the polynomial degree."
1243
            print "Giving up!"
1244
            if precChangedSa:
1245
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1246
            sollya_lib_clear_obj(currentSollyaPrecSo)
1247
            sollya_lib_clear_obj(functionSo)
1248
            sollya_lib_clear_obj(degreeSo)
1249
            sollya_lib_clear_obj(scaledBoundsSo)
1250
            return None
1251
        # Compute the other expansions.
1252
        # Test for insufficient precision.
1253
# End slz_get_intervals_and_polynomials
1254

    
1255
def slz_interval_scaling_expression(boundsInterval, expVar):
1256
    """
1257
    Compute the scaling expression to map an interval that spans at most
1258
    a single binade into [1, 2) and the inverse expression as well.
1259
    If the interval spans more than one binade, result is wrong since
1260
    scaling is only based on the lower bound.
1261
    Not very sure that the transformation makes sense for negative numbers.
1262
    """
1263
    # The "one of the bounds is 0" special case. Here we consider
1264
    # the interval as the subnormals binade.
1265
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1266
        # The upper bound is (or should be) positive.
1267
        if boundsInterval.endpoints()[0] == 0:
1268
            if boundsInterval.endpoints()[1] == 0:
1269
                return None
1270
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1271
            l2     = boundsInterval.endpoints()[1].abs().log2()
1272
            # The upper bound is a power of two
1273
            if binade == l2:
1274
                scalingCoeff    = 2^(-binade)
1275
                invScalingCoeff = 2^(binade)
1276
                scalingOffset   = 1
1277
                return \
1278
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1279
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1280
            else:
1281
                scalingCoeff    = 2^(-binade-1)
1282
                invScalingCoeff = 2^(binade+1)
1283
                scalingOffset   = 1
1284
                return((scalingCoeff * expVar + scalingOffset),\
1285
                    ((expVar - scalingOffset) * invScalingCoeff))
1286
        # The lower bound is (or should be) negative.
1287
        if boundsInterval.endpoints()[1] == 0:
1288
            if boundsInterval.endpoints()[0] == 0:
1289
                return None
1290
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1291
            l2     = boundsInterval.endpoints()[0].abs().log2()
1292
            # The upper bound is a power of two
1293
            if binade == l2:
1294
                scalingCoeff    = -2^(-binade)
1295
                invScalingCoeff = -2^(binade)
1296
                scalingOffset   = 1
1297
                return((scalingCoeff * expVar + scalingOffset),\
1298
                    ((expVar - scalingOffset) * invScalingCoeff))
1299
            else:
1300
                scalingCoeff    = -2^(-binade-1)
1301
                invScalingCoeff = -2^(binade+1)
1302
                scalingOffset   = 1
1303
                return((scalingCoeff * expVar + scalingOffset),\
1304
                   ((expVar - scalingOffset) * invScalingCoeff))
1305
    # General case
1306
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1307
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1308
    # We allow for a single exception in binade spanning is when the
1309
    # "outward bound" is a power of 2 and its binade is that of the
1310
    # "inner bound" + 1.
1311
    if boundsInterval.endpoints()[0] > 0:
1312
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1313
        if lbBinade != ubBinade:
1314
            print "Different binades."
1315
            if ubL2 != ubBinade:
1316
                print "Not a power of 2."
1317
                return None
1318
            elif abs(ubBinade - lbBinade) > 1:
1319
                print "Too large span:", abs(ubBinade - lbBinade)
1320
                return None
1321
    else:
1322
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1323
        if lbBinade != ubBinade:
1324
            print "Different binades."
1325
            if lbL2 != lbBinade:
1326
                print "Not a power of 2."
1327
                return None
1328
            elif abs(ubBinade - lbBinade) > 1:
1329
                print "Too large span:", abs(ubBinade - lbBinade)
1330
                return None
1331
    #print "Lower bound binade:", binade
1332
    if boundsInterval.endpoints()[0] > 0:
1333
        return \
1334
            ((2^(-lbBinade) * expVar).function(expVar),
1335
             (2^(lbBinade) * expVar).function(expVar))
1336
    else:
1337
        return \
1338
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1339
             (-(2^(ubBinade)) * expVar).function(expVar))
1340
"""
1341
    # Code sent to attic. Should be the base for a 
1342
    # "slz_interval_translate_expression" rather than scale.
1343
    # Extra control and special cases code  added in  
1344
    # slz_interval_scaling_expression could (should ?) be added to
1345
    # this new function.
1346
    # The scaling offset is only used for negative numbers.
1347
    # When the absolute value of the lower bound is < 0.
1348
    if abs(boundsInterval.endpoints()[0]) < 1:
1349
        if boundsInterval.endpoints()[0] >= 0:
1350
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1351
            invScalingCoeff = 1/scalingCoeff
1352
            return((scalingCoeff * expVar, 
1353
                    invScalingCoeff * expVar))
1354
        else:
1355
            scalingCoeff = \
1356
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1357
            scalingOffset = -3 * scalingCoeff
1358
            return((scalingCoeff * expVar + scalingOffset,
1359
                    1/scalingCoeff * expVar + 3))
1360
    else: 
1361
        if boundsInterval.endpoints()[0] >= 0:
1362
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1363
            scalingOffset = 0
1364
            return((scalingCoeff * expVar, 
1365
                    1/scalingCoeff * expVar))
1366
        else:
1367
            scalingCoeff = \
1368
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1369
            scalingOffset =  -3 * scalingCoeff
1370
            #scalingOffset = 0
1371
            return((scalingCoeff * expVar + scalingOffset,
1372
                    1/scalingCoeff * expVar + 3))
1373
"""
1374
# End slz_interval_scaling_expression
1375
   
1376
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1377
    """
1378
    Compute the Sage version of the Taylor polynomial and it's
1379
    companion data (interval, center...)
1380
    The input parameter is a five elements tuple:
1381
    - [0]: the polyomial (without variable change), as polynomial over a
1382
           real ring;
1383
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1384
           over a real ring;
1385
    - [2]: the interval (as Sollya range);
1386
    - [3]: the interval center;
1387
    - [4]: the approximation error. 
1388
    
1389
    The function returns a 5 elements tuple: formed with all the 
1390
    input elements converted into their Sollya counterpart.
1391
    """
1392
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
1393
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
1394
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
1395
    intervalSa = \
1396
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1397
    centerSa = \
1398
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1399
    errorSa = \
1400
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1401
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1402
    # End slz_interval_and_polynomial_to_sage
1403

    
1404
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1405
            targetPlusOnePrecRF = None, quasiExactRF = None):
1406
    """
1407
      Check if an element (argument) of the domain of function (function)
1408
      yields a HTRN case (rounding to next) for the target precision 
1409
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1410
      
1411
      The strategy is: 
1412
      - compute the image at high (quasi-exact) precision;
1413
      - round it to nearest to precision;
1414
      - round it to exactly to (precision+1), the computed number has two
1415
        midpoint neighbors;
1416
      - check the distance between these neighbors and the quasi-exact value;
1417
        - if none of them is closer than the targetAccuracy, return False,
1418
        - else return true.
1419
      - Powers of two are a special case when comparing the midpoint in
1420
        the 0 direction..
1421
    """
1422
    ## Arguments filtering. 
1423
    ## TODO: filter the first argument for consistence.
1424
    if targetRF is None:
1425
        targetRF = argument.parent()
1426
    ## Ditto for the real field holding the midpoints.
1427
    if targetPlusOnePrecRF is None:
1428
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1429
    ## If no quasiExactField is provided, create a high accuracy RealField.
1430
    if quasiExactRF is None:
1431
        quasiExactRF = RealField(1536)
1432
    function                      = function.function(function.variables()[0])
1433
    exactArgument                 = quasiExactRF(argument)
1434
    quasiExactValue               = function(exactArgument)
1435
    roundedValue                  = targetRF(quasiExactValue)
1436
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1437
    # Upper midpoint.
1438
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1439
    # Lower midpoint.
1440
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1441
    binade                        = slz_compute_binade(roundedValue)
1442
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1443
    #print "Argument:", argument
1444
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1445
    #print "Binade:", binade
1446
    #print "binadeCorrectedTargetAccuracy:", \
1447
    #binadeCorrectedTargetAccuracy.n(prec=100)
1448
    #print "binadeCorrectedTargetAccuracy:", \
1449
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1450
    #print "Upper midpoint:", \
1451
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1452
    #print "Rounded value :", \
1453
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1454
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1455
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1456
    #print "Lower midpoint:", \
1457
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1458
    ## Make quasiExactValue = 0 a special case to move it out of the way.
1459
    if quasiExactValue == 0:
1460
        return False
1461
    ## Begining of the general case : check with the midpoint of 
1462
    #  greatest absolute value.
1463
    if quasiExactValue > 0:
1464
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1465
           binadeCorrectedTargetAccuracy:
1466
            #print "Too close to the upper midpoint: ", \
1467
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1468
            #print argument.n().str(base=16, \
1469
            #  no_sci=False)
1470
            #print "Lower midpoint:", \
1471
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1472
            #print "Value         :", \
1473
            # quasiExactValue.n(prec=200).str(base=2)
1474
            #print "Upper midpoint:", \
1475
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1476
            return True
1477
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
1478
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1479
           binadeCorrectedTargetAccuracy:
1480
            #print "Too close to the upper midpoint: ", \
1481
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1482
            #print argument.n().str(base=16, \
1483
            #  no_sci=False)
1484
            #print "Lower midpoint:", \
1485
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1486
            #print "Value         :", \
1487
            #  quasiExactValue.n(prec=200).str(base=2)
1488
            #print "Upper midpoint:", \
1489
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1490
            #print
1491
            return True
1492
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1493
    ## For the midpoint of smaller absolute value, 
1494
    #  split cases with the powers of 2.
1495
    if sno_abs_is_power_of_two(roundedValue):
1496
        if quasiExactValue > 0:        
1497
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1498
               binadeCorrectedTargetAccuracy / 2:
1499
                #print "Lower midpoint:", \
1500
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1501
                #print "Value         :", \
1502
                #  quasiExactValue.n(prec=200).str(base=2)
1503
                #print "Upper midpoint:", \
1504
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1505
                #print
1506
                return True
1507
        else:
1508
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1509
              binadeCorrectedTargetAccuracy / 2:
1510
                #print "Lower midpoint:", \
1511
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1512
                #print "Value         :", 
1513
                #  quasiExactValue.n(prec=200).str(base=2)
1514
                #print "Upper midpoint:", 
1515
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1516
                #print
1517
                return True
1518
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1519
    else: ## Not a power of 2, regular comparison with the midpoint of 
1520
          #  smaller absolute value.
1521
        if quasiExactValue > 0:        
1522
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1523
              binadeCorrectedTargetAccuracy:
1524
                #print "Lower midpoint:", \
1525
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1526
                #print "Value         :", \
1527
                #  quasiExactValue.n(prec=200).str(base=2)
1528
                #print "Upper midpoint:", \
1529
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1530
                #print
1531
                return True
1532
        else: # quasiExactValue <= 0
1533
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1534
              binadeCorrectedTargetAccuracy:
1535
                #print "Lower midpoint:", \
1536
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1537
                #print "Value         :", \
1538
                #  quasiExactValue.n(prec=200).str(base=2)
1539
                #print "Upper midpoint:", \
1540
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1541
                #print
1542
                return True
1543
    #print "distance to the upper midpoint:", \
1544
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1545
    #print "distance to the lower midpoint:", \
1546
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1547
    return False
1548
# End slz_is_htrn
1549

    
1550
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1551
                                                precision,
1552
                                                targetHardnessToRound,
1553
                                                variable1,
1554
                                                variable2):
1555
    """
1556
    Creates a new multivariate polynomial with integer coefficients for use
1557
     with the Coppersmith method.
1558
    A the same time it computes :
1559
    - 2^K (N);
1560
    - 2^k (bound on the second variable)
1561
    - lcm
1562

    
1563
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1564
                         variables.
1565
    :param precision: the precision of the floating-point coefficients.
1566
    :param targetHardnessToRound: the hardness to round we want to check.
1567
    :param variable1: the first variable of the polynomial (an expression).
1568
    :param variable2: the second variable of the polynomial (an expression).
1569
    
1570
    :returns: a 4 elements tuple:
1571
                - the polynomial;
1572
                - the modulus (N);
1573
                - the t bound;
1574
                - the lcm used to compute the integral coefficients and the 
1575
                  module.
1576
    """
1577
    # Create a new integer polynomial ring.
1578
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1579
    # Coefficients are issued in the increasing power order.
1580
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1581
    # Print the reversed list for debugging.
1582
    #print
1583
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1584
    # Build the list of number we compute the lcm of.
1585
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1586
    #print "Coefficient denominators:", coefficientDenominators
1587
    coefficientDenominators.append(2^precision)
1588
    coefficientDenominators.append(2^(targetHardnessToRound))
1589
    leastCommonMultiple = lcm(coefficientDenominators)
1590
    # Compute the expression corresponding to the new polynomial
1591
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1592
    #print coefficientNumerators
1593
    polynomialExpression = 0
1594
    power = 0
1595
    # Iterate over two lists at the same time, stop when the shorter
1596
    # (is this case coefficientsNumerators) is 
1597
    # exhausted. Both lists are ordered in the order of increasing powers
1598
    # of variable1.
1599
    for numerator, denominator in \
1600
                        zip(coefficientNumerators, coefficientDenominators):
1601
        multiplicator = leastCommonMultiple / denominator 
1602
        newCoefficient = numerator * multiplicator
1603
        polynomialExpression += newCoefficient * variable1^power
1604
        power +=1
1605
    polynomialExpression += - variable2
1606
    return (IP(polynomialExpression),
1607
            leastCommonMultiple / 2^precision, # 2^K aka N.
1608
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1609
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1610
            leastCommonMultiple) # If we want to make test computations.
1611
        
1612
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1613

    
1614
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1615
                                          precision):
1616
    """
1617
    Makes a variable substitution into the input polynomial so that the output
1618
    polynomial can take integer arguments.
1619
    All variables of the input polynomial "have precision p". That is to say
1620
    that they are rationals with denominator == 2^(precision - 1): 
1621
    x = y/2^(precision - 1).
1622
    We "incorporate" these denominators into the coefficients with, 
1623
    respectively, the "right" power.
1624
    """
1625
    polynomialField = ratPolyOfRat.parent()
1626
    polynomialVariable = ratPolyOfRat.variables()[0]
1627
    #print "The polynomial field is:", polynomialField
1628
    return \
1629
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1630
                                   polynomialVariable/2^(precision-1)}))
1631
    
1632
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1633

    
1634
def slz_reduce_and_test_base(matrixToReduce,
1635
                             nAtAlpha,
1636
                             monomialsCountSqrt):
1637
    """
1638
    Reduces the basis, tests the Coppersmith condition and returns
1639
    a list of rows that comply with the condition.
1640
    """
1641
    ccComplientRowsList = []
1642
    reducedMatrix = matrixToReduce.LLL(None)
1643
    if not reducedMatrix.is_LLL_reduced():
1644
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1645
    else:
1646
        print "reducedMatrix is actually reduced."
1647
    print "N^alpha:", nAtAlpha.n()
1648
    rowIndex = 0
1649
    for row in reducedMatrix.rows():
1650
        l2Norm = row.norm(2)
1651
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1652
                monomialsCountSqrt.n(), ". Is vector OK?", 
1653
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1654
            ccComplientRowsList.append(row)
1655
            print "True"
1656
        else:
1657
            print "False"
1658
    # End for
1659
    return ccComplientRowsList
1660
# End slz_reduce_and_test_base
1661

    
1662
def slz_resultant(poly1, poly2, elimVar):
1663
    """
1664
    Compute the resultant for two polynomials for a given variable
1665
    and return the (poly1, poly2, resultant) if the resultant
1666
    is not 0.
1667
    Return () otherwise.
1668
    """
1669
    polynomialRing0    = poly1.parent()
1670
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1671
    if resultantInElimVar is None:
1672
        return None
1673
    if resultantInElimVar.is_zero():
1674
        return None
1675
    else:
1676
        return resultantInElimVar
1677
# End slz_resultant.
1678
#
1679
def slz_resultant_tuple(poly1, poly2, elimVar):
1680
    """
1681
    Compute the resultant for two polynomials for a given variable
1682
    and return the (poly1, poly2, resultant) if the resultant
1683
    is not 0.
1684
    Return () otherwise.
1685
    """
1686
    polynomialRing0    = poly1.parent()
1687
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1688
    if resultantInElimVar.is_zero():
1689
        return ()
1690
    else:
1691
        return (poly1, poly2, resultantInElimVar)
1692
# End slz_resultant_tuple.
1693
#
1694
print "\t...sageSLZ loaded"