Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (69,24 ko)

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

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

    
7
Examples:
8

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

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

    
233
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
234
                                                                    alpha,
235
                                                                    N,
236
                                                                    iBound,
237
                                                                    tBound):
238
    """
239
    For a given set of arguments (see below), compute a list
240
    of "reduced polynomials" that could be used to compute roots
241
    of the inputPolynomial.
242
    Print the volume of the initial basis as well.
243
    INPUT:
244
    
245
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
246
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
247
    - "N" -- the modulus;
248
    - "iBound" -- the bound on the first variable;
249
    - "tBound" -- the bound on the second variable.
250
    
251
    OUTPUT:
252
    
253
    A list of bivariate integer polynomial obtained using the Coppersmith
254
    algorithm. The polynomials correspond to the rows of the LLL-reduce
255
    reduced base that comply with the Coppersmith condition.
256
    """
257
    # Arguments check.
258
    if iBound == 0 or tBound == 0:
259
        return None
260
    # End arguments check.
261
    nAtAlpha = N^alpha
262
    ## Building polynomials for matrix.
263
    polyRing   = inputPolynomial.parent()
264
    # Whatever the 2 variables are actually called, we call them
265
    # 'i' and 't' in all the variable names.
266
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
267
    #print polyVars[0], type(polyVars[0])
268
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
269
                                              tVariable:tVariable * tBound})
270
##    polynomialsList = \
271
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
272
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
273
    polynomialsList = \
274
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
275
                                             alpha,
276
                                             N,
277
                                             iBound,
278
                                             tBound,
279
                                             0)
280
    #print "Polynomials list:", polynomialsList
281
    ## Building the proto matrix.
282
    knownMonomials = []
283
    protoMatrix    = []
284
    for poly in polynomialsList:
285
        spo_add_polynomial_coeffs_to_matrix_row(poly,
286
                                                knownMonomials,
287
                                                protoMatrix,
288
                                                0)
289
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
290
    matrixToReduceTranspose = matrixToReduce.transpose()
291
    squareMatrix = matrixToReduce * matrixToReduceTranspose
292
    squareMatDet = det(squareMatrix)
293
    latticeVolume = sqrt(squareMatDet)
294
    print "Lattice volume:", latticeVolume.n()
295
    print "Lattice volume / N:", (latticeVolume/N).n()
296
    #print matrixToReduce
297
    ## Reduction and checking.
298
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
299
    #  error message issued when previous code was used.
300
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
301
    reductionTimeStart = cputime() 
302
    reducedMatrix = matrixToReduce.LLL(fp=None)
303
    reductionTime = cputime(reductionTimeStart)
304
    print "Reduction time:", reductionTime
305
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
306
    if not isLLLReduced:
307
        return None
308
    monomialsCount     = len(knownMonomials)
309
    monomialsCountSqrt = sqrt(monomialsCount)
310
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
311
    #print reducedMatrix
312
    ## Check the Coppersmith condition for each row and build the reduced 
313
    #  polynomials.
314
    ccReducedPolynomialsList = []
315
    for row in reducedMatrix.rows():
316
        l2Norm = row.norm(2)
317
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
318
            #print (l2Norm * monomialsCountSqrt).n()
319
            #print l2Norm.n()
320
            ccReducedPolynomial = \
321
                slz_compute_reduced_polynomial(row,
322
                                               knownMonomials,
323
                                               iVariable,
324
                                               iBound,
325
                                               tVariable,
326
                                               tBound)
327
            if not ccReducedPolynomial is None:
328
                ccReducedPolynomialsList.append(ccReducedPolynomial)
329
        else:
330
            #print l2Norm.n() , ">", nAtAlpha
331
            pass
332
    if len(ccReducedPolynomialsList) < 2:
333
        print "Less than 2 Coppersmith condition compliant vectors."
334
        return ()
335
    #print ccReducedPolynomialsList
336
    return ccReducedPolynomialsList
337
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
338

    
339
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
340
                                                 alpha,
341
                                                 N,
342
                                                 iBound,
343
                                                 tBound):
344
    """
345
    For a given set of arguments (see below), compute the polynomial modular 
346
    roots, if any.
347
    
348
    """
349
    # Arguments check.
350
    if iBound == 0 or tBound == 0:
351
        return set()
352
    # End arguments check.
353
    nAtAlpha = N^alpha
354
    ## Building polynomials for matrix.
355
    polyRing   = inputPolynomial.parent()
356
    # Whatever the 2 variables are actually called, we call them
357
    # 'i' and 't' in all the variable names.
358
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
359
    ccReducedPolynomialsList = \
360
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
361
                                                     alpha,
362
                                                     N,
363
                                                     iBound,
364
                                                     tBound)
365
    if len(ccReducedPolynomialsList) == 0:
366
        return set()   
367
    ## Create the valid (poly1 and poly2 are algebraically independent) 
368
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
369
    # Try to mix and match all the polynomial pairs built from the 
370
    # ccReducedPolynomialsList to obtain non zero resultants.
371
    resultantsInITuplesList = []
372
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
373
        for polyInnerIndex in xrange(polyOuterIndex+1, 
374
                                     len(ccReducedPolynomialsList)):
375
            # Compute the resultant in resultants in the
376
            # first variable (is it the optimal choice?).
377
            resultantInI = \
378
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
379
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
380
            #print "Resultant", resultantInI
381
            # Test algebraic independence.
382
            if not resultantInI.is_zero():
383
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
384
                                                 ccReducedPolynomialsList[polyInnerIndex],
385
                                                 resultantInI))
386
    # If no non zero resultant was found: we can't get no algebraically 
387
    # independent polynomials pair. Give up!
388
    if len(resultantsInITuplesList) == 0:
389
        return set()
390
    #print resultantsInITuplesList
391
    # Compute the roots.
392
    Zi = ZZ[str(iVariable)]
393
    Zt = ZZ[str(tVariable)]
394
    polynomialRootsSet = set()
395
    # First, solve in the second variable since resultants are in the first
396
    # variable.
397
    for resultantInITuple in resultantsInITuplesList:
398
        tRootsList = Zt(resultantInITuple[2]).roots()
399
        # For each tRoot, compute the corresponding iRoots and check 
400
        # them in the input polynomial.
401
        for tRoot in tRootsList:
402
            #print "tRoot:", tRoot
403
            # Roots returned by root() are (value, multiplicity) tuples.
404
            iRootsList = \
405
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
406
            print iRootsList
407
            # The iRootsList can be empty, hence the test.
408
            if len(iRootsList) != 0:
409
                for iRoot in iRootsList:
410
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
411
                    # polyEvalModN must be an integer.
412
                    if polyEvalModN == int(polyEvalModN):
413
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
414
    return polynomialRootsSet
415
# End slz_compute_integer_polynomial_modular_roots.
416
#
417
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
418
                                                 alpha,
419
                                                 N,
420
                                                 iBound,
421
                                                 tBound):
422
    """
423
    For a given set of arguments (see below), compute the polynomial modular 
424
    roots, if any.
425
    This version differs in the way resultants are computed.   
426
    """
427
    # Arguments check.
428
    if iBound == 0 or tBound == 0:
429
        return set()
430
    # End arguments check.
431
    nAtAlpha = N^alpha
432
    ## Building polynomials for matrix.
433
    polyRing   = inputPolynomial.parent()
434
    # Whatever the 2 variables are actually called, we call them
435
    # 'i' and 't' in all the variable names.
436
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
437
    #print polyVars[0], type(polyVars[0])
438
    ccReducedPolynomialsList = \
439
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
440
                                                     alpha,
441
                                                     N,
442
                                                     iBound,
443
                                                     tBound)
444
    if len(ccReducedPolynomialsList) == 0:
445
        return set()   
446
    ## Create the valid (poly1 and poly2 are algebraically independent) 
447
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
448
    # Try to mix and match all the polynomial pairs built from the 
449
    # ccReducedPolynomialsList to obtain non zero resultants.
450
    resultantsInTTuplesList = []
451
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
452
        for polyInnerIndex in xrange(polyOuterIndex+1, 
453
                                     len(ccReducedPolynomialsList)):
454
            # Compute the resultant in resultants in the
455
            # first variable (is it the optimal choice?).
456
            resultantInT = \
457
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
458
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
459
            #print "Resultant", resultantInT
460
            # Test algebraic independence.
461
            if not resultantInT.is_zero():
462
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
463
                                                 ccReducedPolynomialsList[polyInnerIndex],
464
                                                 resultantInT))
465
    # If no non zero resultant was found: we can't get no algebraically 
466
    # independent polynomials pair. Give up!
467
    if len(resultantsInTTuplesList) == 0:
468
        return set()
469
    #print resultantsInITuplesList
470
    # Compute the roots.
471
    Zi = ZZ[str(iVariable)]
472
    Zt = ZZ[str(tVariable)]
473
    polynomialRootsSet = set()
474
    # First, solve in the second variable since resultants are in the first
475
    # variable.
476
    for resultantInTTuple in resultantsInTTuplesList:
477
        iRootsList = Zi(resultantInTTuple[2]).roots()
478
        # For each iRoot, compute the corresponding tRoots and check 
479
        # them in the input polynomial.
480
        for iRoot in iRootsList:
481
            #print "iRoot:", iRoot
482
            # Roots returned by root() are (value, multiplicity) tuples.
483
            tRootsList = \
484
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
485
            print tRootsList
486
            # The tRootsList can be empty, hence the test.
487
            if len(tRootsList) != 0:
488
                for tRoot in tRootsList:
489
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
490
                    # polyEvalModN must be an integer.
491
                    if polyEvalModN == int(polyEvalModN):
492
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
493
    return polynomialRootsSet
494
# End slz_compute_integer_polynomial_modular_roots_2.
495
#
496
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
497
                                        upperBoundSa, approxPrecSa, 
498
                                        sollyaPrecSa=None):
499
    """
500
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
501
    a polynomial that approximates the function on a an interval starting
502
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
503
    approximates with the expected precision.
504
    The interval upper bound is lowered until the expected approximation 
505
    precision is reached.
506
    The polynomial, the bounds, the center of the interval and the error
507
    are returned.
508
    OUTPUT:
509
    A tuple made of 4 Sollya objects:
510
    - a polynomial;
511
    - an range (an interval, not in the sense of number given as an interval);
512
    - the center of the interval;
513
    - the maximum error in the approximation of the input functionSo by the
514
      output polynomial ; this error <= approxPrecSaS.
515
    
516
    """
517
    ## Superficial argument check.
518
    if lowerBoundSa > upperBoundSa:
519
        return None
520
    RRR = lowerBoundSa.parent()
521
    intervalShrinkConstFactorSa = RRR('0.9')
522
    absoluteErrorTypeSo = pobyso_absolute_so_so()
523
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
524
    currentUpperBoundSa = upperBoundSa
525
    currentLowerBoundSa = lowerBoundSa
526
    # What we want here is the polynomial without the variable change, 
527
    # since our actual variable will be x-intervalCenter defined over the 
528
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
529
    (polySo, intervalCenterSo, maxErrorSo) = \
530
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
531
                                                    currentRangeSo, 
532
                                                    absoluteErrorTypeSo)
533
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
534
    while maxErrorSa > approxPrecSa:
535
        print "++Approximation error:", maxErrorSa.n()
536
        sollya_lib_clear_obj(polySo)
537
        sollya_lib_clear_obj(intervalCenterSo)
538
        sollya_lib_clear_obj(maxErrorSo)
539
        # Very empirical shrinking factor.
540
        shrinkFactorSa = 1 /  (maxErrorSa/approxPrecSa).log2().abs()
541
        print "Shrink factor:", \
542
              shrinkFactorSa.n(), \
543
              intervalShrinkConstFactorSa
544
        print
545
        #errorRatioSa = approxPrecSa/maxErrorSa
546
        #print "Error ratio: ", errorRatioSa
547
        # Make sure interval shrinks.
548
        if shrinkFactorSa > intervalShrinkConstFactorSa:
549
            actualShrinkFactorSa = intervalShrinkConstFactorSa
550
            #print "Fixed"
551
        else:
552
            actualShrinkFactorSa = shrinkFactorSa
553
            #print "Computed",shrinkFactorSa,maxErrorSa
554
            #print shrinkFactorSa, maxErrorSa
555
        #print "Shrink factor", actualShrinkFactorSa
556
        currentUpperBoundSa = currentLowerBoundSa + \
557
                                (currentUpperBoundSa - currentLowerBoundSa) * \
558
                                actualShrinkFactorSa
559
        #print "Current upper bound:", currentUpperBoundSa
560
        sollya_lib_clear_obj(currentRangeSo)
561
        # Check what is left with the bounds.
562
        if currentUpperBoundSa <= currentLowerBoundSa or \
563
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
564
            sollya_lib_clear_obj(absoluteErrorTypeSo)
565
            print "Can't find an interval."
566
            print "Use either or both a higher polynomial degree or a higher",
567
            print "internal precision."
568
            print "Aborting!"
569
            return (None, None, None, None)
570
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
571
                                                      currentUpperBoundSa)
572
        # print "New interval:",
573
        # pobyso_autoprint(currentRangeSo)
574
        #print "Second Taylor expansion call."
575
        (polySo, intervalCenterSo, maxErrorSo) = \
576
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
577
                                                        currentRangeSo, 
578
                                                        absoluteErrorTypeSo)
579
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
580
        #print "Max errorSo:",
581
        #pobyso_autoprint(maxErrorSo)
582
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
583
        #print "Max errorSa:", maxErrorSa
584
        #print "Sollya prec:",
585
        #pobyso_autoprint(sollya_lib_get_prec(None))
586
    sollya_lib_clear_obj(absoluteErrorTypeSo)
587
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
588
# End slz_compute_polynomial_and_interval
589
def slz_compute_reduced_polynomial(matrixRow,
590
                                    knownMonomials,
591
                                    var1,
592
                                    var1Bound,
593
                                    var2,
594
                                    var2Bound):
595
    """
596
    Compute a polynomial from a single reduced matrix row.
597
    This function was introduced in order to avoid the computation of the
598
    all the polynomials  from the full matrix (even those built from rows 
599
    that do no verify the Coppersmith condition) as this may involves 
600
    expensive operations over (large) integers.
601
    """
602
    ## Check arguments.
603
    if len(knownMonomials) == 0:
604
        return None
605
    # varNounds can be zero since 0^0 returns 1.
606
    if (var1Bound < 0) or (var2Bound < 0):
607
        return None
608
    ## Initialisations. 
609
    polynomialRing = knownMonomials[0].parent() 
610
    currentPolynomial = polynomialRing(0)
611
    # TODO: use zip instead of indices.
612
    for colIndex in xrange(0, len(knownMonomials)):
613
        currentCoefficient = matrixRow[colIndex]
614
        if currentCoefficient != 0:
615
            #print "Current coefficient:", currentCoefficient
616
            currentMonomial = knownMonomials[colIndex]
617
            #print "Monomial as multivariate polynomial:", \
618
            #currentMonomial, type(currentMonomial)
619
            degreeInVar1 = currentMonomial.degree(var1)
620
            #print "Degree in var1", var1, ":", degreeInVar1
621
            degreeInVar2 = currentMonomial.degree(var2)
622
            #print "Degree in var2", var2, ":", degreeInVar2
623
            if degreeInVar1 > 0:
624
                currentCoefficient = \
625
                    currentCoefficient / (var1Bound^degreeInVar1)
626
                #print "varBound1 in degree:", var1Bound^degreeInVar1
627
                #print "Current coefficient(1)", currentCoefficient
628
            if degreeInVar2 > 0:
629
                currentCoefficient = \
630
                    currentCoefficient / (var2Bound^degreeInVar2)
631
                #print "Current coefficient(2)", currentCoefficient
632
            #print "Current reduced monomial:", (currentCoefficient * \
633
            #                                    currentMonomial)
634
            currentPolynomial += (currentCoefficient * currentMonomial)
635
            #print "Current polynomial:", currentPolynomial
636
        # End if
637
    # End for colIndex.
638
    #print "Type of the current polynomial:", type(currentPolynomial)
639
    return(currentPolynomial)
640
# End slz_compute_reduced_polynomial
641
#
642
def slz_compute_reduced_polynomials(reducedMatrix,
643
                                        knownMonomials,
644
                                        var1,
645
                                        var1Bound,
646
                                        var2,
647
                                        var2Bound):
648
    """
649
    Legacy function, use slz_compute_reduced_polynomials_list
650
    """
651
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
652
                                                knownMonomials,
653
                                                var1,
654
                                                var1Bound,
655
                                                var2,
656
                                                var2Bound)
657
    )
658
#
659
def slz_compute_reduced_polynomials_list(reducedMatrix,
660
                                         knownMonomials,
661
                                         var1,
662
                                         var1Bound,
663
                                         var2,
664
                                         var2Bound):
665
    """
666
    From a reduced matrix, holding the coefficients, from a monomials list,
667
    from the bounds of each variable, compute the corresponding polynomials
668
    scaled back by dividing by the "right" powers of the variables bounds.
669
    
670
    The elements in knownMonomials must be of the "right" polynomial type.
671
    They set the polynomial type of the output polynomials in list.
672
    @param reducedMatrix:  the reduced matrix as output from LLL;
673
    @param kwnonMonomials: the ordered list of the monomials used to
674
                           build the polynomials;
675
    @param var1:           the first variable (of the "right" type);
676
    @param var1Bound:      the first variable bound;
677
    @param var2:           the second variable (of the "right" type);
678
    @param var2Bound:      the second variable bound.
679
    @return: a list of polynomials obtained with the reduced coefficients
680
             and scaled down with the bounds
681
    """
682
    
683
    # TODO: check input arguments.
684
    reducedPolynomials = []
685
    #print "type var1:", type(var1), " - type var2:", type(var2)
686
    for matrixRow in reducedMatrix.rows():
687
        currentPolynomial = 0
688
        for colIndex in xrange(0, len(knownMonomials)):
689
            currentCoefficient = matrixRow[colIndex]
690
            if currentCoefficient != 0:
691
                #print "Current coefficient:", currentCoefficient
692
                currentMonomial = knownMonomials[colIndex]
693
                parentRing = currentMonomial.parent()
694
                #print "Monomial as multivariate polynomial:", \
695
                #currentMonomial, type(currentMonomial)
696
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
697
                #print "Degree in var", var1, ":", degreeInVar1
698
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
699
                #print "Degree in var", var2, ":", degreeInVar2
700
                if degreeInVar1 > 0:
701
                    currentCoefficient /= var1Bound^degreeInVar1
702
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
703
                    #print "Current coefficient(1)", currentCoefficient
704
                if degreeInVar2 > 0:
705
                    currentCoefficient /= var2Bound^degreeInVar2
706
                    #print "Current coefficient(2)", currentCoefficient
707
                #print "Current reduced monomial:", (currentCoefficient * \
708
                #                                    currentMonomial)
709
                currentPolynomial += (currentCoefficient * currentMonomial)
710
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
711
                    #print "!!!! constant term !!!!"
712
                #print "Current polynomial:", currentPolynomial
713
            # End if
714
        # End for colIndex.
715
        #print "Type of the current polynomial:", type(currentPolynomial)
716
        reducedPolynomials.append(currentPolynomial)
717
    return reducedPolynomials 
718
# End slz_compute_reduced_polynomials_list.
719

    
720
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
721
                                                   knownMonomials,
722
                                                   var1,
723
                                                   var1Bound,
724
                                                   var2,
725
                                                   var2Bound):
726
    """
727
    From a list of rows, holding the coefficients, from a monomials list,
728
    from the bounds of each variable, compute the corresponding polynomials
729
    scaled back by dividing by the "right" powers of the variables bounds.
730
    
731
    The elements in knownMonomials must be of the "right" polynomial type.
732
    They set the polynomial type of the output polynomials in list.
733
    @param rowsList:       the reduced matrix as output from LLL;
734
    @param kwnonMonomials: the ordered list of the monomials used to
735
                           build the polynomials;
736
    @param var1:           the first variable (of the "right" type);
737
    @param var1Bound:      the first variable bound;
738
    @param var2:           the second variable (of the "right" type);
739
    @param var2Bound:      the second variable bound.
740
    @return: a list of polynomials obtained with the reduced coefficients
741
             and scaled down with the bounds
742
    """
743
    
744
    # TODO: check input arguments.
745
    reducedPolynomials = []
746
    #print "type var1:", type(var1), " - type var2:", type(var2)
747
    for matrixRow in rowsList:
748
        currentPolynomial = 0
749
        for colIndex in xrange(0, len(knownMonomials)):
750
            currentCoefficient = matrixRow[colIndex]
751
            if currentCoefficient != 0:
752
                #print "Current coefficient:", currentCoefficient
753
                currentMonomial = knownMonomials[colIndex]
754
                parentRing = currentMonomial.parent()
755
                #print "Monomial as multivariate polynomial:", \
756
                #currentMonomial, type(currentMonomial)
757
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
758
                #print "Degree in var", var1, ":", degreeInVar1
759
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
760
                #print "Degree in var", var2, ":", degreeInVar2
761
                if degreeInVar1 > 0:
762
                    currentCoefficient /= var1Bound^degreeInVar1
763
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
764
                    #print "Current coefficient(1)", currentCoefficient
765
                if degreeInVar2 > 0:
766
                    currentCoefficient /= var2Bound^degreeInVar2
767
                    #print "Current coefficient(2)", currentCoefficient
768
                #print "Current reduced monomial:", (currentCoefficient * \
769
                #                                    currentMonomial)
770
                currentPolynomial += (currentCoefficient * currentMonomial)
771
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
772
                    #print "!!!! constant term !!!!"
773
                #print "Current polynomial:", currentPolynomial
774
            # End if
775
        # End for colIndex.
776
        #print "Type of the current polynomial:", type(currentPolynomial)
777
        reducedPolynomials.append(currentPolynomial)
778
    return reducedPolynomials 
779
# End slz_compute_reduced_polynomials_list_from_rows.
780
#
781
def slz_compute_scaled_function(functionSa,
782
                                lowerBoundSa,
783
                                upperBoundSa,
784
                                floatingPointPrecSa,
785
                                debug=False):
786
    """
787
    From a function, compute the scaled function whose domain
788
    is included in [1, 2) and whose image is also included in [1,2).
789
    Return a tuple: 
790
    [0]: the scaled function
791
    [1]: the scaled domain lower bound
792
    [2]: the scaled domain upper bound
793
    [3]: the scaled image lower bound
794
    [4]: the scaled image upper bound
795
    """
796
    ## The variable can be called anything.
797
    x = functionSa.variables()[0]
798
    # Scalling the domain -> [1,2[.
799
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
800
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
801
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
802
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
803
    if debug:
804
        print "domainScalingExpression for argument :", \
805
        invDomainScalingExpressionSa
806
        print "function: ", functionSa
807
    ff = functionSa.subs({x : domainScalingExpressionSa})
808
    ## Bless expression as a function.
809
    ff = ff.function(x)
810
    #ff = f.subs_expr(x==domainScalingExpressionSa)
811
    #domainScalingFunction(x) = invDomainScalingExpressionSa
812
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
813
    scaledLowerBoundSa = \
814
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
815
    scaledUpperBoundSa = \
816
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
817
    if debug:
818
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
819
              scaledUpperBoundSa
820
    #
821
    # Scalling the image -> [1,2[.
822
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
823
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
824
    if flbSa <= fubSa: # Increasing
825
        imageBinadeBottomSa = floor(flbSa.log2())
826
    else: # Decreasing
827
        imageBinadeBottomSa = floor(fubSa.log2())
828
    if debug:
829
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
830
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
831
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
832
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
833
    if debug:
834
        print "imageScalingExpression for argument :", \
835
              invImageScalingExpressionSa
836
    iis = invImageScalingExpressionSa.function(x)
837
    fff = iis.subs({x:ff})
838
    if debug:
839
        print "fff:", fff,
840
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
841
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
842
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
843
# End slz_compute_scaled_function
844

    
845
def slz_fix_bounds_for_binades(lowerBound, 
846
                               upperBound, 
847
                               func = None, 
848
                               domainDirection = -1,
849
                               imageDirection  = -1):
850
    """
851
    Assuming the function is increasing or decreasing over the 
852
    [lowerBound, upperBound] interval, return a lower bound lb and 
853
    an upper bound ub such that:
854
    - lb and ub belong to the same binade;
855
    - func(lb) and func(ub) belong to the same binade.
856
    domainDirection indicate how bounds move to fit into the same binade:
857
    - a negative value move the upper bound down;
858
    - a positive value move the lower bound up.
859
    imageDirection indicate how bounds move in order to have their image
860
    fit into the same binade, variation of the function is also condidered.
861
    For an increasing function:
862
    - negative value moves the upper bound down (and its image value as well);
863
    - a positive values moves the lower bound up (and its image value as well);
864
    For a decreasing function it is the other way round.
865
    """
866
    ## Arguments check
867
    if lowerBound > upperBound:
868
        return None
869
    if lowerBound == upperBound:
870
        return (lowerBound, upperBound)
871
    if func is None:
872
        return None
873
    #
874
    #varFunc = func.variables()[0]
875
    lb = lowerBound
876
    ub = upperBound
877
    lbBinade = slz_compute_binade(lb) 
878
    ubBinade = slz_compute_binade(ub)
879
    ## Domain binade.
880
    while lbBinade != ubBinade:
881
        newIntervalWidth = (ub - lb) / 2
882
        if domainDirection < 0:
883
            ub = lb + newIntervalWidth
884
            ubBinade = slz_compute_binade(ub)
885
        else:
886
            lb = lb + newIntervalWidth
887
            lbBinade = slz_compute_binade(lb) 
888
    ## Image binade.
889
    if lb == ub:
890
        return (lb, ub)
891
    lbImg = func(lb)
892
    ubImg = func(ub)
893
    funcIsInc = (ubImg >= lbImg)
894
    lbImgBinade = slz_compute_binade(lbImg)
895
    ubImgBinade = slz_compute_binade(ubImg)
896
    while lbImgBinade != ubImgBinade:
897
        newIntervalWidth = (ub - lb) / 2
898
        if imageDirection < 0:
899
            if funcIsInc:
900
                ub = lb + newIntervalWidth
901
                ubImgBinade = slz_compute_binade(func(ub))
902
                #print ubImgBinade
903
            else:
904
                lb = lb + newIntervalWidth
905
                lbImgBinade = slz_compute_binade(func(lb))
906
                #print lbImgBinade
907
        else:
908
            if funcIsInc:
909
                lb = lb + newIntervalWidth
910
                lbImgBinade = slz_compute_binade(func(lb))
911
                #print lbImgBinade
912
            else:
913
                ub = lb + newIntervalWidth
914
                ubImgBinade = slz_compute_binade(func(ub))
915
                #print ubImgBinade
916
    # End while lbImgBinade != ubImgBinade:
917
    return (lb, ub)
918
# End slz_fix_bounds_for_binades.
919

    
920
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
921
    # Create a polynomial over the rationals.
922
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
923
    return(ratPolynomialRing(polyOfFloat))
924
# End slz_float_poly_of_float_to_rat_poly_of_rat.
925

    
926
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
927
    """
928
     Create a polynomial over the rationals where all denominators are
929
     powers of two.
930
    """
931
    polyVariable = polyOfFloat.variables()[0] 
932
    RPR = QQ[str(polyVariable)]
933
    polyCoeffs      = polyOfFloat.coefficients()
934
    #print polyCoeffs
935
    polyExponents   = polyOfFloat.exponents()
936
    #print polyExponents
937
    polyDenomPtwoCoeffs = []
938
    for coeff in polyCoeffs:
939
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
940
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
941
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
942
    ratPoly = RPR(0)
943
    #print type(ratPoly)
944
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
945
    #  The coefficient becomes plainly wrong when exponent == 0.
946
    #  No clue as to why.
947
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
948
        ratPoly += coeff * RPR(polyVariable^exponent)
949
    return ratPoly
950
# End slz_float_poly_of_float_to_rat_poly_of_rat.
951

    
952
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
953
                                      lowerBoundSa, 
954
                                      upperBoundSa, floatingPointPrecSa, 
955
                                      internalSollyaPrecSa, approxPrecSa):
956
    """
957
    Under the assumption that:
958
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
959
    - lowerBound and upperBound belong to the same binade.
960
    from a:
961
    - function;
962
    - a degree
963
    - a pair of bounds;
964
    - the floating-point precision we work on;
965
    - the internal Sollya precision;
966
    - the requested approximation error
967
    The initial interval is, possibly, splitted into smaller intervals.
968
    It return a list of tuples, each made of:
969
    - a first polynomial (without the changed variable f(x) = p(x-x0));
970
    - a second polynomial (with a changed variable f(x) = q(x))
971
    - the approximation interval;
972
    - the center, x0, of the interval;
973
    - the corresponding approximation error.
974
    TODO: fix endless looping for some parameters sets.
975
    """
976
    resultArray = []
977
    # Set Sollya to the necessary internal precision.
978
    precChangedSa = False
979
    currentSollyaPrecSo = pobyso_get_prec_so()
980
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
981
    if internalSollyaPrecSa > currentSollyaPrecSa:
982
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
983
        precChangedSa = True
984
    #
985
    x = functionSa.variables()[0] # Actual variable name can be anything.
986
    # Scaled function: [1=,2] -> [1,2].
987
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
988
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
989
                slz_compute_scaled_function(functionSa,                       \
990
                                            lowerBoundSa,                     \
991
                                            upperBoundSa,                     \
992
                                            floatingPointPrecSa)
993
    # In case bounds were in the negative real one may need to
994
    # switch scaled bounds.
995
    if scaledLowerBoundSa > scaledUpperBoundSa:
996
        scaledLowerBoundSa, scaledUpperBoundSa = \
997
            scaledUpperBoundSa, scaledLowerBoundSa
998
        #print "Switching!"
999
    print "Approximation precision: ", RR(approxPrecSa)
1000
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1001
    functionSo = \
1002
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1003
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1004
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1005
                                                  scaledUpperBoundSa)
1006

    
1007
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1008
                                              upperBoundSa.parent().precision()))
1009
    currentScaledLowerBoundSa = scaledLowerBoundSa
1010
    currentScaledUpperBoundSa = scaledUpperBoundSa
1011
    while True:
1012
        ## Compute the first Taylor expansion.
1013
        print "Computing a Taylor expansion..."
1014
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1015
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1016
                                        currentScaledLowerBoundSa, 
1017
                                        currentScaledUpperBoundSa,
1018
                                        approxPrecSa, internalSollyaPrecSa)
1019
        print "...done."
1020
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1021
        #  This value goes to the first variable: polySo. Other variables are
1022
        #  not assigned and should not be tested.
1023
        if polySo is None:
1024
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1025
            if precChangedSa:
1026
                pobyso_set_prec_so_so(currentSollyaPrecSo)
1027
                sollya_lib_clear_obj(currentSollyaPrecSo)
1028
            sollya_lib_clear_obj(functionSo)
1029
            sollya_lib_clear_obj(degreeSo)
1030
            sollya_lib_clear_obj(scaledBoundsSo)
1031
            return None
1032
        ## Add to the result array.
1033
        ### Change variable stuff in Sollya x -> x0-x.
1034
        changeVarExpressionSo = \
1035
            sollya_lib_build_function_sub( \
1036
                              sollya_lib_build_function_free_variable(), 
1037
                              sollya_lib_copy_obj(intervalCenterSo))
1038
        polyVarChangedSo = \
1039
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1040
        #### Get rid of the variable change Sollya stuff. 
1041
        sollya_lib_clear_obj(changeVarExpressionSo)
1042
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1043
                            intervalCenterSo, maxErrorSo))
1044
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1045
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1046
        print "Computed approximation error:", errorSa.n(digits=10)
1047
        # If the error and interval are OK a the first try, just return.
1048
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1049
            (errorSa <= approxPrecSa):
1050
            if precChangedSa:
1051
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1052
            sollya_lib_clear_obj(currentSollyaPrecSo)
1053
            sollya_lib_clear_obj(functionSo)
1054
            sollya_lib_clear_obj(degreeSo)
1055
            sollya_lib_clear_obj(scaledBoundsSo)
1056
            #print "Approximation error:", errorSa
1057
            return resultArray
1058
        ## The returned interval upper bound does not reach the requested upper
1059
        #  upper bound: compute the next upper bound.
1060
        ## The following ratio is always >= 1. If errorSa, we may want to
1061
        #  enlarge the interval
1062
        currentErrorRatio = approxPrecSa / errorSa
1063
        ## --|--------------------------------------------------------------|--
1064
        #  --|--------------------|--------------------------------------------
1065
        #  --|----------------------------|------------------------------------
1066
        ## Starting point for the next upper bound.
1067
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1068
        # Compute the increment. 
1069
        newBoundsWidthSa = \
1070
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1071
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1072
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1073
        # Take into account the original interval upper bound.                     
1074
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1075
            currentScaledUpperBoundSa = scaledUpperBoundSa
1076
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1077
            print "Can't shrink the interval anymore!"
1078
            print "You should consider increasing the Sollya internal precision"
1079
            print "or the polynomial degree."
1080
            print "Giving up!"
1081
            if precChangedSa:
1082
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1083
            sollya_lib_clear_obj(currentSollyaPrecSo)
1084
            sollya_lib_clear_obj(functionSo)
1085
            sollya_lib_clear_obj(degreeSo)
1086
            sollya_lib_clear_obj(scaledBoundsSo)
1087
            return None
1088
        # Compute the other expansions.
1089
        # Test for insufficient precision.
1090
# End slz_get_intervals_and_polynomials
1091

    
1092
def slz_interval_scaling_expression(boundsInterval, expVar):
1093
    """
1094
    Compute the scaling expression to map an interval that spans at most
1095
    a single binade into [1, 2) and the inverse expression as well.
1096
    If the interval spans more than one binade, result is wrong since
1097
    scaling is only based on the lower bound.
1098
    Not very sure that the transformation makes sense for negative numbers.
1099
    """
1100
    # The "one of the bounds is 0" special case. Here we consider
1101
    # the interval as the subnormals binade.
1102
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1103
        # The upper bound is (or should be) positive.
1104
        if boundsInterval.endpoints()[0] == 0:
1105
            if boundsInterval.endpoints()[1] == 0:
1106
                return None
1107
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1108
            l2     = boundsInterval.endpoints()[1].abs().log2()
1109
            # The upper bound is a power of two
1110
            if binade == l2:
1111
                scalingCoeff    = 2^(-binade)
1112
                invScalingCoeff = 2^(binade)
1113
                scalingOffset   = 1
1114
                return \
1115
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1116
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1117
            else:
1118
                scalingCoeff    = 2^(-binade-1)
1119
                invScalingCoeff = 2^(binade+1)
1120
                scalingOffset   = 1
1121
                return((scalingCoeff * expVar + scalingOffset),\
1122
                    ((expVar - scalingOffset) * invScalingCoeff))
1123
        # The lower bound is (or should be) negative.
1124
        if boundsInterval.endpoints()[1] == 0:
1125
            if boundsInterval.endpoints()[0] == 0:
1126
                return None
1127
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1128
            l2     = boundsInterval.endpoints()[0].abs().log2()
1129
            # The upper bound is a power of two
1130
            if binade == l2:
1131
                scalingCoeff    = -2^(-binade)
1132
                invScalingCoeff = -2^(binade)
1133
                scalingOffset   = 1
1134
                return((scalingCoeff * expVar + scalingOffset),\
1135
                    ((expVar - scalingOffset) * invScalingCoeff))
1136
            else:
1137
                scalingCoeff    = -2^(-binade-1)
1138
                invScalingCoeff = -2^(binade+1)
1139
                scalingOffset   = 1
1140
                return((scalingCoeff * expVar + scalingOffset),\
1141
                   ((expVar - scalingOffset) * invScalingCoeff))
1142
    # General case
1143
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1144
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1145
    # We allow for a single exception in binade spanning is when the
1146
    # "outward bound" is a power of 2 and its binade is that of the
1147
    # "inner bound" + 1.
1148
    if boundsInterval.endpoints()[0] > 0:
1149
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1150
        if lbBinade != ubBinade:
1151
            print "Different binades."
1152
            if ubL2 != ubBinade:
1153
                print "Not a power of 2."
1154
                return None
1155
            elif abs(ubBinade - lbBinade) > 1:
1156
                print "Too large span:", abs(ubBinade - lbBinade)
1157
                return None
1158
    else:
1159
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1160
        if lbBinade != ubBinade:
1161
            print "Different binades."
1162
            if lbL2 != lbBinade:
1163
                print "Not a power of 2."
1164
                return None
1165
            elif abs(ubBinade - lbBinade) > 1:
1166
                print "Too large span:", abs(ubBinade - lbBinade)
1167
                return None
1168
    #print "Lower bound binade:", binade
1169
    if boundsInterval.endpoints()[0] > 0:
1170
        return \
1171
            ((2^(-lbBinade) * expVar).function(expVar),
1172
             (2^(lbBinade) * expVar).function(expVar))
1173
    else:
1174
        return \
1175
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1176
             (-(2^(ubBinade)) * expVar).function(expVar))
1177
"""
1178
    # Code sent to attic. Should be the base for a 
1179
    # "slz_interval_translate_expression" rather than scale.
1180
    # Extra control and special cases code  added in  
1181
    # slz_interval_scaling_expression could (should ?) be added to
1182
    # this new function.
1183
    # The scaling offset is only used for negative numbers.
1184
    # When the absolute value of the lower bound is < 0.
1185
    if abs(boundsInterval.endpoints()[0]) < 1:
1186
        if boundsInterval.endpoints()[0] >= 0:
1187
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1188
            invScalingCoeff = 1/scalingCoeff
1189
            return((scalingCoeff * expVar, 
1190
                    invScalingCoeff * expVar))
1191
        else:
1192
            scalingCoeff = \
1193
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1194
            scalingOffset = -3 * scalingCoeff
1195
            return((scalingCoeff * expVar + scalingOffset,
1196
                    1/scalingCoeff * expVar + 3))
1197
    else: 
1198
        if boundsInterval.endpoints()[0] >= 0:
1199
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1200
            scalingOffset = 0
1201
            return((scalingCoeff * expVar, 
1202
                    1/scalingCoeff * expVar))
1203
        else:
1204
            scalingCoeff = \
1205
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1206
            scalingOffset =  -3 * scalingCoeff
1207
            #scalingOffset = 0
1208
            return((scalingCoeff * expVar + scalingOffset,
1209
                    1/scalingCoeff * expVar + 3))
1210
"""
1211
# End slz_interval_scaling_expression
1212
   
1213
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1214
    """
1215
    Compute the Sage version of the Taylor polynomial and it's
1216
    companion data (interval, center...)
1217
    The input parameter is a five elements tuple:
1218
    - [0]: the polyomial (without variable change), as polynomial over a
1219
           real ring;
1220
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1221
           over a real ring;
1222
    - [2]: the interval (as Sollya range);
1223
    - [3]: the interval center;
1224
    - [4]: the approximation error. 
1225
    
1226
    The function return a 5 elements tuple: formed with all the 
1227
    input elements converted into their Sollya counterpart.
1228
    """
1229
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
1230
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
1231
    intervalSa = \
1232
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1233
    centerSa = \
1234
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1235
    errorSa = \
1236
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1237
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1238
    # End slz_interval_and_polynomial_to_sage
1239

    
1240
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1241
            targetPlusOnePrecRF = None, quasiExactRF = None):
1242
    """
1243
      Check if an element (argument) of the domain of function (function)
1244
      yields a HTRN case (rounding to next) for the target precision 
1245
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1246
      
1247
      The strategy is: 
1248
      - compute the image at high (quasi-exact) precision;
1249
      - round it to nearest to precision;
1250
      - round it to exactly to (precision+1), the computed number has two
1251
        midpoint neighbors;
1252
      - check the distance between these neighbors and the quasi-exact value;
1253
        - if none of them is closer than the targetAccuracy, return False,
1254
        - else return true.
1255
      - Powers of two are a special case when comparing the midpoint in
1256
        the 0 direction..
1257
    """
1258
    ## Arguments filtering. 
1259
    ## TODO: filter the first argument for consistence.
1260
    if targetRF is None:
1261
        targetRF = argument.parent()
1262
    ## Ditto for the real field holding the midpoints.
1263
    if targetPlusOnePrecRF is None:
1264
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1265
    ## If no quasiExactField is provided, create a high accuracy RealField.
1266
    if quasiExactRF is None:
1267
        quasiExactRF = RealField(1536)
1268
    function                      = function.function(function.variables()[0])
1269
    exactArgument                 = quasiExactRF(argument)
1270
    quasiExactValue               = function(exactArgument)
1271
    roundedValue                  = targetRF(quasiExactValue)
1272
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1273
    # Upper midpoint.
1274
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1275
    # Lower midpoint.
1276
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1277
    binade                        = slz_compute_binade(roundedValue)
1278
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1279
    #print "Argument:", argument
1280
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1281
    #print "Binade:", binade
1282
    #print "binadeCorrectedTargetAccuracy:", \
1283
    #binadeCorrectedTargetAccuracy.n(prec=100)
1284
    #print "binadeCorrectedTargetAccuracy:", \
1285
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1286
    #print "Upper midpoint:", \
1287
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1288
    #print "Rounded value :", \
1289
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1290
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1291
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1292
    #print "Lower midpoint:", \
1293
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1294
    ## Make quasiExactValue = 0 a special case to move it out of the way.
1295
    if quasiExactValue == 0:
1296
        return False
1297
    ## Begining of the general case : check with the midpoint of 
1298
    #  greatest absolute value.
1299
    if quasiExactValue > 0:
1300
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1301
           binadeCorrectedTargetAccuracy:
1302
            #print "Too close to the upper midpoint: ", \
1303
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1304
            #print argument.n().str(base=16, \
1305
            #  no_sci=False)
1306
            #print "Lower midpoint:", \
1307
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1308
            #print "Value         :", \
1309
            # quasiExactValue.n(prec=200).str(base=2)
1310
            #print "Upper midpoint:", \
1311
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1312
            return True
1313
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
1314
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1315
           binadeCorrectedTargetAccuracy:
1316
            #print "Too close to the upper midpoint: ", \
1317
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1318
            #print argument.n().str(base=16, \
1319
            #  no_sci=False)
1320
            #print "Lower midpoint:", \
1321
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1322
            #print "Value         :", \
1323
            #  quasiExactValue.n(prec=200).str(base=2)
1324
            #print "Upper midpoint:", \
1325
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1326
            #print
1327
            return True
1328
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1329
    ## For the midpoint of smaller absolute value, 
1330
    #  split cases with the powers of 2.
1331
    if sno_abs_is_power_of_two(roundedValue):
1332
        if quasiExactValue > 0:        
1333
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1334
               binadeCorrectedTargetAccuracy / 2:
1335
                #print "Lower midpoint:", \
1336
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1337
                #print "Value         :", \
1338
                #  quasiExactValue.n(prec=200).str(base=2)
1339
                #print "Upper midpoint:", \
1340
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1341
                #print
1342
                return True
1343
        else:
1344
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1345
              binadeCorrectedTargetAccuracy / 2:
1346
                #print "Lower midpoint:", \
1347
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1348
                #print "Value         :", 
1349
                #  quasiExactValue.n(prec=200).str(base=2)
1350
                #print "Upper midpoint:", 
1351
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1352
                #print
1353
                return True
1354
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1355
    else: ## Not a power of 2, regular comparison with the midpoint of 
1356
          #  smaller absolute value.
1357
        if quasiExactValue > 0:        
1358
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1359
              binadeCorrectedTargetAccuracy:
1360
                #print "Lower midpoint:", \
1361
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1362
                #print "Value         :", \
1363
                #  quasiExactValue.n(prec=200).str(base=2)
1364
                #print "Upper midpoint:", \
1365
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1366
                #print
1367
                return True
1368
        else: # quasiExactValue <= 0
1369
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1370
              binadeCorrectedTargetAccuracy:
1371
                #print "Lower midpoint:", \
1372
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1373
                #print "Value         :", \
1374
                #  quasiExactValue.n(prec=200).str(base=2)
1375
                #print "Upper midpoint:", \
1376
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1377
                #print
1378
                return True
1379
    #print "distance to the upper midpoint:", \
1380
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1381
    #print "distance to the lower midpoint:", \
1382
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1383
    return False
1384
# End slz_is_htrn
1385

    
1386
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1387
                                                precision,
1388
                                                targetHardnessToRound,
1389
                                                variable1,
1390
                                                variable2):
1391
    """
1392
    Creates a new multivariate polynomial with integer coefficients for use
1393
     with the Coppersmith method.
1394
    A the same time it computes :
1395
    - 2^K (N);
1396
    - 2^k (bound on the second variable)
1397
    - lcm
1398

    
1399
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1400
                         variables.
1401
    :param precision: the precision of the floating-point coefficients.
1402
    :param targetHardnessToRound: the hardness to round we want to check.
1403
    :param variable1: the first variable of the polynomial (an expression).
1404
    :param variable2: the second variable of the polynomial (an expression).
1405
    
1406
    :returns: a 4 elements tuple:
1407
                - the polynomial;
1408
                - the modulus (N);
1409
                - the t bound;
1410
                - the lcm used to compute the integral coefficients and the 
1411
                  module.
1412
    """
1413
    # Create a new integer polynomial ring.
1414
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1415
    # Coefficients are issued in the increasing power order.
1416
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1417
    # Print the reversed list for debugging.
1418
    #print
1419
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1420
    # Build the list of number we compute the lcm of.
1421
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1422
    #print "Coefficient denominators:", coefficientDenominators
1423
    coefficientDenominators.append(2^precision)
1424
    coefficientDenominators.append(2^(targetHardnessToRound))
1425
    leastCommonMultiple = lcm(coefficientDenominators)
1426
    # Compute the expression corresponding to the new polynomial
1427
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1428
    #print coefficientNumerators
1429
    polynomialExpression = 0
1430
    power = 0
1431
    # Iterate over two lists at the same time, stop when the shorter
1432
    # (is this case coefficientsNumerators) is 
1433
    # exhausted. Both lists are ordered in the order of increasing powers
1434
    # of variable1.
1435
    for numerator, denominator in \
1436
                        zip(coefficientNumerators, coefficientDenominators):
1437
        multiplicator = leastCommonMultiple / denominator 
1438
        newCoefficient = numerator * multiplicator
1439
        polynomialExpression += newCoefficient * variable1^power
1440
        power +=1
1441
    polynomialExpression += - variable2
1442
    return (IP(polynomialExpression),
1443
            leastCommonMultiple / 2^precision, # 2^K aka N.
1444
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1445
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1446
            leastCommonMultiple) # If we want to make test computations.
1447
        
1448
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1449

    
1450
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1451
                                          precision):
1452
    """
1453
    Makes a variable substitution into the input polynomial so that the output
1454
    polynomial can take integer arguments.
1455
    All variables of the input polynomial "have precision p". That is to say
1456
    that they are rationals with denominator == 2^(precision - 1): 
1457
    x = y/2^(precision - 1).
1458
    We "incorporate" these denominators into the coefficients with, 
1459
    respectively, the "right" power.
1460
    """
1461
    polynomialField = ratPolyOfRat.parent()
1462
    polynomialVariable = ratPolyOfRat.variables()[0]
1463
    #print "The polynomial field is:", polynomialField
1464
    return \
1465
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1466
                                   polynomialVariable/2^(precision-1)}))
1467
    
1468
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1469

    
1470
def slz_reduce_and_test_base(matrixToReduce,
1471
                             nAtAlpha,
1472
                             monomialsCountSqrt):
1473
    """
1474
    Reduces the basis, tests the Coppersmith condition and returns
1475
    a list of rows that comply with the condition.
1476
    """
1477
    ccComplientRowsList = []
1478
    reducedMatrix = matrixToReduce.LLL(None)
1479
    if not reducedMatrix.is_LLL_reduced():
1480
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1481
    else:
1482
        print "reducedMatrix is actually reduced."
1483
    print "N^alpha:", nAtAlpha.n()
1484
    rowIndex = 0
1485
    for row in reducedMatrix.rows():
1486
        l2Norm = row.norm(2)
1487
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1488
                monomialsCountSqrt.n(), ". Is vector OK?", 
1489
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1490
            ccComplientRowsList.append(row)
1491
            print "True"
1492
        else:
1493
            print "False"
1494
    # End for
1495
    return ccComplientRowsList
1496
# End slz_reduce_and_test_base
1497

    
1498
def slz_resultant(poly1, poly2, elimVar):
1499
    """
1500
    Compute the resultant for two polynomials for a given variable
1501
    and return the (poly1, poly2, resultant) if the resultant
1502
    is not 0.
1503
    Return () otherwise.
1504
    """
1505
    polynomialRing0    = poly1.parent()
1506
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1507
    if resultantInElimVar is None:
1508
        return None
1509
    if resultantInElimVar.is_zero():
1510
        return None
1511
    else:
1512
        return resultantInElimVar
1513
# End slz_resultant.
1514
#
1515
def slz_resultant_tuple(poly1, poly2, elimVar):
1516
    """
1517
    Compute the resultant for two polynomials for a given variable
1518
    and return the (poly1, poly2, resultant) if the resultant
1519
    is not 0.
1520
    Return () otherwise.
1521
    """
1522
    polynomialRing0    = poly1.parent()
1523
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1524
    if resultantInElimVar.is_zero():
1525
        return ()
1526
    else:
1527
        return (poly1, poly2, resultantInElimVar)
1528
# End slz_resultant_tuple.
1529
#
1530
print "\t...sageSLZ loaded"