# -*- coding: utf-8 -*-
r"""
Factor complexity, Bispecial factors and Extension types

This module was developped for the article on the factor complexity of
infinite sequences genereated by substitutions written with Valérie Berthé
[BL2014]_.

.. [BL2014] V. Berthé, S. Labbé, Factor Complexity of S-adic sequences
   generated by the Arnoux-Rauzy-Poincaré Algorithm. arXiv:1404.4189__ (April, 2014).

__ http://arxiv.org/abs/1404.4189

EXAMPLES:

The extension type of an ordinary bispecial factor::

    sage: from slabbe import ExtensionType1to1
    sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
    sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
    sage: E
      E(w)   1   2   3
       1             X
       2             X
       3     X   X   X
     m(w)=0, ord.
    sage: E.is_ordinaire()
    True

Creation of a strong-weak pair of bispecial words from a neutral
not ordinairy word::

    sage: m = WordMorphism({1:[1,2,3],2:[2,3],3:[3]})
    sage: E = ExtensionType1to1([(1,2),(2,3),(3,1),(3,2),(3,3)], [1,2,3])
    sage: E
      E(w)  1   2   3
       1        X
       2            X
       3    X   X   X
     m(w)=0, neutral
    sage: E1, E2 = E.apply(m)
    sage: E1
      E(w)  1   2   3
       1
       2        X   X
       3    X   X   X
     m(w)=1, strong
    sage: E2
      E(w)   1   2   3
        1        X
        2
        3            X
     m(w)=-1, weak

TODO:

    - use __classcall_private__ stuff for ExtensionType ?
    - fix bug of apply for ExtensionTypeLong when the word appears in the
      image of a letter (first initial fix: 18 May 2016, to be confirmed)
    - use this to compute the factor complexity function
    - When should two bispecial extension type be equal? In graphs, we sometimes
      prefer when they are all different...
"""
#*****************************************************************************
#       Copyright (C) 2010-2016 Sébastien Labbé <slabqc@gmail.com>
#
#  Distributed under the terms of the GNU General Public License version 2 (GPLv2)
#
#  The full text of the GPLv2 is available at:
#
#                  http://www.gnu.org/licenses/
#*****************************************************************************
from __future__ import absolute_import, print_function

from collections import defaultdict, Counter
import itertools
from sage.misc.classcall_metaclass import ClasscallMetaclass
from sage.misc.cachefunc import cached_method
from sage.misc.table import table
from sage.combinat.words.word import FiniteWord_class, Word
from sage.combinat.words.morphism import WordMorphism
from sage.graphs.digraph import DiGraph

common_substitutions_dict = dict(
ar1=WordMorphism({1:[1],      2:[2,1],   3:[3,1]}),
ar2=WordMorphism({1:[1,2],   2:[2],     3:[3,2]}),
ar3=WordMorphism({1:[1,3],   2:[2,3],   3:[3]}),
b12=WordMorphism({1:[1,2],   2:[2],     3:[3]}),
b13=WordMorphism({1:[1,3],   2:[2],     3:[3]}),
b21=WordMorphism({1:[1],     2:[2,1],   3:[3]}),
b23=WordMorphism({1:[1],     2:[2,3],   3:[3]}),
b31=WordMorphism({1:[1],     2:[2],     3:[3,1]}),
b32=WordMorphism({1:[1],     2:[2],     3:[3,2]}),
p12=WordMorphism({1:[1,2],   2:[2],     3:[3,1,2]}),
p13=WordMorphism({1:[1,3],   2:[2,1,3], 3:[3]}),
p21=WordMorphism({1:[1],     2:[2,1],   3:[3,2,1]}),
p23=WordMorphism({1:[1,2,3], 2:[2,3],   3:[3]}),
p31=WordMorphism({1:[1],     2:[2,3,1], 3:[3,1]}),
p32=WordMorphism({1:[1,3,2], 2:[2],     3:[3,2]})
)

######################################
# Extension Type
######################################
class ExtensionType(object):
    #__metaclass__ = ClasscallMetaclass
    #@staticmethod
    #def __classcall_private__(cls, *args, **kwds):
    #    if len(args) == 2:
    #        a0, a1 = args
    #        if (isinstance(a0, FiniteWord_class) 
    #            and isinstance(a1, FiniteWord_class)):
    #            return self.from_factor(a0, a1)
    #        else:
    #            raise NotImplementedError
    #    else:
    #        raise NotImplementedError

    @staticmethod
    def from_factor(bispecial, word, nleft=1, nright=1, repr_options=None):
        r"""
        INPUT:

        - ``bispecial`` -- the bispecial factor
        - ``word`` -- the word describing the language
        - ``nleft`` -- length of left extensions (default: ``1``)
        - ``nright`` -- length of right extensions (default: ``1``)
        - ``repr_options`` - dict (default:``None``) representation options
          whether to include the factor and or the valence in the string or
          latex representation. The value ``None`` is replaced by
          ``dict(factor=False,multiplicity=True,valence=False))``.

        EXAMPLES::

            sage: from slabbe import ExtensionType
            sage: W = Words([0,1,2])
            sage: ExtensionType.from_factor(W(), W([0,1,1,2,0]))
              E(w)   0   1   2
               0         X
               1         X   X
               2     X
             m(w)=-1, weak

        ::

            sage: ExtensionType.from_factor(W(), W([0,1,1,2,0]), nleft=2)
              E(w)   0   1   2
               01        X
               11            X
               12    X
             m(w)=-1, weak

        ::

            sage: ExtensionType.from_factor(W(), W([0,1,1,2,0]), nright=2)
              E(w)   11   12   20
               0     X
               1          X    X
             m(w)=0, ord.

        ::

            sage: prefix = words.FibonacciWord()[:1000] 
            sage: ExtensionType.from_factor(W(), prefix, nright=2, nleft=2)
              E(w)   00   01   10
               00              X
               10         X    X
               01    X    X
             m(w)=0, ord.
        """
        W = word.parent()
        left = list(W.iterate_by_length(nleft))
        right = list(W.iterate_by_length(nright))
        L = []
        for a,b in itertools.product(left, right):
            if (a*bispecial*b).is_factor(word):
                L.append((a,b))
        if nleft == 1 and nright == 1:
            L = [(a[0], b[0]) for (a,b) in L]
            return ExtensionType1to1(L, W.alphabet(), factor=bispecial)
        else:
            L = [(tuple(a), tuple(b)) for (a,b) in L]
            factors_lr = word.factor_set(nleft+nright)
            return ExtensionTypeLong(L, W.alphabet(), factor=bispecial,
                    factors_length_k=factors_lr,
                    empty=bispecial.is_empty(),
                    repr_options=repr_options)

    @staticmethod
    def from_morphism(m, repr_options=None):
        r"""
        Return the extension type of the empty word in the language defined by
        the image of the free monoid under the morphism m.

        INPUT:

        - ``m`` - endomorphim
        - ``repr_options`` - dict (default:``None``) representation options
          whether to include the factor and or the valence in the string or
          latex representation. The value ``None`` is replaced by
          ``dict(factor=False,multiplicity=True,valence=False))``.

        EXAMPLES::

            sage: from slabbe import ExtensionType
            sage: ar = WordMorphism({1:[1,3],2:[2,3],3:[3]})
            sage: ExtensionType.from_morphism(ar)
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X
             m(w)=0, ord.

        ::

            sage: p = WordMorphism({1:[1,2,3],2:[2,3],3:[3]})
            sage: ExtensionType.from_morphism(p)
              E(w)   1   2   3
               1         X    
               2             X
               3     X   X   X
             m(w)=0, neutral

        ::

            sage: b12 = WordMorphism({1:[1,2],2:[2],3:[3]})
            sage: ExtensionType.from_morphism(b12)
              E(w)   1   2   3
               1         X    
               2     X   X   X
               3     X   X   X
             m(w)=2, strong

        """
        assert m.is_endomorphism(), "m(=%s) must be an endomorphism"
        L = []
        images = m.images()
        # externes
        for ma in images:
            for mb in images:
                L.append( (ma[-1],mb[0]) )
        # internes
        for image in images:
            for i in range(len(image)-1):
                L.append( (image[i], image[i+1]) )
        alphabet = m.domain().alphabet()
        return ExtensionType1to1(L, alphabet, repr_options=repr_options)

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: hash(E)
            -73163835              # 32-bit
            5904352424964049776    # 64-bit

        """
        #return hash((self._pairs, self._factor))
        return hash(self._pairs)
    def __eq__(self, other):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: ar = WordMorphism({1:[1,3],2:[2,3],3:[3]})
            sage: E.apply(ar) == E
            False
            sage: F = ExtensionType1to1(L, [1,2,3])
            sage: E == F
            True
        """
        if not isinstance(other, type(self)):
            return False
        else:
            #return self._factor == other._factor and self._pairs == other._pairs
            return self._pairs == other._pairs

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: it = E.__iter__()
            sage: sorted(it)
            [(1, 3), (2, 3), (3, 1), (3, 2), (3, 3)]

        """
        return iter(self._pairs)

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X
            m(w)=0, ord.

        With chignons::

            sage: E = ExtensionType1to1(L, [1,2,3], ('a','b'), Word('xyz'))
            sage: E
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X
            m(w)=0, ord.

        The factor may be included in the repr::

            sage: ExtensionType1to1(L, [1,2,3], ('a','b'), Word('xyz'), 
            ....:                   repr_options=dict(factor=True))
            w=as(u)b=xyz
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X
            m(w)=0, ord.

        ::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: ExtensionTypeLong(L, (1,2,3))
              E(w)   1   2   3
               21        X
               31        X
               12    X   X   X
               22    X
               23    X
             m(w)=0, neutral
        """
        mw = self.multiplicity()
        info = self.information()
        lines = []
        if self._repr_options['factor']:
            chignons = self._chignons
            first_line = "w={}s(u){}={}".format(chignons[0], chignons[1],
                                                self._factor.string_rep())
            lines.append(first_line)
        lines.append(repr(self.table()))
        if self._repr_options['multiplicity']:
            lines.append("m(w)={}, {}".format(mw, info))
        if self._repr_options['valence']:
            left_val1 = self.left_valence()
            if hasattr(self, 'left_word_extensions'):
                left_extensions = self.left_word_extensions()
                left_ext_length = len(left_extensions[0])
                left_val2 = len(left_extensions)
                lines.append("d^-_1(w)={}, d^-_{}(w){}".format(left_val1,
                                            left_ext_length, left_val2))
            else:
                lines.append("d^-(w)={}".format(left_val1))
        return "\n".join(lines)

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3], ('a', 'b'))
            sage: latex(E)
            \begin{tabular}{c}
            \begin{tabular}{cccc}
            $E(w)$ & $1$ & $2$ & $3$ \\
            $1$ &   &   & $\times$ \\
            $2$ &   &   & $\times$ \\
            $3$ & $\times$ & $\times$ & $\times$ \\
            \end{tabular}\\
            $m(w) = 0$, ord.\\
            \end{tabular}

        ::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: latex(E)
            \begin{tabular}{c}
            \begin{tabular}{cccc}
            $E(w)$ & $1$ & $2$ & $3$ \\
            $21$ &   & $\times$ &   \\
            $31$ &   & $\times$ &   \\
            $12$ & $\times$ & $\times$ & $\times$ \\
            $22$ & $\times$ &   &   \\
            $23$ & $\times$ &   &   \\
            \end{tabular}\\
            $m(w) = 0$, neutral\\
            \end{tabular}

        With factor appearing::

            sage: E = ExtensionTypeLong(L, (1,2,3), repr_options=dict(factor=True))
            sage: latex(E)
            \begin{tabular}{c}
            $w=s(u)=$\\
            \begin{tabular}{cccc}
            $E(w)$ & $1$ & $2$ & $3$ \\
            $21$ &   & $\times$ &   \\
            $31$ &   & $\times$ &   \\
            $12$ & $\times$ & $\times$ & $\times$ \\
            $22$ & $\times$ &   &   \\
            $23$ & $\times$ &   &   \\
            \end{tabular}\\
            $m(w) = 0$, neutral\\
            \end{tabular}

        With valence appearing::

            sage: E = ExtensionTypeLong(L, (1,2,3), repr_options=dict(valence=True))
            sage: latex(E)
            \begin{tabular}{c}
            \begin{tabular}{cccc}
            $E(w)$ & $1$ & $2$ & $3$ \\
            $21$ &   & $\times$ &   \\
            $31$ &   & $\times$ &   \\
            $12$ & $\times$ & $\times$ & $\times$ \\
            $22$ & $\times$ &   &   \\
            $23$ & $\times$ &   &   \\
            \end{tabular}\\
            $m(w) = 0$, neutral\\
            $d^-_1(w)=3$, $d^-_{2}(w)=5$\\
            \end{tabular}

        """
        mw = self.multiplicity()
        info = self.information()
        lines = []
        lines.append('\\begin{tabular}{c}')
        if self._repr_options['factor']:
            chignons = self._chignons
            s = "$w={}s(u){}={}$\\\\".format(chignons[0], chignons[1], self._factor.string_rep())
            lines.append(s)
        table_latex = self.table()._latex_()
        table_latex = table_latex.replace('E(w)', '$E(w)$')
        table_latex = table_latex.replace(' X ', ' $\\times$ ')
        lines.append(table_latex+'\\\\')
        if self._repr_options['multiplicity']:
            lines.append("$m(w) = {}$, {}\\\\".format(mw, info))
        if self._repr_options['valence']:
            left_val1 = self.left_valence()
            if hasattr(self, 'left_word_extensions'):
                left_extensions = self.left_word_extensions()
                left_val2 = len(left_extensions)
                if left_extensions:
                    left_ext_length = len(left_extensions.pop())
                else:
                    left_ext_length = "ND"
                lines.append("$d^-_1(w)={}$, $d^-_{{{}}}(w)={}$\\\\".format(left_val1,
                                            left_ext_length, left_val2))
            else:
                lines.append("$d^-(w)={}$\\\\".format(left_val1))
        lines.append('\\end{tabular}')
        return '\n'.join(lines)

    def factor(self):
        r"""
        Return the bispecial factor.

        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: p = WordMorphism({1:[1,2,3],2:[2,3],3:[3]})
            sage: E = ExtensionType1to1([(1,2),(2,3),(3,1),(3,2),(3,3)], [1,2,3])
            sage: A,B = E.apply(p)
            sage: A.factor()
            word: 3
            sage: B.factor()
            word: 23

        ::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:      2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: b12 = WordMorphism({1:[1,2],2:[2],3:[3]})
            sage: A,B = E.apply(b12)
            sage: A.factor()
            word:
            sage: B.factor()
            word: 2

        ::

            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: data = [((3, 1), (2,)), ((1, 2), (3,)), ((3, 2), (3,)), ((2,
            ....:      3), (1,)), ((2, 3), (2,)), ((2, 3), (3,)), ((3, 3), (1,))]
            sage: e = ExtensionTypeLong(data, [1,2,3])
            sage: e = e.apply(S[132])[1]
            sage: e.factor()
            word: 2
            sage: e = e.apply(S[321])[0]
            sage: e.factor()
            word: 21
            sage: e = e.apply(S[312])[0]
            sage: e.factor()
            word: 212
        """
        return self._factor
    def information(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.information()
            'ord.'
        """
        mw = self.multiplicity()
        if mw > 0:
            return 'strong'
        elif mw < 0:
            return 'weak'
        elif self.is_ordinaire():
            return 'ord.'
        else:
            return "neutral"
    def equivalence_class(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: len(E.equivalence_class())
            6
        """
        L = []
        for i,j,k in itertools.permutations((1,2,3)):
            perm = WordMorphism({1:[i],2:[j],3:[k]})
            e, = [e for e in self.apply(perm) if e.is_chignons_empty()]
            L.append(e)
        return L

    def is_subset(self, other):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: F = ExtensionTypeLong(L, (1,2,3))
            sage: E.is_subset(F)
            True
            sage: F.is_subset(E)
            False
        """
        if not isinstance(other, ExtensionType):
            return False
        else:
            return self._pairs <= other._pairs

    def is_equivalent(self, other):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.is_equivalent(E)
            True
        """
        if not isinstance(other, ExtensionType):
            return False
        else:
            return other in self.equivalence_class()

    def is_empty(self):
        r"""
        Return whether the word associated to this extension type is empty.

        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.is_empty()
            False

        ::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.is_empty()
            True

        ::

            sage: E = ExtensionTypeLong(L, (1,2,3), empty=False)
            sage: E.is_empty()
            False

        """
        if hasattr(self, '_empty'):
            return self._empty
        else:
            return False

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

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.is_bispecial()
            True
        """
        return self.right_valence() > 1 and self.left_valence() > 1
    def is_ordinaire(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.is_ordinaire()
            True
        """
        raise NotImplementedError

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.is_neutral()
            True
        """
        return self.multiplicity() == 0

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.cardinality()
            5
        """
        return len(self._pairs)
    def left_right_projection(self):
        r"""

        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,2), (2,2), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: left, right = E.left_right_projection()
            sage: dict(left)
            {1: 1, 2: 1, 3: 3}
            sage: dict(right)
            {1: 1, 2: 3, 3: 1}

        ::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: left, right = E.left_right_projection()
            sage: sorted(left.items())
            [(word: 12, 3), (word: 21, 1), (word: 22, 1), (word: 23, 1), (word: 31, 1)]
            sage: sorted(right.items())
            [(word: 1, 3), (word: 2, 3), (word: 3, 1)]
        """
        left_projection = Counter()
        right_projection = Counter()
        for a,b in self:
            left_projection[a] += 1
            right_projection[b] += 1
        return left_projection, right_projection

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.left_extensions()
            {1, 2, 3}

        """
        raise NotImplementedError
    def right_extensions(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.right_extensions()
            {1, 2, 3}

        """
        raise NotImplementedError
    def palindromic_valence(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.palindromic_valence()
            1
        """
        return len(self.palindromic_extensions())

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.multiplicity()
            0

        ::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.multiplicity()
            0
        """
        return self.cardinality() - self.left_valence() - self.right_valence() + 1
    def image(self, m):
        r"""

        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: b23 = WordMorphism({1:[1],2:[2,3],3:[3]})
            sage: E.image(b23)
              E(w)   1   2   3
               31        X
               12            X
               32            X
               23    X   X   X
               33    X
             m(w)=0, neutral
        """
        L = []
        for e in self.apply(m):
            if e.is_chignons_empty():
                L.append(e)
        assert len(L) == 1, "len of L should be 1"
        return L[0]

    def rec_enum_set_under_sadic(self, substitutions, substitutions_dict,
            keep_equal_length=False, growth_limit=float('inf')):
        r"""
        Return the graded recursively enumerated set of all the extension type
        leading to those of age k generated by a finite sequence of
        substitutions of length k.

        INPUT:

        - ``substitutions`` -- the sequence of substitutions
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_equal_length`` -- (default: False) whether to keep images that
          have equal length, thus it will include all bispecial factors of age <= k on the
          highest graded component.
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images

        .. TODO::

            Check whether the second component of vertices should be integer
            instead of the history.

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: R = E1.rec_enum_set_under_sadic([132]*2+[123]*6, S)
            sage: R
            A recursively enumerated set with a graded structure (breadth first search)
            sage: R.graded_component(0)
            {(  E(w)   1   2   3
                 21        X
                 31        X
                 12    X   X   X
                 22            X
                 23    X
              m(w)=0, neutral, word: , ())}
            sage: [len(R.graded_component(i)) for i in range(9)]
            [1, 2, 2, 2, 2, 2, 2, 2, 3]

        This used to be a bug in sage, it is now fixed::

            sage: R.graded_component(9)
            set()

        Including all younger bispecial factors::

            sage: R = E1.rec_enum_set_under_sadic([132]*2+[123]*6, S, keep_equal_length=True)
            sage: [len(R.graded_component(i)) for i in range(9)]
            [1, 3, 4, 5, 6, 7, 8, 9, 16]
            sage: B = R.graded_component(8)
            sage: sorted((Z.factor(),Z.multiplicity()) for Z,_,_ in B)
            [(word: , 0),
             (word: 2, 0),
             (word: 22, 0),
             (word: 22322, 0),
             (word: 22322322, 0),
             (word: 22322322322, 0),
             (word: 22322322322322, 0),
             (word: 22322322322322322, 0),
             (word: 22322322322322322322, -1),
             (word: 2322, 0),
             (word: 2322322, 0),
             (word: 2322322322, 0),
             (word: 2322322322322, 0),
             (word: 2322322322322322, 0),
             (word: 2322322322322322322, 0),
             (word: 2322322322322322322, 1)]

        """
        def child(V):
            Y,w,history = V
            age = len(history)
            rep = []
            if age >= len(substitutions):
                return rep
            a = substitutions[-age-1]
            for Z in Y.apply(substitutions_dict[a], growth_limit=growth_limit):
                if keep_equal_length or not len(Y.factor()) == len(Z.factor()):
                    rep.append((Z,Z.factor(),(a,)+history))
            return rep
        root = (self, self.factor(), tuple())

        from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet
        R = RecursivelyEnumeratedSet([root], child, structure='graded')
        return R

    def rec_enum_set_under_sadic_joined(self, substitutions, 
            substitutions_dict, keep_equal_length=False, keep_unique=False,
            growth_limit=float('inf'), filter_fn=None):
        r"""
        Return the recursively enumerated set of extension type generated
        by a language of substitutions where the extension type of the same
        age are joined.

        INPUT:

        - ``substitutions`` -- the sequence of substitutions
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_equal_length`` -- (default: False) whether to keep images that
          have equal length
        - ``keep_unique`` -- (default: False) whether to keep a unique copy of
          equal extension types
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images
        - ``filter_fn`` -- function (default: ``None``)

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))

        ::

            sage: seq = [231,213,213,321]+[213,231,231,123]+[132,123]
            sage: R = E1.rec_enum_set_under_sadic_joined(seq, S, growth_limit=1)
            sage: R
            A recursively enumerated set with a graded structure (breadth first search)

        ::

            sage: from slabbe.bispecial_extension_type import recursively_enumerated_set_to_digraph
            sage: G = recursively_enumerated_set_to_digraph(R)
            sage: G
            Looped multi-digraph on 11 vertices
        """
        def child(V):
            ExtIN,history = V
            rep = []
            if filter_fn and not filter_fn(ExtIN):
                return rep
            age = len(history)
            if age >= len(substitutions):
                return rep
            a = substitutions[-age-1]
            ExtOUT = [Z for ext in ExtIN 
                        for Z in ext.apply(substitutions_dict[a], growth_limit=growth_limit)
                        if keep_equal_length or not len(ext.factor())==len(Z.factor())]
            ExtOUT = remove_extension_types_subsets(ExtOUT)
            if keep_unique:
                ExtOUT = list(set(ExtOUT))
            ExtOUT.sort(key=lambda ext:len(ext.factor()))
            ExtOUT = tuple(ExtOUT)
            rep.append((ExtOUT,(a,)+history))
            return rep
        root = ((self,), tuple())

        from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet
        return RecursivelyEnumeratedSet([root], child, structure='graded')

    def rec_enum_set_under_language(self, language, initial, substitutions_dict,
            keep_empty=False, label='history', growth_limit=float('inf')):
        r"""
        Return the recursively enumerated set of extension type generated
        by a language of substitutions.

        INPUT:

        - ``language`` -- the language of substitutions
        - ``initial`` -- initial substitution
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_empty`` -- (default: False) whether to keep images that
          are empty
        - ``label`` -- 'history' or 'previous' (default: ``'history'``),
          whether the vertices contain the whole history of the bispecial
          word or only the previous applied substitution
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images

        EXAMPLES::

            sage: from slabbe.mult_cont_frac import Brun
            sage: from slabbe.bispecial_extension_type import ExtensionType
            sage: from slabbe.language import languages
            sage: algo = Brun()
            sage: S = algo.substitutions()
            sage: L = languages.Brun()
            sage: v = algo.image((1,e,pi), 5)
            sage: prefix = algo.s_adic_word(v)[:100000]
            sage: E = ExtensionType.from_factor(prefix.parent()(), prefix, nleft=2)
            sage: E.rec_enum_set_under_language(L, 123, S)
            An enumerated set with a forest structure

        ::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: from slabbe.language import languages
            sage: L = languages.Brun()
            sage: E = [E for E in E1.apply(S[123]) if E.factor().length() == 1][0]
            sage: R = E.rec_enum_set_under_language(L, 123, S, label='previous')
            sage: R
            A recursively enumerated set (breadth first search)
        """
        # what can go before each letter
        before = defaultdict(list)
        for w in language.words_of_length_iterator(2): 
            before[w[1]].append(w[0])
        before = dict(before)

        def child(V):
            Y,w = V
            rep = []
            for a in before[w[0]]:
                for Z in Y.apply(substitutions_dict[a], growth_limit=growth_limit):
                    if keep_empty or not Z.is_empty():
                        if label == 'previous':
                            rep.append((Z,(a,)))
                        elif label == 'history':
                            rep.append((Z,(a,)+w))
                        else:
                            raise ValueError('when label={}'.format(label))
            return rep
        root = (self,(initial,))

        from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet
        if label == 'history':
            structure = 'forest'
        elif label == 'previous':
            structure = None
        else:
            raise ValueError('when label={}'.format(label))
        R = RecursivelyEnumeratedSet([root], child, structure=structure)
        return R

    def rec_enum_set_under_language_joined(self, language, initial,
            substitutions_dict, keep_equal_length=False, keep_unique=False,
            label='history', growth_limit=float('inf')):
        r"""
        Return the recursively enumerated set of extension type generated
        by a language of substitutions where the extension type of the same
        age and joined.

        INPUT:

        - ``language`` -- the language of substitutions
        - ``initial`` -- initial substitution
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_equal_length`` -- (default: False) whether to keep images that
          have equal length
        - ``keep_unique`` -- (default: False) whether to keep a unique copy of
          equal extension types
        - ``label`` -- 'history' or 'previous' (default: ``'history'``),
          whether the vertices contain the whole history of the bispecial
          word or only the previous applied substitution
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionType
            sage: from slabbe.mult_cont_frac import Brun
            sage: from slabbe.language import languages
            sage: algo = Brun()
            sage: S = algo.substitutions()
            sage: L = languages.Brun()
            sage: v = algo.image((1,e,pi), 5)
            sage: prefix = algo.s_adic_word(v)[:100000]
            sage: E = ExtensionType.from_factor(prefix.parent()(), prefix, nleft=2)
            sage: E.rec_enum_set_under_language_joined(L, 123, S)
            A recursively enumerated set (breadth first search)
            sage: E.rec_enum_set_under_language_joined(L, 123, S, label='previous')
            A recursively enumerated set (breadth first search)

        ::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: from slabbe.language import languages
            sage: L = languages.Brun()
            sage: E = [E for E in E1.apply(S[123]) if E.factor().length() == 1][0]
            sage: R = E.rec_enum_set_under_language_joined(L, 123, S, label='previous')
            sage: R
            A recursively enumerated set (breadth first search)
        """
        pairs = [(self, initial)]
        return rec_enum_set_under_language_joined_from_pairs(pairs, language,
            substitutions_dict, keep_equal_length=keep_equal_length,
            keep_unique=keep_unique, label=label,
            growth_limit=growth_limit)

    def graph_under_sadic(self, substitutions, substitutions_dict,
            keep_equal_length=False, raw=False, growth_limit=float('inf')):
        r"""
        Return the graph of extension types under the application of an
        s-adic word.

        INPUT:

        - ``substitutions`` -- the sequence of substitutions
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_equal_length`` -- (default: False) whether to keep images that
          have equal length, thus it will include all bispecial factors of age <= k on the
          highest graded component.
        - ``raw`` -- bool (default: False), whether to keep the vertices
          raw, i.e. including history and factors information
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: E1.graph_under_sadic([132]*2+[123]*6, S)
            Looped multi-digraph on 9 vertices
            sage: E1.graph_under_sadic([132]*2+[123]*6, S, keep_equal_length=True)
            Looped multi-digraph on 19 vertices
            sage: E1.graph_under_sadic([132]*2+[123]*6, S, raw=True)
            Looped multi-digraph on 18 vertices
            sage: E1.graph_under_sadic([132]*2+[123]*6, S, raw=True, keep_equal_length=True)
            Looped multi-digraph on 59 vertices

        ::

            sage: G = E1.graph_under_sadic([132]*2+[123]*6, S)
            sage: from slabbe.tikz_picture import TikzPicture
            sage: _ = TikzPicture.from_graph(G).pdf(view=False) # long time (9s)
        """
        R = self.rec_enum_set_under_sadic(substitutions,
                substitutions_dict, keep_equal_length=keep_equal_length,
                growth_limit=growth_limit)
        G = recursively_enumerated_set_to_digraph(R)
        if raw:
            return G
        else:
            edges = set((u,v,history[0]) for ((u,_,_),(v,_,history),_) in G.edges(sort=False))
            return DiGraph(edges, format='list_of_edges', loops=True, multiedges=True)

    def graph_under_sadic_joined(self, substitutions, 
            substitutions_dict, keep_equal_length=False, keep_unique=False,
            growth_limit=float('inf'), filter_fn=None, raw=False):
        r"""
        Return the graph of extension types under the application of an
        s-adic word where the extension type of the same age are joined.

        INPUT:

        - ``substitutions`` -- the sequence of substitutions
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_equal_length`` -- (default: False) whether to keep images that
          have equal length
        - ``keep_unique`` -- (default: False) whether to keep a unique copy of
          equal extension types
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images
        - ``filter_fn`` -- function (default: ``None``)
        - ``raw`` -- bool (default: False), whether to keep the vertices
          raw, i.e. including history and factors information

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: E1._repr_options=dict(factor=False)
            sage: seq = [231,213,213,213,321]+[213,231,231,231,123]+[132,123]
            sage: E1.graph_under_sadic_joined(seq, S, growth_limit=1)
            Looped multi-digraph on 10 vertices
        """
        R = self.rec_enum_set_under_sadic_joined(substitutions,
                substitutions_dict, keep_equal_length=keep_equal_length,
                keep_unique=keep_unique, growth_limit=growth_limit,
                filter_fn=filter_fn)
        G = recursively_enumerated_set_to_digraph(R)
        if raw:
            return G
        else:
            edges = set((u,v,history[0]) for ((u,_),(v,history),_) in G.edges(sort=False))
            return DiGraph(edges, format='list_of_edges', loops=True, multiedges=True)

    def graph_under_language(self, language, initial, substitutions_dict,
            keep_empty=False, max_depth=float('inf'),
            growth_limit=float('inf')):
        r"""
        Return the recursively enumerated set of extension type generated
        by a language of substitutions.

        INPUT:

        - ``language`` -- the language of substitutions
        - ``initial`` -- initial substitution
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_empty`` -- bool (default: False) whether to keep images
          that are empty
        - ``max_depth`` -- integer (default: ``float('inf')``), max depth
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: from slabbe.language import languages
            sage: L = languages.Brun()
            sage: E = [E for E in E1.apply(S[123]) if E.factor().length() == 1][0]
            sage: E.graph_under_language(L, 123, S, max_depth=2)  # long time (3s)
            Looped multi-digraph on 41 vertices
        """
        R = self.rec_enum_set_under_language(language, initial,
                substitutions_dict, keep_empty, label='previous',
                growth_limit=growth_limit)
        G = recursively_enumerated_set_to_digraph(R, max_depth=max_depth)
        edges = set((u,v,label) for ((u,_),(v,(label,)),_) in G.edges(sort=False))
        return DiGraph(edges, format='list_of_edges', loops=True, multiedges=True)

    def graph_under_language_joined(self, language, initial, substitutions_dict,
            keep_empty=False, max_depth=float('inf'),
            growth_limit=float('inf')):
        r"""
        Return the recursively enumerated set of extension type generated
        by a language of substitutions where the extension type of the same
        age and joined.

        INPUT:

        - ``language`` -- the language of substitutions
        - ``initial`` -- initial substitution
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_empty`` -- bool (default: False) whether to keep images
          that are empty
        - ``max_depth`` -- integer (default: ``float('inf')``), max depth
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: from slabbe.language import languages
            sage: L = languages.Brun()
            sage: E = [E for E in E1.apply(S[123]) if E.factor().length() == 1][0]
            sage: E.graph_under_language_joined(L, 123, S, max_depth=2)
            Looped multi-digraph on 26 vertices
            sage: E.graph_under_language_joined(L, 123, S, max_depth=2, growth_limit=1)
            Looped multi-digraph on 21 vertices
            sage: E.graph_under_language_joined(L, 123, S)   # not tested long time
            Looped multi-digraph on 715 vertices
        """
        R = self.rec_enum_set_under_language_joined(language, initial,
                substitutions_dict, keep_empty, label='previous',
                growth_limit=growth_limit)
        G = recursively_enumerated_set_to_digraph(R, max_depth=max_depth)
        edges = set((u,v,label) for ((u,_),(v,(label,)),_) in G.edges(sort=False))
        return DiGraph(edges, format='list_of_edges', loops=True, multiedges=True)

    def weakstrong_sublanguage(self, language, initial, substitutions_dict, depth, keep_empty=False):
        r"""
        Return the word of length depth+1 ending with initial letter of the
        language that gives weak or strong bispecial factors.

        INPUT:

        - ``language`` -- the language of substitutions
        - ``initial`` -- initial substitution
        - ``substitutions_dict`` - dict of substitutions
        - ``depth`` -- depth
        - ``keep_empty`` -- (default: False) whether to keep images that
          are empty

        EXAMPLES::

            sage: from slabbe.mult_cont_frac import Brun
            sage: from slabbe.bispecial_extension_type import ExtensionType
            sage: from slabbe.language import languages
            sage: algo = Brun()
            sage: S = algo.substitutions()
            sage: L = languages.Brun()
            sage: v = algo.image((1,e,pi), 5)
            sage: prefix = algo.s_adic_word(v)[:100000]
            sage: E = ExtensionType.from_factor(prefix.parent()(), prefix, nleft=2)
            sage: E.weakstrong_sublanguage(L, 123, S, 2)
            set()
            sage: E.weakstrong_sublanguage(L, 123, S, 3)
            {(213, 213, 231, 123), (231, 213, 231, 123)}
            sage: E.weakstrong_sublanguage(L, 123, S, 4)   # long time (8s)
            {(132, 213, 213, 231, 123),
             (213, 213, 213, 231, 123),
             (213, 213, 231, 231, 123),
             (213, 231, 213, 231, 123),
             (231, 213, 213, 231, 123),
             (231, 213, 231, 231, 123),
             (231, 231, 213, 231, 123),
             (312, 231, 213, 231, 123)}
        """
        R = self.rec_enum_set_under_language(language, initial, substitutions_dict, keep_empty)
        it = R.elements_of_depth_iterator(depth) 
        S = [w for (X,w) in it if X.multiplicity() != 0]
        return set(tuple(s) for s in S)

    def weakstrong_poset(self, language, initial, substitutions_dict, depth):
        r"""
        Return the Poset of words of the language ending with initial
        letter that gives weak or strong bispecial factors with the "is
        suffix" relation.

        INPUT:

        - ``language`` -- the language of substitutions
        - ``initial`` -- initial substitution
        - ``substitutions_dict`` - dict of substitutions
        - ``depth`` -- depth

        EXAMPLES::

            sage: from slabbe.mult_cont_frac import Brun
            sage: from slabbe.bispecial_extension_type import ExtensionType
            sage: from slabbe.language import languages
            sage: algo = Brun()
            sage: S = algo.substitutions()
            sage: L = languages.Brun()
            sage: v = algo.image((1,e,pi), 5)
            sage: prefix = algo.s_adic_word(v)[:1000]
            sage: E = ExtensionType.from_factor(prefix.parent()(), prefix, nleft=2)
            sage: P = E.weakstrong_poset(L, 123, S, 4)
            sage: P
            Finite poset containing 2 elements

        ::

            sage: from slabbe.tikz_picture import TikzPicture
            sage: tikz = TikzPicture.from_poset(P)         # optional dot2tex
            sage: _ = tikz.pdf(view=False)                 # optional dot2tex
        """
        from sage.combinat.posets.posets import Poset
        is_suffix = lambda w,u: Word(w).is_suffix(Word(u))
        WS = [self.weakstrong_sublanguage(language, initial, substitutions_dict, depth) 
                                  for depth in range(depth)]
        from functools import reduce
        F = reduce(lambda x,y: x.union(y), WS)
        P = Poset((F,is_suffix))
        return P

    def distinct_bispecial_factors_under_sadic(self, substitutions, substitutions_dict,
            keep_empty=True):
        r"""
        Return the list of distinct bispecial factors obtain from this extension
        type after the application of substitutions.

        This method essentially removes duplicates with distinct extension
        types but subset of others.

        INPUT:

        - ``substitutions`` -- the sequence of substitutions
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_empty`` -- (default: True) whether to keep images that are
          empty, thus it will include all bispecial factors of age <= k on the
          highest graded component.

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: L = E1.distinct_bispecial_factors_under_sadic([132]*2+[123]*6, S)
            sage: sorted(Z.factor() for Z in L)
            [word: ,
             word: 2,
             word: 22,
             word: 22322,
             word: 22322322,
             word: 22322322322,
             word: 22322322322322,
             word: 22322322322322322,
             word: 22322322322322322322,
             word: 2322,
             word: 2322322,
             word: 2322322322,
             word: 2322322322322,
             word: 2322322322322322,
             word: 2322322322322322322]
            sage: sorted(Z.multiplicity() for Z in L)
            [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
        """
        R = self.rec_enum_set_under_sadic(substitutions, substitutions_dict, keep_empty)
        B = R.graded_component(len(substitutions))
        return remove_extension_types_subsets(E for E,w,history in B)

    def bispecial_factors_table_under_sadic(self, substitutions,
            substitutions_dict, keep_empty=True):
        r"""
        Return the summary table of bispecial factors obtain from this extension
        type after the application of substitutions.

        INPUT:

        - ``substitutions`` -- the sequence of substitutions
        - ``substitutions_dict`` - dict of substitutions
        - ``keep_empty`` -- (default: True) whether to keep images that are
          empty, thus it will include all bispecial factors of age <= k on the
          highest graded component.

        EXAMPLES::

            sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
            sage: from slabbe.mult_cont_frac import Brun
            sage: S = Brun().substitutions()
            sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
            ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
            sage: E1 = ExtensionTypeLong(data, (1,2,3))
            sage: E1.bispecial_factors_table_under_sadic([132]*2+[123]*6, S)
              |w|   w                      m(w)   d^-(w)   d2^-(w)   info
            +-----+----------------------+------+--------+---------+---------+
              0                            0      3        5         ord.
              1     2                      0      3        4         neutral
              2     22                     0      2        2         ord.
              4     2322                   0      2        3         ord.
              5     22322                  0      2        2         ord.
              7     2322322                0      2        3         ord.
              8     22322322               0      2        2         ord.
              10    2322322322             0      2        3         ord.
              11    22322322322            0      2        2         ord.
              13    2322322322322          0      2        3         ord.
              14    22322322322322         0      2        2         ord.
              16    2322322322322322       0      2        3         ord.
              17    22322322322322322      0      2        2         ord.
              19    2322322322322322322    1      2        3         strong
              20    22322322322322322322   -1     2        2         weak
        """
        B = self.distinct_bispecial_factors_under_sadic(substitutions, substitutions_dict, keep_empty)
        rows = []
        for ext in B:
            mw = ext.multiplicity()
            info = ext.information()
            w = ext.factor()
            row = [w.length(), w, mw, ext.left_valence(),
                    len(ext.left_word_extensions()), info]
            rows.append(row)
        rows.sort(key=lambda row:row[0])
        return table(rows=rows, header_row=['|w|', 'w', 'm(w)', 'd^-(w)', 'd2^-(w)', 'info'])

class ExtensionType1to1(ExtensionType):
    r"""
    INPUT:

    - ``L`` - list of pairs of letters
    - ``alphabet`` - the alphabet
    - ``chignons`` - optional (default: None), pair of words added to the
      left  and to the right of the image of the previous bispecial
    - ``factor`` - optional (default: empty word), the factor
    - ``repr_options`` - dict (default:``None``)
      representation options whether to include the factor and or the
      valence in the string or latex representation. The value ``None`` is
      replaced by ``dict(factor=False,multiplicity=True,valence=False))``.

    EXAMPLES::

        sage: from slabbe import ExtensionType1to1
        sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
        sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
        sage: E
          E(w)   1   2   3
           1             X
           2             X
           3     X   X   X
         m(w)=0, ord.

    With chignons::

        sage: E = ExtensionType1to1(L, [1,2,3], ('a','b'))
        sage: E
          E(w)   1   2   3
           1             X
           2             X
           3     X   X   X
         m(w)=0, ord.
    """
    def __init__(self, L, alphabet, chignons=('',''), factor=Word(),
            repr_options=None):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X
             m(w)=0, ord.
        """
        self._pairs = frozenset(L)
        self._alphabet = alphabet
        self._chignons = tuple(chignons)
        self._factor = factor
        self._repr_options = dict(factor=False,multiplicity=True,valence=False)
        if repr_options is not None:
            self._repr_options.update(repr_options)

    def table(self):
        r"""
        return a table representation of self.

        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.table()
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X

        ::

            sage: E = ExtensionType1to1(L, alphabet=(1,2,3), chignons=('a', 'b'))
            sage: E.table()
              E(w)   1   2   3
                1            X
                2            X
                3    X   X   X
        """
        lines = []
        L = R = sorted(self._alphabet)
        for a in L:
            line = []
            for b in R:
                if (a,b) in self._pairs:
                    line.append('X')
                else:
                    line.append(' ')
            lines.append(line)
        Ew = "E(w)"
        t = table(rows=lines, header_row=R, header_column=[Ew]+L)
        t.options(header_column=False,header_row=False,align='center')
        return t

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: print(E._repr_old())
            _123
            1OOX
            2OOX
            3XXX

        With chignons::

            sage: E = ExtensionType1to1(L, [1,2,3], ('a','b'))
            sage: print(E._repr_old())
            awb
            _123
            1OOX
            2OOX
            3XXX
        """
        lines = []
        if self._chignons != ('',''):
            line = "%sw%s" % self._chignons
            lines.append(line)
        line = '_' + "".join(map(str, self._alphabet))
        lines.append(line)
        for a in self._alphabet:
            line = str(a)
            for b in self._alphabet:
                if (a,b) in self._pairs:
                    line += 'X'
                else:
                    line += 'O'
            lines.append(line)
        return '\n'.join(lines)

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: print(E._latex_old())
            \begin{array}{r|rrr}
            v & 1 & 2 & 3\\
            \hline
            1 &   &   & \times\\
            2 &   &   & \times\\
            3 & \times & \times & \times
            \end{array}

        ::

            sage: E = ExtensionType1to1(L, [1,2,3], ('a','b'))
            sage: print(E._latex_old())
            \begin{array}{r|rrr}
            awb & 1 & 2 & 3\\
            \hline
            1 &   &   & \times\\
            2 &   &   & \times\\
            3 & \times & \times & \times
            \end{array}
        """
        lines = []
        lines.append(r"\begin{array}{r|rrr}")
        chignons = "%sw%s" % self._chignons if self._chignons != ('','') else 'v'
        lines.append(" & ".join([chignons] + [str(a) for a in self._alphabet]) + r'\\')
        lines.append(r"\hline")
        for a in self._alphabet:
            line = [str(a)]
            for b in self._alphabet:
                if (a,b) in self._pairs:
                    line.append(r'\times')
                else:
                    line += ' '
            lines.append(' & '.join(line) + r'\\')
        assert lines[-1][-2:] == r'\\'
        lines[-1] = lines[-1][:-2]
        lines.append(r"\end{array}")
        return '\n'.join(lines)

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3], ('a', 'b'))
            sage: E.chignons_multiplicity_tuple()
            ('a', 'b', 0)
        """
        return self._chignons + (self.multiplicity(),)

    def apply(self, m, growth_limit=float('inf')):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X
             m(w)=0, ord.
            sage: ar = WordMorphism({1:[1,3],2:[2,3],3:[3]})
            sage: E.apply(ar)
            (  E(w)  1   2   3
                1            X
                2            X
                3    X   X   X
             m(w)=0, ord.,)

        ::

            sage: ar = WordMorphism({1:[3,1],2:[3,2],3:[3]})
            sage: E.apply(ar)
            (  E(w)   1   2   3
                1             X
                2             X
                3     X   X   X
             m(w)=0, ord.,)

        Creation of a pair of ordinaire bispecial words from an
        **ordinaire** word::

            sage: e = ExtensionType1to1([(1,3),(2,3),(3,1),(3,2),(3,3)], [1,2,3])
            sage: p0 = WordMorphism({1:[1,2,3],2:[2,3],3:[3]})
            sage: e.apply(p0)
            ( E(w)   1   2   3
                1
                2            X
                3    X   X   X
             m(w)=0, ord.,)
            sage: p3 = WordMorphism({1:[1,3,2],2:[2],3:[3,2]})
            sage: e.apply(p3)
            (  E(w)   1   2   3
                1
                2             X
                3     X   X   X
             m(w)=0, ord.,
               E(w)   1   2   3
                1             X
                2     X   X   X
                3
             m(w)=0, ord.)

        Creation of a strong-weak pair of bispecial words from a neutral
        **not ordinaire** word::

            sage: p0 = WordMorphism({1:[1,2,3],2:[2,3],3:[3]})
            sage: e = ExtensionType1to1([(1,2),(2,3),(3,1),(3,2),(3,3)], [1,2,3])
            sage: e.apply(p0)
            (  E(w)   1   2   3
                1
                2         X   X
                3     X   X   X
             m(w)=1, strong,
               E(w)   1   2   3
                1         X
                2
                3             X
             m(w)=-1, weak)

        Creation of a pair of ordinaire bispecial words from an **not
        ordinaire** word::

            sage: p1 = WordMorphism({1:[1,2],2:[2],3:[3,1,2]})
            sage: e = ExtensionType1to1([(1,2),(2,3),(3,1),(3,2),(3,3)], [1,2,3])
            sage: e.apply(p1)
            (  E(w)   1   2   3
                1     X   X   X
                2             X
                3
             m(w)=0, ord.,
               E(w)   1   2   3
                1
                2         X
                3     X   X   X
             m(w)=0, ord.)

        This result is now fixed::

            sage: e = ExtensionType1to1([(1,2), (3,3)], [1,2,3])
            sage: p3 = WordMorphism({1:[1,3,2],2:[2],3:[3,2]})
            sage: e.apply(p3)
            (  E(w)   1   2   3
                1         X
                2             X
                3
             m(w)=-1, weak,)

        ::

            sage: e = ExtensionType1to1([(2,2),(2,3),(3,1),(3,2),(3,3)], [1,2,3])
            sage: e.apply(p3)
            (  E(w)   1   2   3
                1
                2         X   X
                3     X   X   X
             m(w)=1, strong,)

        This result is now fixed::

            sage: e = ExtensionType1to1([(2,2),(2,3),(3,1),(3,2),(3,3)], [1,2,3])
            sage: p2 = WordMorphism({1:[1],2:[2,3,1],3:[3,1]})
            sage: e.apply(p2)
            (  E(w)   1   2   3
                1     X   X   X
                2         X   X
                3
             m(w)=1, strong,)

        ::

            sage: e = ExtensionType1to1([(1,2),(3,3)], [1,2,3])
            sage: e.apply(p2)
            (  E(w)   1   2   3
                1         X
                2
                3             X
             m(w)=-1, weak,)

        TESTS::

            sage: L = [(1,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E
              E(w)   1   2   3
               1             X
               2
               3
             m(w)=0, neutral
            sage: ar = WordMorphism({1:[1,3],2:[2,3],3:[3]})
            sage: E.apply(ar)
            ()

        POSSIBLE BUG::

            sage: from slabbe import ExtensionType
            sage: b23 = WordMorphism({1:[1],2:[2,3],3:[3]})
            sage: b13 = WordMorphism({1:[1,3],2:[2],3:[3]})
            sage: b31 = WordMorphism({1:[1],2:[2],3:[3,1]})
            sage: e = ExtensionType.from_morphism(b23)
            sage: r = e.apply(b23)[0]
            sage: r.apply(b13)
            ()
            sage: r.apply(b31)
            ()
        """
        images = m.images()
        common_suffix = images[0]
        common_prefix = images[0]
        for image in images:
            common_suffix = image.longest_common_suffix(common_suffix)
            common_prefix = image.longest_common_prefix(common_prefix)
        word_pairs = []
        for a,b in self:
            left = common_suffix * m(a)
            right = m(b) * common_prefix
            word_pairs.append( (left, right) )
        A, B = zip(*word_pairs)
        left_nb = max(map(len, A))
        right_nb = max(map(len, B))
        #print(left_nb, right_nb)
        L = []
        for i in range(1, left_nb+1):
            for j in range(right_nb):
                extensions = defaultdict(list)
                for left,right in word_pairs:
                    if len(left) < i or len(right) <= j:
                        continue
                    chignons = left[len(left)-i+1:], right[:j]
                    extensions[chignons].append( (left[-i], right[j]) )

                for chignons, extension in extensions.items():
                    if len(chignons[0]) + len(chignons[1]) > growth_limit:
                        continue
                    factor = chignons[0] * m(self._factor) * chignons[1]
                    e = ExtensionType1to1(L=extension, alphabet=self._alphabet,
                            chignons=chignons, factor=factor,
                            repr_options=self._repr_options)
                    if e.is_bispecial():
                        L.append(e)
        return tuple(L)

    def left_valence(self, length=1):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.left_valence()
            3

        """
        if length == 1:
            return len(self.left_extensions())
        else:
            raise NotImplementedError('can not compute left valence of '
                    'length(={})'.format(length))

    def right_valence(self, length=1):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.right_valence()
            3
        """
        if length == 1:
            return len(self.right_extensions())
        else:
            raise NotImplementedError('can not compute right valence of '
                    'length(={})'.format(length))
    def is_ordinaire(self):
        r"""
        EXAMPLES:

        ordinary::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E
              E(w)   1   2   3
               1             X
               2             X
               3     X   X   X
             m(w)=0, ord.
            sage: E.is_ordinaire()
            True

        strong::

            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3), (1,1)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.is_ordinaire()
            False

        neutral but not ordinary::

            sage: L = [(1,1), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E
              E(w)   1   2   3
               1     X
               2             X
               3     X   X   X
             m(w)=0, neutral
            sage: E.is_neutral()
            True
            sage: E.is_ordinaire()
            False

        not neutral, not ordinaire::

            sage: L = [(1,1), (2,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E
              E(w)   1   2   3
               1     X
               2     X
               3         X   X
             m(w)=-1, weak
            sage: E.is_neutral()
            False
            sage: E.is_ordinaire()
            False
        """

        if not self.is_bispecial():
            return False
        elif not self.is_neutral():
            return False
        left_projection, right_projection = self.left_right_projection()  
        left_most = [a for (a,v) in left_projection.items() if v > 1]
        if len(left_most) != 1: 
            return False
        right_most = [b for (b,v) in right_projection.items() if v > 1]
        if len(right_most) != 1: 
            return False
        #print(left_most,right_most)
        if (left_most[0],right_most[0]) not in self._pairs:
            return False
        return True

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.cardinality()
            5
        """
        return len(self._pairs)
    def left_extensions(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.left_extensions()
            {1, 2, 3}

        """
        return set(a for a,b in self)
    def right_extensions(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.right_extensions()
            {1, 2, 3}
        """
        return set(b for a,b in self)
    def palindromic_extensions(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3])
            sage: E.palindromic_extensions()
            {3}
        """
        return set(a for a,b in self if a == b)

    def extension_digraph(self):
        r"""
        Return the extension directed graph made of edges

            (-1,a) -> (+1,b)

        for each pair (a,b) in the extension set.

        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.extension_digraph()
            Bipartite graph on 6 vertices
        """
        left, right = zip(*self)
        left = set([(-1,a) for a in left])
        right = set([(+1,b) for b in right])
        V = left.union(right)
        E = [((-1,a), (+1,b)) for (a,b) in self]
        G = DiGraph([V,E], format='vertices_and_edges')
        from sage.graphs.bipartite_graph import BipartiteGraph
        left = list(left)
        right = list(right)
        return BipartiteGraph(G, partition=(left,right))

    def extension_graph(self, loops=False):
        r"""
        Return the extension graph made of edges (a,b)
        for each pair (a,b) in the extension set.

        EXAMPLES::

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: E.extension_graph()
            Graph on 3 vertices
            sage: E.extension_graph(loops=True)
            Looped graph on 3 vertices

        ::

            sage: L = [(1,1), (1,2), (2,1), (3,3)]
            sage: E = ExtensionType1to1(L, alphabet=(1,2,3))
            sage: G = E.extension_graph()
            sage: G.is_connected()
            False
        """
        from sage.graphs.graph import Graph
        edges = [(a,b) for (a,b) in self if loops or a!=b]
        left, right = zip(*self)
        vertices = set(left).union(right)
        return Graph([vertices,edges], format='vertices_and_edges',
                multiedges=False, loops=loops)

class ExtensionTypeLong(ExtensionType):
    r"""
    Generalized to words.

    INPUT:

    - ``L`` - list of pairs of *words*
    - ``alphabet`` - the alphabet
    - ``chignons`` - optional (default: None), pair of words added to the
      left  and to the right of the image of the previous bispecial
    - ``factor`` - optional (default: empty word), the factor
    - ``factors_length_k`` - list of factors of length 2. If None, they are
      computed from the provided extension assuming the bispecial factor is
      *empty*.
    - ``empty`` - bool, (optional, default: None), if None, then it is
      computed from the chignons and takes value True iff the chignons are
      empty.
    - ``repr_options`` - dict (default:``None``) representation options
      whether to include the factor and or the valence in the string or
      latex representation. The value ``None`` is replaced by
      ``dict(factor=False,multiplicity=True,valence=False))``.

    EXAMPLES::

        sage: from slabbe import ExtensionTypeLong
        sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
        ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
        sage: E = ExtensionTypeLong(L, (1,2,3))
        sage: E
          E(w)   1   2   3
           21        X
           31        X
           12    X   X   X
           22    X
           23    X
        m(w)=0, neutral

    """
    def __init__(self, L, alphabet, chignons=('',''), factor=Word(),
            factors_length_k=None, empty=None,
            repr_options=None):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
        """
        self._pairs = frozenset((Word(a),Word(b)) for a,b in L)
        self._alphabet = alphabet
        self._chignons = tuple(chignons)
        self._factor = factor
        self._factors_length_k = factors_length_k
        if empty is None:
            self._empty = self.is_chignons_empty()
        else:
            self._empty = empty
        self._repr_options = dict(factor=False,multiplicity=True,valence=False)
        if repr_options is not None:
            self._repr_options.update(repr_options)

    def is_subset(self, other):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: F = ExtensionTypeLong(L, (1,2,3))
            sage: E.is_subset(F)
            True
            sage: F.is_subset(E)
            False

        With incomplete word extensions::

            sage: L = [((3,), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: G = ExtensionTypeLong(L, (1,2,3))
            sage: G
              E(w)   1   2   3
               21        X
               31        X
               12    X   X   X
               3     X
            m(w)=0, neutral
            sage: G.is_subset(E)
            True
        """
        if not isinstance(other, ExtensionType):
            return False
        else:
            return (self._pairs <= other._pairs or 
                self._pairs <= frozenset((u[i:],v[:j]) 
                for (u,v) in other._pairs
                for i in range(len(u)) 
                for j in range(1,len(v)+1) ))

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

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.is_chignons_empty()
            True

        """
        return all(len(c)==0 for c in self._chignons)

    def factors_length_k(self, k=None):
        r"""
        Returns the set of factors of length k of the language.

        This is computed from the extension type if it was not provided at
        the construction.

        INPUT:

        - ``k`` -- integer or None, if None, return factors of length k already
          computed

        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: sorted(E.factors_length_k(2))
            [word: 12, word: 21, word: 22, word: 23, word: 31]
            sage: sorted(E.factors_length_k())
            [word: 12, word: 21, word: 22, word: 23, word: 31]

        It stores the value and can not compute for other lengths::

            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: sorted(E.factors_length_k(3))
            [word: 121, word: 122, word: 123, word: 212, word: 221, word: 231, word: 312]
            sage: sorted(E.factors_length_k())
            [word: 121, word: 122, word: 123, word: 212, word: 221, word: 231, word: 312]
            sage: sorted(E.factors_length_k(4))
            Traceback (most recent call last):
            ...
            NotImplementedError: can't compute factors of length k for this word

        ::

            sage: L = [(1, 2), (3, 2), (1, 3), (3, 3), (3, 1), (2, 3), (1, 1)]
            sage: E = ExtensionTypeLong((([a], [b]) for a,b in L), (1,2,3))
            sage: E.factors_length_k(2)
            {word: 11, word: 12, word: 13, word: 23, word: 31, word: 32, word: 33}

        TESTS::

            sage: L = [(1, 2), (3, 2), (1, 3), (3, 3), (3, 1), (2, 3), (1, 1)]
            sage: E = ExtensionTypeLong((([a], [b]) for a,b in L), (1,2,3), factors_length_k=set())
            sage: E.factors_length_k(2)
            Traceback (most recent call last):
            ...
            NotImplementedError: can't compute factors of length k for this word
        """
        if self._factors_length_k is None:
            # We suppose here the factor is the empty word...
            assert self.is_empty(), "can't compute factor of length k for nonempty word"
            assert not k is None, "you must provide a value for k to compute them"
            self._factors_length_k = set(w for a,b in self._pairs for w in (a*b).factor_iterator(k))
        if not k is None and (not self._factors_length_k or
                next(iter(self._factors_length_k)).length() != k):
            raise NotImplementedError("can't compute factors of length k for this word")
        return self._factors_length_k

    def is_valid(self):
        r"""
        Return whether self is valid, i.e, each left and right extension is
        non empty.

        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.is_valid()
            True

        """
        if any(len(a)==0 or len(b)==0 for a,b in self._pairs):
            return False
        return True

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

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: sorted(E.left_word_extensions())
            [word: 12, word: 21, word: 22, word: 23, word: 31]

        """
        return set(a for a,b in self)
    def right_word_extensions(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: sorted(E.right_word_extensions())
            [word: 1, word: 2, word: 3]
        """
        return set(b for a,b in self)
    def left_valence(self, length=1):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.left_valence()
            3
            sage: E.left_valence(2)
            5
        """
        if length == 1:
            return len(self.left_extensions())
        L = self.left_word_extensions()
        if length == len(next(iter(L))):
            return len(L)
        else:
            raise NotImplementedError('can not compute left valence of '
                    'length(={}) for extensions {}'.format(length, L))

    def right_valence(self, length=1):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.right_valence()
            3
        """
        if length == 1:
            return len(self.right_extensions())
        L = self.right_word_extensions()
        if length == len(next(iter(L))):
            return len(L)
        else:
            raise NotImplementedError('can not compute right valence of '
                    'length(={}) for extensions {}'.format(length, L))
    def table(self):
        r"""
        return a table representation of self.

        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E
              E(w)   1   2   3
               21        X
               31        X
               12    X   X   X
               22    X
               23    X
             m(w)=0, neutral
        """
        lines = []
        L = sorted(self.left_word_extensions(), key=lambda w:w.reversal())
        R = sorted(self.right_word_extensions())
        for a in L:
            line = []
            for b in R:
                if (a,b) in self._pairs:
                    line.append('X')
                else:
                    line.append(' ')
            lines.append(line)
        Ew = "E(w)"
        t = table(rows=lines, header_row=R, header_column=[Ew]+L)
        t.options(header_column=False,header_row=False,align='center')
        return t

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

            sage: from slabbe import ExtensionType1to1
            sage: L = [(1,3), (2,3), (3,1), (3,2), (3,3)]
            sage: E = ExtensionType1to1(L, [1,2,3], ('a', 'b'))
            sage: E.chignons_multiplicity_tuple()
            ('a', 'b', 0)
        """
        return self._chignons + (self.multiplicity(),)

    def letters_before_and_after(self, factors):
        r"""
        Returns a pair of dict giving the words letters that goes before
        the left extensions and after the right extensions.

        INPUT:

        - ``factors`` -- factors of length k

        OUTPUT:

        pair of dict

        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:      2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: F = sorted(E.factors_length_k(2))
            sage: E.letters_before_and_after(F)
            ({word: 12: {word: 2, word: 3},
              word: 21: {word: 1, word: 2},
              word: 22: {word: 1, word: 2},
              word: 23: {word: 1, word: 2},
              word: 31: {word: 2}},
             {word: 1: {word: 2},
              word: 2: {word: 1, word: 2, word: 3},
              word: 3: {word: 1}})

        ::

            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: F = sorted(E.factors_length_k(3))
            sage: E.letters_before_and_after(F)
            ({word: 12: {word: 2, word: 3},
              word: 21: {word: 1, word: 2},
              word: 22: {word: 1},
              word: 23: {word: 1},
              word: 31: {word: 2}},
             {word: 1: {word: 21, word: 22, word: 23},
              word: 2: {word: 12, word: 21, word: 31},
              word: 3: {word: 12}})

        ::

            sage: data = [[(1, 1, 1), (3,)], [(1, 1, 1), (1,)], [(1, 1, 1), (2,)], [(1, 2),
            ....:   (1,)], [(2, 1), (1,)], [(1, 3), (1,)], [(3, 1), (2,)]]
            sage: E5 = ExtensionTypeLong(data, (1,2,3))
            sage: b21 = WordMorphism({1:[1],2:[2,1],3:[3]})
            sage: E51, = [E for E in E5.apply(b21) if E.factor().length()==1]
            sage: F = sorted(E51.factors_length_k(3))
            sage: F
            [word: 111, word: 112, word: 113, word: 121, word: 131, word: 211, word: 312]
            sage: before,after = E.letters_before_and_after(F)
            sage: before
            {word: 11: {word: 1, word: 2},
             word: 12: {word: 1, word: 3},
             word: 13: {word: 1},
             word: 21: {word: 1}}
            sage: after
            {word: 1: {word: 11, word: 12, word: 13, word: 21, word: 31},
             word: 2: {word: 11},
             word: 3: {word: 12}}

        """
        possible_before = defaultdict(set)
        for left in self.left_word_extensions():
            for f in factors:
                len_inter = min(f.length()-1, left.length())
                inter = left[:len_inter]
                if inter.is_suffix(f):
                    possible_before[left].add(f[:-len_inter])
        possible_after = defaultdict(set)
        for right in self.right_word_extensions():
            for f in factors:
                len_inter = min(f.length()-1, right.length())
                inter = f[:len_inter]
                if inter.is_suffix(right):
                    possible_after[right].add(f[len_inter:])
        return dict(possible_before), dict(possible_after)

    def apply(self, m, l=2, r=1, growth_limit=float('inf')):
        r"""
        The code works for Brun here because we take length 2 on the left
        and length 1 on the right.

        On utilise les facteurs de longueur 2 pour completer l'info qui
        peut manquer.

        TODO: bien corriger les facteurs de longueurs 2 de l'image!!!

        INPUT:

        - ``m`` - substitution
        - ``l`` - integer, length of left extension
        - ``r`` - integer, length of right extension
        - ``growth_limit`` -- integer (default: ``float('inf')``), the
          maximal growth in length of the bispecial extended images

        OUTPUT:

        list of Extension type of the bispecial images

        POSSIBLE BUG::

            sage: from slabbe import ExtensionType
            sage: b23 = WordMorphism({1:[1],2:[2,3],3:[3]})
            sage: b13 = WordMorphism({1:[1,3],2:[2],3:[3]})
            sage: b31 = WordMorphism({1:[1],2:[2],3:[3,1]})
            sage: e = ExtensionType.from_morphism(b23)
            sage: r = e.apply(b23)[0]
            sage: r.apply(b13)
            ()
            sage: r.apply(b31)
            ()

        Ce bug se corrige avec de plus grandes extensions a gauche et en
        considérant les facteurs de longueur 2::

            sage: from slabbe import ExtensionTypeLong
            sage: E = ExtensionTypeLong((([a],[b]) for a,b in e), (1,2,3))
            sage: R = E.apply(b23, l=1)[0]
            sage: R.apply(b13, l=1)
            (  E(w)   1   2   3
                1             X
                2             X
                3     X   X   X
             m(w)=0, ord.,)
            sage: R.apply(b31, l=1)
            (  E(w)   1   2   3
                1     X   X   X
                2             X
                3     X
             m(w)=0, neutral,   
               E(w)   1   2   3
                1     X   X   X
                3     X   X   X
             m(w)=2, strong)

        EXAMPLES:

        On imagine qu'on vient de faire b12::

            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:      2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E
              E(w)   1   2   3
               21        X
               31        X
               12    X   X   X
               22    X
               23    X
             m(w)=0, neutral
            sage: b12 = WordMorphism({1:[1,2],2:[2],3:[3]})
            sage: E.apply(b12)
            (  E(w)   1   2   3
                21        X
                31        X
                12        X
                22    X   X   X
                23    X
              m(w)=0, neutral,
               E(w)   1   2   3
                21        X
                31        X
                12    X   X   X
                22    X
              m(w)=0, ord.)

        ::

            sage: b21 = WordMorphism({1:[1],2:[2,1],3:[3]})
            sage: E.apply(b21)
            (  E(w)   1   2   3
                11        X
                21    X   X   X
                31        X
                12    X
                13    X
              m(w)=0, ord.,
               E(w)   1   2   3
                21        X
                12    X   X   X
                13        X
              m(w)=0, ord.)
            sage: b23 = WordMorphism({1:[1],2:[2,3],3:[3]})
            sage: E.apply(b23)
            (  E(w)   1   2   3
                31    X   X   X
                23    X
              m(w)=0, ord.,
               E(w)   1   2   3
                31        X
                12            X
                32            X
                23    X   X   X
                33    X
              m(w)=0, neutral,
               E(w)   1   2   3
                12    X   X   X
                32    X
                23    X
              m(w)=0, ord.)

        Not letter is missing::

            sage: data = [[(1, 1, 1), (3,)], [(1, 1, 1), (1,)], [(1, 1, 1), (2,)], [(1, 2),
            ....:   (1,)], [(2, 1), (1,)], [(1, 3), (1,)], [(3, 1), (2,)]]
            sage: E5 = ExtensionTypeLong(data, (1,2,3))
            sage: b21 = WordMorphism({1:[1],2:[2,1],3:[3]})
            sage: E51, = [E for E in E5.apply(b21) if E.factor().length()==1]
            sage: b21 = WordMorphism({1:[1],2:[2,1],3:[3]})
            sage: E51.apply(b21)
            (  E(w)   1   2   3
                11    X   X   X
                21    X
                12    X
                13        X
             m(w)=0, neutral,
               E(w)   1   2   3
                11    X   X   X
                21    X
                12    X
             m(w)=0, ord.)

        We check that 12 is a left extension of X because this can not be
        guessed only from the direct image of left extensions::

            sage: b21 = WordMorphism({1:[1],2:[2,1],3:[3]})
            sage: data = [((1, 1), (2,)), ((2, 1), (3,)), ((2, 1), (2,)),
            ....:         ((1, 2), (1,)), ((2, 1), (1,)), ((1, 3), (2,))]
            sage: F = [(1,1,1), (1,2,1), (1,1,3), (3,1,2), (2,1,1), (1,1,2), (1,3,1)]
            sage: F = [Word(f) for f in F]
            sage: E4_1 = ExtensionTypeLong(data, (1,2,3), factor=Word([1]),
            ....:                          factors_length_k=F, empty=False)
            sage: X,Y = E4_1.apply(b21)
            sage: X
              E(w)   1   2   3
               11    X   X   X
               21    X
               12    X
               13        X
            m(w)=0, neutral
        """
        F = self.factors_length_k(l+r)

        letters_before, letters_after = self.letters_before_and_after(F)
        #print(letters_before)
        #print(letters_after)
        word_before = defaultdict(Word)
        word_after = defaultdict(Word)
        for key,value in letters_before.items():
            word_before[key] = longest_common_suffix([m(v) for v in value])
        for key,value in letters_after.items():
            word_after[key] = longest_common_prefix([m(v) for v in value])
        word_before = dict(word_before)
        word_after = dict(word_after)
        #print(word_before)
        #print(word_after)

        extensions = defaultdict(list)
        for a,b in self:
            left = word_before[a] * m(a)
            right = m(b) * word_after[b]
            length_image_last_a = len(m(a[-1]))
            length_image_first_b = len(m(b[0]))
            for i in range(length_image_last_a+1):
                for j in range(length_image_first_b+1):
                    chignons = left[len(left)-i:], right[:j]
                    new_ext  = left[:len(left)-i][-l:], right[j:j+r]
                    extensions[chignons].append( new_ext )

        # The image of the factor may occur in other places ...
        # This is necessary when self is empty or is a letter ...
        # TODO: improvement, run this code only if necessary (when?)
        if len(self._factor) <= 1:
            chignons = Word(),Word()
            assert chignons in extensions
            for a,b in self:
                left = word_before[a] * m(a)
                right = m(b) * word_after[b]
                m_factor = m(self._factor)
                word = left * m_factor * right
                for f in word.factor_iterator(l+len(m_factor)+r):
                    if f[l:-r] == m_factor:
                        new_ext = f[:l], f[-r:]
                        extensions[chignons].append( new_ext )

        Fimage = set(w for f in F for w in m(f).factor_iterator(l+r))
        L = []
        for chignons, extension in extensions.items():
            if len(chignons[0]) + len(chignons[1]) > growth_limit:
                continue
            empty = self.is_empty() and all(len(c)==0 for c in self._chignons)
            factor = chignons[0] * m(self._factor) * chignons[1]
            e = ExtensionTypeLong(extension, alphabet=self._alphabet,
                    factor=factor, chignons=chignons, factors_length_k=Fimage,
                    empty=empty, repr_options=self._repr_options)
            if e.is_valid() and e.is_bispecial():
                L.append(e)
        return tuple(L)

    @cached_method
    def extension_type_1to1(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.extension_type_1to1()
              E(w)   1   2   3
               1         X
               2     X   X   X
               3     X
             m(w)=0, neutral

        """
        pairs = set((a[-1],b[0]) for a,b in self)
        return ExtensionType1to1(pairs, alphabet=self._alphabet,
                chignons=self._chignons)

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

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.cardinality()
            5

        """
        return self.extension_type_1to1().cardinality()
    def is_ordinaire(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:          2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.extension_type_1to1()
              E(w)   1   2   3
               1         X
               2     X   X   X
               3     X
             m(w)=0, neutral
            sage: E.is_ordinaire()
            False
        """
        return self.extension_type_1to1().is_ordinaire()

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

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.left_extensions()
            {1, 2, 3}

        """
        return self.extension_type_1to1().left_extensions()
        #return set(a[-1:] for a in self.left_word_extensions())
    def right_extensions(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.right_extensions()
            {1, 2, 3}
        """
        return self.extension_type_1to1().right_extensions()
        #return set(b[:1] for b in self.right_word_extensions())
    def palindromic_extensions(self):
        r"""
        EXAMPLES::

            sage: from slabbe import ExtensionTypeLong
            sage: L = [((2, 2), (1,)), ((2, 3), (1,)), ((2, 1), (2,)), ((1,
            ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((3, 1), (2,))]
            sage: E = ExtensionTypeLong(L, (1,2,3))
            sage: E.palindromic_extensions()
            {2}
        """
        return self.extension_type_1to1().palindromic_extensions()
######################################
# methods that should be in Sage
######################################
def longest_common_prefix(L):
    r"""
    Return the longest common prefix of a list of words.

    EXAMPLES::

        sage: from slabbe.bispecial_extension_type import longest_common_prefix
        sage: longest_common_prefix((Word('ab'), Word('abc'), Word('abd')))
        word: ab
    """
    common = L[0]
    for w in L:
        common = w.longest_common_prefix(common)
    return common
def longest_common_suffix(L):
    r"""
    Return the longest common suffix of a list of words.

    EXAMPLES::

        sage: from slabbe.bispecial_extension_type import longest_common_suffix
        sage: longest_common_suffix((Word('abc'), Word('bc'), Word('xabc')))
        word: bc
    """
    common = L[0]
    for w in L:
        common = w.longest_common_suffix(common)
    return common

def table_bispecial(word, k, nleft=1, nright=1):
    r"""
    Return the table of the first k bispecial factors of a word.

    INPUT:

    - ``word`` -- finite word
    - ``k`` -- integer

    OUTPUT:

        table

    EXAMPLES::

        sage: from slabbe.bispecial_extension_type import table_bispecial
        sage: w = words.FibonacciWord()
        sage: table_bispecial(w[:10000], 6)
          |w|   word                  m(w)   info   d^-(w)   d^+(w)
        +-----+---------------------+------+------+--------+--------+
          0                           0      ord.   2        2
          1     0                     0      ord.   2        2
          3     010                   0      ord.   2        2
          6     010010                0      ord.   2        2
          11    01001010010           0      ord.   2        2
          19    0100101001001010010   0      ord.   2        2

    ::

        sage: w = words.FibonacciWord()
        sage: table_bispecial(w[:10000], 6, nleft=2)
          |w|   word                  m(w)   info   d^-(w)   d_2^-(w)   d^+(w)
        +-----+---------------------+------+------+--------+----------+--------+
          0                           0      ord.   2        3          2
          1     0                     0      ord.   2        2          2
          3     010                   0      ord.   2        2          2
          6     010010                0      ord.   2        2          2
          11    01001010010           0      ord.   2        2          2
          19    0100101001001010010   0      ord.   2        2          2

    ::

        sage: w = words.ThueMorseWord()
        sage: table_bispecial(w[:10000], 11)
          |w|   word     m(w)   info     d^-(w)   d^+(w)
        +-----+--------+------+--------+--------+--------+
          0              1      strong   2        2
          1     0        0      ord.     2        2
          1     1        0      ord.     2        2
          2     01       1      strong   2        2
          2     10       1      strong   2        2
          3     010      -1     weak     2        2
          3     101      -1     weak     2        2
          4     0110     1      strong   2        2
          4     1001     1      strong   2        2
          6     011001   -1     weak     2        2
          6     100110   -1     weak     2        2
    """
    it = word.bispecial_factors_iterator()
    bispecials = [next(it) for _ in range(k)]
    rows = []
    for w in bispecials:
        ext = ExtensionType.from_factor(w, word, nleft=nleft, nright=nright)
        mw = ext.multiplicity()
        info = ext.information()
        left_valence = ext.left_valence()
        row = [w.length(), w, mw, info, left_valence]
        if nleft > 1:
            left_word_valence = ext.left_valence(nleft)
            row.append(left_word_valence)
        right_valence = ext.right_valence()
        row.append(right_valence)
        if nright > 1:
            right_word_valence = ext.right_valence(nright)
            row.append(right_word_valence)
        rows.append(row)
    rows.sort(key=lambda row:row[1])
    rows.sort(key=lambda row:row[0])
    header_row=['|w|', 'word', 'm(w)','info', 'd^-(w)']
    if nleft > 1:
        header_row.append('d_{}^-(w)'.format(nleft))
    header_row.append('d^+(w)')
    if nright > 1:
        header_row.append('d_{}^+(w)'.format(nright))
    return table(rows=rows, header_row=header_row)

def recursively_enumerated_set_to_digraph(R, max_depth=float('inf')):
    r"""
    Return the graph of the recursively enumerated set.

    TODO:

        Move this to sage.

    EXAMPLES::

        sage: from slabbe.bispecial_extension_type import recursively_enumerated_set_to_digraph
        sage: child = lambda i: [(i+3) % 10, (i+8)%10]
        sage: R = RecursivelyEnumeratedSet([0], child)
        sage: G = recursively_enumerated_set_to_digraph(R)
        sage: G
        Looped multi-digraph on 10 vertices
    """
    if hasattr(R, 'successors'):
        successors = R.successors
    elif hasattr(R, 'children'):
        successors = R.children
    else:
        raise TypeError("Can't find the successor function for input {}".format(R))
    it = R.breadth_first_search_iterator(max_depth=max_depth)
    E = [(u,v) for u in it for v in successors(u)]
    return DiGraph(E, format='list_of_edges', loops=True, multiedges=True)

######################################
# Set of extension type of the same age
######################################
def remove_extension_types_subsets(extensions):
    r"""
    Remove the extension types that are subset of another one associated to
    the same factor.

    INPUT:

    - ``extensions`` -- iterable for extension types

    EXAMPLES::

        sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
        sage: from slabbe.mult_cont_frac import Brun
        sage: S = Brun().substitutions()
        sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
        ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
        sage: E1 = ExtensionTypeLong(data, (1,2,3))
        sage: R = E1.rec_enum_set_under_sadic([132]*2+[123]*6, S)
        sage: A = [E for E,w,h in R.graded_component(8)]
        sage: A.sort(key=lambda a:(a.factor(), len(a.left_word_extensions())))
        sage: [a.factor() for a in A]
        [word: 22322322322322322322,
         word: 2322322322322322322,
         word: 2322322322322322322]
        sage: A[1].is_subset(A[2])
        True
        sage: from slabbe.bispecial_extension_type import remove_extension_types_subsets
        sage: B = remove_extension_types_subsets(A)
        sage: B
        [  E(w)   1   3
            32        X
            23    X
         m(w)=-1, weak,   
           E(w)   1   2   3
            21    X   X   X
            22            X
            32    X
         m(w)=1, strong]
        sage: [b.factor() for b in B]
        [word: 22322322322322322322,
         word: 2322322322322322322]
    """
    d = defaultdict(list)
    for E in extensions:
        w = E.factor()
        if not d[w]:
            d[w].append(E)
        elif any(E.is_subset(F) for F in d[w]):
            continue
        else:
            d[w] = [F for F in d[w] if not F.is_subset(E)]
            d[w].append(E)
    return [v for value in d.values() for v in value]

def rec_enum_set_under_language_joined_from_pairs(pairs, language,
        substitutions_dict, keep_equal_length=False, keep_unique=False,
        label='history', growth_limit=float('inf'), filter_fn=None):
    r"""
    Return the recursively enumerated set of extension type generated
    by a language of substitutions where the extension type of the same
    age and joined.

    INPUT:

    - ``pairs`` -- list of pairs of (extension type, previous substitution key)
    - ``language`` -- the language of substitutions
    - ``substitutions_dict`` - dict of substitutions
    - ``keep_equal_length`` -- (default: False) whether to keep images that
      have equal length
    - ``keep_unique`` -- (default: False) whether to keep a unique copy of
      equal extension types
    - ``label`` -- 'history' or 'previous' (default: ``'history'``),
      whether the vertices contain the whole history of the bispecial
      word or only the previous applied substitution
    - ``growth_limit`` -- integer (default: ``float('inf')``), the
      maximal growth in length of the bispecial extended images
    - ``filter_fn`` -- function (default: ``None``)

    EXAMPLES::

        sage: from slabbe.bispecial_extension_type import rec_enum_set_under_language_joined_from_pairs
        sage: from slabbe.bispecial_extension_type import ExtensionType
        sage: from slabbe.mult_cont_frac import Brun
        sage: from slabbe.language import languages
        sage: algo = Brun()
        sage: S = algo.substitutions()
        sage: L = languages.Brun()
        sage: v = algo.image((1,e,pi), 5)
        sage: prefix = algo.s_adic_word(v)[:1000]
        sage: E = ExtensionType.from_factor(prefix.parent()(), prefix, nleft=2)
        sage: pairs = [(E,123)]
        sage: rec_enum_set_under_language_joined_from_pairs(pairs, L, S)
        A recursively enumerated set (breadth first search)
        sage: rec_enum_set_under_language_joined_from_pairs(pairs, L, S, label='previous')
        A recursively enumerated set (breadth first search)

    ::

        sage: from slabbe.bispecial_extension_type import ExtensionTypeLong
        sage: from slabbe.mult_cont_frac import Brun
        sage: S = Brun().substitutions()
        sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
        ....:     2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
        sage: E1 = ExtensionTypeLong(data, (1,2,3))
        sage: from slabbe.language import languages
        sage: L = languages.Brun()
        sage: E = [E for E in E1.apply(S[123]) if E.factor().length() == 1][0]
        sage: pairs = [(E,123)]
        sage: rec_enum_set_under_language_joined_from_pairs(pairs, L, S, label='previous')
        A recursively enumerated set (breadth first search)

    ::

        sage: from slabbe.mult_cont_frac import Brun
        sage: algo = Brun()
        sage: S = algo.substitutions()
        sage: from slabbe.language import languages
        sage: LBrun = languages.Brun()
        sage: data = [((2, 1), (2,)), ((3, 1), (2,)), ((2, 2), (3,)), ((1,
        ....:    2), (1,)), ((1, 2), (2,)), ((1, 2), (3,)), ((2, 3), (1,))]
        sage: E1 = ExtensionTypeLong(data, (1,2,3))
        sage: pairs = [(E1, 312)] #, (E2, 312), (E3, 312), (E4, 321), (E5, 321)]
        sage: f = lambda S:any(len(ext.left_word_extensions())>2 for ext in S)
        sage: from slabbe.bispecial_extension_type import rec_enum_set_under_language_joined_from_pairs
        sage: R = rec_enum_set_under_language_joined_from_pairs(pairs,
        ....:    LBrun, S, keep_equal_length=False, label='previous', growth_limit=1, filter_fn=f)
        sage: R
        A recursively enumerated set (breadth first search)
        sage: from slabbe.bispecial_extension_type import recursively_enumerated_set_to_digraph
        sage: recursively_enumerated_set_to_digraph(R)    # long time (12s)
        Looped multi-digraph on 127 vertices

    Testing the keep_unique option (not a good example apparently?)::

        sage: from slabbe.mult_cont_frac import Brun
        sage: algo = Brun()
        sage: S = algo.substitutions()
        sage: from slabbe.language import languages
        sage: LBrun = languages.Brun()
        sage: data = [((1,1),(2,)),((2,1),(3,)),((2,1),(2,)),
        ....:         ((1,2),(1,)),((2,1),(1,)),((1,3),(2,))]
        sage: factors = [Word(w) for w in [(1,1,1),(1,2,1),(1,1,3),(3,1,2),(2,1,1),(1,1,2),(1,3,1)]]
        sage: E4_1 = ExtensionTypeLong(data, (1,2,3), factor=Word([1]), factors_length_k=factors)
        sage: pairs = [(E4_1, 321)]
        sage: f = lambda S:any(len(ext.left_word_extensions())>2 for ext in S)
        sage: R = rec_enum_set_under_language_joined_from_pairs(pairs,
        ....:    LBrun, S, keep_equal_length=False, 
        ....:    keep_unique=False, label='previous', growth_limit=1, filter_fn=f)
        sage: R.to_digraph()                 # long time (15s)
        Looped multi-digraph on 129 vertices

    ::

        sage: R = rec_enum_set_under_language_joined_from_pairs(pairs,
        ....:    LBrun, S, keep_equal_length=False, 
        ....:    keep_unique=True, label='previous', growth_limit=1, filter_fn=f)
        sage: R.to_digraph()                 # long time (15s)
        Looped multi-digraph on 129 vertices

    """
    # what can go before each letter
    before = defaultdict(list)
    for w in language.words_of_length_iterator(2): 
        before[w[1]].append(w[0])
    before = dict(before)

    def child(V):
        ExtIN,w = V
        rep = []
        if filter_fn and not filter_fn(ExtIN):
            return rep
        for a in before[w[0]]:
            ExtOUT = [Z for ext in ExtIN 
                        for Z in ext.apply(substitutions_dict[a], growth_limit=growth_limit)
                        if keep_equal_length or not len(ext.factor())==len(Z.factor())]
            ExtOUT = remove_extension_types_subsets(ExtOUT)
            if keep_unique:
                ExtOUT = list(set(ExtOUT))
            ExtOUT.sort(key=lambda ext:len(ext.factor()))
            ExtOUT = tuple(ExtOUT)
            if label == 'previous':
                rep.append((ExtOUT,(a,)))
            elif label == 'history':
                rep.append((ExtOUT,(a,)+w))
            else:
                raise ValueError('when label={}'.format(label))
        return rep
    roots = [((E,),(b,)) for E,b in pairs]

    from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet
    if label == 'history':
        #structure = 'forest'
        structure = None # because R.to_digraph() does not work when structure is forest...
    elif label == 'previous':
        structure = None
    else:
        raise ValueError('when label={}'.format(label))
    return RecursivelyEnumeratedSet(roots, child, structure=structure)

