Ticket #10194: trac_10194-factories_policy-fh-v2.patch

File trac_10194-factories_policy-fh-v2.patch, 57.2 KB (added by chapoton, 8 years ago)
  • doc/en/reference/structure/index.rst

    # HG changeset patch
    # User Florent Hivert <Florent.Hivert@univ-rouen.fr>
    # Date 1333053022 -7200
    # Node ID bb5c937dc5c28adf6fcc40f44d795d1a0fe8a367
    # Parent  344dcbdc3585c286c457a0b54989193467026c58
    [mq]: trac_10194-factories_policy-fh.patch
    
    diff --git a/doc/en/reference/structure/index.rst b/doc/en/reference/structure/index.rst
    a b Basic Structures 
    2222   sage/structure/element_wrapper
    2323   sage/structure/global_options
    2424
     25   sage/structure/set_factories
     26   sage/structure/set_factories_example
    2527   sage/sets/cartesian_product
    2628   sage/sets/family
    2729   sage/sets/set
  • sage/misc/sagedoc.py

    diff --git a/sage/misc/sagedoc.py b/sage/misc/sagedoc.py
    a b math_substitutes = [ # don't forget lead 
    5656    ('\\cdot', ' *'),
    5757    (' \\times', ' x'),
    5858    ('\\times', ' x'),
     59    ('\\quad', '  '),
     60    ('\\qquad', '    '),
     61    ('\\mid', '|'),
    5962    ('\\backslash','\\'),
    6063    ('\\mapsto', ' |--> '),
    6164]
  • new file sage/structure/set_factories.py

    diff --git a/sage/structure/set_factories.py b/sage/structure/set_factories.py
    new file mode 100644
    - +  
     1r"""
     2Set factories
     3=============
     4
     5A *set factory* `F` is device, whose goal is to construct :class:`Parent` `P`
     6which models subsets of a big set `S`. Typically, the `P` s are constructed
     7within families obtained by putting a bunch of constraints `cons` on the
     8elements of the set `S`. In such a hierarchy of subsets, one needs to have a
     9fine and easy control on the elements construction. That is, one often needs
     10that `P` constructs elements in a subclass of its usual class for element. On
     11the contrary, one also often needs `P` to be a facade parent, meaning that `P`
     12construct element whose actual parent is not `P` itself (see
     13:func:`~sage.categories.facade_sets.Facade`).
     14
     15The role of a set factory is twofold:
     16
     17- *manage a database* of constructors for the different parents `P = F(cons)`
     18  depending on the various kinds of constraints `cons`. Note: currently there
     19  is no real support for that. We are gathering use case before fixing the
     20  interface.
     21
     22- ensure that the elements `e = P(...)` created by the different parents
     23  follows a consistent policy concerning their *class and parent*.
     24
     25.. RUBRIC:: Basic usage: constructing parents through a factory
     26
     27The file :mod:`sage.structure.set_factories_example` shows an example of a
     28:class:`SetFactory` together with typical implementation. Note that the
     29written code is intentionnally kept minimal, many thing and in particular
     30several iterator could be written in a more efficient ways.
     31
     32Consider the set `S` of couple `(x,y)` with `x` and `y` in `I:=\{0,1,2,3,4\}`.
     33We represent the element of `S` as 2-elements tuple, wrapped in a class
     34:class:`~.set_factories_example.XYPair` deriving from :class:`ElementWrapper`.
     35You can create a :class:`~.set_factories_example.XYPair` with any
     36:class:`Parent`::
     37
     38    sage: from sage.structure.set_factories import *
     39    sage: from sage.structure.set_factories_example import *
     40    sage: p = XYPair(Parent(), (0,1)); p
     41    (0, 1)
     42
     43Now, given `(a, b)\in S` we want to consider the following subsets of
     44`S`
     45
     46.. MATH::
     47
     48    S_a := \{(x,y) \in S \mid x = a\},\qquad
     49    S^b := \{(x,y) \in S \mid y = b\},\qquad
     50    S_a^b := \{(x,y) \in S \mid x = a, y = b\}.
     51
     52The constraints considered here are admittedly trivial. In a realistic
     53examples, there would be much more of them. And for some sets of constraints
     54no good enumeration algorithms would be known.
     55
     56In Sage, those sets are constructed passing the constraints to the factory. We
     57first create the set with no constraints at all::
     58
     59    sage: XYPairs
     60    Factory for XY pairs
     61    sage: S = XYPairs(); S.list()
     62    [(0, 0), (1, 0), ..., (4, 0), (0, 1), (1, 1), ..., (3, 4), (4, 4)]
     63    sage: S.cardinality()
     64    25
     65
     66Lets construct `S_2`, `S^3` and `S_2^3`::
     67
     68    sage: Sx2 = XYPairs(x=2); Sx2.list()
     69    [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4)]
     70    sage: Sy3 = XYPairs(y=3); Sy3.list()
     71    [(0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]
     72    sage: S23 = XYPairs(x=2, y=3); S23.list()
     73    [(2, 3)]
     74
     75Set factories provide an alternative way to build subsets of and already
     76constructed set: each set constructed by a factory has a method
     77:meth:`~SetFactoryParent.subset` which accept new constraints. Set
     78constructed by the factory or the :meth:`~SetFactoryParent.subset` methods are
     79identical::
     80
     81    sage: Sx2s = S.subset(x=2); Sx2 is Sx2s
     82    True
     83    sage: Sx2.subset(y=3) is S23
     84    True
     85
     86Is is not possible to change an already given constraints::
     87
     88    sage: S23.subset(y=5)
     89    Traceback (most recent call last):
     90    ...
     91    TypeError: __call__() got multiple values for keyword argument 'y'
     92
     93.. RUBRIC:: Constructing custom elements: policies
     94
     95We now come to the point of factories: constructing custom elements. By
     96default, the writer of :func:`~.set_factories_example.XYPairs` decided that
     97the parent ``Sx2``, ``Sy3`` and ``S23`` are facade for parent ``S``. This
     98means that each elements constructed by those subsets behaves as if they where
     99directly constructed by ``S`` istelf::
     100
     101    sage: Sx2.an_element().parent()
     102    AllPairs
     103    sage: el = Sx2.an_element()
     104    sage: el.parent() is S
     105    True
     106    sage: type(el) is S.element_class
     107    True
     108
     109This is not always desirable. The device which decide how to construct an
     110element is called a *policy* (see :class:`SetFactoryPolicy`). Each factory
     111should have a default policy. Here is the policy of
     112:func:`~.set_factories_example.XYPairs`::
     113
     114    sage: XYPairs._default_policy
     115    Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     116
     117This means that with the current policy, the parent builds element with class
     118``XYPair`` and parent ``AllPairs`` which is itself constructed by calling the
     119factory :func:`~.set_factories_example.XYPairs` with constraints ``()``. There
     120is a lot of flexibility to change that. We now illustrate how to make two
     121different choices.
     122
     1231 - In a first use case, we want to add some method to the constructed
     124elements. As illustration, we add here a new method ``sum`` which returns
     125`x+y`. We therefore inherit from :class:`~.set_factories_example.XYPair`::
     126
     127    sage: class NewXYPair(XYPair):
     128    ...       def sum(self):
     129    ...           return sum(self.value)
     130    sage: el = NewXYPair(Parent(), (2,3))
     131    sage: el.sum()
     132    5
     133
     134We now want to have subsets generating those new elements while still having a
     135single real parent (the one with no constraints) for each element. The
     136corresponding policy is called :class:`TopMostParentPolicy`. It takes tree
     137parameters:
     138
     139- the factory;
     140- the parameters for void constraints;
     141- the class used for elements.
     142
     143Calling the factory with this policy returns a new sets which builds his
     144elements with the new policy::
     145
     146    sage: newpolicy = TopMostParentPolicy(XYPairs, (), NewXYPair)
     147    sage: newS = XYPairs(policy = newpolicy)
     148    sage: el = newS.an_element(); el
     149    (0, 0)
     150    sage: el.sum()
     151    0
     152    sage: el.parent() is newS
     153    True
     154
     155Subsets now inherits the policy::
     156
     157    sage: newS2 = newS.subset(x=2)
     158    sage: el2 = newS2.an_element(); el2
     159    (2, 0)
     160    sage: el2.sum()
     161    2
     162    sage: el2.parent() is newS
     163    True
     164
     1652 - In a second use case, we want the elements to remember which parent
     166created them. The corresponding policy is called
     167:class:`SelfParentPolicy`. It takes only two parameters:
     168
     169- the factory;
     170- the class used for elements.
     171
     172Here is an examples::
     173
     174    sage: selfpolicy = SelfParentPolicy(XYPairs, NewXYPair)
     175    sage: selfS = XYPairs(policy = selfpolicy)
     176    sage: el = selfS.an_element();
     177    sage: el.parent() is selfS
     178    True
     179
     180Now subsets are the parent of the element they created::
     181
     182    sage: selfS2 = selfS.subset(x=2)
     183    sage: el2 = selfS2.an_element()
     184    sage: el2.parent() is newS
     185    False
     186    sage: el2.parent() is selfS2
     187    True
     188
     189Here are the currently implemented policies:
     190
     191- :class:`FacadeParentPolicy`: reuse an existing parent together with
     192  its element_class
     193
     194- :class:`TopMostParentPolicy`: use a parent created by the factory itself and
     195  provide a class ``Element`` for it. In this case, we need to specify for
     196  which sets of constraints the constructed parent needs to be provided with
     197  a class ``Element``.
     198
     199- :class:`SelfParentPolicy`: provide systematically Element and
     200  element_class and ensure that the parent is ``self``.
     201
     202.. TODO:: Generalize :class:`TopMostParentPolicy` to be able to have several
     203    topmost parent.
     204
     205.. RUBRIC:: Technicalities: how policies inform parents
     206
     207Parent built from factories should inherits from
     208:class:`SetFactoryParent`. This class provide a few methods related to
     209factories and policies. The ``__init__`` method of :class:`SetFactoryParent`
     210must be provided with the set of constraints and the policy. A parent built
     211from a factory must creates element trough a call to the method
     212``_element_constructor_``. The current way policies inform parents how to
     213builds their elements is through policy category (see
     214:class:`AbstractPolicyCategory`). We (ab)use the category system to provide the parent with a category whose role is to set ``_element_constructor_`` and the related methods and attributes appropriately::
     215
     216    sage: S = XYPairs()
     217    sage: S.categories()
     218    [..., Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>, ...]
     219
     220    sage: S2 = S.subset(x=2)
     221    sage: S2.categories()
     222    [..., Policy category with facade parent AllPairs, ...]
     223
     224This category is computed from the constraints by the method
     225:meth:`SetFactoryPolicy.category` of the policy::
     226
     227    sage: S.constraints()
     228    ()
     229    sage: XYPairs._default_policy.category(())
     230    Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     231
     232    sage: S2.constraints()
     233    (2,)
     234    sage: XYPairs._default_policy.category((2,))
     235    Policy category with facade parent AllPairs
     236
     237Two concrete policy category deriving from :class:`AbstractPolicyCategory` are
     238available:
     239
     240- :class:`SelfParentPolicyCategory` for usual parent;
     241- :class:`FacadeParentPolicyCategory` for facade parent (see
     242  :func:`~sage.categories.facade_sets.Facade`);
     243
     244
     245.. RUBRIC:: How to write a set factory
     246
     247.. SEEALSO:: :mod:`.set_factories_example` for an example of a factory.
     248
     249Here are the specifications:
     250
     251A parent built from a factory should:
     252
     253- *inherit* from :class:`SetFactoryParent`. It should accept a ``policy``
     254  argument and pass it verbatim to the ``__init__`` method of
     255  :class:`SetFactoryParent` together with the set of constraints;
     256
     257- *create its elements* trough calls to the method ``_element_constructor_``;
     258
     259- *define a method* :class:`SetFactoryParent.check_element` which checks if a
     260  built element indeed belongs to it. The method should accept an extra
     261  keyword parameter called ``check`` specifying which level of check should be
     262  performed. It will only be called when ``bool(check)`` evaluates to
     263  ``True``.
     264
     265The constructor of the elements of a parent from a factory should:
     266
     267- receive the parent as first mandatory argument;
     268
     269- accept an extra optional keyword parameter called ``check`` which is meant
     270  to tell if the input must be checked or not. The precise meaning of
     271  ``check`` is intensionally left vague. The only intend is that if
     272  ``bool(check)`` evaluates to ``False`` no check is performed at all.
     273
     274A factory should
     275
     276- *define a method* ``__call__`` which is responsible for calling the
     277  appropriate parent constructor given the constraints;
     278
     279- *define a method* overloading :meth:`SetFactory.add_constraints` which is
     280  responsible of computing the union of two sets of constraints;
     281
     282- *optionally* define an attribute ``_default_policy`` used by call to pass to
     283  the parent.
     284
     285.. TODO:: There is currently no support for dealing with sets of
     286    constraints. The set factory and the parents must cooperate to
     287    consistently handle them. More support, together with a generic mechanism
     288    to select the appropriate parent class from the constraints, will be added
     289    shortly to sage, as soon as we have gathered sufficiently enough
     290    use-cases.
     291
     292AUTHORS:
     293
     294- Florent Hivert (2011-2012): initial revision
     295"""
     296#*****************************************************************************
     297#  Copyright (C) 2012 Florent Hivert <florent.hivert at lri.fr>
     298#
     299#  Distributed under the terms of the GNU General Public License (GPL)
     300#                  http://www.gnu.org/licenses/
     301#*****************************************************************************
     302
     303
     304from sage.structure.sage_object import SageObject
     305from sage.structure.parent import Parent
     306from sage.structure.unique_representation import UniqueRepresentation
     307from sage.categories.category import Category
     308from sage.categories.sets_cat import Sets
     309from sage.misc.abstract_method import abstract_method
     310from sage.misc.lazy_attribute import lazy_attribute
     311
     312####################################################
     313#                   Factories                      #
     314####################################################
     315
     316
     317class SetFactory(UniqueRepresentation, SageObject):
     318    r"""
     319    This class is currently just a stub we will be using to add more
     320    structures on factories.
     321    """
     322    @abstract_method
     323    def __call__(self, *constraints, **consdict):
     324        r"""
     325        Construct the parent associated with the constraints
     326
     327        Should return a :class:`Parent`.
     328
     329        Currently there is no specification on how constraints are passed as
     330        arguments.
     331
     332        EXAMPLES::
     333
     334            sage: from sage.structure.set_factories_example import XYPairs
     335            sage: XYPairs()
     336            AllPairs
     337            sage: XYPairs(3)
     338            <class 'sage.structure.set_factories_example.PairsX__with_category'>
     339            sage: XYPairs(x=3)
     340            <class 'sage.structure.set_factories_example.PairsX__with_category'>
     341        """
     342
     343    @abstract_method
     344    def add_constraints(self, cons, *args, **opts):
     345        r"""
     346        Add constraints to the set cons
     347
     348        Should return a set of constraints. Currently there is no
     349        specification on how constraints are passed as arguments.
     350
     351        TESTS::
     352
     353            sage: from sage.structure.set_factories_example import XYPairs
     354            sage: XYPairs.add_constraints((3,),((2,), {}))
     355            (3, 2)
     356        """
     357
     358    # TODO ? default policy
     359
     360####################################################
     361#                    Policies                      #
     362####################################################
     363
     364
     365class SetFactoryPolicy(UniqueRepresentation, SageObject):
     366    r"""
     367    Abstract base class for policies.
     368
     369    A policy is a device which is passed to a parent inheriting from
     370    :class:`SetFactoryParent` in order to set-up the element construction
     371    framework.
     372
     373    INPUT:
     374
     375    - ``factory`` -- a :class:`SetFactory`
     376
     377    .. WARNING:: This class is a base class for policies, one should not try
     378       to create instances.
     379    """
     380    def __init__(self, factory):
     381        r"""
     382        TEST::
     383
     384            sage: from sage.structure.set_factories_example import *
     385            sage: from sage.structure.set_factories import *
     386            sage: S = SetFactoryPolicy(XYPairs); S
     387            <class 'sage.structure.set_factories.SetFactoryPolicy'>
     388        """
     389        assert isinstance(factory, SetFactory)
     390        self._factory = factory
     391
     392    def factory(self):
     393        r"""
     394        Return the factory for ``self``
     395
     396        EXAMPLES::
     397
     398            sage: from sage.structure.set_factories_example import *
     399            sage: from sage.structure.set_factories import *
     400            sage: XYPairs._default_policy.factory()
     401            Factory for XY pairs
     402            sage: XYPairs._default_policy.factory() is XYPairs
     403            True
     404
     405        TESTS::
     406
     407            sage: policy = SetFactoryPolicy(XYPairs)
     408            sage: policy.factory()
     409            Factory for XY pairs
     410            sage: SelfParentPolicy(XYPairs, XYPair).factory()
     411            Factory for XY pairs
     412        """
     413        return self._factory
     414
     415    @abstract_method
     416    def category(self, constraints):
     417        r"""
     418        Return the policy category associated to this policy for parent
     419        constructed with the given constraints.
     420
     421        INPUT:
     422
     423        - ``constraints`` -- a set of constraints
     424
     425        OUTPUT:
     426
     427        - a instance of :class:`AbstractPolicyCategory`
     428
     429        This is an abstract method which should be implemented in concrete
     430        policies. It should return an instance of a subclass of
     431        :class:`AbstractPolicyCategory` such as
     432
     433        - :class:`SelfParentPolicyCategory` for usual parent;
     434        - :class:`FacadeParentPolicyCategory` for facade parent (see
     435          :func:`~sage.categories.facade_sets.Facade`);
     436
     437        EXAMPLE::
     438
     439            sage: from sage.structure.set_factories_example import XYPairs
     440            sage: XYPairs._default_policy.category(())
     441            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     442            sage: XYPairs._default_policy.category((3,))
     443            Policy category with facade parent AllPairs
     444        """
     445
     446
     447class SelfParentPolicy(SetFactoryPolicy):
     448    r"""
     449    Policy where each parent is a standard parent
     450
     451    INPUT:
     452
     453    - ``factory`` -- an instance of :class:`SetFactory`
     454    - ``Element`` -- a subclass of :class:`~.element.Element`
     455
     456    Given a factory ``F`` an a class ``E``, returns a policy for parent ``P``
     457    creating element in class ``E`` and parent ``P`` itself.
     458
     459    EXAMPLES::
     460
     461        sage: from sage.structure.set_factories_example import *
     462        sage: from sage.structure.set_factories import *
     463        sage: pol = SelfParentPolicy(XYPairs, XYPair)
     464        sage: S = Pairs_Y(3, pol)
     465        sage: el = S.an_element()
     466        sage: el.parent() is S
     467        True
     468
     469        sage: class Foo(XYPair): pass
     470        sage: pol = SelfParentPolicy(XYPairs, Foo)
     471        sage: S = Pairs_Y(3, pol)
     472        sage: el = S.an_element()
     473        sage: isinstance(el, Foo)
     474        True
     475    """
     476    def __init__(self, factory, Element):
     477        r"""
     478        TEST::
     479
     480            sage: from sage.structure.set_factories_example import *
     481            sage: from sage.structure.set_factories import *
     482            sage: S = SelfParentPolicy(XYPairs, XYPair); S
     483            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent ``self``
     484            sage: TestSuite(S).run(skip='_test_category')
     485        """
     486        self._Element = Element
     487        SetFactoryPolicy.__init__(self, factory)
     488
     489    def category(self, constraints):
     490        r"""
     491        Return the policy category for given constraints
     492
     493        Return the policy category associated to ``self`` for parent
     494        constructed with the given constraints as per
     495        :meth:`SetFactoryPolicy.category`
     496
     497        INPUT:
     498
     499        - ``constraints`` -- a set of constraints
     500
     501        OUTPUT:
     502
     503        - an instance of :class:`SelfParentPolicyCategory`.
     504
     505        EXAMPLE::
     506
     507            sage: from sage.structure.set_factories_example import *
     508            sage: from sage.structure.set_factories import *
     509            sage: pol = SelfParentPolicy(XYPairs, XYPair)
     510            sage: pol.category(())
     511            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     512            sage: pol.category((2,))
     513            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     514        """
     515        return SelfParentPolicyCategory(self._Element)
     516
     517    def _repr_(self):
     518        r"""
     519        TESTS::
     520
     521            sage: from sage.structure.set_factories import SelfParentPolicy
     522            sage: from sage.structure.set_factories_example import *
     523            sage: SelfParentPolicy(XYPairs, XYPair)    # indirect doctest
     524            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent ``self``
     525        """
     526        return "Set factory policy for %s with parent ``self``" % (self._Element)
     527
     528
     529class TopMostParentPolicy(SetFactoryPolicy):
     530    r"""
     531    Policy where the parent of the element is the topmost parent
     532
     533    INPUT:
     534
     535    - ``factory`` -- an instance of :class:`SetFactory`
     536    - ``top_constraints`` -- the empty set of constraints.
     537    - ``Element`` -- a subclass of :class:`~.element.Element`
     538
     539    Given a factory ``F`` an a class ``E``, returns a policy for parent ``P``
     540    creating element in class ``E`` and parent ``factory(top_constraints)``.
     541
     542    EXAMPLES::
     543
     544        sage: from sage.structure.set_factories_example import *
     545        sage: P = XYPairs(); P.policy()
     546        Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     547    """
     548    def __init__(self, factory, top_constraints, Element):
     549        """
     550        TEST::
     551
     552            sage: from sage.structure.set_factories_example import *
     553            sage: from sage.structure.set_factories import *
     554            sage: T = TopMostParentPolicy(XYPairs, (), XYPair); T
     555            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     556            sage: TestSuite(T).run(skip='_test_category')
     557        """
     558        # assert(isinstance(top_constraints, tuple))
     559        self._top_constraints = top_constraints
     560        self._Element = Element
     561        SetFactoryPolicy.__init__(self, factory)
     562
     563    def category(self, constraints):
     564        r"""
     565        Return the policy category for given constraints
     566
     567        Return the policy category associated to ``self`` for parent
     568        constructed with the given constraints as per
     569        :meth:`SetFactoryPolicy.category`
     570
     571        INPUT:
     572
     573        - ``constraints`` -- a set of constraints
     574
     575        OUTPUT:
     576
     577        - the correct policy category. More precisely, if ``constraints`` is
     578          the same as argument ``top_constraints`` passed to the constructor
     579          of ``self``, returns a policy category for normal parent (see class
     580          :class:`SelfParentPolicyCategory`) else returns a policy category
     581          for facade parent (see class :class:`FacadeParentPolicyCategory`).
     582
     583        EXAMPLE::
     584
     585            sage: from sage.structure.set_factories_example import *
     586            sage: from sage.structure.set_factories import *
     587            sage: pol = TopMostParentPolicy(XYPairs, (), XYPair)
     588            sage: pol.category(())
     589            Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     590            sage: pol.category((2,))
     591            Policy category with facade parent AllPairs
     592        """
     593        # assert(isinstance(constraints, tuple))
     594        factory = self._factory
     595        if constraints == self._top_constraints:
     596            return SelfParentPolicyCategory(self._Element)
     597        else:
     598            return FacadeParentPolicyCategory(
     599                factory(*self._top_constraints, policy=self))
     600
     601    def _repr_(self):
     602        r"""
     603        TESTS::
     604
     605            sage: from sage.structure.set_factories_example import *
     606            sage: TopMostParentPolicy(XYPairs, (), XYPair)  # indirect doctest
     607            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     608        """
     609        return "Set factory policy for %s with parent %s[=%s(%s)]" % (
     610            self._Element, self._factory(self._top_constraints),
     611            self._factory, self._top_constraints)
     612
     613
     614class FacadeParentPolicy(SetFactoryPolicy):
     615    r"""
     616    Policy for facade parent
     617
     618    INPUT:
     619
     620    - ``factory`` -- an instance of :class:`SetFactory`
     621    - ``parent`` -- an instance of :class:`Parent`
     622
     623    Given a factory ``F`` an a class ``E``, returns a policy for parent ``P``
     624    creating element as if they were created by ``parent``
     625
     626    EXAMPLES::
     627
     628        sage: from sage.structure.set_factories import *
     629        sage: from sage.structure.set_factories_example import *
     630
     631    We create a custom standard parent ``P``::
     632
     633        sage: selfpolicy = SelfParentPolicy(XYPairs, XYPair)
     634        sage: P = XYPairs(x=2, policy=selfpolicy)
     635        sage: pol = FacadeParentPolicy(XYPairs, P)
     636        sage: P2 = XYPairs(x=2, y=3, policy=pol)
     637        sage: el = P2.an_element()
     638        sage: el.parent() is P
     639        True
     640        sage: type(el) is P.element_class
     641        True
     642
     643    If ``parent`` is itself a facade parent, then transitivity is correctly
     644    applied::
     645
     646        sage: P =  XYPairs()
     647        sage: P2 = XYPairs(x=2)
     648        sage: P2.category()
     649        Join of Category of facade finite enumerated sets and Policy category with facade parent AllPairs
     650        sage: pol = FacadeParentPolicy(XYPairs, P)
     651        sage: P23 = XYPairs(x=2, y=3, policy=pol)
     652        sage: el = P2.an_element()
     653        sage: el.parent() is P
     654        True
     655        sage: type(el) is P.element_class
     656        True
     657    """
     658    def __init__(self, factory, parent):
     659        r"""
     660        TEST::
     661
     662            sage: from sage.structure.set_factories_example import *
     663            sage: from sage.structure.set_factories import *
     664            sage: F = FacadeParentPolicy(XYPairs, XYPairs()); F
     665            Set factory policy for facade parent AllPairs
     666            sage: TestSuite(F).run(skip='_test_category')
     667        """
     668        self._parent_for = parent
     669        SetFactoryPolicy.__init__(self, factory)
     670
     671    def category(self, constraints):
     672        r"""
     673        Return the policy category for given constraints
     674
     675        Return the policy category associated to ``self`` for parent
     676        constructed with the given constraints as per
     677        :meth:`SetFactoryPolicy.category`. Here constraints are ignored.
     678
     679        INPUT:
     680
     681        - ``constraints`` -- a set of constraints (ignored)
     682
     683        OUTPUT:
     684
     685        - an instance of :class:`FacadeParentPolicyCategory`.
     686
     687        EXAMPLE::
     688
     689            sage: from sage.structure.set_factories_example import *
     690            sage: from sage.structure.set_factories import *
     691            sage: F = FacadeParentPolicy(XYPairs, XYPairs())
     692            sage: F.category(())
     693            Policy category with facade parent AllPairs
     694        """
     695        # assert(isinstance(constraints, tuple))
     696        return FacadeParentPolicyCategory(self._parent_for)
     697
     698    def _repr_(self):
     699        r"""
     700        TESTS::
     701
     702            sage: from sage.structure.set_factories import FacadeParentPolicy
     703            sage: from sage.structure.set_factories_example import *
     704            sage: FacadeParentPolicy(XYPairs, XYPairs())  # indirect doctest
     705            Set factory policy for facade parent AllPairs
     706        """
     707        return "Set factory policy for facade parent %s" % (self._parent_for)
     708
     709####################################################
     710#               Policy Categories                  #
     711####################################################
     712
     713
     714class AbstractPolicyCategory(Category):
     715    r"""
     716    Abstract class for policy category
     717
     718    A policy category is a category for parent inheriting from
     719    :class:`SetFactoryParent` which set-up the element construction
     720    framework.
     721
     722    .. warning:: This class is a base class for policy category, one should
     723       not try to create instances.
     724    """
     725
     726    class ParentMethods:
     727
     728        def policy_category(self):
     729            r"""
     730            Return the policy category of ``self``
     731
     732            TESTS::
     733
     734                sage: from sage.structure.set_factories_example import XYPairs
     735                sage: XYPairs().policy_category()
     736                Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     737                sage: XYPairs(x=3).policy_category()
     738                Policy category with facade parent AllPairs
     739            """
     740            return self._policy.category(self._constraints)
     741
     742        # TODO abstract method parent_for
     743
     744        # QUESTION: Should we call:
     745        #     self._parent_for._element_constructor_
     746        # currently we don't call it directly because:
     747        #  - it may do some extra check we dont want to perform ?
     748        #  - calling directly element_class should be faster
     749        def _element_constructor_(self, *args, **keywords):
     750            r"""
     751            TESTS::
     752
     753                sage: from sage.structure.set_factories_example import XYPairs
     754                sage: XYPairs()((2,3)) # indirect doctest
     755                (2, 3)
     756                sage: XYPairs(x=3)((3,3)) # indirect doctest
     757                (3, 3)
     758                sage: XYPairs(x=3)((2,3)) # indirect doctest
     759                Traceback (most recent call last):
     760                ...
     761                ValueError: Wrong first coordinate
     762
     763                sage: XYPairs(x=3)((2,3), check=False) # Don't do this at home, kids
     764                (2, 3)
     765
     766            .. TODO:: Should we always call check element and let it decide
     767               which check has to be performed ?
     768            """
     769            check = keywords.get("check", True)
     770            res = self.element_class(self._parent_for, *args, **keywords)
     771            if check:
     772                self.check_element(res, check)
     773            return res
     774
     775
     776class SelfParentPolicyCategory(AbstractPolicyCategory):
     777    r"""
     778    Category implementing the fixed parent policy
     779
     780    A :class:`SetFactoryParent` which is initialized with a policy returning
     781    this category is a normal parent (ie: the parent of its element).
     782
     783    INPUT:
     784
     785    - ``Element`` -- a subclass of :class:`~.element.Element`: the class used
     786      when creating ``self.element_class`` by the category mechanism.
     787
     788    EXAMPLES::
     789
     790        sage: from sage.structure.set_factories_example import XYPairs
     791        sage: S = XYPairs()
     792        sage: S.policy_category()
     793        Self parent policy category with Element=<class 'sage.structure.set_factories_example.XYPair'>
     794        sage: S in S.policy_category()
     795        True
     796    """
     797    def __init__(self, Element):
     798        r"""
     799        TESTS::
     800
     801            sage: from sage.structure.set_factories import SelfParentPolicyCategory
     802            sage: TestSuite(SelfParentPolicyCategory(tuple)).run()
     803        """
     804        self._Element = Element
     805        # nothing to do:
     806        # super(SelfParentPolicyCategory, self).__init__()
     807
     808    def _repr_(self):
     809        r"""
     810        TESTS::
     811
     812            sage: from sage.structure.set_factories import SelfParentPolicyCategory
     813            sage: SelfParentPolicyCategory(tuple) # indirect doctest
     814            Self parent policy category with Element=<type 'tuple'>
     815        """
     816        return "Self parent policy category with Element=%s" % self._Element
     817
     818    def super_categories(self):
     819        r"""
     820        The super category of ``self`` that is ``Sets()``
     821
     822        TESTS::
     823
     824            sage: from sage.structure.set_factories import SelfParentPolicyCategory
     825            sage: SelfParentPolicyCategory(tuple).super_categories()
     826            [Category of sets]
     827        """
     828        return [Sets()]
     829
     830    class ParentMethods(AbstractPolicyCategory.ParentMethods):
     831
     832        @lazy_attribute
     833        def _parent_for(self):
     834            r"""
     835            The actual parent of the element created by ``self``
     836
     837            TESTS::
     838
     839                sage: from sage.structure.set_factories_example import XYPairs
     840                sage: S = XYPairs()
     841                sage: S._parent_for is S
     842                True
     843            """
     844            return self
     845
     846        @lazy_attribute
     847        def Element(self):
     848            r"""
     849            The class of the element of ``self``
     850
     851            TESTS::
     852
     853                sage: from sage.structure.set_factories_example import *
     854                sage: S = XYPairs()
     855                sage: S.Element is XYPair
     856                True
     857                sage: issubclass(S.element_class, XYPair)
     858                True
     859            """
     860            return self.policy_category()._Element
     861
     862
     863class FacadeParentPolicyCategory(AbstractPolicyCategory):
     864    r"""
     865    Category implementing the facade parent policy.
     866
     867    A :class:`SetFactoryParent` which is initialized with a policy returning
     868    this category is a facade parent (ie: the created parent contruct element
     869    as if they where constructed by the given parent).
     870
     871    INPUT:
     872
     873    - ``parent`` -- an instance of :class:`Parent`: the parent used
     874      when creating an element
     875
     876    EXAMPLES::
     877
     878        sage: from sage.structure.set_factories_example import XYPairs
     879        sage: S = XYPairs(x=3)
     880        sage: S.policy_category()
     881        Policy category with facade parent AllPairs
     882        sage: S in S.policy_category()
     883        True
     884    """
     885    def __init__(self, parent):
     886        r"""
     887        TESTS::
     888
     889            sage: from sage.structure.set_factories import FacadeParentPolicyCategory
     890            sage: TestSuite(FacadeParentPolicyCategory(ZZ)).run()
     891        """
     892        self._parent_for = parent
     893        # nothing to do:
     894        # super(FacadeParentPolicyCategory, self).__init__()
     895
     896    def _repr_(self):
     897        r"""
     898        TESTS::
     899
     900            sage: from sage.structure.set_factories import FacadeParentPolicyCategory
     901            sage: FacadeParentPolicyCategory(ZZ) # indirect doctest
     902            Policy category with facade parent Integer Ring
     903        """
     904        return "Policy category with facade parent %s" % self._parent_for
     905
     906    def super_categories(self):
     907        r"""
     908        The super category of ``self`` that is ``Sets().Facade()``
     909
     910        TESTS::
     911
     912            sage: from sage.structure.set_factories import FacadeParentPolicyCategory
     913            sage: FacadeParentPolicyCategory(ZZ).super_categories()
     914            [Category of facade sets]
     915        """
     916        return [Sets().Facade()]
     917
     918    class ParentMethods(AbstractPolicyCategory.ParentMethods):
     919
     920        @lazy_attribute
     921        def _parent_for(self):
     922            r"""
     923            The actual parent of the element created by ``self``
     924
     925            TESTS::
     926
     927                sage: from sage.structure.set_factories_example import XYPairs
     928                sage: S = XYPairs(x=3)
     929                sage: S._parent_for is XYPairs()
     930                True
     931            """
     932            return self.policy_category()._parent_for._parent_for
     933
     934        _facade_for = _parent_for
     935
     936        @lazy_attribute
     937        def element_class(self):
     938            r"""
     939            The class of the element of ``self``
     940
     941            TESTS::
     942
     943                sage: from sage.structure.set_factories_example import *
     944                sage: S = XYPairs(x=3)
     945                sage: S.element_class is XYPairs().element_class
     946                True
     947            """
     948            return self._parent_for.element_class
     949
     950####################################################
     951#                     Parent                       #
     952####################################################
     953
     954
     955class SetFactoryParent(Parent):
     956    r"""
     957    Abstract class for parent belonging to a set factory
     958
     959    INPUT:
     960
     961    - ``constraints`` -- a set of constraints
     962    - ``policy`` -- the policy for element construction
     963    - ``category`` -- the category of the parent (default to ``None``)
     964
     965    Depending on the constraints and the policy, initialize the parent in a
     966    proper category to set up element construction.
     967
     968    EXAMPLES::
     969
     970        sage: from sage.structure.set_factories_example import *
     971        sage: from sage.structure.set_factories import *
     972        sage: P = PairsX_(3, XYPairs._default_policy)
     973        sage: P is XYPairs(3)
     974        True
     975        sage: P.category()
     976        Join of Category of facade finite enumerated sets and Policy category with facade parent AllPairs
     977    """
     978    def __init__(self, constraints, policy, category=None):
     979        r"""
     980        TESTS::
     981
     982            sage: from sage.structure.set_factories import SetFactoryParent
     983            sage: from sage.structure.set_factories_example import XYPairs
     984            sage: isinstance(XYPairs(3), SetFactoryParent)  # indirect doctest
     985            True
     986        """
     987        # assert(isinstance(constraints, tuple))
     988        self._constraints = constraints
     989        assert(isinstance(policy, SetFactoryPolicy))
     990        self._policy = policy
     991        policy_category = policy.category(constraints)
     992        if category is None:
     993            category = policy_category
     994        elif isinstance(category, tuple):
     995            category = category + (policy_category,)
     996        elif isinstance(category, Category):
     997            category = (category, policy_category)
     998        else:
     999            raise TypeError("%s is not a correct category" % category)
     1000        # print "category = ", category
     1001        Parent.__init__(self, category=category)
     1002
     1003    def constraints(self):
     1004        r"""
     1005        Return the constraints for ``self``
     1006
     1007        Currently there is no specification on how constraints are handled.
     1008
     1009        EXAMPLES::
     1010
     1011            sage: from sage.structure.set_factories_example import XYPairs
     1012            sage: XYPairs().constraints()
     1013            ()
     1014            sage: XYPairs(x=3).constraints()
     1015            (3,)
     1016            sage: XYPairs(y=2).constraints()
     1017            (None, 2)
     1018        """
     1019        return self._constraints
     1020
     1021    def policy(self):
     1022        r"""
     1023        Return the policy used when ``self`` was created
     1024
     1025        EXAMPLES::
     1026
     1027            sage: from sage.structure.set_factories_example import XYPairs
     1028            sage: XYPairs().policy()
     1029            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     1030            sage: XYPairs(x=3).policy()
     1031            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     1032        """
     1033        return self._policy
     1034
     1035    def facade_policy(self):
     1036        r"""
     1037        Return the policy for parent facade for ``self``
     1038
     1039        EXAMPLES::
     1040
     1041            sage: from sage.structure.set_factories import *
     1042            sage: from sage.structure.set_factories_example import *
     1043
     1044        We create a custom standard parent ``P``::
     1045
     1046            sage: selfpolicy = SelfParentPolicy(XYPairs, XYPair)
     1047            sage: P = XYPairs(x=2, policy=selfpolicy)
     1048            sage: P.facade_policy()
     1049            Set factory policy for facade parent <class 'sage.structure.set_factories_example.PairsX__with_category'>
     1050
     1051        Now passing ``P.facade_policy()`` creates parent which are facade for
     1052        ``P``::
     1053
     1054            sage: P3 = XYPairs(x=2, y=3, policy=P.facade_policy())
     1055            sage: P3.facade_for() is P
     1056            True
     1057            sage: el = P3.an_element()
     1058            sage: el.parent() is P
     1059            True
     1060        """
     1061        return FacadeParentPolicy(self.factory(), self)
     1062
     1063    def factory(self):
     1064        r"""
     1065        Return the factory which built ``self``
     1066
     1067        EXAMPLES::
     1068
     1069            sage: from sage.structure.set_factories_example import XYPairs
     1070            sage: XYPairs().factory() is XYPairs
     1071            True
     1072            sage: XYPairs(x=3).factory() is XYPairs
     1073            True
     1074        """
     1075        return self._policy.factory()
     1076
     1077    def subset(self, *args, **options):
     1078        r"""
     1079        Return a subset of ``self`` by adding more constraints
     1080
     1081        EXAMPLES::
     1082
     1083            sage: from sage.structure.set_factories_example import XYPairs
     1084            sage: S = XYPairs()
     1085            sage: S3 = S.subset(x=3)
     1086            sage: S3.list()
     1087            [(3, 0), (3, 1), (3, 2), (3, 3), (3, 4)]
     1088
     1089        TESTS::
     1090
     1091            sage: S3 is XYPairs(3)
     1092            True
     1093            sage: S3 is XYPairs(x=3)
     1094            True
     1095        """
     1096        factory = self.factory()
     1097        constr = factory.add_constraints(self._constraints,
     1098                                         (args, options))
     1099        return factory(*constr, policy=self._policy, **options)
     1100
     1101    def _test_subset(self, **options):
     1102        r"""
     1103        Tests that subsets with no extra parameters returns ``self``
     1104
     1105        Currently only test that one gets the same parent is no more
     1106        constraints are added.
     1107
     1108        .. TODO::
     1109
     1110            Straighten the test when handling of constraints will be
     1111            specified.
     1112
     1113        EXAMPLES::
     1114
     1115            sage: from sage.structure.set_factories_example import XYPairs
     1116            sage: S = XYPairs()
     1117            sage: S._test_subset()
     1118        """
     1119        #TODO verifie avec self.constraints
     1120        tester = self._tester(**options)
     1121        tester.assertTrue(self.subset() is self)
     1122
     1123    @abstract_method
     1124    def check_element(self, x, check):
     1125        r"""
     1126        Check that ``x`` verifies the constraints of ``self``
     1127
     1128        INPUT:
     1129
     1130        - ``x`` -- an instance of ``self.element_class``.
     1131
     1132        - ``check`` -- the level of checking to be performed (usually a
     1133          boolean).
     1134
     1135        This method may assume that ``x`` was properly constructed by ``self``
     1136        or a possible super-set of ``self`` for which ``self`` is a facade. It
     1137        should return nothing is ``x`` verifies the constraints and raise a
     1138        :exc:`~exceptions.ValueError` explaining which constraints ``x``
     1139        fails otherwise.
     1140
     1141        The method should accept an extra parameter check specifying which
     1142        level of check should be performed. It will only be called when
     1143        ``bool(check)`` evaluates to ``True``.
     1144
     1145        .. TODO:: Should we always call check element and let it decide which
     1146           check has to be performed ?
     1147
     1148        EXAMPLES::
     1149
     1150            sage: from sage.structure.set_factories_example import XYPairs
     1151            sage: S = XYPairs()
     1152            sage: el = S((2,3))
     1153            sage: S.check_element(el, True)
     1154            sage: XYPairs(x=2).check_element(el, True)
     1155            sage: XYPairs(x=3).check_element(el, True)
     1156            Traceback (most recent call last):
     1157            ...
     1158            ValueError: Wrong first coordinate
     1159            sage: XYPairs(y=4).check_element(el, True)
     1160            Traceback (most recent call last):
     1161            ...
     1162            ValueError: Wrong second coordinate
     1163        """
     1164
     1165    def __contains__(self, x):
     1166        r"""
     1167        Default implementation for ``__contains__``:
     1168
     1169        INPUT::
     1170
     1171        - ``x`` -- any object
     1172
     1173        Check for class, parent and calls ``self.check_element(x)``
     1174
     1175        TESTS::
     1176
     1177            sage: from sage.structure.set_factories_example import XYPairs
     1178            sage: S = XYPairs()
     1179            sage: el = S((2,3))
     1180            sage: el in S
     1181            True
     1182            sage: el in XYPairs(x=2)
     1183            True
     1184            sage: el in XYPairs(x=3)
     1185            False
     1186            sage: el in XYPairs(y=4)
     1187            False
     1188        """
     1189        if (isinstance(x, self.element_class) and
     1190            x.parent() == self._parent_for):
     1191            try:
     1192                self.check_element(x, True)
     1193            except ValueError:
     1194                return False
     1195            else:
     1196                return True
     1197        else:
     1198            return False
     1199
     1200    def __call__(self, *args, **keywords):
     1201        r"""
     1202        TESTS::
     1203
     1204            sage: from sage.structure.set_factories_example import XYPairs
     1205            sage: S = XYPairs()
     1206            sage: el = S((2,3)); el
     1207            (2, 3)
     1208            sage: S(el) is el
     1209            True
     1210
     1211            sage: XYPairs(x=3)((2,3))
     1212            Traceback (most recent call last):
     1213            ...
     1214            ValueError: Wrong first coordinate
     1215
     1216            sage: XYPairs(x=3)(el)
     1217            Traceback (most recent call last):
     1218            ...
     1219            ValueError: Wrong first coordinate
     1220
     1221        .. TODO:: Should we always call check element and let it decide which
     1222           check has to be performed ?
     1223
     1224        """
     1225        # Ensure idempotence of element construction
     1226        if (len(args) == 1 and
     1227            isinstance(args[0], self.element_class) and
     1228            args[0].parent() == self._parent_for):
     1229            check = keywords.get("check", True)
     1230            if check:
     1231                self.check_element(args[0], check)
     1232            return args[0]
     1233        else:
     1234            return Parent.__call__(self, *args, **keywords)
  • new file sage/structure/set_factories_example.py

    diff --git a/sage/structure/set_factories_example.py b/sage/structure/set_factories_example.py
    new file mode 100644
    - +  
     1"""
     2Examples of set factories
     3=========================
     4
     5The goal of this module is to exemplify the use of set factories. Note that
     6the written code is intentionally kept minimal, many thing and in particular
     7several iterator could be written in a more efficient ways.
     8
     9.. SEEALSO:: :mod:`.set_factories` for an introduction, example of usage and
     10    the specification of set factories.
     11"""
     12#*****************************************************************************
     13#  Copyright (C) 2012 Florent Hivert <florent.hivert at lri.fr>
     14#
     15#  Distributed under the terms of the GNU General Public License (GPL)
     16#                  http://www.gnu.org/licenses/
     17#*****************************************************************************
     18
     19from sage.structure.unique_representation import UniqueRepresentation
     20from sage.structure.element_wrapper import ElementWrapper
     21from sage.structure.set_factories import (
     22    SetFactory, SetFactoryParent, TopMostParentPolicy)
     23from sage.sets.all import (Family, DisjointUnionEnumeratedSets)
     24from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     25from sage.rings.integer import Integer
     26from sage.misc.lazy_attribute import lazy_attribute
     27
     28MAX = 5
     29
     30
     31class XYPair(ElementWrapper):
     32    r"""
     33    A class for Element `(x,y)` with `x` and `y` in `\{0,1,2,3,4\}`
     34
     35    EXAMPLES::
     36
     37        sage: from sage.structure.set_factories_example import *
     38        sage: p = XYPair(Parent(), (0,1)); p
     39        (0, 1)
     40        sage: p = XYPair(Parent(), (0,8))
     41        Traceback (most recent call last):
     42        ...
     43        ValueError: numbers must be in range(5)
     44    """
     45    def __init__(self, parent, value, check=True):
     46        """
     47        TESTS::
     48
     49            sage: from sage.structure.set_factories_example import *
     50            sage: P = XYPairs(); p = P.list()[0]
     51            sage: TestSuite(p).run()
     52        """
     53        if check:
     54            if not isinstance(value, tuple):
     55                raise ValueError("Value %s must be a tuple" % value)
     56            if len(value) != 2:
     57                raise ValueError("Value must be of length 2")
     58            if not all(int(x) in range(MAX) for x in value):
     59                raise ValueError("numbers must be in range(%s)" % MAX)
     60        ElementWrapper.__init__(self, value, parent)
     61
     62
     63class AllPairs(SetFactoryParent, DisjointUnionEnumeratedSets):
     64    r"""
     65    This parent show how one can use set factories together with
     66    :class:`DisjointUnionEnumeratedSets`
     67
     68    TESTS::
     69
     70        sage: from sage.structure.set_factories_example import XYPairs
     71        sage: P = XYPairs(); P.list()
     72        [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4)]
     73    """
     74    def __init__(self, policy):
     75        r"""
     76        TESTS::
     77
     78            sage: from sage.structure.set_factories_example import XYPairs
     79            sage: TestSuite(XYPairs()).run()
     80        """
     81        SetFactoryParent.__init__(self, (), policy,
     82                                  category=FiniteEnumeratedSets())
     83        DisjointUnionEnumeratedSets.__init__(
     84            self, Family(range(MAX), self._single_pair),
     85            facade=True, keepkey=False,
     86            category=FiniteEnumeratedSets())
     87
     88    def _single_pair(self, letter):
     89        r"""
     90        TESTS::
     91
     92            sage: from sage.structure.set_factories_example import XYPairs
     93            sage: XYPairs()._single_pair(1)
     94            Disjoint union of Finite family {0: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 1: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 2: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 3: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>, 4: <class 'sage.structure.set_factories_example.SingletonPair_with_category'>}
     95        """
     96        return Pairs_Y(letter, policy=self.facade_policy())
     97
     98    def _repr_(self):
     99        r"""
     100        TESTS::
     101
     102            sage: from sage.structure.set_factories_example import XYPairs
     103            sage: XYPairs()   # indirect doctest
     104            AllPairs
     105        """
     106        return "AllPairs"
     107
     108    def check_element(self, el, check):
     109        r"""
     110        TESTS::
     111
     112            sage: from sage.structure.set_factories_example import XYPairs
     113            sage: P = XYPairs()
     114            sage: P.check_element(P.an_element(), True)
     115            sage: XYPairs()((7, 0))  # indirect doctest
     116            Traceback (most recent call last):
     117            ...
     118            ValueError: numbers must be in range(5)
     119        """
     120        pass
     121
     122
     123class PairsX_(SetFactoryParent, UniqueRepresentation):
     124    r"""
     125    The set of pair `(x, 0), (x, 1), ..., (x, 4)`
     126
     127    TESTS::
     128
     129        sage: from sage.structure.set_factories_example import XYPairs
     130        sage: P = XYPairs(0); P.list()
     131        [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)]
     132    """
     133    def __init__(self, x, policy):
     134        r"""
     135        TESTS::
     136
     137            sage: from sage.structure.set_factories_example import XYPairs
     138            sage: TestSuite(XYPairs(0)).run()
     139        """
     140        self._x = x
     141        SetFactoryParent.__init__(self, (x,), policy,
     142                                  category=FiniteEnumeratedSets())
     143
     144    def an_element(self):
     145        r"""
     146        TESTS::
     147
     148            sage: from sage.structure.set_factories_example import XYPairs
     149            sage: P = XYPairs(x=0); P.an_element()
     150            (0, 0)
     151        """
     152        return self._element_constructor_((self._x, 0), check=False)
     153
     154    def check_element(self, el, check):
     155        r"""
     156        TESTS::
     157
     158            sage: from sage.structure.set_factories_example import XYPairs
     159            sage: P = XYPairs(x=1)
     160            sage: P.check_element(P.an_element(), True)
     161            sage: XYPairs(x=1)((0, 0))  # indirect doctest
     162            Traceback (most recent call last):
     163            ...
     164            ValueError: Wrong first coordinate
     165        """
     166        (x, y) = el.value
     167        if x != self._x:
     168            raise ValueError("Wrong first coordinate")
     169
     170    def __iter__(self):
     171        r"""
     172        TESTS::
     173
     174            sage: from sage.structure.set_factories_example import XYPairs
     175            sage: list(XYPairs(x=1))
     176            [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4)]
     177        """
     178        for i in range(MAX):
     179            yield self._element_constructor_((self._x, i), check=False)
     180
     181
     182class Pairs_Y(SetFactoryParent, DisjointUnionEnumeratedSets):
     183    r"""
     184    The set of pair `(0, y), (1, y), ..., (4, y)`
     185
     186    TESTS::
     187
     188        sage: from sage.structure.set_factories_example import XYPairs
     189        sage: P = XYPairs(y=1); P.list()
     190        [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)]
     191    """
     192    def __init__(self, y, policy):
     193        r"""
     194        TESTS::
     195
     196            sage: from sage.structure.set_factories_example import XYPairs
     197            sage: TestSuite(XYPairs(y=1)).run()
     198        """
     199        self._y = y
     200        SetFactoryParent.__init__(self, (None, y), policy,
     201                                  category=FiniteEnumeratedSets())
     202        DisjointUnionEnumeratedSets.__init__(
     203            self, Family(range(MAX), self._single_pair),
     204            facade=True, keepkey=False,
     205            category=FiniteEnumeratedSets())  # TODO remove and fix disjoint union.
     206
     207    def an_element(self):
     208        r"""
     209        TESTS::
     210
     211            sage: from sage.structure.set_factories_example import XYPairs
     212            sage: XYPairs(y=1).an_element()
     213            (0, 1)
     214        """
     215        return self._element_constructor_((0, self._y), check=False)
     216
     217    def _single_pair(self, letter):
     218        r"""
     219        TESTS::
     220
     221            sage: from sage.structure.set_factories_example import XYPairs
     222            sage: XYPairs(y=1)._single_pair(0)
     223            <class 'sage.structure.set_factories_example.SingletonPair_with_category'>
     224        """
     225        return SingletonPair(letter, self._y, policy=self.facade_policy())
     226
     227    def check_element(self, el, check):
     228        r"""
     229        TESTS::
     230
     231            sage: from sage.structure.set_factories_example import XYPairs
     232            sage: P = XYPairs(y=1)
     233            sage: P.check_element(P.an_element(), True)
     234            sage: XYPairs(y=1)((1, 0))  # indirect doctest
     235            Traceback (most recent call last):
     236            ...
     237            ValueError: Wrong second coordinate
     238        """
     239        (x, y) = el.value
     240        if y != self._y:
     241            raise ValueError("Wrong second coordinate")
     242
     243
     244class SingletonPair(SetFactoryParent, UniqueRepresentation):
     245    r"""
     246    TESTS::
     247
     248        sage: from sage.structure.set_factories_example import XYPairs
     249        sage: P = XYPairs(0,1); P.list()
     250        [(0, 1)]
     251    """
     252    def __init__(self, x, y, policy):
     253        r"""
     254        TESTS::
     255
     256            sage: from sage.structure.set_factories_example import XYPairs
     257            sage: TestSuite(XYPairs(0,1)).run()
     258        """
     259        self._xy = (x, y)
     260        SetFactoryParent.__init__(self, (x, y), policy,
     261                                  category=FiniteEnumeratedSets())
     262
     263    def check_element(self, el, check):
     264        r"""
     265        TESTS::
     266
     267            sage: from sage.structure.set_factories_example import XYPairs
     268            sage: XYPairs(0,1).check_element(XYPairs()((0,1)), True)
     269            sage: XYPairs(0,1).check_element(XYPairs()((1,0)), True)
     270            Traceback (most recent call last):
     271            ...
     272            ValueError: Wrong coordinate
     273            sage: XYPairs(0,1)((1,1))
     274            Traceback (most recent call last):
     275            ...
     276            ValueError: Wrong coordinate
     277        """
     278        if el.value != self._xy:
     279            raise ValueError("Wrong coordinate")
     280
     281    def __iter__(self):
     282        r"""
     283        TESTS::
     284
     285            sage: from sage.structure.set_factories_example import XYPairs
     286            sage: list(XYPairs(0,1))
     287            [(0, 1)]
     288        """
     289        yield self._element_constructor_(self._xy, check=False)
     290
     291
     292class XYPairsFactory(SetFactory):
     293    r"""
     294    An example of factory for sets of pairs of integers
     295
     296    .. SEEALSO:: :mod:`.set_factories` for an introduction to factories.
     297    """
     298    def __call__(self, x=None, y=None, policy=None):
     299        r"""
     300        Construct the subset from constraints
     301
     302        Consider the set `S` of couple `(x,y)` with `x` and `y` in
     303        `I:=\{0,1,2,3,4\}`. Returns the subsets of element of `S` satisfying
     304        some constraints.
     305
     306        INPUT:
     307
     308        - ``x=a`` -- where ``a`` is an integer (default to ``None``).
     309        - ``y=b`` -- where ``b`` is an integer (default to ``None``).
     310        - ``policy`` -- the policy passed to the created set.
     311
     312        .. SEEALSO:: :class:`.set_factories.SetFactoryPolicy`
     313
     314        EXAMPLES::
     315
     316            sage: from sage.structure.set_factories_example import XYPairsFactory
     317            sage: XYPairs = XYPairsFactory()
     318            sage: P = XYPairs(); P.list()
     319            [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4)]
     320
     321        .. note:: This function is actually the ``__call__`` method of
     322                  :class:`XYPairsFactory`.
     323        """
     324        if policy is None:
     325            policy = self._default_policy
     326
     327        if isinstance(x, (Integer, int)):
     328            if isinstance(y, (Integer, int)):
     329                return SingletonPair(x, y, policy)
     330            else:
     331                return PairsX_(x, policy)
     332        elif isinstance(y, (Integer, int)):
     333                return Pairs_Y(y, policy)
     334        else:
     335            return AllPairs(policy)
     336
     337    def add_constraints(self, cons, (args, opts)):
     338        r"""
     339        Add constraints to the set ``cons`` as per :meth:`SetFactory.add_constraints <.set_factories.SetFactory.add_constraints>`
     340
     341        This is a very crude implementation which ignore optional arguments.
     342        They will be handled directly by the factory :func:`XYPair`
     343
     344        EXAMPLE::
     345
     346            sage: from sage.structure.set_factories_example import XYPairs
     347            sage: XYPairs.add_constraints((3,), ((2,), {}))
     348            (3, 2)
     349            sage: XYPairs.add_constraints((), ((2,), {}))
     350            (2,)
     351        """
     352        return cons + args
     353
     354    @lazy_attribute
     355    def _default_policy(self):
     356        r"""
     357        TESTS::
     358
     359            sage: from sage.structure.set_factories_example import XYPairsFactory
     360            sage: XYPairs = XYPairsFactory()
     361            sage: XYPairs._default_policy
     362            Set factory policy for <class 'sage.structure.set_factories_example.XYPair'> with parent AllPairs[=Factory for XY pairs(())]
     363        """
     364        return TopMostParentPolicy(self, (), XYPair)
     365
     366    def _repr_(self):
     367        """
     368        TESTS::
     369
     370            sage: from sage.structure.set_factories_example import XYPairs
     371            sage: XYPairs   # indirect doctest
     372            Factory for XY pairs
     373        """
     374        return "Factory for XY pairs"
     375
     376XYPairs = XYPairsFactory()
     377XYPairs.__doc__ = XYPairsFactory.__call__.__doc__