Statistiques
| Révision :

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

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

    
53
#
54
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
55
    """
56
    For given "real number", compute the bounds of the binade it belongs to.
57
    
58
    NOTE::
59
        When number >= 2^(emax+1), we return the "fake" binade 
60
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
61
        with interval [-infinity, -2^(emax+1)]. We want to distinguish
62
        this case from that of "really" invalid arguments.
63
        
64
    """
65
    # Check the parameters.
66
    # RealNumbers or RealNumber offspring only.
67
    # The exception construction is necessary since not all objects have
68
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
69
    try:
70
        classTree = [number.__class__] + number.mro()
71
        if not sage.rings.real_mpfr.RealNumber in classTree:
72
            return None
73
    except AttributeError:
74
        return None
75
    # Non zero negative integers only for emin.
76
    if emin >= 0 or int(emin) != emin:
77
        return None
78
    # Non zero positive integers only for emax.
79
    if emax <= 0 or int(emax) != emax:
80
        return None
81
    precision = number.precision()
82
    RF  = RealField(precision)
83
    if number == 0:
84
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
85
    # A more precise RealField is needed to avoid unwanted rounding effects
86
    # when computing number.log2().
87
    RRF = RealField(max(2048, 2 * precision))
88
    # number = 0 special case, the binade bounds are 
89
    # [0, 2^emin - 2^(emin-precision)]
90
    # Begin general case
91
    l2 = RRF(number).abs().log2()
92
    # Another special one: beyond largest representable -> "Fake" binade.
93
    if l2 >= emax + 1:
94
        if number > 0:
95
            return (RF(2^(emax+1)), RF(+infinity) )
96
        else:
97
            return (RF(-infinity), -RF(2^(emax+1)))
98
    # Regular case cont'd.
99
    offset = int(l2)
100
    # number.abs() >= 1.
101
    if l2 >= 0:
102
        if number >= 0:
103
            lb = RF(2^offset)
104
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
105
        else: #number < 0
106
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
107
            ub = -RF(2^offset)
108
    else: # log2 < 0, number.abs() < 1.
109
        if l2 < emin: # Denormal
110
           # print "Denormal:", l2
111
            if number >= 0:
112
                lb = RF(0)
113
                ub = RF(2^(emin)) - RF(2^(emin-precision))
114
            else: # number <= 0
115
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
116
                ub = RF(0)
117
        elif l2 > emin: # Normal number other than +/-2^emin.
118
            if number >= 0:
119
                if int(l2) == l2:
120
                    lb = RF(2^(offset))
121
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
122
                else:
123
                    lb = RF(2^(offset-1))
124
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
125
            else: # number < 0
126
                if int(l2) == l2: # Binade limit.
127
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
128
                    ub = -RF(2^(offset))
129
                else:
130
                    lb = -RF(2^(offset) - 2^(-precision+offset))
131
                    ub = -RF(2^(offset-1))
132
        else: # l2== emin, number == +/-2^emin
133
            if number >= 0:
134
                lb = RF(2^(offset))
135
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
136
            else: # number < 0
137
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
138
                ub = -RF(2^(offset))
139
    return (lb, ub)
140
# End slz_compute_binade_bounds
141
#
142
def slz_compute_coppersmith_reduced_polynomials(inputPolynomial,
143
                                                 alpha,
144
                                                 N,
145
                                                 iBound,
146
                                                 tBound,
147
                                                 debug = False):
148
    """
149
    For a given set of arguments (see below), compute a list
150
    of "reduced polynomials" that could be used to compute roots
151
    of the inputPolynomial.
152
    INPUT:
153
    
154
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
155
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
156
    - "N" -- the modulus;
157
    - "iBound" -- the bound on the first variable;
158
    - "tBound" -- the bound on the second variable.
159
    
160
    OUTPUT:
161
    
162
    A list of bivariate integer polynomial obtained using the Coppersmith
163
    algorithm. The polynomials correspond to the rows of the LLL-reduce
164
    reduced base that comply with the Coppersmith condition.
165
    """
166
    # Arguments check.
167
    if iBound == 0 or tBound == 0:
168
        return None
169
    # End arguments check.
170
    nAtAlpha = N^alpha
171
    ## Building polynomials for matrix.
172
    polyRing   = inputPolynomial.parent()
173
    # Whatever the 2 variables are actually called, we call them
174
    # 'i' and 't' in all the variable names.
175
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
176
    #print polyVars[0], type(polyVars[0])
177
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
178
                                              tVariable:tVariable * tBound})
179
    if debug:
180
        polynomialsList = \
181
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
182
                                                 alpha,
183
                                                 N,
184
                                                 iBound,
185
                                                 tBound,
186
                                                 20)
187
    else:
188
        polynomialsList = \
189
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
190
                                                 alpha,
191
                                                 N,
192
                                                 iBound,
193
                                                 tBound,
194
                                                 0)
195
    #print "Polynomials list:", polynomialsList
196
    ## Building the proto matrix.
197
    knownMonomials = []
198
    protoMatrix    = []
199
    if debug:
200
        for poly in polynomialsList:
201
            spo_add_polynomial_coeffs_to_matrix_row(poly,
202
                                                    knownMonomials,
203
                                                    protoMatrix,
204
                                                    20)
205
    else:
206
        for poly in polynomialsList:
207
            spo_add_polynomial_coeffs_to_matrix_row(poly,
208
                                                    knownMonomials,
209
                                                    protoMatrix,
210
                                                    0)
211
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
212
    #print matrixToReduce
213
    ## Reduction and checking.
214
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
215
    #  error message issued when previous code was used.
216
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
217
    reducedMatrix = matrixToReduce.LLL(fp=None)
218
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
219
    if not isLLLReduced:
220
        return None
221
    monomialsCount     = len(knownMonomials)
222
    monomialsCountSqrt = sqrt(monomialsCount)
223
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
224
    #print reducedMatrix
225
    ## Check the Coppersmith condition for each row and build the reduced 
226
    #  polynomials.
227
    ccReducedPolynomialsList = []
228
    for row in reducedMatrix.rows():
229
        l2Norm = row.norm(2)
230
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
231
            #print (l2Norm * monomialsCountSqrt).n()
232
            #print l2Norm.n()
233
            ccReducedPolynomial = \
234
                slz_compute_reduced_polynomial(row,
235
                                               knownMonomials,
236
                                               iVariable,
237
                                               iBound,
238
                                               tVariable,
239
                                               tBound)
240
            if not ccReducedPolynomial is None:
241
                ccReducedPolynomialsList.append(ccReducedPolynomial)
242
        else:
243
            #print l2Norm.n() , ">", nAtAlpha
244
            pass
245
    if len(ccReducedPolynomialsList) < 2:
246
        print "Less than 2 Coppersmith condition compliant vectors."
247
        return ()
248
    #print ccReducedPolynomialsList
249
    return ccReducedPolynomialsList
250
# End slz_compute_coppersmith_reduced_polynomials
251
#
252
def slz_compute_coppersmith_reduced_polynomials_gram(inputPolynomial,
253
                                                     alpha,
254
                                                     N,
255
                                                     iBound,
256
                                                     tBound,
257
                                                     debug = False):
258
    """
259
    For a given set of arguments (see below), compute a list
260
    of "reduced polynomials" that could be used to compute roots
261
    of the inputPolynomial.
262
    INPUT:
263
    
264
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
265
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
266
    - "N" -- the modulus;
267
    - "iBound" -- the bound on the first variable;
268
    - "tBound" -- the bound on the second variable.
269
    
270
    OUTPUT:
271
    
272
    A list of bivariate integer polynomial obtained using the Coppersmith
273
    algorithm. The polynomials correspond to the rows of the LLL-reduce
274
    reduced base that comply with the Coppersmith condition.
275
    """
276
    # Arguments check.
277
    if iBound == 0 or tBound == 0:
278
        return None
279
    # End arguments check.
280
    nAtAlpha = N^alpha
281
    ## Building polynomials for matrix.
282
    polyRing   = inputPolynomial.parent()
283
    # Whatever the 2 variables are actually called, we call them
284
    # 'i' and 't' in all the variable names.
285
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
286
    #print polyVars[0], type(polyVars[0])
287
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
288
                                              tVariable:tVariable * tBound})
289
    if debug:
290
        polynomialsList = \
291
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
292
                                                 alpha,
293
                                                 N,
294
                                                 iBound,
295
                                                 tBound,
296
                                                 20)
297
    else:
298
        polynomialsList = \
299
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
300
                                                 alpha,
301
                                                 N,
302
                                                 iBound,
303
                                                 tBound,
304
                                                 0)
305
    #print "Polynomials list:", polynomialsList
306
    ## Building the proto matrix.
307
    knownMonomials = []
308
    protoMatrix    = []
309
    if debug:
310
        for poly in polynomialsList:
311
            spo_add_polynomial_coeffs_to_matrix_row(poly,
312
                                                    knownMonomials,
313
                                                    protoMatrix,
314
                                                    20)
315
    else:
316
        for poly in polynomialsList:
317
            spo_add_polynomial_coeffs_to_matrix_row(poly,
318
                                                    knownMonomials,
319
                                                    protoMatrix,
320
                                                    0)
321
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
322
    #print matrixToReduce
323
    ## Reduction and checking.
324
    ### In this variant we use the Pari LLL_gram reduction function.
325
    #   It works with the Gram matrix instead of the plain matrix.
326
    matrixToReduceTransposed = matrixToReduce.transpose() 
327
    matrixToReduceGram = matrixToReduce * matrixToReduceTransposed
328
    ### LLL_gram returns a unimodular transformation matrix such that:
329
    #   umt.transpose() * matrixToReduce * umt is reduced..
330
    umt = matrixToReduceGram.LLL_gram()
331
    #print "Unimodular transformation matrix:"
332
    #for row in umt.rows():
333
    #    print row
334
    ### The computed transformation matrix is transposed and applied to the
335
    #   left side of matrixToReduce. 
336
    reducedMatrix = umt.transpose() * matrixToReduce
337
    #print "Reduced matrix:"
338
    #for row in reducedMatrix.rows():
339
    #    print row
340
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
341
    #if not isLLLReduced:
342
    #    return None
343
    monomialsCount     = len(knownMonomials)
344
    monomialsCountSqrt = sqrt(monomialsCount)
345
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
346
    #print reducedMatrix
347
    ## Check the Coppersmith condition for each row and build the reduced 
348
    #  polynomials.
349
    ccReducedPolynomialsList = []
350
    for row in reducedMatrix.rows():
351
        l2Norm = row.norm(2)
352
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
353
            #print (l2Norm * monomialsCountSqrt).n()
354
            #print l2Norm.n()
355
            ccReducedPolynomial = \
356
                slz_compute_reduced_polynomial(row,
357
                                               knownMonomials,
358
                                               iVariable,
359
                                               iBound,
360
                                               tVariable,
361
                                               tBound)
362
            if not ccReducedPolynomial is None:
363
                ccReducedPolynomialsList.append(ccReducedPolynomial)
364
        else:
365
            #print l2Norm.n() , ">", nAtAlpha
366
            pass
367
    if len(ccReducedPolynomialsList) < 2:
368
        print "Less than 2 Coppersmith condition compliant vectors."
369
        return ()
370
    #print ccReducedPolynomialsList
371
    return ccReducedPolynomialsList
372
# End slz_compute_coppersmith_reduced_polynomials_gram
373
#
374
def slz_compute_coppersmith_reduced_polynomials_proj(inputPolynomial,
375
                                                     alpha,
376
                                                     N,
377
                                                     iBound,
378
                                                     tBound,
379
                                                     debug = False):
380
    """
381
    For a given set of arguments (see below), compute a list
382
    of "reduced polynomials" that could be used to compute roots
383
    of the inputPolynomial.
384
    INPUT:
385
    
386
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
387
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
388
    - "N" -- the modulus;
389
    - "iBound" -- the bound on the first variable;
390
    - "tBound" -- the bound on the second variable.
391
    
392
    OUTPUT:
393
    
394
    A list of bivariate integer polynomial obtained using the Coppersmith
395
    algorithm. The polynomials correspond to the rows of the LLL-reduce
396
    reduced base that comply with the Coppersmith condition.
397
    """
398
    #@par Changes from runSLZ-113.sage
399
    # LLL reduction is not performed on the matrix itself but rather on the
400
    # product of the matrix with a uniform random matrix.
401
    # The reduced matrix obtained is discarded but the transformation matrix 
402
    # obtained is used to multiply the original matrix in order to reduced it.
403
    # If a sufficient level of reduction is obtained, we stop here. If not
404
    # the product matrix obtained above is LLL reduced. But as it has been
405
    # pre-reduced at the above step, reduction is supposed to be much faster.
406
    #
407
    # Arguments check.
408
    if iBound == 0 or tBound == 0:
409
        return None
410
    # End arguments check.
411
    nAtAlpha = N^alpha
412
    ## Building polynomials for matrix.
413
    polyRing   = inputPolynomial.parent()
414
    # Whatever the 2 variables are actually called, we call them
415
    # 'i' and 't' in all the variable names.
416
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
417
    #print polyVars[0], type(polyVars[0])
418
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
419
                                              tVariable:tVariable * tBound})
420
    if debug:
421
        polynomialsList = \
422
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
423
                                                 alpha,
424
                                                 N,
425
                                                 iBound,
426
                                                 tBound,
427
                                                 20)
428
    else:
429
        polynomialsList = \
430
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
431
                                                 alpha,
432
                                                 N,
433
                                                 iBound,
434
                                                 tBound,
435
                                                 0)
436
    #print "Polynomials list:", polynomialsList
437
    ## Building the proto matrix.
438
    knownMonomials = []
439
    protoMatrix    = []
440
    if debug:
441
        for poly in polynomialsList:
442
            spo_add_polynomial_coeffs_to_matrix_row(poly,
443
                                                    knownMonomials,
444
                                                    protoMatrix,
445
                                                    20)
446
    else:
447
        for poly in polynomialsList:
448
            spo_add_polynomial_coeffs_to_matrix_row(poly,
449
                                                    knownMonomials,
450
                                                    protoMatrix,
451
                                                    0)
452
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
453
    #print matrixToReduce
454
    ## Reduction and checking.
455
    ### Reduction with projection
456
    (reducedMatrixStep1, reductionMatrixStep1) = \
457
         slz_reduce_lll_proj(matrixToReduce,16)
458
    #print "Reduced matrix:"
459
    #print reducedMatrixStep1
460
    #for row in reducedMatrix.rows():
461
    #    print row
462
    monomialsCount     = len(knownMonomials)
463
    monomialsCountSqrt = sqrt(monomialsCount)
464
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
465
    #print reducedMatrix
466
    ## Check the Coppersmith condition for each row and build the reduced 
467
    #  polynomials.
468
    ccReducedPolynomialsList = []
469
    for row in reducedMatrixStep1.rows():
470
        l2Norm = row.norm(2)
471
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
472
            #print (l2Norm * monomialsCountSqrt).n()
473
            #print l2Norm.n()
474
            ccReducedPolynomial = \
475
                slz_compute_reduced_polynomial(row,
476
                                               knownMonomials,
477
                                               iVariable,
478
                                               iBound,
479
                                               tVariable,
480
                                               tBound)
481
            if not ccReducedPolynomial is None:
482
                ccReducedPolynomialsList.append(ccReducedPolynomial)
483
        else:
484
            #print l2Norm.n() , ">", nAtAlpha
485
            pass
486
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
487
        print "Less than 2 Coppersmith condition compliant vectors."
488
        print "Extra reduction starting..."
489
        reducedMatrix = reducedMatrixStep1.LLL(algorithm='fpLLL:wrapper')
490
        ### If uncommented, the following statement avoids performing
491
        #   an actual LLL reduction. This allows for demonstrating
492
        #   the behavior of our pseudo-reduction alone.
493
        #return ()
494
    else:
495
        print "First step of reduction afforded enough vectors"
496
        return ccReducedPolynomialsList
497
    #print ccReducedPolynomialsList
498
    ## Check again the Coppersmith condition for each row and build the reduced 
499
    #  polynomials.
500
    ccReducedPolynomialsList = []
501
    for row in reducedMatrix.rows():
502
        l2Norm = row.norm(2)
503
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
504
            #print (l2Norm * monomialsCountSqrt).n()
505
            #print l2Norm.n()
506
            ccReducedPolynomial = \
507
                slz_compute_reduced_polynomial(row,
508
                                               knownMonomials,
509
                                               iVariable,
510
                                               iBound,
511
                                               tVariable,
512
                                               tBound)
513
            if not ccReducedPolynomial is None:
514
                ccReducedPolynomialsList.append(ccReducedPolynomial)
515
        else:
516
            #print l2Norm.n() , ">", nAtAlpha
517
            pass
518
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
519
        print "Less than 2 Coppersmith condition compliant vectors after extra reduction."
520
        return ()
521
    else:
522
        return ccReducedPolynomialsList
523
# End slz_compute_coppersmith_reduced_polynomials_proj
524
#
525
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
526
                                                                    alpha,
527
                                                                    N,
528
                                                                    iBound,
529
                                                                    tBound,
530
                                                                    debug = False):
531
    """
532
    For a given set of arguments (see below), compute a list
533
    of "reduced polynomials" that could be used to compute roots
534
    of the inputPolynomial.
535
    Print the volume of the initial basis as well.
536
    INPUT:
537
    
538
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
539
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
540
    - "N" -- the modulus;
541
    - "iBound" -- the bound on the first variable;
542
    - "tBound" -- the bound on the second variable.
543
    
544
    OUTPUT:
545
    
546
    A list of bivariate integer polynomial obtained using the Coppersmith
547
    algorithm. The polynomials correspond to the rows of the LLL-reduce
548
    reduced base that comply with the Coppersmith condition.
549
    """
550
    # Arguments check.
551
    if iBound == 0 or tBound == 0:
552
        return None
553
    # End arguments check.
554
    nAtAlpha = N^alpha
555
    if debug:
556
        print "N at alpha:", nAtAlpha
557
    ## Building polynomials for matrix.
558
    polyRing   = inputPolynomial.parent()
559
    # Whatever the 2 variables are actually called, we call them
560
    # 'i' and 't' in all the variable names.
561
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
562
    #print polyVars[0], type(polyVars[0])
563
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
564
                                              tVariable:tVariable * tBound})
565
##    polynomialsList = \
566
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
567
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
568
    polynomialsList = \
569
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
570
                                             alpha,
571
                                             N,
572
                                             iBound,
573
                                             tBound,
574
                                             0)
575
    #print "Polynomials list:", polynomialsList
576
    ## Building the proto matrix.
577
    knownMonomials = []
578
    protoMatrix    = []
579
    for poly in polynomialsList:
580
        spo_add_polynomial_coeffs_to_matrix_row(poly,
581
                                                knownMonomials,
582
                                                protoMatrix,
583
                                                0)
584
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
585
    matrixToReduceTranspose = matrixToReduce.transpose()
586
    squareMatrix = matrixToReduce * matrixToReduceTranspose
587
    squareMatDet = det(squareMatrix)
588
    latticeVolume = sqrt(squareMatDet)
589
    print "Lattice volume:", latticeVolume.n()
590
    print "Lattice volume / N:", (latticeVolume/N).n()
591
    #print matrixToReduce
592
    ## Reduction and checking.
593
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
594
    #  error message issued when previous code was used.
595
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
596
    reductionTimeStart = cputime() 
597
    reducedMatrix = matrixToReduce.LLL(fp=None)
598
    reductionTime = cputime(reductionTimeStart)
599
    print "Reduction time:", reductionTime
600
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
601
    if not isLLLReduced:
602
       return None
603
    #
604
    if debug:
605
        matrixFile = file('/tmp/reducedMatrix.txt', 'w')
606
        for row in reducedMatrix.rows():
607
            matrixFile.write(str(row) + "\n")
608
        matrixFile.close()
609
    #
610
    monomialsCount     = len(knownMonomials)
611
    monomialsCountSqrt = sqrt(monomialsCount)
612
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
613
    #print reducedMatrix
614
    ## Check the Coppersmith condition for each row and build the reduced 
615
    #  polynomials.
616
    ccVectorsCount = 0
617
    ccReducedPolynomialsList = []
618
    for row in reducedMatrix.rows():
619
        l2Norm = row.norm(2)
620
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
621
            #print (l2Norm * monomialsCountSqrt).n()
622
            #print l2Norm.n()
623
            ccVectorsCount +=1            
624
            ccReducedPolynomial = \
625
                slz_compute_reduced_polynomial(row,
626
                                               knownMonomials,
627
                                               iVariable,
628
                                               iBound,
629
                                               tVariable,
630
                                               tBound)
631
            if not ccReducedPolynomial is None:
632
                ccReducedPolynomialsList.append(ccReducedPolynomial)
633
        else:
634
            #print l2Norm.n() , ">", nAtAlpha
635
            pass
636
    if debug:
637
        print ccVectorsCount, "out of ", len(ccReducedPolynomialsList), 
638
        print "took Coppersmith text."
639
    if len(ccReducedPolynomialsList) < 2:
640
        print "Less than 2 Coppersmith condition compliant vectors."
641
        return ()
642
    if debug:
643
        print "Reduced and Coppersmith compliant polynomials list", ccReducedPolynomialsList
644
    return ccReducedPolynomialsList
645
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
646

    
647
def slz_compute_initial_lattice_matrix(inputPolynomial,
648
                                       alpha,
649
                                       N,
650
                                       iBound,
651
                                       tBound,
652
                                       debug = False):
653
    """
654
    For a given set of arguments (see below), compute the initial lattice
655
    that could be reduced. 
656
    INPUT:
657
    
658
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
659
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
660
    - "N" -- the modulus;
661
    - "iBound" -- the bound on the first variable;
662
    - "tBound" -- the bound on the second variable.
663
    
664
    OUTPUT:
665
    
666
    A list of bivariate integer polynomial obtained using the Coppersmith
667
    algorithm. The polynomials correspond to the rows of the LLL-reduce
668
    reduced base that comply with the Coppersmith condition.
669
    """
670
    # Arguments check.
671
    if iBound == 0 or tBound == 0:
672
        return None
673
    # End arguments check.
674
    nAtAlpha = N^alpha
675
    ## Building polynomials for matrix.
676
    polyRing   = inputPolynomial.parent()
677
    # Whatever the 2 variables are actually called, we call them
678
    # 'i' and 't' in all the variable names.
679
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
680
    #print polyVars[0], type(polyVars[0])
681
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
682
                                              tVariable:tVariable * tBound})
683
    polynomialsList = \
684
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
685
                                             alpha,
686
                                             N,
687
                                             iBound,
688
                                             tBound,
689
                                             0)
690
    #print "Polynomials list:", polynomialsList
691
    ## Building the proto matrix.
692
    knownMonomials = []
693
    protoMatrix    = []
694
    for poly in polynomialsList:
695
        spo_add_polynomial_coeffs_to_matrix_row(poly,
696
                                                knownMonomials,
697
                                                protoMatrix,
698
                                                0)
699
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
700
    if debug:
701
        print "Initial basis polynomials"
702
        for poly in polynomialsList:
703
            print poly
704
    return matrixToReduce
705
# End slz_compute_initial_lattice_matrix.
706

    
707
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
708
                                                 alpha,
709
                                                 N,
710
                                                 iBound,
711
                                                 tBound):
712
    """
713
    For a given set of arguments (see below), compute the polynomial modular 
714
    roots, if any.
715
    
716
    """
717
    # Arguments check.
718
    if iBound == 0 or tBound == 0:
719
        return set()
720
    # End arguments check.
721
    nAtAlpha = N^alpha
722
    ## Building polynomials for matrix.
723
    polyRing   = inputPolynomial.parent()
724
    # Whatever the 2 variables are actually called, we call them
725
    # 'i' and 't' in all the variable names.
726
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
727
    ccReducedPolynomialsList = \
728
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
729
                                                     alpha,
730
                                                     N,
731
                                                     iBound,
732
                                                     tBound)
733
    if len(ccReducedPolynomialsList) == 0:
734
        return set()   
735
    ## Create the valid (poly1 and poly2 are algebraically independent) 
736
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
737
    # Try to mix and match all the polynomial pairs built from the 
738
    # ccReducedPolynomialsList to obtain non zero resultants.
739
    resultantsInITuplesList = []
740
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
741
        for polyInnerIndex in xrange(polyOuterIndex+1, 
742
                                     len(ccReducedPolynomialsList)):
743
            # Compute the resultant in resultants in the
744
            # first variable (is it the optimal choice?).
745
            resultantInI = \
746
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
747
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
748
            #print "Resultant", resultantInI
749
            # Test algebraic independence.
750
            if not resultantInI.is_zero():
751
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
752
                                                 ccReducedPolynomialsList[polyInnerIndex],
753
                                                 resultantInI))
754
    # If no non zero resultant was found: we can't get no algebraically 
755
    # independent polynomials pair. Give up!
756
    if len(resultantsInITuplesList) == 0:
757
        return set()
758
    #print resultantsInITuplesList
759
    # Compute the roots.
760
    Zi = ZZ[str(iVariable)]
761
    Zt = ZZ[str(tVariable)]
762
    polynomialRootsSet = set()
763
    # First, solve in the second variable since resultants are in the first
764
    # variable.
765
    for resultantInITuple in resultantsInITuplesList:
766
        tRootsList = Zt(resultantInITuple[2]).roots()
767
        # For each tRoot, compute the corresponding iRoots and check 
768
        # them in the input polynomial.
769
        for tRoot in tRootsList:
770
            #print "tRoot:", tRoot
771
            # Roots returned by root() are (value, multiplicity) tuples.
772
            iRootsList = \
773
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
774
            print iRootsList
775
            # The iRootsList can be empty, hence the test.
776
            if len(iRootsList) != 0:
777
                for iRoot in iRootsList:
778
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
779
                    # polyEvalModN must be an integer.
780
                    if polyEvalModN == int(polyEvalModN):
781
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
782
    return polynomialRootsSet
783
# End slz_compute_integer_polynomial_modular_roots.
784
#
785
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
786
                                                 alpha,
787
                                                 N,
788
                                                 iBound,
789
                                                 tBound):
790
    """
791
    For a given set of arguments (see below), compute the polynomial modular 
792
    roots, if any.
793
    This version differs in the way resultants are computed.   
794
    """
795
    # Arguments check.
796
    if iBound == 0 or tBound == 0:
797
        return set()
798
    # End arguments check.
799
    nAtAlpha = N^alpha
800
    ## Building polynomials for matrix.
801
    polyRing   = inputPolynomial.parent()
802
    # Whatever the 2 variables are actually called, we call them
803
    # 'i' and 't' in all the variable names.
804
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
805
    #print polyVars[0], type(polyVars[0])
806
    ccReducedPolynomialsList = \
807
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
808
                                                     alpha,
809
                                                     N,
810
                                                     iBound,
811
                                                     tBound)
812
    if len(ccReducedPolynomialsList) == 0:
813
        return set()   
814
    ## Create the valid (poly1 and poly2 are algebraically independent) 
815
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
816
    # Try to mix and match all the polynomial pairs built from the 
817
    # ccReducedPolynomialsList to obtain non zero resultants.
818
    resultantsInTTuplesList = []
819
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
820
        for polyInnerIndex in xrange(polyOuterIndex+1, 
821
                                     len(ccReducedPolynomialsList)):
822
            # Compute the resultant in resultants in the
823
            # first variable (is it the optimal choice?).
824
            resultantInT = \
825
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
826
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
827
            #print "Resultant", resultantInT
828
            # Test algebraic independence.
829
            if not resultantInT.is_zero():
830
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
831
                                                 ccReducedPolynomialsList[polyInnerIndex],
832
                                                 resultantInT))
833
    # If no non zero resultant was found: we can't get no algebraically 
834
    # independent polynomials pair. Give up!
835
    if len(resultantsInTTuplesList) == 0:
836
        return set()
837
    #print resultantsInITuplesList
838
    # Compute the roots.
839
    Zi = ZZ[str(iVariable)]
840
    Zt = ZZ[str(tVariable)]
841
    polynomialRootsSet = set()
842
    # First, solve in the second variable since resultants are in the first
843
    # variable.
844
    for resultantInTTuple in resultantsInTTuplesList:
845
        iRootsList = Zi(resultantInTTuple[2]).roots()
846
        # For each iRoot, compute the corresponding tRoots and check 
847
        # them in the input polynomial.
848
        for iRoot in iRootsList:
849
            #print "iRoot:", iRoot
850
            # Roots returned by root() are (value, multiplicity) tuples.
851
            tRootsList = \
852
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
853
            print tRootsList
854
            # The tRootsList can be empty, hence the test.
855
            if len(tRootsList) != 0:
856
                for tRoot in tRootsList:
857
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
858
                    # polyEvalModN must be an integer.
859
                    if polyEvalModN == int(polyEvalModN):
860
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
861
    return polynomialRootsSet
862
# End slz_compute_integer_polynomial_modular_roots_2.
863
#
864
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
865
                                        upperBoundSa, approxAccurSa, 
866
                                        precSa=None):
867
    """
868
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
869
    a polynomial that approximates the function on a an interval starting
870
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
871
    approximates with the expected precision.
872
    The interval upper bound is lowered until the expected approximation 
873
    precision is reached.
874
    The polynomial, the bounds, the center of the interval and the error
875
    are returned.
876
    OUTPUT:
877
    A tuple made of 4 Sollya objects:
878
    - a polynomial;
879
    - an range (an interval, not in the sense of number given as an interval);
880
    - the center of the interval;
881
    - the maximum error in the approximation of the input functionSo by the
882
      output polynomial ; this error <= approxAccurSaS.
883
    
884
    """
885
    #print"In slz_compute_polynomial_and_interval..."
886
    ## Superficial argument check.
887
    if lowerBoundSa > upperBoundSa:
888
        return None
889
    ## Change Sollya precision, if requested.
890
    if precSa is None:
891
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
892
        #print "Computed internal precision:", precSa
893
        if precSa < 192:
894
            precSa = 192
895
    sollyaPrecChanged = False
896
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
897
    if precSa > initialSollyaPrecSa:
898
        if precSa <= 2:
899
            print inspect.stack()[0][3], ": precision change <=2 requested."
900
        pobyso_set_prec_sa_so(precSa)
901
        sollyaPrecChanged = True
902
    RRR = lowerBoundSa.parent()
903
    intervalShrinkConstFactorSa = RRR('0.9')
904
    absoluteErrorTypeSo = pobyso_absolute_so_so()
905
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
906
    currentUpperBoundSa = upperBoundSa
907
    currentLowerBoundSa = lowerBoundSa
908
    # What we want here is the polynomial without the variable change, 
909
    # since our actual variable will be x-intervalCenter defined over the 
910
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
911
    (polySo, intervalCenterSo, maxErrorSo) = \
912
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
913
                                                    currentRangeSo, 
914
                                                    absoluteErrorTypeSo)
915
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
916
    while maxErrorSa > approxAccurSa:
917
        print "++Approximation error:", maxErrorSa.n()
918
        sollya_lib_clear_obj(polySo)
919
        sollya_lib_clear_obj(intervalCenterSo)
920
        sollya_lib_clear_obj(maxErrorSo)
921
        # Very empirical shrinking factor.
922
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
923
        print "Shrink factor:", \
924
              shrinkFactorSa.n(), \
925
              intervalShrinkConstFactorSa
926
        print
927
        #errorRatioSa = approxAccurSa/maxErrorSa
928
        #print "Error ratio: ", errorRatioSa
929
        # Make sure interval shrinks.
930
        if shrinkFactorSa > intervalShrinkConstFactorSa:
931
            actualShrinkFactorSa = intervalShrinkConstFactorSa
932
            #print "Fixed"
933
        else:
934
            actualShrinkFactorSa = shrinkFactorSa
935
            #print "Computed",shrinkFactorSa,maxErrorSa
936
            #print shrinkFactorSa, maxErrorSa
937
        #print "Shrink factor", actualShrinkFactorSa
938
        currentUpperBoundSa = currentLowerBoundSa + \
939
                                (currentUpperBoundSa - currentLowerBoundSa) * \
940
                                actualShrinkFactorSa
941
        #print "Current upper bound:", currentUpperBoundSa
942
        sollya_lib_clear_obj(currentRangeSo)
943
        # Check what is left with the bounds.
944
        if currentUpperBoundSa <= currentLowerBoundSa or \
945
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
946
            sollya_lib_clear_obj(absoluteErrorTypeSo)
947
            print "Can't find an interval."
948
            print "Use either or both a higher polynomial degree or a higher",
949
            print "internal precision."
950
            print "Aborting!"
951
            if sollyaPrecChanged:
952
                pobyso_set_prec_so_so(initialSollyaPrecSo)
953
            sollya_lib_clear_obj(initialSollyaPrecSo)
954
            return None
955
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
956
                                                      currentUpperBoundSa)
957
        # print "New interval:",
958
        # pobyso_autoprint(currentRangeSo)
959
        #print "Second Taylor expansion call."
960
        (polySo, intervalCenterSo, maxErrorSo) = \
961
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
962
                                                        currentRangeSo, 
963
                                                        absoluteErrorTypeSo)
964
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
965
        #print "Max errorSo:",
966
        #pobyso_autoprint(maxErrorSo)
967
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
968
        #print "Max errorSa:", maxErrorSa
969
        #print "Sollya prec:",
970
        #pobyso_autoprint(sollya_lib_get_prec(None))
971
    # End while
972
    sollya_lib_clear_obj(absoluteErrorTypeSo)
973
    if sollyaPrecChanged:
974
        pobyso_set_prec_so_so(initialSollyaPrecSo)
975
    sollya_lib_clear_obj(initialSollyaPrecSo)
976
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
977
# End slz_compute_polynomial_and_interval
978

    
979
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
980
                                        upperBoundSa, approxAccurSa, 
981
                                        sollyaPrecSa=None, debug=False):
982
    """
983
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
984
    a polynomial that approximates the function on a an interval starting
985
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
986
    approximates with the expected precision.
987
    The interval upper bound is lowered until the expected approximation 
988
    precision is reached.
989
    The polynomial, the bounds, the center of the interval and the error
990
    are returned.
991
    OUTPUT:
992
    A tuple made of 4 Sollya objects:
993
    - a polynomial;
994
    - an range (an interval, not in the sense of number given as an interval);
995
    - the center of the interval;
996
    - the maximum error in the approximation of the input functionSo by the
997
      output polynomial ; this error <= approxAccurSaS.
998
    
999
    """
1000
    #print"In slz_compute_polynomial_and_interval..."
1001
    ## Superficial argument check.
1002
    if lowerBoundSa > upperBoundSa:
1003
        print inspect.stack()[0][3], ": lower bound is larger than upper bound. "
1004
        return None
1005
    ## Change Sollya precision, if requested.
1006
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1007
    sollyaPrecChangedSa = False
1008
    if sollyaPrecSa is None:
1009
        sollyaPrecSa = initialSollyaPrecSa
1010
    else:
1011
        if sollyaPrecSa > initialSollyaPrecSa:
1012
            if sollyaPrecSa <= 2:
1013
                print inspect.stack()[0][3], ": precision change <= 2 requested."
1014
            pobyso_set_prec_sa_so(sollyaPrecSa)
1015
            sollyaPrecChangedSa = True
1016
    ## Other initializations and data recovery.
1017
    RRR = lowerBoundSa.parent()
1018
    intervalShrinkConstFactorSa = RRR('0.9')
1019
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1020
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1021
    currentUpperBoundSa = upperBoundSa
1022
    currentLowerBoundSa = lowerBoundSa
1023
    # What we want here is the polynomial without the variable change, 
1024
    # since our actual variable will be x-intervalCenter defined over the 
1025
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
1026
    (polySo, intervalCenterSo, maxErrorSo) = \
1027
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1028
                                                    currentRangeSo, 
1029
                                                    absoluteErrorTypeSo)
1030
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1031
    while maxErrorSa > approxAccurSa:
1032
        print "++Approximation error:", maxErrorSa.n()
1033
        sollya_lib_clear_obj(polySo)
1034
        sollya_lib_clear_obj(intervalCenterSo)
1035
        sollya_lib_clear_obj(maxErrorSo)
1036
        # Very empirical shrinking factor.
1037
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1038
        print "Shrink factor:", \
1039
              shrinkFactorSa.n(), \
1040
              intervalShrinkConstFactorSa
1041
        print
1042
        #errorRatioSa = approxAccurSa/maxErrorSa
1043
        #print "Error ratio: ", errorRatioSa
1044
        # Make sure interval shrinks.
1045
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1046
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1047
            #print "Fixed"
1048
        else:
1049
            actualShrinkFactorSa = shrinkFactorSa
1050
            #print "Computed",shrinkFactorSa,maxErrorSa
1051
            #print shrinkFactorSa, maxErrorSa
1052
        #print "Shrink factor", actualShrinkFactorSa
1053
        currentUpperBoundSa = currentLowerBoundSa + \
1054
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1055
                                actualShrinkFactorSa
1056
        #print "Current upper bound:", currentUpperBoundSa
1057
        sollya_lib_clear_obj(currentRangeSo)
1058
        # Check what is left with the bounds.
1059
        if currentUpperBoundSa <= currentLowerBoundSa or \
1060
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1061
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1062
            print "Can't find an interval."
1063
            print "Use either or both a higher polynomial degree or a higher",
1064
            print "internal precision."
1065
            print "Aborting!"
1066
            if sollyaPrecChangedSa:
1067
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1068
            sollya_lib_clear_obj(initialSollyaPrecSo)
1069
            return None
1070
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1071
                                                      currentUpperBoundSa)
1072
        # print "New interval:",
1073
        # pobyso_autoprint(currentRangeSo)
1074
        #print "Second Taylor expansion call."
1075
        (polySo, intervalCenterSo, maxErrorSo) = \
1076
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1077
                                                        currentRangeSo, 
1078
                                                        absoluteErrorTypeSo)
1079
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1080
        #print "Max errorSo:",
1081
        #pobyso_autoprint(maxErrorSo)
1082
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1083
        #print "Max errorSa:", maxErrorSa
1084
        #print "Sollya prec:",
1085
        #pobyso_autoprint(sollya_lib_get_prec(None))
1086
    # End while
1087
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1088
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1089
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1090
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1091
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1092
    if debug:
1093
        print inspect.stack()[0][3], "SollyaPrecSa:", sollyaPrecSa
1094
        print "About to call polynomial rounding with:"
1095
        print "polySo: ",           ; pobyso_autoprint(polySo)
1096
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
1097
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1098
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1099
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
1100
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1101
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1102
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1103
    """
1104
    # Naive rounding.
1105
    (roundedPolySo, roundedPolyMaxErrSo) = \
1106
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
1107
                                                           functionSo,
1108
                                                           intervalCenterSo,
1109
                                                           currentRangeSo,
1110
                                                           itpSo,
1111
                                                           ftpSo,
1112
                                                           maxPrecSo,
1113
                                                           approxAccurSo)
1114
    """
1115
    # Proved rounding.
1116
    (roundedPolySo, roundedPolyMaxErrSo) = \
1117
        pobyso_round_coefficients_progressive_so_so(polySo, 
1118
                                                    functionSo,
1119
                                                    maxPrecSo,
1120
                                                    currentRangeSo,
1121
                                                    intervalCenterSo,
1122
                                                    maxErrorSo,
1123
                                                    approxAccurSo,
1124
                                                    debug=False)
1125
    #### Comment out the two next lines when polynomial rounding is activated.
1126
    #roundedPolySo = sollya_lib_copy_obj(polySo)
1127
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
1128
    sollya_lib_clear_obj(polySo)
1129
    sollya_lib_clear_obj(maxErrorSo)
1130
    sollya_lib_clear_obj(itpSo)
1131
    sollya_lib_clear_obj(ftpSo)
1132
    sollya_lib_clear_obj(approxAccurSo)
1133
    if sollyaPrecChangedSa:
1134
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1135
    sollya_lib_clear_obj(initialSollyaPrecSo)
1136
    if debug:
1137
        print "1: ", ; pobyso_autoprint(roundedPolySo)
1138
        print "2: ", ; pobyso_autoprint(currentRangeSo)
1139
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
1140
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1141
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1142
# End slz_compute_polynomial_and_interval_01
1143

    
1144
def slz_compute_polynomial_and_interval_02(functionSo, degreeSo, lowerBoundSa, 
1145
                                        upperBoundSa, approxAccurSa, 
1146
                                        sollyaPrecSa=None, debug=True ):
1147
    """
1148
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
1149
    a polynomial that approximates the function on a an interval starting
1150
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1151
    approximates with the expected precision.
1152
    The interval upper bound is lowered until the expected approximation 
1153
    precision is reached.
1154
    The polynomial, the bounds, the center of the interval and the error
1155
    are returned.
1156
    OUTPUT:
1157
    A tuple made of 4 Sollya objects:
1158
    - a polynomial;
1159
    - an range (an interval, not in the sense of number given as an interval);
1160
    - the center of the interval;
1161
    - the maximum error in the approximation of the input functionSo by the
1162
      output polynomial ; this error <= approxAccurSaS.
1163
    Changes fom v 01:
1164
        extra verbose.
1165
    """
1166
    print"In slz_compute_polynomial_and_interval..."
1167
    ## Superficial argument check.
1168
    if lowerBoundSa > upperBoundSa:
1169
        return None
1170
    ## Change Sollya precision, if requested.
1171
    sollyaPrecChanged = False
1172
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1173
    #print "Initial Sollya prec:", initialSollyaPrecSa, type(initialSollyaPrecSa)
1174
    if sollyaPrecSa is None:
1175
        sollyaPrecSa = initialSollyaPrecSa
1176
    else:
1177
        if sollyaPrecSa <= 2:
1178
            print inspect.stack()[0][3], ": precision change <=2 requested."
1179
        pobyso_set_prec_sa_so(sollyaPrecSa)
1180
        sollyaPrecChanged = True
1181
    RRR = lowerBoundSa.parent()
1182
    intervalShrinkConstFactorSa = RRR('0.9')
1183
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1184
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1185
    currentUpperBoundSa = upperBoundSa
1186
    currentLowerBoundSa = lowerBoundSa
1187
    #pobyso_autoprint(functionSo)
1188
    #pobyso_autoprint(degreeSo)
1189
    #pobyso_autoprint(currentRangeSo)
1190
    #pobyso_autoprint(absoluteErrorTypeSo)
1191
    ## What we want here is the polynomial without the variable change, 
1192
    #  since our actual variable will be x-intervalCenter defined over the 
1193
    #  domain [lowerBound-intervalCenter , upperBound-intervalCenter].
1194
    (polySo, intervalCenterSo, maxErrorSo) = \
1195
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1196
                                                    currentRangeSo, 
1197
                                                    absoluteErrorTypeSo)
1198
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1199
    print "...after Taylor expansion."
1200
    while maxErrorSa > approxAccurSa:
1201
        print "++Approximation error:", maxErrorSa.n()
1202
        sollya_lib_clear_obj(polySo)
1203
        sollya_lib_clear_obj(intervalCenterSo)
1204
        sollya_lib_clear_obj(maxErrorSo)
1205
        # Very empirical shrinking factor.
1206
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1207
        print "Shrink factor:", \
1208
              shrinkFactorSa.n(), \
1209
              intervalShrinkConstFactorSa
1210
        print
1211
        #errorRatioSa = approxAccurSa/maxErrorSa
1212
        #print "Error ratio: ", errorRatioSa
1213
        # Make sure interval shrinks.
1214
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1215
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1216
            #print "Fixed"
1217
        else:
1218
            actualShrinkFactorSa = shrinkFactorSa
1219
            #print "Computed",shrinkFactorSa,maxErrorSa
1220
            #print shrinkFactorSa, maxErrorSa
1221
        #print "Shrink factor", actualShrinkFactorSa
1222
        currentUpperBoundSa = currentLowerBoundSa + \
1223
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1224
                                actualShrinkFactorSa
1225
        #print "Current upper bound:", currentUpperBoundSa
1226
        sollya_lib_clear_obj(currentRangeSo)
1227
        # Check what is left with the bounds.
1228
        if currentUpperBoundSa <= currentLowerBoundSa or \
1229
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1230
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1231
            print "Can't find an interval."
1232
            print "Use either or both a higher polynomial degree or a higher",
1233
            print "internal precision."
1234
            print "Aborting!"
1235
            if sollyaPrecChanged:
1236
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1237
            sollya_lib_clear_obj(initialSollyaPrecSo)
1238
            return None
1239
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1240
                                                      currentUpperBoundSa)
1241
        # print "New interval:",
1242
        # pobyso_autoprint(currentRangeSo)
1243
        #print "Second Taylor expansion call."
1244
        (polySo, intervalCenterSo, maxErrorSo) = \
1245
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1246
                                                        currentRangeSo, 
1247
                                                        absoluteErrorTypeSo)
1248
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1249
        #print "Max errorSo:",
1250
        #pobyso_autoprint(maxErrorSo)
1251
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1252
        #print "Max errorSa:", maxErrorSa
1253
        #print "Sollya prec:",
1254
        #pobyso_autoprint(sollya_lib_get_prec(None))
1255
    # End while
1256
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1257
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1258
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1259
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1260
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1261
    print "About to call polynomial rounding with:"
1262
    print "polySo: ",           ; pobyso_autoprint(polySo)
1263
    print "functionSo: ",       ; pobyso_autoprint(functionSo)
1264
    print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1265
    print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1266
    print "itpSo: ",            ; pobyso_autoprint(itpSo)
1267
    print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1268
    print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1269
    print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1270
    (roundedPolySo, roundedPolyMaxErrSo) = \
1271
    pobyso_round_coefficients_progressive_so_so(polySo,
1272
                                                functionSo,
1273
                                                maxPrecSo,
1274
                                                currentRangeSo,
1275
                                                intervalCenterSo,
1276
                                                maxErrorSo,
1277
                                                approxAccurSo,
1278
                                                debug = True)
1279
    
1280
    sollya_lib_clear_obj(polySo)
1281
    sollya_lib_clear_obj(maxErrorSo)
1282
    sollya_lib_clear_obj(itpSo)
1283
    sollya_lib_clear_obj(ftpSo)
1284
    sollya_lib_clear_obj(approxAccurSo)
1285
    if sollyaPrecChanged:
1286
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1287
    sollya_lib_clear_obj(initialSollyaPrecSo)
1288
    print "1: ", ; pobyso_autoprint(roundedPolySo)
1289
    print "2: ", ; pobyso_autoprint(currentRangeSo)
1290
    print "3: ", ; pobyso_autoprint(intervalCenterSo)
1291
    print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1292
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1293
# End slz_compute_polynomial_and_interval_02
1294

    
1295
def slz_compute_reduced_polynomial(matrixRow,
1296
                                    knownMonomials,
1297
                                    var1,
1298
                                    var1Bound,
1299
                                    var2,
1300
                                    var2Bound):
1301
    """
1302
    Compute a polynomial from a single reduced matrix row.
1303
    This function was introduced in order to avoid the computation of the
1304
    all the polynomials  from the full matrix (even those built from rows 
1305
    that do no verify the Coppersmith condition) as this may involves 
1306
    expensive operations over (large) integers.
1307
    """
1308
    ## Check arguments.
1309
    if len(knownMonomials) == 0:
1310
        return None
1311
    # varNounds can be zero since 0^0 returns 1.
1312
    if (var1Bound < 0) or (var2Bound < 0):
1313
        return None
1314
    ## Initialisations. 
1315
    polynomialRing = knownMonomials[0].parent() 
1316
    currentPolynomial = polynomialRing(0)
1317
    # TODO: use zip instead of indices.
1318
    for colIndex in xrange(0, len(knownMonomials)):
1319
        currentCoefficient = matrixRow[colIndex]
1320
        if currentCoefficient != 0:
1321
            #print "Current coefficient:", currentCoefficient
1322
            currentMonomial = knownMonomials[colIndex]
1323
            #print "Monomial as multivariate polynomial:", \
1324
            #currentMonomial, type(currentMonomial)
1325
            degreeInVar1 = currentMonomial.degree(var1)
1326
            #print "Degree in var1", var1, ":", degreeInVar1
1327
            degreeInVar2 = currentMonomial.degree(var2)
1328
            #print "Degree in var2", var2, ":", degreeInVar2
1329
            if degreeInVar1 > 0:
1330
                currentCoefficient = \
1331
                    currentCoefficient / (var1Bound^degreeInVar1)
1332
                #print "varBound1 in degree:", var1Bound^degreeInVar1
1333
                #print "Current coefficient(1)", currentCoefficient
1334
            if degreeInVar2 > 0:
1335
                currentCoefficient = \
1336
                    currentCoefficient / (var2Bound^degreeInVar2)
1337
                #print "Current coefficient(2)", currentCoefficient
1338
            #print "Current reduced monomial:", (currentCoefficient * \
1339
            #                                    currentMonomial)
1340
            currentPolynomial += (currentCoefficient * currentMonomial)
1341
            #print "Current polynomial:", currentPolynomial
1342
        # End if
1343
    # End for colIndex.
1344
    #print "Type of the current polynomial:", type(currentPolynomial)
1345
    return(currentPolynomial)
1346
# End slz_compute_reduced_polynomial
1347
#
1348
def slz_compute_reduced_polynomials(reducedMatrix,
1349
                                        knownMonomials,
1350
                                        var1,
1351
                                        var1Bound,
1352
                                        var2,
1353
                                        var2Bound):
1354
    """
1355
    Legacy function, use slz_compute_reduced_polynomials_list
1356
    """
1357
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1358
                                                knownMonomials,
1359
                                                var1,
1360
                                                var1Bound,
1361
                                                var2,
1362
                                                var2Bound)
1363
    )
1364
#
1365
def slz_compute_reduced_polynomials_list(reducedMatrix,
1366
                                         knownMonomials,
1367
                                         var1,
1368
                                         var1Bound,
1369
                                         var2,
1370
                                         var2Bound):
1371
    """
1372
    From a reduced matrix, holding the coefficients, from a monomials list,
1373
    from the bounds of each variable, compute the corresponding polynomials
1374
    scaled back by dividing by the "right" powers of the variables bounds.
1375
    
1376
    The elements in knownMonomials must be of the "right" polynomial type.
1377
    They set the polynomial type of the output polynomials in list.
1378
    @param reducedMatrix:  the reduced matrix as output from LLL;
1379
    @param kwnonMonomials: the ordered list of the monomials used to
1380
                           build the polynomials;
1381
    @param var1:           the first variable (of the "right" type);
1382
    @param var1Bound:      the first variable bound;
1383
    @param var2:           the second variable (of the "right" type);
1384
    @param var2Bound:      the second variable bound.
1385
    @return: a list of polynomials obtained with the reduced coefficients
1386
             and scaled down with the bounds
1387
    """
1388
    
1389
    # TODO: check input arguments.
1390
    reducedPolynomials = []
1391
    #print "type var1:", type(var1), " - type var2:", type(var2)
1392
    for matrixRow in reducedMatrix.rows():
1393
        currentPolynomial = 0
1394
        for colIndex in xrange(0, len(knownMonomials)):
1395
            currentCoefficient = matrixRow[colIndex]
1396
            if currentCoefficient != 0:
1397
                #print "Current coefficient:", currentCoefficient
1398
                currentMonomial = knownMonomials[colIndex]
1399
                parentRing = currentMonomial.parent()
1400
                #print "Monomial as multivariate polynomial:", \
1401
                #currentMonomial, type(currentMonomial)
1402
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1403
                #print "Degree in var", var1, ":", degreeInVar1
1404
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1405
                #print "Degree in var", var2, ":", degreeInVar2
1406
                if degreeInVar1 > 0:
1407
                    currentCoefficient /= var1Bound^degreeInVar1
1408
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1409
                    #print "Current coefficient(1)", currentCoefficient
1410
                if degreeInVar2 > 0:
1411
                    currentCoefficient /= var2Bound^degreeInVar2
1412
                    #print "Current coefficient(2)", currentCoefficient
1413
                #print "Current reduced monomial:", (currentCoefficient * \
1414
                #                                    currentMonomial)
1415
                currentPolynomial += (currentCoefficient * currentMonomial)
1416
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1417
                    #print "!!!! constant term !!!!"
1418
                #print "Current polynomial:", currentPolynomial
1419
            # End if
1420
        # End for colIndex.
1421
        #print "Type of the current polynomial:", type(currentPolynomial)
1422
        reducedPolynomials.append(currentPolynomial)
1423
    return reducedPolynomials 
1424
# End slz_compute_reduced_polynomials_list.
1425

    
1426
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1427
                                                   knownMonomials,
1428
                                                   var1,
1429
                                                   var1Bound,
1430
                                                   var2,
1431
                                                   var2Bound):
1432
    """
1433
    From a list of rows, holding the coefficients, from a monomials list,
1434
    from the bounds of each variable, compute the corresponding polynomials
1435
    scaled back by dividing by the "right" powers of the variables bounds.
1436
    
1437
    The elements in knownMonomials must be of the "right" polynomial type.
1438
    They set the polynomial type of the output polynomials in list.
1439
    @param rowsList:       the reduced matrix as output from LLL;
1440
    @param kwnonMonomials: the ordered list of the monomials used to
1441
                           build the polynomials;
1442
    @param var1:           the first variable (of the "right" type);
1443
    @param var1Bound:      the first variable bound;
1444
    @param var2:           the second variable (of the "right" type);
1445
    @param var2Bound:      the second variable bound.
1446
    @return: a list of polynomials obtained with the reduced coefficients
1447
             and scaled down with the bounds
1448
    """
1449
    
1450
    # TODO: check input arguments.
1451
    reducedPolynomials = []
1452
    #print "type var1:", type(var1), " - type var2:", type(var2)
1453
    for matrixRow in rowsList:
1454
        currentPolynomial = 0
1455
        for colIndex in xrange(0, len(knownMonomials)):
1456
            currentCoefficient = matrixRow[colIndex]
1457
            if currentCoefficient != 0:
1458
                #print "Current coefficient:", currentCoefficient
1459
                currentMonomial = knownMonomials[colIndex]
1460
                parentRing = currentMonomial.parent()
1461
                #print "Monomial as multivariate polynomial:", \
1462
                #currentMonomial, type(currentMonomial)
1463
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1464
                #print "Degree in var", var1, ":", degreeInVar1
1465
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1466
                #print "Degree in var", var2, ":", degreeInVar2
1467
                if degreeInVar1 > 0:
1468
                    currentCoefficient /= var1Bound^degreeInVar1
1469
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1470
                    #print "Current coefficient(1)", currentCoefficient
1471
                if degreeInVar2 > 0:
1472
                    currentCoefficient /= var2Bound^degreeInVar2
1473
                    #print "Current coefficient(2)", currentCoefficient
1474
                #print "Current reduced monomial:", (currentCoefficient * \
1475
                #                                    currentMonomial)
1476
                currentPolynomial += (currentCoefficient * currentMonomial)
1477
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1478
                    #print "!!!! constant term !!!!"
1479
                #print "Current polynomial:", currentPolynomial
1480
            # End if
1481
        # End for colIndex.
1482
        #print "Type of the current polynomial:", type(currentPolynomial)
1483
        reducedPolynomials.append(currentPolynomial)
1484
    return reducedPolynomials 
1485
# End slz_compute_reduced_polynomials_list_from_rows.
1486
#
1487
def slz_compute_scaled_function(functionSa,
1488
                                lowerBoundSa,
1489
                                upperBoundSa,
1490
                                floatingPointPrecSa,
1491
                                debug=False):
1492
    """
1493
    From a function, compute the scaled function whose domain
1494
    is included in [1, 2) and whose image is also included in [1,2).
1495
    Return a tuple: 
1496
    [0]: the scaled function
1497
    [1]: the scaled domain lower bound
1498
    [2]: the scaled domain upper bound
1499
    [3]: the scaled image lower bound
1500
    [4]: the scaled image upper bound
1501
    """
1502
    ## The variable can be called anything.
1503
    x = functionSa.variables()[0]
1504
    # Scalling the domain -> [1,2[.
1505
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1506
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1507
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1508
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1509
    if debug:
1510
        print "domainScalingExpression for argument :", \
1511
        invDomainScalingExpressionSa
1512
        print "function: ", functionSa
1513
    ff = functionSa.subs({x : domainScalingExpressionSa})
1514
    ## Bless expression as a function.
1515
    ff = ff.function(x)
1516
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1517
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1518
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1519
    scaledLowerBoundSa = \
1520
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1521
    scaledUpperBoundSa = \
1522
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1523
    if debug:
1524
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1525
              scaledUpperBoundSa
1526
    ## If both bounds are negative, swap them.
1527
    if lowerBoundSa < 0 and upperBoundSa < 0:
1528
        scaledLowerBoundSa, scaledUpperBoundSa = \
1529
        scaledUpperBoundSa,scaledLowerBoundSa
1530
    #
1531
    # Scalling the image -> [1,2[.
1532
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
1533
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
1534
    if flbSa <= fubSa: # Increasing
1535
        imageBinadeBottomSa = floor(flbSa.log2())
1536
    else: # Decreasing
1537
        imageBinadeBottomSa = floor(fubSa.log2())
1538
    if debug:
1539
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
1540
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
1541
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
1542
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
1543
    if debug:
1544
        print "imageScalingExpression for argument :", \
1545
              invImageScalingExpressionSa
1546
    iis = invImageScalingExpressionSa.function(x)
1547
    fff = iis.subs({x:ff})
1548
    if debug:
1549
        print "fff:", fff,
1550
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
1551
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
1552
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
1553
# End slz_compute_scaled_function
1554

    
1555
def slz_fix_bounds_for_binades(lowerBound, 
1556
                               upperBound, 
1557
                               func = None, 
1558
                               domainDirection = -1,
1559
                               imageDirection  = -1):
1560
    """
1561
    Assuming the function is increasing or decreasing over the 
1562
    [lowerBound, upperBound] interval, return a lower bound lb and 
1563
    an upper bound ub such that:
1564
    - lb and ub belong to the same binade;
1565
    - func(lb) and func(ub) belong to the same binade.
1566
    domainDirection indicate how bounds move to fit into the same binade:
1567
    - a negative value move the upper bound down;
1568
    - a positive value move the lower bound up.
1569
    imageDirection indicate how bounds move in order to have their image
1570
    fit into the same binade, variation of the function is also condidered.
1571
    For an increasing function:
1572
    - negative value moves the upper bound down (and its image value as well);
1573
    - a positive values moves the lower bound up (and its image value as well);
1574
    For a decreasing function it is the other way round.
1575
    """
1576
    ## Arguments check
1577
    if lowerBound > upperBound:
1578
        return None
1579
    if lowerBound == upperBound:
1580
        return (lowerBound, upperBound)
1581
    if func is None:
1582
        return None
1583
    #
1584
    #varFunc = func.variables()[0]
1585
    lb = lowerBound
1586
    ub = upperBound
1587
    lbBinade = slz_compute_binade(lb) 
1588
    ubBinade = slz_compute_binade(ub)
1589
    ## Domain binade.
1590
    while lbBinade != ubBinade:
1591
        newIntervalWidth = (ub - lb) / 2
1592
        if domainDirection < 0:
1593
            ub = lb + newIntervalWidth
1594
            ubBinade = slz_compute_binade(ub)
1595
        else:
1596
            lb = lb + newIntervalWidth
1597
            lbBinade = slz_compute_binade(lb) 
1598
    #print "sfbfb: lower bound:", lb.str(truncate=False)
1599
    #print "sfbfb: upper bound:", ub.str(truncate=False)
1600
    ## At this point, both bounds belond to the same binade.
1601
    ## Image binade.
1602
    if lb == ub:
1603
        return (lb, ub)
1604
    lbImg = func(lb)
1605
    ubImg = func(ub)
1606
    funcIsInc = ((ubImg - lbImg) >= 0)
1607
    lbImgBinade = slz_compute_binade(lbImg)
1608
    ubImgBinade = slz_compute_binade(ubImg)
1609
    while lbImgBinade != ubImgBinade:
1610
        newIntervalWidth = (ub - lb) / 2
1611
        if imageDirection < 0:
1612
            if funcIsInc:
1613
                ub = lb + newIntervalWidth
1614
                ubImgBinade = slz_compute_binade(func(ub))
1615
                #print ubImgBinade
1616
            else:
1617
                lb = lb + newIntervalWidth
1618
                lbImgBinade = slz_compute_binade(func(lb))
1619
                #print lbImgBinade
1620
        else:
1621
            if funcIsInc:
1622
                lb = lb + newIntervalWidth
1623
                lbImgBinade = slz_compute_binade(func(lb))
1624
                #print lbImgBinade
1625
            else:
1626
                ub = lb + newIntervalWidth
1627
                ubImgBinade = slz_compute_binade(func(ub))
1628
                #print ubImgBinade
1629
    # End while lbImgBinade != ubImgBinade:
1630
    return (lb, ub)
1631
# End slz_fix_bounds_for_binades.
1632

    
1633
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
1634
    # Create a polynomial over the rationals.
1635
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
1636
    return(ratPolynomialRing(polyOfFloat))
1637
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1638

    
1639
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
1640
    """
1641
     Create a polynomial over the rationals where all denominators are
1642
     powers of two.
1643
    """
1644
    polyVariable = polyOfFloat.variables()[0] 
1645
    RPR = QQ[str(polyVariable)]
1646
    polyCoeffs      = polyOfFloat.coefficients()
1647
    #print polyCoeffs
1648
    polyExponents   = polyOfFloat.exponents()
1649
    #print polyExponents
1650
    polyDenomPtwoCoeffs = []
1651
    for coeff in polyCoeffs:
1652
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
1653
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
1654
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
1655
    ratPoly = RPR(0)
1656
    #print type(ratPoly)
1657
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
1658
    #  The coefficient becomes plainly wrong when exponent == 0.
1659
    #  No clue as to why.
1660
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
1661
        ratPoly += coeff * RPR(polyVariable^exponent)
1662
    return ratPoly
1663
# End slz_float_poly_of_float_to_rat_poly_of_rat.
1664

    
1665
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
1666
                                      lowerBoundSa, 
1667
                                      upperBoundSa, floatingPointPrecSa, 
1668
                                      internalSollyaPrecSa, approxPrecSa):
1669
    """
1670
    Under the assumption that:
1671
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
1672
    - lowerBound and upperBound belong to the same binade.
1673
    from a:
1674
    - function;
1675
    - a degree
1676
    - a pair of bounds;
1677
    - the floating-point precision we work on;
1678
    - the internal Sollya precision;
1679
    - the requested approximation error
1680
    The initial interval is, possibly, splitted into smaller intervals.
1681
    It return a list of tuples, each made of:
1682
    - a first polynomial (without the changed variable f(x) = p(x-x0));
1683
    - a second polynomial (with a changed variable f(x) = q(x))
1684
    - the approximation interval;
1685
    - the center, x0, of the interval;
1686
    - the corresponding approximation error.
1687
    TODO: fix endless looping for some parameters sets.
1688
    """
1689
    resultArray = []
1690
    # Set Sollya to the necessary internal precision.
1691
    sollyaPrecChangedSa = False
1692
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1693
    if internalSollyaPrecSa > currentSollyaPrecSa:
1694
        if internalSollyaPrecSa <= 2:
1695
            print inspect.stack()[0][3], ": precision change <=2 requested."
1696
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
1697
        sollyaPrecChangedSa = True
1698
    #
1699
    x = functionSa.variables()[0] # Actual variable name can be anything.
1700
    # Scaled function: [1=,2] -> [1,2].
1701
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
1702
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
1703
                slz_compute_scaled_function(functionSa,                       \
1704
                                            lowerBoundSa,                     \
1705
                                            upperBoundSa,                     \
1706
                                            floatingPointPrecSa)
1707
    # In case bounds were in the negative real one may need to
1708
    # switch scaled bounds.
1709
    if scaledLowerBoundSa > scaledUpperBoundSa:
1710
        scaledLowerBoundSa, scaledUpperBoundSa = \
1711
            scaledUpperBoundSa, scaledLowerBoundSa
1712
        #print "Switching!"
1713
    print "Approximation accuracy: ", RR(approxAccurSa)
1714
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1715
    functionSo = \
1716
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1717
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1718
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
1719
                                                  scaledUpperBoundSa)
1720

    
1721
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1722
                                              upperBoundSa.parent().precision()))
1723
    currentScaledLowerBoundSa = scaledLowerBoundSa
1724
    currentScaledUpperBoundSa = scaledUpperBoundSa
1725
    while True:
1726
        ## Compute the first Taylor expansion.
1727
        print "Computing a Taylor expansion..."
1728
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1729
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1730
                                        currentScaledLowerBoundSa, 
1731
                                        currentScaledUpperBoundSa,
1732
                                        approxAccurSa, internalSollyaPrecSa)
1733
        print "...done."
1734
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1735
        #  This value goes to the first variable: polySo. Other variables are
1736
        #  not assigned and should not be tested.
1737
        if polySo is None:
1738
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1739
            if precChangedSa:
1740
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1741
            sollya_lib_clear_obj(initialSollyaPrecSo)
1742
            sollya_lib_clear_obj(functionSo)
1743
            sollya_lib_clear_obj(degreeSo)
1744
            sollya_lib_clear_obj(scaledBoundsSo)
1745
            return None
1746
        ## Add to the result array.
1747
        ### Change variable stuff in Sollya x -> x0-x.
1748
        changeVarExpressionSo = \
1749
            sollya_lib_build_function_sub( \
1750
                              sollya_lib_build_function_free_variable(), 
1751
                              sollya_lib_copy_obj(intervalCenterSo))
1752
        polyVarChangedSo = \
1753
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1754
        #### Get rid of the variable change Sollya stuff. 
1755
        sollya_lib_clear_obj(changeVarExpressionSo)
1756
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1757
                            intervalCenterSo, maxErrorSo))
1758
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1759
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1760
        print "Computed approximation error:", errorSa.n(digits=10)
1761
        # If the error and interval are OK a the first try, just return.
1762
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1763
            (errorSa <= approxAccurSa):
1764
            if precChangedSa:
1765
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1766
            sollya_lib_clear_obj(initialSollyaPrecSo)
1767
            sollya_lib_clear_obj(functionSo)
1768
            sollya_lib_clear_obj(degreeSo)
1769
            sollya_lib_clear_obj(scaledBoundsSo)
1770
            #print "Approximation error:", errorSa
1771
            return resultArray
1772
        ## The returned interval upper bound does not reach the requested upper
1773
        #  upper bound: compute the next upper bound.
1774
        ## The following ratio is always >= 1. If errorSa, we may want to
1775
        #  enlarge the interval
1776
        currentErrorRatio = approxAccurSa / errorSa
1777
        ## --|--------------------------------------------------------------|--
1778
        #  --|--------------------|--------------------------------------------
1779
        #  --|----------------------------|------------------------------------
1780
        ## Starting point for the next upper bound.
1781
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1782
        # Compute the increment. 
1783
        newBoundsWidthSa = \
1784
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1785
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1786
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1787
        # Take into account the original interval upper bound.                     
1788
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1789
            currentScaledUpperBoundSa = scaledUpperBoundSa
1790
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1791
            print "Can't shrink the interval anymore!"
1792
            print "You should consider increasing the Sollya internal precision"
1793
            print "or the polynomial degree."
1794
            print "Giving up!"
1795
            if precChangedSa:
1796
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1797
            sollya_lib_clear_obj(initialSollyaPrecSo)
1798
            sollya_lib_clear_obj(functionSo)
1799
            sollya_lib_clear_obj(degreeSo)
1800
            sollya_lib_clear_obj(scaledBoundsSo)
1801
            return None
1802
        # Compute the other expansions.
1803
        # Test for insufficient precision.
1804
# End slz_get_intervals_and_polynomials
1805

    
1806
def slz_interval_scaling_expression(boundsInterval, expVar):
1807
    """
1808
    Compute the scaling expression to map an interval that spans at most
1809
    a single binade into [1, 2) and the inverse expression as well.
1810
    If the interval spans more than one binade, result is wrong since
1811
    scaling is only based on the lower bound.
1812
    Not very sure that the transformation makes sense for negative numbers.
1813
    """
1814
    # The "one of the bounds is 0" special case. Here we consider
1815
    # the interval as the subnormals binade.
1816
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1817
        # The upper bound is (or should be) positive.
1818
        if boundsInterval.endpoints()[0] == 0:
1819
            if boundsInterval.endpoints()[1] == 0:
1820
                return None
1821
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1822
            l2     = boundsInterval.endpoints()[1].abs().log2()
1823
            # The upper bound is a power of two
1824
            if binade == l2:
1825
                scalingCoeff    = 2^(-binade)
1826
                invScalingCoeff = 2^(binade)
1827
                scalingOffset   = 1
1828
                return \
1829
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1830
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1831
            else:
1832
                scalingCoeff    = 2^(-binade-1)
1833
                invScalingCoeff = 2^(binade+1)
1834
                scalingOffset   = 1
1835
                return((scalingCoeff * expVar + scalingOffset),\
1836
                    ((expVar - scalingOffset) * invScalingCoeff))
1837
        # The lower bound is (or should be) negative.
1838
        if boundsInterval.endpoints()[1] == 0:
1839
            if boundsInterval.endpoints()[0] == 0:
1840
                return None
1841
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1842
            l2     = boundsInterval.endpoints()[0].abs().log2()
1843
            # The upper bound is a power of two
1844
            if binade == l2:
1845
                scalingCoeff    = -2^(-binade)
1846
                invScalingCoeff = -2^(binade)
1847
                scalingOffset   = 1
1848
                return((scalingCoeff * expVar + scalingOffset),\
1849
                    ((expVar - scalingOffset) * invScalingCoeff))
1850
            else:
1851
                scalingCoeff    = -2^(-binade-1)
1852
                invScalingCoeff = -2^(binade+1)
1853
                scalingOffset   = 1
1854
                return((scalingCoeff * expVar + scalingOffset),\
1855
                   ((expVar - scalingOffset) * invScalingCoeff))
1856
    # General case
1857
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1858
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1859
    # We allow for a single exception in binade spanning is when the
1860
    # "outward bound" is a power of 2 and its binade is that of the
1861
    # "inner bound" + 1.
1862
    if boundsInterval.endpoints()[0] > 0:
1863
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1864
        if lbBinade != ubBinade:
1865
            print "Different binades."
1866
            if ubL2 != ubBinade:
1867
                print "Not a power of 2."
1868
                return None
1869
            elif abs(ubBinade - lbBinade) > 1:
1870
                print "Too large span:", abs(ubBinade - lbBinade)
1871
                return None
1872
    else:
1873
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1874
        if lbBinade != ubBinade:
1875
            print "Different binades."
1876
            if lbL2 != lbBinade:
1877
                print "Not a power of 2."
1878
                return None
1879
            elif abs(ubBinade - lbBinade) > 1:
1880
                print "Too large span:", abs(ubBinade - lbBinade)
1881
                return None
1882
    #print "Lower bound binade:", binade
1883
    if boundsInterval.endpoints()[0] > 0:
1884
        return \
1885
            ((2^(-lbBinade) * expVar).function(expVar),
1886
             (2^(lbBinade) * expVar).function(expVar))
1887
    else:
1888
        return \
1889
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1890
             (-(2^(ubBinade)) * expVar).function(expVar))
1891
"""
1892
    # Code sent to attic. Should be the base for a 
1893
    # "slz_interval_translate_expression" rather than scale.
1894
    # Extra control and special cases code  added in  
1895
    # slz_interval_scaling_expression could (should ?) be added to
1896
    # this new function.
1897
    # The scaling offset is only used for negative numbers.
1898
    # When the absolute value of the lower bound is < 0.
1899
    if abs(boundsInterval.endpoints()[0]) < 1:
1900
        if boundsInterval.endpoints()[0] >= 0:
1901
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1902
            invScalingCoeff = 1/scalingCoeff
1903
            return((scalingCoeff * expVar, 
1904
                    invScalingCoeff * expVar))
1905
        else:
1906
            scalingCoeff = \
1907
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1908
            scalingOffset = -3 * scalingCoeff
1909
            return((scalingCoeff * expVar + scalingOffset,
1910
                    1/scalingCoeff * expVar + 3))
1911
    else: 
1912
        if boundsInterval.endpoints()[0] >= 0:
1913
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1914
            scalingOffset = 0
1915
            return((scalingCoeff * expVar, 
1916
                    1/scalingCoeff * expVar))
1917
        else:
1918
            scalingCoeff = \
1919
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1920
            scalingOffset =  -3 * scalingCoeff
1921
            #scalingOffset = 0
1922
            return((scalingCoeff * expVar + scalingOffset,
1923
                    1/scalingCoeff * expVar + 3))
1924
"""
1925
# End slz_interval_scaling_expression
1926
   
1927
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1928
    """
1929
    Compute the Sage version of the Taylor polynomial and it's
1930
    companion data (interval, center...)
1931
    The input parameter is a five elements tuple:
1932
    - [0]: the polyomial (without variable change), as polynomial over a
1933
           real ring;
1934
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1935
           over a real ring;
1936
    - [2]: the interval (as Sollya range);
1937
    - [3]: the interval center;
1938
    - [4]: the approximation error. 
1939
    
1940
    The function returns a 5 elements tuple: formed with all the 
1941
    input elements converted into their Sollya counterpart.
1942
    """
1943
    #print "Sollya polynomial to convert:", 
1944
    #pobyso_autoprint(polyRangeCenterErrorSo)
1945
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
1946
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
1947
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
1948
    intervalSa = \
1949
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1950
    centerSa = \
1951
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1952
    errorSa = \
1953
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1954
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1955
    # End slz_interval_and_polynomial_to_sage
1956

    
1957
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1958
            targetPlusOnePrecRF = None, quasiExactRF = None):
1959
    """
1960
      Check if an element (argument) of the domain of function (function)
1961
      yields a HTRN case (rounding to next) for the target precision 
1962
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1963
      
1964
      The strategy is: 
1965
      - compute the image at high (quasi-exact) precision;
1966
      - round it to nearest to precision;
1967
      - round it to exactly to (precision+1), the computed number has two
1968
        midpoint neighbors;
1969
      - check the distance between these neighbors and the quasi-exact value;
1970
        - if none of them is closer than the targetAccuracy, return False,
1971
        - else return true.
1972
      - Powers of two are a special case when comparing the midpoint in
1973
        the 0 direction..
1974
    """
1975
    ## Arguments filtering. 
1976
    ## TODO: filter the first argument for consistence.
1977
    if targetRF is None:
1978
        targetRF = argument.parent()
1979
    ## Ditto for the real field holding the midpoints.
1980
    if targetPlusOnePrecRF is None:
1981
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1982
    ## If no quasiExactField is provided, create a high accuracy RealField.
1983
    if quasiExactRF is None:
1984
        quasiExactRF = RealField(1536)
1985
    function                      = function.function(function.variables()[0])
1986
    exactArgument                 = quasiExactRF(argument)
1987
    quasiExactValue               = function(exactArgument)
1988
    roundedValue                  = targetRF(quasiExactValue)
1989
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1990
    # Upper midpoint.
1991
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1992
    # Lower midpoint.
1993
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1994
    binade                        = slz_compute_binade(roundedValue)
1995
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1996
    #print "Argument:", argument
1997
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1998
    #print "Binade:", binade
1999
    #print "binadeCorrectedTargetAccuracy:", \
2000
    #binadeCorrectedTargetAccuracy.n(prec=100)
2001
    #print "binadeCorrectedTargetAccuracy:", \
2002
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
2003
    #print "Upper midpoint:", \
2004
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2005
    #print "Rounded value :", \
2006
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
2007
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
2008
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
2009
    #print "Lower midpoint:", \
2010
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2011
    ## Make quasiExactValue = 0 a special case to move it out of the way.
2012
    if quasiExactValue == 0:
2013
        return False
2014
    ## Begining of the general case : check with the midpoint of 
2015
    #  greatest absolute value.
2016
    if quasiExactValue > 0:
2017
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
2018
           binadeCorrectedTargetAccuracy:
2019
            #print "Too close to the upper midpoint: ", \
2020
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2021
            #print argument.n().str(base=16, \
2022
            #  no_sci=False)
2023
            #print "Lower midpoint:", \
2024
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2025
            #print "Value         :", \
2026
            # quasiExactValue.n(prec=200).str(base=2)
2027
            #print "Upper midpoint:", \
2028
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2029
            return True
2030
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
2031
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2032
           binadeCorrectedTargetAccuracy:
2033
            #print "Too close to the upper midpoint: ", \
2034
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2035
            #print argument.n().str(base=16, \
2036
            #  no_sci=False)
2037
            #print "Lower midpoint:", \
2038
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2039
            #print "Value         :", \
2040
            #  quasiExactValue.n(prec=200).str(base=2)
2041
            #print "Upper midpoint:", \
2042
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2043
            #print
2044
            return True
2045
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2046
    ## For the midpoint of smaller absolute value, 
2047
    #  split cases with the powers of 2.
2048
    if sno_abs_is_power_of_two(roundedValue):
2049
        if quasiExactValue > 0:        
2050
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
2051
               binadeCorrectedTargetAccuracy / 2:
2052
                #print "Lower midpoint:", \
2053
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2054
                #print "Value         :", \
2055
                #  quasiExactValue.n(prec=200).str(base=2)
2056
                #print "Upper midpoint:", \
2057
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2058
                #print
2059
                return True
2060
        else:
2061
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2062
              binadeCorrectedTargetAccuracy / 2:
2063
                #print "Lower midpoint:", \
2064
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2065
                #print "Value         :", 
2066
                #  quasiExactValue.n(prec=200).str(base=2)
2067
                #print "Upper midpoint:", 
2068
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2069
                #print
2070
                return True
2071
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2072
    else: ## Not a power of 2, regular comparison with the midpoint of 
2073
          #  smaller absolute value.
2074
        if quasiExactValue > 0:        
2075
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2076
              binadeCorrectedTargetAccuracy:
2077
                #print "Lower midpoint:", \
2078
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2079
                #print "Value         :", \
2080
                #  quasiExactValue.n(prec=200).str(base=2)
2081
                #print "Upper midpoint:", \
2082
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2083
                #print
2084
                return True
2085
        else: # quasiExactValue <= 0
2086
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2087
              binadeCorrectedTargetAccuracy:
2088
                #print "Lower midpoint:", \
2089
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2090
                #print "Value         :", \
2091
                #  quasiExactValue.n(prec=200).str(base=2)
2092
                #print "Upper midpoint:", \
2093
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2094
                #print
2095
                return True
2096
    #print "distance to the upper midpoint:", \
2097
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
2098
    #print "distance to the lower midpoint:", \
2099
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
2100
    return False
2101
# End slz_is_htrn
2102
#
2103
def slz_pm1():
2104
    """
2105
    Compute a uniform RV in {-1, 1} (not zero).
2106
    """
2107
    ## function getrandbits(N) generates a long int with N random bits.
2108
    return getrandbits(1) * 2-1
2109
# End slz_pm1.
2110
#
2111
def slz_random_proj_pm1(r, c):
2112
    """
2113
    r x c matrix with \pm 1 ({-1, 1}) coefficients
2114
    """
2115
    M = matrix(r, c)
2116
    for i in range(0, r):
2117
        for j in range (0, c):
2118
            M[i,j] = slz_pm1()
2119
    return M
2120
# End random_proj_pm1.
2121
#
2122
def slz_random_proj_uniform(n, r, c):
2123
    """
2124
    r x c integer matrix with uniform n-bit integer coefficients
2125
    """
2126
    M = matrix(r, c)
2127
    for i in range(0, r):
2128
        for j in range (0, c):
2129
            M[i,j] = slz_uniform(n)
2130
    return M       
2131
# End slz_random_proj_uniform.
2132
#
2133
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
2134
                                                precision,
2135
                                                targetHardnessToRound,
2136
                                                variable1,
2137
                                                variable2):
2138
    """
2139
    Creates a new multivariate polynomial with integer coefficients for use
2140
     with the Coppersmith method.
2141
    A the same time it computes :
2142
    - 2^K (N);
2143
    - 2^k (bound on the second variable)
2144
    - lcm
2145

    
2146
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
2147
                         variables.
2148
    :param precision: the precision of the floating-point coefficients.
2149
    :param targetHardnessToRound: the hardness to round we want to check.
2150
    :param variable1: the first variable of the polynomial (an expression).
2151
    :param variable2: the second variable of the polynomial (an expression).
2152
    
2153
    :returns: a 4 elements tuple:
2154
                - the polynomial;
2155
                - the modulus (N);
2156
                - the t bound;
2157
                - the lcm used to compute the integral coefficients and the 
2158
                  module.
2159
    """
2160
    # Create a new integer polynomial ring.
2161
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
2162
    # Coefficients are issued in the increasing power order.
2163
    ratPolyCoefficients = ratPolyOfInt.coefficients()
2164
    # Print the reversed list for debugging.
2165
    #print
2166
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
2167
    # Build the list of number we compute the lcm of.
2168
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
2169
    #print "Coefficient denominators:", coefficientDenominators
2170
    coefficientDenominators.append(2^precision)
2171
    coefficientDenominators.append(2^(targetHardnessToRound))
2172
    leastCommonMultiple = lcm(coefficientDenominators)
2173
    # Compute the expression corresponding to the new polynomial
2174
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
2175
    #print coefficientNumerators
2176
    polynomialExpression = 0
2177
    power = 0
2178
    # Iterate over two lists at the same time, stop when the shorter
2179
    # (is this case coefficientsNumerators) is 
2180
    # exhausted. Both lists are ordered in the order of increasing powers
2181
    # of variable1.
2182
    for numerator, denominator in \
2183
                        zip(coefficientNumerators, coefficientDenominators):
2184
        multiplicator = leastCommonMultiple / denominator 
2185
        newCoefficient = numerator * multiplicator
2186
        polynomialExpression += newCoefficient * variable1^power
2187
        power +=1
2188
    polynomialExpression += - variable2
2189
    return (IP(polynomialExpression),
2190
            leastCommonMultiple / 2^precision, # 2^K aka N.
2191
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
2192
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
2193
            leastCommonMultiple) # If we want to make test computations.
2194
        
2195
# End slz_rat_poly_of_int_to_poly_for_coppersmith
2196

    
2197
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
2198
                                          precision):
2199
    """
2200
    Makes a variable substitution into the input polynomial so that the output
2201
    polynomial can take integer arguments.
2202
    All variables of the input polynomial "have precision p". That is to say
2203
    that they are rationals with denominator == 2^(precision - 1): 
2204
    x = y/2^(precision - 1).
2205
    We "incorporate" these denominators into the coefficients with, 
2206
    respectively, the "right" power.
2207
    """
2208
    polynomialField = ratPolyOfRat.parent()
2209
    polynomialVariable = ratPolyOfRat.variables()[0]
2210
    #print "The polynomial field is:", polynomialField
2211
    return \
2212
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
2213
                                   polynomialVariable/2^(precision-1)}))
2214
    
2215
# End slz_rat_poly_of_rat_to_rat_poly_of_int
2216

    
2217
def slz_reduce_and_test_base(matrixToReduce,
2218
                             nAtAlpha,
2219
                             monomialsCountSqrt):
2220
    """
2221
    Reduces the basis, tests the Coppersmith condition and returns
2222
    a list of rows that comply with the condition.
2223
    """
2224
    ccComplientRowsList = []
2225
    reducedMatrix = matrixToReduce.LLL(None)
2226
    if not reducedMatrix.is_LLL_reduced():
2227
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
2228
    else:
2229
        print "reducedMatrix is actually reduced."
2230
    print "N^alpha:", nAtAlpha.n()
2231
    rowIndex = 0
2232
    for row in reducedMatrix.rows():
2233
        l2Norm = row.norm(2)
2234
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
2235
                monomialsCountSqrt.n(), ". Is vector OK?", 
2236
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
2237
            ccComplientRowsList.append(row)
2238
            print "True"
2239
        else:
2240
            print "False"
2241
    # End for
2242
    return ccComplientRowsList
2243
# End slz_reduce_and_test_base
2244

    
2245
def slz_reduce_lll_proj(matToReduce, n):
2246
    """
2247
    Compute the transformation matrix that realizes an LLL reduction on
2248
    the random uniform projected matrix.
2249
    Return both the initial matrix "reduced" by the transformation matrix and
2250
    the transformation matrix itself.
2251
    """
2252
    ## Compute the projected matrix.
2253
    """
2254
    # Random matrix elements {-2^(n-1),...,0,...,2^(n-1)-1}.
2255
    matProjector = slz_random_proj_uniform(n,
2256
                                           matToReduce.ncols(),
2257
                                           matToReduce.nrows())
2258
    """
2259
    # Random matrix elements in {-1,0,1}. 
2260
    matProjector = slz_random_proj_pm1(matToReduce.ncols(),
2261
                                       matToReduce.nrows())
2262
    matProjected = matToReduce * matProjector
2263
    ## Build the argument matrix for LLL in such a way that the transformation
2264
    #  matrix is also returned. This matrix is obtained at almost no extra
2265
    # cost. An identity matrix must be appended to 
2266
    #  the left of the initial matrix. The transformation matrix will
2267
    # will be recovered at the same location from the returned matrix .
2268
    idMat = identity_matrix(matProjected.nrows())
2269
    augmentedMatToReduce = idMat.augment(matProjected)
2270
    reducedProjMat = \
2271
        augmentedMatToReduce.LLL(algorithm='fpLLL:wrapper')
2272
    ## Recover the transformation matrix (the left part of the reduced matrix). 
2273
    #  We discard the reduced matrix itself.
2274
    transMat = reducedProjMat.submatrix(0,
2275
                                        0,
2276
                                        reducedProjMat.nrows(),
2277
                                        reducedProjMat.nrows())
2278
    ## Return the initial matrix "reduced" and the transformation matrix tuple.
2279
    return (transMat * matToReduce, transMat)
2280
# End  slz_reduce_lll_proj.
2281
#
2282
def slz_resultant(poly1, poly2, elimVar, debug = False):
2283
    """
2284
    Compute the resultant for two polynomials for a given variable
2285
    and return the (poly1, poly2, resultant) if the resultant
2286
    is not 0.
2287
    Return () otherwise.
2288
    """
2289
    polynomialRing0    = poly1.parent()
2290
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2291
    if resultantInElimVar is None:
2292
        if debug:
2293
            print poly1
2294
            print poly2
2295
            print "have GCD = ", poly1.gcd(poly2)
2296
        return None
2297
    if resultantInElimVar.is_zero():
2298
        if debug:
2299
            print poly1
2300
            print poly2
2301
            print "have GCD = ", poly1.gcd(poly2)
2302
        return None
2303
    else:
2304
        if debug:
2305
            print poly1
2306
            print poly2
2307
            print "have resultant in t = ", resultantInElimVar
2308
        return resultantInElimVar
2309
# End slz_resultant.
2310
#
2311
def slz_resultant_tuple(poly1, poly2, elimVar):
2312
    """
2313
    Compute the resultant for two polynomials for a given variable
2314
    and return the (poly1, poly2, resultant) if the resultant
2315
    is not 0.
2316
    Return () otherwise.
2317
    """
2318
    polynomialRing0    = poly1.parent()
2319
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2320
    if resultantInElimVar.is_zero():
2321
        return ()
2322
    else:
2323
        return (poly1, poly2, resultantInElimVar)
2324
# End slz_resultant_tuple.
2325
#
2326
def slz_uniform(n):
2327
    """
2328
    Compute a uniform RV in [-2^(n-1), 2^(n-1)-1] (zero is included).
2329
    """
2330
    ## function getrandbits(n) generates a long int with n random bits.
2331
    return getrandbits(n) - 2^(n-1)
2332
# End slz_uniform.
2333
#
2334
sys.stderr.write("\t...sageSLZ loaded\n")
2335