Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (62,05 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_integer_polynomial_modular_roots(inputPolynomial,
234
                                                 alpha,
235
                                                 N,
236
                                                 iBound,
237
                                                 tBound):
238
    """
239
    For a given set of arguments (see below), compute the polynomial modular 
240
    roots, if any.
241
    
242
    """
243
    # Arguments check.
244
    if iBound == 0 or tBound == 0:
245
        return set()
246
    # End arguments check.
247
    nAtAlpha = N^alpha
248
    ## Building polynomials for matrix.
249
    polyRing   = inputPolynomial.parent()
250
    # Whatever the 2 variables are actually called, we call them
251
    # 'i' and 't' in all the variable names.
252
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
253
    ccReducedPolynomialsList = \
254
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
255
                                                     alpha,
256
                                                     N,
257
                                                     iBound,
258
                                                     tBound)
259
    if len(ccReducedPolynomialsList) == 0:
260
        return set()   
261
    ## Create the valid (poly1 and poly2 are algebraically independent) 
262
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
263
    # Try to mix and match all the polynomial pairs built from the 
264
    # ccReducedPolynomialsList to obtain non zero resultants.
265
    resultantsInITuplesList = []
266
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
267
        for polyInnerIndex in xrange(polyOuterIndex+1, 
268
                                     len(ccReducedPolynomialsList)):
269
            # Compute the resultant in resultants in the
270
            # first variable (is it the optimal choice?).
271
            resultantInI = \
272
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
273
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
274
            #print "Resultant", resultantInI
275
            # Test algebraic independence.
276
            if not resultantInI.is_zero():
277
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
278
                                                 ccReducedPolynomialsList[polyInnerIndex],
279
                                                 resultantInI))
280
    # If no non zero resultant was found: we can't get no algebraically 
281
    # independent polynomials pair. Give up!
282
    if len(resultantsInITuplesList) == 0:
283
        return set()
284
    #print resultantsInITuplesList
285
    # Compute the roots.
286
    Zi = ZZ[str(iVariable)]
287
    Zt = ZZ[str(tVariable)]
288
    polynomialRootsSet = set()
289
    # First, solve in the second variable since resultants are in the first
290
    # variable.
291
    for resultantInITuple in resultantsInITuplesList:
292
        tRootsList = Zt(resultantInITuple[2]).roots()
293
        # For each tRoot, compute the corresponding iRoots and check 
294
        # them in the input polynomial.
295
        for tRoot in tRootsList:
296
            #print "tRoot:", tRoot
297
            # Roots returned by root() are (value, multiplicity) tuples.
298
            iRootsList = \
299
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
300
            print iRootsList
301
            # The iRootsList can be empty, hence the test.
302
            if len(iRootsList) != 0:
303
                for iRoot in iRootsList:
304
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
305
                    # polyEvalModN must be an integer.
306
                    if polyEvalModN == int(polyEvalModN):
307
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
308
    return polynomialRootsSet
309
# End slz_compute_integer_polynomial_modular_roots.
310
#
311
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
312
                                                 alpha,
313
                                                 N,
314
                                                 iBound,
315
                                                 tBound):
316
    """
317
    For a given set of arguments (see below), compute the polynomial modular 
318
    roots, if any.
319
    This version differs in the way resultants are computed.   
320
    """
321
    # Arguments check.
322
    if iBound == 0 or tBound == 0:
323
        return set()
324
    # End arguments check.
325
    nAtAlpha = N^alpha
326
    ## Building polynomials for matrix.
327
    polyRing   = inputPolynomial.parent()
328
    # Whatever the 2 variables are actually called, we call them
329
    # 'i' and 't' in all the variable names.
330
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
331
    #print polyVars[0], type(polyVars[0])
332
    ccReducedPolynomialsList = \
333
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
334
                                                     alpha,
335
                                                     N,
336
                                                     iBound,
337
                                                     tBound)
338
    if len(ccReducedPolynomialsList) == 0:
339
        return set()   
340
    ## Create the valid (poly1 and poly2 are algebraically independent) 
341
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
342
    # Try to mix and match all the polynomial pairs built from the 
343
    # ccReducedPolynomialsList to obtain non zero resultants.
344
    resultantsInTTuplesList = []
345
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
346
        for polyInnerIndex in xrange(polyOuterIndex+1, 
347
                                     len(ccReducedPolynomialsList)):
348
            # Compute the resultant in resultants in the
349
            # first variable (is it the optimal choice?).
350
            resultantInT = \
351
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
352
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
353
            #print "Resultant", resultantInT
354
            # Test algebraic independence.
355
            if not resultantInT.is_zero():
356
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
357
                                                 ccReducedPolynomialsList[polyInnerIndex],
358
                                                 resultantInT))
359
    # If no non zero resultant was found: we can't get no algebraically 
360
    # independent polynomials pair. Give up!
361
    if len(resultantsInTTuplesList) == 0:
362
        return set()
363
    #print resultantsInITuplesList
364
    # Compute the roots.
365
    Zi = ZZ[str(iVariable)]
366
    Zt = ZZ[str(tVariable)]
367
    polynomialRootsSet = set()
368
    # First, solve in the second variable since resultants are in the first
369
    # variable.
370
    for resultantInTTuple in resultantsInTTuplesList:
371
        iRootsList = Zi(resultantInTTuple[2]).roots()
372
        # For each iRoot, compute the corresponding tRoots and check 
373
        # them in the input polynomial.
374
        for iRoot in iRootsList:
375
            #print "iRoot:", iRoot
376
            # Roots returned by root() are (value, multiplicity) tuples.
377
            tRootsList = \
378
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
379
            print tRootsList
380
            # The tRootsList can be empty, hence the test.
381
            if len(tRootsList) != 0:
382
                for tRoot in tRootsList:
383
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
384
                    # polyEvalModN must be an integer.
385
                    if polyEvalModN == int(polyEvalModN):
386
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
387
    return polynomialRootsSet
388
# End slz_compute_integer_polynomial_modular_roots_2.
389
#
390
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
391
                                        upperBoundSa, approxPrecSa, 
392
                                        sollyaPrecSa=None):
393
    """
394
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
395
    a polynomial that approximates the function on a an interval starting
396
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
397
    approximates with the expected precision.
398
    The interval upper bound is lowered until the expected approximation 
399
    precision is reached.
400
    The polynomial, the bounds, the center of the interval and the error
401
    are returned.
402
    OUTPUT:
403
    A tuple made of 4 Sollya objects:
404
    - a polynomial;
405
    - an range (an interval, not in the sense of number given as an interval);
406
    - the center of the interval;
407
    - the maximum error in the approximation of the input functionSo by the
408
      output polynomial ; this error <= approxPrecSaS.
409
    
410
    """
411
    ## Superficial argument check.
412
    if lowerBoundSa > upperBoundSa:
413
        return None
414
    RRR = lowerBoundSa.parent()
415
    intervalShrinkConstFactorSa = RRR('0.9')
416
    absoluteErrorTypeSo = pobyso_absolute_so_so()
417
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
418
    currentUpperBoundSa = upperBoundSa
419
    currentLowerBoundSa = lowerBoundSa
420
    # What we want here is the polynomial without the variable change, 
421
    # since our actual variable will be x-intervalCenter defined over the 
422
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
423
    (polySo, intervalCenterSo, maxErrorSo) = \
424
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
425
                                                    currentRangeSo, 
426
                                                    absoluteErrorTypeSo)
427
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
428
    while maxErrorSa > approxPrecSa:
429
        print "++Approximation error:", maxErrorSa
430
        sollya_lib_clear_obj(polySo)
431
        sollya_lib_clear_obj(intervalCenterSo)
432
        sollya_lib_clear_obj(maxErrorSo)
433
        # Very empirical schrinking factor.
434
        shrinkFactorSa = 1 /  (maxErrorSa/approxPrecSa).log2().abs()
435
        print "Shrink factor:", shrinkFactorSa, intervalShrinkConstFactorSa
436
        #errorRatioSa = approxPrecSa/maxErrorSa
437
        #print "Error ratio: ", errorRatioSa
438
        if shrinkFactorSa > intervalShrinkConstFactorSa:
439
            actualShrinkFactorSa = intervalShrinkConstFactorSa
440
            #print "Fixed"
441
        else:
442
            actualShrinkFactorSa = shrinkFactorSa
443
            #print "Computed",shrinkFactorSa,maxErrorSa
444
            #print shrinkFactorSa, maxErrorSa
445
        #print "Shrink factor", actualShrinkFactorSa
446
        currentUpperBoundSa = currentLowerBoundSa + \
447
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
448
                                   actualShrinkFactorSa
449
        #print "Current upper bound:", currentUpperBoundSa
450
        sollya_lib_clear_obj(currentRangeSo)
451
        if currentUpperBoundSa <= currentLowerBoundSa or \
452
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
453
            sollya_lib_clear_obj(absoluteErrorTypeSo)
454
            print "Can't find an interval."
455
            print "Use either or both a higher polynomial degree or a higher",
456
            print "internal precision."
457
            print "Aborting!"
458
            return (None, None, None, None)
459
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
460
                                                      currentUpperBoundSa)
461
        # print "New interval:",
462
        # pobyso_autoprint(currentRangeSo)
463
        #print "Second Taylor expansion call."
464
        (polySo, intervalCenterSo, maxErrorSo) = \
465
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
466
                                                        currentRangeSo, 
467
                                                        absoluteErrorTypeSo)
468
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
469
        #print "Max errorSo:",
470
        #pobyso_autoprint(maxErrorSo)
471
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
472
        #print "Max errorSa:", maxErrorSa
473
        #print "Sollya prec:",
474
        #pobyso_autoprint(sollya_lib_get_prec(None))
475
    sollya_lib_clear_obj(absoluteErrorTypeSo)
476
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
477
# End slz_compute_polynomial_and_interval
478

    
479
def slz_compute_reduced_polynomial(matrixRow,
480
                                    knownMonomials,
481
                                    var1,
482
                                    var1Bound,
483
                                    var2,
484
                                    var2Bound):
485
    """
486
    Compute a polynomial from a single reduced matrix row.
487
    This function was introduced in order to avoid the computation of the
488
    all the polynomials  from the full matrix (even those built from rows 
489
    that do no verify the Coppersmith condition) as this may involves 
490
    expensive operations over (large) integers.
491
    """
492
    ## Check arguments.
493
    if len(knownMonomials) == 0:
494
        return None
495
    # varNounds can be zero since 0^0 returns 1.
496
    if (var1Bound < 0) or (var2Bound < 0):
497
        return None
498
    ## Initialisations. 
499
    polynomialRing = knownMonomials[0].parent() 
500
    currentPolynomial = polynomialRing(0)
501
    # TODO: use zip instead of indices.
502
    for colIndex in xrange(0, len(knownMonomials)):
503
        currentCoefficient = matrixRow[colIndex]
504
        if currentCoefficient != 0:
505
            #print "Current coefficient:", currentCoefficient
506
            currentMonomial = knownMonomials[colIndex]
507
            #print "Monomial as multivariate polynomial:", \
508
            #currentMonomial, type(currentMonomial)
509
            degreeInVar1 = currentMonomial.degree(var1)
510
            #print "Degree in var1", var1, ":", degreeInVar1
511
            degreeInVar2 = currentMonomial.degree(var2)
512
            #print "Degree in var2", var2, ":", degreeInVar2
513
            if degreeInVar1 > 0:
514
                currentCoefficient = \
515
                    currentCoefficient / (var1Bound^degreeInVar1)
516
                #print "varBound1 in degree:", var1Bound^degreeInVar1
517
                #print "Current coefficient(1)", currentCoefficient
518
            if degreeInVar2 > 0:
519
                currentCoefficient = \
520
                    currentCoefficient / (var2Bound^degreeInVar2)
521
                #print "Current coefficient(2)", currentCoefficient
522
            #print "Current reduced monomial:", (currentCoefficient * \
523
            #                                    currentMonomial)
524
            currentPolynomial += (currentCoefficient * currentMonomial)
525
            #print "Current polynomial:", currentPolynomial
526
        # End if
527
    # End for colIndex.
528
    #print "Type of the current polynomial:", type(currentPolynomial)
529
    return(currentPolynomial)
530
# End slz_compute_reduced_polynomial
531
#
532
def slz_compute_reduced_polynomials(reducedMatrix,
533
                                        knownMonomials,
534
                                        var1,
535
                                        var1Bound,
536
                                        var2,
537
                                        var2Bound):
538
    """
539
    Legacy function, use slz_compute_reduced_polynomials_list
540
    """
541
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
542
                                                knownMonomials,
543
                                                var1,
544
                                                var1Bound,
545
                                                var2,
546
                                                var2Bound)
547
    )
548
#
549
def slz_compute_reduced_polynomials_list(reducedMatrix,
550
                                         knownMonomials,
551
                                         var1,
552
                                         var1Bound,
553
                                         var2,
554
                                         var2Bound):
555
    """
556
    From a reduced matrix, holding the coefficients, from a monomials list,
557
    from the bounds of each variable, compute the corresponding polynomials
558
    scaled back by dividing by the "right" powers of the variables bounds.
559
    
560
    The elements in knownMonomials must be of the "right" polynomial type.
561
    They set the polynomial type of the output polynomials in list.
562
    @param reducedMatrix:  the reduced matrix as output from LLL;
563
    @param kwnonMonomials: the ordered list of the monomials used to
564
                           build the polynomials;
565
    @param var1:           the first variable (of the "right" type);
566
    @param var1Bound:      the first variable bound;
567
    @param var2:           the second variable (of the "right" type);
568
    @param var2Bound:      the second variable bound.
569
    @return: a list of polynomials obtained with the reduced coefficients
570
             and scaled down with the bounds
571
    """
572
    
573
    # TODO: check input arguments.
574
    reducedPolynomials = []
575
    #print "type var1:", type(var1), " - type var2:", type(var2)
576
    for matrixRow in reducedMatrix.rows():
577
        currentPolynomial = 0
578
        for colIndex in xrange(0, len(knownMonomials)):
579
            currentCoefficient = matrixRow[colIndex]
580
            if currentCoefficient != 0:
581
                #print "Current coefficient:", currentCoefficient
582
                currentMonomial = knownMonomials[colIndex]
583
                parentRing = currentMonomial.parent()
584
                #print "Monomial as multivariate polynomial:", \
585
                #currentMonomial, type(currentMonomial)
586
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
587
                #print "Degree in var", var1, ":", degreeInVar1
588
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
589
                #print "Degree in var", var2, ":", degreeInVar2
590
                if degreeInVar1 > 0:
591
                    currentCoefficient /= var1Bound^degreeInVar1
592
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
593
                    #print "Current coefficient(1)", currentCoefficient
594
                if degreeInVar2 > 0:
595
                    currentCoefficient /= var2Bound^degreeInVar2
596
                    #print "Current coefficient(2)", currentCoefficient
597
                #print "Current reduced monomial:", (currentCoefficient * \
598
                #                                    currentMonomial)
599
                currentPolynomial += (currentCoefficient * currentMonomial)
600
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
601
                    #print "!!!! constant term !!!!"
602
                #print "Current polynomial:", currentPolynomial
603
            # End if
604
        # End for colIndex.
605
        #print "Type of the current polynomial:", type(currentPolynomial)
606
        reducedPolynomials.append(currentPolynomial)
607
    return reducedPolynomials 
608
# End slz_compute_reduced_polynomials_list.
609

    
610
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
611
                                                   knownMonomials,
612
                                                   var1,
613
                                                   var1Bound,
614
                                                   var2,
615
                                                   var2Bound):
616
    """
617
    From a list of rows, holding the coefficients, from a monomials list,
618
    from the bounds of each variable, compute the corresponding polynomials
619
    scaled back by dividing by the "right" powers of the variables bounds.
620
    
621
    The elements in knownMonomials must be of the "right" polynomial type.
622
    They set the polynomial type of the output polynomials in list.
623
    @param rowsList:       the reduced matrix as output from LLL;
624
    @param kwnonMonomials: the ordered list of the monomials used to
625
                           build the polynomials;
626
    @param var1:           the first variable (of the "right" type);
627
    @param var1Bound:      the first variable bound;
628
    @param var2:           the second variable (of the "right" type);
629
    @param var2Bound:      the second variable bound.
630
    @return: a list of polynomials obtained with the reduced coefficients
631
             and scaled down with the bounds
632
    """
633
    
634
    # TODO: check input arguments.
635
    reducedPolynomials = []
636
    #print "type var1:", type(var1), " - type var2:", type(var2)
637
    for matrixRow in rowsList:
638
        currentPolynomial = 0
639
        for colIndex in xrange(0, len(knownMonomials)):
640
            currentCoefficient = matrixRow[colIndex]
641
            if currentCoefficient != 0:
642
                #print "Current coefficient:", currentCoefficient
643
                currentMonomial = knownMonomials[colIndex]
644
                parentRing = currentMonomial.parent()
645
                #print "Monomial as multivariate polynomial:", \
646
                #currentMonomial, type(currentMonomial)
647
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
648
                #print "Degree in var", var1, ":", degreeInVar1
649
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
650
                #print "Degree in var", var2, ":", degreeInVar2
651
                if degreeInVar1 > 0:
652
                    currentCoefficient /= var1Bound^degreeInVar1
653
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
654
                    #print "Current coefficient(1)", currentCoefficient
655
                if degreeInVar2 > 0:
656
                    currentCoefficient /= var2Bound^degreeInVar2
657
                    #print "Current coefficient(2)", currentCoefficient
658
                #print "Current reduced monomial:", (currentCoefficient * \
659
                #                                    currentMonomial)
660
                currentPolynomial += (currentCoefficient * currentMonomial)
661
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
662
                    #print "!!!! constant term !!!!"
663
                #print "Current polynomial:", currentPolynomial
664
            # End if
665
        # End for colIndex.
666
        #print "Type of the current polynomial:", type(currentPolynomial)
667
        reducedPolynomials.append(currentPolynomial)
668
    return reducedPolynomials 
669
# End slz_compute_reduced_polynomials_list_from_rows.
670
#
671
def slz_compute_scaled_function(functionSa,
672
                                lowerBoundSa,
673
                                upperBoundSa,
674
                                floatingPointPrecSa,
675
                                debug=False):
676
    """
677
    From a function, compute the scaled function whose domain
678
    is included in [1, 2) and whose image is also included in [1,2).
679
    Return a tuple: 
680
    [0]: the scaled function
681
    [1]: the scaled domain lower bound
682
    [2]: the scaled domain upper bound
683
    [3]: the scaled image lower bound
684
    [4]: the scaled image upper bound
685
    """
686
    ## The variable can be called anything.
687
    x = functionSa.variables()[0]
688
    # Scalling the domain -> [1,2[.
689
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
690
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
691
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
692
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
693
    if debug:
694
        print "domainScalingExpression for argument :", \
695
        invDomainScalingExpressionSa
696
        print "f: ", f
697
    ff = f.subs({x : domainScalingExpressionSa})
698
    #ff = f.subs_expr(x==domainScalingExpressionSa)
699
    #domainScalingFunction(x) = invDomainScalingExpressionSa
700
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
701
    scaledLowerBoundSa = \
702
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
703
    scaledUpperBoundSa = \
704
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
705
    if debug:
706
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
707
              scaledUpperBoundSa
708
    #
709
    # Scalling the image -> [1,2[.
710
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
711
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
712
    if flbSa <= fubSa: # Increasing
713
        imageBinadeBottomSa = floor(flbSa.log2())
714
    else: # Decreasing
715
        imageBinadeBottomSa = floor(fubSa.log2())
716
    if debug:
717
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
718
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
719
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
720
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
721
    if debug:
722
        print "imageScalingExpression for argument :", \
723
              invImageScalingExpressionSa
724
    iis = invImageScalingExpressionSa.function(x)
725
    fff = iis.subs({x:ff})
726
    if debug:
727
        print "fff:", fff,
728
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
729
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
730
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
731
# End slz_compute_scaled_function
732

    
733
def slz_fix_bounds_for_binades(lowerBound, 
734
                               upperBound, 
735
                               func=None, 
736
                               domainDirection = -1,
737
                               imageDirection  = -1):
738
    """
739
    Assuming the function is increasing or decreasing over the 
740
    [lowerBound, upperBound] interval, return a lower bound lb and 
741
    an upper bound ub such that:
742
    - lb and ub belong to the same binade;
743
    - func(lb) and func(ub) belong to the same binade.
744
    domainDirection indicate how bounds move to fit into the same binade:
745
    - a negative value move the upper bound down;
746
    - a positive value move the lower bound up.
747
    imageDirection indicate how bounds move in order to have their image
748
    fit into the same binade, variation of the function is also condidered.
749
    For an increasing function:
750
    - negative value moves the upper bound down (and its image value as well);
751
    - a positive values moves the lower bound up (and its image value as well);
752
    For a decreasing function it is the other way round.
753
    """
754
    ## Arguments check
755
    if lowerBound > upperBound:
756
        return None
757
    if func is None:
758
        return None
759
    #
760
    #varFunc = func.variables()[0]
761
    lb = lowerBound
762
    ub = upperBound
763
    lbBinade = slz_compute_binade(lb) 
764
    ubBinade = slz_compute_binade(ub)
765
    ## Domain binade.
766
    while lbBinade != ubBinade:
767
        newIntervalWidth = (ub - lb) / 2
768
        if domainDirection < 0:
769
            ub = lb + newIntervalWidth
770
            ubBinade = slz_compute_binade(ub)
771
        else:
772
            lb = lb + newIntervalWidth
773
            lbBinade = slz_compute_binade(lb) 
774
    ## Image binade.
775
    if lb == ub:
776
        return (lb, ub)
777
    lbImg = func(lb)
778
    ubImg = func(ub)
779
    funcIsInc = (ubImg >= lbImg)
780
    lbImgBinade = slz_compute_binade(lbImg)
781
    ubImgBinade = slz_compute_binade(ubImg)
782
    while lbImgBinade != ubImgBinade:
783
        newIntervalWidth = (ub - lb) / 2
784
        if imageDirection < 0:
785
            if funcIsInc:
786
                ub = lb + newIntervalWidth
787
                ubImgBinade = slz_compute_binade(func(ub))
788
                #print ubImgBinade
789
            else:
790
                lb = lb + newIntervalWidth
791
                lbImgBinade = slz_compute_binade(func(lb))
792
                #print lbImgBinade
793
        else:
794
            if funcIsInc:
795
                lb = lb + newIntervalWidth
796
                lbImgBinade = slz_compute_binade(func(lb))
797
                #print lbImgBinade
798
            else:
799
                ub = lb + newIntervalWidth
800
                ubImgBinade = slz_compute_binade(func(ub))
801
                #print ubImgBinade
802
    # End while lbImgBinade != ubImgBinade:
803
    return (lb, ub)
804
# End slz_fix_bounds_for_binades.
805

    
806
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
807
    # Create a polynomial over the rationals.
808
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
809
    return(ratPolynomialRing(polyOfFloat))
810
# End slz_float_poly_of_float_to_rat_poly_of_rat.
811

    
812
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
813
                                      lowerBoundSa, 
814
                                      upperBoundSa, floatingPointPrecSa, 
815
                                      internalSollyaPrecSa, approxPrecSa):
816
    """
817
    Under the assumption that:
818
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
819
    - lowerBound and upperBound belong to the same binade.
820
    from a:
821
    - function;
822
    - a degree
823
    - a pair of bounds;
824
    - the floating-point precision we work on;
825
    - the internal Sollya precision;
826
    - the requested approximation error
827
    The initial interval is, possibly, splitted into smaller intervals.
828
    It return a list of tuples, each made of:
829
    - a first polynomial (without the changed variable f(x) = p(x-x0));
830
    - a second polynomial (with a changed variable f(x) = q(x))
831
    - the approximation interval;
832
    - the center, x0, of the interval;
833
    - the corresponding approximation error.
834
    TODO: fix endless looping for some parameters sets.
835
    """
836
    resultArray = []
837
    # Set Sollya to the necessary internal precision.
838
    precChangedSa = False
839
    currentSollyaPrecSo = pobyso_get_prec_so()
840
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
841
    if internalSollyaPrecSa > currentSollyaPrecSa:
842
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
843
        precChangedSa = True
844
    #
845
    x = functionSa.variables()[0] # Actual variable name can be anything.
846
    # Scaled function: [1=,2] -> [1,2].
847
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
848
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
849
                slz_compute_scaled_function(functionSa,                       \
850
                                            lowerBoundSa,                     \
851
                                            upperBoundSa,                     \
852
                                            floatingPointPrecSa)
853
    # In case bounds were in the negative real one may need to
854
    # switch scaled bounds.
855
    if scaledLowerBoundSa > scaledUpperBoundSa:
856
        scaledLowerBoundSa, scaledUpperBoundSa = \
857
            scaledUpperBoundSa, scaledLowerBoundSa
858
        #print "Switching!"
859
    print "Approximation precision: ", RR(approxPrecSa)
860
    # Prepare the arguments for the Taylor expansion computation with Sollya.
861
    functionSo = \
862
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
863
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
864
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
865
                                                  scaledUpperBoundSa)
866

    
867
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
868
                                              upperBoundSa.parent().precision()))
869
    currentScaledLowerBoundSa = scaledLowerBoundSa
870
    currentScaledUpperBoundSa = scaledUpperBoundSa
871
    while True:
872
        ## Compute the first Taylor expansion.
873
        print "Computing a Taylor expansion..."
874
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
875
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
876
                                        currentScaledLowerBoundSa, 
877
                                        currentScaledUpperBoundSa,
878
                                        approxPrecSa, internalSollyaPrecSa)
879
        print "...done."
880
        ## If slz_compute_polynomial_and_interval fails, it returns None.
881
        #  This value goes to the first variable: polySo. Other variables are
882
        #  not assigned and should not be tested.
883
        if polySo is None:
884
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
885
            if precChangedSa:
886
                pobyso_set_prec_so_so(currentSollyaPrecSo)
887
                sollya_lib_clear_obj(currentSollyaPrecSo)
888
            sollya_lib_clear_obj(functionSo)
889
            sollya_lib_clear_obj(degreeSo)
890
            sollya_lib_clear_obj(scaledBoundsSo)
891
            return None
892
        ## Add to the result array.
893
        ### Change variable stuff in Sollya x -> x0-x.
894
        changeVarExpressionSo = \
895
            sollya_lib_build_function_sub( \
896
                              sollya_lib_build_function_free_variable(), 
897
                              sollya_lib_copy_obj(intervalCenterSo))
898
        polyVarChangedSo = \
899
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
900
        #### Get rid of the variable change Sollya stuff. 
901
        sollya_lib_clear_obj(changeVarExpressionSo)
902
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
903
                            intervalCenterSo, maxErrorSo))
904
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
905
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
906
        print "Computed approximation error:", errorSa.n(digits=10)
907
        # If the error and interval are OK a the first try, just return.
908
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
909
            (errorSa <= approxPrecSa):
910
            if precChangedSa:
911
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
912
            sollya_lib_clear_obj(currentSollyaPrecSo)
913
            sollya_lib_clear_obj(functionSo)
914
            sollya_lib_clear_obj(degreeSo)
915
            sollya_lib_clear_obj(scaledBoundsSo)
916
            #print "Approximation error:", errorSa
917
            return resultArray
918
        ## The returned interval upper bound does not reach the requested upper
919
        #  upper bound: compute the next upper bound.
920
        ## The following ratio is always >= 1. If errorSa, we may want to
921
        #  enlarge the interval
922
        currentErrorRatio = approxPrecSa / errorSa
923
        ## --|--------------------------------------------------------------|--
924
        #  --|--------------------|--------------------------------------------
925
        #  --|----------------------------|------------------------------------
926
        ## Starting point for the next upper bound.
927
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
928
        # Compute the increment. 
929
        newBoundsWidthSa = \
930
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
931
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
932
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
933
        # Take into account the original interval upper bound.                     
934
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
935
            currentScaledUpperBoundSa = scaledUpperBoundSa
936
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
937
            print "Can't shrink the interval anymore!"
938
            print "You should consider increasing the Sollya internal precision"
939
            print "or the polynomial degree."
940
            print "Giving up!"
941
            if precChangedSa:
942
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
943
            sollya_lib_clear_obj(currentSollyaPrecSo)
944
            sollya_lib_clear_obj(functionSo)
945
            sollya_lib_clear_obj(degreeSo)
946
            sollya_lib_clear_obj(scaledBoundsSo)
947
            return None
948
        # Compute the other expansions.
949
        # Test for insufficient precision.
950
# End slz_get_intervals_and_polynomials
951

    
952
def slz_interval_scaling_expression(boundsInterval, expVar):
953
    """
954
    Compute the scaling expression to map an interval that spans at most
955
    a single binade into [1, 2) and the inverse expression as well.
956
    If the interval spans more than one binade, result is wrong since
957
    scaling is only based on the lower bound.
958
    Not very sure that the transformation makes sense for negative numbers.
959
    """
960
    # The "one of the bounds is 0" special case. Here we consider
961
    # the interval as the subnormals binade.
962
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
963
        # The upper bound is (or should be) positive.
964
        if boundsInterval.endpoints()[0] == 0:
965
            if boundsInterval.endpoints()[1] == 0:
966
                return None
967
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
968
            l2     = boundsInterval.endpoints()[1].abs().log2()
969
            # The upper bound is a power of two
970
            if binade == l2:
971
                scalingCoeff    = 2^(-binade)
972
                invScalingCoeff = 2^(binade)
973
                scalingOffset   = 1
974
                return \
975
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
976
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
977
            else:
978
                scalingCoeff    = 2^(-binade-1)
979
                invScalingCoeff = 2^(binade+1)
980
                scalingOffset   = 1
981
                return((scalingCoeff * expVar + scalingOffset),\
982
                    ((expVar - scalingOffset) * invScalingCoeff))
983
        # The lower bound is (or should be) negative.
984
        if boundsInterval.endpoints()[1] == 0:
985
            if boundsInterval.endpoints()[0] == 0:
986
                return None
987
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
988
            l2     = boundsInterval.endpoints()[0].abs().log2()
989
            # The upper bound is a power of two
990
            if binade == l2:
991
                scalingCoeff    = -2^(-binade)
992
                invScalingCoeff = -2^(binade)
993
                scalingOffset   = 1
994
                return((scalingCoeff * expVar + scalingOffset),\
995
                    ((expVar - scalingOffset) * invScalingCoeff))
996
            else:
997
                scalingCoeff    = -2^(-binade-1)
998
                invScalingCoeff = -2^(binade+1)
999
                scalingOffset   = 1
1000
                return((scalingCoeff * expVar + scalingOffset),\
1001
                   ((expVar - scalingOffset) * invScalingCoeff))
1002
    # General case
1003
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1004
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1005
    # We allow for a single exception in binade spanning is when the
1006
    # "outward bound" is a power of 2 and its binade is that of the
1007
    # "inner bound" + 1.
1008
    if boundsInterval.endpoints()[0] > 0:
1009
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1010
        if lbBinade != ubBinade:
1011
            print "Different binades."
1012
            if ubL2 != ubBinade:
1013
                print "Not a power of 2."
1014
                return None
1015
            elif abs(ubBinade - lbBinade) > 1:
1016
                print "Too large span:", abs(ubBinade - lbBinade)
1017
                return None
1018
    else:
1019
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1020
        if lbBinade != ubBinade:
1021
            print "Different binades."
1022
            if lbL2 != lbBinade:
1023
                print "Not a power of 2."
1024
                return None
1025
            elif abs(ubBinade - lbBinade) > 1:
1026
                print "Too large span:", abs(ubBinade - lbBinade)
1027
                return None
1028
    #print "Lower bound binade:", binade
1029
    if boundsInterval.endpoints()[0] > 0:
1030
        return \
1031
            ((2^(-lbBinade) * expVar).function(expVar),
1032
             (2^(lbBinade) * expVar).function(expVar))
1033
    else:
1034
        return \
1035
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1036
             (-(2^(ubBinade)) * expVar).function(expVar))
1037
"""
1038
    # Code sent to attic. Should be the base for a 
1039
    # "slz_interval_translate_expression" rather than scale.
1040
    # Extra control and special cases code  added in  
1041
    # slz_interval_scaling_expression could (should ?) be added to
1042
    # this new function.
1043
    # The scaling offset is only used for negative numbers.
1044
    # When the absolute value of the lower bound is < 0.
1045
    if abs(boundsInterval.endpoints()[0]) < 1:
1046
        if boundsInterval.endpoints()[0] >= 0:
1047
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1048
            invScalingCoeff = 1/scalingCoeff
1049
            return((scalingCoeff * expVar, 
1050
                    invScalingCoeff * expVar))
1051
        else:
1052
            scalingCoeff = \
1053
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1054
            scalingOffset = -3 * scalingCoeff
1055
            return((scalingCoeff * expVar + scalingOffset,
1056
                    1/scalingCoeff * expVar + 3))
1057
    else: 
1058
        if boundsInterval.endpoints()[0] >= 0:
1059
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1060
            scalingOffset = 0
1061
            return((scalingCoeff * expVar, 
1062
                    1/scalingCoeff * expVar))
1063
        else:
1064
            scalingCoeff = \
1065
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1066
            scalingOffset =  -3 * scalingCoeff
1067
            #scalingOffset = 0
1068
            return((scalingCoeff * expVar + scalingOffset,
1069
                    1/scalingCoeff * expVar + 3))
1070
"""
1071
# End slz_interval_scaling_expression
1072
   
1073
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1074
    """
1075
    Compute the Sage version of the Taylor polynomial and it's
1076
    companion data (interval, center...)
1077
    The input parameter is a five elements tuple:
1078
    - [0]: the polyomial (without variable change), as polynomial over a
1079
           real ring;
1080
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1081
           over a real ring;
1082
    - [2]: the interval (as Sollya range);
1083
    - [3]: the interval center;
1084
    - [4]: the approximation error. 
1085
    
1086
    The function return a 5 elements tuple: formed with all the 
1087
    input elements converted into their Sollya counterpart.
1088
    """
1089
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
1090
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
1091
    intervalSa = \
1092
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1093
    centerSa = \
1094
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1095
    errorSa = \
1096
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1097
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1098
    # End slz_interval_and_polynomial_to_sage
1099

    
1100
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1101
            targetPlusOnePrecRF = None, quasiExactRF = None):
1102
    """
1103
      Check if an element (argument) of the domain of function (function)
1104
      yields a HTRN case (rounding to next) for the target precision 
1105
      (as impersonated by targetRF) for a given accuracy (targetAccuraty). 
1106
    """
1107
    ## Arguments filtering. TODO: filter the first argument for consistence.
1108
    ## If no target accuracy is given, assume it is that of the argument.
1109
    if targetRF is None:
1110
        targetRF = argument.parent()
1111
    ## Ditto for the real field holding the midpoints.
1112
    if targetPlusOnePrecRF is None:
1113
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1114
    ## Create a high accuracy RealField
1115
    if quasiExactRF is None:
1116
        quasiExactRF = RealField(1536)
1117
    #functionVariable              = function.variables()[0]
1118
    exactArgument                 = quasiExactRF(argument)
1119
    quasiExactValue               = function(exactArgument)
1120
    roundedValue                  = targetRF(quasiExactValue)
1121
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1122
    # Upper midpoint.
1123
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1124
    # Lower midpoint.
1125
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1126
    binade                        = slz_compute_binade(roundedValue)
1127
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1128
    #print "Argument:", argument
1129
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1130
    #print "Binade:", binade
1131
    #print "binadeCorrectedTargetAccuracy:", \
1132
    #binadeCorrectedTargetAccuracy.n(prec=100)
1133
    #print "binadeCorrectedTargetAccuracy:", \
1134
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1135
    #print "Upper midpoint:", \
1136
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1137
    #print "Rounded value :", \
1138
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1139
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1140
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1141
    #print "Lower midpoint:", \
1142
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1143
    ## Begining of the general case : check with the midpoint with 
1144
    #  greatest absolute value.
1145
    if quasiExactValue > 0:
1146
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1147
           binadeCorrectedTargetAccuracy:
1148
            #print "Too close to the upper midpoint: ", \ 
1149
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1150
            #print argument.n().str(base=16, \
1151
            #  no_sci=False)
1152
            #print "Lower midpoint:", \
1153
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1154
            #print "Value         :", \
1155
            #  quasiExactValue.n(prec=200).str(base=2)
1156
            #print "Upper midpoint:", \
1157
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1158
            #print
1159
            return True
1160
    else:
1161
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1162
           binadeCorrectedTargetAccuracy:
1163
            #print "Too close to the upper midpoint: ", \
1164
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1165
            #print argument.n().str(base=16, \
1166
            #  no_sci=False)
1167
            #print "Lower midpoint:", \
1168
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1169
            #print "Value         :", \
1170
            #  quasiExactValue.n(prec=200).str(base=2)
1171
            #print "Upper midpoint:", \
1172
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1173
            #print
1174
            return True
1175
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1176
    ## For the midpoint of smaller absolute value, 
1177
    #  split cases with the powers of 2.
1178
    if sno_abs_is_power_of_two(roundedValue):
1179
        if quasiExactValue > 0:        
1180
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1181
               binadeCorrectedTargetAccuracy / 2:
1182
                #print "Lower midpoint:", \
1183
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1184
                #print "Value         :", \
1185
                #  quasiExactValue.n(prec=200).str(base=2)
1186
                #print "Upper midpoint:", \
1187
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1188
                #print
1189
                return True
1190
        else:
1191
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1192
              binadeCorrectedTargetAccuracy / 2:
1193
                #print "Lower midpoint:", \
1194
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1195
                #print "Value         :", 
1196
                #  quasiExactValue.n(prec=200).str(base=2)
1197
                #print "Upper midpoint:", 
1198
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1199
                #print
1200
                return True
1201
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1202
    else: ## Not a power of 2, regular comparison with the midpoint of 
1203
          #  smaller absolute value.
1204
        if quasiExactValue > 0:        
1205
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1206
              binadeCorrectedTargetAccuracy:
1207
                #print "Lower midpoint:", \
1208
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1209
                #print "Value         :", \
1210
                #  quasiExactValue.n(prec=200).str(base=2)
1211
                #print "Upper midpoint:", \
1212
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1213
                #print
1214
                return True
1215
        else: # quasiExactValue <= 0
1216
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1217
              binadeCorrectedTargetAccuracy:
1218
                #print "Lower midpoint:", \
1219
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1220
                #print "Value         :", \
1221
                #  quasiExactValue.n(prec=200).str(base=2)
1222
                #print "Upper midpoint:", \
1223
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1224
                #print
1225
                return True
1226
    #print "distance to the upper midpoint:", \
1227
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1228
    #print "distance to the lower midpoint:", \
1229
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1230
    return False
1231
# End slz_is_htrn
1232

    
1233
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1234
                                                precision,
1235
                                                targetHardnessToRound,
1236
                                                variable1,
1237
                                                variable2):
1238
    """
1239
    Creates a new multivariate polynomial with integer coefficients for use
1240
     with the Coppersmith method.
1241
    A the same time it computes :
1242
    - 2^K (N);
1243
    - 2^k (bound on the second variable)
1244
    - lcm
1245

    
1246
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1247
                         variables.
1248
    :param precision: the precision of the floating-point coefficients.
1249
    :param targetHardnessToRound: the hardness to round we want to check.
1250
    :param variable1: the first variable of the polynomial (an expression).
1251
    :param variable2: the second variable of the polynomial (an expression).
1252
    
1253
    :returns: a 4 elements tuple:
1254
                - the polynomial;
1255
                - the modulus (N);
1256
                - the t bound;
1257
                - the lcm used to compute the integral coefficients and the 
1258
                  module.
1259
    """
1260
    # Create a new integer polynomial ring.
1261
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1262
    # Coefficients are issued in the increasing power order.
1263
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1264
    # Print the reversed list for debugging.
1265
    #print
1266
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1267
    # Build the list of number we compute the lcm of.
1268
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1269
    #print "Coefficient denominators:", coefficientDenominators
1270
    coefficientDenominators.append(2^precision)
1271
    coefficientDenominators.append(2^(targetHardnessToRound))
1272
    leastCommonMultiple = lcm(coefficientDenominators)
1273
    # Compute the expression corresponding to the new polynomial
1274
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1275
    #print coefficientNumerators
1276
    polynomialExpression = 0
1277
    power = 0
1278
    # Iterate over two lists at the same time, stop when the shorter
1279
    # (is this case coefficientsNumerators) is 
1280
    # exhausted. Both lists are ordered in the order of increasing powers
1281
    # of variable1.
1282
    for numerator, denominator in \
1283
                        zip(coefficientNumerators, coefficientDenominators):
1284
        multiplicator = leastCommonMultiple / denominator 
1285
        newCoefficient = numerator * multiplicator
1286
        polynomialExpression += newCoefficient * variable1^power
1287
        power +=1
1288
    polynomialExpression += - variable2
1289
    return (IP(polynomialExpression),
1290
            leastCommonMultiple / 2^precision, # 2^K aka N.
1291
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1292
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1293
            leastCommonMultiple) # If we want to make test computations.
1294
        
1295
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1296

    
1297
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1298
                                          precision):
1299
    """
1300
    Makes a variable substitution into the input polynomial so that the output
1301
    polynomial can take integer arguments.
1302
    All variables of the input polynomial "have precision p". That is to say
1303
    that they are rationals with denominator == 2^(precision - 1): 
1304
    x = y/2^(precision - 1).
1305
    We "incorporate" these denominators into the coefficients with, 
1306
    respectively, the "right" power.
1307
    """
1308
    polynomialField = ratPolyOfRat.parent()
1309
    polynomialVariable = ratPolyOfRat.variables()[0]
1310
    #print "The polynomial field is:", polynomialField
1311
    return \
1312
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1313
                                   polynomialVariable/2^(precision-1)}))
1314
    
1315
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1316

    
1317
def slz_reduce_and_test_base(matrixToReduce,
1318
                             nAtAlpha,
1319
                             monomialsCountSqrt):
1320
    """
1321
    Reduces the basis, tests the Coppersmith condition and returns
1322
    a list of rows that comply with the condition.
1323
    """
1324
    ccComplientRowsList = []
1325
    reducedMatrix = matrixToReduce.LLL(None)
1326
    if not reducedMatrix.is_LLL_reduced():
1327
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1328
    else:
1329
        print "reducedMatrix is actually reduced."
1330
    print "N^alpha:", nAtAlpha.n()
1331
    rowIndex = 0
1332
    for row in reducedMatrix.rows():
1333
        l2Norm = row.norm(2)
1334
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1335
                monomialsCountSqrt.n(), ". Is vector OK?", 
1336
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1337
            ccComplientRowsList.append(row)
1338
            print "True"
1339
        else:
1340
            print "False"
1341
    # End for
1342
    return ccComplientRowsList
1343
# End slz_reduce_and_test_base
1344

    
1345
def slz_resultant_tuple(poly1, poly2, elimVar):
1346
    """
1347
    Compute the resultant for two polynomials for a given variable
1348
    and return the (poly1, poly2, resultant) if the resultant
1349
    is not 0.
1350
    Return () otherwise.
1351
    """
1352
    polynomialRing0 = poly1.parent()
1353
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1354
    if resultantInElimVar.is_zero():
1355
        return ()
1356
    else:
1357
        return (poly1, poly2, resultantInElimVar)
1358
# End slz_resultant_tuple.
1359
#
1360
print "\t...sageSLZ loaded"