U
    ZÊ¹jgS  ã                   @   sT   d dl mZ d dlmZ d dlmZ d dlmZ G dd„ deƒZG dd„ deƒZ	d	S )
é    ©Úisprime)ÚPermutationGroup)ÚDefaultPrinting)Ú
free_groupc                   @   s.   e Zd ZdZdZd	dd„Zdd„ Zdd„ ZdS )
ÚPolycyclicGroupTNc                 C   s.   || _ || _|| _|s$t| j ||ƒn|| _dS )a  

        Parameters
        ==========

        pc_sequence : list
            A sequence of elements whose classes generate the cyclic factor
            groups of pc_series.
        pc_series : list
            A subnormal sequence of subgroups where each factor group is cyclic.
        relative_order : list
            The orders of factor groups of pc_series.
        collector : Collector
            By default, it is None. Collector class provides the
            polycyclic presentation with various other functionalities.

        N)ÚpcgsÚ	pc_seriesÚrelative_orderÚ	CollectorÚ	collector)ÚselfZpc_sequencer	   r
   r   © r   ú\/var/www/html/bwave/python/venv/lib/python3.8/site-packages/sympy/combinatorics/pc_groups.pyÚ__init__   s    zPolycyclicGroup.__init__c                 C   s   t dd„ | jD ƒƒS )Nc                 s   s   | ]}t |ƒV  qd S ©Nr   )Ú.0Úorderr   r   r   Ú	<genexpr>$   s     z1PolycyclicGroup.is_prime_order.<locals>.<genexpr>)Úallr
   ©r   r   r   r   Úis_prime_order#   s    zPolycyclicGroup.is_prime_orderc                 C   s
   t | jƒS r   )Úlenr   r   r   r   r   Úlength&   s    zPolycyclicGroup.length)N)Ú__name__Ú
__module__Ú__qualname__Zis_groupZis_solvabler   r   r   r   r   r   r   r      s
   
r   c                   @   sz   e Zd ZdZddd„Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ ZdS )r   zœ
    References
    ==========

    .. [1] Holt, D., Eick, B., O'Brien, E.
           "Handbook of Computational Group Theory"
           Section 8.1.3
    Nc                 C   sX   || _ || _|| _|s,td t|ƒ¡ƒd n|| _dd„ t| jjƒD ƒ| _|  	¡ | _
dS )a  

        Most of the parameters for the Collector class are the same as for PolycyclicGroup.
        Others are described below.

        Parameters
        ==========

        free_group_ : tuple
            free_group_ provides the mapping of polycyclic generating
            sequence with the free group elements.
        pc_presentation : dict
            Provides the presentation of polycyclic groups with the
            help of power and conjugate relators.

        See Also
        ========

        PolycyclicGroup

        zx:{}r   c                 S   s   i | ]\}}||“qS r   r   )r   ÚiÚsr   r   r   Ú
<dictcomp>O   s      z&Collector.__init__.<locals>.<dictcomp>N)r   r	   r
   r   Úformatr   Ú	enumerateÚsymbolsÚindexÚpc_relatorsÚpc_presentation)r   r   r	   r
   Zfree_group_r%   r   r   r   r   5   s     zCollector.__init__c                 C   sØ   |sdS |j }| j}| j}tt|ƒƒD ]F}|| \}}|||  r&|dk s^||||  d kr&||ff  S q&tt|ƒd ƒD ]T}|| \}}||d  \}}	|| || kr~|	dkrºdnd}
||f||
ff  S q~dS )a¹  
        Returns the minimal uncollected subwords.

        Explanation
        ===========

        A word ``v`` defined on generators in ``X`` is a minimal
        uncollected subword of the word ``w`` if ``v`` is a subword
        of ``w`` and it has one of the following form

        * `v = {x_{i+1}}^{a_j}x_i`

        * `v = {x_{i+1}}^{a_j}{x_i}^{-1}`

        * `v = {x_i}^{a_j}`

        for `a_j` not in `\{1, \ldots, s-1\}`. Where, ``s`` is the power
        exponent of the corresponding generator.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> from sympy.combinatorics import free_group
        >>> G = SymmetricGroup(4)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> F, x1, x2 = free_group("x1, x2")
        >>> word = x2**2*x1**7
        >>> collector.minimal_uncollected_subword(word)
        ((x2, 2),)

        Nr   é   éÿÿÿÿ)Ú
array_formr
   r#   Úranger   )r   ÚwordÚarrayÚrer#   r   Ús1Úe1Ús2Úe2Úer   r   r   Úminimal_uncollected_subwordR   s     #(z%Collector.minimal_uncollected_subwordc                 C   sD   i }i }| j  ¡ D ](\}}t|jƒdkr2|||< q|||< q||fS )a±  
        Separates the given relators of pc presentation in power and
        conjugate relations.

        Returns
        =======

        (power_rel, conj_rel)
            Separates pc presentation into power and conjugate relations.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> G = SymmetricGroup(3)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> power_rel, conj_rel = collector.relations()
        >>> power_rel
        {x0**2: (), x1**3: ()}
        >>> conj_rel
        {x0**-1*x1*x0: x1**2}

        See Also
        ========

        pc_relators

        r&   )r%   Úitemsr   r(   )r   Zpower_relatorsZconjugate_relatorsÚkeyÚvaluer   r   r   Ú	relationsŒ   s    

zCollector.relationsc                 C   sv   d}d}t t|ƒt|ƒ d ƒD ]0}| ||t|ƒ ¡|kr |}|t|ƒ } qRq ||  krfdkrnn ndS ||fS )a  
        Returns the start and ending index of a given
        subword in a word.

        Parameters
        ==========

        word : FreeGroupElement
            word defined on free group elements for a
            polycyclic group.
        w : FreeGroupElement
            subword of a given word, whose starting and
            ending index to be computed.

        Returns
        =======

        (i, j)
            A tuple containing starting and ending index of ``w``
            in the given word.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> from sympy.combinatorics import free_group
        >>> G = SymmetricGroup(4)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> F, x1, x2 = free_group("x1, x2")
        >>> word = x2**2*x1**7
        >>> w = x2**2*x1
        >>> collector.subword_index(word, w)
        (0, 3)
        >>> w = x1**7
        >>> collector.subword_index(word, w)
        (2, 9)

        r'   r&   )r'   r'   )r)   r   Zsubword)r   r*   ÚwÚlowÚhighr   r   r   r   Úsubword_index³   s    (zCollector.subword_indexc                 C   sJ   |j }|d d }|d d }|df|df|dff}| j |¡}| j| S )a  
        Return a conjugate relation.

        Explanation
        ===========

        Given a word formed by two free group elements, the
        corresponding conjugate relation with those free
        group elements is formed and mapped with the collected
        word in the polycyclic presentation.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> from sympy.combinatorics import free_group
        >>> G = SymmetricGroup(3)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> F, x0, x1 = free_group("x0, x1")
        >>> w = x1*x0
        >>> collector.map_relation(w)
        x1**2

        See Also
        ========

        pc_presentation

        r   r&   r'   )r(   r   Údtyper%   )r   r7   r+   r-   r/   r4   r   r   r   Úmap_relationæ   s    zCollector.map_relationc                 C   s
  | j }|  |¡}|s�q|  || |¡¡\}}|dkr8q|d \}}t|ƒdk�r| j| j|  }|| }	||	|  }
|d d |ff}| |¡}| j| rÜ| j| j}|d \}}|d d |
f||	| ff}| |¡}n,|
dk�r|d d |
ff}| |¡}nd}| 	| |¡|¡}t|ƒdk�r�|d d dk�r�|d \}}|dff}| |¡}|  
| |¡¡}|||  }| |¡}| |||¡}qt|ƒdkr|d d dk r|d \}}|dff}| |¡}|  
| |¡¡}|d ||  }| |¡}| |||¡}q|S )a„  
        Return the collected form of a word.

        Explanation
        ===========

        A word ``w`` is called collected, if `w = {x_{i_1}}^{a_1} * \ldots *
        {x_{i_r}}^{a_r}` with `i_1 < i_2< \ldots < i_r` and `a_j` is in
        `\{1, \ldots, {s_j}-1\}`.

        Otherwise w is uncollected.

        Parameters
        ==========

        word : FreeGroupElement
            An uncollected word.

        Returns
        =======

        word
            A collected word of form `w = {x_{i_1}}^{a_1}, \ldots,
            {x_{i_r}}^{a_r}` with `i_1, i_2, \ldots, i_r` and `a_j \in
            \{1, \ldots, {s_j}-1\}`.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> from sympy.combinatorics.perm_groups import PermutationGroup
        >>> from sympy.combinatorics import free_group
        >>> G = SymmetricGroup(4)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3")
        >>> word = x3*x2*x1*x0
        >>> collected_word = collector.collected_word(word)
        >>> free_to_perm = {}
        >>> free_group = collector.free_group
        >>> for sym, gen in zip(free_group.symbols, collector.pcgs):
        ...     free_to_perm[sym] = gen
        >>> G1 = PermutationGroup()
        >>> for w in word:
        ...     sym = w[0]
        ...     perm = free_to_perm[sym]
        ...     G1 = PermutationGroup([perm] + G1.generators)
        >>> G2 = PermutationGroup()
        >>> for w in collected_word:
        ...     sym = w[0]
        ...     perm = free_to_perm[sym]
        ...     G2 = PermutationGroup([perm] + G2.generators)

        The two are not identical, but they are equivalent:

        >>> G1.equals(G2), G1 == G2
        (True, False)

        See Also
        ========

        minimal_uncollected_subword

        r'   r   r&   Né   )r   r2   r:   r;   r   r
   r#   r%   r(   Zeliminate_wordr<   Zsubstituted_word)r   r*   r   r7   r8   r9   r-   r.   r,   ÚqÚrr4   ZpresentationÚsymÚexpZword_r/   r0   r   r   r   Úcollected_word  sR    A



 





zCollector.collected_wordc                 C   sÎ  | j }| j}i }i }| j}t||jƒD ] \}}|d ||d < |||< q&|ddd… }| jddd… }|ddd… }g }	t|ƒD �]F\}
}||
 }|| | }||
 }|j|| dd�}| ¡  |j	}|D ]}|||  }qÊ|  
|¡}|rî|nd||< || _|	 |¡ t|	ƒdkr€|	t|	ƒd  }|| }tt|	ƒd ƒD ]Œ}||	|  }|d | | }|d |	|  | }|j|dd�}| ¡  |j	}|D ]}|||  }�qŽ|  
|¡}|�r¶|nd||< || _�q:q€|S )aM  
        Return the polycyclic presentation.

        Explanation
        ===========

        There are two types of relations used in polycyclic
        presentation.

        * Power relations : Power relators are of the form `x_i^{re_i}`,
          where `i \in \{0, \ldots, \mathrm{len(pcgs)}\}`, ``x`` represents polycyclic
          generator and ``re`` is the corresponding relative order.

        * Conjugate relations : Conjugate relators are of the form `x_j^-1x_ix_j`,
          where `j < i \in \{0, \ldots, \mathrm{len(pcgs)}\}`.

        Returns
        =======

        A dictionary with power and conjugate relations as key and
        their collected form as corresponding values.

        Notes
        =====

        Identity Permutation is mapped with empty ``()``.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> from sympy.combinatorics.permutations import Permutation
        >>> S = SymmetricGroup(49).sylow_subgroup(7)
        >>> der = S.derived_series()
        >>> G = der[len(der)-2]
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> pcgs = PcGroup.pcgs
        >>> len(pcgs)
        6
        >>> free_group = collector.free_group
        >>> pc_resentation = collector.pc_presentation
        >>> free_to_perm = {}
        >>> for s, g in zip(free_group.symbols, pcgs):
        ...     free_to_perm[s] = g

        >>> for k, v in pc_resentation.items():
        ...     k_array = k.array_form
        ...     if v != ():
        ...        v_array = v.array_form
        ...     lhs = Permutation()
        ...     for gen in k_array:
        ...         s = gen[0]
        ...         e = gen[1]
        ...         lhs = lhs*free_to_perm[s]**e
        ...     if v == ():
        ...         assert lhs.is_identity
        ...         continue
        ...     rhs = Permutation()
        ...     for gen in v_array:
        ...         s = gen[0]
        ...         e = gen[1]
        ...         rhs = rhs*free_to_perm[s]**e
        ...     assert lhs == rhs

        r'   NT©Úoriginalr   r&   )r   r
   r   ÚzipÚ
generatorsr	   r!   Úgenerator_productÚreverseÚidentityrB   r%   Úappendr   r)   )r   r   Z	rel_orderr$   Úperm_to_freer   Úgenr   ZseriesZcollected_gensr   r,   ZrelationÚGÚlr*   ÚgZconjZ
conjugatorÚjZ
conjugatedÚgensr   r   r   r$   ‚  sR    C



zCollector.pc_relatorsc                 C   sÔ   | j }tƒ }| jD ]}t|g|j ƒ}q|j|dd�}| ¡  i }t|j| jƒD ] \}}|d ||d < |||< qP|j}|D ]}|||  }q||  |¡}	| j	}
dgt
|ƒ }|	j}	|	D ]}|d ||
|d  < q¶|S )aJ  
        Return the exponent vector of length equal to the
        length of polycyclic generating sequence.

        Explanation
        ===========

        For a given generator/element ``g`` of the polycyclic group,
        it can be represented as `g = {x_1}^{e_1}, \ldots, {x_n}^{e_n}`,
        where `x_i` represents polycyclic generators and ``n`` is
        the number of generators in the free_group equal to the length
        of pcgs.

        Parameters
        ==========

        element : Permutation
            Generator of a polycyclic group.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> from sympy.combinatorics.permutations import Permutation
        >>> G = SymmetricGroup(4)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> pcgs = PcGroup.pcgs
        >>> collector.exponent_vector(G[0])
        [1, 0, 0, 0]
        >>> exp = collector.exponent_vector(G[1])
        >>> g = Permutation()
        >>> for i in range(len(exp)):
        ...     g = g*pcgs[i]**exp[i] if exp[i] else g
        >>> assert g == G[1]

        References
        ==========

        .. [1] Holt, D., Eick, B., O'Brien, E.
               "Handbook of Computational Group Theory"
               Section 8.1.1, Definition 8.4

        TrC   r'   r   r&   )r   r   r   rF   rG   rH   rE   rI   rB   r#   r   r(   )r   Úelementr   rM   rO   rQ   rK   r@   r7   r*   r#   Ú
exp_vectorÚtr   r   r   Úexponent_vectorû  s(    -


zCollector.exponent_vectorc                 C   s,   |   |¡}tdd„ t|ƒD ƒt| jƒd ƒS )a  
        Return the depth of a given element.

        Explanation
        ===========

        The depth of a given element ``g`` is defined by
        `\mathrm{dep}[g] = i` if `e_1 = e_2 = \ldots = e_{i-1} = 0`
        and `e_i != 0`, where ``e`` represents the exponent-vector.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> G = SymmetricGroup(3)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> collector.depth(G[0])
        2
        >>> collector.depth(G[1])
        1

        References
        ==========

        .. [1] Holt, D., Eick, B., O'Brien, E.
               "Handbook of Computational Group Theory"
               Section 8.1.1, Definition 8.5

        c                 s   s   | ]\}}|r|d  V  qdS )r&   Nr   )r   r   Úxr   r   r   r   `  s      z"Collector.depth.<locals>.<genexpr>r&   )rU   Únextr!   r   r   )r   rR   rS   r   r   r   Údepth@  s    
zCollector.depthc                 C   s6   |   |¡}|  |¡}|t| jƒd kr2||d  S dS )a  
        Return the leading non-zero exponent.

        Explanation
        ===========

        The leading exponent for a given element `g` is defined
        by `\mathrm{leading\_exponent}[g]` `= e_i`, if `\mathrm{depth}[g] = i`.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> G = SymmetricGroup(3)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> collector.leading_exponent(G[1])
        1

        r&   N)rU   rX   r   r   )r   rR   rS   rX   r   r   r   Úleading_exponentb  s
    

zCollector.leading_exponentc                 C   s€   |}|   |¡}|t| jƒk r|||d  dkr|||d  }|  |¡|  |¡d  }|| j|d   }||  | }|   |¡}q|S )Nr&   r'   )rX   r   r   rY   r
   )r   ÚzrO   ÚhÚdÚkr1   r   r   r   Ú_sift}  s    
zCollector._siftc                 C   s–   dgt | jƒ }|}|r„| d¡}|  ||¡}|  |¡}|t | jƒk r|D ]*}|dkrJ| |d |d  | | ¡ qJ|||d < qdd„ |D ƒ}|S )a8  

        Parameters
        ==========

        gens : list
            A list of generators on which polycyclic subgroup
            is to be defined.

        Examples
        ========

        >>> from sympy.combinatorics.named_groups import SymmetricGroup
        >>> S = SymmetricGroup(8)
        >>> G = S.sylow_subgroup(2)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> gens = [G[0], G[1]]
        >>> ipcgs = collector.induced_pcgs(gens)
        >>> [gen.order() for gen in ipcgs]
        [2, 2, 2]
        >>> G = S.sylow_subgroup(3)
        >>> PcGroup = G.polycyclic_group()
        >>> collector = PcGroup.collector
        >>> gens = [G[0], G[1]]
        >>> ipcgs = collector.induced_pcgs(gens)
        >>> [gen.order() for gen in ipcgs]
        [3]

        r&   r   r'   c                 S   s   g | ]}|d kr|‘qS )r&   r   )r   rL   r   r   r   Ú
<listcomp>²  s      z*Collector.induced_pcgs.<locals>.<listcomp>)r   r   Úpopr^   rX   rJ   )r   rQ   rZ   rM   rO   r[   r\   rL   r   r   r   Úinduced_pcgsˆ  s    

 zCollector.induced_pcgsc           	      C   s”   dgt |ƒ }|}|  |¡}t|ƒD ]^\}}|  |¡|kr$|  |¡|  |¡ }|| j|d   }||  | }|||< |  |¡}q,q$|dkr�|S dS )z>
        Return the exponent vector for induced pcgs.
        r   r&   F)r   rX   r!   rY   r
   )	r   ZipcgsrO   r1   r[   r\   r   rL   Úfr   r   r   Úconstructive_membership_testµ  s    
z&Collector.constructive_membership_test)NN)r   r   r   Ú__doc__r   r2   r6   r:   r<   rB   r$   rU   rX   rY   r^   ra   rc   r   r   r   r   r   *   s   	
:'3'uyE"-r   N)
Zsympy.ntheory.primetestr   Zsympy.combinatorics.perm_groupsr   Zsympy.printing.defaultsr   Zsympy.combinatorics.free_groupsr   r   r   r   r   r   r   Ú<module>   s
   #