Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (52,06 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_check_htr_value(function, htrValue, lowerBound, upperBound, precision, \
14
                        degree, targetHardnessToRound, alpha):
15
    """
16
    Check an Hard-to-round value.
17
    TODO::
18
    Full rewriting: this is hardly a draft.
19
    """
20
    polyApproxPrec = targetHardnessToRound + 1
21
    polyTargetHardnessToRound = targetHardnessToRound + 1
22
    internalSollyaPrec = ceil((RR('1.5') * targetHardnessToRound) / 64) * 64
23
    RRR = htrValue.parent()
24
    #
25
    ## Compute the scaled function.
26
    fff = slz_compute_scaled_function(f, lowerBound, upperBound, precision)[0]
27
    print "Scaled function:", fff
28
    #
29
    ## Compute the scaling.
30
    boundsIntervalRifSa = RealIntervalField(precision)
31
    domainBoundsInterval = boundsIntervalRifSa(lowerBound, upperBound)
32
    scalingExpressions = \
33
        slz_interval_scaling_expression(domainBoundsInterval, i)
34
    #
35
    ## Get the polynomials, bounds, etc. for all the interval.
36
    resultListOfTuplesOfSo = \
37
        slz_get_intervals_and_polynomials(f, degree, lowerBound, upperBound, \
38
                                          precision, internalSollyaPrec,\
39
                                          2^-(polyApproxPrec))
40
    #
41
    ## We only want one interval.
42
    if len(resultListOfTuplesOfSo) > 1:
43
        print "Too many intervals! Aborting!"
44
        exit
45
    #
46
    ## Get the first tuple of Sollya objects as Sage objects. 
47
    firstTupleSa = \
48
        slz_interval_and_polynomial_to_sage(resultListOfTuplesOfSo[0])
49
    pobyso_set_canonical_on()
50
    #
51
    print "Floatting point polynomial:", firstTupleSa[0]
52
    print "with coefficients precision:", firstTupleSa[0].base_ring().prec()
53
    #
54
    ## From a polynomial over a real ring, create a polynomial over the 
55
    #  rationals ring.
56
    rationalPolynomial = \
57
        slz_float_poly_of_float_to_rat_poly_of_rat(firstTupleSa[0])
58
    print "Rational polynomial:", rationalPolynomial
59
    #
60
    ## Create a polynomial over the rationals that will take integer 
61
    # variables instead of rational. 
62
    rationalPolynomialOfIntegers = \
63
        slz_rat_poly_of_rat_to_rat_poly_of_int(rationalPolynomial, precision)
64
    print "Type:", type(rationalPolynomialOfIntegers)
65
    print "Rational polynomial of integers:", rationalPolynomialOfIntegers
66
    #
67
    ## Check the rational polynomial of integers variables.
68
    # (check against the scaled function).
69
    toIntegerFactor = 2^(precision-1)
70
    intervalCenterAsIntegerSa = int(firstTupleSa[3] * toIntegerFactor)
71
    print "Interval center as integer:", intervalCenterAsIntegerSa
72
    lowerBoundAsIntegerSa = int(firstTupleSa[2].endpoints()[0] * \
73
                                toIntegerFactor) - intervalCenterAsIntegerSa
74
    upperBoundAsIntegerSa = int(firstTupleSa[2].endpoints()[1] * \
75
                                toIntegerFactor) - intervalCenterAsIntegerSa
76
    print "Lower bound as integer:", lowerBoundAsIntegerSa
77
    print "Upper bound as integer:", upperBoundAsIntegerSa
78
    print "Image of the lower bound by the scaled function", \
79
            fff(firstTupleSa[2].endpoints()[0])
80
    print "Image of the lower bound by the approximation polynomial of ints:", \
81
            RRR(rationalPolynomialOfIntegers(lowerBoundAsIntegerSa))
82
    print "Image of the center by the scaled function", fff(firstTupleSa[3])
83
    print "Image of the center by the approximation polynomial of ints:", \
84
            RRR(rationalPolynomialOfIntegers(0))
85
    print "Image of the upper bound by the scaled function", \
86
            fff(firstTupleSa[2].endpoints()[1])
87
    print "Image of the upper bound by the approximation polynomial of ints:", \
88
            RRR(rationalPolynomialOfIntegers(upperBoundAsIntegerSa))
89

    
90
# End slz_check_htr_value.
91

    
92
def slz_compute_binade(number):
93
    """"
94
    For a given number, compute the "binade" that is integer m such that
95
    2^m <= number < 2^(m+1). If number == 0 return None.
96
    """
97
    # Checking the parameter.
98
    # The exception construction is used to detect if numbre is a RealNumber
99
    # since not all numbers have
100
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
101
    try:
102
        classTree = [number.__class__] + number.mro()
103
        # If the number is not a RealNumber (of offspring thereof) try
104
        # to transform it.
105
        if not sage.rings.real_mpfr.RealNumber in classTree:
106
            numberAsRR = RR(number)
107
        else:
108
            numberAsRR = number
109
    except AttributeError:
110
        return None
111
    # Zero special case.
112
    if numberAsRR == 0:
113
        return RR(-infinity)
114
    else:
115
        return floor(numberAsRR.abs().log2())
116
# End slz_compute_binade
117

    
118
#
119
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
120
    """
121
    For given "real number", compute the bounds of the binade it belongs to.
122
    
123
    NOTE::
124
        When number >= 2^(emax+1), we return the "fake" binade 
125
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
126
        with interval [-infinity, -2^(emax+1)]. We want to distinguish
127
        this case from that of "really" invalid arguments.
128
        
129
    """
130
    # Check the parameters.
131
    # RealNumbers or RealNumber offspring only.
132
    # The exception construction is necessary since not all objects have
133
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
134
    try:
135
        classTree = [number.__class__] + number.mro()
136
        if not sage.rings.real_mpfr.RealNumber in classTree:
137
            return None
138
    except AttributeError:
139
        return None
140
    # Non zero negative integers only for emin.
141
    if emin >= 0 or int(emin) != emin:
142
        return None
143
    # Non zero positive integers only for emax.
144
    if emax <= 0 or int(emax) != emax:
145
        return None
146
    precision = number.precision()
147
    RF  = RealField(precision)
148
    if number == 0:
149
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
150
    # A more precise RealField is needed to avoid unwanted rounding effects
151
    # when computing number.log2().
152
    RRF = RealField(max(2048, 2 * precision))
153
    # number = 0 special case, the binade bounds are 
154
    # [0, 2^emin - 2^(emin-precision)]
155
    # Begin general case
156
    l2 = RRF(number).abs().log2()
157
    # Another special one: beyond largest representable -> "Fake" binade.
158
    if l2 >= emax + 1:
159
        if number > 0:
160
            return (RF(2^(emax+1)), RF(+infinity) )
161
        else:
162
            return (RF(-infinity), -RF(2^(emax+1)))
163
    # Regular case cont'd.
164
    offset = int(l2)
165
    # number.abs() >= 1.
166
    if l2 >= 0:
167
        if number >= 0:
168
            lb = RF(2^offset)
169
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
170
        else: #number < 0
171
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
172
            ub = -RF(2^offset)
173
    else: # log2 < 0, number.abs() < 1.
174
        if l2 < emin: # Denormal
175
           # print "Denormal:", l2
176
            if number >= 0:
177
                lb = RF(0)
178
                ub = RF(2^(emin)) - RF(2^(emin-precision))
179
            else: # number <= 0
180
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
181
                ub = RF(0)
182
        elif l2 > emin: # Normal number other than +/-2^emin.
183
            if number >= 0:
184
                if int(l2) == l2:
185
                    lb = RF(2^(offset))
186
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
187
                else:
188
                    lb = RF(2^(offset-1))
189
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
190
            else: # number < 0
191
                if int(l2) == l2: # Binade limit.
192
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
193
                    ub = -RF(2^(offset))
194
                else:
195
                    lb = -RF(2^(offset) - 2^(-precision+offset))
196
                    ub = -RF(2^(offset-1))
197
        else: # l2== emin, number == +/-2^emin
198
            if number >= 0:
199
                lb = RF(2^(offset))
200
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
201
            else: # number < 0
202
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
203
                ub = -RF(2^(offset))
204
    return (lb, ub)
205
# End slz_compute_binade_bounds
206
#
207
def slz_compute_coppersmith_reduced_polynomials(inputPolynomial,
208
                                                 alpha,
209
                                                 N,
210
                                                 iBound,
211
                                                 tBound):
212
    """
213
    For a given set of arguments (see below), compute a list
214
    of "reduced polynomials" that could be used to compute roots
215
    of the inputPolynomial.
216
    INPUT:
217
    
218
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
219
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
220
    - "N" -- the modulus;
221
    - "iBound" -- the bound on the first variable;
222
    - "tBound" -- the bound on the second variable.
223
    
224
    OUTPUT:
225
    
226
    A list of bivariate integer polynomial obtained using the Coppersmith
227
    algorithm. The polynomials correspond to the rows of the LLL-reduce
228
    reduced base that comply with the Coppersmith condition.
229
    """
230
    # Arguments check.
231
    if iBound == 0 or tBound == 0:
232
        return ()
233
    # End arguments check.
234
    nAtAlpha = N^alpha
235
    ## Building polynomials for matrix.
236
    polyRing   = inputPolynomial.parent()
237
    # Whatever the 2 variables are actually called, we call them
238
    # 'i' and 't' in all the variable names.
239
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
240
    #print polyVars[0], type(polyVars[0])
241
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
242
                                              tVariable:tVariable * tBound})
243
    polynomialsList = \
244
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
245
                                             alpha,
246
                                             N,
247
                                             iBound,
248
                                             tBound,
249
                                             0)
250
    #print "Polynomials list:", polynomialsList
251
    ## Building the proto matrix.
252
    knownMonomials = []
253
    protoMatrix    = []
254
    for poly in polynomialsList:
255
        spo_add_polynomial_coeffs_to_matrix_row(poly,
256
                                                knownMonomials,
257
                                                protoMatrix,
258
                                                0)
259
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
260
    #print matrixToReduce
261
    ## Reduction and checking.
262
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
263
    #  error message issued when previous code was used.
264
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
265
    reducedMatrix = matrixToReduce.LLL(fp=None)
266
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
267
    if not isLLLReduced:
268
        return ()
269
    monomialsCount     = len(knownMonomials)
270
    monomialsCountSqrt = sqrt(monomialsCount)
271
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
272
    #print reducedMatrix
273
    ## Check the Coppersmith condition for each row and build the reduced 
274
    #  polynomials.
275
    ccReducedPolynomialsList = []
276
    for row in reducedMatrix.rows():
277
        l2Norm = row.norm(2)
278
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
279
            #print (l2Norm * monomialsCountSqrt).n()
280
            #print l2Norm.n()
281
            ccReducedPolynomial = \
282
                slz_compute_reduced_polynomial(row,
283
                                               knownMonomials,
284
                                               iVariable,
285
                                               iBound,
286
                                               tVariable,
287
                                               tBound)
288
            if not ccReducedPolynomial is None:
289
                ccReducedPolynomialsList.append(ccReducedPolynomial)
290
        else:
291
            #print l2Norm.n() , ">", nAtAlpha
292
            pass
293
    if len(ccReducedPolynomialsList) < 2:
294
        print "Less than 2 Coppersmith condition compliant vectors."
295
        return ()
296
    
297
    #print ccReducedPolynomialsList
298
    return ccReducedPolynomialsList
299
# End slz_compute_coppersmith_reduced_polynomials
300

    
301
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
302
                                                 alpha,
303
                                                 N,
304
                                                 iBound,
305
                                                 tBound):
306
    """
307
    For a given set of arguments (see below), compute the polynomial modular 
308
    roots, if any.
309
    
310
    """
311
    # Arguments check.
312
    if iBound == 0 or tBound == 0:
313
        return set()
314
    # End arguments check.
315
    nAtAlpha = N^alpha
316
    ## Building polynomials for matrix.
317
    polyRing   = inputPolynomial.parent()
318
    # Whatever the 2 variables are actually called, we call them
319
    # 'i' and 't' in all the variable names.
320
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
321
    ccReducedPolynomialsList = \
322
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
323
                                                     alpha,
324
                                                     N,
325
                                                     iBound,
326
                                                     tBound)
327
    if len(ccReducedPolynomialsList) == 0:
328
        return set()   
329
    ## Create the valid (poly1 and poly2 are algebraically independent) 
330
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
331
    # Try to mix and match all the polynomial pairs built from the 
332
    # ccReducedPolynomialsList to obtain non zero resultants.
333
    resultantsInITuplesList = []
334
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
335
        for polyInnerIndex in xrange(polyOuterIndex+1, 
336
                                     len(ccReducedPolynomialsList)):
337
            # Compute the resultant in resultants in the
338
            # first variable (is it the optimal choice?).
339
            resultantInI = \
340
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
341
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
342
            #print "Resultant", resultantInI
343
            # Test algebraic independence.
344
            if not resultantInI.is_zero():
345
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
346
                                                 ccReducedPolynomialsList[polyInnerIndex],
347
                                                 resultantInI))
348
    # If no non zero resultant was found: we can't get no algebraically 
349
    # independent polynomials pair. Give up!
350
    if len(resultantsInITuplesList) == 0:
351
        return set()
352
    #print resultantsInITuplesList
353
    # Compute the roots.
354
    Zi = ZZ[str(iVariable)]
355
    Zt = ZZ[str(tVariable)]
356
    polynomialRootsSet = set()
357
    # First, solve in the second variable since resultants are in the first
358
    # variable.
359
    for resultantInITuple in resultantsInITuplesList:
360
        tRootsList = Zt(resultantInITuple[2]).roots()
361
        # For each tRoot, compute the corresponding iRoots and check 
362
        # them in the input polynomial.
363
        for tRoot in tRootsList:
364
            #print "tRoot:", tRoot
365
            # Roots returned by root() are (value, multiplicity) tuples.
366
            iRootsList = \
367
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
368
            print iRootsList
369
            # The iRootsList can be empty, hence the test.
370
            if len(iRootsList) != 0:
371
                for iRoot in iRootsList:
372
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
373
                    # polyEvalModN must be an integer.
374
                    if polyEvalModN == int(polyEvalModN):
375
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
376
    return polynomialRootsSet
377
# End slz_compute_integer_polynomial_modular_roots.
378
#
379
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
380
                                                 alpha,
381
                                                 N,
382
                                                 iBound,
383
                                                 tBound):
384
    """
385
    For a given set of arguments (see below), compute the polynomial modular 
386
    roots, if any.
387
    This version differs in the way resultants are computed.   
388
    """
389
    # Arguments check.
390
    if iBound == 0 or tBound == 0:
391
        return set()
392
    # End arguments check.
393
    nAtAlpha = N^alpha
394
    ## Building polynomials for matrix.
395
    polyRing   = inputPolynomial.parent()
396
    # Whatever the 2 variables are actually called, we call them
397
    # 'i' and 't' in all the variable names.
398
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
399
    #print polyVars[0], type(polyVars[0])
400
    ccReducedPolynomialsList = \
401
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
402
                                                     alpha,
403
                                                     N,
404
                                                     iBound,
405
                                                     tBound)
406
    if len(ccReducedPolynomialsList) == 0:
407
        return set()   
408
    ## Create the valid (poly1 and poly2 are algebraically independent) 
409
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
410
    # Try to mix and match all the polynomial pairs built from the 
411
    # ccReducedPolynomialsList to obtain non zero resultants.
412
    resultantsInTTuplesList = []
413
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
414
        for polyInnerIndex in xrange(polyOuterIndex+1, 
415
                                     len(ccReducedPolynomialsList)):
416
            # Compute the resultant in resultants in the
417
            # first variable (is it the optimal choice?).
418
            resultantInT = \
419
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
420
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
421
            #print "Resultant", resultantInT
422
            # Test algebraic independence.
423
            if not resultantInT.is_zero():
424
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
425
                                                 ccReducedPolynomialsList[polyInnerIndex],
426
                                                 resultantInT))
427
    # If no non zero resultant was found: we can't get no algebraically 
428
    # independent polynomials pair. Give up!
429
    if len(resultantsInTTuplesList) == 0:
430
        return set()
431
    #print resultantsInITuplesList
432
    # Compute the roots.
433
    Zi = ZZ[str(iVariable)]
434
    Zt = ZZ[str(tVariable)]
435
    polynomialRootsSet = set()
436
    # First, solve in the second variable since resultants are in the first
437
    # variable.
438
    for resultantInTTuple in resultantsInTTuplesList:
439
        iRootsList = Zi(resultantInTTuple[2]).roots()
440
        # For each iRoot, compute the corresponding tRoots and check 
441
        # them in the input polynomial.
442
        for iRoot in iRootsList:
443
            #print "iRoot:", iRoot
444
            # Roots returned by root() are (value, multiplicity) tuples.
445
            tRootsList = \
446
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
447
            print tRootsList
448
            # The tRootsList can be empty, hence the test.
449
            if len(tRootsList) != 0:
450
                for tRoot in tRootsList:
451
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
452
                    # polyEvalModN must be an integer.
453
                    if polyEvalModN == int(polyEvalModN):
454
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
455
    return polynomialRootsSet
456
# End slz_compute_integer_polynomial_modular_roots_2.
457
#
458
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
459
                                        upperBoundSa, approxPrecSa, 
460
                                        sollyaPrecSa=None):
461
    """
462
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
463
    a polynomial that approximates the function on a an interval starting
464
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
465
    approximates with the expected precision.
466
    The interval upper bound is lowered until the expected approximation 
467
    precision is reached.
468
    The polynomial, the bounds, the center of the interval and the error
469
    are returned.
470
    OUTPUT:
471
    A tuple made of 4 Sollya objects:
472
    - a polynomial;
473
    - an range (an interval, not in the sense of number given as an interval);
474
    - the center of the interval;
475
    - the maximum error in the approximation of the input functionSo by the
476
      output polynomial ; this error <= approxPrecSaS.
477
    
478
    """
479
    ## Superficial argument check.
480
    if lowerBoundSa > upperBoundSa:
481
        return None
482
    RRR = lowerBoundSa.parent()
483
    intervalShrinkConstFactorSa = RRR('0.8')
484
    absoluteErrorTypeSo = pobyso_absolute_so_so()
485
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
486
    currentUpperBoundSa = upperBoundSa
487
    currentLowerBoundSa = lowerBoundSa
488
    # What we want here is the polynomial without the variable change, 
489
    # since our actual variable will be x-intervalCenter defined over the 
490
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
491
    (polySo, intervalCenterSo, maxErrorSo) = \
492
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
493
                                                    currentRangeSo, 
494
                                                    absoluteErrorTypeSo)
495
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
496
    while maxErrorSa > approxPrecSa:
497
        print "++Approximation error:", maxErrorSa
498
        sollya_lib_clear_obj(polySo)
499
        sollya_lib_clear_obj(intervalCenterSo)
500
        sollya_lib_clear_obj(maxErrorSo)
501
        shrinkFactorSa = RRR('5')/(maxErrorSa/approxPrecSa).log2().abs()
502
        #shrinkFactorSa = 1.5/(maxErrorSa/approxPrecSa)
503
        #errorRatioSa = approxPrecSa/maxErrorSa
504
        #print "Error ratio: ", errorRatioSa
505
        if shrinkFactorSa > intervalShrinkConstFactorSa:
506
            actualShrinkFactorSa = intervalShrinkConstFactorSa
507
            #print "Fixed"
508
        else:
509
            actualShrinkFactorSa = shrinkFactorSa
510
            #print "Computed",shrinkFactorSa,maxErrorSa
511
            #print shrinkFactorSa, maxErrorSa
512
        #print "Shrink factor", actualShrinkFactorSa
513
        currentUpperBoundSa = currentLowerBoundSa + \
514
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
515
                                   actualShrinkFactorSa
516
        #print "Current upper bound:", currentUpperBoundSa
517
        sollya_lib_clear_obj(currentRangeSo)
518
        if currentUpperBoundSa <= currentLowerBoundSa or \
519
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
520
            sollya_lib_clear_obj(absoluteErrorTypeSo)
521
            print "Can't find an interval."
522
            print "Use either or both a higher polynomial degree or a higher",
523
            print "internal precision."
524
            print "Aborting!"
525
            return (None, None, None, None)
526
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
527
                                                      currentUpperBoundSa)
528
        # print "New interval:",
529
        # pobyso_autoprint(currentRangeSo)
530
        #print "Second Taylor expansion call."
531
        (polySo, intervalCenterSo, maxErrorSo) = \
532
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
533
                                                        currentRangeSo, 
534
                                                        absoluteErrorTypeSo)
535
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
536
        #print "Max errorSo:",
537
        #pobyso_autoprint(maxErrorSo)
538
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
539
        #print "Max errorSa:", maxErrorSa
540
        #print "Sollya prec:",
541
        #pobyso_autoprint(sollya_lib_get_prec(None))
542
    sollya_lib_clear_obj(absoluteErrorTypeSo)
543
    return((polySo, currentRangeSo, intervalCenterSo, maxErrorSo))
544
# End slz_compute_polynomial_and_interval
545

    
546
def slz_compute_reduced_polynomial(matrixRow,
547
                                    knownMonomials,
548
                                    var1,
549
                                    var1Bound,
550
                                    var2,
551
                                    var2Bound):
552
    """
553
    Compute a polynomial from a single reduced matrix row.
554
    This function was introduced in order to avoid the computation of the
555
    all the polynomials  from the full matrix (even those built from rows 
556
    that do no verify the Coppersmith condition) as this may involves 
557
    expensive operations over (large) integers.
558
    """
559
    ## Check arguments.
560
    if len(knownMonomials) == 0:
561
        return None
562
    # varNounds can be zero since 0^0 returns 1.
563
    if (var1Bound < 0) or (var2Bound < 0):
564
        return None
565
    ## Initialisations. 
566
    polynomialRing = knownMonomials[0].parent() 
567
    currentPolynomial = polynomialRing(0)
568
    # TODO: use zip instead of indices.
569
    for colIndex in xrange(0, len(knownMonomials)):
570
        currentCoefficient = matrixRow[colIndex]
571
        if currentCoefficient != 0:
572
            #print "Current coefficient:", currentCoefficient
573
            currentMonomial = knownMonomials[colIndex]
574
            #print "Monomial as multivariate polynomial:", \
575
            #currentMonomial, type(currentMonomial)
576
            degreeInVar1 = currentMonomial.degree(var1)
577
            #print "Degree in var1", var1, ":", degreeInVar1
578
            degreeInVar2 = currentMonomial.degree(var2)
579
            #print "Degree in var2", var2, ":", degreeInVar2
580
            if degreeInVar1 > 0:
581
                currentCoefficient = \
582
                    currentCoefficient / (var1Bound^degreeInVar1)
583
                #print "varBound1 in degree:", var1Bound^degreeInVar1
584
                #print "Current coefficient(1)", currentCoefficient
585
            if degreeInVar2 > 0:
586
                currentCoefficient = \
587
                    currentCoefficient / (var2Bound^degreeInVar2)
588
                #print "Current coefficient(2)", currentCoefficient
589
            #print "Current reduced monomial:", (currentCoefficient * \
590
            #                                    currentMonomial)
591
            currentPolynomial += (currentCoefficient * currentMonomial)
592
            #print "Current polynomial:", currentPolynomial
593
        # End if
594
    # End for colIndex.
595
    #print "Type of the current polynomial:", type(currentPolynomial)
596
    return(currentPolynomial)
597
# End slz_compute_reduced_polynomial
598
#
599
def slz_compute_reduced_polynomials(reducedMatrix,
600
                                        knownMonomials,
601
                                        var1,
602
                                        var1Bound,
603
                                        var2,
604
                                        var2Bound):
605
    """
606
    Legacy function, use slz_compute_reduced_polynomials_list
607
    """
608
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
609
                                                knownMonomials,
610
                                                var1,
611
                                                var1Bound,
612
                                                var2,
613
                                                var2Bound)
614
    )
615
def slz_compute_reduced_polynomials_list(reducedMatrix,
616
                                         knownMonomials,
617
                                         var1,
618
                                         var1Bound,
619
                                         var2,
620
                                         var2Bound):
621
    """
622
    From a reduced matrix, holding the coefficients, from a monomials list,
623
    from the bounds of each variable, compute the corresponding polynomials
624
    scaled back by dividing by the "right" powers of the variables bounds.
625
    
626
    The elements in knownMonomials must be of the "right" polynomial type.
627
    They set the polynomial type of the output polynomials list.
628
    @param reducedMatrix:  the reduced matrix as output from LLL;
629
    @param kwnonMonomials: the ordered list of the monomials used to
630
                           build the polynomials;
631
    @param var1:           the first variable (of the "right" type);
632
    @param var1Bound:      the first variable bound;
633
    @param var2:           the second variable (of the "right" type);
634
    @param var2Bound:      the second variable bound.
635
    @return: a list of polynomials obtained with the reduced coefficients
636
             and scaled down with the bounds
637
    """
638
    
639
    # TODO: check input arguments.
640
    reducedPolynomials = []
641
    #print "type var1:", type(var1), " - type var2:", type(var2)
642
    for matrixRow in reducedMatrix.rows():
643
        currentPolynomial = 0
644
        for colIndex in xrange(0, len(knownMonomials)):
645
            currentCoefficient = matrixRow[colIndex]
646
            if currentCoefficient != 0:
647
                #print "Current coefficient:", currentCoefficient
648
                currentMonomial = knownMonomials[colIndex]
649
                parentRing = currentMonomial.parent()
650
                #print "Monomial as multivariate polynomial:", \
651
                #currentMonomial, type(currentMonomial)
652
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
653
                #print "Degree in var", var1, ":", degreeInVar1
654
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
655
                #print "Degree in var", var2, ":", degreeInVar2
656
                if degreeInVar1 > 0:
657
                    currentCoefficient = \
658
                        currentCoefficient / var1Bound^degreeInVar1
659
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
660
                    #print "Current coefficient(1)", currentCoefficient
661
                if degreeInVar2 > 0:
662
                    currentCoefficient = \
663
                        currentCoefficient / var2Bound^degreeInVar2
664
                    #print "Current coefficient(2)", currentCoefficient
665
                #print "Current reduced monomial:", (currentCoefficient * \
666
                #                                    currentMonomial)
667
                currentPolynomial += (currentCoefficient * currentMonomial)
668
                #print "Current polynomial:", currentPolynomial
669
            # End if
670
        # End for colIndex.
671
        #print "Type of the current polynomial:", type(currentPolynomial)
672
        reducedPolynomials.append(currentPolynomial)
673
    return reducedPolynomials 
674
# End slz_compute_reduced_polynomials.
675

    
676
def slz_compute_scaled_function(functionSa,
677
                                lowerBoundSa,
678
                                upperBoundSa,
679
                                floatingPointPrecSa,
680
                                debug=False):
681
    """
682
    From a function, compute the scaled function whose domain
683
    is included in [1, 2) and whose image is also included in [1,2).
684
    Return a tuple: 
685
    [0]: the scaled function
686
    [1]: the scaled domain lower bound
687
    [2]: the scaled domain upper bound
688
    [3]: the scaled image lower bound
689
    [4]: the scaled image upper bound
690
    """
691
    x = functionSa.variables()[0]
692
    # Reassert f as a function (an not a mere expression).
693
    
694
    # Scalling the domain -> [1,2[.
695
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
696
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
697
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
698
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
699
    if debug:
700
        print "domainScalingExpression for argument :", \
701
        invDomainScalingExpressionSa
702
        print "f: ", f
703
    ff = f.subs({x : domainScalingExpressionSa})
704
    #ff = f.subs_expr(x==domainScalingExpressionSa)
705
    domainScalingFunction(x) = invDomainScalingExpressionSa
706
    scaledLowerBoundSa = \
707
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
708
    scaledUpperBoundSa = \
709
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
710
    if debug:
711
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
712
              scaledUpperBoundSa
713
    #
714
    # Scalling the image -> [1,2[.
715
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
716
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
717
    if flbSa <= fubSa: # Increasing
718
        imageBinadeBottomSa = floor(flbSa.log2())
719
    else: # Decreasing
720
        imageBinadeBottomSa = floor(fubSa.log2())
721
    if debug:
722
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
723
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
724
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
725
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
726
    if debug:
727
        print "imageScalingExpression for argument :", \
728
              invImageScalingExpressionSa
729
    iis = invImageScalingExpressionSa.function(x)
730
    fff = iis.subs({x:ff})
731
    if debug:
732
        print "fff:", fff,
733
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
734
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
735
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
736
# End slz_compute_scaled_function
737

    
738
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
739
    # Create a polynomial over the rationals.
740
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
741
    return(polynomialRing(polyOfFloat))
742
# End slz_float_poly_of_float_to_rat_poly_of_rat.
743

    
744
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
745
                                      lowerBoundSa, 
746
                                      upperBoundSa, floatingPointPrecSa, 
747
                                      internalSollyaPrecSa, approxPrecSa):
748
    """
749
    Under the assumption that:
750
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
751
    - lowerBound and upperBound belong to the same binade.
752
    from a:
753
    - function;
754
    - a degree
755
    - a pair of bounds;
756
    - the floating-point precision we work on;
757
    - the internal Sollya precision;
758
    - the requested approximation error
759
    The initial interval is, possibly, splitted into smaller intervals.
760
    It return a list of tuples, each made of:
761
    - a first polynomial (without the changed variable f(x) = p(x-x0));
762
    - a second polynomial (with a changed variable f(x) = q(x))
763
    - the approximation interval;
764
    - the center, x0, of the interval;
765
    - the corresponding approximation error.
766
    TODO: fix endless looping for some parameters sets.
767
    """
768
    resultArray = []
769
    # Set Sollya to the necessary internal precision.
770
    precChangedSa = False
771
    currentSollyaPrecSo = pobyso_get_prec_so()
772
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
773
    if internalSollyaPrecSa > currentSollyaPrecSa:
774
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
775
        precChangedSa = True
776
    #
777
    x = functionSa.variables()[0] # Actual variable name can be anything.
778
    # Scaled function: [1=,2] -> [1,2].
779
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
780
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
781
                slz_compute_scaled_function(functionSa,                       \
782
                                            lowerBoundSa,                     \
783
                                            upperBoundSa,                     \
784
                                            floatingPointPrecSa)
785
    # In case bounds were in the negative real one may need to
786
    # switch scaled bounds.
787
    if scaledLowerBoundSa > scaledUpperBoundSa:
788
        scaledLowerBoundSa, scaledUpperBoundSa = \
789
            scaledUpperBoundSa, scaledLowerBoundSa
790
        #print "Switching!"
791
    print "Approximation precision: ", RR(approxPrecSa)
792
    # Prepare the arguments for the Taylor expansion computation with Sollya.
793
    functionSo = \
794
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
795
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
796
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
797
                                                  scaledUpperBoundSa)
798
    # Compute the first Taylor expansion.
799
    print "Computing first Taylor expansion..."
800
    (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
801
        slz_compute_polynomial_and_interval(functionSo, degreeSo,
802
                                        scaledLowerBoundSa, scaledUpperBoundSa,
803
                                        approxPrecSa, internalSollyaPrecSa)
804
    print "...done."
805
    if polySo is None:
806
        print "slz_get_intervals_and_polynomials: Aborting and returning None!"
807
        if precChangedSa:
808
            pobyso_set_prec_so_so(currentSollyaPrecSo)
809
            sollya_lib_clear_obj(currentSollyaPrecSo)
810
        sollya_lib_clear_obj(functionSo)
811
        sollya_lib_clear_obj(degreeSo)
812
        sollya_lib_clear_obj(scaledBoundsSo)
813
        return None
814
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
815
                                              upperBoundSa.parent().precision()))
816
    boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
817
    errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
818
    print "First approximation error:", errorSa.n(digits=10)
819
    # If the error and interval are OK a the first try, just return.
820
    if boundsSa.endpoints()[1] >= scaledUpperBoundSa:
821
        # Change variable stuff in Sollya x -> x0-x.
822
        changeVarExpressionSo = sollya_lib_build_function_sub( \
823
                              sollya_lib_build_function_free_variable(), \
824
                              sollya_lib_copy_obj(intervalCenterSo))
825
        polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
826
        sollya_lib_clear_obj(changeVarExpressionSo)
827
        resultArray.append((polySo, polyVarChangedSo, boundsSo, \
828
                            intervalCenterSo, maxErrorSo))
829
        if internalSollyaPrecSa != currentSollyaPrecSa:
830
            pobyso_set_prec_sa_so(currentSollyaPrecSa)
831
        sollya_lib_clear_obj(currentSollyaPrecSo)
832
        sollya_lib_clear_obj(functionSo)
833
        sollya_lib_clear_obj(degreeSo)
834
        sollya_lib_clear_obj(scaledBoundsSo)
835
        #print "Approximation error:", errorSa
836
        return resultArray
837
    # The returned interval upper bound does not reach the requested upper
838
    # upper bound: compute the next upper bound.
839
    # The following ratio is always >= 1
840
    currentErrorRatio = approxPrecSa / errorSa
841
    # Starting point for the next upper bound.
842
    currentScaledUpperBoundSa = boundsSa.endpoints()[1]
843
    boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
844
    # Compute the increment. 
845
    if currentErrorRatio > RR('1000'): # ]1.5, infinity[
846
        currentScaledUpperBoundSa += \
847
                        currentErrorRatio * boundsWidthSa * 2
848
    else:  # [1, 1.5]
849
        currentScaledUpperBoundSa += \
850
                        (RR('1.0') + currentErrorRatio.log() / 500) * boundsWidthSa
851
    # Take into account the original interval upper bound.                     
852
    if currentScaledUpperBoundSa > scaledUpperBoundSa:
853
        currentScaledUpperBoundSa = scaledUpperBoundSa
854
    # Compute the other expansions.
855
    while boundsSa.endpoints()[1] < scaledUpperBoundSa:
856
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
857
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
858
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
859
                                            currentScaledLowerBoundSa, 
860
                                            currentScaledUpperBoundSa, 
861
                                            approxPrecSa, 
862
                                            internalSollyaPrecSa)
863
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
864
        if  errorSa < approxPrecSa:
865
            # Change variable stuff
866
            #print "Approximation error:", errorSa
867
            changeVarExpressionSo = sollya_lib_build_function_sub(
868
                                    sollya_lib_build_function_free_variable(), 
869
                                    sollya_lib_copy_obj(intervalCenterSo))
870
            polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
871
            sollya_lib_clear_obj(changeVarExpressionSo)
872
            resultArray.append((polySo, polyVarChangedSo, boundsSo, \
873
                                intervalCenterSo, maxErrorSo))
874
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
875
        # Compute the next upper bound.
876
        # The following ratio is always >= 1
877
        currentErrorRatio = approxPrecSa / errorSa
878
        # Starting point for the next upper bound.
879
        currentScaledUpperBoundSa = boundsSa.endpoints()[1]
880
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
881
        # Compute the increment. 
882
        if currentErrorRatio > RR('1000'): # ]1.5, infinity[
883
            currentScaledUpperBoundSa += \
884
                            currentErrorRatio * boundsWidthSa * 2
885
        else:  # [1, 1.5]
886
            currentScaledUpperBoundSa += \
887
                            (RR('1.0') + currentErrorRatio.log()/500) * boundsWidthSa
888
        #print "currentErrorRatio:", currentErrorRatio
889
        #print "currentScaledUpperBoundSa", currentScaledUpperBoundSa
890
        # Test for insufficient precision.
891
        if currentScaledUpperBoundSa == scaledLowerBoundSa:
892
            print "Can't shrink the interval anymore!"
893
            print "You should consider increasing the Sollya internal precision"
894
            print "or the polynomial degree."
895
            print "Giving up!"
896
            if internalSollyaPrecSa != currentSollyaPrecSa:
897
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
898
            sollya_lib_clear_obj(currentSollyaPrecSo)
899
            sollya_lib_clear_obj(functionSo)
900
            sollya_lib_clear_obj(degreeSo)
901
            sollya_lib_clear_obj(scaledBoundsSo)
902
            return None
903
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
904
            currentScaledUpperBoundSa = scaledUpperBoundSa
905
    if internalSollyaPrecSa > currentSollyaPrecSa:
906
        pobyso_set_prec_so_so(currentSollyaPrecSo)
907
    sollya_lib_clear_obj(currentSollyaPrecSo)
908
    sollya_lib_clear_obj(functionSo)
909
    sollya_lib_clear_obj(degreeSo)
910
    sollya_lib_clear_obj(scaledBoundsSo)
911
    return(resultArray)
912
# End slz_get_intervals_and_polynomials
913

    
914

    
915
def slz_interval_scaling_expression(boundsInterval, expVar):
916
    """
917
    Compute the scaling expression to map an interval that spans at most
918
    a single binade into [1, 2) and the inverse expression as well.
919
    If the interval spans more than one binade, result is wrong since
920
    scaling is only based on the lower bound.
921
    Not very sure that the transformation makes sense for negative numbers.
922
    """
923
    # The "one of the bounds is 0" special case. Here we consider
924
    # the interval as the subnormals binade.
925
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
926
        # The upper bound is (or should be) positive.
927
        if boundsInterval.endpoints()[0] == 0:
928
            if boundsInterval.endpoints()[1] == 0:
929
                return None
930
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
931
            l2     = boundsInterval.endpoints()[1].abs().log2()
932
            # The upper bound is a power of two
933
            if binade == l2:
934
                scalingCoeff    = 2^(-binade)
935
                invScalingCoeff = 2^(binade)
936
                scalingOffset   = 1
937
                return((scalingCoeff * expVar + scalingOffset),\
938
                       ((expVar - scalingOffset) * invScalingCoeff))
939
            else:
940
                scalingCoeff    = 2^(-binade-1)
941
                invScalingCoeff = 2^(binade+1)
942
                scalingOffset   = 1
943
                return((scalingCoeff * expVar + scalingOffset),\
944
                    ((expVar - scalingOffset) * invScalingCoeff))
945
        # The lower bound is (or should be) negative.
946
        if boundsInterval.endpoints()[1] == 0:
947
            if boundsInterval.endpoints()[0] == 0:
948
                return None
949
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
950
            l2     = boundsInterval.endpoints()[0].abs().log2()
951
            # The upper bound is a power of two
952
            if binade == l2:
953
                scalingCoeff    = -2^(-binade)
954
                invScalingCoeff = -2^(binade)
955
                scalingOffset   = 1
956
                return((scalingCoeff * expVar + scalingOffset),\
957
                    ((expVar - scalingOffset) * invScalingCoeff))
958
            else:
959
                scalingCoeff    = -2^(-binade-1)
960
                invScalingCoeff = -2^(binade+1)
961
                scalingOffset   = 1
962
                return((scalingCoeff * expVar + scalingOffset),\
963
                   ((expVar - scalingOffset) * invScalingCoeff))
964
    # General case
965
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
966
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
967
    # We allow for a single exception in binade spanning is when the
968
    # "outward bound" is a power of 2 and its binade is that of the
969
    # "inner bound" + 1.
970
    if boundsInterval.endpoints()[0] > 0:
971
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
972
        if lbBinade != ubBinade:
973
            print "Different binades."
974
            if ubL2 != ubBinade:
975
                print "Not a power of 2."
976
                return None
977
            elif abs(ubBinade - lbBinade) > 1:
978
                print "Too large span:", abs(ubBinade - lbBinade)
979
                return None
980
    else:
981
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
982
        if lbBinade != ubBinade:
983
            print "Different binades."
984
            if lbL2 != lbBinade:
985
                print "Not a power of 2."
986
                return None
987
            elif abs(ubBinade - lbBinade) > 1:
988
                print "Too large span:", abs(ubBinade - lbBinade)
989
                return None
990
    #print "Lower bound binade:", binade
991
    if boundsInterval.endpoints()[0] > 0:
992
        return((2^(-lbBinade) * expVar),(2^(lbBinade) * expVar))
993
    else:
994
        return((-(2^(-ubBinade)) * expVar),(-(2^(ubBinade)) * expVar))
995
"""
996
    # Code sent to attic. Should be the base for a 
997
    # "slz_interval_translate_expression" rather than scale.
998
    # Extra control and special cases code  added in  
999
    # slz_interval_scaling_expression could (should ?) be added to
1000
    # this new function.
1001
    # The scaling offset is only used for negative numbers.
1002
    # When the absolute value of the lower bound is < 0.
1003
    if abs(boundsInterval.endpoints()[0]) < 1:
1004
        if boundsInterval.endpoints()[0] >= 0:
1005
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1006
            invScalingCoeff = 1/scalingCoeff
1007
            return((scalingCoeff * expVar, 
1008
                    invScalingCoeff * expVar))
1009
        else:
1010
            scalingCoeff = \
1011
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1012
            scalingOffset = -3 * scalingCoeff
1013
            return((scalingCoeff * expVar + scalingOffset,
1014
                    1/scalingCoeff * expVar + 3))
1015
    else: 
1016
        if boundsInterval.endpoints()[0] >= 0:
1017
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1018
            scalingOffset = 0
1019
            return((scalingCoeff * expVar, 
1020
                    1/scalingCoeff * expVar))
1021
        else:
1022
            scalingCoeff = \
1023
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1024
            scalingOffset =  -3 * scalingCoeff
1025
            #scalingOffset = 0
1026
            return((scalingCoeff * expVar + scalingOffset,
1027
                    1/scalingCoeff * expVar + 3))
1028
"""
1029
# End slz_interval_scaling_expression
1030
   
1031
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1032
    """
1033
    Compute the Sage version of the Taylor polynomial and it's
1034
    companion data (interval, center...)
1035
    The input parameter is a five elements tuple:
1036
    - [0]: the polyomial (without variable change), as polynomial over a
1037
           real ring;
1038
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1039
           over a real ring;
1040
    - [2]: the interval (as Sollya range);
1041
    - [3]: the interval center;
1042
    - [4]: the approximation error. 
1043
    
1044
    The function return a 5 elements tuple: formed with all the 
1045
    input elements converted into their Sollya counterpart.
1046
    """
1047
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
1048
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
1049
    intervalSa = \
1050
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1051
    centerSa = \
1052
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1053
    errorSa = \
1054
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1055
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1056
    # End slz_interval_and_polynomial_to_sage
1057

    
1058
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1059
                                                precision,
1060
                                                targetHardnessToRound,
1061
                                                variable1,
1062
                                                variable2):
1063
    """
1064
    Creates a new multivariate polynomial with integer coefficients for use
1065
     with the Coppersmith method.
1066
    A the same time it computes :
1067
    - 2^K (N);
1068
    - 2^k (bound on the second variable)
1069
    - lcm
1070

    
1071
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1072
                         variables.
1073
    :param precision: the precision of the floating-point coefficients.
1074
    :param targetHardnessToRound: the hardness to round we want to check.
1075
    :param variable1: the first variable of the polynomial (an expression).
1076
    :param variable2: the second variable of the polynomial (an expression).
1077
    
1078
    :returns: a 4 elements tuple:
1079
                - the polynomial;
1080
                - the modulus (N);
1081
                - the t bound;
1082
                - the lcm used to compute the integral coefficients and the 
1083
                  module.
1084
    """
1085
    # Create a new integer polynomial ring.
1086
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1087
    # Coefficients are issued in the increasing power order.
1088
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1089
    # Print the reversed list for debugging.
1090
    print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1091
    # Build the list of number we compute the lcm of.
1092
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1093
    coefficientDenominators.append(2^precision)
1094
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
1095
    leastCommonMultiple = lcm(coefficientDenominators)
1096
    # Compute the expression corresponding to the new polynomial
1097
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1098
    #print coefficientNumerators
1099
    polynomialExpression = 0
1100
    power = 0
1101
    # Iterate over two lists at the same time, stop when the shorter is
1102
    # exhausted.
1103
    for numerator, denominator in \
1104
                        zip(coefficientNumerators, coefficientDenominators):
1105
        multiplicator = leastCommonMultiple / denominator 
1106
        newCoefficient = numerator * multiplicator
1107
        polynomialExpression += newCoefficient * variable1^power
1108
        power +=1
1109
    polynomialExpression += - variable2
1110
    return (IP(polynomialExpression),
1111
            leastCommonMultiple / 2^precision, # 2^K or N.
1112
            leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1113
            leastCommonMultiple) # If we want to make test computations.
1114
        
1115
# End slz_ratPoly_of_int_to_poly_for_coppersmith
1116

    
1117
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1118
                                          precision):
1119
    """
1120
    Makes a variable substitution into the input polynomial so that the output
1121
    polynomial can take integer arguments.
1122
    All variables of the input polynomial "have precision p". That is to say
1123
    that they are rationals with denominator == 2^(precision - 1): 
1124
    x = y/2^(precision - 1).
1125
    We "incorporate" these denominators into the coefficients with, 
1126
    respectively, the "right" power.
1127
    """
1128
    polynomialField = ratPolyOfRat.parent()
1129
    polynomialVariable = ratPolyOfRat.variables()[0]
1130
    #print "The polynomial field is:", polynomialField
1131
    return \
1132
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1133
                                   polynomialVariable/2^(precision-1)}))
1134
    
1135
    # Return a tuple:
1136
    # - the bivariate integer polynomial in (i,j);
1137
    # - 2^K
1138
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1139

    
1140

    
1141
print "\t...sageSLZ loaded"