Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (59 ko)

1
r"""
2
Sage core functions needed for the implementation of SLZ.
3

    
4
AUTHORS:
5
- S.T. (2013-08): initial version
6

    
7
Examples:
8

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

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

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

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

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

    
734
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
735
    # Create a polynomial over the rationals.
736
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
737
    return(polynomialRing(polyOfFloat))
738
# End slz_float_poly_of_float_to_rat_poly_of_rat.
739

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

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

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

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

    
1156
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1157
                                                precision,
1158
                                                targetHardnessToRound,
1159
                                                variable1,
1160
                                                variable2):
1161
    """
1162
    Creates a new multivariate polynomial with integer coefficients for use
1163
     with the Coppersmith method.
1164
    A the same time it computes :
1165
    - 2^K (N);
1166
    - 2^k (bound on the second variable)
1167
    - lcm
1168

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

    
1220
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1221
                                          precision):
1222
    """
1223
    Makes a variable substitution into the input polynomial so that the output
1224
    polynomial can take integer arguments.
1225
    All variables of the input polynomial "have precision p". That is to say
1226
    that they are rationals with denominator == 2^(precision - 1): 
1227
    x = y/2^(precision - 1).
1228
    We "incorporate" these denominators into the coefficients with, 
1229
    respectively, the "right" power.
1230
    """
1231
    polynomialField = ratPolyOfRat.parent()
1232
    polynomialVariable = ratPolyOfRat.variables()[0]
1233
    #print "The polynomial field is:", polynomialField
1234
    return \
1235
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1236
                                   polynomialVariable/2^(precision-1)}))
1237
    
1238
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1239

    
1240
def slz_reduce_and_test_base(matrixToReduce,
1241
                             nAtAlpha,
1242
                             monomialsCountSqrt):
1243
    """
1244
    Reduces the basis, tests the Coppersmith condition and returns
1245
    a list of rows that comply with the condition.
1246
    """
1247
    ccComplientRowsList = []
1248
    reducedMatrix = matrixToReduce.LLL(None)
1249
    if not reducedMatrix.is_LLL_reduced():
1250
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1251
    else:
1252
        print "reducedMatrix is actually reduced."
1253
    print "N^alpha:", nAtAlpha.n()
1254
    rowIndex = 0
1255
    for row in reducedMatrix.rows():
1256
        l2Norm = row.norm(2)
1257
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1258
                monomialsCountSqrt.n(), ". Is vector OK?", 
1259
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1260
            ccComplientRowsList.append(row)
1261
            print "True"
1262
        else:
1263
            print "False"
1264
    # End for
1265
    return ccComplientRowsList
1266
# End slz_reduce_and_test_base
1267

    
1268
def slz_resultant_tuple(poly1, poly2, elimVar):
1269
    polynomialRing0 = poly1.parent()
1270
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1271
    if resultantInElimVar.is_zero():
1272
        return ()
1273
    else:
1274
        return (poly1, poly2, resultantInElimVar)
1275
# End slz_resultant_tuple.
1276
#
1277
print "\t...sageSLZ loaded"