Statistiques
| Révision :

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

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

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

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

    
646
def slz_compute_scaled_function(functionSa,
647
                                lowerBoundSa,
648
                                upperBoundSa,
649
                                floatingPointPrecSa,
650
                                debug=False):
651
    """
652
    From a function, compute the scaled function whose domain
653
    is included in [1, 2) and whose image is also included in [1,2).
654
    Return a tuple: 
655
    [0]: the scaled function
656
    [1]: the scaled domain lower bound
657
    [2]: the scaled domain upper bound
658
    [3]: the scaled image lower bound
659
    [4]: the scaled image upper bound
660
    """
661
    x = functionSa.variables()[0]
662
    # Reassert f as a function (an not a mere expression).
663
    
664
    # Scalling the domain -> [1,2[.
665
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
666
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
667
    (domainScalingExpressionSa, invDomainScalingExpressionSa) = \
668
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
669
    if debug:
670
        print "domainScalingExpression for argument :", \
671
        invDomainScalingExpressionSa
672
        print "f: ", f
673
    ff = f.subs({x : domainScalingExpressionSa})
674
    #ff = f.subs_expr(x==domainScalingExpressionSa)
675
    domainScalingFunction(x) = invDomainScalingExpressionSa
676
    scaledLowerBoundSa = \
677
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
678
    scaledUpperBoundSa = \
679
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
680
    if debug:
681
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
682
              scaledUpperBoundSa
683
    #
684
    # Scalling the image -> [1,2[.
685
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
686
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
687
    if flbSa <= fubSa: # Increasing
688
        imageBinadeBottomSa = floor(flbSa.log2())
689
    else: # Decreasing
690
        imageBinadeBottomSa = floor(fubSa.log2())
691
    if debug:
692
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
693
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
694
    (imageScalingExpressionSa, invImageScalingExpressionSa) = \
695
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
696
    if debug:
697
        print "imageScalingExpression for argument :", \
698
              invImageScalingExpressionSa
699
    iis = invImageScalingExpressionSa.function(x)
700
    fff = iis.subs({x:ff})
701
    if debug:
702
        print "fff:", fff,
703
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
704
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
705
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
706
# End slz_compute_scaled_function
707

    
708
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
709
    # Create a polynomial over the rationals.
710
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
711
    return(polynomialRing(polyOfFloat))
712
# End slz_float_poly_of_float_to_rat_poly_of_rat.
713

    
714
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
715
                                      lowerBoundSa, 
716
                                      upperBoundSa, floatingPointPrecSa, 
717
                                      internalSollyaPrecSa, approxPrecSa):
718
    """
719
    Under the assumption that:
720
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
721
    - lowerBound and upperBound belong to the same binade.
722
    from a:
723
    - function;
724
    - a degree
725
    - a pair of bounds;
726
    - the floating-point precision we work on;
727
    - the internal Sollya precision;
728
    - the requested approximation error
729
    The initial interval is, possibly, splitted into smaller intervals.
730
    It return a list of tuples, each made of:
731
    - a first polynomial (without the changed variable f(x) = p(x-x0));
732
    - a second polynomial (with a changed variable f(x) = q(x))
733
    - the approximation interval;
734
    - the center, x0, of the interval;
735
    - the corresponding approximation error.
736
    TODO: fix endless looping for some parameters sets.
737
    """
738
    resultArray = []
739
    # Set Sollya to the necessary internal precision.
740
    precChangedSa = False
741
    currentSollyaPrecSo = pobyso_get_prec_so()
742
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
743
    if internalSollyaPrecSa > currentSollyaPrecSa:
744
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
745
        precChangedSa = True
746
    #
747
    x = functionSa.variables()[0] # Actual variable name can be anything.
748
    # Scaled function: [1=,2] -> [1,2].
749
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
750
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
751
                slz_compute_scaled_function(functionSa,                       \
752
                                            lowerBoundSa,                     \
753
                                            upperBoundSa,                     \
754
                                            floatingPointPrecSa)
755
    #
756
    print "Approximation precision: ", RR(approxPrecSa)
757
    # Prepare the arguments for the Taylor expansion computation with Sollya.
758
    functionSo = \
759
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
760
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
761
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
762
                                                  scaledUpperBoundSa)
763
    # Compute the first Taylor expansion.
764
    (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
765
        slz_compute_polynomial_and_interval(functionSo, degreeSo,
766
                                        scaledLowerBoundSa, scaledUpperBoundSa,
767
                                        approxPrecSa, internalSollyaPrecSa)
768
    if polySo is None:
769
        print "slz_get_intervals_and_polynomials: Aborting and returning None!"
770
        if precChangedSa:
771
            pobyso_set_prec_so_so(currentSollyaPrecSo)
772
            sollya_lib_clear_obj(currentSollyaPrecSo)
773
        sollya_lib_clear_obj(functionSo)
774
        sollya_lib_clear_obj(degreeSo)
775
        sollya_lib_clear_obj(scaledBoundsSo)
776
        return None
777
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
778
                                              upperBoundSa.parent().precision()))
779
    boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
780
    errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
781
    print "First approximation error:", errorSa.n(digits=50)
782
    # If the error and interval are OK a the first try, just return.
783
    if boundsSa.endpoints()[1] >= scaledUpperBoundSa:
784
        # Change variable stuff in Sollya x -> x0-x.
785
        changeVarExpressionSo = sollya_lib_build_function_sub( \
786
                              sollya_lib_build_function_free_variable(), \
787
                              sollya_lib_copy_obj(intervalCenterSo))
788
        polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
789
        sollya_lib_clear_obj(changeVarExpressionSo)
790
        resultArray.append((polySo, polyVarChangedSo, boundsSo, \
791
                            intervalCenterSo, maxErrorSo))
792
        if internalSollyaPrecSa != currentSollyaPrecSa:
793
            pobyso_set_prec_sa_so(currentSollyaPrecSa)
794
        sollya_lib_clear_obj(currentSollyaPrecSo)
795
        sollya_lib_clear_obj(functionSo)
796
        sollya_lib_clear_obj(degreeSo)
797
        sollya_lib_clear_obj(scaledBoundsSo)
798
        #print "Approximation error:", errorSa
799
        return resultArray
800
    # The returned interval upper bound does not reach the requested upper
801
    # upper bound: compute the next upper bound.
802
    # The following ratio is always >= 1
803
    currentErrorRatio = approxPrecSa / errorSa
804
    # Starting point for the next upper bound.
805
    currentScaledUpperBoundSa = boundsSa.endpoints()[1]
806
    boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
807
    # Compute the increment. 
808
    if currentErrorRatio > RR('1000'): # ]1.5, infinity[
809
        currentScaledUpperBoundSa += \
810
                        currentErrorRatio * boundsWidthSa * 2
811
    else:  # [1, 1.5]
812
        currentScaledUpperBoundSa += \
813
                        (RR('1.0') + currentErrorRatio.log() / 500) * boundsWidthSa
814
    # Take into account the original interval upper bound.                     
815
    if currentScaledUpperBoundSa > scaledUpperBoundSa:
816
        currentScaledUpperBoundSa = scaledUpperBoundSa
817
    # Compute the other expansions.
818
    while boundsSa.endpoints()[1] < scaledUpperBoundSa:
819
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
820
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
821
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
822
                                            currentScaledLowerBoundSa, 
823
                                            currentScaledUpperBoundSa, 
824
                                            approxPrecSa, 
825
                                            internalSollyaPrecSa)
826
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
827
        if  errorSa < approxPrecSa:
828
            # Change variable stuff
829
            #print "Approximation error:", errorSa
830
            changeVarExpressionSo = sollya_lib_build_function_sub(
831
                                    sollya_lib_build_function_free_variable(), 
832
                                    sollya_lib_copy_obj(intervalCenterSo))
833
            polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
834
            sollya_lib_clear_obj(changeVarExpressionSo)
835
            resultArray.append((polySo, polyVarChangedSo, boundsSo, \
836
                                intervalCenterSo, maxErrorSo))
837
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
838
        # 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
        #print "currentErrorRatio:", currentErrorRatio
852
        #print "currentScaledUpperBoundSa", currentScaledUpperBoundSa
853
        # Test for insufficient precision.
854
        if currentScaledUpperBoundSa == scaledLowerBoundSa:
855
            print "Can't shrink the interval anymore!"
856
            print "You should consider increasing the Sollya internal precision"
857
            print "or the polynomial degree."
858
            print "Giving up!"
859
            if internalSollyaPrecSa != currentSollyaPrecSa:
860
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
861
            sollya_lib_clear_obj(currentSollyaPrecSo)
862
            sollya_lib_clear_obj(functionSo)
863
            sollya_lib_clear_obj(degreeSo)
864
            sollya_lib_clear_obj(scaledBoundsSo)
865
            return None
866
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
867
            currentScaledUpperBoundSa = scaledUpperBoundSa
868
    if internalSollyaPrecSa > currentSollyaPrecSa:
869
        pobyso_set_prec_so_so(currentSollyaPrecSo)
870
    sollya_lib_clear_obj(currentSollyaPrecSo)
871
    sollya_lib_clear_obj(functionSo)
872
    sollya_lib_clear_obj(degreeSo)
873
    sollya_lib_clear_obj(scaledBoundsSo)
874
    return(resultArray)
875
# End slz_get_intervals_and_polynomials
876

    
877

    
878
def slz_interval_scaling_expression(boundsInterval, expVar):
879
    """
880
    Compute the scaling expression to map an interval that spans at most
881
    a single binade to [1, 2) and the inverse expression as well.
882
    Not very sure that the transformation makes sense for negative numbers.
883
    """
884
    # The scaling offset is only used for negative numbers.
885
    # When the absolute value of the lower bound is < 0.
886
    if abs(boundsInterval.endpoints()[0]) < 1:
887
        if boundsInterval.endpoints()[0] >= 0:
888
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
889
            invScalingCoeff = 1/scalingCoeff
890
            return((scalingCoeff * expVar, 
891
                    invScalingCoeff * expVar))
892
        else:
893
            scalingCoeff = \
894
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
895
            scalingOffset = -3 * scalingCoeff
896
            return((scalingCoeff * expVar + scalingOffset,
897
                    1/scalingCoeff * expVar + 3))
898
    else: 
899
        if boundsInterval.endpoints()[0] >= 0:
900
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
901
            scalingOffset = 0
902
            return((scalingCoeff * expVar, 
903
                    1/scalingCoeff * expVar))
904
        else:
905
            scalingCoeff = \
906
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
907
            scalingOffset =  -3 * scalingCoeff
908
            #scalingOffset = 0
909
            return((scalingCoeff * expVar + scalingOffset,
910
                    1/scalingCoeff * expVar + 3))
911
# End slz_interval_scaling_expression
912
   
913
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
914
    """
915
    Compute the Sage version of the Taylor polynomial and it's
916
    companion data (interval, center...)
917
    The input parameter is a five elements tuple:
918
    - [0]: the polyomial (without variable change), as polynomial over a
919
           real ring;
920
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
921
           over a real ring;
922
    - [2]: the interval (as Sollya range);
923
    - [3]: the interval center;
924
    - [4]: the approximation error. 
925
    
926
    The function return a 5 elements tuple: formed with all the 
927
    input elements converted into their Sollya counterpart.
928
    """
929
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
930
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
931
    intervalSa = \
932
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
933
    centerSa = \
934
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
935
    errorSa = \
936
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
937
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
938
    # End slz_interval_and_polynomial_to_sage
939

    
940
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
941
                                                precision,
942
                                                targetHardnessToRound,
943
                                                variable1,
944
                                                variable2):
945
    """
946
    Creates a new multivariate polynomial with integer coefficients for use
947
     with the Coppersmith method.
948
    A the same time it computes :
949
    - 2^K (N);
950
    - 2^k (bound on the second variable)
951
    - lcm
952

    
953
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
954
                         variables.
955
    :param precision: the precision of the floating-point coefficients.
956
    :param targetHardnessToRound: the hardness to round we want to check.
957
    :param variable1: the first variable of the polynomial (an expression).
958
    :param variable2: the second variable of the polynomial (an expression).
959
    
960
    :returns: a 4 elements tuple:
961
                - the polynomial;
962
                - the modulus (N);
963
                - the t bound;
964
                - the lcm used to compute the integral coefficients and the 
965
                  module.
966
    """
967
    # Create a new integer polynomial ring.
968
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
969
    # Coefficients are issued in the increasing power order.
970
    ratPolyCoefficients = ratPolyOfInt.coefficients()
971
    # Print the reversed list for debugging.
972
    print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
973
    # Build the list of number we compute the lcm of.
974
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
975
    coefficientDenominators.append(2^precision)
976
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
977
    leastCommonMultiple = lcm(coefficientDenominators)
978
    # Compute the expression corresponding to the new polynomial
979
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
980
    #print coefficientNumerators
981
    polynomialExpression = 0
982
    power = 0
983
    # Iterate over two lists at the same time, stop when the shorter is
984
    # exhausted.
985
    for numerator, denominator in \
986
                        zip(coefficientNumerators, coefficientDenominators):
987
        multiplicator = leastCommonMultiple / denominator 
988
        newCoefficient = numerator * multiplicator
989
        polynomialExpression += newCoefficient * variable1^power
990
        power +=1
991
    polynomialExpression += - variable2
992
    return (IP(polynomialExpression),
993
            leastCommonMultiple / 2^precision, # 2^K or N.
994
            leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
995
            leastCommonMultiple) # If we want to make test computations.
996
        
997
# End slz_ratPoly_of_int_to_poly_for_coppersmith
998

    
999
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1000
                                          precision):
1001
    """
1002
    Makes a variable substitution into the input polynomial so that the output
1003
    polynomial can take integer arguments.
1004
    All variables of the input polynomial "have precision p". That is to say
1005
    that they are rationals with denominator == 2^(precision - 1): 
1006
    x = y/2^(precision - 1).
1007
    We "incorporate" these denominators into the coefficients with, 
1008
    respectively, the "right" power.
1009
    """
1010
    polynomialField = ratPolyOfRat.parent()
1011
    polynomialVariable = ratPolyOfRat.variables()[0]
1012
    #print "The polynomial field is:", polynomialField
1013
    return \
1014
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1015
                                   polynomialVariable/2^(precision-1)}))
1016
    
1017
    # Return a tuple:
1018
    # - the bivariate integer polynomial in (i,j);
1019
    # - 2^K
1020
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1021

    
1022

    
1023
print "\t...sageSLZ loaded"