| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197 | 
							- """Real and complex root isolation and refinement algorithms. """
 
- from sympy.polys.densearith import (
 
-     dup_neg, dup_rshift, dup_rem,
 
-     dup_l2_norm_squared)
 
- from sympy.polys.densebasic import (
 
-     dup_LC, dup_TC, dup_degree,
 
-     dup_strip, dup_reverse,
 
-     dup_convert,
 
-     dup_terms_gcd)
 
- from sympy.polys.densetools import (
 
-     dup_clear_denoms,
 
-     dup_mirror, dup_scale, dup_shift,
 
-     dup_transform,
 
-     dup_diff,
 
-     dup_eval, dmp_eval_in,
 
-     dup_sign_variations,
 
-     dup_real_imag)
 
- from sympy.polys.euclidtools import (
 
-     dup_discriminant)
 
- from sympy.polys.factortools import (
 
-     dup_factor_list)
 
- from sympy.polys.polyerrors import (
 
-     RefinementFailed,
 
-     DomainError,
 
-     PolynomialError)
 
- from sympy.polys.sqfreetools import (
 
-     dup_sqf_part, dup_sqf_list)
 
- def dup_sturm(f, K):
 
-     """
 
-     Computes the Sturm sequence of ``f`` in ``F[x]``.
 
-     Given a univariate, square-free polynomial ``f(x)`` returns the
 
-     associated Sturm sequence ``f_0(x), ..., f_n(x)`` defined by::
 
-        f_0(x), f_1(x) = f(x), f'(x)
 
-        f_n = -rem(f_{n-2}(x), f_{n-1}(x))
 
-     Examples
 
-     ========
 
-     >>> from sympy.polys import ring, QQ
 
-     >>> R, x = ring("x", QQ)
 
-     >>> R.dup_sturm(x**3 - 2*x**2 + x - 3)
 
-     [x**3 - 2*x**2 + x - 3, 3*x**2 - 4*x + 1, 2/9*x + 25/9, -2079/4]
 
-     References
 
-     ==========
 
-     .. [1] [Davenport88]_
 
-     """
 
-     if not K.is_Field:
 
-         raise DomainError("Cannot compute Sturm sequence over %s" % K)
 
-     f = dup_sqf_part(f, K)
 
-     sturm = [f, dup_diff(f, 1, K)]
 
-     while sturm[-1]:
 
-         s = dup_rem(sturm[-2], sturm[-1], K)
 
-         sturm.append(dup_neg(s, K))
 
-     return sturm[:-1]
 
- def dup_root_upper_bound(f, K):
 
-     """Compute the LMQ upper bound for the positive roots of `f`;
 
-        LMQ (Local Max Quadratic) was developed by Akritas-Strzebonski-Vigklas.
 
-     References
 
-     ==========
 
-     .. [1] Alkiviadis G. Akritas: "Linear and Quadratic Complexity Bounds on the
 
-         Values of the Positive Roots of Polynomials"
 
-         Journal of Universal Computer Science, Vol. 15, No. 3, 523-537, 2009.
 
-     """
 
-     n, P = len(f), []
 
-     t = n * [K.one]
 
-     if dup_LC(f, K) < 0:
 
-         f = dup_neg(f, K)
 
-     f = list(reversed(f))
 
-     for i in range(0, n):
 
-         if f[i] >= 0:
 
-             continue
 
-         a, QL = K.log(-f[i], 2), []
 
-         for j in range(i + 1, n):
 
-             if f[j] <= 0:
 
-                 continue
 
-             q = t[j] + a - K.log(f[j], 2)
 
-             QL.append([q // (j - i), j])
 
-         if not QL:
 
-             continue
 
-         q = min(QL)
 
-         t[q[1]] = t[q[1]] + 1
 
-         P.append(q[0])
 
-     if not P:
 
-         return None
 
-     else:
 
-         return K.get_field()(2)**(max(P) + 1)
 
- def dup_root_lower_bound(f, K):
 
-     """Compute the LMQ lower bound for the positive roots of `f`;
 
-        LMQ (Local Max Quadratic) was developed by Akritas-Strzebonski-Vigklas.
 
-        References
 
-        ==========
 
-        .. [1] Alkiviadis G. Akritas: "Linear and Quadratic Complexity Bounds on the
 
-               Values of the Positive Roots of Polynomials"
 
-               Journal of Universal Computer Science, Vol. 15, No. 3, 523-537, 2009.
 
-     """
 
-     bound = dup_root_upper_bound(dup_reverse(f), K)
 
-     if bound is not None:
 
-         return 1/bound
 
-     else:
 
-         return None
 
- def dup_cauchy_upper_bound(f, K):
 
-     """
 
-     Compute the Cauchy upper bound on the absolute value of all roots of f,
 
-     real or complex.
 
-     References
 
-     ==========
 
-     .. [1] https://en.wikipedia.org/wiki/Geometrical_properties_of_polynomial_roots#Lagrange's_and_Cauchy's_bounds
 
-     """
 
-     n = dup_degree(f)
 
-     if n < 1:
 
-         raise PolynomialError('Polynomial has no roots.')
 
-     if K.is_ZZ:
 
-         L = K.get_field()
 
-         f, K = dup_convert(f, K, L), L
 
-     elif not K.is_QQ or K.is_RR or K.is_CC:
 
-         # We need to compute absolute value, and we are not supporting cases
 
-         # where this would take us outside the domain (or its quotient field).
 
-         raise DomainError('Cauchy bound not supported over %s' % K)
 
-     else:
 
-         f = f[:]
 
-     while K.is_zero(f[-1]):
 
-         f.pop()
 
-     if len(f) == 1:
 
-         # Monomial. All roots are zero.
 
-         return K.zero
 
-     lc = f[0]
 
-     return K.one + max(abs(n / lc) for n in f[1:])
 
- def dup_cauchy_lower_bound(f, K):
 
-     """Compute the Cauchy lower bound on the absolute value of all non-zero
 
-        roots of f, real or complex."""
 
-     g = dup_reverse(f)
 
-     if len(g) < 2:
 
-         raise PolynomialError('Polynomial has no non-zero roots.')
 
-     if K.is_ZZ:
 
-         K = K.get_field()
 
-     b = dup_cauchy_upper_bound(g, K)
 
-     return K.one / b
 
- def dup_mignotte_sep_bound_squared(f, K):
 
-     """
 
-     Return the square of the Mignotte lower bound on separation between
 
-     distinct roots of f. The square is returned so that the bound lies in
 
-     K or its quotient field.
 
-     References
 
-     ==========
 
-     .. [1] Mignotte, Maurice. "Some useful bounds." Computer algebra.
 
-         Springer, Vienna, 1982. 259-263.
 
-         https://people.dm.unipi.it/gianni/AC-EAG/Mignotte.pdf
 
-     """
 
-     n = dup_degree(f)
 
-     if n < 2:
 
-         raise PolynomialError('Polynomials of degree < 2 have no distinct roots.')
 
-     if K.is_ZZ:
 
-         L = K.get_field()
 
-         f, K = dup_convert(f, K, L), L
 
-     elif not K.is_QQ or K.is_RR or K.is_CC:
 
-         # We need to compute absolute value, and we are not supporting cases
 
-         # where this would take us outside the domain (or its quotient field).
 
-         raise DomainError('Mignotte bound not supported over %s' % K)
 
-     D = dup_discriminant(f, K)
 
-     l2sq = dup_l2_norm_squared(f, K)
 
-     return K(3)*K.abs(D) / ( K(n)**(n+1) * l2sq**(n-1) )
 
- def _mobius_from_interval(I, field):
 
-     """Convert an open interval to a Mobius transform. """
 
-     s, t = I
 
-     a, c = field.numer(s), field.denom(s)
 
-     b, d = field.numer(t), field.denom(t)
 
-     return a, b, c, d
 
- def _mobius_to_interval(M, field):
 
-     """Convert a Mobius transform to an open interval. """
 
-     a, b, c, d = M
 
-     s, t = field(a, c), field(b, d)
 
-     if s <= t:
 
-         return (s, t)
 
-     else:
 
-         return (t, s)
 
- def dup_step_refine_real_root(f, M, K, fast=False):
 
-     """One step of positive real root refinement algorithm. """
 
-     a, b, c, d = M
 
-     if a == b and c == d:
 
-         return f, (a, b, c, d)
 
-     A = dup_root_lower_bound(f, K)
 
-     if A is not None:
 
-         A = K(int(A))
 
-     else:
 
-         A = K.zero
 
-     if fast and A > 16:
 
-         f = dup_scale(f, A, K)
 
-         a, c, A = A*a, A*c, K.one
 
-     if A >= K.one:
 
-         f = dup_shift(f, A, K)
 
-         b, d = A*a + b, A*c + d
 
-         if not dup_eval(f, K.zero, K):
 
-             return f, (b, b, d, d)
 
-     f, g = dup_shift(f, K.one, K), f
 
-     a1, b1, c1, d1 = a, a + b, c, c + d
 
-     if not dup_eval(f, K.zero, K):
 
-         return f, (b1, b1, d1, d1)
 
-     k = dup_sign_variations(f, K)
 
-     if k == 1:
 
-         a, b, c, d = a1, b1, c1, d1
 
-     else:
 
-         f = dup_shift(dup_reverse(g), K.one, K)
 
-         if not dup_eval(f, K.zero, K):
 
-             f = dup_rshift(f, 1, K)
 
-         a, b, c, d = b, a + b, d, c + d
 
-     return f, (a, b, c, d)
 
- def dup_inner_refine_real_root(f, M, K, eps=None, steps=None, disjoint=None, fast=False, mobius=False):
 
-     """Refine a positive root of `f` given a Mobius transform or an interval. """
 
-     F = K.get_field()
 
-     if len(M) == 2:
 
-         a, b, c, d = _mobius_from_interval(M, F)
 
-     else:
 
-         a, b, c, d = M
 
-     while not c:
 
-         f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c,
 
-             d), K, fast=fast)
 
-     if eps is not None and steps is not None:
 
-         for i in range(0, steps):
 
-             if abs(F(a, c) - F(b, d)) >= eps:
 
-                 f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
 
-             else:
 
-                 break
 
-     else:
 
-         if eps is not None:
 
-             while abs(F(a, c) - F(b, d)) >= eps:
 
-                 f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
 
-         if steps is not None:
 
-             for i in range(0, steps):
 
-                 f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
 
-     if disjoint is not None:
 
-         while True:
 
-             u, v = _mobius_to_interval((a, b, c, d), F)
 
-             if v <= disjoint or disjoint <= u:
 
-                 break
 
-             else:
 
-                 f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast)
 
-     if not mobius:
 
-         return _mobius_to_interval((a, b, c, d), F)
 
-     else:
 
-         return f, (a, b, c, d)
 
- def dup_outer_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False):
 
-     """Refine a positive root of `f` given an interval `(s, t)`. """
 
-     a, b, c, d = _mobius_from_interval((s, t), K.get_field())
 
-     f = dup_transform(f, dup_strip([a, b]),
 
-                          dup_strip([c, d]), K)
 
-     if dup_sign_variations(f, K) != 1:
 
-         raise RefinementFailed("there should be exactly one root in (%s, %s) interval" % (s, t))
 
-     return dup_inner_refine_real_root(f, (a, b, c, d), K, eps=eps, steps=steps, disjoint=disjoint, fast=fast)
 
- def dup_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False):
 
-     """Refine real root's approximating interval to the given precision. """
 
-     if K.is_QQ:
 
-         (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring()
 
-     elif not K.is_ZZ:
 
-         raise DomainError("real root refinement not supported over %s" % K)
 
-     if s == t:
 
-         return (s, t)
 
-     if s > t:
 
-         s, t = t, s
 
-     negative = False
 
-     if s < 0:
 
-         if t <= 0:
 
-             f, s, t, negative = dup_mirror(f, K), -t, -s, True
 
-         else:
 
-             raise ValueError("Cannot refine a real root in (%s, %s)" % (s, t))
 
-     if negative and disjoint is not None:
 
-         if disjoint < 0:
 
-             disjoint = -disjoint
 
-         else:
 
-             disjoint = None
 
-     s, t = dup_outer_refine_real_root(
 
-         f, s, t, K, eps=eps, steps=steps, disjoint=disjoint, fast=fast)
 
-     if negative:
 
-         return (-t, -s)
 
-     else:
 
-         return ( s, t)
 
- def dup_inner_isolate_real_roots(f, K, eps=None, fast=False):
 
-     """Internal function for isolation positive roots up to given precision.
 
-        References
 
-        ==========
 
-            1. Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative Study of Two Real Root
 
-            Isolation Methods . Nonlinear Analysis: Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
 
-            2. Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S. Vigklas: Improving the
 
-            Performance of the Continued Fractions Method Using new Bounds of Positive Roots. Nonlinear
 
-            Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
 
-     """
 
-     a, b, c, d = K.one, K.zero, K.zero, K.one
 
-     k = dup_sign_variations(f, K)
 
-     if k == 0:
 
-         return []
 
-     if k == 1:
 
-         roots = [dup_inner_refine_real_root(
 
-             f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True)]
 
-     else:
 
-         roots, stack = [], [(a, b, c, d, f, k)]
 
-         while stack:
 
-             a, b, c, d, f, k = stack.pop()
 
-             A = dup_root_lower_bound(f, K)
 
-             if A is not None:
 
-                 A = K(int(A))
 
-             else:
 
-                 A = K.zero
 
-             if fast and A > 16:
 
-                 f = dup_scale(f, A, K)
 
-                 a, c, A = A*a, A*c, K.one
 
-             if A >= K.one:
 
-                 f = dup_shift(f, A, K)
 
-                 b, d = A*a + b, A*c + d
 
-                 if not dup_TC(f, K):
 
-                     roots.append((f, (b, b, d, d)))
 
-                     f = dup_rshift(f, 1, K)
 
-                 k = dup_sign_variations(f, K)
 
-                 if k == 0:
 
-                     continue
 
-                 if k == 1:
 
-                     roots.append(dup_inner_refine_real_root(
 
-                         f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True))
 
-                     continue
 
-             f1 = dup_shift(f, K.one, K)
 
-             a1, b1, c1, d1, r = a, a + b, c, c + d, 0
 
-             if not dup_TC(f1, K):
 
-                 roots.append((f1, (b1, b1, d1, d1)))
 
-                 f1, r = dup_rshift(f1, 1, K), 1
 
-             k1 = dup_sign_variations(f1, K)
 
-             k2 = k - k1 - r
 
-             a2, b2, c2, d2 = b, a + b, d, c + d
 
-             if k2 > 1:
 
-                 f2 = dup_shift(dup_reverse(f), K.one, K)
 
-                 if not dup_TC(f2, K):
 
-                     f2 = dup_rshift(f2, 1, K)
 
-                 k2 = dup_sign_variations(f2, K)
 
-             else:
 
-                 f2 = None
 
-             if k1 < k2:
 
-                 a1, a2, b1, b2 = a2, a1, b2, b1
 
-                 c1, c2, d1, d2 = c2, c1, d2, d1
 
-                 f1, f2, k1, k2 = f2, f1, k2, k1
 
-             if not k1:
 
-                 continue
 
-             if f1 is None:
 
-                 f1 = dup_shift(dup_reverse(f), K.one, K)
 
-                 if not dup_TC(f1, K):
 
-                     f1 = dup_rshift(f1, 1, K)
 
-             if k1 == 1:
 
-                 roots.append(dup_inner_refine_real_root(
 
-                     f1, (a1, b1, c1, d1), K, eps=eps, fast=fast, mobius=True))
 
-             else:
 
-                 stack.append((a1, b1, c1, d1, f1, k1))
 
-             if not k2:
 
-                 continue
 
-             if f2 is None:
 
-                 f2 = dup_shift(dup_reverse(f), K.one, K)
 
-                 if not dup_TC(f2, K):
 
-                     f2 = dup_rshift(f2, 1, K)
 
-             if k2 == 1:
 
-                 roots.append(dup_inner_refine_real_root(
 
-                     f2, (a2, b2, c2, d2), K, eps=eps, fast=fast, mobius=True))
 
-             else:
 
-                 stack.append((a2, b2, c2, d2, f2, k2))
 
-     return roots
 
- def _discard_if_outside_interval(f, M, inf, sup, K, negative, fast, mobius):
 
-     """Discard an isolating interval if outside ``(inf, sup)``. """
 
-     F = K.get_field()
 
-     while True:
 
-         u, v = _mobius_to_interval(M, F)
 
-         if negative:
 
-             u, v = -v, -u
 
-         if (inf is None or u >= inf) and (sup is None or v <= sup):
 
-             if not mobius:
 
-                 return u, v
 
-             else:
 
-                 return f, M
 
-         elif (sup is not None and u > sup) or (inf is not None and v < inf):
 
-             return None
 
-         else:
 
-             f, M = dup_step_refine_real_root(f, M, K, fast=fast)
 
- def dup_inner_isolate_positive_roots(f, K, eps=None, inf=None, sup=None, fast=False, mobius=False):
 
-     """Iteratively compute disjoint positive root isolation intervals. """
 
-     if sup is not None and sup < 0:
 
-         return []
 
-     roots = dup_inner_isolate_real_roots(f, K, eps=eps, fast=fast)
 
-     F, results = K.get_field(), []
 
-     if inf is not None or sup is not None:
 
-         for f, M in roots:
 
-             result = _discard_if_outside_interval(f, M, inf, sup, K, False, fast, mobius)
 
-             if result is not None:
 
-                 results.append(result)
 
-     elif not mobius:
 
-         for f, M in roots:
 
-             u, v = _mobius_to_interval(M, F)
 
-             results.append((u, v))
 
-     else:
 
-         results = roots
 
-     return results
 
- def dup_inner_isolate_negative_roots(f, K, inf=None, sup=None, eps=None, fast=False, mobius=False):
 
-     """Iteratively compute disjoint negative root isolation intervals. """
 
-     if inf is not None and inf >= 0:
 
-         return []
 
-     roots = dup_inner_isolate_real_roots(dup_mirror(f, K), K, eps=eps, fast=fast)
 
-     F, results = K.get_field(), []
 
-     if inf is not None or sup is not None:
 
-         for f, M in roots:
 
-             result = _discard_if_outside_interval(f, M, inf, sup, K, True, fast, mobius)
 
-             if result is not None:
 
-                 results.append(result)
 
-     elif not mobius:
 
-         for f, M in roots:
 
-             u, v = _mobius_to_interval(M, F)
 
-             results.append((-v, -u))
 
-     else:
 
-         results = roots
 
-     return results
 
- def _isolate_zero(f, K, inf, sup, basis=False, sqf=False):
 
-     """Handle special case of CF algorithm when ``f`` is homogeneous. """
 
-     j, f = dup_terms_gcd(f, K)
 
-     if j > 0:
 
-         F = K.get_field()
 
-         if (inf is None or inf <= 0) and (sup is None or 0 <= sup):
 
-             if not sqf:
 
-                 if not basis:
 
-                     return [((F.zero, F.zero), j)], f
 
-                 else:
 
-                     return [((F.zero, F.zero), j, [K.one, K.zero])], f
 
-             else:
 
-                 return [(F.zero, F.zero)], f
 
-     return [], f
 
- def dup_isolate_real_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False):
 
-     """Isolate real roots of a square-free polynomial using the Vincent-Akritas-Strzebonski (VAS) CF approach.
 
-        References
 
-        ==========
 
-        .. [1] Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative
 
-               Study of Two Real Root Isolation Methods. Nonlinear Analysis:
 
-               Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
 
-        .. [2] Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S.
 
-               Vigklas: Improving the Performance of the Continued Fractions
 
-               Method Using New Bounds of Positive Roots. Nonlinear Analysis:
 
-               Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
 
-     """
 
-     if K.is_QQ:
 
-         (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring()
 
-     elif not K.is_ZZ:
 
-         raise DomainError("isolation of real roots not supported over %s" % K)
 
-     if dup_degree(f) <= 0:
 
-         return []
 
-     I_zero, f = _isolate_zero(f, K, inf, sup, basis=False, sqf=True)
 
-     I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
 
-     I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
 
-     roots = sorted(I_neg + I_zero + I_pos)
 
-     if not blackbox:
 
-         return roots
 
-     else:
 
-         return [ RealInterval((a, b), f, K) for (a, b) in roots ]
 
- def dup_isolate_real_roots(f, K, eps=None, inf=None, sup=None, basis=False, fast=False):
 
-     """Isolate real roots using Vincent-Akritas-Strzebonski (VAS) continued fractions approach.
 
-        References
 
-        ==========
 
-        .. [1] Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative
 
-               Study of Two Real Root Isolation Methods. Nonlinear Analysis:
 
-               Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
 
-        .. [2] Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S.
 
-               Vigklas: Improving the Performance of the Continued Fractions
 
-               Method Using New Bounds of Positive Roots.
 
-               Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
 
-     """
 
-     if K.is_QQ:
 
-         (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring()
 
-     elif not K.is_ZZ:
 
-         raise DomainError("isolation of real roots not supported over %s" % K)
 
-     if dup_degree(f) <= 0:
 
-         return []
 
-     I_zero, f = _isolate_zero(f, K, inf, sup, basis=basis, sqf=False)
 
-     _, factors = dup_sqf_list(f, K)
 
-     if len(factors) == 1:
 
-         ((f, k),) = factors
 
-         I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
 
-         I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast)
 
-         I_neg = [ ((u, v), k) for u, v in I_neg ]
 
-         I_pos = [ ((u, v), k) for u, v in I_pos ]
 
-     else:
 
-         I_neg, I_pos = _real_isolate_and_disjoin(factors, K,
 
-             eps=eps, inf=inf, sup=sup, basis=basis, fast=fast)
 
-     return sorted(I_neg + I_zero + I_pos)
 
- def dup_isolate_real_roots_list(polys, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False):
 
-     """Isolate real roots of a list of square-free polynomial using Vincent-Akritas-Strzebonski (VAS) CF approach.
 
-        References
 
-        ==========
 
-        .. [1] Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative
 
-               Study of Two Real Root Isolation Methods. Nonlinear Analysis:
 
-               Modelling and Control, Vol. 10, No. 4, 297-304, 2005.
 
-        .. [2] Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S.
 
-               Vigklas: Improving the Performance of the Continued Fractions
 
-               Method Using New Bounds of Positive Roots.
 
-               Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008.
 
-     """
 
-     if K.is_QQ:
 
-         K, F, polys = K.get_ring(), K, polys[:]
 
-         for i, p in enumerate(polys):
 
-             polys[i] = dup_clear_denoms(p, F, K, convert=True)[1]
 
-     elif not K.is_ZZ:
 
-         raise DomainError("isolation of real roots not supported over %s" % K)
 
-     zeros, factors_dict = False, {}
 
-     if (inf is None or inf <= 0) and (sup is None or 0 <= sup):
 
-         zeros, zero_indices = True, {}
 
-     for i, p in enumerate(polys):
 
-         j, p = dup_terms_gcd(p, K)
 
-         if zeros and j > 0:
 
-             zero_indices[i] = j
 
-         for f, k in dup_factor_list(p, K)[1]:
 
-             f = tuple(f)
 
-             if f not in factors_dict:
 
-                 factors_dict[f] = {i: k}
 
-             else:
 
-                 factors_dict[f][i] = k
 
-     factors_list = []
 
-     for f, indices in factors_dict.items():
 
-         factors_list.append((list(f), indices))
 
-     I_neg, I_pos = _real_isolate_and_disjoin(factors_list, K, eps=eps,
 
-         inf=inf, sup=sup, strict=strict, basis=basis, fast=fast)
 
-     F = K.get_field()
 
-     if not zeros or not zero_indices:
 
-         I_zero = []
 
-     else:
 
-         if not basis:
 
-             I_zero = [((F.zero, F.zero), zero_indices)]
 
-         else:
 
-             I_zero = [((F.zero, F.zero), zero_indices, [K.one, K.zero])]
 
-     return sorted(I_neg + I_zero + I_pos)
 
- def _disjoint_p(M, N, strict=False):
 
-     """Check if Mobius transforms define disjoint intervals. """
 
-     a1, b1, c1, d1 = M
 
-     a2, b2, c2, d2 = N
 
-     a1d1, b1c1 = a1*d1, b1*c1
 
-     a2d2, b2c2 = a2*d2, b2*c2
 
-     if a1d1 == b1c1 and a2d2 == b2c2:
 
-         return True
 
-     if a1d1 > b1c1:
 
-         a1, c1, b1, d1 = b1, d1, a1, c1
 
-     if a2d2 > b2c2:
 
-         a2, c2, b2, d2 = b2, d2, a2, c2
 
-     if not strict:
 
-         return a2*d1 >= c2*b1 or b2*c1 <= d2*a1
 
-     else:
 
-         return a2*d1 > c2*b1 or b2*c1 < d2*a1
 
- def _real_isolate_and_disjoin(factors, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False):
 
-     """Isolate real roots of a list of polynomials and disjoin intervals. """
 
-     I_pos, I_neg = [], []
 
-     for i, (f, k) in enumerate(factors):
 
-         for F, M in dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True):
 
-             I_pos.append((F, M, k, f))
 
-         for G, N in dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True):
 
-             I_neg.append((G, N, k, f))
 
-     for i, (f, M, k, F) in enumerate(I_pos):
 
-         for j, (g, N, m, G) in enumerate(I_pos[i + 1:]):
 
-             while not _disjoint_p(M, N, strict=strict):
 
-                 f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True)
 
-                 g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True)
 
-             I_pos[i + j + 1] = (g, N, m, G)
 
-         I_pos[i] = (f, M, k, F)
 
-     for i, (f, M, k, F) in enumerate(I_neg):
 
-         for j, (g, N, m, G) in enumerate(I_neg[i + 1:]):
 
-             while not _disjoint_p(M, N, strict=strict):
 
-                 f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True)
 
-                 g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True)
 
-             I_neg[i + j + 1] = (g, N, m, G)
 
-         I_neg[i] = (f, M, k, F)
 
-     if strict:
 
-         for i, (f, M, k, F) in enumerate(I_neg):
 
-             if not M[0]:
 
-                 while not M[0]:
 
-                     f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True)
 
-                 I_neg[i] = (f, M, k, F)
 
-                 break
 
-         for j, (g, N, m, G) in enumerate(I_pos):
 
-             if not N[0]:
 
-                 while not N[0]:
 
-                     g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True)
 
-                 I_pos[j] = (g, N, m, G)
 
-                 break
 
-     field = K.get_field()
 
-     I_neg = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_neg ]
 
-     I_pos = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_pos ]
 
-     if not basis:
 
-         I_neg = [ ((-v, -u), k) for ((u, v), k, _) in I_neg ]
 
-         I_pos = [ (( u, v), k) for ((u, v), k, _) in I_pos ]
 
-     else:
 
-         I_neg = [ ((-v, -u), k, f) for ((u, v), k, f) in I_neg ]
 
-         I_pos = [ (( u, v), k, f) for ((u, v), k, f) in I_pos ]
 
-     return I_neg, I_pos
 
- def dup_count_real_roots(f, K, inf=None, sup=None):
 
-     """Returns the number of distinct real roots of ``f`` in ``[inf, sup]``. """
 
-     if dup_degree(f) <= 0:
 
-         return 0
 
-     if not K.is_Field:
 
-         R, K = K, K.get_field()
 
-         f = dup_convert(f, R, K)
 
-     sturm = dup_sturm(f, K)
 
-     if inf is None:
 
-         signs_inf = dup_sign_variations([ dup_LC(s, K)*(-1)**dup_degree(s) for s in sturm ], K)
 
-     else:
 
-         signs_inf = dup_sign_variations([ dup_eval(s, inf, K) for s in sturm ], K)
 
-     if sup is None:
 
-         signs_sup = dup_sign_variations([ dup_LC(s, K) for s in sturm ], K)
 
-     else:
 
-         signs_sup = dup_sign_variations([ dup_eval(s, sup, K) for s in sturm ], K)
 
-     count = abs(signs_inf - signs_sup)
 
-     if inf is not None and not dup_eval(f, inf, K):
 
-         count += 1
 
-     return count
 
- OO = 'OO'  # Origin of (re, im) coordinate system
 
- Q1 = 'Q1'  # Quadrant #1 (++): re > 0 and im > 0
 
- Q2 = 'Q2'  # Quadrant #2 (-+): re < 0 and im > 0
 
- Q3 = 'Q3'  # Quadrant #3 (--): re < 0 and im < 0
 
- Q4 = 'Q4'  # Quadrant #4 (+-): re > 0 and im < 0
 
- A1 = 'A1'  # Axis #1 (+0): re > 0 and im = 0
 
- A2 = 'A2'  # Axis #2 (0+): re = 0 and im > 0
 
- A3 = 'A3'  # Axis #3 (-0): re < 0 and im = 0
 
- A4 = 'A4'  # Axis #4 (0-): re = 0 and im < 0
 
- _rules_simple = {
 
-     # Q --> Q (same) => no change
 
-     (Q1, Q1): 0,
 
-     (Q2, Q2): 0,
 
-     (Q3, Q3): 0,
 
-     (Q4, Q4): 0,
 
-     # A -- CCW --> Q => +1/4 (CCW)
 
-     (A1, Q1): 1,
 
-     (A2, Q2): 1,
 
-     (A3, Q3): 1,
 
-     (A4, Q4): 1,
 
-     # A --  CW --> Q => -1/4 (CCW)
 
-     (A1, Q4): 2,
 
-     (A2, Q1): 2,
 
-     (A3, Q2): 2,
 
-     (A4, Q3): 2,
 
-     # Q -- CCW --> A => +1/4 (CCW)
 
-     (Q1, A2): 3,
 
-     (Q2, A3): 3,
 
-     (Q3, A4): 3,
 
-     (Q4, A1): 3,
 
-     # Q --  CW --> A => -1/4 (CCW)
 
-     (Q1, A1): 4,
 
-     (Q2, A2): 4,
 
-     (Q3, A3): 4,
 
-     (Q4, A4): 4,
 
-     # Q -- CCW --> Q => +1/2 (CCW)
 
-     (Q1, Q2): +5,
 
-     (Q2, Q3): +5,
 
-     (Q3, Q4): +5,
 
-     (Q4, Q1): +5,
 
-     # Q --  CW --> Q => -1/2 (CW)
 
-     (Q1, Q4): -5,
 
-     (Q2, Q1): -5,
 
-     (Q3, Q2): -5,
 
-     (Q4, Q3): -5,
 
- }
 
- _rules_ambiguous = {
 
-     # A -- CCW --> Q => { +1/4 (CCW), -9/4 (CW) }
 
-     (A1, OO, Q1): -1,
 
-     (A2, OO, Q2): -1,
 
-     (A3, OO, Q3): -1,
 
-     (A4, OO, Q4): -1,
 
-     # A --  CW --> Q => { -1/4 (CCW), +7/4 (CW) }
 
-     (A1, OO, Q4): -2,
 
-     (A2, OO, Q1): -2,
 
-     (A3, OO, Q2): -2,
 
-     (A4, OO, Q3): -2,
 
-     # Q -- CCW --> A => { +1/4 (CCW), -9/4 (CW) }
 
-     (Q1, OO, A2): -3,
 
-     (Q2, OO, A3): -3,
 
-     (Q3, OO, A4): -3,
 
-     (Q4, OO, A1): -3,
 
-     # Q --  CW --> A => { -1/4 (CCW), +7/4 (CW) }
 
-     (Q1, OO, A1): -4,
 
-     (Q2, OO, A2): -4,
 
-     (Q3, OO, A3): -4,
 
-     (Q4, OO, A4): -4,
 
-     # A --  OO --> A => { +1 (CCW), -1 (CW) }
 
-     (A1, A3): 7,
 
-     (A2, A4): 7,
 
-     (A3, A1): 7,
 
-     (A4, A2): 7,
 
-     (A1, OO, A3): 7,
 
-     (A2, OO, A4): 7,
 
-     (A3, OO, A1): 7,
 
-     (A4, OO, A2): 7,
 
-     # Q -- DIA --> Q => { +1 (CCW), -1 (CW) }
 
-     (Q1, Q3): 8,
 
-     (Q2, Q4): 8,
 
-     (Q3, Q1): 8,
 
-     (Q4, Q2): 8,
 
-     (Q1, OO, Q3): 8,
 
-     (Q2, OO, Q4): 8,
 
-     (Q3, OO, Q1): 8,
 
-     (Q4, OO, Q2): 8,
 
-     # A --- R ---> A => { +1/2 (CCW), -3/2 (CW) }
 
-     (A1, A2): 9,
 
-     (A2, A3): 9,
 
-     (A3, A4): 9,
 
-     (A4, A1): 9,
 
-     (A1, OO, A2): 9,
 
-     (A2, OO, A3): 9,
 
-     (A3, OO, A4): 9,
 
-     (A4, OO, A1): 9,
 
-     # A --- L ---> A => { +3/2 (CCW), -1/2 (CW) }
 
-     (A1, A4): 10,
 
-     (A2, A1): 10,
 
-     (A3, A2): 10,
 
-     (A4, A3): 10,
 
-     (A1, OO, A4): 10,
 
-     (A2, OO, A1): 10,
 
-     (A3, OO, A2): 10,
 
-     (A4, OO, A3): 10,
 
-     # Q --- 1 ---> A => { +3/4 (CCW), -5/4 (CW) }
 
-     (Q1, A3): 11,
 
-     (Q2, A4): 11,
 
-     (Q3, A1): 11,
 
-     (Q4, A2): 11,
 
-     (Q1, OO, A3): 11,
 
-     (Q2, OO, A4): 11,
 
-     (Q3, OO, A1): 11,
 
-     (Q4, OO, A2): 11,
 
-     # Q --- 2 ---> A => { +5/4 (CCW), -3/4 (CW) }
 
-     (Q1, A4): 12,
 
-     (Q2, A1): 12,
 
-     (Q3, A2): 12,
 
-     (Q4, A3): 12,
 
-     (Q1, OO, A4): 12,
 
-     (Q2, OO, A1): 12,
 
-     (Q3, OO, A2): 12,
 
-     (Q4, OO, A3): 12,
 
-     # A --- 1 ---> Q => { +5/4 (CCW), -3/4 (CW) }
 
-     (A1, Q3): 13,
 
-     (A2, Q4): 13,
 
-     (A3, Q1): 13,
 
-     (A4, Q2): 13,
 
-     (A1, OO, Q3): 13,
 
-     (A2, OO, Q4): 13,
 
-     (A3, OO, Q1): 13,
 
-     (A4, OO, Q2): 13,
 
-     # A --- 2 ---> Q => { +3/4 (CCW), -5/4 (CW) }
 
-     (A1, Q2): 14,
 
-     (A2, Q3): 14,
 
-     (A3, Q4): 14,
 
-     (A4, Q1): 14,
 
-     (A1, OO, Q2): 14,
 
-     (A2, OO, Q3): 14,
 
-     (A3, OO, Q4): 14,
 
-     (A4, OO, Q1): 14,
 
-     # Q --> OO --> Q => { +1/2 (CCW), -3/2 (CW) }
 
-     (Q1, OO, Q2): 15,
 
-     (Q2, OO, Q3): 15,
 
-     (Q3, OO, Q4): 15,
 
-     (Q4, OO, Q1): 15,
 
-     # Q --> OO --> Q => { +3/2 (CCW), -1/2 (CW) }
 
-     (Q1, OO, Q4): 16,
 
-     (Q2, OO, Q1): 16,
 
-     (Q3, OO, Q2): 16,
 
-     (Q4, OO, Q3): 16,
 
-     # A --> OO --> A => { +2 (CCW), 0 (CW) }
 
-     (A1, OO, A1): 17,
 
-     (A2, OO, A2): 17,
 
-     (A3, OO, A3): 17,
 
-     (A4, OO, A4): 17,
 
-     # Q --> OO --> Q => { +2 (CCW), 0 (CW) }
 
-     (Q1, OO, Q1): 18,
 
-     (Q2, OO, Q2): 18,
 
-     (Q3, OO, Q3): 18,
 
-     (Q4, OO, Q4): 18,
 
- }
 
- _values = {
 
-     0: [( 0, 1)],
 
-     1: [(+1, 4)],
 
-     2: [(-1, 4)],
 
-     3: [(+1, 4)],
 
-     4: [(-1, 4)],
 
-     -1: [(+9, 4), (+1, 4)],
 
-     -2: [(+7, 4), (-1, 4)],
 
-     -3: [(+9, 4), (+1, 4)],
 
-     -4: [(+7, 4), (-1, 4)],
 
-     +5: [(+1, 2)],
 
-     -5: [(-1, 2)],
 
-     7: [(+1, 1), (-1, 1)],
 
-     8: [(+1, 1), (-1, 1)],
 
-     9: [(+1, 2), (-3, 2)],
 
-     10: [(+3, 2), (-1, 2)],
 
-     11: [(+3, 4), (-5, 4)],
 
-     12: [(+5, 4), (-3, 4)],
 
-     13: [(+5, 4), (-3, 4)],
 
-     14: [(+3, 4), (-5, 4)],
 
-     15: [(+1, 2), (-3, 2)],
 
-     16: [(+3, 2), (-1, 2)],
 
-     17: [(+2, 1), ( 0, 1)],
 
-     18: [(+2, 1), ( 0, 1)],
 
- }
 
- def _classify_point(re, im):
 
-     """Return the half-axis (or origin) on which (re, im) point is located. """
 
-     if not re and not im:
 
-         return OO
 
-     if not re:
 
-         if im > 0:
 
-             return A2
 
-         else:
 
-             return A4
 
-     elif not im:
 
-         if re > 0:
 
-             return A1
 
-         else:
 
-             return A3
 
- def _intervals_to_quadrants(intervals, f1, f2, s, t, F):
 
-     """Generate a sequence of extended quadrants from a list of critical points. """
 
-     if not intervals:
 
-         return []
 
-     Q = []
 
-     if not f1:
 
-         (a, b), _, _ = intervals[0]
 
-         if a == b == s:
 
-             if len(intervals) == 1:
 
-                 if dup_eval(f2, t, F) > 0:
 
-                     return [OO, A2]
 
-                 else:
 
-                     return [OO, A4]
 
-             else:
 
-                 (a, _), _, _ = intervals[1]
 
-                 if dup_eval(f2, (s + a)/2, F) > 0:
 
-                     Q.extend([OO, A2])
 
-                     f2_sgn = +1
 
-                 else:
 
-                     Q.extend([OO, A4])
 
-                     f2_sgn = -1
 
-                 intervals = intervals[1:]
 
-         else:
 
-             if dup_eval(f2, s, F) > 0:
 
-                 Q.append(A2)
 
-                 f2_sgn = +1
 
-             else:
 
-                 Q.append(A4)
 
-                 f2_sgn = -1
 
-         for (a, _), indices, _ in intervals:
 
-             Q.append(OO)
 
-             if indices[1] % 2 == 1:
 
-                 f2_sgn = -f2_sgn
 
-             if a != t:
 
-                 if f2_sgn > 0:
 
-                     Q.append(A2)
 
-                 else:
 
-                     Q.append(A4)
 
-         return Q
 
-     if not f2:
 
-         (a, b), _, _ = intervals[0]
 
-         if a == b == s:
 
-             if len(intervals) == 1:
 
-                 if dup_eval(f1, t, F) > 0:
 
-                     return [OO, A1]
 
-                 else:
 
-                     return [OO, A3]
 
-             else:
 
-                 (a, _), _, _ = intervals[1]
 
-                 if dup_eval(f1, (s + a)/2, F) > 0:
 
-                     Q.extend([OO, A1])
 
-                     f1_sgn = +1
 
-                 else:
 
-                     Q.extend([OO, A3])
 
-                     f1_sgn = -1
 
-                 intervals = intervals[1:]
 
-         else:
 
-             if dup_eval(f1, s, F) > 0:
 
-                 Q.append(A1)
 
-                 f1_sgn = +1
 
-             else:
 
-                 Q.append(A3)
 
-                 f1_sgn = -1
 
-         for (a, _), indices, _ in intervals:
 
-             Q.append(OO)
 
-             if indices[0] % 2 == 1:
 
-                 f1_sgn = -f1_sgn
 
-             if a != t:
 
-                 if f1_sgn > 0:
 
-                     Q.append(A1)
 
-                 else:
 
-                     Q.append(A3)
 
-         return Q
 
-     re = dup_eval(f1, s, F)
 
-     im = dup_eval(f2, s, F)
 
-     if not re or not im:
 
-         Q.append(_classify_point(re, im))
 
-         if len(intervals) == 1:
 
-             re = dup_eval(f1, t, F)
 
-             im = dup_eval(f2, t, F)
 
-         else:
 
-             (a, _), _, _ = intervals[1]
 
-             re = dup_eval(f1, (s + a)/2, F)
 
-             im = dup_eval(f2, (s + a)/2, F)
 
-         intervals = intervals[1:]
 
-     if re > 0:
 
-         f1_sgn = +1
 
-     else:
 
-         f1_sgn = -1
 
-     if im > 0:
 
-         f2_sgn = +1
 
-     else:
 
-         f2_sgn = -1
 
-     sgn = {
 
-         (+1, +1): Q1,
 
-         (-1, +1): Q2,
 
-         (-1, -1): Q3,
 
-         (+1, -1): Q4,
 
-     }
 
-     Q.append(sgn[(f1_sgn, f2_sgn)])
 
-     for (a, b), indices, _ in intervals:
 
-         if a == b:
 
-             re = dup_eval(f1, a, F)
 
-             im = dup_eval(f2, a, F)
 
-             cls = _classify_point(re, im)
 
-             if cls is not None:
 
-                 Q.append(cls)
 
-         if 0 in indices:
 
-             if indices[0] % 2 == 1:
 
-                 f1_sgn = -f1_sgn
 
-         if 1 in indices:
 
-             if indices[1] % 2 == 1:
 
-                 f2_sgn = -f2_sgn
 
-         if not (a == b and b == t):
 
-             Q.append(sgn[(f1_sgn, f2_sgn)])
 
-     return Q
 
- def _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=None):
 
-     """Transform sequences of quadrants to a sequence of rules. """
 
-     if exclude is True:
 
-         edges = [1, 1, 0, 0]
 
-         corners = {
 
-             (0, 1): 1,
 
-             (1, 2): 1,
 
-             (2, 3): 0,
 
-             (3, 0): 1,
 
-         }
 
-     else:
 
-         edges = [0, 0, 0, 0]
 
-         corners = {
 
-             (0, 1): 0,
 
-             (1, 2): 0,
 
-             (2, 3): 0,
 
-             (3, 0): 0,
 
-         }
 
-     if exclude is not None and exclude is not True:
 
-         exclude = set(exclude)
 
-         for i, edge in enumerate(['S', 'E', 'N', 'W']):
 
-             if edge in exclude:
 
-                 edges[i] = 1
 
-         for i, corner in enumerate(['SW', 'SE', 'NE', 'NW']):
 
-             if corner in exclude:
 
-                 corners[((i - 1) % 4, i)] = 1
 
-     QQ, rules = [Q_L1, Q_L2, Q_L3, Q_L4], []
 
-     for i, Q in enumerate(QQ):
 
-         if not Q:
 
-             continue
 
-         if Q[-1] == OO:
 
-             Q = Q[:-1]
 
-         if Q[0] == OO:
 
-             j, Q = (i - 1) % 4, Q[1:]
 
-             qq = (QQ[j][-2], OO, Q[0])
 
-             if qq in _rules_ambiguous:
 
-                 rules.append((_rules_ambiguous[qq], corners[(j, i)]))
 
-             else:
 
-                 raise NotImplementedError("3 element rule (corner): " + str(qq))
 
-         q1, k = Q[0], 1
 
-         while k < len(Q):
 
-             q2, k = Q[k], k + 1
 
-             if q2 != OO:
 
-                 qq = (q1, q2)
 
-                 if qq in _rules_simple:
 
-                     rules.append((_rules_simple[qq], 0))
 
-                 elif qq in _rules_ambiguous:
 
-                     rules.append((_rules_ambiguous[qq], edges[i]))
 
-                 else:
 
-                     raise NotImplementedError("2 element rule (inside): " + str(qq))
 
-             else:
 
-                 qq, k = (q1, q2, Q[k]), k + 1
 
-                 if qq in _rules_ambiguous:
 
-                     rules.append((_rules_ambiguous[qq], edges[i]))
 
-                 else:
 
-                     raise NotImplementedError("3 element rule (edge): " + str(qq))
 
-             q1 = qq[-1]
 
-     return rules
 
- def _reverse_intervals(intervals):
 
-     """Reverse intervals for traversal from right to left and from top to bottom. """
 
-     return [ ((b, a), indices, f) for (a, b), indices, f in reversed(intervals) ]
 
- def _winding_number(T, field):
 
-     """Compute the winding number of the input polynomial, i.e. the number of roots. """
 
-     return int(sum([ field(*_values[t][i]) for t, i in T ]) / field(2))
 
- def dup_count_complex_roots(f, K, inf=None, sup=None, exclude=None):
 
-     """Count all roots in [u + v*I, s + t*I] rectangle using Collins-Krandick algorithm. """
 
-     if not K.is_ZZ and not K.is_QQ:
 
-         raise DomainError("complex root counting is not supported over %s" % K)
 
-     if K.is_ZZ:
 
-         R, F = K, K.get_field()
 
-     else:
 
-         R, F = K.get_ring(), K
 
-     f = dup_convert(f, K, F)
 
-     if inf is None or sup is None:
 
-         _, lc = dup_degree(f), abs(dup_LC(f, F))
 
-         B = 2*max([ F.quo(abs(c), lc) for c in f ])
 
-     if inf is None:
 
-         (u, v) = (-B, -B)
 
-     else:
 
-         (u, v) = inf
 
-     if sup is None:
 
-         (s, t) = (+B, +B)
 
-     else:
 
-         (s, t) = sup
 
-     f1, f2 = dup_real_imag(f, F)
 
-     f1L1F = dmp_eval_in(f1, v, 1, 1, F)
 
-     f2L1F = dmp_eval_in(f2, v, 1, 1, F)
 
-     _, f1L1R = dup_clear_denoms(f1L1F, F, R, convert=True)
 
-     _, f2L1R = dup_clear_denoms(f2L1F, F, R, convert=True)
 
-     f1L2F = dmp_eval_in(f1, s, 0, 1, F)
 
-     f2L2F = dmp_eval_in(f2, s, 0, 1, F)
 
-     _, f1L2R = dup_clear_denoms(f1L2F, F, R, convert=True)
 
-     _, f2L2R = dup_clear_denoms(f2L2F, F, R, convert=True)
 
-     f1L3F = dmp_eval_in(f1, t, 1, 1, F)
 
-     f2L3F = dmp_eval_in(f2, t, 1, 1, F)
 
-     _, f1L3R = dup_clear_denoms(f1L3F, F, R, convert=True)
 
-     _, f2L3R = dup_clear_denoms(f2L3F, F, R, convert=True)
 
-     f1L4F = dmp_eval_in(f1, u, 0, 1, F)
 
-     f2L4F = dmp_eval_in(f2, u, 0, 1, F)
 
-     _, f1L4R = dup_clear_denoms(f1L4F, F, R, convert=True)
 
-     _, f2L4R = dup_clear_denoms(f2L4F, F, R, convert=True)
 
-     S_L1 = [f1L1R, f2L1R]
 
-     S_L2 = [f1L2R, f2L2R]
 
-     S_L3 = [f1L3R, f2L3R]
 
-     S_L4 = [f1L4R, f2L4R]
 
-     I_L1 = dup_isolate_real_roots_list(S_L1, R, inf=u, sup=s, fast=True, basis=True, strict=True)
 
-     I_L2 = dup_isolate_real_roots_list(S_L2, R, inf=v, sup=t, fast=True, basis=True, strict=True)
 
-     I_L3 = dup_isolate_real_roots_list(S_L3, R, inf=u, sup=s, fast=True, basis=True, strict=True)
 
-     I_L4 = dup_isolate_real_roots_list(S_L4, R, inf=v, sup=t, fast=True, basis=True, strict=True)
 
-     I_L3 = _reverse_intervals(I_L3)
 
-     I_L4 = _reverse_intervals(I_L4)
 
-     Q_L1 = _intervals_to_quadrants(I_L1, f1L1F, f2L1F, u, s, F)
 
-     Q_L2 = _intervals_to_quadrants(I_L2, f1L2F, f2L2F, v, t, F)
 
-     Q_L3 = _intervals_to_quadrants(I_L3, f1L3F, f2L3F, s, u, F)
 
-     Q_L4 = _intervals_to_quadrants(I_L4, f1L4F, f2L4F, t, v, F)
 
-     T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=exclude)
 
-     return _winding_number(T, F)
 
- def _vertical_bisection(N, a, b, I, Q, F1, F2, f1, f2, F):
 
-     """Vertical bisection step in Collins-Krandick root isolation algorithm. """
 
-     (u, v), (s, t) = a, b
 
-     I_L1, I_L2, I_L3, I_L4 = I
 
-     Q_L1, Q_L2, Q_L3, Q_L4 = Q
 
-     f1L1F, f1L2F, f1L3F, f1L4F = F1
 
-     f2L1F, f2L2F, f2L3F, f2L4F = F2
 
-     x = (u + s) / 2
 
-     f1V = dmp_eval_in(f1, x, 0, 1, F)
 
-     f2V = dmp_eval_in(f2, x, 0, 1, F)
 
-     I_V = dup_isolate_real_roots_list([f1V, f2V], F, inf=v, sup=t, fast=True, strict=True, basis=True)
 
-     I_L1_L, I_L1_R = [], []
 
-     I_L2_L, I_L2_R = I_V, I_L2
 
-     I_L3_L, I_L3_R = [], []
 
-     I_L4_L, I_L4_R = I_L4, _reverse_intervals(I_V)
 
-     for I in I_L1:
 
-         (a, b), indices, h = I
 
-         if a == b:
 
-             if a == x:
 
-                 I_L1_L.append(I)
 
-                 I_L1_R.append(I)
 
-             elif a < x:
 
-                 I_L1_L.append(I)
 
-             else:
 
-                 I_L1_R.append(I)
 
-         else:
 
-             if b <= x:
 
-                 I_L1_L.append(I)
 
-             elif a >= x:
 
-                 I_L1_R.append(I)
 
-             else:
 
-                 a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True)
 
-                 if b <= x:
 
-                     I_L1_L.append(((a, b), indices, h))
 
-                 if a >= x:
 
-                     I_L1_R.append(((a, b), indices, h))
 
-     for I in I_L3:
 
-         (b, a), indices, h = I
 
-         if a == b:
 
-             if a == x:
 
-                 I_L3_L.append(I)
 
-                 I_L3_R.append(I)
 
-             elif a < x:
 
-                 I_L3_L.append(I)
 
-             else:
 
-                 I_L3_R.append(I)
 
-         else:
 
-             if b <= x:
 
-                 I_L3_L.append(I)
 
-             elif a >= x:
 
-                 I_L3_R.append(I)
 
-             else:
 
-                 a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True)
 
-                 if b <= x:
 
-                     I_L3_L.append(((b, a), indices, h))
 
-                 if a >= x:
 
-                     I_L3_R.append(((b, a), indices, h))
 
-     Q_L1_L = _intervals_to_quadrants(I_L1_L, f1L1F, f2L1F, u, x, F)
 
-     Q_L2_L = _intervals_to_quadrants(I_L2_L, f1V, f2V, v, t, F)
 
-     Q_L3_L = _intervals_to_quadrants(I_L3_L, f1L3F, f2L3F, x, u, F)
 
-     Q_L4_L = Q_L4
 
-     Q_L1_R = _intervals_to_quadrants(I_L1_R, f1L1F, f2L1F, x, s, F)
 
-     Q_L2_R = Q_L2
 
-     Q_L3_R = _intervals_to_quadrants(I_L3_R, f1L3F, f2L3F, s, x, F)
 
-     Q_L4_R = _intervals_to_quadrants(I_L4_R, f1V, f2V, t, v, F)
 
-     T_L = _traverse_quadrants(Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L, exclude=True)
 
-     T_R = _traverse_quadrants(Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R, exclude=True)
 
-     N_L = _winding_number(T_L, F)
 
-     N_R = _winding_number(T_R, F)
 
-     I_L = (I_L1_L, I_L2_L, I_L3_L, I_L4_L)
 
-     Q_L = (Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L)
 
-     I_R = (I_L1_R, I_L2_R, I_L3_R, I_L4_R)
 
-     Q_R = (Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R)
 
-     F1_L = (f1L1F, f1V, f1L3F, f1L4F)
 
-     F2_L = (f2L1F, f2V, f2L3F, f2L4F)
 
-     F1_R = (f1L1F, f1L2F, f1L3F, f1V)
 
-     F2_R = (f2L1F, f2L2F, f2L3F, f2V)
 
-     a, b = (u, v), (x, t)
 
-     c, d = (x, v), (s, t)
 
-     D_L = (N_L, a, b, I_L, Q_L, F1_L, F2_L)
 
-     D_R = (N_R, c, d, I_R, Q_R, F1_R, F2_R)
 
-     return D_L, D_R
 
- def _horizontal_bisection(N, a, b, I, Q, F1, F2, f1, f2, F):
 
-     """Horizontal bisection step in Collins-Krandick root isolation algorithm. """
 
-     (u, v), (s, t) = a, b
 
-     I_L1, I_L2, I_L3, I_L4 = I
 
-     Q_L1, Q_L2, Q_L3, Q_L4 = Q
 
-     f1L1F, f1L2F, f1L3F, f1L4F = F1
 
-     f2L1F, f2L2F, f2L3F, f2L4F = F2
 
-     y = (v + t) / 2
 
-     f1H = dmp_eval_in(f1, y, 1, 1, F)
 
-     f2H = dmp_eval_in(f2, y, 1, 1, F)
 
-     I_H = dup_isolate_real_roots_list([f1H, f2H], F, inf=u, sup=s, fast=True, strict=True, basis=True)
 
-     I_L1_B, I_L1_U = I_L1, I_H
 
-     I_L2_B, I_L2_U = [], []
 
-     I_L3_B, I_L3_U = _reverse_intervals(I_H), I_L3
 
-     I_L4_B, I_L4_U = [], []
 
-     for I in I_L2:
 
-         (a, b), indices, h = I
 
-         if a == b:
 
-             if a == y:
 
-                 I_L2_B.append(I)
 
-                 I_L2_U.append(I)
 
-             elif a < y:
 
-                 I_L2_B.append(I)
 
-             else:
 
-                 I_L2_U.append(I)
 
-         else:
 
-             if b <= y:
 
-                 I_L2_B.append(I)
 
-             elif a >= y:
 
-                 I_L2_U.append(I)
 
-             else:
 
-                 a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True)
 
-                 if b <= y:
 
-                     I_L2_B.append(((a, b), indices, h))
 
-                 if a >= y:
 
-                     I_L2_U.append(((a, b), indices, h))
 
-     for I in I_L4:
 
-         (b, a), indices, h = I
 
-         if a == b:
 
-             if a == y:
 
-                 I_L4_B.append(I)
 
-                 I_L4_U.append(I)
 
-             elif a < y:
 
-                 I_L4_B.append(I)
 
-             else:
 
-                 I_L4_U.append(I)
 
-         else:
 
-             if b <= y:
 
-                 I_L4_B.append(I)
 
-             elif a >= y:
 
-                 I_L4_U.append(I)
 
-             else:
 
-                 a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True)
 
-                 if b <= y:
 
-                     I_L4_B.append(((b, a), indices, h))
 
-                 if a >= y:
 
-                     I_L4_U.append(((b, a), indices, h))
 
-     Q_L1_B = Q_L1
 
-     Q_L2_B = _intervals_to_quadrants(I_L2_B, f1L2F, f2L2F, v, y, F)
 
-     Q_L3_B = _intervals_to_quadrants(I_L3_B, f1H, f2H, s, u, F)
 
-     Q_L4_B = _intervals_to_quadrants(I_L4_B, f1L4F, f2L4F, y, v, F)
 
-     Q_L1_U = _intervals_to_quadrants(I_L1_U, f1H, f2H, u, s, F)
 
-     Q_L2_U = _intervals_to_quadrants(I_L2_U, f1L2F, f2L2F, y, t, F)
 
-     Q_L3_U = Q_L3
 
-     Q_L4_U = _intervals_to_quadrants(I_L4_U, f1L4F, f2L4F, t, y, F)
 
-     T_B = _traverse_quadrants(Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B, exclude=True)
 
-     T_U = _traverse_quadrants(Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U, exclude=True)
 
-     N_B = _winding_number(T_B, F)
 
-     N_U = _winding_number(T_U, F)
 
-     I_B = (I_L1_B, I_L2_B, I_L3_B, I_L4_B)
 
-     Q_B = (Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B)
 
-     I_U = (I_L1_U, I_L2_U, I_L3_U, I_L4_U)
 
-     Q_U = (Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U)
 
-     F1_B = (f1L1F, f1L2F, f1H, f1L4F)
 
-     F2_B = (f2L1F, f2L2F, f2H, f2L4F)
 
-     F1_U = (f1H, f1L2F, f1L3F, f1L4F)
 
-     F2_U = (f2H, f2L2F, f2L3F, f2L4F)
 
-     a, b = (u, v), (s, y)
 
-     c, d = (u, y), (s, t)
 
-     D_B = (N_B, a, b, I_B, Q_B, F1_B, F2_B)
 
-     D_U = (N_U, c, d, I_U, Q_U, F1_U, F2_U)
 
-     return D_B, D_U
 
- def _depth_first_select(rectangles):
 
-     """Find a rectangle of minimum area for bisection. """
 
-     min_area, j = None, None
 
-     for i, (_, (u, v), (s, t), _, _, _, _) in enumerate(rectangles):
 
-         area = (s - u)*(t - v)
 
-         if min_area is None or area < min_area:
 
-             min_area, j = area, i
 
-     return rectangles.pop(j)
 
- def _rectangle_small_p(a, b, eps):
 
-     """Return ``True`` if the given rectangle is small enough. """
 
-     (u, v), (s, t) = a, b
 
-     if eps is not None:
 
-         return s - u < eps and t - v < eps
 
-     else:
 
-         return True
 
- def dup_isolate_complex_roots_sqf(f, K, eps=None, inf=None, sup=None, blackbox=False):
 
-     """Isolate complex roots of a square-free polynomial using Collins-Krandick algorithm. """
 
-     if not K.is_ZZ and not K.is_QQ:
 
-         raise DomainError("isolation of complex roots is not supported over %s" % K)
 
-     if dup_degree(f) <= 0:
 
-         return []
 
-     if K.is_ZZ:
 
-         F = K.get_field()
 
-     else:
 
-         F = K
 
-     f = dup_convert(f, K, F)
 
-     lc = abs(dup_LC(f, F))
 
-     B = 2*max([ F.quo(abs(c), lc) for c in f ])
 
-     (u, v), (s, t) = (-B, F.zero), (B, B)
 
-     if inf is not None:
 
-         u = inf
 
-     if sup is not None:
 
-         s = sup
 
-     if v < 0 or t <= v or s <= u:
 
-         raise ValueError("not a valid complex isolation rectangle")
 
-     f1, f2 = dup_real_imag(f, F)
 
-     f1L1 = dmp_eval_in(f1, v, 1, 1, F)
 
-     f2L1 = dmp_eval_in(f2, v, 1, 1, F)
 
-     f1L2 = dmp_eval_in(f1, s, 0, 1, F)
 
-     f2L2 = dmp_eval_in(f2, s, 0, 1, F)
 
-     f1L3 = dmp_eval_in(f1, t, 1, 1, F)
 
-     f2L3 = dmp_eval_in(f2, t, 1, 1, F)
 
-     f1L4 = dmp_eval_in(f1, u, 0, 1, F)
 
-     f2L4 = dmp_eval_in(f2, u, 0, 1, F)
 
-     S_L1 = [f1L1, f2L1]
 
-     S_L2 = [f1L2, f2L2]
 
-     S_L3 = [f1L3, f2L3]
 
-     S_L4 = [f1L4, f2L4]
 
-     I_L1 = dup_isolate_real_roots_list(S_L1, F, inf=u, sup=s, fast=True, strict=True, basis=True)
 
-     I_L2 = dup_isolate_real_roots_list(S_L2, F, inf=v, sup=t, fast=True, strict=True, basis=True)
 
-     I_L3 = dup_isolate_real_roots_list(S_L3, F, inf=u, sup=s, fast=True, strict=True, basis=True)
 
-     I_L4 = dup_isolate_real_roots_list(S_L4, F, inf=v, sup=t, fast=True, strict=True, basis=True)
 
-     I_L3 = _reverse_intervals(I_L3)
 
-     I_L4 = _reverse_intervals(I_L4)
 
-     Q_L1 = _intervals_to_quadrants(I_L1, f1L1, f2L1, u, s, F)
 
-     Q_L2 = _intervals_to_quadrants(I_L2, f1L2, f2L2, v, t, F)
 
-     Q_L3 = _intervals_to_quadrants(I_L3, f1L3, f2L3, s, u, F)
 
-     Q_L4 = _intervals_to_quadrants(I_L4, f1L4, f2L4, t, v, F)
 
-     T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4)
 
-     N = _winding_number(T, F)
 
-     if not N:
 
-         return []
 
-     I = (I_L1, I_L2, I_L3, I_L4)
 
-     Q = (Q_L1, Q_L2, Q_L3, Q_L4)
 
-     F1 = (f1L1, f1L2, f1L3, f1L4)
 
-     F2 = (f2L1, f2L2, f2L3, f2L4)
 
-     rectangles, roots = [(N, (u, v), (s, t), I, Q, F1, F2)], []
 
-     while rectangles:
 
-         N, (u, v), (s, t), I, Q, F1, F2 = _depth_first_select(rectangles)
 
-         if s - u > t - v:
 
-             D_L, D_R = _vertical_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F)
 
-             N_L, a, b, I_L, Q_L, F1_L, F2_L = D_L
 
-             N_R, c, d, I_R, Q_R, F1_R, F2_R = D_R
 
-             if N_L >= 1:
 
-                 if N_L == 1 and _rectangle_small_p(a, b, eps):
 
-                     roots.append(ComplexInterval(a, b, I_L, Q_L, F1_L, F2_L, f1, f2, F))
 
-                 else:
 
-                     rectangles.append(D_L)
 
-             if N_R >= 1:
 
-                 if N_R == 1 and _rectangle_small_p(c, d, eps):
 
-                     roots.append(ComplexInterval(c, d, I_R, Q_R, F1_R, F2_R, f1, f2, F))
 
-                 else:
 
-                     rectangles.append(D_R)
 
-         else:
 
-             D_B, D_U = _horizontal_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F)
 
-             N_B, a, b, I_B, Q_B, F1_B, F2_B = D_B
 
-             N_U, c, d, I_U, Q_U, F1_U, F2_U = D_U
 
-             if N_B >= 1:
 
-                 if N_B == 1 and _rectangle_small_p(a, b, eps):
 
-                     roots.append(ComplexInterval(
 
-                         a, b, I_B, Q_B, F1_B, F2_B, f1, f2, F))
 
-                 else:
 
-                     rectangles.append(D_B)
 
-             if N_U >= 1:
 
-                 if N_U == 1 and _rectangle_small_p(c, d, eps):
 
-                     roots.append(ComplexInterval(
 
-                         c, d, I_U, Q_U, F1_U, F2_U, f1, f2, F))
 
-                 else:
 
-                     rectangles.append(D_U)
 
-     _roots, roots = sorted(roots, key=lambda r: (r.ax, r.ay)), []
 
-     for root in _roots:
 
-         roots.extend([root.conjugate(), root])
 
-     if blackbox:
 
-         return roots
 
-     else:
 
-         return [ r.as_tuple() for r in roots ]
 
- def dup_isolate_all_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False):
 
-     """Isolate real and complex roots of a square-free polynomial ``f``. """
 
-     return (
 
-         dup_isolate_real_roots_sqf( f, K, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox),
 
-         dup_isolate_complex_roots_sqf(f, K, eps=eps, inf=inf, sup=sup, blackbox=blackbox))
 
- def dup_isolate_all_roots(f, K, eps=None, inf=None, sup=None, fast=False):
 
-     """Isolate real and complex roots of a non-square-free polynomial ``f``. """
 
-     if not K.is_ZZ and not K.is_QQ:
 
-         raise DomainError("isolation of real and complex roots is not supported over %s" % K)
 
-     _, factors = dup_sqf_list(f, K)
 
-     if len(factors) == 1:
 
-         ((f, k),) = factors
 
-         real_part, complex_part = dup_isolate_all_roots_sqf(
 
-             f, K, eps=eps, inf=inf, sup=sup, fast=fast)
 
-         real_part = [ ((a, b), k) for (a, b) in real_part ]
 
-         complex_part = [ ((a, b), k) for (a, b) in complex_part ]
 
-         return real_part, complex_part
 
-     else:
 
-         raise NotImplementedError( "only trivial square-free polynomials are supported")
 
- class RealInterval:
 
-     """A fully qualified representation of a real isolation interval. """
 
-     def __init__(self, data, f, dom):
 
-         """Initialize new real interval with complete information. """
 
-         if len(data) == 2:
 
-             s, t = data
 
-             self.neg = False
 
-             if s < 0:
 
-                 if t <= 0:
 
-                     f, s, t, self.neg = dup_mirror(f, dom), -t, -s, True
 
-                 else:
 
-                     raise ValueError("Cannot refine a real root in (%s, %s)" % (s, t))
 
-             a, b, c, d = _mobius_from_interval((s, t), dom.get_field())
 
-             f = dup_transform(f, dup_strip([a, b]),
 
-                                  dup_strip([c, d]), dom)
 
-             self.mobius = a, b, c, d
 
-         else:
 
-             self.mobius = data[:-1]
 
-             self.neg = data[-1]
 
-         self.f, self.dom = f, dom
 
-     @property
 
-     def func(self):
 
-         return RealInterval
 
-     @property
 
-     def args(self):
 
-         i = self
 
-         return (i.mobius + (i.neg,), i.f, i.dom)
 
-     def __eq__(self, other):
 
-         if type(other) is not type(self):
 
-             return False
 
-         return self.args == other.args
 
-     @property
 
-     def a(self):
 
-         """Return the position of the left end. """
 
-         field = self.dom.get_field()
 
-         a, b, c, d = self.mobius
 
-         if not self.neg:
 
-             if a*d < b*c:
 
-                 return field(a, c)
 
-             return field(b, d)
 
-         else:
 
-             if a*d > b*c:
 
-                 return -field(a, c)
 
-             return -field(b, d)
 
-     @property
 
-     def b(self):
 
-         """Return the position of the right end. """
 
-         was = self.neg
 
-         self.neg = not was
 
-         rv = -self.a
 
-         self.neg = was
 
-         return rv
 
-     @property
 
-     def dx(self):
 
-         """Return width of the real isolating interval. """
 
-         return self.b - self.a
 
-     @property
 
-     def center(self):
 
-         """Return the center of the real isolating interval. """
 
-         return (self.a + self.b)/2
 
-     @property
 
-     def max_denom(self):
 
-         """Return the largest denominator occurring in either endpoint. """
 
-         return max(self.a.denominator, self.b.denominator)
 
-     def as_tuple(self):
 
-         """Return tuple representation of real isolating interval. """
 
-         return (self.a, self.b)
 
-     def __repr__(self):
 
-         return "(%s, %s)" % (self.a, self.b)
 
-     def __contains__(self, item):
 
-         """
 
-         Say whether a complex number belongs to this real interval.
 
-         Parameters
 
-         ==========
 
-         item : pair (re, im) or number re
 
-             Either a pair giving the real and imaginary parts of the number,
 
-             or else a real number.
 
-         """
 
-         if isinstance(item, tuple):
 
-             re, im = item
 
-         else:
 
-             re, im = item, 0
 
-         return im == 0 and self.a <= re <= self.b
 
-     def is_disjoint(self, other):
 
-         """Return ``True`` if two isolation intervals are disjoint. """
 
-         if isinstance(other, RealInterval):
 
-             return (self.b < other.a or other.b < self.a)
 
-         assert isinstance(other, ComplexInterval)
 
-         return (self.b < other.ax or other.bx < self.a
 
-             or other.ay*other.by > 0)
 
-     def _inner_refine(self):
 
-         """Internal one step real root refinement procedure. """
 
-         if self.mobius is None:
 
-             return self
 
-         f, mobius = dup_inner_refine_real_root(
 
-             self.f, self.mobius, self.dom, steps=1, mobius=True)
 
-         return RealInterval(mobius + (self.neg,), f, self.dom)
 
-     def refine_disjoint(self, other):
 
-         """Refine an isolating interval until it is disjoint with another one. """
 
-         expr = self
 
-         while not expr.is_disjoint(other):
 
-             expr, other = expr._inner_refine(), other._inner_refine()
 
-         return expr, other
 
-     def refine_size(self, dx):
 
-         """Refine an isolating interval until it is of sufficiently small size. """
 
-         expr = self
 
-         while not (expr.dx < dx):
 
-             expr = expr._inner_refine()
 
-         return expr
 
-     def refine_step(self, steps=1):
 
-         """Perform several steps of real root refinement algorithm. """
 
-         expr = self
 
-         for _ in range(steps):
 
-             expr = expr._inner_refine()
 
-         return expr
 
-     def refine(self):
 
-         """Perform one step of real root refinement algorithm. """
 
-         return self._inner_refine()
 
- class ComplexInterval:
 
-     """A fully qualified representation of a complex isolation interval.
 
-     The printed form is shown as (ax, bx) x (ay, by) where (ax, ay)
 
-     and (bx, by) are the coordinates of the southwest and northeast
 
-     corners of the interval's rectangle, respectively.
 
-     Examples
 
-     ========
 
-     >>> from sympy import CRootOf, S
 
-     >>> from sympy.abc import x
 
-     >>> CRootOf.clear_cache()  # for doctest reproducibility
 
-     >>> root = CRootOf(x**10 - 2*x + 3, 9)
 
-     >>> i = root._get_interval(); i
 
-     (3/64, 3/32) x (9/8, 75/64)
 
-     The real part of the root lies within the range [0, 3/4] while
 
-     the imaginary part lies within the range [9/8, 3/2]:
 
-     >>> root.n(3)
 
-     0.0766 + 1.14*I
 
-     The width of the ranges in the x and y directions on the complex
 
-     plane are:
 
-     >>> i.dx, i.dy
 
-     (3/64, 3/64)
 
-     The center of the range is
 
-     >>> i.center
 
-     (9/128, 147/128)
 
-     The northeast coordinate of the rectangle bounding the root in the
 
-     complex plane is given by attribute b and the x and y components
 
-     are accessed by bx and by:
 
-     >>> i.b, i.bx, i.by
 
-     ((3/32, 75/64), 3/32, 75/64)
 
-     The southwest coordinate is similarly given by i.a
 
-     >>> i.a, i.ax, i.ay
 
-     ((3/64, 9/8), 3/64, 9/8)
 
-     Although the interval prints to show only the real and imaginary
 
-     range of the root, all the information of the underlying root
 
-     is contained as properties of the interval.
 
-     For example, an interval with a nonpositive imaginary range is
 
-     considered to be the conjugate. Since the y values of y are in the
 
-     range [0, 1/4] it is not the conjugate:
 
-     >>> i.conj
 
-     False
 
-     The conjugate's interval is
 
-     >>> ic = i.conjugate(); ic
 
-     (3/64, 3/32) x (-75/64, -9/8)
 
-         NOTE: the values printed still represent the x and y range
 
-         in which the root -- conjugate, in this case -- is located,
 
-         but the underlying a and b values of a root and its conjugate
 
-         are the same:
 
-         >>> assert i.a == ic.a and i.b == ic.b
 
-         What changes are the reported coordinates of the bounding rectangle:
 
-         >>> (i.ax, i.ay), (i.bx, i.by)
 
-         ((3/64, 9/8), (3/32, 75/64))
 
-         >>> (ic.ax, ic.ay), (ic.bx, ic.by)
 
-         ((3/64, -75/64), (3/32, -9/8))
 
-     The interval can be refined once:
 
-     >>> i  # for reference, this is the current interval
 
-     (3/64, 3/32) x (9/8, 75/64)
 
-     >>> i.refine()
 
-     (3/64, 3/32) x (9/8, 147/128)
 
-     Several refinement steps can be taken:
 
-     >>> i.refine_step(2)  # 2 steps
 
-     (9/128, 3/32) x (9/8, 147/128)
 
-     It is also possible to refine to a given tolerance:
 
-     >>> tol = min(i.dx, i.dy)/2
 
-     >>> i.refine_size(tol)
 
-     (9/128, 21/256) x (9/8, 291/256)
 
-     A disjoint interval is one whose bounding rectangle does not
 
-     overlap with another. An interval, necessarily, is not disjoint with
 
-     itself, but any interval is disjoint with a conjugate since the
 
-     conjugate rectangle will always be in the lower half of the complex
 
-     plane and the non-conjugate in the upper half:
 
-     >>> i.is_disjoint(i), i.is_disjoint(i.conjugate())
 
-     (False, True)
 
-     The following interval j is not disjoint from i:
 
-     >>> close = CRootOf(x**10 - 2*x + 300/S(101), 9)
 
-     >>> j = close._get_interval(); j
 
-     (75/1616, 75/808) x (225/202, 1875/1616)
 
-     >>> i.is_disjoint(j)
 
-     False
 
-     The two can be made disjoint, however:
 
-     >>> newi, newj = i.refine_disjoint(j)
 
-     >>> newi
 
-     (39/512, 159/2048) x (2325/2048, 4653/4096)
 
-     >>> newj
 
-     (3975/51712, 2025/25856) x (29325/25856, 117375/103424)
 
-     Even though the real ranges overlap, the imaginary do not, so
 
-     the roots have been resolved as distinct. Intervals are disjoint
 
-     when either the real or imaginary component of the intervals is
 
-     distinct. In the case above, the real components have not been
 
-     resolved (so we do not know, yet, which root has the smaller real
 
-     part) but the imaginary part of ``close`` is larger than ``root``:
 
-     >>> close.n(3)
 
-     0.0771 + 1.13*I
 
-     >>> root.n(3)
 
-     0.0766 + 1.14*I
 
-     """
 
-     def __init__(self, a, b, I, Q, F1, F2, f1, f2, dom, conj=False):
 
-         """Initialize new complex interval with complete information. """
 
-         # a and b are the SW and NE corner of the bounding interval,
 
-         # (ax, ay) and (bx, by), respectively, for the NON-CONJUGATE
 
-         # root (the one with the positive imaginary part); when working
 
-         # with the conjugate, the a and b value are still non-negative
 
-         # but the ay, by are reversed and have oppositite sign
 
-         self.a, self.b = a, b
 
-         self.I, self.Q = I, Q
 
-         self.f1, self.F1 = f1, F1
 
-         self.f2, self.F2 = f2, F2
 
-         self.dom = dom
 
-         self.conj = conj
 
-     @property
 
-     def func(self):
 
-         return ComplexInterval
 
-     @property
 
-     def args(self):
 
-         i = self
 
-         return (i.a, i.b, i.I, i.Q, i.F1, i.F2, i.f1, i.f2, i.dom, i.conj)
 
-     def __eq__(self, other):
 
-         if type(other) is not type(self):
 
-             return False
 
-         return self.args == other.args
 
-     @property
 
-     def ax(self):
 
-         """Return ``x`` coordinate of south-western corner. """
 
-         return self.a[0]
 
-     @property
 
-     def ay(self):
 
-         """Return ``y`` coordinate of south-western corner. """
 
-         if not self.conj:
 
-             return self.a[1]
 
-         else:
 
-             return -self.b[1]
 
-     @property
 
-     def bx(self):
 
-         """Return ``x`` coordinate of north-eastern corner. """
 
-         return self.b[0]
 
-     @property
 
-     def by(self):
 
-         """Return ``y`` coordinate of north-eastern corner. """
 
-         if not self.conj:
 
-             return self.b[1]
 
-         else:
 
-             return -self.a[1]
 
-     @property
 
-     def dx(self):
 
-         """Return width of the complex isolating interval. """
 
-         return self.b[0] - self.a[0]
 
-     @property
 
-     def dy(self):
 
-         """Return height of the complex isolating interval. """
 
-         return self.b[1] - self.a[1]
 
-     @property
 
-     def center(self):
 
-         """Return the center of the complex isolating interval. """
 
-         return ((self.ax + self.bx)/2, (self.ay + self.by)/2)
 
-     @property
 
-     def max_denom(self):
 
-         """Return the largest denominator occurring in either endpoint. """
 
-         return max(self.ax.denominator, self.bx.denominator,
 
-                    self.ay.denominator, self.by.denominator)
 
-     def as_tuple(self):
 
-         """Return tuple representation of the complex isolating
 
-         interval's SW and NE corners, respectively. """
 
-         return ((self.ax, self.ay), (self.bx, self.by))
 
-     def __repr__(self):
 
-         return "(%s, %s) x (%s, %s)" % (self.ax, self.bx, self.ay, self.by)
 
-     def conjugate(self):
 
-         """This complex interval really is located in lower half-plane. """
 
-         return ComplexInterval(self.a, self.b, self.I, self.Q,
 
-             self.F1, self.F2, self.f1, self.f2, self.dom, conj=True)
 
-     def __contains__(self, item):
 
-         """
 
-         Say whether a complex number belongs to this complex rectangular
 
-         region.
 
-         Parameters
 
-         ==========
 
-         item : pair (re, im) or number re
 
-             Either a pair giving the real and imaginary parts of the number,
 
-             or else a real number.
 
-         """
 
-         if isinstance(item, tuple):
 
-             re, im = item
 
-         else:
 
-             re, im = item, 0
 
-         return self.ax <= re <= self.bx and self.ay <= im <= self.by
 
-     def is_disjoint(self, other):
 
-         """Return ``True`` if two isolation intervals are disjoint. """
 
-         if isinstance(other, RealInterval):
 
-             return other.is_disjoint(self)
 
-         if self.conj != other.conj:  # above and below real axis
 
-             return True
 
-         re_distinct = (self.bx < other.ax or other.bx < self.ax)
 
-         if re_distinct:
 
-             return True
 
-         im_distinct = (self.by < other.ay or other.by < self.ay)
 
-         return im_distinct
 
-     def _inner_refine(self):
 
-         """Internal one step complex root refinement procedure. """
 
-         (u, v), (s, t) = self.a, self.b
 
-         I, Q = self.I, self.Q
 
-         f1, F1 = self.f1, self.F1
 
-         f2, F2 = self.f2, self.F2
 
-         dom = self.dom
 
-         if s - u > t - v:
 
-             D_L, D_R = _vertical_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom)
 
-             if D_L[0] == 1:
 
-                 _, a, b, I, Q, F1, F2 = D_L
 
-             else:
 
-                 _, a, b, I, Q, F1, F2 = D_R
 
-         else:
 
-             D_B, D_U = _horizontal_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom)
 
-             if D_B[0] == 1:
 
-                 _, a, b, I, Q, F1, F2 = D_B
 
-             else:
 
-                 _, a, b, I, Q, F1, F2 = D_U
 
-         return ComplexInterval(a, b, I, Q, F1, F2, f1, f2, dom, self.conj)
 
-     def refine_disjoint(self, other):
 
-         """Refine an isolating interval until it is disjoint with another one. """
 
-         expr = self
 
-         while not expr.is_disjoint(other):
 
-             expr, other = expr._inner_refine(), other._inner_refine()
 
-         return expr, other
 
-     def refine_size(self, dx, dy=None):
 
-         """Refine an isolating interval until it is of sufficiently small size. """
 
-         if dy is None:
 
-             dy = dx
 
-         expr = self
 
-         while not (expr.dx < dx and expr.dy < dy):
 
-             expr = expr._inner_refine()
 
-         return expr
 
-     def refine_step(self, steps=1):
 
-         """Perform several steps of complex root refinement algorithm. """
 
-         expr = self
 
-         for _ in range(steps):
 
-             expr = expr._inner_refine()
 
-         return expr
 
-     def refine(self):
 
-         """Perform one step of complex root refinement algorithm. """
 
-         return self._inner_refine()
 
 
  |