Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (86,44 ko)

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

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

    
7
Examples:
8

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

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

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

    
339
def slz_compute_initial_lattice_matrix(inputPolynomial,
340
                                       alpha,
341
                                       N,
342
                                       iBound,
343
                                       tBound):
344
    """
345
    For a given set of arguments (see below), compute the initial lattice
346
    that could be reduced. 
347
    INPUT:
348
    
349
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
350
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
351
    - "N" -- the modulus;
352
    - "iBound" -- the bound on the first variable;
353
    - "tBound" -- the bound on the second variable.
354
    
355
    OUTPUT:
356
    
357
    A list of bivariate integer polynomial obtained using the Coppersmith
358
    algorithm. The polynomials correspond to the rows of the LLL-reduce
359
    reduced base that comply with the Coppersmith condition.
360
    """
361
    # Arguments check.
362
    if iBound == 0 or tBound == 0:
363
        return None
364
    # End arguments check.
365
    nAtAlpha = N^alpha
366
    ## Building polynomials for matrix.
367
    polyRing   = inputPolynomial.parent()
368
    # Whatever the 2 variables are actually called, we call them
369
    # 'i' and 't' in all the variable names.
370
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
371
    #print polyVars[0], type(polyVars[0])
372
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
373
                                              tVariable:tVariable * tBound})
374
    polynomialsList = \
375
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
376
                                             alpha,
377
                                             N,
378
                                             iBound,
379
                                             tBound,
380
                                             0)
381
    #print "Polynomials list:", polynomialsList
382
    ## Building the proto matrix.
383
    knownMonomials = []
384
    protoMatrix    = []
385
    for poly in polynomialsList:
386
        spo_add_polynomial_coeffs_to_matrix_row(poly,
387
                                                knownMonomials,
388
                                                protoMatrix,
389
                                                0)
390
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
391
    return matrixToReduce
392
# End slz_compute_initial_lattice_matrix.
393

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

    
666
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
667
                                        upperBoundSa, approxAccurSa, 
668
                                        sollyaPrecSa=None, debug=False):
669
    """
670
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
671
    a polynomial that approximates the function on a an interval starting
672
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
673
    approximates with the expected precision.
674
    The interval upper bound is lowered until the expected approximation 
675
    precision is reached.
676
    The polynomial, the bounds, the center of the interval and the error
677
    are returned.
678
    OUTPUT:
679
    A tuple made of 4 Sollya objects:
680
    - a polynomial;
681
    - an range (an interval, not in the sense of number given as an interval);
682
    - the center of the interval;
683
    - the maximum error in the approximation of the input functionSo by the
684
      output polynomial ; this error <= approxAccurSaS.
685
    
686
    """
687
    #print"In slz_compute_polynomial_and_interval..."
688
    ## Superficial argument check.
689
    if lowerBoundSa > upperBoundSa:
690
        return None
691
    ## Change Sollya precision, if requested.
692
    if not sollyaPrecSa is None:
693
        sollyaPrecSo = pobyso_get_prec_so()
694
        pobyso_set_prec_sa_so(sollyaPrecSa)
695
    else:
696
        sollyaPrecSa = pobyso_get_prec_so_sa()
697
        sollyaPrecSo = None
698
    RRR = lowerBoundSa.parent()
699
    intervalShrinkConstFactorSa = RRR('0.9')
700
    absoluteErrorTypeSo = pobyso_absolute_so_so()
701
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
702
    currentUpperBoundSa = upperBoundSa
703
    currentLowerBoundSa = lowerBoundSa
704
    # What we want here is the polynomial without the variable change, 
705
    # since our actual variable will be x-intervalCenter defined over the 
706
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
707
    (polySo, intervalCenterSo, maxErrorSo) = \
708
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
709
                                                    currentRangeSo, 
710
                                                    absoluteErrorTypeSo)
711
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
712
    while maxErrorSa > approxAccurSa:
713
        print "++Approximation error:", maxErrorSa.n()
714
        sollya_lib_clear_obj(polySo)
715
        sollya_lib_clear_obj(intervalCenterSo)
716
        sollya_lib_clear_obj(maxErrorSo)
717
        # Very empirical shrinking factor.
718
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
719
        print "Shrink factor:", \
720
              shrinkFactorSa.n(), \
721
              intervalShrinkConstFactorSa
722
        print
723
        #errorRatioSa = approxAccurSa/maxErrorSa
724
        #print "Error ratio: ", errorRatioSa
725
        # Make sure interval shrinks.
726
        if shrinkFactorSa > intervalShrinkConstFactorSa:
727
            actualShrinkFactorSa = intervalShrinkConstFactorSa
728
            #print "Fixed"
729
        else:
730
            actualShrinkFactorSa = shrinkFactorSa
731
            #print "Computed",shrinkFactorSa,maxErrorSa
732
            #print shrinkFactorSa, maxErrorSa
733
        #print "Shrink factor", actualShrinkFactorSa
734
        currentUpperBoundSa = currentLowerBoundSa + \
735
                                (currentUpperBoundSa - currentLowerBoundSa) * \
736
                                actualShrinkFactorSa
737
        #print "Current upper bound:", currentUpperBoundSa
738
        sollya_lib_clear_obj(currentRangeSo)
739
        # Check what is left with the bounds.
740
        if currentUpperBoundSa <= currentLowerBoundSa or \
741
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
742
            sollya_lib_clear_obj(absoluteErrorTypeSo)
743
            print "Can't find an interval."
744
            print "Use either or both a higher polynomial degree or a higher",
745
            print "internal precision."
746
            print "Aborting!"
747
            if not sollyaPrecSo is None:
748
                pobyso_set_prec_so_so(sollyaPrecSo)
749
                sollya_lib_clear_obj(sollyaPrecSo)
750
            return None
751
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
752
                                                      currentUpperBoundSa)
753
        # print "New interval:",
754
        # pobyso_autoprint(currentRangeSo)
755
        #print "Second Taylor expansion call."
756
        (polySo, intervalCenterSo, maxErrorSo) = \
757
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
758
                                                        currentRangeSo, 
759
                                                        absoluteErrorTypeSo)
760
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
761
        #print "Max errorSo:",
762
        #pobyso_autoprint(maxErrorSo)
763
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
764
        #print "Max errorSa:", maxErrorSa
765
        #print "Sollya prec:",
766
        #pobyso_autoprint(sollya_lib_get_prec(None))
767
    # End while
768
    sollya_lib_clear_obj(absoluteErrorTypeSo)
769
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
770
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
771
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
772
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
773
    if debug:
774
        print "About to call polynomial rounding with:"
775
        print "polySo: ",           ; pobyso_autoprint(polySo)
776
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
777
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
778
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
779
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
780
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
781
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
782
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
783
    (roundedPolySo, roundedPolyMaxErrSo) = \
784
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
785
                                                           functionSo,
786
                                                           intervalCenterSo,
787
                                                           currentRangeSo,
788
                                                           itpSo,
789
                                                           ftpSo,
790
                                                           maxPrecSo,
791
                                                           approxAccurSo)
792
    
793
    sollya_lib_clear_obj(polySo)
794
    sollya_lib_clear_obj(maxErrorSo)
795
    sollya_lib_clear_obj(itpSo)
796
    sollya_lib_clear_obj(ftpSo)
797
    sollya_lib_clear_obj(approxAccurSo)
798
    if not sollyaPrecSo is None:
799
        pobyso_set_prec_so_so(sollyaPrecSo)
800
        sollya_lib_clear_obj(sollyaPrecSo)
801
    if debug:
802
        print "1: ", ; pobyso_autoprint(roundedPolySo)
803
        print "2: ", ; pobyso_autoprint(currentRangeSo)
804
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
805
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
806
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
807
# End slz_compute_polynomial_and_interval_01
808

    
809
def slz_compute_polynomial_and_interval_02(functionSo, degreeSo, lowerBoundSa, 
810
                                        upperBoundSa, approxAccurSa, 
811
                                        sollyaPrecSa=None):
812
    """
813
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
814
    a polynomial that approximates the function on a an interval starting
815
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
816
    approximates with the expected precision.
817
    The interval upper bound is lowered until the expected approximation 
818
    precision is reached.
819
    The polynomial, the bounds, the center of the interval and the error
820
    are returned.
821
    OUTPUT:
822
    A tuple made of 4 Sollya objects:
823
    - a polynomial;
824
    - an range (an interval, not in the sense of number given as an interval);
825
    - the center of the interval;
826
    - the maximum error in the approximation of the input functionSo by the
827
      output polynomial ; this error <= approxAccurSaS.
828
    
829
    """
830
    print"In slz_compute_polynomial_and_interval..."
831
    ## Superficial argument check.
832
    if lowerBoundSa > upperBoundSa:
833
        return None
834
    ## Change Sollya precision, if requested.
835
    if not sollyaPrecSa is None:
836
        sollyaPrecSo = pobyso_get_prec_so()
837
        pobyso_set_prec_sa_so(sollyaPrecSa)
838
    else:
839
        sollyaPrecSa = pobyso_get_prec_so_sa()
840
        sollyaPrecSo = None
841
    RRR = lowerBoundSa.parent()
842
    intervalShrinkConstFactorSa = RRR('0.9')
843
    absoluteErrorTypeSo = pobyso_absolute_so_so()
844
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
845
    currentUpperBoundSa = upperBoundSa
846
    currentLowerBoundSa = lowerBoundSa
847
    # What we want here is the polynomial without the variable change, 
848
    # since our actual variable will be x-intervalCenter defined over the 
849
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
850
    (polySo, intervalCenterSo, maxErrorSo) = \
851
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
852
                                                    currentRangeSo, 
853
                                                    absoluteErrorTypeSo)
854
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
855
    while maxErrorSa > approxAccurSa:
856
        print "++Approximation error:", maxErrorSa.n()
857
        sollya_lib_clear_obj(polySo)
858
        sollya_lib_clear_obj(intervalCenterSo)
859
        sollya_lib_clear_obj(maxErrorSo)
860
        # Very empirical shrinking factor.
861
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
862
        print "Shrink factor:", \
863
              shrinkFactorSa.n(), \
864
              intervalShrinkConstFactorSa
865
        print
866
        #errorRatioSa = approxAccurSa/maxErrorSa
867
        #print "Error ratio: ", errorRatioSa
868
        # Make sure interval shrinks.
869
        if shrinkFactorSa > intervalShrinkConstFactorSa:
870
            actualShrinkFactorSa = intervalShrinkConstFactorSa
871
            #print "Fixed"
872
        else:
873
            actualShrinkFactorSa = shrinkFactorSa
874
            #print "Computed",shrinkFactorSa,maxErrorSa
875
            #print shrinkFactorSa, maxErrorSa
876
        #print "Shrink factor", actualShrinkFactorSa
877
        currentUpperBoundSa = currentLowerBoundSa + \
878
                                (currentUpperBoundSa - currentLowerBoundSa) * \
879
                                actualShrinkFactorSa
880
        #print "Current upper bound:", currentUpperBoundSa
881
        sollya_lib_clear_obj(currentRangeSo)
882
        # Check what is left with the bounds.
883
        if currentUpperBoundSa <= currentLowerBoundSa or \
884
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
885
            sollya_lib_clear_obj(absoluteErrorTypeSo)
886
            print "Can't find an interval."
887
            print "Use either or both a higher polynomial degree or a higher",
888
            print "internal precision."
889
            print "Aborting!"
890
            if not sollyaPrecSo is None:
891
                pobyso_set_prec_so_so(sollyaPrecSo)
892
                sollya_lib_clear_obj(sollyaPrecSo)
893
            return None
894
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
895
                                                      currentUpperBoundSa)
896
        # print "New interval:",
897
        # pobyso_autoprint(currentRangeSo)
898
        #print "Second Taylor expansion call."
899
        (polySo, intervalCenterSo, maxErrorSo) = \
900
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
901
                                                        currentRangeSo, 
902
                                                        absoluteErrorTypeSo)
903
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
904
        #print "Max errorSo:",
905
        #pobyso_autoprint(maxErrorSo)
906
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
907
        #print "Max errorSa:", maxErrorSa
908
        #print "Sollya prec:",
909
        #pobyso_autoprint(sollya_lib_get_prec(None))
910
    # End while
911
    sollya_lib_clear_obj(absoluteErrorTypeSo)
912
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
913
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
914
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
915
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
916
    print "About to call polynomial rounding with:"
917
    print "polySo: ",           ; pobyso_autoprint(polySo)
918
    print "functionSo: ",       ; pobyso_autoprint(functionSo)
919
    print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
920
    print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
921
    print "itpSo: ",            ; pobyso_autoprint(itpSo)
922
    print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
923
    print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
924
    print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
925
    (roundedPolySo, roundedPolyMaxErrSo) = \
926
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
927
                                                           functionSo,
928
                                                           intervalCenterSo,
929
                                                           currentRangeSo,
930
                                                           itpSo,
931
                                                           ftpSo,
932
                                                           maxPrecSo,
933
                                                           approxAccurSo)
934
    
935
    sollya_lib_clear_obj(polySo)
936
    sollya_lib_clear_obj(maxErrorSo)
937
    sollya_lib_clear_obj(itpSo)
938
    sollya_lib_clear_obj(ftpSo)
939
    sollya_lib_clear_obj(approxAccurSo)
940
    if not sollyaPrecSo is None:
941
        pobyso_set_prec_so_so(sollyaPrecSo)
942
        sollya_lib_clear_obj(sollyaPrecSo)
943
    print "1: ", ; pobyso_autoprint(roundedPolySo)
944
    print "2: ", ; pobyso_autoprint(currentRangeSo)
945
    print "3: ", ; pobyso_autoprint(intervalCenterSo)
946
    print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
947
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
948
# End slz_compute_polynomial_and_interval_02
949

    
950
def slz_compute_reduced_polynomial(matrixRow,
951
                                    knownMonomials,
952
                                    var1,
953
                                    var1Bound,
954
                                    var2,
955
                                    var2Bound):
956
    """
957
    Compute a polynomial from a single reduced matrix row.
958
    This function was introduced in order to avoid the computation of the
959
    all the polynomials  from the full matrix (even those built from rows 
960
    that do no verify the Coppersmith condition) as this may involves 
961
    expensive operations over (large) integers.
962
    """
963
    ## Check arguments.
964
    if len(knownMonomials) == 0:
965
        return None
966
    # varNounds can be zero since 0^0 returns 1.
967
    if (var1Bound < 0) or (var2Bound < 0):
968
        return None
969
    ## Initialisations. 
970
    polynomialRing = knownMonomials[0].parent() 
971
    currentPolynomial = polynomialRing(0)
972
    # TODO: use zip instead of indices.
973
    for colIndex in xrange(0, len(knownMonomials)):
974
        currentCoefficient = matrixRow[colIndex]
975
        if currentCoefficient != 0:
976
            #print "Current coefficient:", currentCoefficient
977
            currentMonomial = knownMonomials[colIndex]
978
            #print "Monomial as multivariate polynomial:", \
979
            #currentMonomial, type(currentMonomial)
980
            degreeInVar1 = currentMonomial.degree(var1)
981
            #print "Degree in var1", var1, ":", degreeInVar1
982
            degreeInVar2 = currentMonomial.degree(var2)
983
            #print "Degree in var2", var2, ":", degreeInVar2
984
            if degreeInVar1 > 0:
985
                currentCoefficient = \
986
                    currentCoefficient / (var1Bound^degreeInVar1)
987
                #print "varBound1 in degree:", var1Bound^degreeInVar1
988
                #print "Current coefficient(1)", currentCoefficient
989
            if degreeInVar2 > 0:
990
                currentCoefficient = \
991
                    currentCoefficient / (var2Bound^degreeInVar2)
992
                #print "Current coefficient(2)", currentCoefficient
993
            #print "Current reduced monomial:", (currentCoefficient * \
994
            #                                    currentMonomial)
995
            currentPolynomial += (currentCoefficient * currentMonomial)
996
            #print "Current polynomial:", currentPolynomial
997
        # End if
998
    # End for colIndex.
999
    #print "Type of the current polynomial:", type(currentPolynomial)
1000
    return(currentPolynomial)
1001
# End slz_compute_reduced_polynomial
1002
#
1003
def slz_compute_reduced_polynomials(reducedMatrix,
1004
                                        knownMonomials,
1005
                                        var1,
1006
                                        var1Bound,
1007
                                        var2,
1008
                                        var2Bound):
1009
    """
1010
    Legacy function, use slz_compute_reduced_polynomials_list
1011
    """
1012
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1013
                                                knownMonomials,
1014
                                                var1,
1015
                                                var1Bound,
1016
                                                var2,
1017
                                                var2Bound)
1018
    )
1019
#
1020
def slz_compute_reduced_polynomials_list(reducedMatrix,
1021
                                         knownMonomials,
1022
                                         var1,
1023
                                         var1Bound,
1024
                                         var2,
1025
                                         var2Bound):
1026
    """
1027
    From a reduced matrix, holding the coefficients, from a monomials list,
1028
    from the bounds of each variable, compute the corresponding polynomials
1029
    scaled back by dividing by the "right" powers of the variables bounds.
1030
    
1031
    The elements in knownMonomials must be of the "right" polynomial type.
1032
    They set the polynomial type of the output polynomials in list.
1033
    @param reducedMatrix:  the reduced matrix as output from LLL;
1034
    @param kwnonMonomials: the ordered list of the monomials used to
1035
                           build the polynomials;
1036
    @param var1:           the first variable (of the "right" type);
1037
    @param var1Bound:      the first variable bound;
1038
    @param var2:           the second variable (of the "right" type);
1039
    @param var2Bound:      the second variable bound.
1040
    @return: a list of polynomials obtained with the reduced coefficients
1041
             and scaled down with the bounds
1042
    """
1043
    
1044
    # TODO: check input arguments.
1045
    reducedPolynomials = []
1046
    #print "type var1:", type(var1), " - type var2:", type(var2)
1047
    for matrixRow in reducedMatrix.rows():
1048
        currentPolynomial = 0
1049
        for colIndex in xrange(0, len(knownMonomials)):
1050
            currentCoefficient = matrixRow[colIndex]
1051
            if currentCoefficient != 0:
1052
                #print "Current coefficient:", currentCoefficient
1053
                currentMonomial = knownMonomials[colIndex]
1054
                parentRing = currentMonomial.parent()
1055
                #print "Monomial as multivariate polynomial:", \
1056
                #currentMonomial, type(currentMonomial)
1057
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1058
                #print "Degree in var", var1, ":", degreeInVar1
1059
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1060
                #print "Degree in var", var2, ":", degreeInVar2
1061
                if degreeInVar1 > 0:
1062
                    currentCoefficient /= var1Bound^degreeInVar1
1063
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1064
                    #print "Current coefficient(1)", currentCoefficient
1065
                if degreeInVar2 > 0:
1066
                    currentCoefficient /= var2Bound^degreeInVar2
1067
                    #print "Current coefficient(2)", currentCoefficient
1068
                #print "Current reduced monomial:", (currentCoefficient * \
1069
                #                                    currentMonomial)
1070
                currentPolynomial += (currentCoefficient * currentMonomial)
1071
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1072
                    #print "!!!! constant term !!!!"
1073
                #print "Current polynomial:", currentPolynomial
1074
            # End if
1075
        # End for colIndex.
1076
        #print "Type of the current polynomial:", type(currentPolynomial)
1077
        reducedPolynomials.append(currentPolynomial)
1078
    return reducedPolynomials 
1079
# End slz_compute_reduced_polynomials_list.
1080

    
1081
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1082
                                                   knownMonomials,
1083
                                                   var1,
1084
                                                   var1Bound,
1085
                                                   var2,
1086
                                                   var2Bound):
1087
    """
1088
    From a list of rows, holding the coefficients, from a monomials list,
1089
    from the bounds of each variable, compute the corresponding polynomials
1090
    scaled back by dividing by the "right" powers of the variables bounds.
1091
    
1092
    The elements in knownMonomials must be of the "right" polynomial type.
1093
    They set the polynomial type of the output polynomials in list.
1094
    @param rowsList:       the reduced matrix as output from LLL;
1095
    @param kwnonMonomials: the ordered list of the monomials used to
1096
                           build the polynomials;
1097
    @param var1:           the first variable (of the "right" type);
1098
    @param var1Bound:      the first variable bound;
1099
    @param var2:           the second variable (of the "right" type);
1100
    @param var2Bound:      the second variable bound.
1101
    @return: a list of polynomials obtained with the reduced coefficients
1102
             and scaled down with the bounds
1103
    """
1104
    
1105
    # TODO: check input arguments.
1106
    reducedPolynomials = []
1107
    #print "type var1:", type(var1), " - type var2:", type(var2)
1108
    for matrixRow in rowsList:
1109
        currentPolynomial = 0
1110
        for colIndex in xrange(0, len(knownMonomials)):
1111
            currentCoefficient = matrixRow[colIndex]
1112
            if currentCoefficient != 0:
1113
                #print "Current coefficient:", currentCoefficient
1114
                currentMonomial = knownMonomials[colIndex]
1115
                parentRing = currentMonomial.parent()
1116
                #print "Monomial as multivariate polynomial:", \
1117
                #currentMonomial, type(currentMonomial)
1118
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1119
                #print "Degree in var", var1, ":", degreeInVar1
1120
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1121
                #print "Degree in var", var2, ":", degreeInVar2
1122
                if degreeInVar1 > 0:
1123
                    currentCoefficient /= var1Bound^degreeInVar1
1124
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1125
                    #print "Current coefficient(1)", currentCoefficient
1126
                if degreeInVar2 > 0:
1127
                    currentCoefficient /= var2Bound^degreeInVar2
1128
                    #print "Current coefficient(2)", currentCoefficient
1129
                #print "Current reduced monomial:", (currentCoefficient * \
1130
                #                                    currentMonomial)
1131
                currentPolynomial += (currentCoefficient * currentMonomial)
1132
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1133
                    #print "!!!! constant term !!!!"
1134
                #print "Current polynomial:", currentPolynomial
1135
            # End if
1136
        # End for colIndex.
1137
        #print "Type of the current polynomial:", type(currentPolynomial)
1138
        reducedPolynomials.append(currentPolynomial)
1139
    return reducedPolynomials 
1140
# End slz_compute_reduced_polynomials_list_from_rows.
1141
#
1142
def slz_compute_scaled_function(functionSa,
1143
                                lowerBoundSa,
1144
                                upperBoundSa,
1145
                                floatingPointPrecSa,
1146
                                debug=False):
1147
    """
1148
    From a function, compute the scaled function whose domain
1149
    is included in [1, 2) and whose image is also included in [1,2).
1150
    Return a tuple: 
1151
    [0]: the scaled function
1152
    [1]: the scaled domain lower bound
1153
    [2]: the scaled domain upper bound
1154
    [3]: the scaled image lower bound
1155
    [4]: the scaled image upper bound
1156
    """
1157
    ## The variable can be called anything.
1158
    x = functionSa.variables()[0]
1159
    # Scalling the domain -> [1,2[.
1160
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1161
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1162
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1163
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1164
    if debug:
1165
        print "domainScalingExpression for argument :", \
1166
        invDomainScalingExpressionSa
1167
        print "function: ", functionSa
1168
    ff = functionSa.subs({x : domainScalingExpressionSa})
1169
    ## Bless expression as a function.
1170
    ff = ff.function(x)
1171
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1172
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1173
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1174
    scaledLowerBoundSa = \
1175
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1176
    scaledUpperBoundSa = \
1177
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1178
    if debug:
1179
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1180
              scaledUpperBoundSa
1181
    #
1182
    # Scalling the image -> [1,2[.
1183
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
1184
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
1185
    if flbSa <= fubSa: # Increasing
1186
        imageBinadeBottomSa = floor(flbSa.log2())
1187
    else: # Decreasing
1188
        imageBinadeBottomSa = floor(fubSa.log2())
1189
    if debug:
1190
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
1191
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
1192
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
1193
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
1194
    if debug:
1195
        print "imageScalingExpression for argument :", \
1196
              invImageScalingExpressionSa
1197
    iis = invImageScalingExpressionSa.function(x)
1198
    fff = iis.subs({x:ff})
1199
    if debug:
1200
        print "fff:", fff,
1201
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1202
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1203
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1204
# End slz_compute_scaled_function
1205

    
1206
def slz_fix_bounds_for_binades(lowerBound, 
1207
                               upperBound, 
1208
                               func = None, 
1209
                               domainDirection = -1,
1210
                               imageDirection  = -1):
1211
    """
1212
    Assuming the function is increasing or decreasing over the 
1213
    [lowerBound, upperBound] interval, return a lower bound lb and 
1214
    an upper bound ub such that:
1215
    - lb and ub belong to the same binade;
1216
    - func(lb) and func(ub) belong to the same binade.
1217
    domainDirection indicate how bounds move to fit into the same binade:
1218
    - a negative value move the upper bound down;
1219
    - a positive value move the lower bound up.
1220
    imageDirection indicate how bounds move in order to have their image
1221
    fit into the same binade, variation of the function is also condidered.
1222
    For an increasing function:
1223
    - negative value moves the upper bound down (and its image value as well);
1224
    - a positive values moves the lower bound up (and its image value as well);
1225
    For a decreasing function it is the other way round.
1226
    """
1227
    ## Arguments check
1228
    if lowerBound > upperBound:
1229
        return None
1230
    if lowerBound == upperBound:
1231
        return (lowerBound, upperBound)
1232
    if func is None:
1233
        return None
1234
    #
1235
    #varFunc = func.variables()[0]
1236
    lb = lowerBound
1237
    ub = upperBound
1238
    lbBinade = slz_compute_binade(lb) 
1239
    ubBinade = slz_compute_binade(ub)
1240
    ## Domain binade.
1241
    while lbBinade != ubBinade:
1242
        newIntervalWidth = (ub - lb) / 2
1243
        if domainDirection < 0:
1244
            ub = lb + newIntervalWidth
1245
            ubBinade = slz_compute_binade(ub)
1246
        else:
1247
            lb = lb + newIntervalWidth
1248
            lbBinade = slz_compute_binade(lb) 
1249
    ## Image binade.
1250
    if lb == ub:
1251
        return (lb, ub)
1252
    lbImg = func(lb)
1253
    ubImg = func(ub)
1254
    funcIsInc = (ubImg >= lbImg)
1255
    lbImgBinade = slz_compute_binade(lbImg)
1256
    ubImgBinade = slz_compute_binade(ubImg)
1257
    while lbImgBinade != ubImgBinade:
1258
        newIntervalWidth = (ub - lb) / 2
1259
        if imageDirection < 0:
1260
            if funcIsInc:
1261
                ub = lb + newIntervalWidth
1262
                ubImgBinade = slz_compute_binade(func(ub))
1263
                #print ubImgBinade
1264
            else:
1265
                lb = lb + newIntervalWidth
1266
                lbImgBinade = slz_compute_binade(func(lb))
1267
                #print lbImgBinade
1268
        else:
1269
            if funcIsInc:
1270
                lb = lb + newIntervalWidth
1271
                lbImgBinade = slz_compute_binade(func(lb))
1272
                #print lbImgBinade
1273
            else:
1274
                ub = lb + newIntervalWidth
1275
                ubImgBinade = slz_compute_binade(func(ub))
1276
                #print ubImgBinade
1277
    # End while lbImgBinade != ubImgBinade:
1278
    return (lb, ub)
1279
# End slz_fix_bounds_for_binades.
1280

    
1281
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1282
    # Create a polynomial over the rationals.
1283
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1284
    return(ratPolynomialRing(polyOfFloat))
1285
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1286

    
1287
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1288
    """
1289
     Create a polynomial over the rationals where all denominators are
1290
     powers of two.
1291
    """
1292
    polyVariable = polyOfFloat.variables()[0] 
1293
    RPR = QQ[str(polyVariable)]
1294
    polyCoeffs      = polyOfFloat.coefficients()
1295
    #print polyCoeffs
1296
    polyExponents   = polyOfFloat.exponents()
1297
    #print polyExponents
1298
    polyDenomPtwoCoeffs = []
1299
    for coeff in polyCoeffs:
1300
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1301
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1302
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1303
    ratPoly = RPR(0)
1304
    #print type(ratPoly)
1305
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1306
    #  The coefficient becomes plainly wrong when exponent == 0.
1307
    #  No clue as to why.
1308
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1309
        ratPoly += coeff * RPR(polyVariable^exponent)
1310
    return ratPoly
1311
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1312

    
1313
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1314
                                      lowerBoundSa, 
1315
                                      upperBoundSa, floatingPointPrecSa, 
1316
                                      internalSollyaPrecSa, approxPrecSa):
1317
    """
1318
    Under the assumption that:
1319
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1320
    - lowerBound and upperBound belong to the same binade.
1321
    from a:
1322
    - function;
1323
    - a degree
1324
    - a pair of bounds;
1325
    - the floating-point precision we work on;
1326
    - the internal Sollya precision;
1327
    - the requested approximation error
1328
    The initial interval is, possibly, splitted into smaller intervals.
1329
    It return a list of tuples, each made of:
1330
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1331
    - a second polynomial (with a changed variable f(x) = q(x))
1332
    - the approximation interval;
1333
    - the center, x0, of the interval;
1334
    - the corresponding approximation error.
1335
    TODO: fix endless looping for some parameters sets.
1336
    """
1337
    resultArray = []
1338
    # Set Sollya to the necessary internal precision.
1339
    precChangedSa = False
1340
    currentSollyaPrecSo = pobyso_get_prec_so()
1341
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
1342
    if internalSollyaPrecSa > currentSollyaPrecSa:
1343
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1344
        precChangedSa = True
1345
    #
1346
    x = functionSa.variables()[0] # Actual variable name can be anything.
1347
    # Scaled function: [1=,2] -> [1,2].
1348
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1349
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1350
                slz_compute_scaled_function(functionSa,                       \
1351
                                            lowerBoundSa,                     \
1352
                                            upperBoundSa,                     \
1353
                                            floatingPointPrecSa)
1354
    # In case bounds were in the negative real one may need to
1355
    # switch scaled bounds.
1356
    if scaledLowerBoundSa > scaledUpperBoundSa:
1357
        scaledLowerBoundSa, scaledUpperBoundSa = \
1358
            scaledUpperBoundSa, scaledLowerBoundSa
1359
        #print "Switching!"
1360
    print "Approximation accuracy: ", RR(approxAccurSa)
1361
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1362
    functionSo = \
1363
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1364
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1365
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1366
                                                  scaledUpperBoundSa)
1367

    
1368
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1369
                                              upperBoundSa.parent().precision()))
1370
    currentScaledLowerBoundSa = scaledLowerBoundSa
1371
    currentScaledUpperBoundSa = scaledUpperBoundSa
1372
    while True:
1373
        ## Compute the first Taylor expansion.
1374
        print "Computing a Taylor expansion..."
1375
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1376
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1377
                                        currentScaledLowerBoundSa, 
1378
                                        currentScaledUpperBoundSa,
1379
                                        approxAccurSa, internalSollyaPrecSa)
1380
        print "...done."
1381
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1382
        #  This value goes to the first variable: polySo. Other variables are
1383
        #  not assigned and should not be tested.
1384
        if polySo is None:
1385
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1386
            if precChangedSa:
1387
                pobyso_set_prec_so_so(currentSollyaPrecSo)
1388
                sollya_lib_clear_obj(currentSollyaPrecSo)
1389
            sollya_lib_clear_obj(functionSo)
1390
            sollya_lib_clear_obj(degreeSo)
1391
            sollya_lib_clear_obj(scaledBoundsSo)
1392
            return None
1393
        ## Add to the result array.
1394
        ### Change variable stuff in Sollya x -> x0-x.
1395
        changeVarExpressionSo = \
1396
            sollya_lib_build_function_sub( \
1397
                              sollya_lib_build_function_free_variable(), 
1398
                              sollya_lib_copy_obj(intervalCenterSo))
1399
        polyVarChangedSo = \
1400
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1401
        #### Get rid of the variable change Sollya stuff. 
1402
        sollya_lib_clear_obj(changeVarExpressionSo)
1403
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1404
                            intervalCenterSo, maxErrorSo))
1405
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1406
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1407
        print "Computed approximation error:", errorSa.n(digits=10)
1408
        # If the error and interval are OK a the first try, just return.
1409
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1410
            (errorSa <= approxAccurSa):
1411
            if precChangedSa:
1412
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1413
            sollya_lib_clear_obj(currentSollyaPrecSo)
1414
            sollya_lib_clear_obj(functionSo)
1415
            sollya_lib_clear_obj(degreeSo)
1416
            sollya_lib_clear_obj(scaledBoundsSo)
1417
            #print "Approximation error:", errorSa
1418
            return resultArray
1419
        ## The returned interval upper bound does not reach the requested upper
1420
        #  upper bound: compute the next upper bound.
1421
        ## The following ratio is always >= 1. If errorSa, we may want to
1422
        #  enlarge the interval
1423
        currentErrorRatio = approxAccurSa / errorSa
1424
        ## --|--------------------------------------------------------------|--
1425
        #  --|--------------------|--------------------------------------------
1426
        #  --|----------------------------|------------------------------------
1427
        ## Starting point for the next upper bound.
1428
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1429
        # Compute the increment. 
1430
        newBoundsWidthSa = \
1431
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1432
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1433
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1434
        # Take into account the original interval upper bound.                     
1435
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1436
            currentScaledUpperBoundSa = scaledUpperBoundSa
1437
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1438
            print "Can't shrink the interval anymore!"
1439
            print "You should consider increasing the Sollya internal precision"
1440
            print "or the polynomial degree."
1441
            print "Giving up!"
1442
            if precChangedSa:
1443
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1444
            sollya_lib_clear_obj(currentSollyaPrecSo)
1445
            sollya_lib_clear_obj(functionSo)
1446
            sollya_lib_clear_obj(degreeSo)
1447
            sollya_lib_clear_obj(scaledBoundsSo)
1448
            return None
1449
        # Compute the other expansions.
1450
        # Test for insufficient precision.
1451
# End slz_get_intervals_and_polynomials
1452

    
1453
def slz_interval_scaling_expression(boundsInterval, expVar):
1454
    """
1455
    Compute the scaling expression to map an interval that spans at most
1456
    a single binade into [1, 2) and the inverse expression as well.
1457
    If the interval spans more than one binade, result is wrong since
1458
    scaling is only based on the lower bound.
1459
    Not very sure that the transformation makes sense for negative numbers.
1460
    """
1461
    # The "one of the bounds is 0" special case. Here we consider
1462
    # the interval as the subnormals binade.
1463
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1464
        # The upper bound is (or should be) positive.
1465
        if boundsInterval.endpoints()[0] == 0:
1466
            if boundsInterval.endpoints()[1] == 0:
1467
                return None
1468
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1469
            l2     = boundsInterval.endpoints()[1].abs().log2()
1470
            # The upper bound is a power of two
1471
            if binade == l2:
1472
                scalingCoeff    = 2^(-binade)
1473
                invScalingCoeff = 2^(binade)
1474
                scalingOffset   = 1
1475
                return \
1476
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1477
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1478
            else:
1479
                scalingCoeff    = 2^(-binade-1)
1480
                invScalingCoeff = 2^(binade+1)
1481
                scalingOffset   = 1
1482
                return((scalingCoeff * expVar + scalingOffset),\
1483
                    ((expVar - scalingOffset) * invScalingCoeff))
1484
        # The lower bound is (or should be) negative.
1485
        if boundsInterval.endpoints()[1] == 0:
1486
            if boundsInterval.endpoints()[0] == 0:
1487
                return None
1488
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1489
            l2     = boundsInterval.endpoints()[0].abs().log2()
1490
            # The upper bound is a power of two
1491
            if binade == l2:
1492
                scalingCoeff    = -2^(-binade)
1493
                invScalingCoeff = -2^(binade)
1494
                scalingOffset   = 1
1495
                return((scalingCoeff * expVar + scalingOffset),\
1496
                    ((expVar - scalingOffset) * invScalingCoeff))
1497
            else:
1498
                scalingCoeff    = -2^(-binade-1)
1499
                invScalingCoeff = -2^(binade+1)
1500
                scalingOffset   = 1
1501
                return((scalingCoeff * expVar + scalingOffset),\
1502
                   ((expVar - scalingOffset) * invScalingCoeff))
1503
    # General case
1504
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1505
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1506
    # We allow for a single exception in binade spanning is when the
1507
    # "outward bound" is a power of 2 and its binade is that of the
1508
    # "inner bound" + 1.
1509
    if boundsInterval.endpoints()[0] > 0:
1510
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1511
        if lbBinade != ubBinade:
1512
            print "Different binades."
1513
            if ubL2 != ubBinade:
1514
                print "Not a power of 2."
1515
                return None
1516
            elif abs(ubBinade - lbBinade) > 1:
1517
                print "Too large span:", abs(ubBinade - lbBinade)
1518
                return None
1519
    else:
1520
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1521
        if lbBinade != ubBinade:
1522
            print "Different binades."
1523
            if lbL2 != lbBinade:
1524
                print "Not a power of 2."
1525
                return None
1526
            elif abs(ubBinade - lbBinade) > 1:
1527
                print "Too large span:", abs(ubBinade - lbBinade)
1528
                return None
1529
    #print "Lower bound binade:", binade
1530
    if boundsInterval.endpoints()[0] > 0:
1531
        return \
1532
            ((2^(-lbBinade) * expVar).function(expVar),
1533
             (2^(lbBinade) * expVar).function(expVar))
1534
    else:
1535
        return \
1536
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1537
             (-(2^(ubBinade)) * expVar).function(expVar))
1538
"""
1539
    # Code sent to attic. Should be the base for a 
1540
    # "slz_interval_translate_expression" rather than scale.
1541
    # Extra control and special cases code  added in  
1542
    # slz_interval_scaling_expression could (should ?) be added to
1543
    # this new function.
1544
    # The scaling offset is only used for negative numbers.
1545
    # When the absolute value of the lower bound is < 0.
1546
    if abs(boundsInterval.endpoints()[0]) < 1:
1547
        if boundsInterval.endpoints()[0] >= 0:
1548
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1549
            invScalingCoeff = 1/scalingCoeff
1550
            return((scalingCoeff * expVar, 
1551
                    invScalingCoeff * expVar))
1552
        else:
1553
            scalingCoeff = \
1554
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1555
            scalingOffset = -3 * scalingCoeff
1556
            return((scalingCoeff * expVar + scalingOffset,
1557
                    1/scalingCoeff * expVar + 3))
1558
    else: 
1559
        if boundsInterval.endpoints()[0] >= 0:
1560
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1561
            scalingOffset = 0
1562
            return((scalingCoeff * expVar, 
1563
                    1/scalingCoeff * expVar))
1564
        else:
1565
            scalingCoeff = \
1566
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1567
            scalingOffset =  -3 * scalingCoeff
1568
            #scalingOffset = 0
1569
            return((scalingCoeff * expVar + scalingOffset,
1570
                    1/scalingCoeff * expVar + 3))
1571
"""
1572
# End slz_interval_scaling_expression
1573
   
1574
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1575
    """
1576
    Compute the Sage version of the Taylor polynomial and it's
1577
    companion data (interval, center...)
1578
    The input parameter is a five elements tuple:
1579
    - [0]: the polyomial (without variable change), as polynomial over a
1580
           real ring;
1581
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1582
           over a real ring;
1583
    - [2]: the interval (as Sollya range);
1584
    - [3]: the interval center;
1585
    - [4]: the approximation error. 
1586
    
1587
    The function returns a 5 elements tuple: formed with all the 
1588
    input elements converted into their Sollya counterpart.
1589
    """
1590
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
1591
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
1592
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
1593
    intervalSa = \
1594
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1595
    centerSa = \
1596
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1597
    errorSa = \
1598
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1599
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1600
    # End slz_interval_and_polynomial_to_sage
1601

    
1602
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1603
            targetPlusOnePrecRF = None, quasiExactRF = None):
1604
    """
1605
      Check if an element (argument) of the domain of function (function)
1606
      yields a HTRN case (rounding to next) for the target precision 
1607
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1608
      
1609
      The strategy is: 
1610
      - compute the image at high (quasi-exact) precision;
1611
      - round it to nearest to precision;
1612
      - round it to exactly to (precision+1), the computed number has two
1613
        midpoint neighbors;
1614
      - check the distance between these neighbors and the quasi-exact value;
1615
        - if none of them is closer than the targetAccuracy, return False,
1616
        - else return true.
1617
      - Powers of two are a special case when comparing the midpoint in
1618
        the 0 direction..
1619
    """
1620
    ## Arguments filtering. 
1621
    ## TODO: filter the first argument for consistence.
1622
    if targetRF is None:
1623
        targetRF = argument.parent()
1624
    ## Ditto for the real field holding the midpoints.
1625
    if targetPlusOnePrecRF is None:
1626
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1627
    ## If no quasiExactField is provided, create a high accuracy RealField.
1628
    if quasiExactRF is None:
1629
        quasiExactRF = RealField(1536)
1630
    function                      = function.function(function.variables()[0])
1631
    exactArgument                 = quasiExactRF(argument)
1632
    quasiExactValue               = function(exactArgument)
1633
    roundedValue                  = targetRF(quasiExactValue)
1634
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1635
    # Upper midpoint.
1636
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1637
    # Lower midpoint.
1638
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1639
    binade                        = slz_compute_binade(roundedValue)
1640
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1641
    #print "Argument:", argument
1642
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1643
    #print "Binade:", binade
1644
    #print "binadeCorrectedTargetAccuracy:", \
1645
    #binadeCorrectedTargetAccuracy.n(prec=100)
1646
    #print "binadeCorrectedTargetAccuracy:", \
1647
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1648
    #print "Upper midpoint:", \
1649
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1650
    #print "Rounded value :", \
1651
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1652
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1653
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1654
    #print "Lower midpoint:", \
1655
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1656
    ## Make quasiExactValue = 0 a special case to move it out of the way.
1657
    if quasiExactValue == 0:
1658
        return False
1659
    ## Begining of the general case : check with the midpoint of 
1660
    #  greatest absolute value.
1661
    if quasiExactValue > 0:
1662
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1663
           binadeCorrectedTargetAccuracy:
1664
            #print "Too close to the upper midpoint: ", \
1665
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1666
            #print argument.n().str(base=16, \
1667
            #  no_sci=False)
1668
            #print "Lower midpoint:", \
1669
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1670
            #print "Value         :", \
1671
            # quasiExactValue.n(prec=200).str(base=2)
1672
            #print "Upper midpoint:", \
1673
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1674
            return True
1675
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
1676
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1677
           binadeCorrectedTargetAccuracy:
1678
            #print "Too close to the upper midpoint: ", \
1679
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1680
            #print argument.n().str(base=16, \
1681
            #  no_sci=False)
1682
            #print "Lower midpoint:", \
1683
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1684
            #print "Value         :", \
1685
            #  quasiExactValue.n(prec=200).str(base=2)
1686
            #print "Upper midpoint:", \
1687
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1688
            #print
1689
            return True
1690
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1691
    ## For the midpoint of smaller absolute value, 
1692
    #  split cases with the powers of 2.
1693
    if sno_abs_is_power_of_two(roundedValue):
1694
        if quasiExactValue > 0:        
1695
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1696
               binadeCorrectedTargetAccuracy / 2:
1697
                #print "Lower midpoint:", \
1698
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1699
                #print "Value         :", \
1700
                #  quasiExactValue.n(prec=200).str(base=2)
1701
                #print "Upper midpoint:", \
1702
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1703
                #print
1704
                return True
1705
        else:
1706
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1707
              binadeCorrectedTargetAccuracy / 2:
1708
                #print "Lower midpoint:", \
1709
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1710
                #print "Value         :", 
1711
                #  quasiExactValue.n(prec=200).str(base=2)
1712
                #print "Upper midpoint:", 
1713
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1714
                #print
1715
                return True
1716
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1717
    else: ## Not a power of 2, regular comparison with the midpoint of 
1718
          #  smaller absolute value.
1719
        if quasiExactValue > 0:        
1720
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1721
              binadeCorrectedTargetAccuracy:
1722
                #print "Lower midpoint:", \
1723
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1724
                #print "Value         :", \
1725
                #  quasiExactValue.n(prec=200).str(base=2)
1726
                #print "Upper midpoint:", \
1727
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1728
                #print
1729
                return True
1730
        else: # quasiExactValue <= 0
1731
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1732
              binadeCorrectedTargetAccuracy:
1733
                #print "Lower midpoint:", \
1734
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1735
                #print "Value         :", \
1736
                #  quasiExactValue.n(prec=200).str(base=2)
1737
                #print "Upper midpoint:", \
1738
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1739
                #print
1740
                return True
1741
    #print "distance to the upper midpoint:", \
1742
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1743
    #print "distance to the lower midpoint:", \
1744
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1745
    return False
1746
# End slz_is_htrn
1747

    
1748
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1749
                                                precision,
1750
                                                targetHardnessToRound,
1751
                                                variable1,
1752
                                                variable2):
1753
    """
1754
    Creates a new multivariate polynomial with integer coefficients for use
1755
     with the Coppersmith method.
1756
    A the same time it computes :
1757
    - 2^K (N);
1758
    - 2^k (bound on the second variable)
1759
    - lcm
1760

    
1761
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1762
                         variables.
1763
    :param precision: the precision of the floating-point coefficients.
1764
    :param targetHardnessToRound: the hardness to round we want to check.
1765
    :param variable1: the first variable of the polynomial (an expression).
1766
    :param variable2: the second variable of the polynomial (an expression).
1767
    
1768
    :returns: a 4 elements tuple:
1769
                - the polynomial;
1770
                - the modulus (N);
1771
                - the t bound;
1772
                - the lcm used to compute the integral coefficients and the 
1773
                  module.
1774
    """
1775
    # Create a new integer polynomial ring.
1776
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1777
    # Coefficients are issued in the increasing power order.
1778
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1779
    # Print the reversed list for debugging.
1780
    #print
1781
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1782
    # Build the list of number we compute the lcm of.
1783
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1784
    #print "Coefficient denominators:", coefficientDenominators
1785
    coefficientDenominators.append(2^precision)
1786
    coefficientDenominators.append(2^(targetHardnessToRound))
1787
    leastCommonMultiple = lcm(coefficientDenominators)
1788
    # Compute the expression corresponding to the new polynomial
1789
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1790
    #print coefficientNumerators
1791
    polynomialExpression = 0
1792
    power = 0
1793
    # Iterate over two lists at the same time, stop when the shorter
1794
    # (is this case coefficientsNumerators) is 
1795
    # exhausted. Both lists are ordered in the order of increasing powers
1796
    # of variable1.
1797
    for numerator, denominator in \
1798
                        zip(coefficientNumerators, coefficientDenominators):
1799
        multiplicator = leastCommonMultiple / denominator 
1800
        newCoefficient = numerator * multiplicator
1801
        polynomialExpression += newCoefficient * variable1^power
1802
        power +=1
1803
    polynomialExpression += - variable2
1804
    return (IP(polynomialExpression),
1805
            leastCommonMultiple / 2^precision, # 2^K aka N.
1806
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1807
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1808
            leastCommonMultiple) # If we want to make test computations.
1809
        
1810
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1811

    
1812
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1813
                                          precision):
1814
    """
1815
    Makes a variable substitution into the input polynomial so that the output
1816
    polynomial can take integer arguments.
1817
    All variables of the input polynomial "have precision p". That is to say
1818
    that they are rationals with denominator == 2^(precision - 1): 
1819
    x = y/2^(precision - 1).
1820
    We "incorporate" these denominators into the coefficients with, 
1821
    respectively, the "right" power.
1822
    """
1823
    polynomialField = ratPolyOfRat.parent()
1824
    polynomialVariable = ratPolyOfRat.variables()[0]
1825
    #print "The polynomial field is:", polynomialField
1826
    return \
1827
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1828
                                   polynomialVariable/2^(precision-1)}))
1829
    
1830
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1831

    
1832
def slz_reduce_and_test_base(matrixToReduce,
1833
                             nAtAlpha,
1834
                             monomialsCountSqrt):
1835
    """
1836
    Reduces the basis, tests the Coppersmith condition and returns
1837
    a list of rows that comply with the condition.
1838
    """
1839
    ccComplientRowsList = []
1840
    reducedMatrix = matrixToReduce.LLL(None)
1841
    if not reducedMatrix.is_LLL_reduced():
1842
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1843
    else:
1844
        print "reducedMatrix is actually reduced."
1845
    print "N^alpha:", nAtAlpha.n()
1846
    rowIndex = 0
1847
    for row in reducedMatrix.rows():
1848
        l2Norm = row.norm(2)
1849
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1850
                monomialsCountSqrt.n(), ". Is vector OK?", 
1851
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1852
            ccComplientRowsList.append(row)
1853
            print "True"
1854
        else:
1855
            print "False"
1856
    # End for
1857
    return ccComplientRowsList
1858
# End slz_reduce_and_test_base
1859

    
1860
def slz_resultant(poly1, poly2, elimVar):
1861
    """
1862
    Compute the resultant for two polynomials for a given variable
1863
    and return the (poly1, poly2, resultant) if the resultant
1864
    is not 0.
1865
    Return () otherwise.
1866
    """
1867
    polynomialRing0    = poly1.parent()
1868
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1869
    if resultantInElimVar is None:
1870
        return None
1871
    if resultantInElimVar.is_zero():
1872
        return None
1873
    else:
1874
        return resultantInElimVar
1875
# End slz_resultant.
1876
#
1877
def slz_resultant_tuple(poly1, poly2, elimVar):
1878
    """
1879
    Compute the resultant for two polynomials for a given variable
1880
    and return the (poly1, poly2, resultant) if the resultant
1881
    is not 0.
1882
    Return () otherwise.
1883
    """
1884
    polynomialRing0    = poly1.parent()
1885
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1886
    if resultantInElimVar.is_zero():
1887
        return ()
1888
    else:
1889
        return (poly1, poly2, resultantInElimVar)
1890
# End slz_resultant_tuple.
1891
#
1892
print "\t...sageSLZ loaded"