Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (69,24 ko)

1 115 storres
r"""
2 115 storres
Sage core functions needed for the implementation of SLZ.
3 90 storres
4 115 storres
AUTHORS:
5 115 storres
- S.T. (2013-08): initial version
6 90 storres
7 115 storres
Examples:
8 119 storres
9 119 storres
TODO::
10 90 storres
"""
11 87 storres
print "sageSLZ loading..."
12 115 storres
#
13 165 storres
def slz_compute_binade(number):
14 165 storres
    """"
15 165 storres
    For a given number, compute the "binade" that is integer m such that
16 165 storres
    2^m <= number < 2^(m+1). If number == 0 return None.
17 165 storres
    """
18 165 storres
    # Checking the parameter.
19 172 storres
    # The exception construction is used to detect if number is a RealNumber
20 165 storres
    # since not all numbers have
21 165 storres
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
22 165 storres
    try:
23 165 storres
        classTree = [number.__class__] + number.mro()
24 172 storres
        # If the number is not a RealNumber (or offspring thereof) try
25 165 storres
        # to transform it.
26 165 storres
        if not sage.rings.real_mpfr.RealNumber in classTree:
27 165 storres
            numberAsRR = RR(number)
28 165 storres
        else:
29 165 storres
            numberAsRR = number
30 165 storres
    except AttributeError:
31 165 storres
        return None
32 165 storres
    # Zero special case.
33 165 storres
    if numberAsRR == 0:
34 165 storres
        return RR(-infinity)
35 165 storres
    else:
36 176 storres
        realField           = numberAsRR.parent()
37 176 storres
        numberLog2          = numberAsRR.abs().log2()
38 176 storres
        floorNumberLog2     = floor(numberLog2)
39 176 storres
        ## Do not get caught by rounding of log2() both ways.
40 176 storres
        ## When numberLog2 is an integer, compare numberAsRR
41 176 storres
        #  with 2^numberLog2.
42 176 storres
        if floorNumberLog2 == numberLog2:
43 176 storres
            if numberAsRR.abs() < realField(2^floorNumberLog2):
44 176 storres
                return floorNumberLog2 - 1
45 176 storres
            else:
46 176 storres
                return floorNumberLog2
47 176 storres
        else:
48 176 storres
            return floorNumberLog2
49 165 storres
# End slz_compute_binade
50 165 storres
51 115 storres
#
52 121 storres
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
53 119 storres
    """
54 119 storres
    For given "real number", compute the bounds of the binade it belongs to.
55 121 storres
56 121 storres
    NOTE::
57 121 storres
        When number >= 2^(emax+1), we return the "fake" binade
58 121 storres
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
59 125 storres
        with interval [-infinity, -2^(emax+1)]. We want to distinguish
60 125 storres
        this case from that of "really" invalid arguments.
61 121 storres
62 119 storres
    """
63 121 storres
    # Check the parameters.
64 125 storres
    # RealNumbers or RealNumber offspring only.
65 165 storres
    # The exception construction is necessary since not all objects have
66 125 storres
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
67 124 storres
    try:
68 124 storres
        classTree = [number.__class__] + number.mro()
69 124 storres
        if not sage.rings.real_mpfr.RealNumber in classTree:
70 124 storres
            return None
71 124 storres
    except AttributeError:
72 121 storres
        return None
73 121 storres
    # Non zero negative integers only for emin.
74 121 storres
    if emin >= 0 or int(emin) != emin:
75 121 storres
        return None
76 121 storres
    # Non zero positive integers only for emax.
77 121 storres
    if emax <= 0 or int(emax) != emax:
78 121 storres
        return None
79 121 storres
    precision = number.precision()
80 121 storres
    RF  = RealField(precision)
81 125 storres
    if number == 0:
82 125 storres
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
83 121 storres
    # A more precise RealField is needed to avoid unwanted rounding effects
84 121 storres
    # when computing number.log2().
85 121 storres
    RRF = RealField(max(2048, 2 * precision))
86 121 storres
    # number = 0 special case, the binade bounds are
87 121 storres
    # [0, 2^emin - 2^(emin-precision)]
88 121 storres
    # Begin general case
89 119 storres
    l2 = RRF(number).abs().log2()
90 121 storres
    # Another special one: beyond largest representable -> "Fake" binade.
91 121 storres
    if l2 >= emax + 1:
92 121 storres
        if number > 0:
93 125 storres
            return (RF(2^(emax+1)), RF(+infinity) )
94 121 storres
        else:
95 121 storres
            return (RF(-infinity), -RF(2^(emax+1)))
96 165 storres
    # Regular case cont'd.
97 119 storres
    offset = int(l2)
98 121 storres
    # number.abs() >= 1.
99 119 storres
    if l2 >= 0:
100 119 storres
        if number >= 0:
101 119 storres
            lb = RF(2^offset)
102 119 storres
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
103 119 storres
        else: #number < 0
104 119 storres
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
105 119 storres
            ub = -RF(2^offset)
106 121 storres
    else: # log2 < 0, number.abs() < 1.
107 119 storres
        if l2 < emin: # Denormal
108 121 storres
           # print "Denormal:", l2
109 119 storres
            if number >= 0:
110 119 storres
                lb = RF(0)
111 119 storres
                ub = RF(2^(emin)) - RF(2^(emin-precision))
112 119 storres
            else: # number <= 0
113 119 storres
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
114 119 storres
                ub = RF(0)
115 119 storres
        elif l2 > emin: # Normal number other than +/-2^emin.
116 119 storres
            if number >= 0:
117 121 storres
                if int(l2) == l2:
118 121 storres
                    lb = RF(2^(offset))
119 121 storres
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
120 121 storres
                else:
121 121 storres
                    lb = RF(2^(offset-1))
122 121 storres
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
123 119 storres
            else: # number < 0
124 121 storres
                if int(l2) == l2: # Binade limit.
125 121 storres
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
126 121 storres
                    ub = -RF(2^(offset))
127 121 storres
                else:
128 121 storres
                    lb = -RF(2^(offset) - 2^(-precision+offset))
129 121 storres
                    ub = -RF(2^(offset-1))
130 121 storres
        else: # l2== emin, number == +/-2^emin
131 119 storres
            if number >= 0:
132 119 storres
                lb = RF(2^(offset))
133 119 storres
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
134 119 storres
            else: # number < 0
135 119 storres
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
136 119 storres
                ub = -RF(2^(offset))
137 119 storres
    return (lb, ub)
138 119 storres
# End slz_compute_binade_bounds
139 119 storres
#
140 123 storres
def slz_compute_coppersmith_reduced_polynomials(inputPolynomial,
141 123 storres
                                                 alpha,
142 123 storres
                                                 N,
143 123 storres
                                                 iBound,
144 123 storres
                                                 tBound):
145 123 storres
    """
146 123 storres
    For a given set of arguments (see below), compute a list
147 123 storres
    of "reduced polynomials" that could be used to compute roots
148 123 storres
    of the inputPolynomial.
149 124 storres
    INPUT:
150 124 storres
151 124 storres
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
152 124 storres
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
153 124 storres
    - "N" -- the modulus;
154 124 storres
    - "iBound" -- the bound on the first variable;
155 124 storres
    - "tBound" -- the bound on the second variable.
156 124 storres
157 124 storres
    OUTPUT:
158 124 storres
159 124 storres
    A list of bivariate integer polynomial obtained using the Coppersmith
160 124 storres
    algorithm. The polynomials correspond to the rows of the LLL-reduce
161 124 storres
    reduced base that comply with the Coppersmith condition.
162 123 storres
    """
163 123 storres
    # Arguments check.
164 123 storres
    if iBound == 0 or tBound == 0:
165 179 storres
        return None
166 123 storres
    # End arguments check.
167 123 storres
    nAtAlpha = N^alpha
168 123 storres
    ## Building polynomials for matrix.
169 123 storres
    polyRing   = inputPolynomial.parent()
170 123 storres
    # Whatever the 2 variables are actually called, we call them
171 123 storres
    # 'i' and 't' in all the variable names.
172 123 storres
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
173 123 storres
    #print polyVars[0], type(polyVars[0])
174 123 storres
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
175 123 storres
                                              tVariable:tVariable * tBound})
176 123 storres
    polynomialsList = \
177 179 storres
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
178 123 storres
                                             alpha,
179 123 storres
                                             N,
180 123 storres
                                             iBound,
181 123 storres
                                             tBound,
182 123 storres
                                             0)
183 123 storres
    #print "Polynomials list:", polynomialsList
184 123 storres
    ## Building the proto matrix.
185 123 storres
    knownMonomials = []
186 123 storres
    protoMatrix    = []
187 123 storres
    for poly in polynomialsList:
188 123 storres
        spo_add_polynomial_coeffs_to_matrix_row(poly,
189 123 storres
                                                knownMonomials,
190 123 storres
                                                protoMatrix,
191 123 storres
                                                0)
192 123 storres
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
193 123 storres
    #print matrixToReduce
194 123 storres
    ## Reduction and checking.
195 163 storres
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
196 163 storres
    #  error message issued when previous code was used.
197 163 storres
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
198 163 storres
    reducedMatrix = matrixToReduce.LLL(fp=None)
199 123 storres
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
200 123 storres
    if not isLLLReduced:
201 179 storres
        return None
202 123 storres
    monomialsCount     = len(knownMonomials)
203 123 storres
    monomialsCountSqrt = sqrt(monomialsCount)
204 123 storres
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
205 123 storres
    #print reducedMatrix
206 123 storres
    ## Check the Coppersmith condition for each row and build the reduced
207 123 storres
    #  polynomials.
208 123 storres
    ccReducedPolynomialsList = []
209 123 storres
    for row in reducedMatrix.rows():
210 123 storres
        l2Norm = row.norm(2)
211 123 storres
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
212 123 storres
            #print (l2Norm * monomialsCountSqrt).n()
213 125 storres
            #print l2Norm.n()
214 123 storres
            ccReducedPolynomial = \
215 123 storres
                slz_compute_reduced_polynomial(row,
216 123 storres
                                               knownMonomials,
217 123 storres
                                               iVariable,
218 123 storres
                                               iBound,
219 123 storres
                                               tVariable,
220 123 storres
                                               tBound)
221 123 storres
            if not ccReducedPolynomial is None:
222 123 storres
                ccReducedPolynomialsList.append(ccReducedPolynomial)
223 123 storres
        else:
224 125 storres
            #print l2Norm.n() , ">", nAtAlpha
225 123 storres
            pass
226 123 storres
    if len(ccReducedPolynomialsList) < 2:
227 125 storres
        print "Less than 2 Coppersmith condition compliant vectors."
228 123 storres
        return ()
229 125 storres
    #print ccReducedPolynomialsList
230 123 storres
    return ccReducedPolynomialsList
231 123 storres
# End slz_compute_coppersmith_reduced_polynomials
232 123 storres
233 212 storres
def slz_compute_coppersmith_reduced_polynomials_with_lattice_volume(inputPolynomial,
234 212 storres
                                                                    alpha,
235 212 storres
                                                                    N,
236 212 storres
                                                                    iBound,
237 212 storres
                                                                    tBound):
238 212 storres
    """
239 212 storres
    For a given set of arguments (see below), compute a list
240 212 storres
    of "reduced polynomials" that could be used to compute roots
241 212 storres
    of the inputPolynomial.
242 212 storres
    Print the volume of the initial basis as well.
243 212 storres
    INPUT:
244 212 storres
245 212 storres
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
246 212 storres
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
247 212 storres
    - "N" -- the modulus;
248 212 storres
    - "iBound" -- the bound on the first variable;
249 212 storres
    - "tBound" -- the bound on the second variable.
250 212 storres
251 212 storres
    OUTPUT:
252 212 storres
253 212 storres
    A list of bivariate integer polynomial obtained using the Coppersmith
254 212 storres
    algorithm. The polynomials correspond to the rows of the LLL-reduce
255 212 storres
    reduced base that comply with the Coppersmith condition.
256 212 storres
    """
257 212 storres
    # Arguments check.
258 212 storres
    if iBound == 0 or tBound == 0:
259 212 storres
        return None
260 212 storres
    # End arguments check.
261 212 storres
    nAtAlpha = N^alpha
262 212 storres
    ## Building polynomials for matrix.
263 212 storres
    polyRing   = inputPolynomial.parent()
264 212 storres
    # Whatever the 2 variables are actually called, we call them
265 212 storres
    # 'i' and 't' in all the variable names.
266 212 storres
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
267 212 storres
    #print polyVars[0], type(polyVars[0])
268 212 storres
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
269 212 storres
                                              tVariable:tVariable * tBound})
270 212 storres
##    polynomialsList = \
271 212 storres
##        spo_polynomial_to_polynomials_list_8(initialPolynomial,
272 212 storres
##        spo_polynomial_to_polynomials_list_5(initialPolynomial,
273 212 storres
    polynomialsList = \
274 212 storres
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
275 212 storres
                                             alpha,
276 212 storres
                                             N,
277 212 storres
                                             iBound,
278 212 storres
                                             tBound,
279 212 storres
                                             0)
280 212 storres
    #print "Polynomials list:", polynomialsList
281 212 storres
    ## Building the proto matrix.
282 212 storres
    knownMonomials = []
283 212 storres
    protoMatrix    = []
284 212 storres
    for poly in polynomialsList:
285 212 storres
        spo_add_polynomial_coeffs_to_matrix_row(poly,
286 212 storres
                                                knownMonomials,
287 212 storres
                                                protoMatrix,
288 212 storres
                                                0)
289 212 storres
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
290 212 storres
    matrixToReduceTranspose = matrixToReduce.transpose()
291 212 storres
    squareMatrix = matrixToReduce * matrixToReduceTranspose
292 212 storres
    squareMatDet = det(squareMatrix)
293 212 storres
    latticeVolume = sqrt(squareMatDet)
294 212 storres
    print "Lattice volume:", latticeVolume.n()
295 212 storres
    print "Lattice volume / N:", (latticeVolume/N).n()
296 212 storres
    #print matrixToReduce
297 212 storres
    ## Reduction and checking.
298 212 storres
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
299 212 storres
    #  error message issued when previous code was used.
300 212 storres
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
301 212 storres
    reductionTimeStart = cputime()
302 212 storres
    reducedMatrix = matrixToReduce.LLL(fp=None)
303 212 storres
    reductionTime = cputime(reductionTimeStart)
304 212 storres
    print "Reduction time:", reductionTime
305 212 storres
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
306 212 storres
    if not isLLLReduced:
307 212 storres
        return None
308 212 storres
    monomialsCount     = len(knownMonomials)
309 212 storres
    monomialsCountSqrt = sqrt(monomialsCount)
310 212 storres
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
311 212 storres
    #print reducedMatrix
312 212 storres
    ## Check the Coppersmith condition for each row and build the reduced
313 212 storres
    #  polynomials.
314 212 storres
    ccReducedPolynomialsList = []
315 212 storres
    for row in reducedMatrix.rows():
316 212 storres
        l2Norm = row.norm(2)
317 212 storres
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
318 212 storres
            #print (l2Norm * monomialsCountSqrt).n()
319 212 storres
            #print l2Norm.n()
320 212 storres
            ccReducedPolynomial = \
321 212 storres
                slz_compute_reduced_polynomial(row,
322 212 storres
                                               knownMonomials,
323 212 storres
                                               iVariable,
324 212 storres
                                               iBound,
325 212 storres
                                               tVariable,
326 212 storres
                                               tBound)
327 212 storres
            if not ccReducedPolynomial is None:
328 212 storres
                ccReducedPolynomialsList.append(ccReducedPolynomial)
329 212 storres
        else:
330 212 storres
            #print l2Norm.n() , ">", nAtAlpha
331 212 storres
            pass
332 212 storres
    if len(ccReducedPolynomialsList) < 2:
333 212 storres
        print "Less than 2 Coppersmith condition compliant vectors."
334 212 storres
        return ()
335 212 storres
    #print ccReducedPolynomialsList
336 212 storres
    return ccReducedPolynomialsList
337 212 storres
# End slz_compute_coppersmith_reduced_polynomials_with_lattice volume
338 212 storres
339 122 storres
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
340 122 storres
                                                 alpha,
341 122 storres
                                                 N,
342 122 storres
                                                 iBound,
343 122 storres
                                                 tBound):
344 122 storres
    """
345 123 storres
    For a given set of arguments (see below), compute the polynomial modular
346 122 storres
    roots, if any.
347 124 storres
348 122 storres
    """
349 123 storres
    # Arguments check.
350 123 storres
    if iBound == 0 or tBound == 0:
351 123 storres
        return set()
352 123 storres
    # End arguments check.
353 122 storres
    nAtAlpha = N^alpha
354 122 storres
    ## Building polynomials for matrix.
355 122 storres
    polyRing   = inputPolynomial.parent()
356 122 storres
    # Whatever the 2 variables are actually called, we call them
357 122 storres
    # 'i' and 't' in all the variable names.
358 122 storres
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
359 125 storres
    ccReducedPolynomialsList = \
360 125 storres
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
361 125 storres
                                                     alpha,
362 125 storres
                                                     N,
363 125 storres
                                                     iBound,
364 125 storres
                                                     tBound)
365 125 storres
    if len(ccReducedPolynomialsList) == 0:
366 125 storres
        return set()
367 122 storres
    ## Create the valid (poly1 and poly2 are algebraically independent)
368 122 storres
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
369 122 storres
    # Try to mix and match all the polynomial pairs built from the
370 122 storres
    # ccReducedPolynomialsList to obtain non zero resultants.
371 122 storres
    resultantsInITuplesList = []
372 122 storres
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
373 122 storres
        for polyInnerIndex in xrange(polyOuterIndex+1,
374 122 storres
                                     len(ccReducedPolynomialsList)):
375 122 storres
            # Compute the resultant in resultants in the
376 122 storres
            # first variable (is it the optimal choice?).
377 122 storres
            resultantInI = \
378 122 storres
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
379 122 storres
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
380 122 storres
            #print "Resultant", resultantInI
381 122 storres
            # Test algebraic independence.
382 122 storres
            if not resultantInI.is_zero():
383 122 storres
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
384 122 storres
                                                 ccReducedPolynomialsList[polyInnerIndex],
385 122 storres
                                                 resultantInI))
386 122 storres
    # If no non zero resultant was found: we can't get no algebraically
387 122 storres
    # independent polynomials pair. Give up!
388 122 storres
    if len(resultantsInITuplesList) == 0:
389 123 storres
        return set()
390 123 storres
    #print resultantsInITuplesList
391 122 storres
    # Compute the roots.
392 122 storres
    Zi = ZZ[str(iVariable)]
393 122 storres
    Zt = ZZ[str(tVariable)]
394 122 storres
    polynomialRootsSet = set()
395 122 storres
    # First, solve in the second variable since resultants are in the first
396 122 storres
    # variable.
397 122 storres
    for resultantInITuple in resultantsInITuplesList:
398 122 storres
        tRootsList = Zt(resultantInITuple[2]).roots()
399 122 storres
        # For each tRoot, compute the corresponding iRoots and check
400 123 storres
        # them in the input polynomial.
401 122 storres
        for tRoot in tRootsList:
402 123 storres
            #print "tRoot:", tRoot
403 122 storres
            # Roots returned by root() are (value, multiplicity) tuples.
404 122 storres
            iRootsList = \
405 122 storres
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
406 123 storres
            print iRootsList
407 122 storres
            # The iRootsList can be empty, hence the test.
408 122 storres
            if len(iRootsList) != 0:
409 122 storres
                for iRoot in iRootsList:
410 122 storres
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
411 122 storres
                    # polyEvalModN must be an integer.
412 122 storres
                    if polyEvalModN == int(polyEvalModN):
413 122 storres
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
414 122 storres
    return polynomialRootsSet
415 122 storres
# End slz_compute_integer_polynomial_modular_roots.
416 122 storres
#
417 125 storres
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
418 125 storres
                                                 alpha,
419 125 storres
                                                 N,
420 125 storres
                                                 iBound,
421 125 storres
                                                 tBound):
422 125 storres
    """
423 125 storres
    For a given set of arguments (see below), compute the polynomial modular
424 125 storres
    roots, if any.
425 125 storres
    This version differs in the way resultants are computed.
426 125 storres
    """
427 125 storres
    # Arguments check.
428 125 storres
    if iBound == 0 or tBound == 0:
429 125 storres
        return set()
430 125 storres
    # End arguments check.
431 125 storres
    nAtAlpha = N^alpha
432 125 storres
    ## Building polynomials for matrix.
433 125 storres
    polyRing   = inputPolynomial.parent()
434 125 storres
    # Whatever the 2 variables are actually called, we call them
435 125 storres
    # 'i' and 't' in all the variable names.
436 125 storres
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
437 125 storres
    #print polyVars[0], type(polyVars[0])
438 125 storres
    ccReducedPolynomialsList = \
439 125 storres
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
440 125 storres
                                                     alpha,
441 125 storres
                                                     N,
442 125 storres
                                                     iBound,
443 125 storres
                                                     tBound)
444 125 storres
    if len(ccReducedPolynomialsList) == 0:
445 125 storres
        return set()
446 125 storres
    ## Create the valid (poly1 and poly2 are algebraically independent)
447 125 storres
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
448 125 storres
    # Try to mix and match all the polynomial pairs built from the
449 125 storres
    # ccReducedPolynomialsList to obtain non zero resultants.
450 125 storres
    resultantsInTTuplesList = []
451 125 storres
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
452 125 storres
        for polyInnerIndex in xrange(polyOuterIndex+1,
453 125 storres
                                     len(ccReducedPolynomialsList)):
454 125 storres
            # Compute the resultant in resultants in the
455 125 storres
            # first variable (is it the optimal choice?).
456 125 storres
            resultantInT = \
457 125 storres
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
458 125 storres
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
459 125 storres
            #print "Resultant", resultantInT
460 125 storres
            # Test algebraic independence.
461 125 storres
            if not resultantInT.is_zero():
462 125 storres
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
463 125 storres
                                                 ccReducedPolynomialsList[polyInnerIndex],
464 125 storres
                                                 resultantInT))
465 125 storres
    # If no non zero resultant was found: we can't get no algebraically
466 125 storres
    # independent polynomials pair. Give up!
467 125 storres
    if len(resultantsInTTuplesList) == 0:
468 125 storres
        return set()
469 125 storres
    #print resultantsInITuplesList
470 125 storres
    # Compute the roots.
471 125 storres
    Zi = ZZ[str(iVariable)]
472 125 storres
    Zt = ZZ[str(tVariable)]
473 125 storres
    polynomialRootsSet = set()
474 125 storres
    # First, solve in the second variable since resultants are in the first
475 125 storres
    # variable.
476 125 storres
    for resultantInTTuple in resultantsInTTuplesList:
477 125 storres
        iRootsList = Zi(resultantInTTuple[2]).roots()
478 125 storres
        # For each iRoot, compute the corresponding tRoots and check
479 125 storres
        # them in the input polynomial.
480 125 storres
        for iRoot in iRootsList:
481 125 storres
            #print "iRoot:", iRoot
482 125 storres
            # Roots returned by root() are (value, multiplicity) tuples.
483 125 storres
            tRootsList = \
484 125 storres
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
485 125 storres
            print tRootsList
486 125 storres
            # The tRootsList can be empty, hence the test.
487 125 storres
            if len(tRootsList) != 0:
488 125 storres
                for tRoot in tRootsList:
489 125 storres
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
490 125 storres
                    # polyEvalModN must be an integer.
491 125 storres
                    if polyEvalModN == int(polyEvalModN):
492 125 storres
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
493 125 storres
    return polynomialRootsSet
494 125 storres
# End slz_compute_integer_polynomial_modular_roots_2.
495 125 storres
#
496 61 storres
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa,
497 61 storres
                                        upperBoundSa, approxPrecSa,
498 61 storres
                                        sollyaPrecSa=None):
499 61 storres
    """
500 61 storres
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
501 61 storres
    a polynomial that approximates the function on a an interval starting
502 61 storres
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
503 61 storres
    approximates with the expected precision.
504 61 storres
    The interval upper bound is lowered until the expected approximation
505 61 storres
    precision is reached.
506 61 storres
    The polynomial, the bounds, the center of the interval and the error
507 61 storres
    are returned.
508 156 storres
    OUTPUT:
509 124 storres
    A tuple made of 4 Sollya objects:
510 124 storres
    - a polynomial;
511 124 storres
    - an range (an interval, not in the sense of number given as an interval);
512 124 storres
    - the center of the interval;
513 124 storres
    - the maximum error in the approximation of the input functionSo by the
514 124 storres
      output polynomial ; this error <= approxPrecSaS.
515 124 storres
516 61 storres
    """
517 166 storres
    ## Superficial argument check.
518 166 storres
    if lowerBoundSa > upperBoundSa:
519 166 storres
        return None
520 61 storres
    RRR = lowerBoundSa.parent()
521 176 storres
    intervalShrinkConstFactorSa = RRR('0.9')
522 61 storres
    absoluteErrorTypeSo = pobyso_absolute_so_so()
523 61 storres
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
524 61 storres
    currentUpperBoundSa = upperBoundSa
525 61 storres
    currentLowerBoundSa = lowerBoundSa
526 61 storres
    # What we want here is the polynomial without the variable change,
527 61 storres
    # since our actual variable will be x-intervalCenter defined over the
528 61 storres
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter].
529 61 storres
    (polySo, intervalCenterSo, maxErrorSo) = \
530 61 storres
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo,
531 61 storres
                                                    currentRangeSo,
532 61 storres
                                                    absoluteErrorTypeSo)
533 61 storres
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
534 61 storres
    while maxErrorSa > approxPrecSa:
535 181 storres
        print "++Approximation error:", maxErrorSa.n()
536 81 storres
        sollya_lib_clear_obj(polySo)
537 81 storres
        sollya_lib_clear_obj(intervalCenterSo)
538 120 storres
        sollya_lib_clear_obj(maxErrorSo)
539 181 storres
        # Very empirical shrinking factor.
540 176 storres
        shrinkFactorSa = 1 /  (maxErrorSa/approxPrecSa).log2().abs()
541 181 storres
        print "Shrink factor:", \
542 181 storres
              shrinkFactorSa.n(), \
543 181 storres
              intervalShrinkConstFactorSa
544 182 storres
        print
545 81 storres
        #errorRatioSa = approxPrecSa/maxErrorSa
546 61 storres
        #print "Error ratio: ", errorRatioSa
547 181 storres
        # Make sure interval shrinks.
548 81 storres
        if shrinkFactorSa > intervalShrinkConstFactorSa:
549 81 storres
            actualShrinkFactorSa = intervalShrinkConstFactorSa
550 81 storres
            #print "Fixed"
551 61 storres
        else:
552 81 storres
            actualShrinkFactorSa = shrinkFactorSa
553 81 storres
            #print "Computed",shrinkFactorSa,maxErrorSa
554 81 storres
            #print shrinkFactorSa, maxErrorSa
555 101 storres
        #print "Shrink factor", actualShrinkFactorSa
556 81 storres
        currentUpperBoundSa = currentLowerBoundSa + \
557 181 storres
                                (currentUpperBoundSa - currentLowerBoundSa) * \
558 181 storres
                                actualShrinkFactorSa
559 71 storres
        #print "Current upper bound:", currentUpperBoundSa
560 61 storres
        sollya_lib_clear_obj(currentRangeSo)
561 181 storres
        # Check what is left with the bounds.
562 101 storres
        if currentUpperBoundSa <= currentLowerBoundSa or \
563 101 storres
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
564 86 storres
            sollya_lib_clear_obj(absoluteErrorTypeSo)
565 86 storres
            print "Can't find an interval."
566 86 storres
            print "Use either or both a higher polynomial degree or a higher",
567 86 storres
            print "internal precision."
568 86 storres
            print "Aborting!"
569 86 storres
            return (None, None, None, None)
570 61 storres
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa,
571 61 storres
                                                      currentUpperBoundSa)
572 86 storres
        # print "New interval:",
573 86 storres
        # pobyso_autoprint(currentRangeSo)
574 120 storres
        #print "Second Taylor expansion call."
575 61 storres
        (polySo, intervalCenterSo, maxErrorSo) = \
576 61 storres
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo,
577 61 storres
                                                        currentRangeSo,
578 61 storres
                                                        absoluteErrorTypeSo)
579 61 storres
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
580 85 storres
        #print "Max errorSo:",
581 85 storres
        #pobyso_autoprint(maxErrorSo)
582 61 storres
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
583 85 storres
        #print "Max errorSa:", maxErrorSa
584 85 storres
        #print "Sollya prec:",
585 85 storres
        #pobyso_autoprint(sollya_lib_get_prec(None))
586 61 storres
    sollya_lib_clear_obj(absoluteErrorTypeSo)
587 176 storres
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
588 81 storres
# End slz_compute_polynomial_and_interval
589 61 storres
590 122 storres
def slz_compute_reduced_polynomial(matrixRow,
591 98 storres
                                    knownMonomials,
592 106 storres
                                    var1,
593 98 storres
                                    var1Bound,
594 106 storres
                                    var2,
595 99 storres
                                    var2Bound):
596 98 storres
    """
597 125 storres
    Compute a polynomial from a single reduced matrix row.
598 122 storres
    This function was introduced in order to avoid the computation of the
599 125 storres
    all the polynomials  from the full matrix (even those built from rows
600 125 storres
    that do no verify the Coppersmith condition) as this may involves
601 152 storres
    expensive operations over (large) integers.
602 122 storres
    """
603 122 storres
    ## Check arguments.
604 122 storres
    if len(knownMonomials) == 0:
605 122 storres
        return None
606 122 storres
    # varNounds can be zero since 0^0 returns 1.
607 122 storres
    if (var1Bound < 0) or (var2Bound < 0):
608 122 storres
        return None
609 122 storres
    ## Initialisations.
610 122 storres
    polynomialRing = knownMonomials[0].parent()
611 122 storres
    currentPolynomial = polynomialRing(0)
612 123 storres
    # TODO: use zip instead of indices.
613 122 storres
    for colIndex in xrange(0, len(knownMonomials)):
614 122 storres
        currentCoefficient = matrixRow[colIndex]
615 122 storres
        if currentCoefficient != 0:
616 122 storres
            #print "Current coefficient:", currentCoefficient
617 122 storres
            currentMonomial = knownMonomials[colIndex]
618 122 storres
            #print "Monomial as multivariate polynomial:", \
619 122 storres
            #currentMonomial, type(currentMonomial)
620 122 storres
            degreeInVar1 = currentMonomial.degree(var1)
621 123 storres
            #print "Degree in var1", var1, ":", degreeInVar1
622 122 storres
            degreeInVar2 = currentMonomial.degree(var2)
623 123 storres
            #print "Degree in var2", var2, ":", degreeInVar2
624 122 storres
            if degreeInVar1 > 0:
625 122 storres
                currentCoefficient = \
626 123 storres
                    currentCoefficient / (var1Bound^degreeInVar1)
627 122 storres
                #print "varBound1 in degree:", var1Bound^degreeInVar1
628 122 storres
                #print "Current coefficient(1)", currentCoefficient
629 122 storres
            if degreeInVar2 > 0:
630 122 storres
                currentCoefficient = \
631 123 storres
                    currentCoefficient / (var2Bound^degreeInVar2)
632 122 storres
                #print "Current coefficient(2)", currentCoefficient
633 122 storres
            #print "Current reduced monomial:", (currentCoefficient * \
634 122 storres
            #                                    currentMonomial)
635 122 storres
            currentPolynomial += (currentCoefficient * currentMonomial)
636 122 storres
            #print "Current polynomial:", currentPolynomial
637 122 storres
        # End if
638 122 storres
    # End for colIndex.
639 122 storres
    #print "Type of the current polynomial:", type(currentPolynomial)
640 122 storres
    return(currentPolynomial)
641 122 storres
# End slz_compute_reduced_polynomial
642 122 storres
#
643 122 storres
def slz_compute_reduced_polynomials(reducedMatrix,
644 122 storres
                                        knownMonomials,
645 122 storres
                                        var1,
646 122 storres
                                        var1Bound,
647 122 storres
                                        var2,
648 122 storres
                                        var2Bound):
649 122 storres
    """
650 122 storres
    Legacy function, use slz_compute_reduced_polynomials_list
651 122 storres
    """
652 122 storres
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
653 122 storres
                                                knownMonomials,
654 122 storres
                                                var1,
655 122 storres
                                                var1Bound,
656 122 storres
                                                var2,
657 122 storres
                                                var2Bound)
658 122 storres
    )
659 177 storres
#
660 122 storres
def slz_compute_reduced_polynomials_list(reducedMatrix,
661 152 storres
                                         knownMonomials,
662 152 storres
                                         var1,
663 152 storres
                                         var1Bound,
664 152 storres
                                         var2,
665 152 storres
                                         var2Bound):
666 122 storres
    """
667 98 storres
    From a reduced matrix, holding the coefficients, from a monomials list,
668 98 storres
    from the bounds of each variable, compute the corresponding polynomials
669 98 storres
    scaled back by dividing by the "right" powers of the variables bounds.
670 99 storres
671 99 storres
    The elements in knownMonomials must be of the "right" polynomial type.
672 172 storres
    They set the polynomial type of the output polynomials in list.
673 152 storres
    @param reducedMatrix:  the reduced matrix as output from LLL;
674 152 storres
    @param kwnonMonomials: the ordered list of the monomials used to
675 152 storres
                           build the polynomials;
676 152 storres
    @param var1:           the first variable (of the "right" type);
677 152 storres
    @param var1Bound:      the first variable bound;
678 152 storres
    @param var2:           the second variable (of the "right" type);
679 152 storres
    @param var2Bound:      the second variable bound.
680 152 storres
    @return: a list of polynomials obtained with the reduced coefficients
681 152 storres
             and scaled down with the bounds
682 98 storres
    """
683 99 storres
684 98 storres
    # TODO: check input arguments.
685 98 storres
    reducedPolynomials = []
686 106 storres
    #print "type var1:", type(var1), " - type var2:", type(var2)
687 98 storres
    for matrixRow in reducedMatrix.rows():
688 102 storres
        currentPolynomial = 0
689 98 storres
        for colIndex in xrange(0, len(knownMonomials)):
690 98 storres
            currentCoefficient = matrixRow[colIndex]
691 106 storres
            if currentCoefficient != 0:
692 106 storres
                #print "Current coefficient:", currentCoefficient
693 106 storres
                currentMonomial = knownMonomials[colIndex]
694 106 storres
                parentRing = currentMonomial.parent()
695 106 storres
                #print "Monomial as multivariate polynomial:", \
696 106 storres
                #currentMonomial, type(currentMonomial)
697 106 storres
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
698 106 storres
                #print "Degree in var", var1, ":", degreeInVar1
699 106 storres
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
700 106 storres
                #print "Degree in var", var2, ":", degreeInVar2
701 106 storres
                if degreeInVar1 > 0:
702 167 storres
                    currentCoefficient /= var1Bound^degreeInVar1
703 106 storres
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
704 106 storres
                    #print "Current coefficient(1)", currentCoefficient
705 106 storres
                if degreeInVar2 > 0:
706 167 storres
                    currentCoefficient /= var2Bound^degreeInVar2
707 106 storres
                    #print "Current coefficient(2)", currentCoefficient
708 106 storres
                #print "Current reduced monomial:", (currentCoefficient * \
709 106 storres
                #                                    currentMonomial)
710 106 storres
                currentPolynomial += (currentCoefficient * currentMonomial)
711 168 storres
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
712 168 storres
                    #print "!!!! constant term !!!!"
713 106 storres
                #print "Current polynomial:", currentPolynomial
714 106 storres
            # End if
715 106 storres
        # End for colIndex.
716 99 storres
        #print "Type of the current polynomial:", type(currentPolynomial)
717 99 storres
        reducedPolynomials.append(currentPolynomial)
718 98 storres
    return reducedPolynomials
719 177 storres
# End slz_compute_reduced_polynomials_list.
720 98 storres
721 177 storres
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
722 177 storres
                                                   knownMonomials,
723 177 storres
                                                   var1,
724 177 storres
                                                   var1Bound,
725 177 storres
                                                   var2,
726 177 storres
                                                   var2Bound):
727 177 storres
    """
728 177 storres
    From a list of rows, holding the coefficients, from a monomials list,
729 177 storres
    from the bounds of each variable, compute the corresponding polynomials
730 177 storres
    scaled back by dividing by the "right" powers of the variables bounds.
731 177 storres
732 177 storres
    The elements in knownMonomials must be of the "right" polynomial type.
733 177 storres
    They set the polynomial type of the output polynomials in list.
734 177 storres
    @param rowsList:       the reduced matrix as output from LLL;
735 177 storres
    @param kwnonMonomials: the ordered list of the monomials used to
736 177 storres
                           build the polynomials;
737 177 storres
    @param var1:           the first variable (of the "right" type);
738 177 storres
    @param var1Bound:      the first variable bound;
739 177 storres
    @param var2:           the second variable (of the "right" type);
740 177 storres
    @param var2Bound:      the second variable bound.
741 177 storres
    @return: a list of polynomials obtained with the reduced coefficients
742 177 storres
             and scaled down with the bounds
743 177 storres
    """
744 177 storres
745 177 storres
    # TODO: check input arguments.
746 177 storres
    reducedPolynomials = []
747 177 storres
    #print "type var1:", type(var1), " - type var2:", type(var2)
748 177 storres
    for matrixRow in rowsList:
749 177 storres
        currentPolynomial = 0
750 177 storres
        for colIndex in xrange(0, len(knownMonomials)):
751 177 storres
            currentCoefficient = matrixRow[colIndex]
752 177 storres
            if currentCoefficient != 0:
753 177 storres
                #print "Current coefficient:", currentCoefficient
754 177 storres
                currentMonomial = knownMonomials[colIndex]
755 177 storres
                parentRing = currentMonomial.parent()
756 177 storres
                #print "Monomial as multivariate polynomial:", \
757 177 storres
                #currentMonomial, type(currentMonomial)
758 177 storres
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
759 177 storres
                #print "Degree in var", var1, ":", degreeInVar1
760 177 storres
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
761 177 storres
                #print "Degree in var", var2, ":", degreeInVar2
762 177 storres
                if degreeInVar1 > 0:
763 177 storres
                    currentCoefficient /= var1Bound^degreeInVar1
764 177 storres
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
765 177 storres
                    #print "Current coefficient(1)", currentCoefficient
766 177 storres
                if degreeInVar2 > 0:
767 177 storres
                    currentCoefficient /= var2Bound^degreeInVar2
768 177 storres
                    #print "Current coefficient(2)", currentCoefficient
769 177 storres
                #print "Current reduced monomial:", (currentCoefficient * \
770 177 storres
                #                                    currentMonomial)
771 177 storres
                currentPolynomial += (currentCoefficient * currentMonomial)
772 177 storres
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
773 177 storres
                    #print "!!!! constant term !!!!"
774 177 storres
                #print "Current polynomial:", currentPolynomial
775 177 storres
            # End if
776 177 storres
        # End for colIndex.
777 177 storres
        #print "Type of the current polynomial:", type(currentPolynomial)
778 177 storres
        reducedPolynomials.append(currentPolynomial)
779 177 storres
    return reducedPolynomials
780 177 storres
# End slz_compute_reduced_polynomials_list_from_rows.
781 177 storres
#
782 114 storres
def slz_compute_scaled_function(functionSa,
783 114 storres
                                lowerBoundSa,
784 114 storres
                                upperBoundSa,
785 156 storres
                                floatingPointPrecSa,
786 156 storres
                                debug=False):
787 72 storres
    """
788 72 storres
    From a function, compute the scaled function whose domain
789 72 storres
    is included in [1, 2) and whose image is also included in [1,2).
790 72 storres
    Return a tuple:
791 72 storres
    [0]: the scaled function
792 72 storres
    [1]: the scaled domain lower bound
793 72 storres
    [2]: the scaled domain upper bound
794 72 storres
    [3]: the scaled image lower bound
795 72 storres
    [4]: the scaled image upper bound
796 72 storres
    """
797 177 storres
    ## The variable can be called anything.
798 80 storres
    x = functionSa.variables()[0]
799 72 storres
    # Scalling the domain -> [1,2[.
800 72 storres
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
801 72 storres
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
802 166 storres
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
803 80 storres
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
804 156 storres
    if debug:
805 156 storres
        print "domainScalingExpression for argument :", \
806 156 storres
        invDomainScalingExpressionSa
807 190 storres
        print "function: ", functionSa
808 190 storres
    ff = functionSa.subs({x : domainScalingExpressionSa})
809 190 storres
    ## Bless expression as a function.
810 190 storres
    ff = ff.function(x)
811 72 storres
    #ff = f.subs_expr(x==domainScalingExpressionSa)
812 177 storres
    #domainScalingFunction(x) = invDomainScalingExpressionSa
813 177 storres
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
814 151 storres
    scaledLowerBoundSa = \
815 151 storres
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
816 151 storres
    scaledUpperBoundSa = \
817 151 storres
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
818 156 storres
    if debug:
819 156 storres
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
820 156 storres
              scaledUpperBoundSa
821 72 storres
    #
822 72 storres
    # Scalling the image -> [1,2[.
823 151 storres
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
824 151 storres
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
825 72 storres
    if flbSa <= fubSa: # Increasing
826 72 storres
        imageBinadeBottomSa = floor(flbSa.log2())
827 72 storres
    else: # Decreasing
828 72 storres
        imageBinadeBottomSa = floor(fubSa.log2())
829 156 storres
    if debug:
830 156 storres
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
831 72 storres
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
832 166 storres
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
833 80 storres
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
834 156 storres
    if debug:
835 156 storres
        print "imageScalingExpression for argument :", \
836 156 storres
              invImageScalingExpressionSa
837 72 storres
    iis = invImageScalingExpressionSa.function(x)
838 72 storres
    fff = iis.subs({x:ff})
839 156 storres
    if debug:
840 156 storres
        print "fff:", fff,
841 156 storres
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
842 72 storres
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
843 72 storres
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
844 151 storres
# End slz_compute_scaled_function
845 72 storres
846 179 storres
def slz_fix_bounds_for_binades(lowerBound,
847 179 storres
                               upperBound,
848 190 storres
                               func = None,
849 179 storres
                               domainDirection = -1,
850 179 storres
                               imageDirection  = -1):
851 179 storres
    """
852 179 storres
    Assuming the function is increasing or decreasing over the
853 179 storres
    [lowerBound, upperBound] interval, return a lower bound lb and
854 179 storres
    an upper bound ub such that:
855 179 storres
    - lb and ub belong to the same binade;
856 179 storres
    - func(lb) and func(ub) belong to the same binade.
857 179 storres
    domainDirection indicate how bounds move to fit into the same binade:
858 179 storres
    - a negative value move the upper bound down;
859 179 storres
    - a positive value move the lower bound up.
860 179 storres
    imageDirection indicate how bounds move in order to have their image
861 179 storres
    fit into the same binade, variation of the function is also condidered.
862 179 storres
    For an increasing function:
863 179 storres
    - negative value moves the upper bound down (and its image value as well);
864 179 storres
    - a positive values moves the lower bound up (and its image value as well);
865 179 storres
    For a decreasing function it is the other way round.
866 179 storres
    """
867 179 storres
    ## Arguments check
868 179 storres
    if lowerBound > upperBound:
869 179 storres
        return None
870 190 storres
    if lowerBound == upperBound:
871 190 storres
        return (lowerBound, upperBound)
872 179 storres
    if func is None:
873 179 storres
        return None
874 179 storres
    #
875 179 storres
    #varFunc = func.variables()[0]
876 179 storres
    lb = lowerBound
877 179 storres
    ub = upperBound
878 179 storres
    lbBinade = slz_compute_binade(lb)
879 179 storres
    ubBinade = slz_compute_binade(ub)
880 179 storres
    ## Domain binade.
881 179 storres
    while lbBinade != ubBinade:
882 179 storres
        newIntervalWidth = (ub - lb) / 2
883 179 storres
        if domainDirection < 0:
884 179 storres
            ub = lb + newIntervalWidth
885 179 storres
            ubBinade = slz_compute_binade(ub)
886 179 storres
        else:
887 179 storres
            lb = lb + newIntervalWidth
888 179 storres
            lbBinade = slz_compute_binade(lb)
889 179 storres
    ## Image binade.
890 179 storres
    if lb == ub:
891 179 storres
        return (lb, ub)
892 179 storres
    lbImg = func(lb)
893 179 storres
    ubImg = func(ub)
894 179 storres
    funcIsInc = (ubImg >= lbImg)
895 179 storres
    lbImgBinade = slz_compute_binade(lbImg)
896 179 storres
    ubImgBinade = slz_compute_binade(ubImg)
897 179 storres
    while lbImgBinade != ubImgBinade:
898 179 storres
        newIntervalWidth = (ub - lb) / 2
899 179 storres
        if imageDirection < 0:
900 179 storres
            if funcIsInc:
901 179 storres
                ub = lb + newIntervalWidth
902 179 storres
                ubImgBinade = slz_compute_binade(func(ub))
903 179 storres
                #print ubImgBinade
904 179 storres
            else:
905 179 storres
                lb = lb + newIntervalWidth
906 179 storres
                lbImgBinade = slz_compute_binade(func(lb))
907 179 storres
                #print lbImgBinade
908 179 storres
        else:
909 179 storres
            if funcIsInc:
910 179 storres
                lb = lb + newIntervalWidth
911 179 storres
                lbImgBinade = slz_compute_binade(func(lb))
912 179 storres
                #print lbImgBinade
913 179 storres
            else:
914 179 storres
                ub = lb + newIntervalWidth
915 179 storres
                ubImgBinade = slz_compute_binade(func(ub))
916 179 storres
                #print ubImgBinade
917 179 storres
    # End while lbImgBinade != ubImgBinade:
918 179 storres
    return (lb, ub)
919 179 storres
# End slz_fix_bounds_for_binades.
920 179 storres
921 79 storres
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
922 79 storres
    # Create a polynomial over the rationals.
923 179 storres
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
924 179 storres
    return(ratPolynomialRing(polyOfFloat))
925 86 storres
# End slz_float_poly_of_float_to_rat_poly_of_rat.
926 81 storres
927 188 storres
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
928 188 storres
    """
929 188 storres
     Create a polynomial over the rationals where all denominators are
930 188 storres
     powers of two.
931 188 storres
    """
932 188 storres
    polyVariable = polyOfFloat.variables()[0]
933 188 storres
    RPR = QQ[str(polyVariable)]
934 188 storres
    polyCoeffs      = polyOfFloat.coefficients()
935 188 storres
    #print polyCoeffs
936 188 storres
    polyExponents   = polyOfFloat.exponents()
937 188 storres
    #print polyExponents
938 188 storres
    polyDenomPtwoCoeffs = []
939 188 storres
    for coeff in polyCoeffs:
940 188 storres
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
941 188 storres
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
942 188 storres
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
943 188 storres
    ratPoly = RPR(0)
944 188 storres
    #print type(ratPoly)
945 188 storres
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
946 188 storres
    #  The coefficient becomes plainly wrong when exponent == 0.
947 188 storres
    #  No clue as to why.
948 188 storres
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
949 188 storres
        ratPoly += coeff * RPR(polyVariable^exponent)
950 188 storres
    return ratPoly
951 188 storres
# End slz_float_poly_of_float_to_rat_poly_of_rat.
952 188 storres
953 80 storres
def slz_get_intervals_and_polynomials(functionSa, degreeSa,
954 63 storres
                                      lowerBoundSa,
955 60 storres
                                      upperBoundSa, floatingPointPrecSa,
956 64 storres
                                      internalSollyaPrecSa, approxPrecSa):
957 60 storres
    """
958 60 storres
    Under the assumption that:
959 60 storres
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
960 60 storres
    - lowerBound and upperBound belong to the same binade.
961 60 storres
    from a:
962 60 storres
    - function;
963 60 storres
    - a degree
964 60 storres
    - a pair of bounds;
965 60 storres
    - the floating-point precision we work on;
966 60 storres
    - the internal Sollya precision;
967 64 storres
    - the requested approximation error
968 61 storres
    The initial interval is, possibly, splitted into smaller intervals.
969 61 storres
    It return a list of tuples, each made of:
970 72 storres
    - a first polynomial (without the changed variable f(x) = p(x-x0));
971 79 storres
    - a second polynomial (with a changed variable f(x) = q(x))
972 61 storres
    - the approximation interval;
973 72 storres
    - the center, x0, of the interval;
974 61 storres
    - the corresponding approximation error.
975 100 storres
    TODO: fix endless looping for some parameters sets.
976 60 storres
    """
977 120 storres
    resultArray = []
978 101 storres
    # Set Sollya to the necessary internal precision.
979 120 storres
    precChangedSa = False
980 85 storres
    currentSollyaPrecSo = pobyso_get_prec_so()
981 85 storres
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
982 85 storres
    if internalSollyaPrecSa > currentSollyaPrecSa:
983 85 storres
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
984 120 storres
        precChangedSa = True
985 101 storres
    #
986 80 storres
    x = functionSa.variables()[0] # Actual variable name can be anything.
987 101 storres
    # Scaled function: [1=,2] -> [1,2].
988 115 storres
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
989 115 storres
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
990 115 storres
                slz_compute_scaled_function(functionSa,                       \
991 115 storres
                                            lowerBoundSa,                     \
992 115 storres
                                            upperBoundSa,                     \
993 80 storres
                                            floatingPointPrecSa)
994 166 storres
    # In case bounds were in the negative real one may need to
995 166 storres
    # switch scaled bounds.
996 166 storres
    if scaledLowerBoundSa > scaledUpperBoundSa:
997 166 storres
        scaledLowerBoundSa, scaledUpperBoundSa = \
998 166 storres
            scaledUpperBoundSa, scaledLowerBoundSa
999 166 storres
        #print "Switching!"
1000 60 storres
    print "Approximation precision: ", RR(approxPrecSa)
1001 61 storres
    # Prepare the arguments for the Taylor expansion computation with Sollya.
1002 159 storres
    functionSo = \
1003 159 storres
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
1004 60 storres
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
1005 61 storres
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa,
1006 61 storres
                                                  scaledUpperBoundSa)
1007 176 storres
1008 60 storres
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
1009 60 storres
                                              upperBoundSa.parent().precision()))
1010 176 storres
    currentScaledLowerBoundSa = scaledLowerBoundSa
1011 176 storres
    currentScaledUpperBoundSa = scaledUpperBoundSa
1012 176 storres
    while True:
1013 176 storres
        ## Compute the first Taylor expansion.
1014 176 storres
        print "Computing a Taylor expansion..."
1015 176 storres
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
1016 176 storres
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
1017 176 storres
                                        currentScaledLowerBoundSa,
1018 176 storres
                                        currentScaledUpperBoundSa,
1019 176 storres
                                        approxPrecSa, internalSollyaPrecSa)
1020 176 storres
        print "...done."
1021 176 storres
        ## If slz_compute_polynomial_and_interval fails, it returns None.
1022 176 storres
        #  This value goes to the first variable: polySo. Other variables are
1023 176 storres
        #  not assigned and should not be tested.
1024 176 storres
        if polySo is None:
1025 176 storres
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
1026 176 storres
            if precChangedSa:
1027 176 storres
                pobyso_set_prec_so_so(currentSollyaPrecSo)
1028 176 storres
                sollya_lib_clear_obj(currentSollyaPrecSo)
1029 176 storres
            sollya_lib_clear_obj(functionSo)
1030 176 storres
            sollya_lib_clear_obj(degreeSo)
1031 176 storres
            sollya_lib_clear_obj(scaledBoundsSo)
1032 176 storres
            return None
1033 176 storres
        ## Add to the result array.
1034 176 storres
        ### Change variable stuff in Sollya x -> x0-x.
1035 176 storres
        changeVarExpressionSo = \
1036 176 storres
            sollya_lib_build_function_sub( \
1037 176 storres
                              sollya_lib_build_function_free_variable(),
1038 101 storres
                              sollya_lib_copy_obj(intervalCenterSo))
1039 176 storres
        polyVarChangedSo = \
1040 176 storres
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
1041 176 storres
        #### Get rid of the variable change Sollya stuff.
1042 115 storres
        sollya_lib_clear_obj(changeVarExpressionSo)
1043 176 storres
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
1044 101 storres
                            intervalCenterSo, maxErrorSo))
1045 176 storres
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
1046 101 storres
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
1047 176 storres
        print "Computed approximation error:", errorSa.n(digits=10)
1048 176 storres
        # If the error and interval are OK a the first try, just return.
1049 176 storres
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
1050 176 storres
            (errorSa <= approxPrecSa):
1051 176 storres
            if precChangedSa:
1052 176 storres
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1053 176 storres
            sollya_lib_clear_obj(currentSollyaPrecSo)
1054 176 storres
            sollya_lib_clear_obj(functionSo)
1055 176 storres
            sollya_lib_clear_obj(degreeSo)
1056 176 storres
            sollya_lib_clear_obj(scaledBoundsSo)
1057 101 storres
            #print "Approximation error:", errorSa
1058 176 storres
            return resultArray
1059 176 storres
        ## The returned interval upper bound does not reach the requested upper
1060 176 storres
        #  upper bound: compute the next upper bound.
1061 176 storres
        ## The following ratio is always >= 1. If errorSa, we may want to
1062 176 storres
        #  enlarge the interval
1063 81 storres
        currentErrorRatio = approxPrecSa / errorSa
1064 176 storres
        ## --|--------------------------------------------------------------|--
1065 176 storres
        #  --|--------------------|--------------------------------------------
1066 176 storres
        #  --|----------------------------|------------------------------------
1067 176 storres
        ## Starting point for the next upper bound.
1068 101 storres
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
1069 101 storres
        # Compute the increment.
1070 176 storres
        newBoundsWidthSa = \
1071 176 storres
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
1072 176 storres
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
1073 176 storres
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
1074 176 storres
        # Take into account the original interval upper bound.
1075 176 storres
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
1076 176 storres
            currentScaledUpperBoundSa = scaledUpperBoundSa
1077 176 storres
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
1078 85 storres
            print "Can't shrink the interval anymore!"
1079 85 storres
            print "You should consider increasing the Sollya internal precision"
1080 85 storres
            print "or the polynomial degree."
1081 85 storres
            print "Giving up!"
1082 176 storres
            if precChangedSa:
1083 101 storres
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
1084 115 storres
            sollya_lib_clear_obj(currentSollyaPrecSo)
1085 85 storres
            sollya_lib_clear_obj(functionSo)
1086 85 storres
            sollya_lib_clear_obj(degreeSo)
1087 85 storres
            sollya_lib_clear_obj(scaledBoundsSo)
1088 85 storres
            return None
1089 176 storres
        # Compute the other expansions.
1090 176 storres
        # Test for insufficient precision.
1091 81 storres
# End slz_get_intervals_and_polynomials
1092 60 storres
1093 80 storres
def slz_interval_scaling_expression(boundsInterval, expVar):
1094 61 storres
    """
1095 151 storres
    Compute the scaling expression to map an interval that spans at most
1096 166 storres
    a single binade into [1, 2) and the inverse expression as well.
1097 165 storres
    If the interval spans more than one binade, result is wrong since
1098 165 storres
    scaling is only based on the lower bound.
1099 62 storres
    Not very sure that the transformation makes sense for negative numbers.
1100 61 storres
    """
1101 165 storres
    # The "one of the bounds is 0" special case. Here we consider
1102 165 storres
    # the interval as the subnormals binade.
1103 165 storres
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
1104 165 storres
        # The upper bound is (or should be) positive.
1105 165 storres
        if boundsInterval.endpoints()[0] == 0:
1106 165 storres
            if boundsInterval.endpoints()[1] == 0:
1107 165 storres
                return None
1108 165 storres
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1109 165 storres
            l2     = boundsInterval.endpoints()[1].abs().log2()
1110 165 storres
            # The upper bound is a power of two
1111 165 storres
            if binade == l2:
1112 165 storres
                scalingCoeff    = 2^(-binade)
1113 165 storres
                invScalingCoeff = 2^(binade)
1114 165 storres
                scalingOffset   = 1
1115 179 storres
                return \
1116 179 storres
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1117 179 storres
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1118 165 storres
            else:
1119 165 storres
                scalingCoeff    = 2^(-binade-1)
1120 165 storres
                invScalingCoeff = 2^(binade+1)
1121 165 storres
                scalingOffset   = 1
1122 165 storres
                return((scalingCoeff * expVar + scalingOffset),\
1123 165 storres
                    ((expVar - scalingOffset) * invScalingCoeff))
1124 165 storres
        # The lower bound is (or should be) negative.
1125 165 storres
        if boundsInterval.endpoints()[1] == 0:
1126 165 storres
            if boundsInterval.endpoints()[0] == 0:
1127 165 storres
                return None
1128 165 storres
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1129 165 storres
            l2     = boundsInterval.endpoints()[0].abs().log2()
1130 165 storres
            # The upper bound is a power of two
1131 165 storres
            if binade == l2:
1132 165 storres
                scalingCoeff    = -2^(-binade)
1133 165 storres
                invScalingCoeff = -2^(binade)
1134 165 storres
                scalingOffset   = 1
1135 165 storres
                return((scalingCoeff * expVar + scalingOffset),\
1136 165 storres
                    ((expVar - scalingOffset) * invScalingCoeff))
1137 165 storres
            else:
1138 165 storres
                scalingCoeff    = -2^(-binade-1)
1139 165 storres
                invScalingCoeff = -2^(binade+1)
1140 165 storres
                scalingOffset   = 1
1141 165 storres
                return((scalingCoeff * expVar + scalingOffset),\
1142 165 storres
                   ((expVar - scalingOffset) * invScalingCoeff))
1143 165 storres
    # General case
1144 165 storres
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1145 165 storres
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1146 165 storres
    # We allow for a single exception in binade spanning is when the
1147 165 storres
    # "outward bound" is a power of 2 and its binade is that of the
1148 165 storres
    # "inner bound" + 1.
1149 165 storres
    if boundsInterval.endpoints()[0] > 0:
1150 165 storres
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1151 165 storres
        if lbBinade != ubBinade:
1152 165 storres
            print "Different binades."
1153 165 storres
            if ubL2 != ubBinade:
1154 165 storres
                print "Not a power of 2."
1155 165 storres
                return None
1156 165 storres
            elif abs(ubBinade - lbBinade) > 1:
1157 165 storres
                print "Too large span:", abs(ubBinade - lbBinade)
1158 165 storres
                return None
1159 165 storres
    else:
1160 165 storres
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1161 165 storres
        if lbBinade != ubBinade:
1162 165 storres
            print "Different binades."
1163 165 storres
            if lbL2 != lbBinade:
1164 165 storres
                print "Not a power of 2."
1165 165 storres
                return None
1166 165 storres
            elif abs(ubBinade - lbBinade) > 1:
1167 165 storres
                print "Too large span:", abs(ubBinade - lbBinade)
1168 165 storres
                return None
1169 165 storres
    #print "Lower bound binade:", binade
1170 165 storres
    if boundsInterval.endpoints()[0] > 0:
1171 179 storres
        return \
1172 179 storres
            ((2^(-lbBinade) * expVar).function(expVar),
1173 179 storres
             (2^(lbBinade) * expVar).function(expVar))
1174 165 storres
    else:
1175 179 storres
        return \
1176 179 storres
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1177 179 storres
             (-(2^(ubBinade)) * expVar).function(expVar))
1178 165 storres
"""
1179 165 storres
    # Code sent to attic. Should be the base for a
1180 165 storres
    # "slz_interval_translate_expression" rather than scale.
1181 165 storres
    # Extra control and special cases code  added in
1182 165 storres
    # slz_interval_scaling_expression could (should ?) be added to
1183 165 storres
    # this new function.
1184 62 storres
    # The scaling offset is only used for negative numbers.
1185 151 storres
    # When the absolute value of the lower bound is < 0.
1186 61 storres
    if abs(boundsInterval.endpoints()[0]) < 1:
1187 61 storres
        if boundsInterval.endpoints()[0] >= 0:
1188 62 storres
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1189 62 storres
            invScalingCoeff = 1/scalingCoeff
1190 80 storres
            return((scalingCoeff * expVar,
1191 80 storres
                    invScalingCoeff * expVar))
1192 60 storres
        else:
1193 62 storres
            scalingCoeff = \
1194 62 storres
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1195 62 storres
            scalingOffset = -3 * scalingCoeff
1196 80 storres
            return((scalingCoeff * expVar + scalingOffset,
1197 80 storres
                    1/scalingCoeff * expVar + 3))
1198 61 storres
    else:
1199 61 storres
        if boundsInterval.endpoints()[0] >= 0:
1200 62 storres
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1201 61 storres
            scalingOffset = 0
1202 80 storres
            return((scalingCoeff * expVar,
1203 80 storres
                    1/scalingCoeff * expVar))
1204 61 storres
        else:
1205 62 storres
            scalingCoeff = \
1206 62 storres
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1207 62 storres
            scalingOffset =  -3 * scalingCoeff
1208 62 storres
            #scalingOffset = 0
1209 80 storres
            return((scalingCoeff * expVar + scalingOffset,
1210 80 storres
                    1/scalingCoeff * expVar + 3))
1211 165 storres
"""
1212 151 storres
# End slz_interval_scaling_expression
1213 61 storres
1214 83 storres
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1215 72 storres
    """
1216 72 storres
    Compute the Sage version of the Taylor polynomial and it's
1217 72 storres
    companion data (interval, center...)
1218 72 storres
    The input parameter is a five elements tuple:
1219 79 storres
    - [0]: the polyomial (without variable change), as polynomial over a
1220 79 storres
           real ring;
1221 79 storres
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1222 79 storres
           over a real ring;
1223 72 storres
    - [2]: the interval (as Sollya range);
1224 72 storres
    - [3]: the interval center;
1225 72 storres
    - [4]: the approximation error.
1226 72 storres
1227 72 storres
    The function return a 5 elements tuple: formed with all the
1228 72 storres
    input elements converted into their Sollya counterpart.
1229 72 storres
    """
1230 60 storres
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
1231 64 storres
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
1232 60 storres
    intervalSa = \
1233 64 storres
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1234 60 storres
    centerSa = \
1235 64 storres
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1236 60 storres
    errorSa = \
1237 64 storres
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1238 64 storres
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1239 83 storres
    # End slz_interval_and_polynomial_to_sage
1240 62 storres
1241 172 storres
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None,
1242 172 storres
            targetPlusOnePrecRF = None, quasiExactRF = None):
1243 172 storres
    """
1244 172 storres
      Check if an element (argument) of the domain of function (function)
1245 172 storres
      yields a HTRN case (rounding to next) for the target precision
1246 183 storres
      (as impersonated by targetRF) for a given accuracy (targetAccuracy).
1247 205 storres
1248 205 storres
      The strategy is:
1249 205 storres
      - compute the image at high (quasi-exact) precision;
1250 205 storres
      - round it to nearest to precision;
1251 205 storres
      - round it to exactly to (precision+1), the computed number has two
1252 205 storres
        midpoint neighbors;
1253 205 storres
      - check the distance between these neighbors and the quasi-exact value;
1254 205 storres
        - if none of them is closer than the targetAccuracy, return False,
1255 205 storres
        - else return true.
1256 205 storres
      - Powers of two are a special case when comparing the midpoint in
1257 205 storres
        the 0 direction..
1258 172 storres
    """
1259 183 storres
    ## Arguments filtering.
1260 183 storres
    ## TODO: filter the first argument for consistence.
1261 172 storres
    if targetRF is None:
1262 172 storres
        targetRF = argument.parent()
1263 172 storres
    ## Ditto for the real field holding the midpoints.
1264 172 storres
    if targetPlusOnePrecRF is None:
1265 172 storres
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1266 183 storres
    ## If no quasiExactField is provided, create a high accuracy RealField.
1267 172 storres
    if quasiExactRF is None:
1268 172 storres
        quasiExactRF = RealField(1536)
1269 195 storres
    function                      = function.function(function.variables()[0])
1270 172 storres
    exactArgument                 = quasiExactRF(argument)
1271 172 storres
    quasiExactValue               = function(exactArgument)
1272 172 storres
    roundedValue                  = targetRF(quasiExactValue)
1273 172 storres
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1274 172 storres
    # Upper midpoint.
1275 172 storres
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1276 172 storres
    # Lower midpoint.
1277 172 storres
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1278 172 storres
    binade                        = slz_compute_binade(roundedValue)
1279 172 storres
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1280 172 storres
    #print "Argument:", argument
1281 172 storres
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1282 174 storres
    #print "Binade:", binade
1283 172 storres
    #print "binadeCorrectedTargetAccuracy:", \
1284 174 storres
    #binadeCorrectedTargetAccuracy.n(prec=100)
1285 172 storres
    #print "binadeCorrectedTargetAccuracy:", \
1286 172 storres
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1287 172 storres
    #print "Upper midpoint:", \
1288 172 storres
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1289 172 storres
    #print "Rounded value :", \
1290 172 storres
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1291 172 storres
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1292 172 storres
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1293 172 storres
    #print "Lower midpoint:", \
1294 172 storres
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1295 205 storres
    ## Make quasiExactValue = 0 a special case to move it out of the way.
1296 205 storres
    if quasiExactValue == 0:
1297 205 storres
        return False
1298 205 storres
    ## Begining of the general case : check with the midpoint of
1299 172 storres
    #  greatest absolute value.
1300 172 storres
    if quasiExactValue > 0:
1301 172 storres
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1302 172 storres
           binadeCorrectedTargetAccuracy:
1303 183 storres
            #print "Too close to the upper midpoint: ", \
1304 174 storres
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1305 172 storres
            #print argument.n().str(base=16, \
1306 172 storres
            #  no_sci=False)
1307 172 storres
            #print "Lower midpoint:", \
1308 172 storres
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1309 172 storres
            #print "Value         :", \
1310 183 storres
            # quasiExactValue.n(prec=200).str(base=2)
1311 172 storres
            #print "Upper midpoint:", \
1312 172 storres
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1313 172 storres
            return True
1314 205 storres
    else: # quasiExactValue < 0, the 0 case has been previously filtered out.
1315 172 storres
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1316 172 storres
           binadeCorrectedTargetAccuracy:
1317 172 storres
            #print "Too close to the upper midpoint: ", \
1318 172 storres
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1319 172 storres
            #print argument.n().str(base=16, \
1320 172 storres
            #  no_sci=False)
1321 172 storres
            #print "Lower midpoint:", \
1322 172 storres
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1323 172 storres
            #print "Value         :", \
1324 172 storres
            #  quasiExactValue.n(prec=200).str(base=2)
1325 172 storres
            #print "Upper midpoint:", \
1326 172 storres
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1327 172 storres
            #print
1328 172 storres
            return True
1329 172 storres
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1330 172 storres
    ## For the midpoint of smaller absolute value,
1331 172 storres
    #  split cases with the powers of 2.
1332 172 storres
    if sno_abs_is_power_of_two(roundedValue):
1333 172 storres
        if quasiExactValue > 0:
1334 172 storres
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1335 172 storres
               binadeCorrectedTargetAccuracy / 2:
1336 172 storres
                #print "Lower midpoint:", \
1337 172 storres
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1338 172 storres
                #print "Value         :", \
1339 172 storres
                #  quasiExactValue.n(prec=200).str(base=2)
1340 172 storres
                #print "Upper midpoint:", \
1341 172 storres
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1342 172 storres
                #print
1343 172 storres
                return True
1344 172 storres
        else:
1345 172 storres
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1346 172 storres
              binadeCorrectedTargetAccuracy / 2:
1347 172 storres
                #print "Lower midpoint:", \
1348 172 storres
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1349 172 storres
                #print "Value         :",
1350 172 storres
                #  quasiExactValue.n(prec=200).str(base=2)
1351 172 storres
                #print "Upper midpoint:",
1352 172 storres
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1353 172 storres
                #print
1354 172 storres
                return True
1355 172 storres
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1356 172 storres
    else: ## Not a power of 2, regular comparison with the midpoint of
1357 172 storres
          #  smaller absolute value.
1358 172 storres
        if quasiExactValue > 0:
1359 172 storres
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1360 172 storres
              binadeCorrectedTargetAccuracy:
1361 172 storres
                #print "Lower midpoint:", \
1362 172 storres
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1363 172 storres
                #print "Value         :", \
1364 172 storres
                #  quasiExactValue.n(prec=200).str(base=2)
1365 172 storres
                #print "Upper midpoint:", \
1366 172 storres
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1367 172 storres
                #print
1368 172 storres
                return True
1369 172 storres
        else: # quasiExactValue <= 0
1370 172 storres
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1371 172 storres
              binadeCorrectedTargetAccuracy:
1372 172 storres
                #print "Lower midpoint:", \
1373 172 storres
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1374 172 storres
                #print "Value         :", \
1375 172 storres
                #  quasiExactValue.n(prec=200).str(base=2)
1376 172 storres
                #print "Upper midpoint:", \
1377 172 storres
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1378 172 storres
                #print
1379 172 storres
                return True
1380 172 storres
    #print "distance to the upper midpoint:", \
1381 172 storres
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1382 172 storres
    #print "distance to the lower midpoint:", \
1383 172 storres
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1384 172 storres
    return False
1385 172 storres
# End slz_is_htrn
1386 172 storres
1387 80 storres
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt,
1388 80 storres
                                                precision,
1389 80 storres
                                                targetHardnessToRound,
1390 80 storres
                                                variable1,
1391 80 storres
                                                variable2):
1392 80 storres
    """
1393 90 storres
    Creates a new multivariate polynomial with integer coefficients for use
1394 90 storres
     with the Coppersmith method.
1395 80 storres
    A the same time it computes :
1396 80 storres
    - 2^K (N);
1397 90 storres
    - 2^k (bound on the second variable)
1398 80 storres
    - lcm
1399 90 storres
1400 90 storres
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1401 90 storres
                         variables.
1402 90 storres
    :param precision: the precision of the floating-point coefficients.
1403 90 storres
    :param targetHardnessToRound: the hardness to round we want to check.
1404 90 storres
    :param variable1: the first variable of the polynomial (an expression).
1405 90 storres
    :param variable2: the second variable of the polynomial (an expression).
1406 90 storres
1407 90 storres
    :returns: a 4 elements tuple:
1408 90 storres
                - the polynomial;
1409 91 storres
                - the modulus (N);
1410 91 storres
                - the t bound;
1411 90 storres
                - the lcm used to compute the integral coefficients and the
1412 90 storres
                  module.
1413 80 storres
    """
1414 80 storres
    # Create a new integer polynomial ring.
1415 80 storres
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1416 80 storres
    # Coefficients are issued in the increasing power order.
1417 80 storres
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1418 91 storres
    # Print the reversed list for debugging.
1419 179 storres
    #print
1420 179 storres
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1421 94 storres
    # Build the list of number we compute the lcm of.
1422 80 storres
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1423 179 storres
    #print "Coefficient denominators:", coefficientDenominators
1424 80 storres
    coefficientDenominators.append(2^precision)
1425 170 storres
    coefficientDenominators.append(2^(targetHardnessToRound))
1426 87 storres
    leastCommonMultiple = lcm(coefficientDenominators)
1427 80 storres
    # Compute the expression corresponding to the new polynomial
1428 80 storres
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1429 91 storres
    #print coefficientNumerators
1430 80 storres
    polynomialExpression = 0
1431 80 storres
    power = 0
1432 170 storres
    # Iterate over two lists at the same time, stop when the shorter
1433 170 storres
    # (is this case coefficientsNumerators) is
1434 170 storres
    # exhausted. Both lists are ordered in the order of increasing powers
1435 170 storres
    # of variable1.
1436 80 storres
    for numerator, denominator in \
1437 94 storres
                        zip(coefficientNumerators, coefficientDenominators):
1438 80 storres
        multiplicator = leastCommonMultiple / denominator
1439 80 storres
        newCoefficient = numerator * multiplicator
1440 80 storres
        polynomialExpression += newCoefficient * variable1^power
1441 80 storres
        power +=1
1442 80 storres
    polynomialExpression += - variable2
1443 80 storres
    return (IP(polynomialExpression),
1444 170 storres
            leastCommonMultiple / 2^precision, # 2^K aka N.
1445 170 storres
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1446 170 storres
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1447 91 storres
            leastCommonMultiple) # If we want to make test computations.
1448 80 storres
1449 170 storres
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1450 79 storres
1451 79 storres
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat,
1452 79 storres
                                          precision):
1453 79 storres
    """
1454 79 storres
    Makes a variable substitution into the input polynomial so that the output
1455 79 storres
    polynomial can take integer arguments.
1456 79 storres
    All variables of the input polynomial "have precision p". That is to say
1457 103 storres
    that they are rationals with denominator == 2^(precision - 1):
1458 103 storres
    x = y/2^(precision - 1).
1459 79 storres
    We "incorporate" these denominators into the coefficients with,
1460 79 storres
    respectively, the "right" power.
1461 79 storres
    """
1462 79 storres
    polynomialField = ratPolyOfRat.parent()
1463 91 storres
    polynomialVariable = ratPolyOfRat.variables()[0]
1464 91 storres
    #print "The polynomial field is:", polynomialField
1465 79 storres
    return \
1466 91 storres
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1467 79 storres
                                   polynomialVariable/2^(precision-1)}))
1468 79 storres
1469 79 storres
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1470 79 storres
1471 177 storres
def slz_reduce_and_test_base(matrixToReduce,
1472 177 storres
                             nAtAlpha,
1473 177 storres
                             monomialsCountSqrt):
1474 177 storres
    """
1475 177 storres
    Reduces the basis, tests the Coppersmith condition and returns
1476 177 storres
    a list of rows that comply with the condition.
1477 177 storres
    """
1478 177 storres
    ccComplientRowsList = []
1479 177 storres
    reducedMatrix = matrixToReduce.LLL(None)
1480 177 storres
    if not reducedMatrix.is_LLL_reduced():
1481 177 storres
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1482 177 storres
    else:
1483 177 storres
        print "reducedMatrix is actually reduced."
1484 177 storres
    print "N^alpha:", nAtAlpha.n()
1485 177 storres
    rowIndex = 0
1486 177 storres
    for row in reducedMatrix.rows():
1487 177 storres
        l2Norm = row.norm(2)
1488 177 storres
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1489 177 storres
                monomialsCountSqrt.n(), ". Is vector OK?",
1490 177 storres
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1491 177 storres
            ccComplientRowsList.append(row)
1492 177 storres
            print "True"
1493 177 storres
        else:
1494 177 storres
            print "False"
1495 177 storres
    # End for
1496 177 storres
    return ccComplientRowsList
1497 177 storres
# End slz_reduce_and_test_base
1498 115 storres
1499 205 storres
def slz_resultant(poly1, poly2, elimVar):
1500 205 storres
    """
1501 205 storres
    Compute the resultant for two polynomials for a given variable
1502 205 storres
    and return the (poly1, poly2, resultant) if the resultant
1503 205 storres
    is not 0.
1504 205 storres
    Return () otherwise.
1505 205 storres
    """
1506 205 storres
    polynomialRing0    = poly1.parent()
1507 205 storres
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1508 213 storres
    if resultantInElimVar is None:
1509 213 storres
        return None
1510 205 storres
    if resultantInElimVar.is_zero():
1511 205 storres
        return None
1512 205 storres
    else:
1513 205 storres
        return resultantInElimVar
1514 205 storres
# End slz_resultant.
1515 205 storres
#
1516 177 storres
def slz_resultant_tuple(poly1, poly2, elimVar):
1517 179 storres
    """
1518 179 storres
    Compute the resultant for two polynomials for a given variable
1519 179 storres
    and return the (poly1, poly2, resultant) if the resultant
1520 180 storres
    is not 0.
1521 179 storres
    Return () otherwise.
1522 179 storres
    """
1523 181 storres
    polynomialRing0    = poly1.parent()
1524 177 storres
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1525 180 storres
    if resultantInElimVar.is_zero():
1526 177 storres
        return ()
1527 177 storres
    else:
1528 177 storres
        return (poly1, poly2, resultantInElimVar)
1529 177 storres
# End slz_resultant_tuple.
1530 177 storres
#
1531 87 storres
print "\t...sageSLZ loaded"