Statistiques
| Révision :

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

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

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

    
252
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
253
                                                                    alpha,
254
                                                                    N,
255
                                                                    iBound,
256
                                                                    tBound,
257
                                                                    debug = False):
258
    """
259
    For a given set of arguments (see below), compute a list
260
    of "reduced polynomials" that could be used to compute roots
261
    of the inputPolynomial.
262
    Print the volume of the initial basis as well.
263
    INPUT:
264
    
265
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
266
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
267
    - "N" -- the modulus;
268
    - "iBound" -- the bound on the first variable;
269
    - "tBound" -- the bound on the second variable.
270
    
271
    OUTPUT:
272
    
273
    A list of bivariate integer polynomial obtained using the Coppersmith
274
    algorithm. The polynomials correspond to the rows of the LLL-reduce
275
    reduced base that comply with the Coppersmith condition.
276
    """
277
    # Arguments check.
278
    if iBound == 0 or tBound == 0:
279
        return None
280
    # End arguments check.
281
    nAtAlpha = N^alpha
282
    if debug:
283
        print "N at alpha:", nAtAlpha
284
    ## Building polynomials for matrix.
285
    polyRing   = inputPolynomial.parent()
286
    # Whatever the 2 variables are actually called, we call them
287
    # 'i' and 't' in all the variable names.
288
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
289
    #print polyVars[0], type(polyVars[0])
290
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
291
                                              tVariable:tVariable * tBound})
292
##    polynomialsList = \
293
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
294
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
295
    polynomialsList = \
296
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
297
                                             alpha,
298
                                             N,
299
                                             iBound,
300
                                             tBound,
301
                                             0)
302
    #print "Polynomials list:", polynomialsList
303
    ## Building the proto matrix.
304
    knownMonomials = []
305
    protoMatrix    = []
306
    for poly in polynomialsList:
307
        spo_add_polynomial_coeffs_to_matrix_row(poly,
308
                                                knownMonomials,
309
                                                protoMatrix,
310
                                                0)
311
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
312
    matrixToReduceTranspose = matrixToReduce.transpose()
313
    squareMatrix = matrixToReduce * matrixToReduceTranspose
314
    squareMatDet = det(squareMatrix)
315
    latticeVolume = sqrt(squareMatDet)
316
    print "Lattice volume:", latticeVolume.n()
317
    print "Lattice volume / N:", (latticeVolume/N).n()
318
    #print matrixToReduce
319
    ## Reduction and checking.
320
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
321
    #  error message issued when previous code was used.
322
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
323
    reductionTimeStart = cputime() 
324
    reducedMatrix = matrixToReduce.LLL(fp=None)
325
    reductionTime = cputime(reductionTimeStart)
326
    print "Reduction time:", reductionTime
327
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
328
    if not isLLLReduced:
329
       return None
330
    #
331
    if debug:
332
        matrixFile = file('/tmp/reducedMatrix.txt', 'w')
333
        for row in reducedMatrix.rows():
334
            matrixFile.write(str(row) + "\n")
335
        matrixFile.close()
336
    #
337
    monomialsCount     = len(knownMonomials)
338
    monomialsCountSqrt = sqrt(monomialsCount)
339
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
340
    #print reducedMatrix
341
    ## Check the Coppersmith condition for each row and build the reduced 
342
    #  polynomials.
343
    ccVectorsCount = 0
344
    ccReducedPolynomialsList = []
345
    for row in reducedMatrix.rows():
346
        l2Norm = row.norm(2)
347
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
348
            #print (l2Norm * monomialsCountSqrt).n()
349
            #print l2Norm.n()
350
            ccVectorsCount +=1            
351
            ccReducedPolynomial = \
352
                slz_compute_reduced_polynomial(row,
353
                                               knownMonomials,
354
                                               iVariable,
355
                                               iBound,
356
                                               tVariable,
357
                                               tBound)
358
            if not ccReducedPolynomial is None:
359
                ccReducedPolynomialsList.append(ccReducedPolynomial)
360
        else:
361
            #print l2Norm.n() , ">", nAtAlpha
362
            pass
363
    if debug:
364
        print ccVectorsCount, "out of ", len(ccReducedPolynomialsList), 
365
        print "took Coppersmith text."
366
    if len(ccReducedPolynomialsList) < 2:
367
        print "Less than 2 Coppersmith condition compliant vectors."
368
        return ()
369
    if debug:
370
        print "Reduced and Coppersmith compliant polynomials list", ccReducedPolynomialsList
371
    return ccReducedPolynomialsList
372
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
373

    
374
def slz_compute_initial_lattice_matrix(inputPolynomial,
375
                                       alpha,
376
                                       N,
377
                                       iBound,
378
                                       tBound,
379
                                       debug = False):
380
    """
381
    For a given set of arguments (see below), compute the initial lattice
382
    that could be reduced. 
383
    INPUT:
384
    
385
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
386
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
387
    - "N" -- the modulus;
388
    - "iBound" -- the bound on the first variable;
389
    - "tBound" -- the bound on the second variable.
390
    
391
    OUTPUT:
392
    
393
    A list of bivariate integer polynomial obtained using the Coppersmith
394
    algorithm. The polynomials correspond to the rows of the LLL-reduce
395
    reduced base that comply with the Coppersmith condition.
396
    """
397
    # Arguments check.
398
    if iBound == 0 or tBound == 0:
399
        return None
400
    # End arguments check.
401
    nAtAlpha = N^alpha
402
    ## Building polynomials for matrix.
403
    polyRing   = inputPolynomial.parent()
404
    # Whatever the 2 variables are actually called, we call them
405
    # 'i' and 't' in all the variable names.
406
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
407
    #print polyVars[0], type(polyVars[0])
408
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
409
                                              tVariable:tVariable * tBound})
410
    polynomialsList = \
411
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
412
                                             alpha,
413
                                             N,
414
                                             iBound,
415
                                             tBound,
416
                                             0)
417
    #print "Polynomials list:", polynomialsList
418
    ## Building the proto matrix.
419
    knownMonomials = []
420
    protoMatrix    = []
421
    for poly in polynomialsList:
422
        spo_add_polynomial_coeffs_to_matrix_row(poly,
423
                                                knownMonomials,
424
                                                protoMatrix,
425
                                                0)
426
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
427
    if debug:
428
        print "Initial basis polynomials"
429
        for poly in polynomialsList:
430
            print poly
431
    return matrixToReduce
432
# End slz_compute_initial_lattice_matrix.
433

    
434
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
435
                                                 alpha,
436
                                                 N,
437
                                                 iBound,
438
                                                 tBound):
439
    """
440
    For a given set of arguments (see below), compute the polynomial modular 
441
    roots, if any.
442
    
443
    """
444
    # Arguments check.
445
    if iBound == 0 or tBound == 0:
446
        return set()
447
    # End arguments check.
448
    nAtAlpha = N^alpha
449
    ## Building polynomials for matrix.
450
    polyRing   = inputPolynomial.parent()
451
    # Whatever the 2 variables are actually called, we call them
452
    # 'i' and 't' in all the variable names.
453
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
454
    ccReducedPolynomialsList = \
455
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
456
                                                     alpha,
457
                                                     N,
458
                                                     iBound,
459
                                                     tBound)
460
    if len(ccReducedPolynomialsList) == 0:
461
        return set()   
462
    ## Create the valid (poly1 and poly2 are algebraically independent) 
463
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
464
    # Try to mix and match all the polynomial pairs built from the 
465
    # ccReducedPolynomialsList to obtain non zero resultants.
466
    resultantsInITuplesList = []
467
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
468
        for polyInnerIndex in xrange(polyOuterIndex+1, 
469
                                     len(ccReducedPolynomialsList)):
470
            # Compute the resultant in resultants in the
471
            # first variable (is it the optimal choice?).
472
            resultantInI = \
473
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
474
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
475
            #print "Resultant", resultantInI
476
            # Test algebraic independence.
477
            if not resultantInI.is_zero():
478
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
479
                                                 ccReducedPolynomialsList[polyInnerIndex],
480
                                                 resultantInI))
481
    # If no non zero resultant was found: we can't get no algebraically 
482
    # independent polynomials pair. Give up!
483
    if len(resultantsInITuplesList) == 0:
484
        return set()
485
    #print resultantsInITuplesList
486
    # Compute the roots.
487
    Zi = ZZ[str(iVariable)]
488
    Zt = ZZ[str(tVariable)]
489
    polynomialRootsSet = set()
490
    # First, solve in the second variable since resultants are in the first
491
    # variable.
492
    for resultantInITuple in resultantsInITuplesList:
493
        tRootsList = Zt(resultantInITuple[2]).roots()
494
        # For each tRoot, compute the corresponding iRoots and check 
495
        # them in the input polynomial.
496
        for tRoot in tRootsList:
497
            #print "tRoot:", tRoot
498
            # Roots returned by root() are (value, multiplicity) tuples.
499
            iRootsList = \
500
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
501
            print iRootsList
502
            # The iRootsList can be empty, hence the test.
503
            if len(iRootsList) != 0:
504
                for iRoot in iRootsList:
505
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
506
                    # polyEvalModN must be an integer.
507
                    if polyEvalModN == int(polyEvalModN):
508
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
509
    return polynomialRootsSet
510
# End slz_compute_integer_polynomial_modular_roots.
511
#
512
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
513
                                                 alpha,
514
                                                 N,
515
                                                 iBound,
516
                                                 tBound):
517
    """
518
    For a given set of arguments (see below), compute the polynomial modular 
519
    roots, if any.
520
    This version differs in the way resultants are computed.   
521
    """
522
    # Arguments check.
523
    if iBound == 0 or tBound == 0:
524
        return set()
525
    # End arguments check.
526
    nAtAlpha = N^alpha
527
    ## Building polynomials for matrix.
528
    polyRing   = inputPolynomial.parent()
529
    # Whatever the 2 variables are actually called, we call them
530
    # 'i' and 't' in all the variable names.
531
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
532
    #print polyVars[0], type(polyVars[0])
533
    ccReducedPolynomialsList = \
534
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
535
                                                     alpha,
536
                                                     N,
537
                                                     iBound,
538
                                                     tBound)
539
    if len(ccReducedPolynomialsList) == 0:
540
        return set()   
541
    ## Create the valid (poly1 and poly2 are algebraically independent) 
542
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
543
    # Try to mix and match all the polynomial pairs built from the 
544
    # ccReducedPolynomialsList to obtain non zero resultants.
545
    resultantsInTTuplesList = []
546
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
547
        for polyInnerIndex in xrange(polyOuterIndex+1, 
548
                                     len(ccReducedPolynomialsList)):
549
            # Compute the resultant in resultants in the
550
            # first variable (is it the optimal choice?).
551
            resultantInT = \
552
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
553
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
554
            #print "Resultant", resultantInT
555
            # Test algebraic independence.
556
            if not resultantInT.is_zero():
557
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
558
                                                 ccReducedPolynomialsList[polyInnerIndex],
559
                                                 resultantInT))
560
    # If no non zero resultant was found: we can't get no algebraically 
561
    # independent polynomials pair. Give up!
562
    if len(resultantsInTTuplesList) == 0:
563
        return set()
564
    #print resultantsInITuplesList
565
    # Compute the roots.
566
    Zi = ZZ[str(iVariable)]
567
    Zt = ZZ[str(tVariable)]
568
    polynomialRootsSet = set()
569
    # First, solve in the second variable since resultants are in the first
570
    # variable.
571
    for resultantInTTuple in resultantsInTTuplesList:
572
        iRootsList = Zi(resultantInTTuple[2]).roots()
573
        # For each iRoot, compute the corresponding tRoots and check 
574
        # them in the input polynomial.
575
        for iRoot in iRootsList:
576
            #print "iRoot:", iRoot
577
            # Roots returned by root() are (value, multiplicity) tuples.
578
            tRootsList = \
579
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
580
            print tRootsList
581
            # The tRootsList can be empty, hence the test.
582
            if len(tRootsList) != 0:
583
                for tRoot in tRootsList:
584
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
585
                    # polyEvalModN must be an integer.
586
                    if polyEvalModN == int(polyEvalModN):
587
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
588
    return polynomialRootsSet
589
# End slz_compute_integer_polynomial_modular_roots_2.
590
#
591
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
592
                                        upperBoundSa, approxAccurSa, 
593
                                        precSa=None):
594
    """
595
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
596
    a polynomial that approximates the function on a an interval starting
597
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
598
    approximates with the expected precision.
599
    The interval upper bound is lowered until the expected approximation 
600
    precision is reached.
601
    The polynomial, the bounds, the center of the interval and the error
602
    are returned.
603
    OUTPUT:
604
    A tuple made of 4 Sollya objects:
605
    - a polynomial;
606
    - an range (an interval, not in the sense of number given as an interval);
607
    - the center of the interval;
608
    - the maximum error in the approximation of the input functionSo by the
609
      output polynomial ; this error <= approxAccurSaS.
610
    
611
    """
612
    #print"In slz_compute_polynomial_and_interval..."
613
    ## Superficial argument check.
614
    if lowerBoundSa > upperBoundSa:
615
        return None
616
    ## Change Sollya precision, if requested.
617
    if precSa is None:
618
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
619
        #print "Computed internal precision:", precSa
620
        if precSa < 192:
621
            precSa = 192
622
    sollyaPrecChanged = False
623
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
624
    if precSa > initialSollyaPrecSa:
625
        if precSa <= 2:
626
            print inspect.stack()[0][3], ": precision change <=2 requested."
627
        pobyso_set_prec_sa_so(precSa)
628
        sollyaPrecChanged = True
629
    RRR = lowerBoundSa.parent()
630
    intervalShrinkConstFactorSa = RRR('0.9')
631
    absoluteErrorTypeSo = pobyso_absolute_so_so()
632
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
633
    currentUpperBoundSa = upperBoundSa
634
    currentLowerBoundSa = lowerBoundSa
635
    # What we want here is the polynomial without the variable change, 
636
    # since our actual variable will be x-intervalCenter defined over the 
637
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
638
    (polySo, intervalCenterSo, maxErrorSo) = \
639
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
640
                                                    currentRangeSo, 
641
                                                    absoluteErrorTypeSo)
642
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
643
    while maxErrorSa > approxAccurSa:
644
        print "++Approximation error:", maxErrorSa.n()
645
        sollya_lib_clear_obj(polySo)
646
        sollya_lib_clear_obj(intervalCenterSo)
647
        sollya_lib_clear_obj(maxErrorSo)
648
        # Very empirical shrinking factor.
649
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
650
        print "Shrink factor:", \
651
              shrinkFactorSa.n(), \
652
              intervalShrinkConstFactorSa
653
        print
654
        #errorRatioSa = approxAccurSa/maxErrorSa
655
        #print "Error ratio: ", errorRatioSa
656
        # Make sure interval shrinks.
657
        if shrinkFactorSa > intervalShrinkConstFactorSa:
658
            actualShrinkFactorSa = intervalShrinkConstFactorSa
659
            #print "Fixed"
660
        else:
661
            actualShrinkFactorSa = shrinkFactorSa
662
            #print "Computed",shrinkFactorSa,maxErrorSa
663
            #print shrinkFactorSa, maxErrorSa
664
        #print "Shrink factor", actualShrinkFactorSa
665
        currentUpperBoundSa = currentLowerBoundSa + \
666
                                (currentUpperBoundSa - currentLowerBoundSa) * \
667
                                actualShrinkFactorSa
668
        #print "Current upper bound:", currentUpperBoundSa
669
        sollya_lib_clear_obj(currentRangeSo)
670
        # Check what is left with the bounds.
671
        if currentUpperBoundSa <= currentLowerBoundSa or \
672
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
673
            sollya_lib_clear_obj(absoluteErrorTypeSo)
674
            print "Can't find an interval."
675
            print "Use either or both a higher polynomial degree or a higher",
676
            print "internal precision."
677
            print "Aborting!"
678
            if sollyaPrecChanged:
679
                pobyso_set_prec_so_so(initialSollyaPrecSo)
680
            sollya_lib_clear_obj(initialSollyaPrecSo)
681
            return None
682
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
683
                                                      currentUpperBoundSa)
684
        # print "New interval:",
685
        # pobyso_autoprint(currentRangeSo)
686
        #print "Second Taylor expansion call."
687
        (polySo, intervalCenterSo, maxErrorSo) = \
688
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
689
                                                        currentRangeSo, 
690
                                                        absoluteErrorTypeSo)
691
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
692
        #print "Max errorSo:",
693
        #pobyso_autoprint(maxErrorSo)
694
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
695
        #print "Max errorSa:", maxErrorSa
696
        #print "Sollya prec:",
697
        #pobyso_autoprint(sollya_lib_get_prec(None))
698
    # End while
699
    sollya_lib_clear_obj(absoluteErrorTypeSo)
700
    if sollyaPrecChanged:
701
        pobyso_set_prec_so_so(initialSollyaPrecSo)
702
    sollya_lib_clear_obj(initialSollyaPrecSo)
703
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
704
# End slz_compute_polynomial_and_interval
705

    
706
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
707
                                        upperBoundSa, approxAccurSa, 
708
                                        sollyaPrecSa=None, debug=False):
709
    """
710
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
711
    a polynomial that approximates the function on a an interval starting
712
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
713
    approximates with the expected precision.
714
    The interval upper bound is lowered until the expected approximation 
715
    precision is reached.
716
    The polynomial, the bounds, the center of the interval and the error
717
    are returned.
718
    OUTPUT:
719
    A tuple made of 4 Sollya objects:
720
    - a polynomial;
721
    - an range (an interval, not in the sense of number given as an interval);
722
    - the center of the interval;
723
    - the maximum error in the approximation of the input functionSo by the
724
      output polynomial ; this error <= approxAccurSaS.
725
    
726
    """
727
    #print"In slz_compute_polynomial_and_interval..."
728
    ## Superficial argument check.
729
    if lowerBoundSa > upperBoundSa:
730
        print inspect.stack()[0][3], ": lower bound is larger than upper bound. "
731
        return None
732
    ## Change Sollya precision, if requested.
733
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
734
    sollyaPrecChangedSa = False
735
    if sollyaPrecSa is None:
736
        sollyaPrecSa = initialSollyaPrecSa
737
    else:
738
        if sollyaPrecSa > initialSollyaPrecSa:
739
            if sollyaPrecSa <= 2:
740
                print inspect.stack()[0][3], ": precision change <= 2 requested."
741
            pobyso_set_prec_sa_so(sollyaPrecSa)
742
            sollyaPrecChangedSa = True
743
    ## Other initializations and data recovery.
744
    RRR = lowerBoundSa.parent()
745
    intervalShrinkConstFactorSa = RRR('0.9')
746
    absoluteErrorTypeSo = pobyso_absolute_so_so()
747
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
748
    currentUpperBoundSa = upperBoundSa
749
    currentLowerBoundSa = lowerBoundSa
750
    # What we want here is the polynomial without the variable change, 
751
    # since our actual variable will be x-intervalCenter defined over the 
752
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
753
    (polySo, intervalCenterSo, maxErrorSo) = \
754
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
755
                                                    currentRangeSo, 
756
                                                    absoluteErrorTypeSo)
757
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
758
    while maxErrorSa > approxAccurSa:
759
        print "++Approximation error:", maxErrorSa.n()
760
        sollya_lib_clear_obj(polySo)
761
        sollya_lib_clear_obj(intervalCenterSo)
762
        sollya_lib_clear_obj(maxErrorSo)
763
        # Very empirical shrinking factor.
764
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
765
        print "Shrink factor:", \
766
              shrinkFactorSa.n(), \
767
              intervalShrinkConstFactorSa
768
        print
769
        #errorRatioSa = approxAccurSa/maxErrorSa
770
        #print "Error ratio: ", errorRatioSa
771
        # Make sure interval shrinks.
772
        if shrinkFactorSa > intervalShrinkConstFactorSa:
773
            actualShrinkFactorSa = intervalShrinkConstFactorSa
774
            #print "Fixed"
775
        else:
776
            actualShrinkFactorSa = shrinkFactorSa
777
            #print "Computed",shrinkFactorSa,maxErrorSa
778
            #print shrinkFactorSa, maxErrorSa
779
        #print "Shrink factor", actualShrinkFactorSa
780
        currentUpperBoundSa = currentLowerBoundSa + \
781
                                (currentUpperBoundSa - currentLowerBoundSa) * \
782
                                actualShrinkFactorSa
783
        #print "Current upper bound:", currentUpperBoundSa
784
        sollya_lib_clear_obj(currentRangeSo)
785
        # Check what is left with the bounds.
786
        if currentUpperBoundSa <= currentLowerBoundSa or \
787
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
788
            sollya_lib_clear_obj(absoluteErrorTypeSo)
789
            print "Can't find an interval."
790
            print "Use either or both a higher polynomial degree or a higher",
791
            print "internal precision."
792
            print "Aborting!"
793
            if sollyaPrecChangedSa:
794
                pobyso_set_prec_so_so(initialSollyaPrecSo)
795
            sollya_lib_clear_obj(initialSollyaPrecSo)
796
            return None
797
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
798
                                                      currentUpperBoundSa)
799
        # print "New interval:",
800
        # pobyso_autoprint(currentRangeSo)
801
        #print "Second Taylor expansion call."
802
        (polySo, intervalCenterSo, maxErrorSo) = \
803
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
804
                                                        currentRangeSo, 
805
                                                        absoluteErrorTypeSo)
806
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
807
        #print "Max errorSo:",
808
        #pobyso_autoprint(maxErrorSo)
809
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
810
        #print "Max errorSa:", maxErrorSa
811
        #print "Sollya prec:",
812
        #pobyso_autoprint(sollya_lib_get_prec(None))
813
    # End while
814
    sollya_lib_clear_obj(absoluteErrorTypeSo)
815
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
816
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
817
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
818
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
819
    if debug:
820
        print inspect.stack()[0][3], "SollyaPrecSa:", sollyaPrecSa
821
        print "About to call polynomial rounding with:"
822
        print "polySo: ",           ; pobyso_autoprint(polySo)
823
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
824
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
825
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
826
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
827
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
828
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
829
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
830
    """
831
    # Naive rounding.
832
    (roundedPolySo, roundedPolyMaxErrSo) = \
833
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
834
                                                           functionSo,
835
                                                           intervalCenterSo,
836
                                                           currentRangeSo,
837
                                                           itpSo,
838
                                                           ftpSo,
839
                                                           maxPrecSo,
840
                                                           approxAccurSo)
841
    """
842
    # Proved rounding.
843
    (roundedPolySo, roundedPolyMaxErrSo) = \
844
        pobyso_round_coefficients_progressive_so_so(polySo, 
845
                                                    functionSo,
846
                                                    maxPrecSo,
847
                                                    currentRangeSo,
848
                                                    intervalCenterSo,
849
                                                    maxErrorSo,
850
                                                    approxAccurSo,
851
                                                    debug=False)
852
    #### Comment out the two next lines when polynomial rounding is activated.
853
    #roundedPolySo = sollya_lib_copy_obj(polySo)
854
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
855
    sollya_lib_clear_obj(polySo)
856
    sollya_lib_clear_obj(maxErrorSo)
857
    sollya_lib_clear_obj(itpSo)
858
    sollya_lib_clear_obj(ftpSo)
859
    sollya_lib_clear_obj(approxAccurSo)
860
    if sollyaPrecChangedSa:
861
        pobyso_set_prec_so_so(initialSollyaPrecSo)
862
    sollya_lib_clear_obj(initialSollyaPrecSo)
863
    if debug:
864
        print "1: ", ; pobyso_autoprint(roundedPolySo)
865
        print "2: ", ; pobyso_autoprint(currentRangeSo)
866
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
867
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
868
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
869
# End slz_compute_polynomial_and_interval_01
870

    
871
def slz_compute_polynomial_and_interval_02(functionSo, degreeSo, lowerBoundSa, 
872
                                        upperBoundSa, approxAccurSa, 
873
                                        sollyaPrecSa=None, debug=True ):
874
    """
875
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
876
    a polynomial that approximates the function on a an interval starting
877
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
878
    approximates with the expected precision.
879
    The interval upper bound is lowered until the expected approximation 
880
    precision is reached.
881
    The polynomial, the bounds, the center of the interval and the error
882
    are returned.
883
    OUTPUT:
884
    A tuple made of 4 Sollya objects:
885
    - a polynomial;
886
    - an range (an interval, not in the sense of number given as an interval);
887
    - the center of the interval;
888
    - the maximum error in the approximation of the input functionSo by the
889
      output polynomial ; this error <= approxAccurSaS.
890
    Changes fom v 01:
891
        extra verbose.
892
    """
893
    print"In slz_compute_polynomial_and_interval..."
894
    ## Superficial argument check.
895
    if lowerBoundSa > upperBoundSa:
896
        return None
897
    ## Change Sollya precision, if requested.
898
    sollyaPrecChanged = False
899
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
900
    #print "Initial Sollya prec:", initialSollyaPrecSa, type(initialSollyaPrecSa)
901
    if sollyaPrecSa is None:
902
        sollyaPrecSa = initialSollyaPrecSa
903
    else:
904
        if sollyaPrecSa <= 2:
905
            print inspect.stack()[0][3], ": precision change <=2 requested."
906
        pobyso_set_prec_sa_so(sollyaPrecSa)
907
        sollyaPrecChanged = True
908
    RRR = lowerBoundSa.parent()
909
    intervalShrinkConstFactorSa = RRR('0.9')
910
    absoluteErrorTypeSo = pobyso_absolute_so_so()
911
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
912
    currentUpperBoundSa = upperBoundSa
913
    currentLowerBoundSa = lowerBoundSa
914
    #pobyso_autoprint(functionSo)
915
    #pobyso_autoprint(degreeSo)
916
    #pobyso_autoprint(currentRangeSo)
917
    #pobyso_autoprint(absoluteErrorTypeSo)
918
    ## What we want here is the polynomial without the variable change, 
919
    #  since our actual variable will be x-intervalCenter defined over the 
920
    #  domain [lowerBound-intervalCenter , upperBound-intervalCenter].
921
    (polySo, intervalCenterSo, maxErrorSo) = \
922
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
923
                                                    currentRangeSo, 
924
                                                    absoluteErrorTypeSo)
925
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
926
    print "...after Taylor expansion."
927
    while maxErrorSa > approxAccurSa:
928
        print "++Approximation error:", maxErrorSa.n()
929
        sollya_lib_clear_obj(polySo)
930
        sollya_lib_clear_obj(intervalCenterSo)
931
        sollya_lib_clear_obj(maxErrorSo)
932
        # Very empirical shrinking factor.
933
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
934
        print "Shrink factor:", \
935
              shrinkFactorSa.n(), \
936
              intervalShrinkConstFactorSa
937
        print
938
        #errorRatioSa = approxAccurSa/maxErrorSa
939
        #print "Error ratio: ", errorRatioSa
940
        # Make sure interval shrinks.
941
        if shrinkFactorSa > intervalShrinkConstFactorSa:
942
            actualShrinkFactorSa = intervalShrinkConstFactorSa
943
            #print "Fixed"
944
        else:
945
            actualShrinkFactorSa = shrinkFactorSa
946
            #print "Computed",shrinkFactorSa,maxErrorSa
947
            #print shrinkFactorSa, maxErrorSa
948
        #print "Shrink factor", actualShrinkFactorSa
949
        currentUpperBoundSa = currentLowerBoundSa + \
950
                                (currentUpperBoundSa - currentLowerBoundSa) * \
951
                                actualShrinkFactorSa
952
        #print "Current upper bound:", currentUpperBoundSa
953
        sollya_lib_clear_obj(currentRangeSo)
954
        # Check what is left with the bounds.
955
        if currentUpperBoundSa <= currentLowerBoundSa or \
956
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
957
            sollya_lib_clear_obj(absoluteErrorTypeSo)
958
            print "Can't find an interval."
959
            print "Use either or both a higher polynomial degree or a higher",
960
            print "internal precision."
961
            print "Aborting!"
962
            if sollyaPrecChanged:
963
                pobyso_set_prec_so_so(initialSollyaPrecSo)
964
            sollya_lib_clear_obj(initialSollyaPrecSo)
965
            return None
966
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
967
                                                      currentUpperBoundSa)
968
        # print "New interval:",
969
        # pobyso_autoprint(currentRangeSo)
970
        #print "Second Taylor expansion call."
971
        (polySo, intervalCenterSo, maxErrorSo) = \
972
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
973
                                                        currentRangeSo, 
974
                                                        absoluteErrorTypeSo)
975
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
976
        #print "Max errorSo:",
977
        #pobyso_autoprint(maxErrorSo)
978
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
979
        #print "Max errorSa:", maxErrorSa
980
        #print "Sollya prec:",
981
        #pobyso_autoprint(sollya_lib_get_prec(None))
982
    # End while
983
    sollya_lib_clear_obj(absoluteErrorTypeSo)
984
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
985
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
986
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
987
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
988
    print "About to call polynomial rounding with:"
989
    print "polySo: ",           ; pobyso_autoprint(polySo)
990
    print "functionSo: ",       ; pobyso_autoprint(functionSo)
991
    print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
992
    print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
993
    print "itpSo: ",            ; pobyso_autoprint(itpSo)
994
    print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
995
    print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
996
    print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
997
    (roundedPolySo, roundedPolyMaxErrSo) = \
998
    pobyso_round_coefficients_progressive_so_so(polySo,
999
                                                functionSo,
1000
                                                maxPrecSo,
1001
                                                currentRangeSo,
1002
                                                intervalCenterSo,
1003
                                                maxErrorSo,
1004
                                                approxAccurSo,
1005
                                                debug = True)
1006
    
1007
    sollya_lib_clear_obj(polySo)
1008
    sollya_lib_clear_obj(maxErrorSo)
1009
    sollya_lib_clear_obj(itpSo)
1010
    sollya_lib_clear_obj(ftpSo)
1011
    sollya_lib_clear_obj(approxAccurSo)
1012
    if sollyaPrecChanged:
1013
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1014
    sollya_lib_clear_obj(initialSollyaPrecSo)
1015
    print "1: ", ; pobyso_autoprint(roundedPolySo)
1016
    print "2: ", ; pobyso_autoprint(currentRangeSo)
1017
    print "3: ", ; pobyso_autoprint(intervalCenterSo)
1018
    print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1019
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1020
# End slz_compute_polynomial_and_interval_02
1021

    
1022
def slz_compute_reduced_polynomial(matrixRow,
1023
                                    knownMonomials,
1024
                                    var1,
1025
                                    var1Bound,
1026
                                    var2,
1027
                                    var2Bound):
1028
    """
1029
    Compute a polynomial from a single reduced matrix row.
1030
    This function was introduced in order to avoid the computation of the
1031
    all the polynomials  from the full matrix (even those built from rows 
1032
    that do no verify the Coppersmith condition) as this may involves 
1033
    expensive operations over (large) integers.
1034
    """
1035
    ## Check arguments.
1036
    if len(knownMonomials) == 0:
1037
        return None
1038
    # varNounds can be zero since 0^0 returns 1.
1039
    if (var1Bound < 0) or (var2Bound < 0):
1040
        return None
1041
    ## Initialisations. 
1042
    polynomialRing = knownMonomials[0].parent() 
1043
    currentPolynomial = polynomialRing(0)
1044
    # TODO: use zip instead of indices.
1045
    for colIndex in xrange(0, len(knownMonomials)):
1046
        currentCoefficient = matrixRow[colIndex]
1047
        if currentCoefficient != 0:
1048
            #print "Current coefficient:", currentCoefficient
1049
            currentMonomial = knownMonomials[colIndex]
1050
            #print "Monomial as multivariate polynomial:", \
1051
            #currentMonomial, type(currentMonomial)
1052
            degreeInVar1 = currentMonomial.degree(var1)
1053
            #print "Degree in var1", var1, ":", degreeInVar1
1054
            degreeInVar2 = currentMonomial.degree(var2)
1055
            #print "Degree in var2", var2, ":", degreeInVar2
1056
            if degreeInVar1 > 0:
1057
                currentCoefficient = \
1058
                    currentCoefficient / (var1Bound^degreeInVar1)
1059
                #print "varBound1 in degree:", var1Bound^degreeInVar1
1060
                #print "Current coefficient(1)", currentCoefficient
1061
            if degreeInVar2 > 0:
1062
                currentCoefficient = \
1063
                    currentCoefficient / (var2Bound^degreeInVar2)
1064
                #print "Current coefficient(2)", currentCoefficient
1065
            #print "Current reduced monomial:", (currentCoefficient * \
1066
            #                                    currentMonomial)
1067
            currentPolynomial += (currentCoefficient * currentMonomial)
1068
            #print "Current polynomial:", currentPolynomial
1069
        # End if
1070
    # End for colIndex.
1071
    #print "Type of the current polynomial:", type(currentPolynomial)
1072
    return(currentPolynomial)
1073
# End slz_compute_reduced_polynomial
1074
#
1075
def slz_compute_reduced_polynomials(reducedMatrix,
1076
                                        knownMonomials,
1077
                                        var1,
1078
                                        var1Bound,
1079
                                        var2,
1080
                                        var2Bound):
1081
    """
1082
    Legacy function, use slz_compute_reduced_polynomials_list
1083
    """
1084
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1085
                                                knownMonomials,
1086
                                                var1,
1087
                                                var1Bound,
1088
                                                var2,
1089
                                                var2Bound)
1090
    )
1091
#
1092
def slz_compute_reduced_polynomials_list(reducedMatrix,
1093
                                         knownMonomials,
1094
                                         var1,
1095
                                         var1Bound,
1096
                                         var2,
1097
                                         var2Bound):
1098
    """
1099
    From a reduced matrix, holding the coefficients, from a monomials list,
1100
    from the bounds of each variable, compute the corresponding polynomials
1101
    scaled back by dividing by the "right" powers of the variables bounds.
1102
    
1103
    The elements in knownMonomials must be of the "right" polynomial type.
1104
    They set the polynomial type of the output polynomials in list.
1105
    @param reducedMatrix:  the reduced matrix as output from LLL;
1106
    @param kwnonMonomials: the ordered list of the monomials used to
1107
                           build the polynomials;
1108
    @param var1:           the first variable (of the "right" type);
1109
    @param var1Bound:      the first variable bound;
1110
    @param var2:           the second variable (of the "right" type);
1111
    @param var2Bound:      the second variable bound.
1112
    @return: a list of polynomials obtained with the reduced coefficients
1113
             and scaled down with the bounds
1114
    """
1115
    
1116
    # TODO: check input arguments.
1117
    reducedPolynomials = []
1118
    #print "type var1:", type(var1), " - type var2:", type(var2)
1119
    for matrixRow in reducedMatrix.rows():
1120
        currentPolynomial = 0
1121
        for colIndex in xrange(0, len(knownMonomials)):
1122
            currentCoefficient = matrixRow[colIndex]
1123
            if currentCoefficient != 0:
1124
                #print "Current coefficient:", currentCoefficient
1125
                currentMonomial = knownMonomials[colIndex]
1126
                parentRing = currentMonomial.parent()
1127
                #print "Monomial as multivariate polynomial:", \
1128
                #currentMonomial, type(currentMonomial)
1129
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1130
                #print "Degree in var", var1, ":", degreeInVar1
1131
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1132
                #print "Degree in var", var2, ":", degreeInVar2
1133
                if degreeInVar1 > 0:
1134
                    currentCoefficient /= var1Bound^degreeInVar1
1135
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1136
                    #print "Current coefficient(1)", currentCoefficient
1137
                if degreeInVar2 > 0:
1138
                    currentCoefficient /= var2Bound^degreeInVar2
1139
                    #print "Current coefficient(2)", currentCoefficient
1140
                #print "Current reduced monomial:", (currentCoefficient * \
1141
                #                                    currentMonomial)
1142
                currentPolynomial += (currentCoefficient * currentMonomial)
1143
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1144
                    #print "!!!! constant term !!!!"
1145
                #print "Current polynomial:", currentPolynomial
1146
            # End if
1147
        # End for colIndex.
1148
        #print "Type of the current polynomial:", type(currentPolynomial)
1149
        reducedPolynomials.append(currentPolynomial)
1150
    return reducedPolynomials 
1151
# End slz_compute_reduced_polynomials_list.
1152

    
1153
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1154
                                                   knownMonomials,
1155
                                                   var1,
1156
                                                   var1Bound,
1157
                                                   var2,
1158
                                                   var2Bound):
1159
    """
1160
    From a list of rows, holding the coefficients, from a monomials list,
1161
    from the bounds of each variable, compute the corresponding polynomials
1162
    scaled back by dividing by the "right" powers of the variables bounds.
1163
    
1164
    The elements in knownMonomials must be of the "right" polynomial type.
1165
    They set the polynomial type of the output polynomials in list.
1166
    @param rowsList:       the reduced matrix as output from LLL;
1167
    @param kwnonMonomials: the ordered list of the monomials used to
1168
                           build the polynomials;
1169
    @param var1:           the first variable (of the "right" type);
1170
    @param var1Bound:      the first variable bound;
1171
    @param var2:           the second variable (of the "right" type);
1172
    @param var2Bound:      the second variable bound.
1173
    @return: a list of polynomials obtained with the reduced coefficients
1174
             and scaled down with the bounds
1175
    """
1176
    
1177
    # TODO: check input arguments.
1178
    reducedPolynomials = []
1179
    #print "type var1:", type(var1), " - type var2:", type(var2)
1180
    for matrixRow in rowsList:
1181
        currentPolynomial = 0
1182
        for colIndex in xrange(0, len(knownMonomials)):
1183
            currentCoefficient = matrixRow[colIndex]
1184
            if currentCoefficient != 0:
1185
                #print "Current coefficient:", currentCoefficient
1186
                currentMonomial = knownMonomials[colIndex]
1187
                parentRing = currentMonomial.parent()
1188
                #print "Monomial as multivariate polynomial:", \
1189
                #currentMonomial, type(currentMonomial)
1190
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1191
                #print "Degree in var", var1, ":", degreeInVar1
1192
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1193
                #print "Degree in var", var2, ":", degreeInVar2
1194
                if degreeInVar1 > 0:
1195
                    currentCoefficient /= var1Bound^degreeInVar1
1196
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1197
                    #print "Current coefficient(1)", currentCoefficient
1198
                if degreeInVar2 > 0:
1199
                    currentCoefficient /= var2Bound^degreeInVar2
1200
                    #print "Current coefficient(2)", currentCoefficient
1201
                #print "Current reduced monomial:", (currentCoefficient * \
1202
                #                                    currentMonomial)
1203
                currentPolynomial += (currentCoefficient * currentMonomial)
1204
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1205
                    #print "!!!! constant term !!!!"
1206
                #print "Current polynomial:", currentPolynomial
1207
            # End if
1208
        # End for colIndex.
1209
        #print "Type of the current polynomial:", type(currentPolynomial)
1210
        reducedPolynomials.append(currentPolynomial)
1211
    return reducedPolynomials 
1212
# End slz_compute_reduced_polynomials_list_from_rows.
1213
#
1214
def slz_compute_scaled_function(functionSa,
1215
                                lowerBoundSa,
1216
                                upperBoundSa,
1217
                                floatingPointPrecSa,
1218
                                debug=False):
1219
    """
1220
    From a function, compute the scaled function whose domain
1221
    is included in [1, 2) and whose image is also included in [1,2).
1222
    Return a tuple: 
1223
    [0]: the scaled function
1224
    [1]: the scaled domain lower bound
1225
    [2]: the scaled domain upper bound
1226
    [3]: the scaled image lower bound
1227
    [4]: the scaled image upper bound
1228
    """
1229
    ## The variable can be called anything.
1230
    x = functionSa.variables()[0]
1231
    # Scalling the domain -> [1,2[.
1232
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1233
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1234
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1235
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1236
    if debug:
1237
        print "domainScalingExpression for argument :", \
1238
        invDomainScalingExpressionSa
1239
        print "function: ", functionSa
1240
    ff = functionSa.subs({x : domainScalingExpressionSa})
1241
    ## Bless expression as a function.
1242
    ff = ff.function(x)
1243
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1244
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1245
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1246
    scaledLowerBoundSa = \
1247
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1248
    scaledUpperBoundSa = \
1249
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1250
    if debug:
1251
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1252
              scaledUpperBoundSa
1253
    #
1254
    # Scalling the image -> [1,2[.
1255
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
1256
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
1257
    if flbSa <= fubSa: # Increasing
1258
        imageBinadeBottomSa = floor(flbSa.log2())
1259
    else: # Decreasing
1260
        imageBinadeBottomSa = floor(fubSa.log2())
1261
    if debug:
1262
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
1263
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
1264
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
1265
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
1266
    if debug:
1267
        print "imageScalingExpression for argument :", \
1268
              invImageScalingExpressionSa
1269
    iis = invImageScalingExpressionSa.function(x)
1270
    fff = iis.subs({x:ff})
1271
    if debug:
1272
        print "fff:", fff,
1273
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1274
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1275
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1276
# End slz_compute_scaled_function
1277

    
1278
def slz_fix_bounds_for_binades(lowerBound, 
1279
                               upperBound, 
1280
                               func = None, 
1281
                               domainDirection = -1,
1282
                               imageDirection  = -1):
1283
    """
1284
    Assuming the function is increasing or decreasing over the 
1285
    [lowerBound, upperBound] interval, return a lower bound lb and 
1286
    an upper bound ub such that:
1287
    - lb and ub belong to the same binade;
1288
    - func(lb) and func(ub) belong to the same binade.
1289
    domainDirection indicate how bounds move to fit into the same binade:
1290
    - a negative value move the upper bound down;
1291
    - a positive value move the lower bound up.
1292
    imageDirection indicate how bounds move in order to have their image
1293
    fit into the same binade, variation of the function is also condidered.
1294
    For an increasing function:
1295
    - negative value moves the upper bound down (and its image value as well);
1296
    - a positive values moves the lower bound up (and its image value as well);
1297
    For a decreasing function it is the other way round.
1298
    """
1299
    ## Arguments check
1300
    if lowerBound > upperBound:
1301
        return None
1302
    if lowerBound == upperBound:
1303
        return (lowerBound, upperBound)
1304
    if func is None:
1305
        return None
1306
    #
1307
    #varFunc = func.variables()[0]
1308
    lb = lowerBound
1309
    ub = upperBound
1310
    lbBinade = slz_compute_binade(lb) 
1311
    ubBinade = slz_compute_binade(ub)
1312
    ## Domain binade.
1313
    while lbBinade != ubBinade:
1314
        newIntervalWidth = (ub - lb) / 2
1315
        if domainDirection < 0:
1316
            ub = lb + newIntervalWidth
1317
            ubBinade = slz_compute_binade(ub)
1318
        else:
1319
            lb = lb + newIntervalWidth
1320
            lbBinade = slz_compute_binade(lb) 
1321
    ## Image binade.
1322
    if lb == ub:
1323
        return (lb, ub)
1324
    lbImg = func(lb)
1325
    ubImg = func(ub)
1326
    funcIsInc = (ubImg >= lbImg)
1327
    lbImgBinade = slz_compute_binade(lbImg)
1328
    ubImgBinade = slz_compute_binade(ubImg)
1329
    while lbImgBinade != ubImgBinade:
1330
        newIntervalWidth = (ub - lb) / 2
1331
        if imageDirection < 0:
1332
            if funcIsInc:
1333
                ub = lb + newIntervalWidth
1334
                ubImgBinade = slz_compute_binade(func(ub))
1335
                #print ubImgBinade
1336
            else:
1337
                lb = lb + newIntervalWidth
1338
                lbImgBinade = slz_compute_binade(func(lb))
1339
                #print lbImgBinade
1340
        else:
1341
            if funcIsInc:
1342
                lb = lb + newIntervalWidth
1343
                lbImgBinade = slz_compute_binade(func(lb))
1344
                #print lbImgBinade
1345
            else:
1346
                ub = lb + newIntervalWidth
1347
                ubImgBinade = slz_compute_binade(func(ub))
1348
                #print ubImgBinade
1349
    # End while lbImgBinade != ubImgBinade:
1350
    return (lb, ub)
1351
# End slz_fix_bounds_for_binades.
1352

    
1353
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1354
    # Create a polynomial over the rationals.
1355
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1356
    return(ratPolynomialRing(polyOfFloat))
1357
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1358

    
1359
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1360
    """
1361
     Create a polynomial over the rationals where all denominators are
1362
     powers of two.
1363
    """
1364
    polyVariable = polyOfFloat.variables()[0] 
1365
    RPR = QQ[str(polyVariable)]
1366
    polyCoeffs      = polyOfFloat.coefficients()
1367
    #print polyCoeffs
1368
    polyExponents   = polyOfFloat.exponents()
1369
    #print polyExponents
1370
    polyDenomPtwoCoeffs = []
1371
    for coeff in polyCoeffs:
1372
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1373
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1374
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1375
    ratPoly = RPR(0)
1376
    #print type(ratPoly)
1377
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1378
    #  The coefficient becomes plainly wrong when exponent == 0.
1379
    #  No clue as to why.
1380
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1381
        ratPoly += coeff * RPR(polyVariable^exponent)
1382
    return ratPoly
1383
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1384

    
1385
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1386
                                      lowerBoundSa, 
1387
                                      upperBoundSa, floatingPointPrecSa, 
1388
                                      internalSollyaPrecSa, approxPrecSa):
1389
    """
1390
    Under the assumption that:
1391
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1392
    - lowerBound and upperBound belong to the same binade.
1393
    from a:
1394
    - function;
1395
    - a degree
1396
    - a pair of bounds;
1397
    - the floating-point precision we work on;
1398
    - the internal Sollya precision;
1399
    - the requested approximation error
1400
    The initial interval is, possibly, splitted into smaller intervals.
1401
    It return a list of tuples, each made of:
1402
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1403
    - a second polynomial (with a changed variable f(x) = q(x))
1404
    - the approximation interval;
1405
    - the center, x0, of the interval;
1406
    - the corresponding approximation error.
1407
    TODO: fix endless looping for some parameters sets.
1408
    """
1409
    resultArray = []
1410
    # Set Sollya to the necessary internal precision.
1411
    sollyaPrecChangedSa = False
1412
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1413
    if internalSollyaPrecSa > currentSollyaPrecSa:
1414
        if internalSollyaPrecSa <= 2:
1415
            print inspect.stack()[0][3], ": precision change <=2 requested."
1416
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1417
        sollyaPrecChangedSa = True
1418
    #
1419
    x = functionSa.variables()[0] # Actual variable name can be anything.
1420
    # Scaled function: [1=,2] -> [1,2].
1421
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1422
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1423
                slz_compute_scaled_function(functionSa,                       \
1424
                                            lowerBoundSa,                     \
1425
                                            upperBoundSa,                     \
1426
                                            floatingPointPrecSa)
1427
    # In case bounds were in the negative real one may need to
1428
    # switch scaled bounds.
1429
    if scaledLowerBoundSa > scaledUpperBoundSa:
1430
        scaledLowerBoundSa, scaledUpperBoundSa = \
1431
            scaledUpperBoundSa, scaledLowerBoundSa
1432
        #print "Switching!"
1433
    print "Approximation accuracy: ", RR(approxAccurSa)
1434
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1435
    functionSo = \
1436
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1437
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1438
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1439
                                                  scaledUpperBoundSa)
1440

    
1441
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1442
                                              upperBoundSa.parent().precision()))
1443
    currentScaledLowerBoundSa = scaledLowerBoundSa
1444
    currentScaledUpperBoundSa = scaledUpperBoundSa
1445
    while True:
1446
        ## Compute the first Taylor expansion.
1447
        print "Computing a Taylor expansion..."
1448
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1449
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1450
                                        currentScaledLowerBoundSa, 
1451
                                        currentScaledUpperBoundSa,
1452
                                        approxAccurSa, internalSollyaPrecSa)
1453
        print "...done."
1454
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1455
        #  This value goes to the first variable: polySo. Other variables are
1456
        #  not assigned and should not be tested.
1457
        if polySo is None:
1458
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1459
            if precChangedSa:
1460
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1461
            sollya_lib_clear_obj(initialSollyaPrecSo)
1462
            sollya_lib_clear_obj(functionSo)
1463
            sollya_lib_clear_obj(degreeSo)
1464
            sollya_lib_clear_obj(scaledBoundsSo)
1465
            return None
1466
        ## Add to the result array.
1467
        ### Change variable stuff in Sollya x -> x0-x.
1468
        changeVarExpressionSo = \
1469
            sollya_lib_build_function_sub( \
1470
                              sollya_lib_build_function_free_variable(), 
1471
                              sollya_lib_copy_obj(intervalCenterSo))
1472
        polyVarChangedSo = \
1473
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1474
        #### Get rid of the variable change Sollya stuff. 
1475
        sollya_lib_clear_obj(changeVarExpressionSo)
1476
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1477
                            intervalCenterSo, maxErrorSo))
1478
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1479
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1480
        print "Computed approximation error:", errorSa.n(digits=10)
1481
        # If the error and interval are OK a the first try, just return.
1482
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1483
            (errorSa <= approxAccurSa):
1484
            if precChangedSa:
1485
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1486
            sollya_lib_clear_obj(initialSollyaPrecSo)
1487
            sollya_lib_clear_obj(functionSo)
1488
            sollya_lib_clear_obj(degreeSo)
1489
            sollya_lib_clear_obj(scaledBoundsSo)
1490
            #print "Approximation error:", errorSa
1491
            return resultArray
1492
        ## The returned interval upper bound does not reach the requested upper
1493
        #  upper bound: compute the next upper bound.
1494
        ## The following ratio is always >= 1. If errorSa, we may want to
1495
        #  enlarge the interval
1496
        currentErrorRatio = approxAccurSa / errorSa
1497
        ## --|--------------------------------------------------------------|--
1498
        #  --|--------------------|--------------------------------------------
1499
        #  --|----------------------------|------------------------------------
1500
        ## Starting point for the next upper bound.
1501
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1502
        # Compute the increment. 
1503
        newBoundsWidthSa = \
1504
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1505
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1506
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1507
        # Take into account the original interval upper bound.                     
1508
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1509
            currentScaledUpperBoundSa = scaledUpperBoundSa
1510
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1511
            print "Can't shrink the interval anymore!"
1512
            print "You should consider increasing the Sollya internal precision"
1513
            print "or the polynomial degree."
1514
            print "Giving up!"
1515
            if precChangedSa:
1516
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1517
            sollya_lib_clear_obj(initialSollyaPrecSo)
1518
            sollya_lib_clear_obj(functionSo)
1519
            sollya_lib_clear_obj(degreeSo)
1520
            sollya_lib_clear_obj(scaledBoundsSo)
1521
            return None
1522
        # Compute the other expansions.
1523
        # Test for insufficient precision.
1524
# End slz_get_intervals_and_polynomials
1525

    
1526
def slz_interval_scaling_expression(boundsInterval, expVar):
1527
    """
1528
    Compute the scaling expression to map an interval that spans at most
1529
    a single binade into [1, 2) and the inverse expression as well.
1530
    If the interval spans more than one binade, result is wrong since
1531
    scaling is only based on the lower bound.
1532
    Not very sure that the transformation makes sense for negative numbers.
1533
    """
1534
    # The "one of the bounds is 0" special case. Here we consider
1535
    # the interval as the subnormals binade.
1536
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1537
        # The upper bound is (or should be) positive.
1538
        if boundsInterval.endpoints()[0] == 0:
1539
            if boundsInterval.endpoints()[1] == 0:
1540
                return None
1541
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1542
            l2     = boundsInterval.endpoints()[1].abs().log2()
1543
            # The upper bound is a power of two
1544
            if binade == l2:
1545
                scalingCoeff    = 2^(-binade)
1546
                invScalingCoeff = 2^(binade)
1547
                scalingOffset   = 1
1548
                return \
1549
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1550
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1551
            else:
1552
                scalingCoeff    = 2^(-binade-1)
1553
                invScalingCoeff = 2^(binade+1)
1554
                scalingOffset   = 1
1555
                return((scalingCoeff * expVar + scalingOffset),\
1556
                    ((expVar - scalingOffset) * invScalingCoeff))
1557
        # The lower bound is (or should be) negative.
1558
        if boundsInterval.endpoints()[1] == 0:
1559
            if boundsInterval.endpoints()[0] == 0:
1560
                return None
1561
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1562
            l2     = boundsInterval.endpoints()[0].abs().log2()
1563
            # The upper bound is a power of two
1564
            if binade == l2:
1565
                scalingCoeff    = -2^(-binade)
1566
                invScalingCoeff = -2^(binade)
1567
                scalingOffset   = 1
1568
                return((scalingCoeff * expVar + scalingOffset),\
1569
                    ((expVar - scalingOffset) * invScalingCoeff))
1570
            else:
1571
                scalingCoeff    = -2^(-binade-1)
1572
                invScalingCoeff = -2^(binade+1)
1573
                scalingOffset   = 1
1574
                return((scalingCoeff * expVar + scalingOffset),\
1575
                   ((expVar - scalingOffset) * invScalingCoeff))
1576
    # General case
1577
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1578
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1579
    # We allow for a single exception in binade spanning is when the
1580
    # "outward bound" is a power of 2 and its binade is that of the
1581
    # "inner bound" + 1.
1582
    if boundsInterval.endpoints()[0] > 0:
1583
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1584
        if lbBinade != ubBinade:
1585
            print "Different binades."
1586
            if ubL2 != ubBinade:
1587
                print "Not a power of 2."
1588
                return None
1589
            elif abs(ubBinade - lbBinade) > 1:
1590
                print "Too large span:", abs(ubBinade - lbBinade)
1591
                return None
1592
    else:
1593
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1594
        if lbBinade != ubBinade:
1595
            print "Different binades."
1596
            if lbL2 != lbBinade:
1597
                print "Not a power of 2."
1598
                return None
1599
            elif abs(ubBinade - lbBinade) > 1:
1600
                print "Too large span:", abs(ubBinade - lbBinade)
1601
                return None
1602
    #print "Lower bound binade:", binade
1603
    if boundsInterval.endpoints()[0] > 0:
1604
        return \
1605
            ((2^(-lbBinade) * expVar).function(expVar),
1606
             (2^(lbBinade) * expVar).function(expVar))
1607
    else:
1608
        return \
1609
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1610
             (-(2^(ubBinade)) * expVar).function(expVar))
1611
"""
1612
    # Code sent to attic. Should be the base for a 
1613
    # "slz_interval_translate_expression" rather than scale.
1614
    # Extra control and special cases code  added in  
1615
    # slz_interval_scaling_expression could (should ?) be added to
1616
    # this new function.
1617
    # The scaling offset is only used for negative numbers.
1618
    # When the absolute value of the lower bound is < 0.
1619
    if abs(boundsInterval.endpoints()[0]) < 1:
1620
        if boundsInterval.endpoints()[0] >= 0:
1621
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1622
            invScalingCoeff = 1/scalingCoeff
1623
            return((scalingCoeff * expVar, 
1624
                    invScalingCoeff * expVar))
1625
        else:
1626
            scalingCoeff = \
1627
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1628
            scalingOffset = -3 * scalingCoeff
1629
            return((scalingCoeff * expVar + scalingOffset,
1630
                    1/scalingCoeff * expVar + 3))
1631
    else: 
1632
        if boundsInterval.endpoints()[0] >= 0:
1633
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1634
            scalingOffset = 0
1635
            return((scalingCoeff * expVar, 
1636
                    1/scalingCoeff * expVar))
1637
        else:
1638
            scalingCoeff = \
1639
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1640
            scalingOffset =  -3 * scalingCoeff
1641
            #scalingOffset = 0
1642
            return((scalingCoeff * expVar + scalingOffset,
1643
                    1/scalingCoeff * expVar + 3))
1644
"""
1645
# End slz_interval_scaling_expression
1646
   
1647
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1648
    """
1649
    Compute the Sage version of the Taylor polynomial and it's
1650
    companion data (interval, center...)
1651
    The input parameter is a five elements tuple:
1652
    - [0]: the polyomial (without variable change), as polynomial over a
1653
           real ring;
1654
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1655
           over a real ring;
1656
    - [2]: the interval (as Sollya range);
1657
    - [3]: the interval center;
1658
    - [4]: the approximation error. 
1659
    
1660
    The function returns a 5 elements tuple: formed with all the 
1661
    input elements converted into their Sollya counterpart.
1662
    """
1663
    #print "Sollya polynomial to convert:", 
1664
    #pobyso_autoprint(polyRangeCenterErrorSo)
1665
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
1666
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
1667
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
1668
    intervalSa = \
1669
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1670
    centerSa = \
1671
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1672
    errorSa = \
1673
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1674
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1675
    # End slz_interval_and_polynomial_to_sage
1676

    
1677
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1678
            targetPlusOnePrecRF = None, quasiExactRF = None):
1679
    """
1680
      Check if an element (argument) of the domain of function (function)
1681
      yields a HTRN case (rounding to next) for the target precision 
1682
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1683
      
1684
      The strategy is: 
1685
      - compute the image at high (quasi-exact) precision;
1686
      - round it to nearest to precision;
1687
      - round it to exactly to (precision+1), the computed number has two
1688
        midpoint neighbors;
1689
      - check the distance between these neighbors and the quasi-exact value;
1690
        - if none of them is closer than the targetAccuracy, return False,
1691
        - else return true.
1692
      - Powers of two are a special case when comparing the midpoint in
1693
        the 0 direction..
1694
    """
1695
    ## Arguments filtering. 
1696
    ## TODO: filter the first argument for consistence.
1697
    if targetRF is None:
1698
        targetRF = argument.parent()
1699
    ## Ditto for the real field holding the midpoints.
1700
    if targetPlusOnePrecRF is None:
1701
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1702
    ## If no quasiExactField is provided, create a high accuracy RealField.
1703
    if quasiExactRF is None:
1704
        quasiExactRF = RealField(1536)
1705
    function                      = function.function(function.variables()[0])
1706
    exactArgument                 = quasiExactRF(argument)
1707
    quasiExactValue               = function(exactArgument)
1708
    roundedValue                  = targetRF(quasiExactValue)
1709
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1710
    # Upper midpoint.
1711
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1712
    # Lower midpoint.
1713
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1714
    binade                        = slz_compute_binade(roundedValue)
1715
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1716
    #print "Argument:", argument
1717
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1718
    #print "Binade:", binade
1719
    #print "binadeCorrectedTargetAccuracy:", \
1720
    #binadeCorrectedTargetAccuracy.n(prec=100)
1721
    #print "binadeCorrectedTargetAccuracy:", \
1722
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1723
    #print "Upper midpoint:", \
1724
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1725
    #print "Rounded value :", \
1726
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1727
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1728
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1729
    #print "Lower midpoint:", \
1730
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1731
    ## Make quasiExactValue = 0 a special case to move it out of the way.
1732
    if quasiExactValue == 0:
1733
        return False
1734
    ## Begining of the general case : check with the midpoint of 
1735
    #  greatest absolute value.
1736
    if quasiExactValue > 0:
1737
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1738
           binadeCorrectedTargetAccuracy:
1739
            #print "Too close to the upper midpoint: ", \
1740
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1741
            #print argument.n().str(base=16, \
1742
            #  no_sci=False)
1743
            #print "Lower midpoint:", \
1744
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1745
            #print "Value         :", \
1746
            # quasiExactValue.n(prec=200).str(base=2)
1747
            #print "Upper midpoint:", \
1748
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1749
            return True
1750
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
1751
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1752
           binadeCorrectedTargetAccuracy:
1753
            #print "Too close to the upper midpoint: ", \
1754
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1755
            #print argument.n().str(base=16, \
1756
            #  no_sci=False)
1757
            #print "Lower midpoint:", \
1758
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1759
            #print "Value         :", \
1760
            #  quasiExactValue.n(prec=200).str(base=2)
1761
            #print "Upper midpoint:", \
1762
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1763
            #print
1764
            return True
1765
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1766
    ## For the midpoint of smaller absolute value, 
1767
    #  split cases with the powers of 2.
1768
    if sno_abs_is_power_of_two(roundedValue):
1769
        if quasiExactValue > 0:        
1770
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1771
               binadeCorrectedTargetAccuracy / 2:
1772
                #print "Lower midpoint:", \
1773
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1774
                #print "Value         :", \
1775
                #  quasiExactValue.n(prec=200).str(base=2)
1776
                #print "Upper midpoint:", \
1777
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1778
                #print
1779
                return True
1780
        else:
1781
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1782
              binadeCorrectedTargetAccuracy / 2:
1783
                #print "Lower midpoint:", \
1784
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1785
                #print "Value         :", 
1786
                #  quasiExactValue.n(prec=200).str(base=2)
1787
                #print "Upper midpoint:", 
1788
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1789
                #print
1790
                return True
1791
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1792
    else: ## Not a power of 2, regular comparison with the midpoint of 
1793
          #  smaller absolute value.
1794
        if quasiExactValue > 0:        
1795
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1796
              binadeCorrectedTargetAccuracy:
1797
                #print "Lower midpoint:", \
1798
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1799
                #print "Value         :", \
1800
                #  quasiExactValue.n(prec=200).str(base=2)
1801
                #print "Upper midpoint:", \
1802
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1803
                #print
1804
                return True
1805
        else: # quasiExactValue <= 0
1806
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1807
              binadeCorrectedTargetAccuracy:
1808
                #print "Lower midpoint:", \
1809
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1810
                #print "Value         :", \
1811
                #  quasiExactValue.n(prec=200).str(base=2)
1812
                #print "Upper midpoint:", \
1813
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1814
                #print
1815
                return True
1816
    #print "distance to the upper midpoint:", \
1817
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1818
    #print "distance to the lower midpoint:", \
1819
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1820
    return False
1821
# End slz_is_htrn
1822

    
1823
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1824
                                                precision,
1825
                                                targetHardnessToRound,
1826
                                                variable1,
1827
                                                variable2):
1828
    """
1829
    Creates a new multivariate polynomial with integer coefficients for use
1830
     with the Coppersmith method.
1831
    A the same time it computes :
1832
    - 2^K (N);
1833
    - 2^k (bound on the second variable)
1834
    - lcm
1835

    
1836
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1837
                         variables.
1838
    :param precision: the precision of the floating-point coefficients.
1839
    :param targetHardnessToRound: the hardness to round we want to check.
1840
    :param variable1: the first variable of the polynomial (an expression).
1841
    :param variable2: the second variable of the polynomial (an expression).
1842
    
1843
    :returns: a 4 elements tuple:
1844
                - the polynomial;
1845
                - the modulus (N);
1846
                - the t bound;
1847
                - the lcm used to compute the integral coefficients and the 
1848
                  module.
1849
    """
1850
    # Create a new integer polynomial ring.
1851
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1852
    # Coefficients are issued in the increasing power order.
1853
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1854
    # Print the reversed list for debugging.
1855
    #print
1856
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1857
    # Build the list of number we compute the lcm of.
1858
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1859
    #print "Coefficient denominators:", coefficientDenominators
1860
    coefficientDenominators.append(2^precision)
1861
    coefficientDenominators.append(2^(targetHardnessToRound))
1862
    leastCommonMultiple = lcm(coefficientDenominators)
1863
    # Compute the expression corresponding to the new polynomial
1864
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1865
    #print coefficientNumerators
1866
    polynomialExpression = 0
1867
    power = 0
1868
    # Iterate over two lists at the same time, stop when the shorter
1869
    # (is this case coefficientsNumerators) is 
1870
    # exhausted. Both lists are ordered in the order of increasing powers
1871
    # of variable1.
1872
    for numerator, denominator in \
1873
                        zip(coefficientNumerators, coefficientDenominators):
1874
        multiplicator = leastCommonMultiple / denominator 
1875
        newCoefficient = numerator * multiplicator
1876
        polynomialExpression += newCoefficient * variable1^power
1877
        power +=1
1878
    polynomialExpression += - variable2
1879
    return (IP(polynomialExpression),
1880
            leastCommonMultiple / 2^precision, # 2^K aka N.
1881
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1882
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1883
            leastCommonMultiple) # If we want to make test computations.
1884
        
1885
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1886

    
1887
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1888
                                          precision):
1889
    """
1890
    Makes a variable substitution into the input polynomial so that the output
1891
    polynomial can take integer arguments.
1892
    All variables of the input polynomial "have precision p". That is to say
1893
    that they are rationals with denominator == 2^(precision - 1): 
1894
    x = y/2^(precision - 1).
1895
    We "incorporate" these denominators into the coefficients with, 
1896
    respectively, the "right" power.
1897
    """
1898
    polynomialField = ratPolyOfRat.parent()
1899
    polynomialVariable = ratPolyOfRat.variables()[0]
1900
    #print "The polynomial field is:", polynomialField
1901
    return \
1902
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1903
                                   polynomialVariable/2^(precision-1)}))
1904
    
1905
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1906

    
1907
def slz_reduce_and_test_base(matrixToReduce,
1908
                             nAtAlpha,
1909
                             monomialsCountSqrt):
1910
    """
1911
    Reduces the basis, tests the Coppersmith condition and returns
1912
    a list of rows that comply with the condition.
1913
    """
1914
    ccComplientRowsList = []
1915
    reducedMatrix = matrixToReduce.LLL(None)
1916
    if not reducedMatrix.is_LLL_reduced():
1917
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1918
    else:
1919
        print "reducedMatrix is actually reduced."
1920
    print "N^alpha:", nAtAlpha.n()
1921
    rowIndex = 0
1922
    for row in reducedMatrix.rows():
1923
        l2Norm = row.norm(2)
1924
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1925
                monomialsCountSqrt.n(), ". Is vector OK?", 
1926
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1927
            ccComplientRowsList.append(row)
1928
            print "True"
1929
        else:
1930
            print "False"
1931
    # End for
1932
    return ccComplientRowsList
1933
# End slz_reduce_and_test_base
1934

    
1935
def slz_resultant(poly1, poly2, elimVar, debug = False):
1936
    """
1937
    Compute the resultant for two polynomials for a given variable
1938
    and return the (poly1, poly2, resultant) if the resultant
1939
    is not 0.
1940
    Return () otherwise.
1941
    """
1942
    polynomialRing0    = poly1.parent()
1943
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1944
    if resultantInElimVar is None:
1945
        if debug:
1946
            print poly1
1947
            print poly2
1948
            print "have GCD = ", poly1.gcd(poly2)
1949
        return None
1950
    if resultantInElimVar.is_zero():
1951
        if debug:
1952
            print poly1
1953
            print poly2
1954
            print "have GCD = ", poly1.gcd(poly2)
1955
        return None
1956
    else:
1957
        if debug:
1958
            print poly1
1959
            print poly2
1960
            print "have resultant in t = ", resultantInElimVar
1961
        return resultantInElimVar
1962
# End slz_resultant.
1963
#
1964
def slz_resultant_tuple(poly1, poly2, elimVar):
1965
    """
1966
    Compute the resultant for two polynomials for a given variable
1967
    and return the (poly1, poly2, resultant) if the resultant
1968
    is not 0.
1969
    Return () otherwise.
1970
    """
1971
    polynomialRing0    = poly1.parent()
1972
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1973
    if resultantInElimVar.is_zero():
1974
        return ()
1975
    else:
1976
        return (poly1, poly2, resultantInElimVar)
1977
# End slz_resultant_tuple.
1978
#
1979
print "\t...sageSLZ loaded"