o
    8VaQ                     @   s   d dl mZmZmZ d dlmZmZ d dlmZ d dl	m
Z
 d dlmZ d dlmZmZ d dlmZmZ d dlmZ G d	d
 d
eZG dd deZdddZdd Zdd Zdd Zdd ZdS )    )BasicDictsympify)as_intdefault_sort_key)_sympifybell)zeros)	FiniteSetUnion)flattengroup)defaultdictc                   @   sz   e Zd ZdZdZdZdd ZdddZedd Z	d	d
 Z
dd Zdd Zdd Zedd Zedd Zedd ZdS )	Partitionz
    This class represents an abstract partition.

    A partition is a set of disjoint sets whose union equals a given set.

    See Also
    ========

    sympy.utilities.iterables.partitions,
    sympy.utilities.iterables.multiset_partitions
    Nc                 G   s   g }d}|D ] }t |trt|}t|t|k rd} n
|}|t| qtdd |D s4tdt| }|sGt|t	dd |D k rKtdt
j| g|R  }t||_t||_|S )al  
        Generates a new partition object.

        This method also verifies if the arguments passed are
        valid and raises a ValueError if they are not.

        Examples
        ========

        Creating Partition from Python lists:

        >>> from sympy.combinatorics.partitions import Partition
        >>> a = Partition([1, 2], [3])
        >>> a
        Partition({3}, {1, 2})
        >>> a.partition
        [[1, 2], [3]]
        >>> len(a)
        2
        >>> a.members
        (1, 2, 3)

        Creating Partition from Python sets:

        >>> Partition({1, 2, 3}, {4, 5})
        Partition({4, 5}, {1, 2, 3})

        Creating Partition from SymPy finite sets:

        >>> from sympy.sets.sets import FiniteSet
        >>> a = FiniteSet(1, 2, 3)
        >>> b = FiniteSet(4, 5)
        >>> Partition(a, b)
        Partition({4, 5}, {1, 2, 3})
        FTc                 s   s    | ]}t |tV  qd S N)
isinstancer   ).0part r   @/usr/lib/python3/dist-packages/sympy/combinatorics/partitions.py	<genexpr>K   s    z$Partition.__new__.<locals>.<genexpr>z@Each argument to Partition should be a list, set, or a FiniteSetc                 s   s    | ]}t |V  qd S r   )len)r   argr   r   r   r   R       z'Partition contained duplicate elements.)r   listsetr   appendr   all
ValueErrorr   sumr   __new__tuplememberssize)cls	partitionargsZdupsr   Zas_setUobjr   r   r   r!      s*   $


zPartition.__new__c                    sB    du r| j }ntt| j  fddd}ttt| j|| jfS )a  Return a canonical key that can be used for sorting.

        Ordering is based on the size and sorted elements of the partition
        and ties are broken with the rank.

        Examples
        ========

        >>> from sympy.utilities.iterables import default_sort_key
        >>> from sympy.combinatorics.partitions import Partition
        >>> from sympy.abc import x
        >>> a = Partition([1, 2])
        >>> b = Partition([3, 4])
        >>> c = Partition([1, x])
        >>> d = Partition(list(range(4)))
        >>> l = [d, b, a + 1, a, c]
        >>> l.sort(key=default_sort_key); l
        [Partition({1, 2}), Partition({1}, {2}), Partition({1, x}), Partition({3, 4}), Partition({0, 1, 2, 3})]
        Nc                    s
   t |  S r   )r   )worderr   r   <lambda>r   s   
 z$Partition.sort_key.<locals>.<lambda>key)r#   r"   sortedmapr   r$   rank)selfr,   r#   r   r+   r   sort_keyZ   s   
zPartition.sort_keyc                 C   s&   | j du rtdd | jD | _ | j S )zReturn partition as a sorted list of lists.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> Partition([1], [2, 3]).partition
        [[1], [2, 3]]
        Nc                 S   s   g | ]}t |td qS )r.   )r0   r   r   pr   r   r   
<listcomp>   s    z'Partition.partition.<locals>.<listcomp>)
_partitionr0   r'   r3   r   r   r   r&   u   s
   

zPartition.partitionc                 C   s6   t |}| j| }t|t| j | j}t|| jS )at  
        Return permutation whose rank is ``other`` greater than current rank,
        (mod the maximum rank for the set).

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> a = Partition([1, 2], [3])
        >>> a.rank
        1
        >>> (a + 1).rank
        2
        >>> (a + 100).rank
        1
        )r   r2   
RGS_unrankRGS_enumr$   r   from_rgsr#   )r3   otheroffsetresultr   r   r   __add__   s   
zPartition.__add__c                 C   s   |  | S )aq  
        Return permutation whose rank is ``other`` less than current rank,
        (mod the maximum rank for the set).

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> a = Partition([1, 2], [3])
        >>> a.rank
        1
        >>> (a - 1).rank
        0
        >>> (a - 100).rank
        1
        )r@   r3   r=   r   r   r   __sub__   s   zPartition.__sub__c                 C   s   |   t|  kS )a  
        Checks if a partition is less than or equal to
        the other based on rank.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> a = Partition([1, 2], [3, 4, 5])
        >>> b = Partition([1], [2, 3], [4], [5])
        >>> a.rank, b.rank
        (9, 34)
        >>> a <= a
        True
        >>> a <= b
        True
        r4   r   rA   r   r   r   __le__   s   zPartition.__le__c                 C   s   |   t|  k S )aL  
        Checks if a partition is less than the other.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> a = Partition([1, 2], [3, 4, 5])
        >>> b = Partition([1], [2, 3], [4], [5])
        >>> a.rank, b.rank
        (9, 34)
        >>> a < b
        True
        rC   rA   r   r   r   __lt__   s   zPartition.__lt__c                 C   s"   | j dur| j S t| j| _ | j S )z
        Gets the rank of a partition.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> a = Partition([1, 2], [3], [4, 5])
        >>> a.rank
        13
        N)_rankRGS_rankRGSr9   r   r   r   r2      s   
zPartition.rankc                    sV   i  | j }t|D ]\}}|D ]}| |< qq	t fddtdd |D tdD S )a  
        Returns the "restricted growth string" of the partition.

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

        The RGS is returned as a list of indices, L, where L[i] indicates
        the block in which element i appears. For example, in a partition
        of 3 elements (a, b, c) into 2 blocks ([c], [a, b]) the RGS is
        [1, 1, 0]: "a" is in block 1, "b" is in block 1 and "c" is in block 0.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> a = Partition([1, 2], [3], [4, 5])
        >>> a.members
        (1, 2, 3, 4, 5)
        >>> a.RGS
        (0, 0, 1, 2, 2)
        >>> a + 1
        Partition({3}, {4}, {5}, {1, 2})
        >>> _.RGS
        (0, 0, 1, 2, 3)
        c                    s   g | ]} | qS r   r   r   irgsr   r   r7         z!Partition.RGS.<locals>.<listcomp>c                 S   s   g | ]	}|D ]}|qqS r   r   )r   r6   rJ   r   r   r   r7         r.   )r&   	enumerater"   r0   r   )r3   r&   rJ   r   jr   rK   r   rH      s   
zPartition.RGSc                 C   s   t |t |krtdt|d }dd t|D }d}|D ]}|| ||  |d7 }qtdd |D s<tdt| S )	aM  
        Creates a set partition from a restricted growth string.

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

        The indices given in rgs are assumed to be the index
        of the element as given in elements *as provided* (the
        elements are not sorted by this routine). Block numbering
        starts from 0. If any block was not referenced in ``rgs``
        an error will be raised.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import Partition
        >>> Partition.from_rgs([0, 1, 2, 0, 1], list('abcde'))
        Partition({c}, {a, d}, {b, e})
        >>> Partition.from_rgs([0, 1, 2, 0, 1], list('cbead'))
        Partition({e}, {a, c}, {b, d})
        >>> a = Partition([1, 4], [2], [3, 5])
        >>> Partition.from_rgs(a.RGS, a.members)
        Partition({2}, {1, 4}, {3, 5})
        z#mismatch in rgs and element lengths   c                 S   s   g | ]}g qS r   r   rI   r   r   r   r7   '  s    z&Partition.from_rgs.<locals>.<listcomp>r   c                 s   s    | ]}|V  qd S r   r   r5   r   r   r   r   ,  s    z%Partition.from_rgs.<locals>.<genexpr>z(some blocks of the partition were empty.)r   r   maxranger   r   r   )r3   rL   elementsZmax_elemr&   rP   rJ   r   r   r   r<   
  s   
zPartition.from_rgsr   )__name__
__module____qualname____doc__rF   r8   r!   r4   propertyr&   r@   rB   rD   rE   r2   rH   classmethodr<   r   r   r   r   r      s$    
>


"r   c                   @   sh   e Zd ZdZdZdZdddZdd Zdd Zd	d
 Z	e
dd Zdd Zdd ZdddZdd ZdS )IntegerPartitionaS  
    This class represents an integer partition.

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

    In number theory and combinatorics, a partition of a positive integer,
    ``n``, also called an integer partition, is a way of writing ``n`` as a
    list of positive integers that sum to n. Two partitions that differ only
    in the order of summands are considered to be the same partition; if order
    matters then the partitions are referred to as compositions. For example,
    4 has five partitions: [4], [3, 1], [2, 2], [2, 1, 1], and [1, 1, 1, 1];
    the compositions [1, 2, 1] and [1, 1, 2] are the same as partition
    [2, 1, 1].

    See Also
    ========

    sympy.utilities.iterables.partitions,
    sympy.utilities.iterables.multiset_partitions

    References
    ==========

    https://en.wikipedia.org/wiki/Partition_%28number_theory%29
    Nc                 C   s  |dur	||}}t |ttfr:g }tt| ddD ]\}}|s#qt|t|}}||g|  qt|}nttt	t|dd}d}|du rRt
|}d}nt|}|sdt
||krdtd| tdd |D rqtdt| ||}t||_||_|S )	a  
        Generates a new IntegerPartition object from a list or dictionary.

        Explantion
        ==========

        The partition can be given as a list of positive integers or a
        dictionary of (integer, multiplicity) items. If the partition is
        preceded by an integer an error will be raised if the partition
        does not sum to that given integer.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> a = IntegerPartition([5, 4, 3, 1, 1])
        >>> a
        IntegerPartition(14, (5, 4, 3, 1, 1))
        >>> print(a)
        [5, 4, 3, 1, 1]
        >>> IntegerPartition({1:3, 2:1})
        IntegerPartition(5, (2, 1, 1, 1))

        If the value that the partition should sum to is given first, a check
        will be made to see n error will be raised if there is a discrepancy:

        >>> IntegerPartition(10, [5, 4, 3, 1])
        Traceback (most recent call last):
        ...
        ValueError: The partition is not valid

        NTreverseFzPartition did not add to %sc                 s   s    | ]}|d k V  qdS )rQ   Nr   rI   r   r   r   r     r   z+IntegerPartition.__new__.<locals>.<genexpr>z-All integer summands must be greater than one)r   dictr   r0   r   itemsr   extendr"   r1   r    r   anyr   r!   r&   integer)r%   r&   rb   _kvZsum_okr)   r   r   r   r!   P  s0   !


zIntegerPartition.__new__c                 C   s  t t}||   | j}|dgkrt| jdiS |d dkrB||d   d8  < |d dkr5d|d< nGd ||d d < |d< n:||d   d8  < |d |d  }|d }d|d< |r||d8 }|| dkrz||  || 7  < ||| | 8 }|s^t| j|S )a  Return the previous partition of the integer, n, in lexical order,
        wrapping around to [1, ..., 1] if the partition is [n].

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> p = IntegerPartition([4])
        >>> print(p.prev_lex())
        [3, 1]
        >>> p.partition > p.prev_lex().partition
        True
        rQ      r   )r   intupdateas_dict_keysr[   rb   )r3   dkeysleftnewr   r   r   prev_lex  s*   

zIntegerPartition.prev_lexc                 C   s  t t}||   | j}|d }|| jkr!|  | j|d< n|dkr[|| dkr>||d   d7  < ||  d8  < n|d }||d   d7  < || d | |d< d||< ne|| dkrt|dkr{|  d||d < | j| d |d< nE|d }||  d7  < || | | |d< d||< n*|d }|d }||  d7  < || | || |  | }d ||< ||< ||d< t| j|S )a  Return the next partition of the integer, n, in lexical order,
        wrapping around to [n] if the partition is [1, ..., 1].

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> p = IntegerPartition([3, 1])
        >>> print(p.next_lex())
        [4]
        >>> p.partition < p.next_lex().partition
        True
        rf   rQ   rg   rh   r   )	r   ri   rj   rk   rl   rb   clearr   r[   )r3   rm   r/   abZa1Zb1Zneedr   r   r   next_lex  s>   


zIntegerPartition.next_lexc                 C   s8   | j du rt| jdd}dd |D | _t|| _ | j S )a[  Return the partition as a dictionary whose keys are the
        partition integers and the values are the multiplicity of that
        integer.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> IntegerPartition([1]*3 + [2] + [3]*4).as_dict()
        {1: 3, 2: 1, 3: 4}
        NF)Zmultiplec                 S   s   g | ]}|d  qS )r   r   )r   gr   r   r   r7     rM   z,IntegerPartition.as_dict.<locals>.<listcomp>)_dictr   r&   rl   r^   )r3   groupsr   r   r   rk     s
   

zIntegerPartition.as_dictc                 C   sn   d}t | jdg }|d }dg| }|dkr5||| kr-|||d < |d8 }||| ks|d7 }|dks|S )a  
        Computes the conjugate partition of itself.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> a = IntegerPartition([6, 3, 3, 2, 1])
        >>> a.conjugate
        [5, 4, 3, 1, 1, 1]
        rQ   r   )r   r&   )r3   rP   Ztemp_arrrd   rt   r   r   r   	conjugate  s   
zIntegerPartition.conjugatec                 C   s   t t| jt t|jk S )a  Return True if self is less than other when the partition
        is listed from smallest to biggest.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> a = IntegerPartition([3, 1])
        >>> a < a
        False
        >>> b = a.next_lex()
        >>> a < b
        True
        >>> a == b
        False
        r   reversedr&   rA   r   r   r   rE     s   zIntegerPartition.__lt__c                 C   s   t t| jt t|jkS )a   Return True if self is less than other when the partition
        is listed from smallest to biggest.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> a = IntegerPartition([4])
        >>> a <= a
        True
        rz   rA   r   r   r   rD   "  s   zIntegerPartition.__le__#c                    s   d  fdd| jD S )a  
        Prints the ferrer diagram of a partition.

        Examples
        ========

        >>> from sympy.combinatorics.partitions import IntegerPartition
        >>> print(IntegerPartition([1, 1, 5]).as_ferrers())
        #####
        #
        #
        
c                    s   g | ]} | qS r   r   rI   charr   r   r7   =  rM   z/IntegerPartition.as_ferrers.<locals>.<listcomp>)joinr&   )r3   r   r   r~   r   
as_ferrers0  s   zIntegerPartition.as_ferrersc                 C   s   t t| jS r   )strr   r&   r9   r   r   r   __str__?  s   zIntegerPartition.__str__r   )r|   )rU   rV   rW   rX   rw   rl   r!   rq   ru   rk   rY   ry   rE   rD   r   r   r   r   r   r   r[   1  s    
>%2

r[   Nc                 C   s   ddl m} t| } | dk rtd||}g }| dkr9|d| }|d| | }|||f | || 8 } | dks|jdd tdd |D }|S )	a  
    Generates a random integer partition summing to ``n`` as a list
    of reverse-sorted integers.

    Examples
    ========

    >>> from sympy.combinatorics.partitions import random_integer_partition

    For the following, a seed is given so a known value can be shown; in
    practice, the seed would not be given.

    >>> random_integer_partition(100, seed=[1, 1, 12, 1, 2, 1, 85, 1])
    [85, 12, 2, 1]
    >>> random_integer_partition(10, seed=[1, 2, 3, 1, 5, 1])
    [5, 3, 1, 1]
    >>> random_integer_partition(1)
    [1]
    r   )_randintrQ   zn must be a positive integerTr\   c                 S   s   g | ]	\}}|g| qS r   r   )r   rd   mr   r   r   r7   f  rN   z,random_integer_partition.<locals>.<listcomp>)Zsympy.testing.randtestr   r   r   r   sortr   )nZseedr   Zrandintr&   rd   Zmultr   r   r   random_integer_partitionC  s   
r   c                 C   s   t | d }td| d D ]}d|d|f< qtd| d D ].}t| D ]'}|| | krD|||d |f  ||d |d f  |||f< q#d|||f< q#q|S )a  
    Computes the m + 1 generalized unrestricted growth strings
    and returns them as rows in matrix.

    Examples
    ========

    >>> from sympy.combinatorics.partitions import RGS_generalized
    >>> RGS_generalized(6)
    Matrix([
    [  1,   1,   1,  1,  1, 1, 1],
    [  1,   2,   3,  4,  5, 6, 0],
    [  2,   5,  10, 17, 26, 0, 0],
    [  5,  15,  37, 77,  0, 0, 0],
    [ 15,  52, 151,  0,  0, 0, 0],
    [ 52, 203,   0,  0,  0, 0, 0],
    [203,   0,   0,  0,  0, 0, 0]])
    rQ   r   )r
   rS   )r   rm   rJ   rP   r   r   r   RGS_generalizedj  s   2r   c                 C   s    | dk rdS | dkrdS t | S )a  
    RGS_enum computes the total number of restricted growth strings
    possible for a superset of size m.

    Examples
    ========

    >>> from sympy.combinatorics.partitions import RGS_enum
    >>> from sympy.combinatorics.partitions import Partition
    >>> RGS_enum(4)
    15
    >>> RGS_enum(5)
    52
    >>> RGS_enum(6)
    203

    We can check that the enumeration is correct by actually generating
    the partitions. Here, the 15 partitions of 4 items are generated:

    >>> a = Partition(list(range(4)))
    >>> s = set()
    >>> for i in range(20):
    ...     s.add(a)
    ...     a += 1
    ...
    >>> assert len(s) == 15

    rQ   r   r   )r   r   r   r   r;     s
   r;   c                 C   s   |dk rt d| dk st|| krt ddg|d  }d}t|}td|d D ]/}||| |f }|| }|| krK|d ||< | |8 } |d7 }q*t| | d ||< | |; } q*dd |dd D S )	a  
    Gives the unranked restricted growth string for a given
    superset size.

    Examples
    ========

    >>> from sympy.combinatorics.partitions import RGS_unrank
    >>> RGS_unrank(14, 4)
    [0, 1, 2, 3]
    >>> RGS_unrank(0, 4)
    [0, 0, 0, 0]
    rQ   zThe superset size must be >= 1r   zInvalid argumentsrg   c                 S   s   g | ]}|d  qS )rQ   r   )r   xr   r   r   r7     rM   zRGS_unrank.<locals>.<listcomp>N)r   r;   r   rS   ri   )r2   r   LrP   DrJ   re   Zcrr   r   r   r:     s"   

r:   c                 C   sh   t | }d}t|}td|D ]"}t | |d d }t| d| }||||d f | |  7 }q|S )z
    Computes the rank of a restricted growth string.

    Examples
    ========

    >>> from sympy.combinatorics.partitions import RGS_rank, RGS_unrank
    >>> RGS_rank([0, 1, 2, 1, 3])
    42
    >>> RGS_rank(RGS_unrank(4, 7))
    4
    r   rQ   N)r   r   rS   rR   )rL   Zrgs_sizer2   r   rJ   r   r   r   r   r   rG     s   rG   r   )Z
sympy.corer   r   r   Zsympy.core.compatibilityr   r   Zsympy.core.sympifyr   Z%sympy.functions.combinatorial.numbersr	   Zsympy.matricesr
   Zsympy.sets.setsr   r   Zsympy.utilities.iterablesr   r   collectionsr   r   r[   r   r   r;   r:   rG   r   r   r   r   <module>   s&      '  
' %#