コード例 #1
0
    def _coerce_map_from_(self, P):
        r"""
        Return whether ``P`` coerces into this symbolic subring.

        INPUT:

        - ``P`` -- a parent.

        OUTPUT:

        A boolean or ``None``.

        TESTS::

            sage: from sage.symbolic.subring import GenericSymbolicSubring
            sage: GenericSymbolicSubring(vars=tuple()).has_coerce_map_from(SR)  # indirect doctest  # not tested see #19231
            False

        ::
            sage: from sage.symbolic.subring import SymbolicSubring
            sage: C = SymbolicSubring(no_variables=True)
            sage: C.has_coerce_map_from(ZZ)  # indirect doctest
            True
            sage: C.has_coerce_map_from(QQ)  # indirect doctest
            True
            sage: C.has_coerce_map_from(RR)  # indirect doctest
            True
            sage: C.has_coerce_map_from(RIF)  # indirect doctest
            True
            sage: C.has_coerce_map_from(CC)  # indirect doctest
            True
            sage: C.has_coerce_map_from(CIF)  # indirect doctest
            True
            sage: C.has_coerce_map_from(AA)  # indirect doctest
            True
            sage: C.has_coerce_map_from(QQbar)  # indirect doctest
            True
            sage: C.has_coerce_map_from(SR)  # indirect doctest
            False
        """
        if P == SR:
            # Workaround; can be deleted once #19231 is fixed
            return False

        from sage.rings.real_mpfr import mpfr_prec_min
        from sage.rings.all import (ComplexField, RLF, CLF, AA, QQbar,
                                    InfinityRing)
        from sage.rings.real_mpfi import is_RealIntervalField
        from sage.rings.complex_interval_field import is_ComplexIntervalField

        if isinstance(P, type):
            return SR._coerce_map_from_(P)

        elif RLF.has_coerce_map_from(P) or \
             CLF.has_coerce_map_from(P) or \
             AA.has_coerce_map_from(P) or \
             QQbar.has_coerce_map_from(P):
            return True

        elif (P is InfinityRing or is_RealIntervalField(P)
              or is_ComplexIntervalField(P)):
            return True

        elif ComplexField(mpfr_prec_min()).has_coerce_map_from(P):
            return P not in (RLF, CLF, AA, QQbar)
コード例 #2
0
ファイル: rings.py プロジェクト: BrentBaccala/sage
        def __getitem__(self, arg):
            """
            Extend this ring by one or several elements to create a polynomial
            ring, a power series ring, or an algebraic extension.

            This is a convenience method intended primarily for interactive
            use.

            .. SEEALSO::

                :func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`,
                :func:`~sage.rings.power_series_ring.PowerSeriesRing`,
                :meth:`~sage.rings.ring.Ring.extension`,
                :meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`,
                :meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`,
                :meth:`sage.structure.parent.Parent.__getitem__`

            EXAMPLES:

            We create several polynomial rings::

                sage: ZZ['x']
                Univariate Polynomial Ring in x over Integer Ring
                sage: QQ['x']
                Univariate Polynomial Ring in x over Rational Field
                sage: GF(17)['abc']
                Univariate Polynomial Ring in abc over Finite Field of size 17
                sage: GF(17)['a,b,c']
                Multivariate Polynomial Ring in a, b, c over Finite Field of size 17
                sage: GF(17)['a']['b']
                Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17

            We can create skew polynomial rings::

                sage: k.<t> = GF(5^3)
                sage: Frob = k.frobenius_endomorphism()
                sage: k['x',Frob]
                Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5

            We can also create power series rings by using double brackets::

                sage: QQ[['t']]
                Power Series Ring in t over Rational Field
                sage: ZZ[['W']]
                Power Series Ring in W over Integer Ring

                sage: ZZ[['x,y,z']]
                Multivariate Power Series Ring in x, y, z over Integer Ring
                sage: ZZ[['x','T']]
                Multivariate Power Series Ring in x, T over Integer Ring

            Use :func:`~sage.rings.fraction_field.Frac` or
            :meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain
            the fields of rational functions and Laurent series::

                sage: Frac(QQ['t'])
                Fraction Field of Univariate Polynomial Ring in t over Rational Field
                sage: Frac(QQ[['t']])
                Laurent Series Ring in t over Rational Field
                sage: QQ[['t']].fraction_field()
                Laurent Series Ring in t over Rational Field

            Note that the same syntax can be used to create number fields::

                sage: QQ[I]
                Number Field in I with defining polynomial x^2 + 1
                sage: QQ[I].coerce_embedding()
                Generic morphism:
                 From: Number Field in I with defining polynomial x^2 + 1
                 To:   Complex Lazy Field
                 Defn: I -> 1*I

            ::

                sage: QQ[sqrt(2)]
                Number Field in sqrt2 with defining polynomial x^2 - 2
                sage: QQ[sqrt(2)].coerce_embedding()
                Generic morphism:
                 From: Number Field in sqrt2 with defining polynomial x^2 - 2
                 To:   Real Lazy Field
                 Defn: sqrt2 -> 1.414213562373095?

            ::

                sage: QQ[sqrt(2),sqrt(3)]
                Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field

            and orders in number fields::

                sage: ZZ[I]
                Order in Number Field in I with defining polynomial x^2 + 1
                sage: ZZ[sqrt(5)]
                Order in Number Field in sqrt5 with defining polynomial x^2 - 5
                sage: ZZ[sqrt(2)+sqrt(3)]
                Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1

            Embeddings are found for simple extensions (when that makes sense)::

                sage: QQi.<i> = QuadraticField(-1, 'i')
                sage: QQ[i].coerce_embedding()
                Generic morphism:
                  From: Number Field in i with defining polynomial x^2 + 1
                  To:   Complex Lazy Field
                  Defn: i -> 1*I

            TESTS:

            A few corner cases::

                sage: QQ[()]
                Multivariate Polynomial Ring in no variables over Rational Field

                sage: QQ[[]]
                Traceback (most recent call last):
                ...
                TypeError: power series rings must have at least one variable

            These kind of expressions do not work::

                sage: QQ['a,b','c']
                Traceback (most recent call last):
                ...
                ValueError: variable name 'a,b' is not alphanumeric
                sage: QQ[['a,b','c']]
                Traceback (most recent call last):
                ...
                ValueError: variable name 'a,b' is not alphanumeric

                sage: QQ[[['x']]]
                Traceback (most recent call last):
                ...
                TypeError: expected R[...] or R[[...]], not R[[[...]]]

            Extension towers are built as follows and use distinct generator names::

                sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)]
                sage: K
                Number Field in a with defining polynomial x^3 - 2 over its base field
                sage: K.base_field()
                Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field
                sage: K.base_field().base_field()
                Number Field in b with defining polynomial x^3 - 3

            Embeddings::

                sage: QQ[I](I.pyobject())
                I
                sage: a = 10^100; expr = (2*a + sqrt(2))/(2*a^2-1)
                sage: QQ[expr].coerce_embedding() is None
                False
                sage: QQ[sqrt(5)].gen() > 0
                True
                sage: expr = sqrt(2) + I*(cos(pi/4, hold=True) - sqrt(2)/2)
                sage: QQ[expr].coerce_embedding()
                Generic morphism:
                  From: Number Field in a with defining polynomial x^2 - 2
                  To:   Real Lazy Field
                  Defn: a -> 1.414213562373095?
            """
            def normalize_arg(arg):
                if isinstance(arg, (tuple, list)):
                    # Allowing arbitrary iterables would create confusion, but we
                    # may want to support a few more.
                    return tuple(arg)
                elif isinstance(arg, str):
                    return tuple(arg.split(','))
                else:
                    return (arg,)

            # 1. If arg is a list, try to return a power series ring.

            if isinstance(arg, list):
                if arg == []:
                    raise TypeError("power series rings must have at least one variable")
                elif len(arg) == 1:
                    # R[["a,b"]], R[[(a,b)]]...
                    if isinstance(arg[0], list):
                        raise TypeError("expected R[...] or R[[...]], not R[[[...]]]")
                    elts = normalize_arg(arg[0])
                else:
                    elts = normalize_arg(arg)
                from sage.rings.power_series_ring import PowerSeriesRing
                return PowerSeriesRing(self, elts)

            if isinstance(arg, tuple):
                from sage.categories.morphism import Morphism
                if len(arg) == 2 and isinstance(arg[1], Morphism):
                   from sage.rings.polynomial.skew_polynomial_ring_constructor import SkewPolynomialRing
                   return SkewPolynomialRing(self, arg[1], names=arg[0])

            # 2. Otherwise, if all specified elements are algebraic, try to
            #    return an algebraic extension

            elts = normalize_arg(arg)

            try:
                minpolys = [a.minpoly() for a in elts]
            except (AttributeError, NotImplementedError, ValueError, TypeError):
                minpolys = None

            if minpolys:
                # how to pass in names?
                names = tuple(_gen_names(elts))
                if len(elts) == 1:
                    from sage.rings.all import CIF, CLF, RIF, RLF
                    elt = elts[0]
                    try:
                        iv = CIF(elt)
                    except (TypeError, ValueError):
                        emb = None
                    else:
                        # First try creating an ANRoot manually, because
                        # extension(..., embedding=CLF(expr)) (or
                        # ...QQbar(expr)) would normalize the expression in
                        # QQbar, which currently is VERY slow in many cases.
                        # This may fail when minpoly has close roots or elt is
                        # a complicated symbolic expression.
                        # TODO: Rewrite using #19362 and/or #17886 and/or
                        # #15600 once those issues are solved.
                        from sage.rings.qqbar import AlgebraicNumber, ANRoot
                        try:
                            elt = AlgebraicNumber(ANRoot(minpolys[0], iv))
                        except ValueError:
                            pass
                        # Force a real embedding when possible, to get the
                        # right ordered ring structure.
                        if (iv.imag().is_zero() or iv.imag().contains_zero()
                                                   and elt.imag().is_zero()):
                            emb = RLF(elt)
                        else:
                            emb = CLF(elt)
                        return self.extension(minpolys[0], names[0], embedding=emb)
                try:
                    # Doing the extension all at once is best, if possible...
                    return self.extension(minpolys, names)
                except (TypeError, ValueError):
                    # ...but we can also construct it iteratively
                    return reduce(lambda R, ext: R.extension(*ext), zip(minpolys, names), self)

            # 2. Otherwise, try to return a polynomial ring

            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            return PolynomialRing(self, elts)
コード例 #3
0
ファイル: path.py プロジェクト: videlec/ore_algebra
    def __init__(self, point, dop=None):
        """
        TESTS::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: [Point(z, Dx)
            ....:  for z in [1, 1/2, 1+I, QQbar(I), RIF(1/3), CIF(1/3), pi,
            ....:  RDF(1), CDF(I), 0.5r, 0.5jr, 10r, QQbar(1), AA(1/3)]]
            [1, 1/2, I + 1, I, [0.333333333333333...], [0.333333333333333...],
            3.141592653589794?, 1.000000000000000, 1.000000000000000*I,
            0.5000000000000000, 0.5000000000000000*I, 10, 1, 1/3]
            sage: Point(sqrt(2), Dx).iv()
            [1.414...]
        """
        SageObject.__init__(self)

        from sage.rings.complex_double import ComplexDoubleField_class
        from sage.rings.complex_field import ComplexField_class
        from sage.rings.complex_interval_field import ComplexIntervalField_class
        from sage.rings.real_double import RealDoubleField_class
        from sage.rings.real_mpfi import RealIntervalField_class
        from sage.rings.real_mpfr import RealField_class

        point = sage.structure.coerce.py_scalar_to_element(point)
        try:
            parent = point.parent()
        except AttributeError:
            raise TypeError("unexpected value for point: " + repr(point))
        if isinstance(point, Point):
            self.value = point.value
        elif isinstance(
                parent,
            (number_field_base.NumberField, RealBallField, ComplexBallField)):
            self.value = point
        elif QQ.has_coerce_map_from(parent):
            self.value = QQ.coerce(point)
        # must come before QQbar, due to a bogus coerce map (#14485)
        elif parent is sage.symbolic.ring.SR:
            try:
                return self.__init__(point.pyobject(), dop)
            except TypeError:
                pass
            try:
                return self.__init__(QQbar(point), dop)
            except (TypeError, ValueError, NotImplementedError):
                pass
            try:
                self.value = RLF(point)
            except (TypeError, ValueError):
                self.value = CLF(point)
        elif QQbar.has_coerce_map_from(parent):
            alg = QQbar.coerce(point)
            NF, val, hom = alg.as_number_field_element()
            if NF is QQ:
                self.value = QQ.coerce(val)  # parent may be ZZ
            else:
                embNF = number_field.NumberField(NF.polynomial(),
                                                 NF.variable_name(),
                                                 embedding=hom(NF.gen()))
                self.value = val.polynomial()(embNF.gen())
        elif isinstance(
                parent,
            (RealField_class, RealDoubleField_class, RealIntervalField_class)):
            self.value = RealBallField(point.prec())(point)
        elif isinstance(parent, (ComplexField_class, ComplexDoubleField_class,
                                 ComplexIntervalField_class)):
            self.value = ComplexBallField(point.prec())(point)
        else:
            try:
                self.value = RLF.coerce(point)
            except TypeError:
                self.value = CLF.coerce(point)
        parent = self.value.parent()
        assert (isinstance(
            parent,
            (number_field_base.NumberField, RealBallField, ComplexBallField))
                or parent is RLF or parent is CLF)

        self.dop = dop or point.dop

        self.keep_value = False
コード例 #4
0
ファイル: subring.py プロジェクト: ProgVal/sage
    def _coerce_map_from_(self, P):
        r"""
        Return whether ``P`` coerces into this symbolic subring.

        INPUT:

        - ``P`` -- a parent.

        OUTPUT:

        A boolean or ``None``.

        TESTS::

            sage: from sage.symbolic.subring import GenericSymbolicSubring
            sage: GenericSymbolicSubring(vars=tuple()).has_coerce_map_from(SR)  # indirect doctest  # not tested see #19231
            False

        ::
            sage: from sage.symbolic.subring import SymbolicSubring
            sage: C = SymbolicSubring(no_variables=True)
            sage: C.has_coerce_map_from(ZZ)  # indirect doctest
            True
            sage: C.has_coerce_map_from(QQ)  # indirect doctest
            True
            sage: C.has_coerce_map_from(RR)  # indirect doctest
            True
            sage: C.has_coerce_map_from(RIF)  # indirect doctest
            True
            sage: C.has_coerce_map_from(CC)  # indirect doctest
            True
            sage: C.has_coerce_map_from(CIF)  # indirect doctest
            True
            sage: C.has_coerce_map_from(AA)  # indirect doctest
            True
            sage: C.has_coerce_map_from(QQbar)  # indirect doctest
            True
            sage: C.has_coerce_map_from(SR)  # indirect doctest
            False
        """
        if P == SR:
            # Workaround; can be deleted once #19231 is fixed
            return False

        from sage.rings.real_mpfr import mpfr_prec_min
        from sage.rings.all import (ComplexField,
                                    RLF, CLF, AA, QQbar, InfinityRing)
        from sage.rings.real_mpfi import is_RealIntervalField
        from sage.rings.complex_interval_field import is_ComplexIntervalField

        if isinstance(P, type):
            return SR._coerce_map_from_(P)

        elif RLF.has_coerce_map_from(P) or \
             CLF.has_coerce_map_from(P) or \
             AA.has_coerce_map_from(P) or \
             QQbar.has_coerce_map_from(P):
            return True

        elif (P is InfinityRing or
              is_RealIntervalField(P) or is_ComplexIntervalField(P)):
            return True

        elif ComplexField(mpfr_prec_min()).has_coerce_map_from(P):
            return P not in (RLF, CLF, AA, QQbar)
コード例 #5
0
ファイル: path.py プロジェクト: lairez/ore_algebra
    def __init__(self, point, dop=None, singular=None, **kwds):
        """
        INPUT:

        - ``singular``: can be set to True to force this point to be considered
          a singular point, even if this cannot be checked (e.g. because we only
          have an enclosure)

        TESTS::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: [Point(z, Dx)
            ....:  for z in [1, 1/2, 1+I, QQbar(I), RIF(1/3), CIF(1/3), pi,
            ....:  RDF(1), CDF(I), 0.5r, 0.5jr, 10r, QQbar(1), AA(1/3)]]
            [1, 1/2, I + 1, I, [0.333333333333333...], [0.333333333333333...],
            3.141592653589794?, ~1.0000, ~1.0000*I, ~0.50000, ~0.50000*I, 10,
            1, 1/3]
            sage: Point(sqrt(2), Dx).iv()
            [1.414...]
            sage: Point(RBF(0), (x-1)*x*Dx, singular=True).dist_to_sing()
            1.000000000000000
        """
        SageObject.__init__(self)

        from sage.rings.complex_double import ComplexDoubleField_class
        from sage.rings.complex_field import ComplexField_class
        from sage.rings.complex_interval_field import ComplexIntervalField_class
        from sage.rings.real_double import RealDoubleField_class
        from sage.rings.real_mpfi import RealIntervalField_class
        from sage.rings.real_mpfr import RealField_class

        point = sage.structure.coerce.py_scalar_to_element(point)
        try:
            parent = point.parent()
        except AttributeError:
            raise TypeError("unexpected value for point: " + repr(point))
        if isinstance(point, Point):
            self.value = point.value
        elif isinstance(parent, (RealBallField, ComplexBallField)):
            self.value = point
        elif isinstance(parent, number_field_base.NumberField):
            _, hom = good_number_field(point.parent())
            self.value = hom(point)
        elif QQ.has_coerce_map_from(parent):
            self.value = QQ.coerce(point)
        elif QQbar.has_coerce_map_from(parent):
            alg = QQbar.coerce(point)
            NF, val, hom = alg.as_number_field_element()
            if NF is QQ:
                self.value = QQ.coerce(val)  # parent may be ZZ
            else:
                embNF = number_field.NumberField(NF.polynomial(),
                                                 NF.variable_name(),
                                                 embedding=hom(NF.gen()))
                self.value = val.polynomial()(embNF.gen())
        elif isinstance(
                parent,
            (RealField_class, RealDoubleField_class, RealIntervalField_class)):
            self.value = RealBallField(point.prec())(point)
        elif isinstance(parent, (ComplexField_class, ComplexDoubleField_class,
                                 ComplexIntervalField_class)):
            self.value = ComplexBallField(point.prec())(point)
        elif parent is sage.symbolic.ring.SR:
            try:
                return self.__init__(point.pyobject(), dop)
            except TypeError:
                pass
            try:
                return self.__init__(QQbar(point), dop)
            except (TypeError, ValueError, NotImplementedError):
                pass
            try:
                self.value = RLF(point)
            except (TypeError, ValueError):
                self.value = CLF(point)
        else:
            try:
                self.value = RLF.coerce(point)
            except TypeError:
                self.value = CLF.coerce(point)

        parent = self.value.parent()
        assert (isinstance(
            parent,
            (number_field_base.NumberField, RealBallField, ComplexBallField))
                or parent is RLF or parent is CLF)

        if dop is None:  # TBI
            if isinstance(point, Point):
                self.dop = point.dop
        else:
            self.dop = DifferentialOperator(dop.numerator())
        self._force_singular = bool(singular)
        self.options = kwds
コード例 #6
0
ファイル: path.py プロジェクト: lairez/ore_algebra
class Point(SageObject):
    r"""
    A point on the complex plane with an associated differential operator.

    A point can be exact (a number field element) or inexact (a real or complex
    interval or ball). It can be classified as ordinary, regular singular, etc.
    The main reason for making the operator part of the definition of Points is
    that this gives a convenient place to cache information that depend on both,
    with an appropriate lifetime. Note however that the point is considered to
    lie on the complex plane, not on the Riemann surface of the operator.
    """
    def __init__(self, point, dop=None, singular=None, **kwds):
        """
        INPUT:

        - ``singular``: can be set to True to force this point to be considered
          a singular point, even if this cannot be checked (e.g. because we only
          have an enclosure)

        TESTS::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: [Point(z, Dx)
            ....:  for z in [1, 1/2, 1+I, QQbar(I), RIF(1/3), CIF(1/3), pi,
            ....:  RDF(1), CDF(I), 0.5r, 0.5jr, 10r, QQbar(1), AA(1/3)]]
            [1, 1/2, I + 1, I, [0.333333333333333...], [0.333333333333333...],
            3.141592653589794?, ~1.0000, ~1.0000*I, ~0.50000, ~0.50000*I, 10,
            1, 1/3]
            sage: Point(sqrt(2), Dx).iv()
            [1.414...]
            sage: Point(RBF(0), (x-1)*x*Dx, singular=True).dist_to_sing()
            1.000000000000000
        """
        SageObject.__init__(self)

        from sage.rings.complex_double import ComplexDoubleField_class
        from sage.rings.complex_field import ComplexField_class
        from sage.rings.complex_interval_field import ComplexIntervalField_class
        from sage.rings.real_double import RealDoubleField_class
        from sage.rings.real_mpfi import RealIntervalField_class
        from sage.rings.real_mpfr import RealField_class

        point = sage.structure.coerce.py_scalar_to_element(point)
        try:
            parent = point.parent()
        except AttributeError:
            raise TypeError("unexpected value for point: " + repr(point))
        if isinstance(point, Point):
            self.value = point.value
        elif isinstance(parent, (RealBallField, ComplexBallField)):
            self.value = point
        elif isinstance(parent, number_field_base.NumberField):
            _, hom = good_number_field(point.parent())
            self.value = hom(point)
        elif QQ.has_coerce_map_from(parent):
            self.value = QQ.coerce(point)
        elif QQbar.has_coerce_map_from(parent):
            alg = QQbar.coerce(point)
            NF, val, hom = alg.as_number_field_element()
            if NF is QQ:
                self.value = QQ.coerce(val)  # parent may be ZZ
            else:
                embNF = number_field.NumberField(NF.polynomial(),
                                                 NF.variable_name(),
                                                 embedding=hom(NF.gen()))
                self.value = val.polynomial()(embNF.gen())
        elif isinstance(
                parent,
            (RealField_class, RealDoubleField_class, RealIntervalField_class)):
            self.value = RealBallField(point.prec())(point)
        elif isinstance(parent, (ComplexField_class, ComplexDoubleField_class,
                                 ComplexIntervalField_class)):
            self.value = ComplexBallField(point.prec())(point)
        elif parent is sage.symbolic.ring.SR:
            try:
                return self.__init__(point.pyobject(), dop)
            except TypeError:
                pass
            try:
                return self.__init__(QQbar(point), dop)
            except (TypeError, ValueError, NotImplementedError):
                pass
            try:
                self.value = RLF(point)
            except (TypeError, ValueError):
                self.value = CLF(point)
        else:
            try:
                self.value = RLF.coerce(point)
            except TypeError:
                self.value = CLF.coerce(point)

        parent = self.value.parent()
        assert (isinstance(
            parent,
            (number_field_base.NumberField, RealBallField, ComplexBallField))
                or parent is RLF or parent is CLF)

        if dop is None:  # TBI
            if isinstance(point, Point):
                self.dop = point.dop
        else:
            self.dop = DifferentialOperator(dop.numerator())
        self._force_singular = bool(singular)
        self.options = kwds

    def _repr_(self, size=False):
        """
        TESTS::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: Point(10**20, Dx)
            ~1.0000e20
        """
        if self.is_exact():
            try:
                len = (self.value.parent().precision() if isinstance(
                    self.value, (RealBall, ComplexBall)) else self.nbits())
                if len > 50:
                    res = repr(self.value.n(digits=5))
                    if size:
                        return "~[{}b]{}".format(self.nbits(), res)
                    else:
                        return "~" + res
            except AttributeError:
                pass
        return repr(self.value)

    def keep_value(self):
        return bool(self.options.get("keep_value"))

    def nbits(self):
        if isinstance(self.value, (RealBall, ComplexBall)):
            return self.value.nbits()
        else:
            res = self.value.denominator().nbits()
            res += max(self.value.numerator().real().numerator().nbits(),
                       self.value.numerator().imag().numerator().nbits())
            return res

    @cached_method
    def is_fast(self):
        return isinstance(self.value,
                          (RealBall, ComplexBall, rings.Integer,
                           rings.Rational)) or is_QQi(self.value.parent())

    def bit_burst_bits(self, tgt_prec):
        if self.is_fast():
            return self.nbits()
        else:
            # RLF, CLF, other number fields (debatable!)
            return tgt_prec

    # Numeric representations

    @cached_method
    def iv(self):
        """
        EXAMPLES::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: [Point(z, Dx).iv()
            ....: for z in [1, 1/2, 1+I, QQbar(I), RIF(1/3), CIF(1/3), pi]]
            [1.000000000000000,
            0.5000000000000000,
            1.000000000000000 + 1.000000000000000*I,
            1.000000000000000*I,
            [0.333333333333333 +/- 3.99e-16],
            [0.333333333333333 +/- 3.99e-16],
            [3.141592653589793 +/- 7.83e-16]]
        """
        return IC(self.value)

    def exact(self):
        r"""
        EXAMPLES::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: QQi.<i> = QuadraticField(-1)
            sage: [Point(z, Dx).exact() for z in [1, 1/2, 1+i, QQbar(I)]]
            [1, 1/2, i + 1, I]
            sage: [Point(z, Dx).exact() for z in [RBF(3/4), RBF(1) + I]]
            [3/4, i + 1]
            sage: Point(RIF(1/3), Dx).exact()
            Traceback (most recent call last):
            ...
            ValueError
        """
        if self.value.parent().is_exact():
            return self
        elif isinstance(self.value, RealBall) and self.value.is_exact():
            return Point(QQ(self.value), self.dop, **self.options)
        elif isinstance(self.value, ComplexBall) and self.value.is_exact():
            value = QQi((QQ(self.value.real()), QQ(self.value.imag())))
            return Point(value, self.dop, **self.options)
        raise ValueError

    def approx_abs_real(self, prec):
        r"""
        Compute an approximation with absolute error about 2^(-prec).
        """
        if isinstance(self.value.parent(), RealBallField):
            return self.value
        elif self.value.is_zero():
            return RealBallField(max(2, prec)).zero()
        elif self.is_real():
            expo = ZZ(IR(self.value).abs().log(2).upper().ceil())
            rel_prec = max(2, prec + expo + 10)
            val = RealBallField(rel_prec)(self.value)
            return val
        else:
            raise ValueError("point may not be real")

    def is_real(self):
        return is_real_parent(self.value.parent())

    def is_exact(self):
        r"""
        Is this point exact in the sense that we can use it in the coefficients
        of an operator?
        """
        return (isinstance(
            self.value,
            (rings.Integer, rings.Rational, rings.NumberFieldElement)) or
                isinstance(self.value,
                           (RealBall, ComplexBall)) and self.value.is_exact())

    def rationalize(self):
        a = self.iv()
        if any(a.overlaps(s) for s in self.dop._singularities(IC)):
            raise PathPrecisionError
        else:
            return Point(_rationalize(a), self.dop)

    def truncate(self, prec, tgt_prec):
        Ivs = RealBallField if self.is_real() else ComplexBallField
        approx = Ivs(prec)(self.value).round()
        lc = self.dop.leading_coefficient()
        if lc(approx).contains_zero():
            raise PathPrecisionError  # appropriate?
        approx = approx.squash()
        return Point(Ivs(tgt_prec)(approx), self.dop)

    def __complex__(self):
        return complex(self.value)

    # Point equality is identity

    def __eq__(self, other):
        return self is other

    def __hash__(self):
        return id(self)

    def __lt__(self, other):
        r"""
        Temporary kludge (Sage graphs require vertices to be comparable).
        """
        return id(self) < id(other)

    ### Methods that depend on dop

    @cached_method
    def is_ordinary(self):
        if self._force_singular:
            return False
        lc = self.dop.leading_coefficient()
        if not lc(self.iv()).contains_zero():
            return True
        if self.is_exact():
            try:
                val = lc(self.value)
            except TypeError:  # work around coercion weaknesses
                val = lc.change_ring(QQbar)(QQbar.coerce(self.value))
            return not val.is_zero()
        else:
            raise ValueError("can't tell if inexact point is singular")

    def is_singular(self):
        return not self.is_ordinary()

    @cached_method
    def is_regular(self):
        try:
            if self.is_ordinary():
                return True
        except ValueError:
            # we could handle balls containing no irregular singular point...
            raise NotImplementedError("can't tell if inexact point is regular")
        assert self.is_exact()
        # Fuchs criterion
        dop, pt = self.dop.extend_scalars(self.value)
        Pols = dop.base_ring()
        lin = Pols([pt, -1])
        ref = dop.leading_coefficient().valuation(lin) - dop.order()
        return all(
            coef.valuation(lin) - k >= ref for k, coef in enumerate(dop))

    def is_regular_singular(self):
        return not self.is_ordinary() and self.is_regular()

    def is_irregular(self):
        return not self.is_regular()

    def singularity_type(self, short=False):
        r"""
        EXAMPLES::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()

            sage: dop = (x^2 + 1)*Dx^2 + 2*x*Dx
            sage: Point(1, dop).singularity_type()
            'ordinary point'
            sage: Point(i, dop).singularity_type()
            'regular singular point'
            sage: Point(0, x^2*Dx + 1).singularity_type()
            'irregular singular point'
            sage: Point(CIF(1/3), x^2*Dx + 1).singularity_type()
            'ordinary point'
            sage: Point(CIF(1/3)-1/3, x^2*Dx + 1).singularity_type()
            'point of unknown singularity type'
        """
        try:
            if self.is_ordinary():
                return "" if short else "ordinary point"
            elif self.is_regular():
                return "regular singular point"
            else:
                return "irregular singular point"
        except (ValueError, NotImplementedError):
            return "point of unknown singularity type"

    def descr(self):
        t = self.singularity_type(short=True)
        if t == "":
            return repr(self)
        else:
            return t + " " + repr(self)

    def dist_to_sing(self):
        """
        Distance of self to the singularities of self.dop *other than self*.

        TESTS::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: dop = (x^2 + 1)*Dx^2 + 2*x*Dx
            sage: Point(1, dop).dist_to_sing()
            [1.41421356237309...]
            sage: Point(i, dop).dist_to_sing()
            2.00...
            sage: Point(1+i, dop).dist_to_sing()
            1.00...

        """
        sing = self.dop._singularities(IC)
        close, distant = split(lambda s: s.overlaps(self.iv()), sing)
        if (len(close) >= 2 or len(close) == 1 and not self.is_singular()):
            raise NotImplementedError  # refine?
        dist = [(self.iv() - s).abs() for s in distant]
        min_dist = IR(rings.infinity).min(*dist)
        if min_dist.contains_zero():
            raise NotImplementedError  # refine???
        return IR(min_dist.lower())

    def local_basis_structure(self):
        r"""
        EXAMPLES::

            sage: from ore_algebra import *
            sage: from ore_algebra.analytic.path import Point
            sage: Dops, x, Dx = DifferentialOperators()
            sage: Point(0, x*Dx^2 + Dx + x).local_basis_structure()
            [FundamentalSolution(leftmost=0, shift=0, log_power=1, value=None),
             FundamentalSolution(leftmost=0, shift=0, log_power=0, value=None)]
            sage: Point(0, Dx^3 + x*Dx + x).local_basis_structure()
            [FundamentalSolution(leftmost=0, shift=0, log_power=0, value=None),
             FundamentalSolution(leftmost=0, shift=1, log_power=0, value=None),
             FundamentalSolution(leftmost=0, shift=2, log_power=0, value=None)]
        """
        # TODO: provide a way to compute the first terms of the series. First
        # need a good way to share code with fundamental_matrix_regular. Or
        # perhaps modify generalized_series_solutions() to agree with our
        # definition of the basis?
        if self.is_ordinary():  # support inexact points in this case
            return [
                FundamentalSolution(QQbar.zero(), ZZ(expo), ZZ.zero(), None)
                for expo in range(self.dop.order())
            ]
        elif not self.is_regular():
            raise NotImplementedError("irregular singular point")
        return LocalBasisMapper(self.dop.shift(self)).run()

    @cached_method
    def simple_approx(self, ctx=dctx):
        r"""
        Return an approximation of this point suitable as a starting point for
        the next analytic continuation step.

        For intermediate steps via simple points to reach points of large bit
        size or with irrational coordinates in the context of binary splitting,
        see bit_burst_split().

        For intermediate steps where thick balls are shrinked to their center,
        see exact_approx().
        """
        # Point options become meaningless (and are lost) when not returning
        # self.
        thr = ctx.simple_approx_thr
        if (self.is_singular()
                or self.is_fast() and self.is_exact() and self.nbits() <= thr):
            return self
        else:
            rad = RBF.one().min(self.dist_to_sing() / 16)
            ball = self.iv().add_error(rad)
            if any(s.overlaps(ball) for s in self.dop._singularities(IC)):
                return self
            rat = _rationalize(ball, real=self.is_real())
            return Point(rat, self.dop)

    @cached_method
    def exact_approx(self):
        if isinstance(self.value, (RealBall, ComplexBall)):
            if not self.value.is_exact():
                return Point(self.value.trim().squash(), self.dop)
        return self