Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (129,06 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_weak_coppersmith_reduced_polynomials_proj_02(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
    INPUT:
536
    
537
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
538
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
539
    - "N" -- the modulus;
540
    - "iBound" -- the bound on the first variable;
541
    - "tBound" -- the bound on the second variable.
542
    
543
    OUTPUT:
544
    
545
    A list of bivariate integer polynomial obtained using the Coppersmith
546
    algorithm. The polynomials correspond to the rows of the LLL-reduce
547
    reduced base that comply with the weak version of Coppersmith condition.
548
    """
549
    #@par Changes from runSLZ-113.sage
550
    # LLL reduction is not performed on the matrix itself but rather on the
551
    # product of the matrix with a uniform random matrix.
552
    # The reduced matrix obtained is discarded but the transformation matrix 
553
    # obtained is used to multiply the original matrix in order to reduced it.
554
    # If a sufficient level of reduction is obtained, we stop here. If not
555
    # the product matrix obtained above is LLL reduced. But as it has been
556
    # pre-reduced at the above step, reduction is supposed to be much faster.
557
    #
558
    # Arguments check.
559
    if iBound == 0 or tBound == 0:
560
        return None
561
    # End arguments check.
562
    nAtAlpha = N^alpha
563
    ## Building polynomials for matrix.
564
    polyRing   = inputPolynomial.parent()
565
    # Whatever the 2 variables are actually called, we call them
566
    # 'i' and 't' in all the variable names.
567
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
568
    #print polyVars[0], type(polyVars[0])
569
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
570
                                              tVariable:tVariable * tBound})
571
    if debug:
572
        polynomialsList = \
573
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
574
                                                 alpha,
575
                                                 N,
576
                                                 iBound,
577
                                                 tBound,
578
                                                 20)
579
    else:
580
        polynomialsList = \
581
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
582
                                                 alpha,
583
                                                 N,
584
                                                 iBound,
585
                                                 tBound,
586
                                                 0)
587
    #print "Polynomials list:", polynomialsList
588
    ## Building the proto matrix.
589
    knownMonomials = []
590
    protoMatrix    = []
591
    if debug:
592
        for poly in polynomialsList:
593
            spo_add_polynomial_coeffs_to_matrix_row(poly,
594
                                                    knownMonomials,
595
                                                    protoMatrix,
596
                                                    20)
597
    else:
598
        for poly in polynomialsList:
599
            spo_add_polynomial_coeffs_to_matrix_row(poly,
600
                                                    knownMonomials,
601
                                                    protoMatrix,
602
                                                    0)
603
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
604
    #print matrixToReduce
605
    ## Reduction and checking.
606
    ### Reduction with projection
607
    (reducedMatrixStep1, reductionMatrixStep1) = \
608
         slz_reduce_lll_proj_02(matrixToReduce,16)
609
    #print "Reduced matrix:"
610
    #print reducedMatrixStep1
611
    #for row in reducedMatrix.rows():
612
    #    print row
613
    monomialsCount     = len(knownMonomials)
614
    monomialsCountSqrt = sqrt(monomialsCount)
615
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
616
    #print reducedMatrix
617
    ## Check the Coppersmith condition for each row and build the reduced 
618
    #  polynomials.
619
    ccReducedPolynomialsList = []
620
    for row in reducedMatrixStep1.rows():
621
        l1Norm = row.norm(1)
622
        l2Norm = row.norm(2)
623
        if l2Norm * monomialsCountSqrt < l1Norm:
624
            print "l1norm is smaller than l2norm*sqrt(w)."
625
        else:
626
            print "l1norm is NOT smaller than l2norm*sqrt(w)."
627
            print (l2Norm * monomialsCountSqrt).n(), 'vs ', l1Norm.n()
628
        if l1Norm < nAtAlpha:
629
            #print l2Norm.n()
630
            ccReducedPolynomial = \
631
                slz_compute_reduced_polynomial(row,
632
                                               knownMonomials,
633
                                               iVariable,
634
                                               iBound,
635
                                               tVariable,
636
                                               tBound)
637
            if not ccReducedPolynomial is None:
638
                ccReducedPolynomialsList.append(ccReducedPolynomial)
639
        else:
640
            #print l2Norm.n() , ">", nAtAlpha
641
            pass
642
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
643
        print "Less than 2 Coppersmith condition compliant vectors."
644
        print "Extra reduction starting..."
645
        reducedMatrix = reducedMatrixStep1.LLL(algorithm='fpLLL:wrapper')
646
        ### If uncommented, the following statement avoids performing
647
        #   an actual LLL reduction. This allows for demonstrating
648
        #   the behavior of our pseudo-reduction alone.
649
        #return ()
650
    else:
651
        print "First step of reduction afforded enough vectors"
652
        return ccReducedPolynomialsList
653
    #print ccReducedPolynomialsList
654
    ## Check again the Coppersmith condition for each row and build the reduced 
655
    #  polynomials.
656
    ccReducedPolynomialsList = []
657
    for row in reducedMatrix.rows():
658
        l2Norm = row.norm(2)
659
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
660
            #print (l2Norm * monomialsCountSqrt).n()
661
            #print l2Norm.n()
662
            ccReducedPolynomial = \
663
                slz_compute_reduced_polynomial(row,
664
                                               knownMonomials,
665
                                               iVariable,
666
                                               iBound,
667
                                               tVariable,
668
                                               tBound)
669
            if not ccReducedPolynomial is None:
670
                ccReducedPolynomialsList.append(ccReducedPolynomial)
671
        else:
672
            #print l2Norm.n() , ">", nAtAlpha
673
            pass
674
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
675
        print "Less than 2 Coppersmith condition compliant vectors after extra reduction."
676
        return ()
677
    else:
678
        return ccReducedPolynomialsList
679
# End slz_compute_coppersmith_reduced_polynomials_proj_02
680
#
681
def slz_compute_coppersmith_reduced_polynomials_proj(inputPolynomial,
682
                                                     alpha,
683
                                                     N,
684
                                                     iBound,
685
                                                     tBound,
686
                                                     debug = False):
687
    """
688
    For a given set of arguments (see below), compute a list
689
    of "reduced polynomials" that could be used to compute roots
690
    of the inputPolynomial.
691
    INPUT:
692
    
693
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
694
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
695
    - "N" -- the modulus;
696
    - "iBound" -- the bound on the first variable;
697
    - "tBound" -- the bound on the second variable.
698
    
699
    OUTPUT:
700
    
701
    A list of bivariate integer polynomial obtained using the Coppersmith
702
    algorithm. The polynomials correspond to the rows of the LLL-reduce
703
    reduced base that comply with the Coppersmith condition.
704
    """
705
    #@par Changes from runSLZ-113.sage
706
    # LLL reduction is not performed on the matrix itself but rather on the
707
    # product of the matrix with a uniform random matrix.
708
    # The reduced matrix obtained is discarded but the transformation matrix 
709
    # obtained is used to multiply the original matrix in order to reduced it.
710
    # If a sufficient level of reduction is obtained, we stop here. If not
711
    # the product matrix obtained above is LLL reduced. But as it has been
712
    # pre-reduced at the above step, reduction is supposed to be much faster.
713
    #
714
    # Arguments check.
715
    if iBound == 0 or tBound == 0:
716
        return None
717
    # End arguments check.
718
    nAtAlpha = N^alpha
719
    ## Building polynomials for matrix.
720
    polyRing   = inputPolynomial.parent()
721
    # Whatever the 2 variables are actually called, we call them
722
    # 'i' and 't' in all the variable names.
723
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
724
    #print polyVars[0], type(polyVars[0])
725
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
726
                                              tVariable:tVariable * tBound})
727
    if debug:
728
        polynomialsList = \
729
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
730
                                                 alpha,
731
                                                 N,
732
                                                 iBound,
733
                                                 tBound,
734
                                                 20)
735
    else:
736
        polynomialsList = \
737
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
738
                                                 alpha,
739
                                                 N,
740
                                                 iBound,
741
                                                 tBound,
742
                                                 0)
743
    #print "Polynomials list:", polynomialsList
744
    ## Building the proto matrix.
745
    knownMonomials = []
746
    protoMatrix    = []
747
    if debug:
748
        for poly in polynomialsList:
749
            spo_add_polynomial_coeffs_to_matrix_row(poly,
750
                                                    knownMonomials,
751
                                                    protoMatrix,
752
                                                    20)
753
    else:
754
        for poly in polynomialsList:
755
            spo_add_polynomial_coeffs_to_matrix_row(poly,
756
                                                    knownMonomials,
757
                                                    protoMatrix,
758
                                                    0)
759
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
760
    #print matrixToReduce
761
    ## Reduction and checking.
762
    ### Reduction with projection
763
    (reducedMatrixStep1, reductionMatrixStep1) = \
764
         slz_reduce_lll_proj(matrixToReduce,16)
765
    #print "Reduced matrix:"
766
    #print reducedMatrixStep1
767
    #for row in reducedMatrix.rows():
768
    #    print row
769
    monomialsCount     = len(knownMonomials)
770
    monomialsCountSqrt = sqrt(monomialsCount)
771
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
772
    #print reducedMatrix
773
    ## Check the Coppersmith condition for each row and build the reduced 
774
    #  polynomials.
775
    ccReducedPolynomialsList = []
776
    for row in reducedMatrixStep1.rows():
777
        l2Norm = row.norm(2)
778
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
779
            #print (l2Norm * monomialsCountSqrt).n()
780
            #print l2Norm.n()
781
            ccReducedPolynomial = \
782
                slz_compute_reduced_polynomial(row,
783
                                               knownMonomials,
784
                                               iVariable,
785
                                               iBound,
786
                                               tVariable,
787
                                               tBound)
788
            if not ccReducedPolynomial is None:
789
                ccReducedPolynomialsList.append(ccReducedPolynomial)
790
        else:
791
            #print l2Norm.n() , ">", nAtAlpha
792
            pass
793
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
794
        print "Less than 2 Coppersmith condition compliant vectors."
795
        print "Extra reduction starting..."
796
        reducedMatrix = reducedMatrixStep1.LLL(algorithm='fpLLL:wrapper')
797
        ### If uncommented, the following statement avoids performing
798
        #   an actual LLL reduction. This allows for demonstrating
799
        #   the behavior of our pseudo-reduction alone.
800
        #return ()
801
    else:
802
        print "First step of reduction afforded enough vectors"
803
        return ccReducedPolynomialsList
804
    #print ccReducedPolynomialsList
805
    ## Check again the Coppersmith condition for each row and build the reduced 
806
    #  polynomials.
807
    ccReducedPolynomialsList = []
808
    for row in reducedMatrix.rows():
809
        l2Norm = row.norm(2)
810
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
811
            #print (l2Norm * monomialsCountSqrt).n()
812
            #print l2Norm.n()
813
            ccReducedPolynomial = \
814
                slz_compute_reduced_polynomial(row,
815
                                               knownMonomials,
816
                                               iVariable,
817
                                               iBound,
818
                                               tVariable,
819
                                               tBound)
820
            if not ccReducedPolynomial is None:
821
                ccReducedPolynomialsList.append(ccReducedPolynomial)
822
        else:
823
            #print l2Norm.n() , ">", nAtAlpha
824
            pass
825
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
826
        print "Less than 2 Coppersmith condition compliant vectors after extra reduction."
827
        return ()
828
    else:
829
        return ccReducedPolynomialsList
830
# End slz_compute_coppersmith_reduced_polynomials_proj
831
def slz_compute_weak_coppersmith_reduced_polynomials_proj(inputPolynomial,
832
                                                          alpha,
833
                                                          N,
834
                                                          iBound,
835
                                                          tBound,
836
                                                          debug = False):
837
    """
838
    For a given set of arguments (see below), compute a list
839
    of "reduced polynomials" that could be used to compute roots
840
    of the inputPolynomial.
841
    INPUT:
842
    
843
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
844
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
845
    - "N" -- the modulus;
846
    - "iBound" -- the bound on the first variable;
847
    - "tBound" -- the bound on the second variable.
848
    
849
    OUTPUT:
850
    
851
    A list of bivariate integer polynomial obtained using the Coppersmith
852
    algorithm. The polynomials correspond to the rows of the LLL-reduce
853
    reduced base that comply with the weak version of Coppersmith condition.
854
    """
855
    #@par Changes from runSLZ-113.sage
856
    # LLL reduction is not performed on the matrix itself but rather on the
857
    # product of the matrix with a uniform random matrix.
858
    # The reduced matrix obtained is discarded but the transformation matrix 
859
    # obtained is used to multiply the original matrix in order to reduced it.
860
    # If a sufficient level of reduction is obtained, we stop here. If not
861
    # the product matrix obtained above is LLL reduced. But as it has been
862
    # pre-reduced at the above step, reduction is supposed to be much faster.
863
    #
864
    # Arguments check.
865
    if iBound == 0 or tBound == 0:
866
        return None
867
    # End arguments check.
868
    nAtAlpha = N^alpha
869
    ## Building polynomials for matrix.
870
    polyRing   = inputPolynomial.parent()
871
    # Whatever the 2 variables are actually called, we call them
872
    # 'i' and 't' in all the variable names.
873
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
874
    #print polyVars[0], type(polyVars[0])
875
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
876
                                              tVariable:tVariable * tBound})
877
    if debug:
878
        polynomialsList = \
879
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
880
                                                 alpha,
881
                                                 N,
882
                                                 iBound,
883
                                                 tBound,
884
                                                 20)
885
    else:
886
        polynomialsList = \
887
            spo_polynomial_to_polynomials_list_8(initialPolynomial,
888
                                                 alpha,
889
                                                 N,
890
                                                 iBound,
891
                                                 tBound,
892
                                                 0)
893
    #print "Polynomials list:", polynomialsList
894
    ## Building the proto matrix.
895
    knownMonomials = []
896
    protoMatrix    = []
897
    if debug:
898
        for poly in polynomialsList:
899
            spo_add_polynomial_coeffs_to_matrix_row(poly,
900
                                                    knownMonomials,
901
                                                    protoMatrix,
902
                                                    20)
903
    else:
904
        for poly in polynomialsList:
905
            spo_add_polynomial_coeffs_to_matrix_row(poly,
906
                                                    knownMonomials,
907
                                                    protoMatrix,
908
                                                    0)
909
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
910
    #print matrixToReduce
911
    ## Reduction and checking.
912
    ### Reduction with projection
913
    (reducedMatrixStep1, reductionMatrixStep1) = \
914
         slz_reduce_lll_proj(matrixToReduce,16)
915
    #print "Reduced matrix:"
916
    #print reducedMatrixStep1
917
    #for row in reducedMatrix.rows():
918
    #    print row
919
    monomialsCount     = len(knownMonomials)
920
    monomialsCountSqrt = sqrt(monomialsCount)
921
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
922
    #print reducedMatrix
923
    ## Check the Coppersmith condition for each row and build the reduced 
924
    #  polynomials.
925
    ccReducedPolynomialsList = []
926
    for row in reducedMatrixStep1.rows():
927
        l1Norm = row.norm(1)
928
        l2Norm = row.norm(2)
929
        if l2Norm * monomialsCountSqrt < l1Norm:
930
            print "l1norm is smaller than l2norm*sqrt(w)."
931
        else:
932
            print "l1norm is NOT smaller than l2norm*sqrt(w)."
933
            print (l2Norm * monomialsCountSqrt).n(), 'vs ', l1Norm.n()
934
        if l1Norm < nAtAlpha:
935
            #print l2Norm.n()
936
            ccReducedPolynomial = \
937
                slz_compute_reduced_polynomial(row,
938
                                               knownMonomials,
939
                                               iVariable,
940
                                               iBound,
941
                                               tVariable,
942
                                               tBound)
943
            if not ccReducedPolynomial is None:
944
                ccReducedPolynomialsList.append(ccReducedPolynomial)
945
        else:
946
            #print l2Norm.n() , ">", nAtAlpha
947
            pass
948
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
949
        print "Less than 2 Coppersmith condition compliant vectors."
950
        print "Extra reduction starting..."
951
        reducedMatrix = reducedMatrixStep1.LLL(algorithm='fpLLL:wrapper')
952
        ### If uncommented, the following statement avoids performing
953
        #   an actual LLL reduction. This allows for demonstrating
954
        #   the behavior of our pseudo-reduction alone.
955
        #return ()
956
    else:
957
        print "First step of reduction afforded enough vectors"
958
        return ccReducedPolynomialsList
959
    #print ccReducedPolynomialsList
960
    ## Check again the Coppersmith condition for each row and build the reduced 
961
    #  polynomials.
962
    ccReducedPolynomialsList = []
963
    for row in reducedMatrix.rows():
964
        l2Norm = row.norm(2)
965
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
966
            #print (l2Norm * monomialsCountSqrt).n()
967
            #print l2Norm.n()
968
            ccReducedPolynomial = \
969
                slz_compute_reduced_polynomial(row,
970
                                               knownMonomials,
971
                                               iVariable,
972
                                               iBound,
973
                                               tVariable,
974
                                               tBound)
975
            if not ccReducedPolynomial is None:
976
                ccReducedPolynomialsList.append(ccReducedPolynomial)
977
        else:
978
            #print l2Norm.n() , ">", nAtAlpha
979
            pass
980
    if len(ccReducedPolynomialsList) < 2: # Insufficient reduction.
981
        print "Less than 2 Coppersmith condition compliant vectors after extra reduction."
982
        return ()
983
    else:
984
        return ccReducedPolynomialsList
985
# End slz_compute_coppersmith_reduced_polynomials_proj2
986
#
987
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
988
                                                                    alpha,
989
                                                                    N,
990
                                                                    iBound,
991
                                                                    tBound,
992
                                                                    debug = False):
993
    """
994
    For a given set of arguments (see below), compute a list
995
    of "reduced polynomials" that could be used to compute roots
996
    of the inputPolynomial.
997
    Print the volume of the initial basis as well.
998
    INPUT:
999
    
1000
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
1001
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
1002
    - "N" -- the modulus;
1003
    - "iBound" -- the bound on the first variable;
1004
    - "tBound" -- the bound on the second variable.
1005
    
1006
    OUTPUT:
1007
    
1008
    A list of bivariate integer polynomial obtained using the Coppersmith
1009
    algorithm. The polynomials correspond to the rows of the LLL-reduce
1010
    reduced base that comply with the Coppersmith condition.
1011
    """
1012
    # Arguments check.
1013
    if iBound == 0 or tBound == 0:
1014
        return None
1015
    # End arguments check.
1016
    nAtAlpha = N^alpha
1017
    if debug:
1018
        print "N at alpha:", nAtAlpha
1019
    ## Building polynomials for matrix.
1020
    polyRing   = inputPolynomial.parent()
1021
    # Whatever the 2 variables are actually called, we call them
1022
    # 'i' and 't' in all the variable names.
1023
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
1024
    #print polyVars[0], type(polyVars[0])
1025
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
1026
                                              tVariable:tVariable * tBound})
1027
##    polynomialsList = \
1028
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
1029
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
1030
    polynomialsList = \
1031
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
1032
                                             alpha,
1033
                                             N,
1034
                                             iBound,
1035
                                             tBound,
1036
                                             0)
1037
    #print "Polynomials list:", polynomialsList
1038
    ## Building the proto matrix.
1039
    knownMonomials = []
1040
    protoMatrix    = []
1041
    for poly in polynomialsList:
1042
        spo_add_polynomial_coeffs_to_matrix_row(poly,
1043
                                                knownMonomials,
1044
                                                protoMatrix,
1045
                                                0)
1046
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
1047
    matrixToReduceTranspose = matrixToReduce.transpose()
1048
    squareMatrix = matrixToReduce * matrixToReduceTranspose
1049
    squareMatDet = det(squareMatrix)
1050
    latticeVolume = sqrt(squareMatDet)
1051
    print "Lattice volume:", latticeVolume.n()
1052
    print "Lattice volume / N:", (latticeVolume/N).n()
1053
    #print matrixToReduce
1054
    ## Reduction and checking.
1055
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
1056
    #  error message issued when previous code was used.
1057
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
1058
    reductionTimeStart = cputime() 
1059
    reducedMatrix = matrixToReduce.LLL(fp=None)
1060
    reductionTime = cputime(reductionTimeStart)
1061
    print "Reduction time:", reductionTime
1062
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
1063
    if not isLLLReduced:
1064
       return None
1065
    #
1066
    if debug:
1067
        matrixFile = file('/tmp/reducedMatrix.txt', 'w')
1068
        for row in reducedMatrix.rows():
1069
            matrixFile.write(str(row) + "\n")
1070
        matrixFile.close()
1071
    #
1072
    monomialsCount     = len(knownMonomials)
1073
    monomialsCountSqrt = sqrt(monomialsCount)
1074
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
1075
    #print reducedMatrix
1076
    ## Check the Coppersmith condition for each row and build the reduced 
1077
    #  polynomials.
1078
    ccVectorsCount = 0
1079
    ccReducedPolynomialsList = []
1080
    for row in reducedMatrix.rows():
1081
        l2Norm = row.norm(2)
1082
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
1083
            #print (l2Norm * monomialsCountSqrt).n()
1084
            #print l2Norm.n()
1085
            ccVectorsCount +=1            
1086
            ccReducedPolynomial = \
1087
                slz_compute_reduced_polynomial(row,
1088
                                               knownMonomials,
1089
                                               iVariable,
1090
                                               iBound,
1091
                                               tVariable,
1092
                                               tBound)
1093
            if not ccReducedPolynomial is None:
1094
                ccReducedPolynomialsList.append(ccReducedPolynomial)
1095
        else:
1096
            #print l2Norm.n() , ">", nAtAlpha
1097
            pass
1098
    if debug:
1099
        print ccVectorsCount, "out of ", len(ccReducedPolynomialsList), 
1100
        print "took Coppersmith text."
1101
    if len(ccReducedPolynomialsList) < 2:
1102
        print "Less than 2 Coppersmith condition compliant vectors."
1103
        return ()
1104
    if debug:
1105
        print "Reduced and Coppersmith compliant polynomials list", ccReducedPolynomialsList
1106
    return ccReducedPolynomialsList
1107
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
1108

    
1109
def slz_compute_initial_lattice_matrix(inputPolynomial,
1110
                                       alpha,
1111
                                       N,
1112
                                       iBound,
1113
                                       tBound,
1114
                                       debug = False):
1115
    """
1116
    For a given set of arguments (see below), compute the initial lattice
1117
    that could be reduced. 
1118
    INPUT:
1119
    
1120
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
1121
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
1122
    - "N" -- the modulus;
1123
    - "iBound" -- the bound on the first variable;
1124
    - "tBound" -- the bound on the second variable.
1125
    
1126
    OUTPUT:
1127
    
1128
    A list of bivariate integer polynomial obtained using the Coppersmith
1129
    algorithm. The polynomials correspond to the rows of the LLL-reduce
1130
    reduced base that comply with the Coppersmith condition.
1131
    """
1132
    # Arguments check.
1133
    if iBound == 0 or tBound == 0:
1134
        return None
1135
    # End arguments check.
1136
    nAtAlpha = N^alpha
1137
    ## Building polynomials for matrix.
1138
    polyRing   = inputPolynomial.parent()
1139
    # Whatever the 2 variables are actually called, we call them
1140
    # 'i' and 't' in all the variable names.
1141
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
1142
    #print polyVars[0], type(polyVars[0])
1143
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
1144
                                              tVariable:tVariable * tBound})
1145
    polynomialsList = \
1146
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
1147
                                             alpha,
1148
                                             N,
1149
                                             iBound,
1150
                                             tBound,
1151
                                             0)
1152
    #print "Polynomials list:", polynomialsList
1153
    ## Building the proto matrix.
1154
    knownMonomials = []
1155
    protoMatrix    = []
1156
    for poly in polynomialsList:
1157
        spo_add_polynomial_coeffs_to_matrix_row(poly,
1158
                                                knownMonomials,
1159
                                                protoMatrix,
1160
                                                0)
1161
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
1162
    if debug:
1163
        print "Initial basis polynomials"
1164
        for poly in polynomialsList:
1165
            print poly
1166
    return matrixToReduce
1167
# End slz_compute_initial_lattice_matrix.
1168

    
1169
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
1170
                                                 alpha,
1171
                                                 N,
1172
                                                 iBound,
1173
                                                 tBound):
1174
    """
1175
    For a given set of arguments (see below), compute the polynomial modular 
1176
    roots, if any.
1177
    
1178
    """
1179
    # Arguments check.
1180
    if iBound == 0 or tBound == 0:
1181
        return set()
1182
    # End arguments check.
1183
    nAtAlpha = N^alpha
1184
    ## Building polynomials for matrix.
1185
    polyRing   = inputPolynomial.parent()
1186
    # Whatever the 2 variables are actually called, we call them
1187
    # 'i' and 't' in all the variable names.
1188
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
1189
    ccReducedPolynomialsList = \
1190
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
1191
                                                     alpha,
1192
                                                     N,
1193
                                                     iBound,
1194
                                                     tBound)
1195
    if len(ccReducedPolynomialsList) == 0:
1196
        return set()   
1197
    ## Create the valid (poly1 and poly2 are algebraically independent) 
1198
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
1199
    # Try to mix and match all the polynomial pairs built from the 
1200
    # ccReducedPolynomialsList to obtain non zero resultants.
1201
    resultantsInITuplesList = []
1202
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
1203
        for polyInnerIndex in xrange(polyOuterIndex+1, 
1204
                                     len(ccReducedPolynomialsList)):
1205
            # Compute the resultant in resultants in the
1206
            # first variable (is it the optimal choice?).
1207
            resultantInI = \
1208
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
1209
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
1210
            #print "Resultant", resultantInI
1211
            # Test algebraic independence.
1212
            if not resultantInI.is_zero():
1213
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
1214
                                                 ccReducedPolynomialsList[polyInnerIndex],
1215
                                                 resultantInI))
1216
    # If no non zero resultant was found: we can't get no algebraically 
1217
    # independent polynomials pair. Give up!
1218
    if len(resultantsInITuplesList) == 0:
1219
        return set()
1220
    #print resultantsInITuplesList
1221
    # Compute the roots.
1222
    Zi = ZZ[str(iVariable)]
1223
    Zt = ZZ[str(tVariable)]
1224
    polynomialRootsSet = set()
1225
    # First, solve in the second variable since resultants are in the first
1226
    # variable.
1227
    for resultantInITuple in resultantsInITuplesList:
1228
        tRootsList = Zt(resultantInITuple[2]).roots()
1229
        # For each tRoot, compute the corresponding iRoots and check 
1230
        # them in the input polynomial.
1231
        for tRoot in tRootsList:
1232
            #print "tRoot:", tRoot
1233
            # Roots returned by root() are (value, multiplicity) tuples.
1234
            iRootsList = \
1235
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
1236
            print iRootsList
1237
            # The iRootsList can be empty, hence the test.
1238
            if len(iRootsList) != 0:
1239
                for iRoot in iRootsList:
1240
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
1241
                    # polyEvalModN must be an integer.
1242
                    if polyEvalModN == int(polyEvalModN):
1243
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
1244
    return polynomialRootsSet
1245
# End slz_compute_integer_polynomial_modular_roots.
1246
#
1247
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
1248
                                                 alpha,
1249
                                                 N,
1250
                                                 iBound,
1251
                                                 tBound):
1252
    """
1253
    For a given set of arguments (see below), compute the polynomial modular 
1254
    roots, if any.
1255
    This version differs in the way resultants are computed.   
1256
    """
1257
    # Arguments check.
1258
    if iBound == 0 or tBound == 0:
1259
        return set()
1260
    # End arguments check.
1261
    nAtAlpha = N^alpha
1262
    ## Building polynomials for matrix.
1263
    polyRing   = inputPolynomial.parent()
1264
    # Whatever the 2 variables are actually called, we call them
1265
    # 'i' and 't' in all the variable names.
1266
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
1267
    #print polyVars[0], type(polyVars[0])
1268
    ccReducedPolynomialsList = \
1269
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
1270
                                                     alpha,
1271
                                                     N,
1272
                                                     iBound,
1273
                                                     tBound)
1274
    if len(ccReducedPolynomialsList) == 0:
1275
        return set()   
1276
    ## Create the valid (poly1 and poly2 are algebraically independent) 
1277
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
1278
    # Try to mix and match all the polynomial pairs built from the 
1279
    # ccReducedPolynomialsList to obtain non zero resultants.
1280
    resultantsInTTuplesList = []
1281
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
1282
        for polyInnerIndex in xrange(polyOuterIndex+1, 
1283
                                     len(ccReducedPolynomialsList)):
1284
            # Compute the resultant in resultants in the
1285
            # first variable (is it the optimal choice?).
1286
            resultantInT = \
1287
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
1288
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
1289
            #print "Resultant", resultantInT
1290
            # Test algebraic independence.
1291
            if not resultantInT.is_zero():
1292
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
1293
                                                 ccReducedPolynomialsList[polyInnerIndex],
1294
                                                 resultantInT))
1295
    # If no non zero resultant was found: we can't get no algebraically 
1296
    # independent polynomials pair. Give up!
1297
    if len(resultantsInTTuplesList) == 0:
1298
        return set()
1299
    #print resultantsInITuplesList
1300
    # Compute the roots.
1301
    Zi = ZZ[str(iVariable)]
1302
    Zt = ZZ[str(tVariable)]
1303
    polynomialRootsSet = set()
1304
    # First, solve in the second variable since resultants are in the first
1305
    # variable.
1306
    for resultantInTTuple in resultantsInTTuplesList:
1307
        iRootsList = Zi(resultantInTTuple[2]).roots()
1308
        # For each iRoot, compute the corresponding tRoots and check 
1309
        # them in the input polynomial.
1310
        for iRoot in iRootsList:
1311
            #print "iRoot:", iRoot
1312
            # Roots returned by root() are (value, multiplicity) tuples.
1313
            tRootsList = \
1314
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
1315
            print tRootsList
1316
            # The tRootsList can be empty, hence the test.
1317
            if len(tRootsList) != 0:
1318
                for tRoot in tRootsList:
1319
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
1320
                    # polyEvalModN must be an integer.
1321
                    if polyEvalModN == int(polyEvalModN):
1322
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
1323
    return polynomialRootsSet
1324
# End slz_compute_integer_polynomial_modular_roots_2.
1325
#
1326
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
1327
                                        upperBoundSa, approxAccurSa, 
1328
                                        precSa=None, debug=False):
1329
    """
1330
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
1331
    a polynomial that approximates the function on a an interval starting
1332
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1333
    approximates with the expected precision.
1334
    The interval upper bound is lowered until the expected approximation 
1335
    precision is reached.
1336
    The polynomial, the bounds, the center of the interval and the error
1337
    are returned.
1338
    Argument debug is not used.
1339
    OUTPUT:
1340
    A tuple made of 4 Sollya objects:
1341
    - a polynomial;
1342
    - an range (an interval, not in the sense of number given as an interval);
1343
    - the center of the interval;
1344
    - the maximum error in the approximation of the input functionSo by the
1345
      output polynomial ; this error <= approxAccurSaS.
1346
    
1347
    """
1348
    #print"In slz_compute_polynomial_and_interval..."
1349
    ## Superficial argument check.
1350
    if lowerBoundSa > upperBoundSa:
1351
        return None
1352
    ## Change Sollya precision, if requested.
1353
    if precSa is None:
1354
        precSa = ceil((RR('1.5') * abs(RR(approxAccurSa).log2())) / 64) * 64
1355
        #print "Computed internal precision:", precSa
1356
        if precSa < 192:
1357
            precSa = 192
1358
    sollyaPrecChanged = False
1359
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1360
    if precSa > initialSollyaPrecSa:
1361
        if precSa <= 2:
1362
            print inspect.stack()[0][3], ": precision change <=2 requested."
1363
        pobyso_set_prec_sa_so(precSa)
1364
        sollyaPrecChanged = True
1365
    RRR = lowerBoundSa.parent()
1366
    intervalShrinkConstFactorSa = RRR('0.9')
1367
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1368
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1369
    currentUpperBoundSa = upperBoundSa
1370
    currentLowerBoundSa = lowerBoundSa
1371
    # What we want here is the polynomial without the variable change, 
1372
    # since our actual variable will be x-intervalCenter defined over the 
1373
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
1374
    (polySo, intervalCenterSo, maxErrorSo) = \
1375
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1376
                                                    currentRangeSo, 
1377
                                                    absoluteErrorTypeSo)
1378
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1379
    while maxErrorSa > approxAccurSa:
1380
        print "++Approximation error:", maxErrorSa.n()
1381
        sollya_lib_clear_obj(polySo)
1382
        sollya_lib_clear_obj(intervalCenterSo)
1383
        sollya_lib_clear_obj(maxErrorSo)
1384
        # Very empirical shrinking factor.
1385
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1386
        print "Shrink factor:", \
1387
              shrinkFactorSa.n(), \
1388
              intervalShrinkConstFactorSa
1389
        print
1390
        #errorRatioSa = approxAccurSa/maxErrorSa
1391
        #print "Error ratio: ", errorRatioSa
1392
        # Make sure interval shrinks.
1393
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1394
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1395
            #print "Fixed"
1396
        else:
1397
            actualShrinkFactorSa = shrinkFactorSa
1398
            #print "Computed",shrinkFactorSa,maxErrorSa
1399
            #print shrinkFactorSa, maxErrorSa
1400
        #print "Shrink factor", actualShrinkFactorSa
1401
        currentUpperBoundSa = currentLowerBoundSa + \
1402
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1403
                                actualShrinkFactorSa
1404
        #print "Current upper bound:", currentUpperBoundSa
1405
        sollya_lib_clear_obj(currentRangeSo)
1406
        # Check what is left with the bounds.
1407
        if currentUpperBoundSa <= currentLowerBoundSa or \
1408
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1409
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1410
            print "Can't find an interval."
1411
            print "Use either or both a higher polynomial degree or a higher",
1412
            print "internal precision."
1413
            print "Aborting!"
1414
            if sollyaPrecChanged:
1415
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1416
            sollya_lib_clear_obj(initialSollyaPrecSo)
1417
            return None
1418
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1419
                                                      currentUpperBoundSa)
1420
        # print "New interval:",
1421
        # pobyso_autoprint(currentRangeSo)
1422
        #print "Second Taylor expansion call."
1423
        (polySo, intervalCenterSo, maxErrorSo) = \
1424
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1425
                                                        currentRangeSo, 
1426
                                                        absoluteErrorTypeSo)
1427
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1428
        #print "Max errorSo:",
1429
        #pobyso_autoprint(maxErrorSo)
1430
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1431
        #print "Max errorSa:", maxErrorSa
1432
        #print "Sollya prec:",
1433
        #pobyso_autoprint(sollya_lib_get_prec(None))
1434
    # End while
1435
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1436
    if sollyaPrecChanged:
1437
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1438
    sollya_lib_clear_obj(initialSollyaPrecSo)
1439
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
1440
# End slz_compute_polynomial_and_interval
1441

    
1442
def slz_compute_polynomial_and_interval_01(functionSo, degreeSo, lowerBoundSa, 
1443
                                        upperBoundSa, approxAccurSa, 
1444
                                        sollyaPrecSa=None, debug=False):
1445
    """
1446
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
1447
    a polynomial that approximates the function on a an interval starting
1448
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1449
    approximates with the expected precision.
1450
    The interval upper bound is lowered until the expected approximation 
1451
    precision is reached.
1452
    The polynomial, the bounds, the center of the interval and the error
1453
    are returned.
1454
    OUTPUT:
1455
    A tuple made of 4 Sollya objects:
1456
    - a polynomial;
1457
    - an range (an interval, not in the sense of number given as an interval);
1458
    - the center of the interval;
1459
    - the maximum error in the approximation of the input functionSo by the
1460
      output polynomial ; this error <= approxAccurSaS.
1461
      
1462
    Changes from version 0 (no number):
1463
    - make use of debug argument;
1464
    - polynomial coefficients are "shaved".
1465
    
1466
    """
1467
    #print"In slz_compute_polynomial_and_interval..."
1468
    ## Superficial argument check.
1469
    if lowerBoundSa > upperBoundSa:
1470
        print inspect.stack()[0][3], ": lower bound is larger than upper bound. "
1471
        return None
1472
    ## Change Sollya precision, if requested.
1473
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1474
    sollyaPrecChangedSa = False
1475
    if sollyaPrecSa is None:
1476
        sollyaPrecSa = initialSollyaPrecSa
1477
    else:
1478
        if sollyaPrecSa > initialSollyaPrecSa:
1479
            if sollyaPrecSa <= 2:
1480
                print inspect.stack()[0][3], ": precision change <= 2 requested."
1481
            pobyso_set_prec_sa_so(sollyaPrecSa)
1482
            sollyaPrecChangedSa = True
1483
    ## Other initializations and data recovery.
1484
    RRR = lowerBoundSa.parent()
1485
    intervalShrinkConstFactorSa = RRR('0.9')
1486
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1487
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1488
    currentUpperBoundSa = upperBoundSa
1489
    currentLowerBoundSa = lowerBoundSa
1490
    # What we want here is the polynomial without the variable change, 
1491
    # since our actual variable will be x-intervalCenter defined over the 
1492
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
1493
    (polySo, intervalCenterSo, maxErrorSo) = \
1494
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1495
                                                    currentRangeSo, 
1496
                                                    absoluteErrorTypeSo)
1497
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1498
    ## Expression "approxAccurSa/2" (instead of just approxAccurSa) is added 
1499
    #  since we use polynomial shaving. In order for it to work, we must start 
1500
    #  with a better accuracy.
1501
    while maxErrorSa > approxAccurSa/2:
1502
        print "++Approximation error:", maxErrorSa.n()
1503
        sollya_lib_clear_obj(polySo)
1504
        sollya_lib_clear_obj(intervalCenterSo)
1505
        sollya_lib_clear_obj(maxErrorSo)
1506
        # Very empirical shrinking factor.
1507
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1508
        print "Shrink factor:", \
1509
              shrinkFactorSa.n(), \
1510
              intervalShrinkConstFactorSa
1511
        print
1512
        #errorRatioSa = approxAccurSa/maxErrorSa
1513
        #print "Error ratio: ", errorRatioSa
1514
        # Make sure interval shrinks.
1515
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1516
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1517
            #print "Fixed"
1518
        else:
1519
            actualShrinkFactorSa = shrinkFactorSa
1520
            #print "Computed",shrinkFactorSa,maxErrorSa
1521
            #print shrinkFactorSa, maxErrorSa
1522
        #print "Shrink factor", actualShrinkFactorSa
1523
        currentUpperBoundSa = currentLowerBoundSa + \
1524
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1525
                                actualShrinkFactorSa
1526
        #print "Current upper bound:", currentUpperBoundSa
1527
        sollya_lib_clear_obj(currentRangeSo)
1528
        # Check what is left with the bounds.
1529
        if currentUpperBoundSa <= currentLowerBoundSa or \
1530
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1531
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1532
            print "Can't find an interval."
1533
            print "Use either or both a higher polynomial degree or a higher",
1534
            print "internal precision."
1535
            print "Aborting!"
1536
            if sollyaPrecChangedSa:
1537
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1538
            sollya_lib_clear_obj(initialSollyaPrecSo)
1539
            return None
1540
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1541
                                                      currentUpperBoundSa)
1542
        # print "New interval:",
1543
        # pobyso_autoprint(currentRangeSo)
1544
        #print "Second Taylor expansion call."
1545
        (polySo, intervalCenterSo, maxErrorSo) = \
1546
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1547
                                                        currentRangeSo, 
1548
                                                        absoluteErrorTypeSo)
1549
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1550
        #print "Max errorSo:",
1551
        #pobyso_autoprint(maxErrorSo)
1552
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1553
        #print "Max errorSa:", maxErrorSa
1554
        #print "Sollya prec:",
1555
        #pobyso_autoprint(sollya_lib_get_prec(None))
1556
    # End while
1557
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1558
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1559
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1560
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1561
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1562
    if debug:
1563
        print inspect.stack()[0][3], "SollyaPrecSa:", sollyaPrecSa
1564
        print "About to call polynomial rounding with:"
1565
        print "polySo: ",           ; pobyso_autoprint(polySo)
1566
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
1567
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1568
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1569
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
1570
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1571
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1572
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1573
    """
1574
    # Naive rounding.
1575
    (roundedPolySo, roundedPolyMaxErrSo) = \
1576
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
1577
                                                           functionSo,
1578
                                                           intervalCenterSo,
1579
                                                           currentRangeSo,
1580
                                                           itpSo,
1581
                                                           ftpSo,
1582
                                                           maxPrecSo,
1583
                                                           approxAccurSo)
1584
    """
1585
    # Proved rounding.
1586
    (roundedPolySo, roundedPolyMaxErrSo) = \
1587
        pobyso_round_coefficients_progressive_so_so(polySo, 
1588
                                                    functionSo,
1589
                                                    maxPrecSo,
1590
                                                    currentRangeSo,
1591
                                                    intervalCenterSo,
1592
                                                    maxErrorSo,
1593
                                                    approxAccurSo,
1594
                                                    debug=False)
1595
    #### Comment out the two next lines when polynomial rounding is activated.
1596
    #roundedPolySo = sollya_lib_copy_obj(polySo)
1597
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
1598
    sollya_lib_clear_obj(polySo)
1599
    sollya_lib_clear_obj(maxErrorSo)
1600
    sollya_lib_clear_obj(itpSo)
1601
    sollya_lib_clear_obj(ftpSo)
1602
    sollya_lib_clear_obj(approxAccurSo)
1603
    if sollyaPrecChangedSa:
1604
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1605
    sollya_lib_clear_obj(initialSollyaPrecSo)
1606
    if debug:
1607
        print "1: ", ; pobyso_autoprint(roundedPolySo)
1608
        print "2: ", ; pobyso_autoprint(currentRangeSo)
1609
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
1610
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1611
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1612
# End slz_compute_polynomial_and_interval_01
1613

    
1614
def slz_compute_polynomial_and_interval_02(functionSo, degreeSo, lowerBoundSa, 
1615
                                        upperBoundSa, approxAccurSa, 
1616
                                        sollyaPrecSa=None, debug=True ):
1617
    """
1618
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
1619
    a polynomial that approximates the function on a an interval starting
1620
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
1621
    approximates with the expected precision.
1622
    The interval upper bound is lowered until the expected approximation 
1623
    precision is reached.
1624
    The polynomial, the bounds, the center of the interval and the error
1625
    are returned.
1626
    OUTPUT:
1627
    A tuple made of 4 Sollya objects:
1628
    - a polynomial;
1629
    - an range (an interval, not in the sense of number given as an interval);
1630
    - the center of the interval;
1631
    - the maximum error in the approximation of the input functionSo by the
1632
      output polynomial ; this error <= approxAccurSaS.
1633
      
1634
    Changes fom v 01:
1635
        extra verbose.
1636
    """
1637
    print"In slz_compute_polynomial_and_interval..."
1638
    ## Superficial argument check.
1639
    if lowerBoundSa > upperBoundSa:
1640
        return None
1641
    ## Change Sollya precision, if requested.
1642
    sollyaPrecChanged = False
1643
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
1644
    #print "Initial Sollya prec:", initialSollyaPrecSa, type(initialSollyaPrecSa)
1645
    if sollyaPrecSa is None:
1646
        sollyaPrecSa = initialSollyaPrecSa
1647
    else:
1648
        if sollyaPrecSa <= 2:
1649
            print inspect.stack()[0][3], ": precision change <=2 requested."
1650
        pobyso_set_prec_sa_so(sollyaPrecSa)
1651
        sollyaPrecChanged = True
1652
    RRR = lowerBoundSa.parent()
1653
    intervalShrinkConstFactorSa = RRR('0.9')
1654
    absoluteErrorTypeSo = pobyso_absolute_so_so()
1655
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
1656
    currentUpperBoundSa = upperBoundSa
1657
    currentLowerBoundSa = lowerBoundSa
1658
    #pobyso_autoprint(functionSo)
1659
    #pobyso_autoprint(degreeSo)
1660
    #pobyso_autoprint(currentRangeSo)
1661
    #pobyso_autoprint(absoluteErrorTypeSo)
1662
    ## What we want here is the polynomial without the variable change, 
1663
    #  since our actual variable will be x-intervalCenter defined over the 
1664
    #  domain [lowerBound-intervalCenter , upperBound-intervalCenter].
1665
    (polySo, intervalCenterSo, maxErrorSo) = \
1666
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1667
                                                    currentRangeSo, 
1668
                                                    absoluteErrorTypeSo)
1669
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1670
    print "...after Taylor expansion."
1671
    ## Expression "approxAccurSa/2" (instead of just approxAccurSa) is added 
1672
    #  since we use polynomial shaving. In order for it to work, we must start 
1673
    #  with a better accuracy.
1674
    while maxErrorSa > approxAccurSa/2:
1675
        print "++Approximation error:", maxErrorSa.n()
1676
        sollya_lib_clear_obj(polySo)
1677
        sollya_lib_clear_obj(intervalCenterSo)
1678
        sollya_lib_clear_obj(maxErrorSo)
1679
        # Very empirical shrinking factor.
1680
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1681
        print "Shrink factor:", \
1682
              shrinkFactorSa.n(), \
1683
              intervalShrinkConstFactorSa
1684
        print
1685
        #errorRatioSa = approxAccurSa/maxErrorSa
1686
        #print "Error ratio: ", errorRatioSa
1687
        # Make sure interval shrinks.
1688
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1689
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1690
            #print "Fixed"
1691
        else:
1692
            actualShrinkFactorSa = shrinkFactorSa
1693
            #print "Computed",shrinkFactorSa,maxErrorSa
1694
            #print shrinkFactorSa, maxErrorSa
1695
        #print "Shrink factor", actualShrinkFactorSa
1696
        currentUpperBoundSa = currentLowerBoundSa + \
1697
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1698
                                actualShrinkFactorSa
1699
        #print "Current upper bound:", currentUpperBoundSa
1700
        sollya_lib_clear_obj(currentRangeSo)
1701
        # Check what is left with the bounds.
1702
        if currentUpperBoundSa <= currentLowerBoundSa or \
1703
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1704
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1705
            print "Can't find an interval."
1706
            print "Use either or both a higher polynomial degree or a higher",
1707
            print "internal precision."
1708
            print "Aborting!"
1709
            if sollyaPrecChanged:
1710
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1711
            sollya_lib_clear_obj(initialSollyaPrecSo)
1712
            return None
1713
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1714
                                                      currentUpperBoundSa)
1715
        # print "New interval:",
1716
        # pobyso_autoprint(currentRangeSo)
1717
        #print "Second Taylor expansion call."
1718
        (polySo, intervalCenterSo, maxErrorSo) = \
1719
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1720
                                                        currentRangeSo, 
1721
                                                        absoluteErrorTypeSo)
1722
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1723
        #print "Max errorSo:",
1724
        #pobyso_autoprint(maxErrorSo)
1725
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1726
        #print "Max errorSa:", maxErrorSa
1727
        #print "Sollya prec:",
1728
        #pobyso_autoprint(sollya_lib_get_prec(None))
1729
    # End while
1730
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1731
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1732
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1733
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1734
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1735
    print "About to call polynomial rounding with:"
1736
    print "polySo: ",           ; pobyso_autoprint(polySo)
1737
    print "functionSo: ",       ; pobyso_autoprint(functionSo)
1738
    print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1739
    print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1740
    print "itpSo: ",            ; pobyso_autoprint(itpSo)
1741
    print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1742
    print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1743
    print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1744
    (roundedPolySo, roundedPolyMaxErrSo) = \
1745
    pobyso_round_coefficients_progressive_so_so(polySo,
1746
                                                functionSo,
1747
                                                maxPrecSo,
1748
                                                currentRangeSo,
1749
                                                intervalCenterSo,
1750
                                                maxErrorSo,
1751
                                                approxAccurSo,
1752
                                                debug = True)
1753
    
1754
    sollya_lib_clear_obj(polySo)
1755
    sollya_lib_clear_obj(maxErrorSo)
1756
    sollya_lib_clear_obj(itpSo)
1757
    sollya_lib_clear_obj(ftpSo)
1758
    sollya_lib_clear_obj(approxAccurSo)
1759
    if sollyaPrecChanged:
1760
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1761
    sollya_lib_clear_obj(initialSollyaPrecSo)
1762
    print "1: ", ; pobyso_autoprint(roundedPolySo)
1763
    print "2: ", ; pobyso_autoprint(currentRangeSo)
1764
    print "3: ", ; pobyso_autoprint(intervalCenterSo)
1765
    print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1766
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1767
# End slz_compute_polynomial_and_interval_02
1768

    
1769
def slz_compute_reduced_polynomial(matrixRow,
1770
                                    knownMonomials,
1771
                                    var1,
1772
                                    var1Bound,
1773
                                    var2,
1774
                                    var2Bound):
1775
    """
1776
    Compute a polynomial from a single reduced matrix row.
1777
    This function was introduced in order to avoid the computation of the
1778
    all the polynomials  from the full matrix (even those built from rows 
1779
    that do no verify the Coppersmith condition) as this may involves 
1780
    expensive operations over (large) integers.
1781
    """
1782
    ## Check arguments.
1783
    if len(knownMonomials) == 0:
1784
        return None
1785
    # varNounds can be zero since 0^0 returns 1.
1786
    if (var1Bound < 0) or (var2Bound < 0):
1787
        return None
1788
    ## Initialisations. 
1789
    polynomialRing = knownMonomials[0].parent() 
1790
    currentPolynomial = polynomialRing(0)
1791
    # TODO: use zip instead of indices.
1792
    for colIndex in xrange(0, len(knownMonomials)):
1793
        currentCoefficient = matrixRow[colIndex]
1794
        if currentCoefficient != 0:
1795
            #print "Current coefficient:", currentCoefficient
1796
            currentMonomial = knownMonomials[colIndex]
1797
            #print "Monomial as multivariate polynomial:", \
1798
            #currentMonomial, type(currentMonomial)
1799
            degreeInVar1 = currentMonomial.degree(var1)
1800
            #print "Degree in var1", var1, ":", degreeInVar1
1801
            degreeInVar2 = currentMonomial.degree(var2)
1802
            #print "Degree in var2", var2, ":", degreeInVar2
1803
            if degreeInVar1 > 0:
1804
                currentCoefficient = \
1805
                    currentCoefficient / (var1Bound^degreeInVar1)
1806
                #print "varBound1 in degree:", var1Bound^degreeInVar1
1807
                #print "Current coefficient(1)", currentCoefficient
1808
            if degreeInVar2 > 0:
1809
                currentCoefficient = \
1810
                    currentCoefficient / (var2Bound^degreeInVar2)
1811
                #print "Current coefficient(2)", currentCoefficient
1812
            #print "Current reduced monomial:", (currentCoefficient * \
1813
            #                                    currentMonomial)
1814
            currentPolynomial += (currentCoefficient * currentMonomial)
1815
            #print "Current polynomial:", currentPolynomial
1816
        # End if
1817
    # End for colIndex.
1818
    #print "Type of the current polynomial:", type(currentPolynomial)
1819
    return(currentPolynomial)
1820
# End slz_compute_reduced_polynomial
1821
#
1822
def slz_compute_reduced_polynomials(reducedMatrix,
1823
                                        knownMonomials,
1824
                                        var1,
1825
                                        var1Bound,
1826
                                        var2,
1827
                                        var2Bound):
1828
    """
1829
    Legacy function, use slz_compute_reduced_polynomials_list
1830
    """
1831
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
1832
                                                knownMonomials,
1833
                                                var1,
1834
                                                var1Bound,
1835
                                                var2,
1836
                                                var2Bound)
1837
    )
1838
#
1839
def slz_compute_reduced_polynomials_list(reducedMatrix,
1840
                                         knownMonomials,
1841
                                         var1,
1842
                                         var1Bound,
1843
                                         var2,
1844
                                         var2Bound):
1845
    """
1846
    From a reduced matrix, holding the coefficients, from a monomials list,
1847
    from the bounds of each variable, compute the corresponding polynomials
1848
    scaled back by dividing by the "right" powers of the variables bounds.
1849
    
1850
    The elements in knownMonomials must be of the "right" polynomial type.
1851
    They set the polynomial type of the output polynomials in list.
1852
    @param reducedMatrix:  the reduced matrix as output from LLL;
1853
    @param kwnonMonomials: the ordered list of the monomials used to
1854
                           build the polynomials;
1855
    @param var1:           the first variable (of the "right" type);
1856
    @param var1Bound:      the first variable bound;
1857
    @param var2:           the second variable (of the "right" type);
1858
    @param var2Bound:      the second variable bound.
1859
    @return: a list of polynomials obtained with the reduced coefficients
1860
             and scaled down with the bounds
1861
    """
1862
    
1863
    # TODO: check input arguments.
1864
    reducedPolynomials = []
1865
    #print "type var1:", type(var1), " - type var2:", type(var2)
1866
    for matrixRow in reducedMatrix.rows():
1867
        currentPolynomial = 0
1868
        for colIndex in xrange(0, len(knownMonomials)):
1869
            currentCoefficient = matrixRow[colIndex]
1870
            if currentCoefficient != 0:
1871
                #print "Current coefficient:", currentCoefficient
1872
                currentMonomial = knownMonomials[colIndex]
1873
                parentRing = currentMonomial.parent()
1874
                #print "Monomial as multivariate polynomial:", \
1875
                #currentMonomial, type(currentMonomial)
1876
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1877
                #print "Degree in var", var1, ":", degreeInVar1
1878
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1879
                #print "Degree in var", var2, ":", degreeInVar2
1880
                if degreeInVar1 > 0:
1881
                    currentCoefficient /= var1Bound^degreeInVar1
1882
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1883
                    #print "Current coefficient(1)", currentCoefficient
1884
                if degreeInVar2 > 0:
1885
                    currentCoefficient /= var2Bound^degreeInVar2
1886
                    #print "Current coefficient(2)", currentCoefficient
1887
                #print "Current reduced monomial:", (currentCoefficient * \
1888
                #                                    currentMonomial)
1889
                currentPolynomial += (currentCoefficient * currentMonomial)
1890
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1891
                    #print "!!!! constant term !!!!"
1892
                #print "Current polynomial:", currentPolynomial
1893
            # End if
1894
        # End for colIndex.
1895
        #print "Type of the current polynomial:", type(currentPolynomial)
1896
        reducedPolynomials.append(currentPolynomial)
1897
    return reducedPolynomials 
1898
# End slz_compute_reduced_polynomials_list.
1899

    
1900
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
1901
                                                   knownMonomials,
1902
                                                   var1,
1903
                                                   var1Bound,
1904
                                                   var2,
1905
                                                   var2Bound):
1906
    """
1907
    From a list of rows, holding the coefficients, from a monomials list,
1908
    from the bounds of each variable, compute the corresponding polynomials
1909
    scaled back by dividing by the "right" powers of the variables bounds.
1910
    
1911
    The elements in knownMonomials must be of the "right" polynomial type.
1912
    They set the polynomial type of the output polynomials in list.
1913
    @param rowsList:       the reduced matrix as output from LLL;
1914
    @param kwnonMonomials: the ordered list of the monomials used to
1915
                           build the polynomials;
1916
    @param var1:           the first variable (of the "right" type);
1917
    @param var1Bound:      the first variable bound;
1918
    @param var2:           the second variable (of the "right" type);
1919
    @param var2Bound:      the second variable bound.
1920
    @return: a list of polynomials obtained with the reduced coefficients
1921
             and scaled down with the bounds
1922
    """
1923
    
1924
    # TODO: check input arguments.
1925
    reducedPolynomials = []
1926
    #print "type var1:", type(var1), " - type var2:", type(var2)
1927
    for matrixRow in rowsList:
1928
        currentPolynomial = 0
1929
        for colIndex in xrange(0, len(knownMonomials)):
1930
            currentCoefficient = matrixRow[colIndex]
1931
            if currentCoefficient != 0:
1932
                #print "Current coefficient:", currentCoefficient
1933
                currentMonomial = knownMonomials[colIndex]
1934
                parentRing = currentMonomial.parent()
1935
                #print "Monomial as multivariate polynomial:", \
1936
                #currentMonomial, type(currentMonomial)
1937
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
1938
                #print "Degree in var", var1, ":", degreeInVar1
1939
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
1940
                #print "Degree in var", var2, ":", degreeInVar2
1941
                if degreeInVar1 > 0:
1942
                    currentCoefficient /= var1Bound^degreeInVar1
1943
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
1944
                    #print "Current coefficient(1)", currentCoefficient
1945
                if degreeInVar2 > 0:
1946
                    currentCoefficient /= var2Bound^degreeInVar2
1947
                    #print "Current coefficient(2)", currentCoefficient
1948
                #print "Current reduced monomial:", (currentCoefficient * \
1949
                #                                    currentMonomial)
1950
                currentPolynomial += (currentCoefficient * currentMonomial)
1951
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
1952
                    #print "!!!! constant term !!!!"
1953
                #print "Current polynomial:", currentPolynomial
1954
            # End if
1955
        # End for colIndex.
1956
        #print "Type of the current polynomial:", type(currentPolynomial)
1957
        reducedPolynomials.append(currentPolynomial)
1958
    return reducedPolynomials 
1959
# End slz_compute_reduced_polynomials_list_from_rows.
1960
#
1961
def slz_compute_scaled_function(functionSa,
1962
                                lowerBoundSa,
1963
                                upperBoundSa,
1964
                                floatingPointPrecSa,
1965
                                debug=False):
1966
    """
1967
    From a function, compute the scaled function whose domain
1968
    is included in [1, 2) and whose image is also included in [1,2).
1969
    Return a tuple: 
1970
    [0]: the scaled function
1971
    [1]: the scaled domain lower bound
1972
    [2]: the scaled domain upper bound
1973
    [3]: the scaled image lower bound
1974
    [4]: the scaled image upper bound
1975
    """
1976
    ## The variable can be called anything.
1977
    x = functionSa.variables()[0]
1978
    # Scalling the domain -> [1,2[.
1979
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
1980
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
1981
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
1982
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
1983
    if debug:
1984
        print "domainScalingExpression for argument :", \
1985
        invDomainScalingExpressionSa
1986
        print "function: ", functionSa
1987
    ff = functionSa.subs({x : domainScalingExpressionSa})
1988
    ## Bless expression as a function.
1989
    ff = ff.function(x)
1990
    #ff = f.subs_expr(x==domainScalingExpressionSa)
1991
    #domainScalingFunction(x) = invDomainScalingExpressionSa
1992
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
1993
    scaledLowerBoundSa = \
1994
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
1995
    scaledUpperBoundSa = \
1996
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
1997
    if debug:
1998
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
1999
              scaledUpperBoundSa
2000
    ## If both bounds are negative, swap them.
2001
    if lowerBoundSa < 0 and upperBoundSa < 0:
2002
        scaledLowerBoundSa, scaledUpperBoundSa = \
2003
        scaledUpperBoundSa,scaledLowerBoundSa
2004
    #
2005
    # Scalling the image -> [1,2[.
2006
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
2007
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
2008
    if flbSa <= fubSa: # Increasing
2009
        imageBinadeBottomSa = floor(flbSa.log2())
2010
    else: # Decreasing
2011
        imageBinadeBottomSa = floor(fubSa.log2())
2012
    if debug:
2013
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
2014
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
2015
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
2016
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
2017
    if debug:
2018
        print "imageScalingExpression for argument :", \
2019
              invImageScalingExpressionSa
2020
    iis = invImageScalingExpressionSa.function(x)
2021
    fff = iis.subs({x:ff})
2022
    if debug:
2023
        print "fff:", fff,
2024
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
2025
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
2026
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
2027
# End slz_compute_scaled_function
2028

    
2029
def slz_fix_bounds_for_binades(lowerBound, 
2030
                               upperBound, 
2031
                               func = None, 
2032
                               domainDirection = -1,
2033
                               imageDirection  = -1):
2034
    """
2035
    Assuming the function is increasing or decreasing over the 
2036
    [lowerBound, upperBound] interval, return a lower bound lb and 
2037
    an upper bound ub such that:
2038
    - lb and ub belong to the same binade;
2039
    - func(lb) and func(ub) belong to the same binade.
2040
    domainDirection indicate how bounds move to fit into the same binade:
2041
    - a negative value move the upper bound down;
2042
    - a positive value move the lower bound up.
2043
    imageDirection indicate how bounds move in order to have their image
2044
    fit into the same binade, variation of the function is also condidered.
2045
    For an increasing function:
2046
    - negative value moves the upper bound down (and its image value as well);
2047
    - a positive values moves the lower bound up (and its image value as well);
2048
    For a decreasing function it is the other way round.
2049
    """
2050
    ## Arguments check
2051
    if lowerBound > upperBound:
2052
        return None
2053
    if lowerBound == upperBound:
2054
        return (lowerBound, upperBound)
2055
    if func is None:
2056
        return None
2057
    #
2058
    #varFunc = func.variables()[0]
2059
    lb = lowerBound
2060
    ub = upperBound
2061
    lbBinade = slz_compute_binade(lb) 
2062
    ubBinade = slz_compute_binade(ub)
2063
    ## Domain binade.
2064
    while lbBinade != ubBinade:
2065
        newIntervalWidth = (ub - lb) / 2
2066
        if domainDirection < 0:
2067
            ub = lb + newIntervalWidth
2068
            ubBinade = slz_compute_binade(ub)
2069
        else:
2070
            lb = lb + newIntervalWidth
2071
            lbBinade = slz_compute_binade(lb) 
2072
    #print "sfbfb: lower bound:", lb.str(truncate=False)
2073
    #print "sfbfb: upper bound:", ub.str(truncate=False)
2074
    ## At this point, both bounds belond to the same binade.
2075
    ## Image binade.
2076
    if lb == ub:
2077
        return (lb, ub)
2078
    lbImg = func(lb)
2079
    ubImg = func(ub)
2080
    funcIsInc = ((ubImg - lbImg) >= 0)
2081
    lbImgBinade = slz_compute_binade(lbImg)
2082
    ubImgBinade = slz_compute_binade(ubImg)
2083
    while lbImgBinade != ubImgBinade:
2084
        newIntervalWidth = (ub - lb) / 2
2085
        if imageDirection < 0:
2086
            if funcIsInc:
2087
                ub = lb + newIntervalWidth
2088
                ubImgBinade = slz_compute_binade(func(ub))
2089
                #print ubImgBinade
2090
            else:
2091
                lb = lb + newIntervalWidth
2092
                lbImgBinade = slz_compute_binade(func(lb))
2093
                #print lbImgBinade
2094
        else:
2095
            if funcIsInc:
2096
                lb = lb + newIntervalWidth
2097
                lbImgBinade = slz_compute_binade(func(lb))
2098
                #print lbImgBinade
2099
            else:
2100
                ub = lb + newIntervalWidth
2101
                ubImgBinade = slz_compute_binade(func(ub))
2102
                #print ubImgBinade
2103
    # End while lbImgBinade != ubImgBinade:
2104
    return (lb, ub)
2105
# End slz_fix_bounds_for_binades.
2106

    
2107
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
2108
    # Create a polynomial over the rationals.
2109
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
2110
    return(ratPolynomialRing(polyOfFloat))
2111
# End slz_float_poly_of_float_to_rat_poly_of_rat.
2112

    
2113
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
2114
    """
2115
     Create a polynomial over the rationals where all denominators are
2116
     powers of two.
2117
    """
2118
    polyVariable = polyOfFloat.variables()[0] 
2119
    RPR = QQ[str(polyVariable)]
2120
    polyCoeffs      = polyOfFloat.coefficients()
2121
    #print polyCoeffs
2122
    polyExponents   = polyOfFloat.exponents()
2123
    #print polyExponents
2124
    polyDenomPtwoCoeffs = []
2125
    for coeff in polyCoeffs:
2126
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
2127
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
2128
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
2129
    ratPoly = RPR(0)
2130
    #print type(ratPoly)
2131
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
2132
    #  The coefficient becomes plainly wrong when exponent == 0.
2133
    #  No clue as to why.
2134
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
2135
        ratPoly += coeff * RPR(polyVariable^exponent)
2136
    return ratPoly
2137
# End slz_float_poly_of_float_to_rat_poly_of_rat.
2138

    
2139
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
2140
                                      lowerBoundSa, 
2141
                                      upperBoundSa, 
2142
                                      floatingPointPrecSa, 
2143
                                      internalSollyaPrecSa, 
2144
                                      approxAccurSa):
2145
    """
2146
    Under the assumption that:
2147
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
2148
    - lowerBound and upperBound belong to the same binade.
2149
    from a:
2150
    - function;
2151
    - a degree
2152
    - a pair of bounds;
2153
    - the floating-point precision we work on;
2154
    - the internal Sollya precision;
2155
    - the requested approximation error
2156
    The initial interval is, possibly, splitted into smaller intervals.
2157
    It return a list of tuples, each made of:
2158
    - a first polynomial (without the changed variable f(x) = p(x-x0));
2159
    - a second polynomial (with a changed variable f(x) = q(x))
2160
    - the approximation interval;
2161
    - the center, x0, of the interval;
2162
    - the corresponding approximation error.
2163
    TODO: fix endless looping for some parameters sets.
2164
    """
2165
    resultArray = []
2166
    # Set Sollya to the necessary internal precision.
2167
    sollyaPrecChangedSa = False
2168
    (initialSollyaPrecSo, initialSollyaPrecSa) = pobyso_get_prec_so_so_sa()
2169
    if internalSollyaPrecSa > initialSollyaPrecSa:
2170
        if internalSollyaPrecSa <= 2:
2171
            print inspect.stack()[0][3], ": precision change <=2 requested."
2172
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
2173
        sollyaPrecChangedSa = True
2174
    #
2175
    x = functionSa.variables()[0] # Actual variable name can be anything.
2176
    # Scaled function: [1=,2] -> [1,2].
2177
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
2178
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
2179
                slz_compute_scaled_function(functionSa,                       \
2180
                                            lowerBoundSa,                     \
2181
                                            upperBoundSa,                     \
2182
                                            floatingPointPrecSa)
2183
    # In case bounds were in the negative real one may need to
2184
    # switch scaled bounds.
2185
    if scaledLowerBoundSa > scaledUpperBoundSa:
2186
        scaledLowerBoundSa, scaledUpperBoundSa = \
2187
            scaledUpperBoundSa, scaledLowerBoundSa
2188
        #print "Switching!"
2189
    print "Approximation accuracy: ", RR(approxAccurSa)
2190
    # Prepare the arguments for the Taylor expansion computation with Sollya.
2191
    functionSo = \
2192
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
2193
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
2194
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
2195
                                                  scaledUpperBoundSa)
2196

    
2197
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
2198
                                              upperBoundSa.parent().precision()))
2199
    currentScaledLowerBoundSa = scaledLowerBoundSa
2200
    currentScaledUpperBoundSa = scaledUpperBoundSa
2201
    while True:
2202
        ## Compute the first Taylor expansion.
2203
        print "Computing a Taylor expansion..."
2204
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
2205
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
2206
                                        currentScaledLowerBoundSa, 
2207
                                        currentScaledUpperBoundSa,
2208
                                        approxAccurSa, internalSollyaPrecSa)
2209
        print "...done."
2210
        ## If slz_compute_polynomial_and_interval fails, it returns None.
2211
        #  This value goes to the first variable: polySo. Other variables are
2212
        #  not assigned and should not be tested.
2213
        if polySo is None:
2214
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
2215
            if precChangedSa:
2216
                pobyso_set_prec_so_so(initialSollyaPrecSo)
2217
            sollya_lib_clear_obj(initialSollyaPrecSo)
2218
            sollya_lib_clear_obj(functionSo)
2219
            sollya_lib_clear_obj(degreeSo)
2220
            sollya_lib_clear_obj(scaledBoundsSo)
2221
            return None
2222
        ## Add to the result array.
2223
        ### Change variable stuff in Sollya x -> x0-x.
2224
        changeVarExpressionSo = \
2225
            sollya_lib_build_function_sub( \
2226
                              sollya_lib_build_function_free_variable(), 
2227
                              sollya_lib_copy_obj(intervalCenterSo))
2228
        polyVarChangedSo = \
2229
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
2230
        #### Get rid of the variable change Sollya stuff. 
2231
        sollya_lib_clear_obj(changeVarExpressionSo)
2232
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
2233
                            intervalCenterSo, maxErrorSo))
2234
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
2235
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
2236
        print "Computed approximation error:", errorSa.n(digits=10)
2237
        # If the error and interval are OK a the first try, just return.
2238
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
2239
            (errorSa <= approxAccurSa):
2240
            if sollyaPrecChangedSa:
2241
                pobyso_set_prec_so_so(initialSollyaPrecSo)
2242
            sollya_lib_clear_obj(initialSollyaPrecSo)
2243
            sollya_lib_clear_obj(functionSo)
2244
            sollya_lib_clear_obj(degreeSo)
2245
            sollya_lib_clear_obj(scaledBoundsSo)
2246
            #print "Approximation error:", errorSa
2247
            return resultArray
2248
        ## The returned interval upper bound does not reach the requested upper
2249
        #  upper bound: compute the next upper bound.
2250
        ## The following ratio is always >= 1. If errorSa, we may want to
2251
        #  enlarge the interval
2252
        currentErrorRatio = approxAccurSa / errorSa
2253
        ## --|--------------------------------------------------------------|--
2254
        #  --|--------------------|--------------------------------------------
2255
        #  --|----------------------------|------------------------------------
2256
        ## Starting point for the next upper bound.
2257
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
2258
        # Compute the increment. 
2259
        newBoundsWidthSa = \
2260
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
2261
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
2262
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
2263
        # Take into account the original interval upper bound.                     
2264
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
2265
            currentScaledUpperBoundSa = scaledUpperBoundSa
2266
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
2267
            print "Can't shrink the interval anymore!"
2268
            print "You should consider increasing the Sollya internal precision"
2269
            print "or the polynomial degree."
2270
            print "Giving up!"
2271
            if precChangedSa:
2272
                pobyso_set_prec_so_so(initialSollyaPrecSo)
2273
            sollya_lib_clear_obj(initialSollyaPrecSo)
2274
            sollya_lib_clear_obj(functionSo)
2275
            sollya_lib_clear_obj(degreeSo)
2276
            sollya_lib_clear_obj(scaledBoundsSo)
2277
            return None
2278
        # Compute the other expansions.
2279
        # Test for insufficient precision.
2280
# End slz_get_intervals_and_polynomials
2281

    
2282
def slz_interval_scaling_expression(boundsInterval, expVar):
2283
    """
2284
    Compute the scaling expression to map an interval that spans at most
2285
    a single binade into [1, 2) and the inverse expression as well.
2286
    If the interval spans more than one binade, result is wrong since
2287
    scaling is only based on the lower bound.
2288
    Not very sure that the transformation makes sense for negative numbers.
2289
    """
2290
    # The "one of the bounds is 0" special case. Here we consider
2291
    # the interval as the subnormals binade.
2292
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
2293
        # The upper bound is (or should be) positive.
2294
        if boundsInterval.endpoints()[0] == 0:
2295
            if boundsInterval.endpoints()[1] == 0:
2296
                return None
2297
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
2298
            l2     = boundsInterval.endpoints()[1].abs().log2()
2299
            # The upper bound is a power of two
2300
            if binade == l2:
2301
                scalingCoeff    = 2^(-binade)
2302
                invScalingCoeff = 2^(binade)
2303
                scalingOffset   = 1
2304
                return \
2305
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
2306
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
2307
            else:
2308
                scalingCoeff    = 2^(-binade-1)
2309
                invScalingCoeff = 2^(binade+1)
2310
                scalingOffset   = 1
2311
                return((scalingCoeff * expVar + scalingOffset),\
2312
                    ((expVar - scalingOffset) * invScalingCoeff))
2313
        # The lower bound is (or should be) negative.
2314
        if boundsInterval.endpoints()[1] == 0:
2315
            if boundsInterval.endpoints()[0] == 0:
2316
                return None
2317
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
2318
            l2     = boundsInterval.endpoints()[0].abs().log2()
2319
            # The upper bound is a power of two
2320
            if binade == l2:
2321
                scalingCoeff    = -2^(-binade)
2322
                invScalingCoeff = -2^(binade)
2323
                scalingOffset   = 1
2324
                return((scalingCoeff * expVar + scalingOffset),\
2325
                    ((expVar - scalingOffset) * invScalingCoeff))
2326
            else:
2327
                scalingCoeff    = -2^(-binade-1)
2328
                invScalingCoeff = -2^(binade+1)
2329
                scalingOffset   = 1
2330
                return((scalingCoeff * expVar + scalingOffset),\
2331
                   ((expVar - scalingOffset) * invScalingCoeff))
2332
    # General case
2333
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
2334
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
2335
    # We allow for a single exception in binade spanning is when the
2336
    # "outward bound" is a power of 2 and its binade is that of the
2337
    # "inner bound" + 1.
2338
    if boundsInterval.endpoints()[0] > 0:
2339
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
2340
        if lbBinade != ubBinade:
2341
            print "Different binades."
2342
            if ubL2 != ubBinade:
2343
                print "Not a power of 2."
2344
                return None
2345
            elif abs(ubBinade - lbBinade) > 1:
2346
                print "Too large span:", abs(ubBinade - lbBinade)
2347
                return None
2348
    else:
2349
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
2350
        if lbBinade != ubBinade:
2351
            print "Different binades."
2352
            if lbL2 != lbBinade:
2353
                print "Not a power of 2."
2354
                return None
2355
            elif abs(ubBinade - lbBinade) > 1:
2356
                print "Too large span:", abs(ubBinade - lbBinade)
2357
                return None
2358
    #print "Lower bound binade:", binade
2359
    if boundsInterval.endpoints()[0] > 0:
2360
        return \
2361
            ((2^(-lbBinade) * expVar).function(expVar),
2362
             (2^(lbBinade) * expVar).function(expVar))
2363
    else:
2364
        return \
2365
            ((-(2^(-ubBinade)) * expVar).function(expVar),
2366
             (-(2^(ubBinade)) * expVar).function(expVar))
2367
"""
2368
    # Code sent to attic. Should be the base for a 
2369
    # "slz_interval_translate_expression" rather than scale.
2370
    # Extra control and special cases code  added in  
2371
    # slz_interval_scaling_expression could (should ?) be added to
2372
    # this new function.
2373
    # The scaling offset is only used for negative numbers.
2374
    # When the absolute value of the lower bound is < 0.
2375
    if abs(boundsInterval.endpoints()[0]) < 1:
2376
        if boundsInterval.endpoints()[0] >= 0:
2377
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
2378
            invScalingCoeff = 1/scalingCoeff
2379
            return((scalingCoeff * expVar, 
2380
                    invScalingCoeff * expVar))
2381
        else:
2382
            scalingCoeff = \
2383
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
2384
            scalingOffset = -3 * scalingCoeff
2385
            return((scalingCoeff * expVar + scalingOffset,
2386
                    1/scalingCoeff * expVar + 3))
2387
    else: 
2388
        if boundsInterval.endpoints()[0] >= 0:
2389
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
2390
            scalingOffset = 0
2391
            return((scalingCoeff * expVar, 
2392
                    1/scalingCoeff * expVar))
2393
        else:
2394
            scalingCoeff = \
2395
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
2396
            scalingOffset =  -3 * scalingCoeff
2397
            #scalingOffset = 0
2398
            return((scalingCoeff * expVar + scalingOffset,
2399
                    1/scalingCoeff * expVar + 3))
2400
"""
2401
# End slz_interval_scaling_expression
2402
   
2403
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
2404
    """
2405
    Compute the Sage version of the Taylor polynomial and it's
2406
    companion data (interval, center...)
2407
    The input parameter is a five elements tuple:
2408
    - [0]: the polyomial (without variable change), as polynomial over a
2409
           real ring;
2410
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
2411
           over a real ring;
2412
    - [2]: the interval (as Sollya range);
2413
    - [3]: the interval center;
2414
    - [4]: the approximation error. 
2415
    
2416
    The function returns a 5 elements tuple: formed with all the 
2417
    input elements converted into their Sollya counterpart.
2418
    """
2419
    #print "Sollya polynomial to convert:", 
2420
    #pobyso_autoprint(polyRangeCenterErrorSo)
2421
    polynomialSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[0])
2422
    #print "Polynomial after first conversion: ", pobyso_autoprint(polyRangeCenterErrorSo[1])
2423
    polynomialChangedVarSa = pobyso_float_poly_so_sa(polyRangeCenterErrorSo[1])
2424
    intervalSa = \
2425
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
2426
    centerSa = \
2427
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
2428
    errorSa = \
2429
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
2430
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
2431
    # End slz_interval_and_polynomial_to_sage
2432

    
2433
def slz_is_htrn(argument, 
2434
                function, 
2435
                targetAccuracy, 
2436
                targetRF = None, 
2437
                targetPlusOnePrecRF = None, 
2438
                quasiExactRF = None):
2439
    """
2440
      Check if an element (argument) of the domain of function (function)
2441
      yields a HTRN case (rounding to next) for the target precision 
2442
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
2443
      
2444
      The strategy is: 
2445
      - compute the image at high (quasi-exact) precision;
2446
      - round it to nearest to precision;
2447
      - round it to exactly to (precision+1), the computed number has two
2448
        midpoint neighbors;
2449
      - check the distance between these neighbors and the quasi-exact value;
2450
        - if none of them is closer than the targetAccuracy, return False,
2451
        - else return true.
2452
      - Powers of two are a special case when comparing the midpoint in
2453
        the 0 direction..
2454
    """
2455
    ## Arguments filtering. 
2456
    ## TODO: filter the first argument for consistence.
2457
    if targetRF is None:
2458
        targetRF = argument.parent()
2459
    ## Ditto for the real field holding the midpoints.
2460
    if targetPlusOnePrecRF is None:
2461
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
2462
    ## If no quasiExactField is provided, create a high accuracy RealField.
2463
    if quasiExactRF is None:
2464
        quasiExactRF = RealField(1536)
2465
    function                      = function.function(function.variables()[0])
2466
    exactArgument                 = quasiExactRF(argument)
2467
    quasiExactValue               = function(exactArgument)
2468
    roundedValue                  = targetRF(quasiExactValue)
2469
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
2470
    # Upper midpoint.
2471
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
2472
    # Lower midpoint.
2473
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
2474
    binade                        = slz_compute_binade(roundedValue)
2475
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
2476
    #print "Argument:", argument
2477
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
2478
    #print "Binade:", binade
2479
    #print "binadeCorrectedTargetAccuracy:", \
2480
    #binadeCorrectedTargetAccuracy.n(prec=100)
2481
    #print "binadeCorrectedTargetAccuracy:", \
2482
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
2483
    #print "Upper midpoint:", \
2484
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2485
    #print "Rounded value :", \
2486
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
2487
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
2488
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
2489
    #print "Lower midpoint:", \
2490
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2491
    ## Make quasiExactValue = 0 a special case to move it out of the way.
2492
    if quasiExactValue == 0:
2493
        return False
2494
    ## Begining of the general case : check with the midpoint of 
2495
    #  greatest absolute value.
2496
    if quasiExactValue > 0:
2497
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
2498
           binadeCorrectedTargetAccuracy:
2499
            #print "Too close to the upper midpoint: ", \
2500
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2501
            #print argument.n().str(base=16, \
2502
            #  no_sci=False)
2503
            #print "Lower midpoint:", \
2504
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2505
            #print "Value         :", \
2506
            # quasiExactValue.n(prec=200).str(base=2)
2507
            #print "Upper midpoint:", \
2508
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2509
            return True
2510
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
2511
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2512
           binadeCorrectedTargetAccuracy:
2513
            #print "Too close to the upper midpoint: ", \
2514
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
2515
            #print argument.n().str(base=16, \
2516
            #  no_sci=False)
2517
            #print "Lower midpoint:", \
2518
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2519
            #print "Value         :", \
2520
            #  quasiExactValue.n(prec=200).str(base=2)
2521
            #print "Upper midpoint:", \
2522
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2523
            #print
2524
            return True
2525
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2526
    ## For the midpoint of smaller absolute value, 
2527
    #  split cases with the powers of 2.
2528
    if sno_abs_is_power_of_two(roundedValue):
2529
        if quasiExactValue > 0:        
2530
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
2531
               binadeCorrectedTargetAccuracy / 2:
2532
                #print "Lower midpoint:", \
2533
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2534
                #print "Value         :", \
2535
                #  quasiExactValue.n(prec=200).str(base=2)
2536
                #print "Upper midpoint:", \
2537
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2538
                #print
2539
                return True
2540
        else:
2541
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2542
              binadeCorrectedTargetAccuracy / 2:
2543
                #print "Lower midpoint:", \
2544
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2545
                #print "Value         :", 
2546
                #  quasiExactValue.n(prec=200).str(base=2)
2547
                #print "Upper midpoint:", 
2548
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2549
                #print
2550
                return True
2551
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
2552
    else: ## Not a power of 2, regular comparison with the midpoint of 
2553
          #  smaller absolute value.
2554
        if quasiExactValue > 0:        
2555
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
2556
              binadeCorrectedTargetAccuracy:
2557
                #print "Lower midpoint:", \
2558
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2559
                #print "Value         :", \
2560
                #  quasiExactValue.n(prec=200).str(base=2)
2561
                #print "Upper midpoint:", \
2562
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2563
                #print
2564
                return True
2565
        else: # quasiExactValue <= 0
2566
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
2567
              binadeCorrectedTargetAccuracy:
2568
                #print "Lower midpoint:", \
2569
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2570
                #print "Value         :", \
2571
                #  quasiExactValue.n(prec=200).str(base=2)
2572
                #print "Upper midpoint:", \
2573
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
2574
                #print
2575
                return True
2576
    #print "distance to the upper midpoint:", \
2577
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
2578
    #print "distance to the lower midpoint:", \
2579
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
2580
    return False
2581
# End slz_is_htrn
2582
#
2583
def slz_pm1():
2584
    """
2585
    Compute a uniform RV in {-1, 1} (not zero).
2586
    """
2587
    ## function getrandbits(N) generates a long int with N random bits.
2588
    #  Here it generates either 0 or 1. The multiplication by 2 and the
2589
    #  subtraction of 1 make that:
2590
    #  if getranbits(1) == 0 -> O * 2 - 1 = -1
2591
    #  else                  -> 1 * 1 - 1 =  1. 
2592
    return getrandbits(1) * 2-1
2593
# End slz_pm1.
2594
#
2595
def slz_random_proj_pm1(r, c):
2596
    """
2597
    r x c matrix with \pm 1 ({-1, 1}) coefficients
2598
    """
2599
    M = matrix(r, c)
2600
    for i in range(0, r):
2601
        for j in range (0, c):
2602
            M[i,j] = slz_pm1()
2603
    return M
2604
# End random_proj_pm1.
2605
#
2606
def slz_random_proj_uniform(n, r, c):
2607
    """
2608
    r x c integer matrix with uniform n-bit integer elements.
2609
    """
2610
    M = matrix(r, c)
2611
    for i in range(0, r):
2612
        for j in range (0, c):
2613
            M[i,j] = slz_uniform(n)
2614
    return M       
2615
# End slz_random_proj_uniform.
2616
#
2617
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
2618
                                                precision,
2619
                                                targetHardnessToRound,
2620
                                                variable1,
2621
                                                variable2):
2622
    """
2623
    Creates a new multivariate polynomial with integer coefficients for use
2624
     with the Coppersmith method.
2625
    A the same time it computes :
2626
    - 2^K (N);
2627
    - 2^k (bound on the second variable)
2628
    - lcm
2629

    
2630
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
2631
                         variables.
2632
    :param precision: the precision of the floating-point coefficients.
2633
    :param targetHardnessToRound: the hardness to round we want to check.
2634
    :param variable1: the first variable of the polynomial (an expression).
2635
    :param variable2: the second variable of the polynomial (an expression).
2636
    
2637
    :returns: a 4 elements tuple:
2638
                - the polynomial;
2639
                - the modulus (N);
2640
                - the t bound;
2641
                - the lcm used to compute the integral coefficients and the 
2642
                  module.
2643
    """
2644
    # Create a new integer polynomial ring.
2645
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
2646
    # Coefficients are issued in the increasing power order.
2647
    ratPolyCoefficients = ratPolyOfInt.coefficients()
2648
    # Print the reversed list for debugging.
2649
    #print
2650
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
2651
    # Build the list of number we compute the lcm of.
2652
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
2653
    #print "Coefficient denominators:", coefficientDenominators
2654
    coefficientDenominators.append(2^precision)
2655
    coefficientDenominators.append(2^(targetHardnessToRound))
2656
    leastCommonMultiple = lcm(coefficientDenominators)
2657
    # Compute the expression corresponding to the new polynomial
2658
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
2659
    #print coefficientNumerators
2660
    polynomialExpression = 0
2661
    power = 0
2662
    # Iterate over two lists at the same time, stop when the shorter
2663
    # (is this case coefficientsNumerators) is 
2664
    # exhausted. Both lists are ordered in the order of increasing powers
2665
    # of variable1.
2666
    for numerator, denominator in \
2667
                        zip(coefficientNumerators, coefficientDenominators):
2668
        multiplicator = leastCommonMultiple / denominator 
2669
        newCoefficient = numerator * multiplicator
2670
        polynomialExpression += newCoefficient * variable1^power
2671
        power +=1
2672
    polynomialExpression += - variable2
2673
    return (IP(polynomialExpression),
2674
            leastCommonMultiple / 2^precision, # 2^K aka N.
2675
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
2676
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
2677
            leastCommonMultiple) # If we want to make test computations.
2678
        
2679
# End slz_rat_poly_of_int_to_poly_for_coppersmith
2680

    
2681
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
2682
                                          precision):
2683
    """
2684
    Makes a variable substitution into the input polynomial so that the output
2685
    polynomial can take integer arguments.
2686
    All variables of the input polynomial "have precision p". That is to say
2687
    that they are rationals with denominator == 2^(precision - 1): 
2688
    x = y/2^(precision - 1).
2689
    We "incorporate" these denominators into the coefficients with, 
2690
    respectively, the "right" power.
2691
    """
2692
    polynomialField = ratPolyOfRat.parent()
2693
    polynomialVariable = ratPolyOfRat.variables()[0]
2694
    #print "The polynomial field is:", polynomialField
2695
    return \
2696
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
2697
                                   polynomialVariable/2^(precision-1)}))
2698
    
2699
# End slz_rat_poly_of_rat_to_rat_poly_of_int
2700

    
2701
def slz_reduce_and_test_base(matrixToReduce,
2702
                             nAtAlpha,
2703
                             monomialsCountSqrt):
2704
    """
2705
    Reduces the basis, tests the Coppersmith condition and returns
2706
    a list of rows that comply with the condition.
2707
    """
2708
    ccComplientRowsList = []
2709
    reducedMatrix = matrixToReduce.LLL(None)
2710
    if not reducedMatrix.is_LLL_reduced():
2711
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
2712
    else:
2713
        print "reducedMatrix is actually reduced."
2714
    print "N^alpha:", nAtAlpha.n()
2715
    rowIndex = 0
2716
    for row in reducedMatrix.rows():
2717
        l2Norm = row.norm(2)
2718
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
2719
                monomialsCountSqrt.n(), ". Is vector OK?", 
2720
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
2721
            ccComplientRowsList.append(row)
2722
            print "True"
2723
        else:
2724
            print "False"
2725
    # End for
2726
    return ccComplientRowsList
2727
# End slz_reduce_and_test_base
2728

    
2729
def slz_reduce_lll_proj(matToReduce, n):
2730
    """
2731
    Compute the transformation matrix that realizes an LLL reduction on
2732
    the random uniform projected matrix.
2733
    Return both the initial matrix "reduced" by the transformation matrix and
2734
    the transformation matrix itself.
2735
    """
2736
    ## Compute the projected matrix.
2737
    """
2738
    # Random matrix elements {-2^(n-1),...,0,...,2^(n-1)-1}.
2739
    matProjector = slz_random_proj_uniform(n,
2740
                                           matToReduce.ncols(),
2741
                                           matToReduce.nrows())
2742
    """
2743
    # Random matrix elements in {-1,0,1}. 
2744
    matProjector = slz_random_proj_pm1(matToReduce.ncols(),
2745
                                       matToReduce.nrows())
2746
    matProjected = matToReduce * matProjector
2747
    ## Build the argument matrix for LLL in such a way that the transformation
2748
    #  matrix is also returned. This matrix is obtained at almost no extra
2749
    # cost. An identity matrix must be appended to 
2750
    #  the left of the initial matrix. The transformation matrix will
2751
    # will be recovered at the same location from the returned matrix .
2752
    idMat = identity_matrix(matProjected.nrows())
2753
    augmentedMatToReduce = idMat.augment(matProjected)
2754
    reducedProjMat = \
2755
        augmentedMatToReduce.LLL(algorithm='fpLLL:wrapper')
2756
    ## Recover the transformation matrix (the left part of the reduced matrix). 
2757
    #  We discard the reduced matrix itself.
2758
    transMat = reducedProjMat.submatrix(0,
2759
                                        0,
2760
                                        reducedProjMat.nrows(),
2761
                                        reducedProjMat.nrows())
2762
    ## Return the initial matrix "reduced" and the transformation matrix tuple.
2763
    return (transMat * matToReduce, transMat)
2764
# End  slz_reduce_lll_proj.
2765
#
2766
def slz_reduce_lll_proj_02(matToReduce, n):
2767
    """
2768
    Compute the transformation matrix that realizes an LLL reduction on
2769
    the random uniform projected matrix.
2770
    Return both the initial matrix "reduced" by the transformation matrix and
2771
    the transformation matrix itself.
2772
    """
2773
    ## Compute the projected matrix.
2774
    """
2775
    # Random matrix elements {-2^(n-1),...,0,...,2^(n-1)-1}.
2776
    matProjector = slz_random_proj_uniform(n,
2777
                                           matToReduce.ncols(),
2778
                                           matToReduce.nrows())
2779
    """
2780
    # Random matrix elements in {-8,0,7} -> 4. 
2781
    matProjector = slz_random_proj_uniform(matToReduce.ncols(),
2782
                                           matToReduce.nrows(),
2783
                                           4)
2784
    matProjected = matToReduce * matProjector
2785
    ## Build the argument matrix for LLL in such a way that the transformation
2786
    #  matrix is also returned. This matrix is obtained at almost no extra
2787
    # cost. An identity matrix must be appended to 
2788
    #  the left of the initial matrix. The transformation matrix will
2789
    # will be recovered at the same location from the returned matrix .
2790
    idMat = identity_matrix(matProjected.nrows())
2791
    augmentedMatToReduce = idMat.augment(matProjected)
2792
    reducedProjMat = \
2793
        augmentedMatToReduce.LLL(algorithm='fpLLL:wrapper')
2794
    ## Recover the transformation matrix (the left part of the reduced matrix). 
2795
    #  We discard the reduced matrix itself.
2796
    transMat = reducedProjMat.submatrix(0,
2797
                                        0,
2798
                                        reducedProjMat.nrows(),
2799
                                        reducedProjMat.nrows())
2800
    ## Return the initial matrix "reduced" and the transformation matrix tuple.
2801
    return (transMat * matToReduce, transMat)
2802
# End  slz_reduce_lll_proj_02.
2803
#
2804
def slz_resultant(poly1, poly2, elimVar, debug = False):
2805
    """
2806
    Compute the resultant for two polynomials for a given variable
2807
    and return the (poly1, poly2, resultant) if the resultant
2808
    is not 0.
2809
    Return () otherwise.
2810
    """
2811
    polynomialRing0    = poly1.parent()
2812
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2813
    if resultantInElimVar is None:
2814
        if debug:
2815
            print poly1
2816
            print poly2
2817
            print "have GCD = ", poly1.gcd(poly2)
2818
        return None
2819
    if resultantInElimVar.is_zero():
2820
        if debug:
2821
            print poly1
2822
            print poly2
2823
            print "have GCD = ", poly1.gcd(poly2)
2824
        return None
2825
    else:
2826
        if debug:
2827
            print poly1
2828
            print poly2
2829
            print "have resultant in t = ", resultantInElimVar
2830
        return resultantInElimVar
2831
# End slz_resultant.
2832
#
2833
def slz_resultant_tuple(poly1, poly2, elimVar):
2834
    """
2835
    Compute the resultant for two polynomials for a given variable
2836
    and return the (poly1, poly2, resultant) if the resultant
2837
    is not 0.
2838
    Return () otherwise.
2839
    """
2840
    polynomialRing0    = poly1.parent()
2841
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
2842
    if resultantInElimVar.is_zero():
2843
        return ()
2844
    else:
2845
        return (poly1, poly2, resultantInElimVar)
2846
# End slz_resultant_tuple.
2847
#
2848
def slz_uniform(n):
2849
    """
2850
    Compute a uniform RV in [-2^(n-1), 2^(n-1)-1] (zero is included).
2851
    """
2852
    ## function getrandbits(n) generates a long int with n random bits.
2853
    return getrandbits(n) - 2^(n-1)
2854
# End slz_uniform.
2855
#
2856
sys.stderr.write("\t...sageSLZ loaded\n")
2857