Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (58 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 number 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 (or 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
        realField           = numberAsRR.parent()
116
        numberLog2          = numberAsRR.abs().log2()
117
        floorNumberLog2     = floor(numberLog2)
118
        ## Do not get caught by rounding of log2() both ways.
119
        ## When numberLog2 is an integer, compare numberAsRR
120
        #  with 2^numberLog2.
121
        if floorNumberLog2 == numberLog2:
122
            if numberAsRR.abs() < realField(2^floorNumberLog2):
123
                return floorNumberLog2 - 1
124
            else:
125
                return floorNumberLog2
126
        else:
127
            return floorNumberLog2
128
# End slz_compute_binade
129

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

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

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

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

    
751
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
752
    # Create a polynomial over the rationals.
753
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
754
    return(polynomialRing(polyOfFloat))
755
# End slz_float_poly_of_float_to_rat_poly_of_rat.
756

    
757
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
758
                                      lowerBoundSa, 
759
                                      upperBoundSa, floatingPointPrecSa, 
760
                                      internalSollyaPrecSa, approxPrecSa):
761
    """
762
    Under the assumption that:
763
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
764
    - lowerBound and upperBound belong to the same binade.
765
    from a:
766
    - function;
767
    - a degree
768
    - a pair of bounds;
769
    - the floating-point precision we work on;
770
    - the internal Sollya precision;
771
    - the requested approximation error
772
    The initial interval is, possibly, splitted into smaller intervals.
773
    It return a list of tuples, each made of:
774
    - a first polynomial (without the changed variable f(x) = p(x-x0));
775
    - a second polynomial (with a changed variable f(x) = q(x))
776
    - the approximation interval;
777
    - the center, x0, of the interval;
778
    - the corresponding approximation error.
779
    TODO: fix endless looping for some parameters sets.
780
    """
781
    resultArray = []
782
    # Set Sollya to the necessary internal precision.
783
    precChangedSa = False
784
    currentSollyaPrecSo = pobyso_get_prec_so()
785
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
786
    if internalSollyaPrecSa > currentSollyaPrecSa:
787
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
788
        precChangedSa = True
789
    #
790
    x = functionSa.variables()[0] # Actual variable name can be anything.
791
    # Scaled function: [1=,2] -> [1,2].
792
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
793
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
794
                slz_compute_scaled_function(functionSa,                       \
795
                                            lowerBoundSa,                     \
796
                                            upperBoundSa,                     \
797
                                            floatingPointPrecSa)
798
    # In case bounds were in the negative real one may need to
799
    # switch scaled bounds.
800
    if scaledLowerBoundSa > scaledUpperBoundSa:
801
        scaledLowerBoundSa, scaledUpperBoundSa = \
802
            scaledUpperBoundSa, scaledLowerBoundSa
803
        #print "Switching!"
804
    print "Approximation precision: ", RR(approxPrecSa)
805
    # Prepare the arguments for the Taylor expansion computation with Sollya.
806
    functionSo = \
807
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
808
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
809
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
810
                                                  scaledUpperBoundSa)
811

    
812
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
813
                                              upperBoundSa.parent().precision()))
814
    currentScaledLowerBoundSa = scaledLowerBoundSa
815
    currentScaledUpperBoundSa = scaledUpperBoundSa
816
    while True:
817
        ## Compute the first Taylor expansion.
818
        print "Computing a Taylor expansion..."
819
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
820
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
821
                                        currentScaledLowerBoundSa, 
822
                                        currentScaledUpperBoundSa,
823
                                        approxPrecSa, internalSollyaPrecSa)
824
        print "...done."
825
        ## If slz_compute_polynomial_and_interval fails, it returns None.
826
        #  This value goes to the first variable: polySo. Other variables are
827
        #  not assigned and should not be tested.
828
        if polySo is None:
829
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
830
            if precChangedSa:
831
                pobyso_set_prec_so_so(currentSollyaPrecSo)
832
                sollya_lib_clear_obj(currentSollyaPrecSo)
833
            sollya_lib_clear_obj(functionSo)
834
            sollya_lib_clear_obj(degreeSo)
835
            sollya_lib_clear_obj(scaledBoundsSo)
836
            return None
837
        ## Add to the result array.
838
        ### Change variable stuff in Sollya x -> x0-x.
839
        changeVarExpressionSo = \
840
            sollya_lib_build_function_sub( \
841
                              sollya_lib_build_function_free_variable(), 
842
                              sollya_lib_copy_obj(intervalCenterSo))
843
        polyVarChangedSo = \
844
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
845
        #### Get rid of the variable change Sollya stuff. 
846
        sollya_lib_clear_obj(changeVarExpressionSo)
847
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
848
                            intervalCenterSo, maxErrorSo))
849
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
850
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
851
        print "Computed approximation error:", errorSa.n(digits=10)
852
        # If the error and interval are OK a the first try, just return.
853
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
854
            (errorSa <= approxPrecSa):
855
            if precChangedSa:
856
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
857
            sollya_lib_clear_obj(currentSollyaPrecSo)
858
            sollya_lib_clear_obj(functionSo)
859
            sollya_lib_clear_obj(degreeSo)
860
            sollya_lib_clear_obj(scaledBoundsSo)
861
            #print "Approximation error:", errorSa
862
            return resultArray
863
        ## The returned interval upper bound does not reach the requested upper
864
        #  upper bound: compute the next upper bound.
865
        ## The following ratio is always >= 1. If errorSa, we may want to
866
        #  enlarge the interval
867
        currentErrorRatio = approxPrecSa / errorSa
868
        ## --|--------------------------------------------------------------|--
869
        #  --|--------------------|--------------------------------------------
870
        #  --|----------------------------|------------------------------------
871
        ## Starting point for the next upper bound.
872
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
873
        # Compute the increment. 
874
        newBoundsWidthSa = \
875
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
876
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
877
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
878
        # Take into account the original interval upper bound.                     
879
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
880
            currentScaledUpperBoundSa = scaledUpperBoundSa
881
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
882
            print "Can't shrink the interval anymore!"
883
            print "You should consider increasing the Sollya internal precision"
884
            print "or the polynomial degree."
885
            print "Giving up!"
886
            if precChangedSa:
887
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
888
            sollya_lib_clear_obj(currentSollyaPrecSo)
889
            sollya_lib_clear_obj(functionSo)
890
            sollya_lib_clear_obj(degreeSo)
891
            sollya_lib_clear_obj(scaledBoundsSo)
892
            return None
893
        # Compute the other expansions.
894
        # Test for insufficient precision.
895
# End slz_get_intervals_and_polynomials
896

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

    
1040
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1041
            targetPlusOnePrecRF = None, quasiExactRF = None):
1042
    """
1043
      Check if an element (argument) of the domain of function (function)
1044
      yields a HTRN case (rounding to next) for the target precision 
1045
      (as impersonated by targetRF) for a given accuracy (targetAccuraty). 
1046
    """
1047
    ## Arguments filtering. TODO: filter the first argument for consistence.
1048
    ## If no target accuracy is given, assume it is that of the argument.
1049
    if targetRF is None:
1050
        targetRF = argument.parent()
1051
    ## Ditto for the real field holding the midpoints.
1052
    if targetPlusOnePrecRF is None:
1053
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1054
    ## Create a high accuracy RealField
1055
    if quasiExactRF is None:
1056
        quasiExactRF = RealField(1536)
1057
        
1058
    exactArgument                 = quasiExactRF(argument)
1059
    quasiExactValue               = function(exactArgument)
1060
    roundedValue                  = targetRF(quasiExactValue)
1061
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1062
    # Upper midpoint.
1063
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1064
    # Lower midpoint.
1065
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1066
    binade                        = slz_compute_binade(roundedValue)
1067
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1068
    #print "Argument:", argument
1069
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1070
    #print "Binade:", binade
1071
    #print "binadeCorrectedTargetAccuracy:", \
1072
    #binadeCorrectedTargetAccuracy.n(prec=100)
1073
    #print "binadeCorrectedTargetAccuracy:", \
1074
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1075
    #print "Upper midpoint:", \
1076
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1077
    #print "Rounded value :", \
1078
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1079
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1080
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1081
    #print "Lower midpoint:", \
1082
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1083
    ## Begining of the general case : check with the midpoint with 
1084
    #  greatest absolute value.
1085
    if quasiExactValue > 0:
1086
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1087
           binadeCorrectedTargetAccuracy:
1088
            #print "Too close to the upper midpoint: ", \ 
1089
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1090
            #print argument.n().str(base=16, \
1091
            #  no_sci=False)
1092
            #print "Lower midpoint:", \
1093
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1094
            #print "Value         :", \
1095
            #  quasiExactValue.n(prec=200).str(base=2)
1096
            #print "Upper midpoint:", \
1097
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1098
            #print
1099
            return True
1100
    else:
1101
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1102
           binadeCorrectedTargetAccuracy:
1103
            #print "Too close to the upper midpoint: ", \
1104
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1105
            #print argument.n().str(base=16, \
1106
            #  no_sci=False)
1107
            #print "Lower midpoint:", \
1108
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1109
            #print "Value         :", \
1110
            #  quasiExactValue.n(prec=200).str(base=2)
1111
            #print "Upper midpoint:", \
1112
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1113
            #print
1114
            return True
1115
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1116
    ## For the midpoint of smaller absolute value, 
1117
    #  split cases with the powers of 2.
1118
    if sno_abs_is_power_of_two(roundedValue):
1119
        if quasiExactValue > 0:        
1120
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1121
               binadeCorrectedTargetAccuracy / 2:
1122
                #print "Lower midpoint:", \
1123
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1124
                #print "Value         :", \
1125
                #  quasiExactValue.n(prec=200).str(base=2)
1126
                #print "Upper midpoint:", \
1127
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1128
                #print
1129
                return True
1130
        else:
1131
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1132
              binadeCorrectedTargetAccuracy / 2:
1133
                #print "Lower midpoint:", \
1134
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1135
                #print "Value         :", 
1136
                #  quasiExactValue.n(prec=200).str(base=2)
1137
                #print "Upper midpoint:", 
1138
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1139
                #print
1140
                return True
1141
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1142
    else: ## Not a power of 2, regular comparison with the midpoint of 
1143
          #  smaller absolute value.
1144
        if quasiExactValue > 0:        
1145
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1146
              binadeCorrectedTargetAccuracy:
1147
                #print "Lower midpoint:", \
1148
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1149
                #print "Value         :", \
1150
                #  quasiExactValue.n(prec=200).str(base=2)
1151
                #print "Upper midpoint:", \
1152
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1153
                #print
1154
                return True
1155
        else: # quasiExactValue <= 0
1156
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1157
              binadeCorrectedTargetAccuracy:
1158
                #print "Lower midpoint:", \
1159
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1160
                #print "Value         :", \
1161
                #  quasiExactValue.n(prec=200).str(base=2)
1162
                #print "Upper midpoint:", \
1163
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1164
                #print
1165
                return True
1166
    #print "distance to the upper midpoint:", \
1167
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1168
    #print "distance to the lower midpoint:", \
1169
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1170
    return False
1171
# End slz_is_htrn
1172

    
1173
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1174
                                                precision,
1175
                                                targetHardnessToRound,
1176
                                                variable1,
1177
                                                variable2):
1178
    """
1179
    Creates a new multivariate polynomial with integer coefficients for use
1180
     with the Coppersmith method.
1181
    A the same time it computes :
1182
    - 2^K (N);
1183
    - 2^k (bound on the second variable)
1184
    - lcm
1185

    
1186
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1187
                         variables.
1188
    :param precision: the precision of the floating-point coefficients.
1189
    :param targetHardnessToRound: the hardness to round we want to check.
1190
    :param variable1: the first variable of the polynomial (an expression).
1191
    :param variable2: the second variable of the polynomial (an expression).
1192
    
1193
    :returns: a 4 elements tuple:
1194
                - the polynomial;
1195
                - the modulus (N);
1196
                - the t bound;
1197
                - the lcm used to compute the integral coefficients and the 
1198
                  module.
1199
    """
1200
    # Create a new integer polynomial ring.
1201
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1202
    # Coefficients are issued in the increasing power order.
1203
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1204
    # Print the reversed list for debugging.
1205
    print
1206
    print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1207
    # Build the list of number we compute the lcm of.
1208
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1209
    print "Coefficient denominators:", coefficientDenominators
1210
    coefficientDenominators.append(2^precision)
1211
    coefficientDenominators.append(2^(targetHardnessToRound))
1212
    leastCommonMultiple = lcm(coefficientDenominators)
1213
    # Compute the expression corresponding to the new polynomial
1214
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1215
    #print coefficientNumerators
1216
    polynomialExpression = 0
1217
    power = 0
1218
    # Iterate over two lists at the same time, stop when the shorter
1219
    # (is this case coefficientsNumerators) is 
1220
    # exhausted. Both lists are ordered in the order of increasing powers
1221
    # of variable1.
1222
    for numerator, denominator in \
1223
                        zip(coefficientNumerators, coefficientDenominators):
1224
        multiplicator = leastCommonMultiple / denominator 
1225
        newCoefficient = numerator * multiplicator
1226
        polynomialExpression += newCoefficient * variable1^power
1227
        power +=1
1228
    polynomialExpression += - variable2
1229
    return (IP(polynomialExpression),
1230
            leastCommonMultiple / 2^precision, # 2^K aka N.
1231
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1232
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1233
            leastCommonMultiple) # If we want to make test computations.
1234
        
1235
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1236

    
1237
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1238
                                          precision):
1239
    """
1240
    Makes a variable substitution into the input polynomial so that the output
1241
    polynomial can take integer arguments.
1242
    All variables of the input polynomial "have precision p". That is to say
1243
    that they are rationals with denominator == 2^(precision - 1): 
1244
    x = y/2^(precision - 1).
1245
    We "incorporate" these denominators into the coefficients with, 
1246
    respectively, the "right" power.
1247
    """
1248
    polynomialField = ratPolyOfRat.parent()
1249
    polynomialVariable = ratPolyOfRat.variables()[0]
1250
    #print "The polynomial field is:", polynomialField
1251
    return \
1252
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1253
                                   polynomialVariable/2^(precision-1)}))
1254
    
1255
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1256

    
1257

    
1258
print "\t...sageSLZ loaded"