Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (128,68 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
    while maxErrorSa > approxAccurSa:
1499
        print "++Approximation error:", maxErrorSa.n()
1500
        sollya_lib_clear_obj(polySo)
1501
        sollya_lib_clear_obj(intervalCenterSo)
1502
        sollya_lib_clear_obj(maxErrorSo)
1503
        # Very empirical shrinking factor.
1504
        shrinkFactorSa = 1 /  (maxErrorSa/approxAccurSa).log2().abs()
1505
        print "Shrink factor:", \
1506
              shrinkFactorSa.n(), \
1507
              intervalShrinkConstFactorSa
1508
        print
1509
        #errorRatioSa = approxAccurSa/maxErrorSa
1510
        #print "Error ratio: ", errorRatioSa
1511
        # Make sure interval shrinks.
1512
        if shrinkFactorSa > intervalShrinkConstFactorSa:
1513
            actualShrinkFactorSa = intervalShrinkConstFactorSa
1514
            #print "Fixed"
1515
        else:
1516
            actualShrinkFactorSa = shrinkFactorSa
1517
            #print "Computed",shrinkFactorSa,maxErrorSa
1518
            #print shrinkFactorSa, maxErrorSa
1519
        #print "Shrink factor", actualShrinkFactorSa
1520
        currentUpperBoundSa = currentLowerBoundSa + \
1521
                                (currentUpperBoundSa - currentLowerBoundSa) * \
1522
                                actualShrinkFactorSa
1523
        #print "Current upper bound:", currentUpperBoundSa
1524
        sollya_lib_clear_obj(currentRangeSo)
1525
        # Check what is left with the bounds.
1526
        if currentUpperBoundSa <= currentLowerBoundSa or \
1527
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
1528
            sollya_lib_clear_obj(absoluteErrorTypeSo)
1529
            print "Can't find an interval."
1530
            print "Use either or both a higher polynomial degree or a higher",
1531
            print "internal precision."
1532
            print "Aborting!"
1533
            if sollyaPrecChangedSa:
1534
                pobyso_set_prec_so_so(initialSollyaPrecSo)
1535
            sollya_lib_clear_obj(initialSollyaPrecSo)
1536
            return None
1537
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
1538
                                                      currentUpperBoundSa)
1539
        # print "New interval:",
1540
        # pobyso_autoprint(currentRangeSo)
1541
        #print "Second Taylor expansion call."
1542
        (polySo, intervalCenterSo, maxErrorSo) = \
1543
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
1544
                                                        currentRangeSo, 
1545
                                                        absoluteErrorTypeSo)
1546
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
1547
        #print "Max errorSo:",
1548
        #pobyso_autoprint(maxErrorSo)
1549
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1550
        #print "Max errorSa:", maxErrorSa
1551
        #print "Sollya prec:",
1552
        #pobyso_autoprint(sollya_lib_get_prec(None))
1553
    # End while
1554
    sollya_lib_clear_obj(absoluteErrorTypeSo)
1555
    itpSo           = pobyso_constant_from_int_sa_so(floor(sollyaPrecSa/3))
1556
    ftpSo           = pobyso_constant_from_int_sa_so(floor(2*sollyaPrecSa/3))
1557
    maxPrecSo       = pobyso_constant_from_int_sa_so(sollyaPrecSa)
1558
    approxAccurSo   = pobyso_constant_sa_so(RR(approxAccurSa))
1559
    if debug:
1560
        print inspect.stack()[0][3], "SollyaPrecSa:", sollyaPrecSa
1561
        print "About to call polynomial rounding with:"
1562
        print "polySo: ",           ; pobyso_autoprint(polySo)
1563
        print "functionSo: ",       ; pobyso_autoprint(functionSo)
1564
        print "intervalCenterSo: ", ; pobyso_autoprint(intervalCenterSo)
1565
        print "currentRangeSo: ",   ; pobyso_autoprint(currentRangeSo)
1566
        print "itpSo: ",            ; pobyso_autoprint(itpSo)
1567
        print "ftpSo: ",            ; pobyso_autoprint(ftpSo)
1568
        print "maxPrecSo: ",        ; pobyso_autoprint(maxPrecSo)
1569
        print "approxAccurSo: ",    ; pobyso_autoprint(approxAccurSo)
1570
    """
1571
    # Naive rounding.
1572
    (roundedPolySo, roundedPolyMaxErrSo) = \
1573
    pobyso_polynomial_coefficients_progressive_round_so_so(polySo,
1574
                                                           functionSo,
1575
                                                           intervalCenterSo,
1576
                                                           currentRangeSo,
1577
                                                           itpSo,
1578
                                                           ftpSo,
1579
                                                           maxPrecSo,
1580
                                                           approxAccurSo)
1581
    """
1582
    # Proved rounding.
1583
    (roundedPolySo, roundedPolyMaxErrSo) = \
1584
        pobyso_round_coefficients_progressive_so_so(polySo, 
1585
                                                    functionSo,
1586
                                                    maxPrecSo,
1587
                                                    currentRangeSo,
1588
                                                    intervalCenterSo,
1589
                                                    maxErrorSo,
1590
                                                    approxAccurSo,
1591
                                                    debug=False)
1592
    #### Comment out the two next lines when polynomial rounding is activated.
1593
    #roundedPolySo = sollya_lib_copy_obj(polySo)
1594
    #roundedPolyMaxErrSo =  sollya_lib_copy_obj(maxErrorSo)
1595
    sollya_lib_clear_obj(polySo)
1596
    sollya_lib_clear_obj(maxErrorSo)
1597
    sollya_lib_clear_obj(itpSo)
1598
    sollya_lib_clear_obj(ftpSo)
1599
    sollya_lib_clear_obj(approxAccurSo)
1600
    if sollyaPrecChangedSa:
1601
        pobyso_set_prec_so_so(initialSollyaPrecSo)
1602
    sollya_lib_clear_obj(initialSollyaPrecSo)
1603
    if debug:
1604
        print "1: ", ; pobyso_autoprint(roundedPolySo)
1605
        print "2: ", ; pobyso_autoprint(currentRangeSo)
1606
        print "3: ", ; pobyso_autoprint(intervalCenterSo)
1607
        print "4: ", ; pobyso_autoprint(roundedPolyMaxErrSo)
1608
    return (roundedPolySo, currentRangeSo, intervalCenterSo, roundedPolyMaxErrSo)
1609
# End slz_compute_polynomial_and_interval_01
1610

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

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

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

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

    
2100
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
2101
    # Create a polynomial over the rationals.
2102
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
2103
    return(ratPolynomialRing(polyOfFloat))
2104
# End slz_float_poly_of_float_to_rat_poly_of_rat.
2105

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

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

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

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

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

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

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

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

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