
    ܖi                        d Z ddlZddlZddlmZ ddlmZ ddlmZ	 dZ
dZdZd	Zd
ZdZdZdZdZedededededededediZdZdZd	Zd
ZdZdZedededededediZ G d de      Z G d d e      Z G d! d"e      Z G d# d$e      Z  G d% d&e       Z! G d' d(e       Z" G d) d*e      Z# G d+ d,e      Z$ G d- de$      Z% G d. d/e$      Z& G d0 de&      Z' G d1 de&      Z(y)2a6  
Boolean expressions algebra.

This module defines a Boolean algebra over the set {TRUE, FALSE} with boolean
variables called Symbols and the boolean functions AND, OR, NOT.

Some basic logic comparison is supported: two expressions can be
compared for equivalence or containment. Furthermore you can simplify
an expression and obtain its normal form.

You can create expressions in Python using familiar boolean operators
or parse expressions from strings. The parsing can be extended with
your own tokenizer.  You can also customize how expressions behave and
how they are presented.

For extensive documentation look either into the docs directory or view it
online, at https://booleanpy.readthedocs.org/en/latest/.

Copyright (c) Sebastian Kraemer, basti.kr@gmail.com and others

SPDX-License-Identifier: BSD-2-Clause
    N)reduce)and_)or_F                        ANDORNOT()TRUEFALSESYMBOLzUnknown tokenzUnbalanced parenthesiszInvalid expressionz+Invalid expression nesting such as (AND xx)z&Invalid symbols sequence such as (A B)zAInvalid operator sequence without symbols such as AND OR or OR ORc                       e Zd ZdZddZd Zy)
ParseErrora  
    Raised when the parser or tokenizer encounters a syntax error. Instances of
    this class have attributes token_type, token_string, position, error_code to
    access the details of the error. str() of the exception instance returns a
    formatted message.
    Nc                 <    || _         || _        || _        || _        y N)
token_typetoken_stringposition
error_code)selfr   r   r   r   s        S/var/www/html/content-pipeline/venv/lib/python3.12/site-packages/boolean/boolean.py__init__zParseError.__init__P   s    $( $    c                     t         j                  | j                  d      }d}| j                  rd| j                   d}d}| j                  dkD  rd| j                   }| | | S )NzUnknown parsing error z for token: ""r   z at position: )PARSE_ERRORSgetr   r   r   )r   argskwargsemsgtstrposs         r   __str__zParseError.__str__V   sp    1HI"4#4#4"5Q7D==1"4==/2CvcU##r!   )Nr#   r   )__name__
__module____qualname____doc__r    r,    r!   r   r   r   H   s    %$r!   r   c                   f    e Zd ZdZ	 	 	 	 	 	 	 ddZd Zd ZddZd Zd Z	d	 Z
d
 Zd ZeZd ZeZy)BooleanAlgebraa  
    An algebra is defined by:

    - the types of its operations and Symbol.
    - the tokenizer used when parsing expressions from strings.

    This class also serves as a base class for all boolean expressions,
    including base elements, functions and variable symbols.
    Nc                    |xs t         | _        | j                         | _        |xs t        | _        | j                         | _        | j                  | j                  _        | j                  | j                  _        |xs t
        | _        |xs t        | _        |xs t        | _        |xs t        | _        | j                  | j                  | j
                  | j                  | j                  | j                  d}|j                         D ]'  }	|j                         D ]  \  }
}t        |	|
|        ) || _        y)a8  
        The types for TRUE, FALSE, NOT, AND, OR and Symbol define the boolean
        algebra elements, operations and Symbol variable. They default to the
        standard classes if not provided.

        You can customize an algebra by providing alternative subclasses of the
        standard types.
        r   r   r   r   r   SymbolN)_TRUEr   _FALSEr   dualr   r   r   r7   valuesitemssetattrallowed_in_token)r   
TRUE_classFALSE_classSymbol_class	NOT_class	AND_classOR_classr>   tf_naoobjnamevalues               r   r    zBooleanAlgebra.__init__o   s   & '%	IIK	 *F
ZZ\
 		))

 ##.b #,f IIZZ8888''kk
 ==? 	*C%||~ *eT5)*	*
 !1r!   c                     | j                   | j                  | j                  | j                  | j                  | j
                  fS )z{
        Return a tuple of this algebra defined elements and types as:
        (TRUE, FALSE, NOT, AND, OR, Symbol)
        r6   r   s    r   
definitionzBooleanAlgebra.definition   s/    
 yy$**dhh$''4;;NNr!   c                 @    t        t        | j                  |            S )zU
        Return a tuple of symbols building a new Symbol from each argument.
        )tuplemapr7   r   r'   s     r   symbolszBooleanAlgebra.symbols   s     Sd+,,r!   c                 8   | j                   d| j                  d| j                  dt        di}t	        |t
              r| j                  |      }nt        |      }t        r3t        |      }t        d       |D ]  }t        |        t        |      }ddg}d }d }d}	|D ]  \  }
}}t        r+t        d	t        |
      d
t        |      dt        |             |	rn|	\  }}}t        rt        dt        |	              ||      r ||
      rt        |
||t               ||      r= ||
      s	|
t        k(  r,t        |
||t               ||
      rt        |
||t              |
t         k(  r>|j#                  | j%                  |             t        rt        dt        |             nt	        |
t$              r/|j#                  |
       t        rt        dt        |             n|
t&        k(  r9|j#                  | j(                         t        rt        dt        |             n||
t*        k(  r9|j#                  | j,                         t        rQt        dt        |             n:|
t.        k(  r,|| j                   g}t        rt        dt        |             n|
t0        k(  r2| j3                  || j                  |      }t        rt        d|       n|
t4        k(  r2| j3                  || j                  |      }t        rt        d|       n|
t        k(  r6|	r*t.        t0        t4        t        fvrt        |
||t6              |t        g}nP|
t        k(  r4	 |d   t        |
||t8              |d   t        u rS|d   j#                  |d          t        rt        dt        |             |d   }t        rt        dt        |             nt	        |d   t:              rt        |
||t8              t=        j>                  |d         rtA        |d   tB              st        |
||t6               |d   |dd  }|d   j#                  |       t        rt        dt        |             |d   }t        rt        dt        |             3t        |
||tD              |
||f}	 	 	 |d   t        rt        dt        |             |d   Zt        rt        dt        |             tG        |      dk7  rt        tH              |d   }t        r<t        dt        |             n& |d   |dd  }t        rt        d t        |             nwt        rt        d!t        |              |d   |dd  }|d   j#                  |       t        rt        d"t        |             |d   }t        rt        d#t        |             	 |r|jM                         S t        rt        d$t        |             |S # tJ        $ r t        tH              w xY w)%a  
        Return a boolean expression parsed from `expr` either a unicode string
        or tokens iterable.

        Optionally simplify the expression if `simplify` is True.

        Raise ParseError on errors.

        If `expr` is a string, the standard `tokenizer` is used for tokenization
        and the algebra configured Symbol type is used to create Symbol
        instances from Symbol tokens.

        If `expr` is an iterable, it should contain 3-tuples of: (token_type,
        token_string, token_position). In this case, the `token_type` can be
        a Symbol instance or one of the TOKEN_* constant types.
        See the `tokenize()` method for detailed specification.
        r
   
         ztokens:Nc                 L    t        | t              xs | t        t        t        fv S r   )
isinstancer7   
TOKEN_TRUETOKEN_FALSETOKEN_SYMBOL_ts    r   is_symz$BooleanAlgebra.parse.<locals>.is_sym   s     b&)ZRJ\3Z-ZZr!   c                     | t         t        fv S r   )	TOKEN_ANDTOKEN_ORrZ   s    r   is_operatorz)BooleanAlgebra.parse.<locals>.is_operator   s    )X...r!   z
processing token_type:ztoken_string:ztoken_position:z  prev_token:z3 ast: token_type is TOKEN_SYMBOL: append new symbolz3 ast: token_type is Symbol): append existing symbolz ast: token_type is TOKEN_TRUE:z  ast: token_type is TOKEN_FALSE:z ast: token_type is TOKEN_NOT:z.  ast:token_type is TOKEN_AND: start_operationz-  ast:token_type is TOKEN_OR: start_operationr   r   r   zast9:zast10:zast11:zast12:zast[0] is None:z  ast[1] is None:r   r   z    parsed = ast[2]:z  parsed = ast[1](*ast[2:]):zsubex = ast[1](*ast[2:]):z  ast[0].append(subex):z    ast = ast[0]:zfinal parsed:)'r   r   r   
TOKEN_LPARrV   strtokenizeiterTRACE_PARSElistprintreprr   PARSE_INVALID_SYMBOL_SEQUENCE
TOKEN_RPARPARSE_INVALID_OPERATOR_SEQUENCErY   appendr7   rW   r   rX   r   	TOKEN_NOTr^   _start_operationr_   PARSE_INVALID_NESTINGPARSE_UNBALANCED_CLOSING_PARENSintinspectisclass
issubclassFunctionPARSE_UNKNOWN_TOKENlenPARSE_INVALID_EXPRESSION	TypeErrorsimplify)r   exprr{   
precedence	tokenizedtastr\   r`   
prev_tokenr   r   token_positionprev_token_type_prev_token_string_prev_token_positionsubexparseds                     r   parsezBooleanAlgebra.parse   s   & hh488R"j"M
dC d+IT
IYI) aYI Tl	[	/ 
8A }	D4Jn.$#&%( LVI!35I/4
+;</*:&$"L.B_  /
+zZ/G$"L.Ba 
 z*$"L.Ba  \)

4;;|45OQUVYQZ[J/

:&OQUVYQZ[z)

499%;T#YG{*

4::&<d3iHy(DHHo:DIFy(++C:FJCPx'++C*EI3Oz) 'y)Xz.ZZ(&nF[  J'z)1v~(&(*;	  1v+Ac!f-&!'495!!f&!(DI6!#a&#.(&(*;	  $OOCF3
3q688T(&nF[  #CFCG,EFMM%("hS	2a&C"hS	2O R !\>K^__$lNCJ{}	D~	Bq6>"/c;1v~&!"5tCyAs8q=",8P"QQ!$Q&!"8$v,G "(QQR!1&!"@$v,O"949E"CFCG,EFMM%("7cCa&C"149=9 $  ??$$/4<0  	B(@AA	Bs   1DW? ?Xc                    t         rt        dt        |      d|       ||   }	 |d   =t         rt        dt        |             ||d<   t         rt        dt        |             |S ||d      }||kD  rLt         rt        dt        |             |||j                  d      g}t         rt        d	t        |             |S ||k(  rt         rt        d
t        |             |S t	        j
                  |d         rt        |d   t              st        t              |d   Kt         rt        dt        |              |d   |dd  }|d   ||g}t         rt        dt        |             |S t         rt        dt        |             |d   j                   |d   |dd         |d   }t         rt        dt        |             )zW
        Return an AST where all operations of lower precedence are finalized.
        z   start_operation:zAST:r   Nz     start_op: ast[1] is None:z"     --> start_op: ast[1] is None:z     start_op: prec > op_prec:r-   z"     --> start_op: prec > op_prec:z     start_op: prec == op_prec:ra   r   z     start_op: ast[0] is None:r   z"     --> start_op: ast[0] is None:z     start_op: else:z     --> start_op: else:)rf   rh   ri   poprs   rt   ru   rv   r   rp   rm   )r   r   	operationr}   op_precprecsubexpnew_asts           r   ro   zBooleanAlgebra._start_operation  s    'i&#FY'1v~:DIF"A>S	J
c!f%Dg~:DIFIswwr{3>S	J
w;T#YG
OOCF+
3q680L ,ABB1v~:DIFQQR)q69f5>WN 0$s)<Afc!fc!"g./!f4d3i@W r!   c              #     K   t        |t              st        dt        |       d      i dt        dt        dt        dt
        dt
        dt
        d	t        d
t        dt        dt        dt        dt        dt        dt        dt        dt        dt        dt        i}d}t        |      }||k  r||   }|j                         xs |dk(  }|rD|dz  }||k  r5||   }|j                         s|| j                  v r|dz  }||z  }nn||k  r5|dz  }	 ||j                            ||f |dz  }||k  ryy# t        $ r' |rt         ||f n|dvrt#        ||t$              Y <w xY ww)aF  
        Return an iterable of 3-tuple describing each token given an expression
        unicode string.

        This 3-tuple contains (token, token string, position):

        - token: either a Symbol instance or one of TOKEN_* token types.
        - token string: the original token unicode string.
        - position: some simple object describing the starting position of the
          original token string in the `expr` string. It can be an int for a
          character offset, or a tuple of starting (row/line, column).

        The token position is used only for error reporting and can be None or
        empty.

        Raise ParseError on errors. The ParseError.args is a tuple of:
        (token_string, position, error message)

        You can use this tokenizer as a base to create specialized tokenizers
        for your custom algebra by subclassing BooleanAlgebra. See also the
        tests for other examples of alternative tokenizers.

        This tokenizer has these characteristics:

        - The `expr` string can span multiple lines,
        - Whitespace is not significant.
        - The returned position is the starting character offset of a token.
        - A TOKEN_SYMBOL is returned for valid identifiers which is a string
          without spaces.

            - These are valid identifiers:
                - Python identifiers.
                - a string even if starting with digits
                - digits (except for 0 and 1).
                - dotted names : foo.bar consist of one token.
                - names with colons: foo:bar consist of one token.
            
            - These are not identifiers:
                - quoted strings.
                - any punctuation which is not an operation

        - Recognized operators are (in any upper/lower case combinations):

            - for and:  '*', '&', 'and'
            - for or: '+', '|', 'or'
            - for not: '~', '!', 'not'

        - Recognized special symbols are (in any upper/lower case combinations):

            - True symbols: 1 and True
            - False symbols: 0, False and None
        zexpr must be string but it is .*&and+|or~!notr   r   []true1false0noner   _r   ) 	
)r   r   r   N)rV   rc   rz   typer^   r_   rn   rb   rk   rW   rX   rx   isalnumr>   lowerKeyErrorrY   r   rw   )r   r|   TOKENSr   lengthtoksymchars           r   rd   zBooleanAlgebra.tokenize  s
    j $$<T$ZLJKK



 9
 	

 
 (
 
 
 9
 
 
 
 
 J
 
  [!
" #
$ K%
* Tx.C++--3#:CA'>D||~1F1F)F At ' ASYY[)388 MH3 "  &X55 77$%(8H[ 	s6   DE9E9!E 9
E9E9-E63E95E66E9c                 
    |j                   r|S  fd|j                  D        }t        d |D              }t        |      dk(  r|d   S  |j                  | }j
                  }t        ||      r|j                         }|S )z
        Recursively flatten, simplify and apply the distributive laws to the
        `expr` expression. Distributivity is considered for the AND or OR
        `operation_inst` instance.
        c              3   B   K   | ]  }j                  |        y wr   )_recurse_distributive).0argoperation_instr   s     r   	<genexpr>z7BooleanAlgebra._recurse_distributive.<locals>.<genexpr>5  s     UC**3?U   c              3   <   K   | ]  }|j                           y wr   r{   r   r   s     r   r   z7BooleanAlgebra._recurse_distributive.<locals>.<genexpr>6  s     4S\\^4   r   r   )	isliteralr'   rM   rx   	__class__r:   rV   distributive)r   r|   r   r'   flattened_exprdualoperations   ` `   r   r   z$BooleanAlgebra._recurse_distributive,  s|     >>KU499U4t44t9>7N'.&++nm4+88:Nr!   c                      j                    j                  fv sJ |j                         }|j                         }  j                   j
                        }t         fd|j                  D              |_        t        |j                        dkD  r j                   k(  rt        | j                        s% j                  k(  rjt        | j                         rT|j                  }|j                  }|d   }|dd D ]-  } |||      } j                  ||      }|j                         }/ |S  j                  ||      }|j                         }|S )a  
        Return a normalized expression transformed to its normal form in the
        given AND or OR operation.

        The new expression arguments will satisfy these conditions:
    
        - ``operation(*args) == expr`` (here mathematical equality is meant)
        - the operation does not occur in any of its arg.
        - NOT is only appearing in literals (aka. Negation normal form).

        The operation must be an AND or OR operation or a subclass.
        c              3   B   K   | ]  }j                  |        y wr   )	normalize)r   ar   r   s     r   r   z+BooleanAlgebra.normalize.<locals>.<genexpr>[  s     J1$..I6Jr   r   r   N)r   r   
literalizer{   r   r   rM   r'   rx   rV   r   r   )r   r|   r   operation_exampler'   
expr_classr   s   ` `    r   r   zBooleanAlgebra.normalizeA  s8    HHGG
 
 	
 

  }}%dii< J		JJ	tyy>A$(("z$'@TWW$D$(()C99DJ7DABx '!$,11$8IJ}}	' 	 --d4EFD==?Dr!   c                 :    | j                  || j                        S )zL
        Return a conjunctive normal form of the `expr` expression.
        )r   r   r   r|   s     r   cnfzBooleanAlgebra.cnfp  s     ~~dDHH--r!   c                 :    | j                  || j                        S )zL
        Return a disjunctive normal form of the `expr` expression.
        )r   r   r   s     r   dnfzBooleanAlgebra.dnfx  s     ~~dDGG,,r!   )NNNNNN)r   :r   )F)r.   r/   r0   r1   r    rK   rP   r   ro   rd   r   r   r   conjunctive_normal_formr   disjunctive_normal_formr2   r!   r   r4   r4   d   si     (61pO-Sj3Ajl\*-^. "- "r!   r4   c                       e Zd ZdZdZdZdZdZdZdZ	d Z
ed        Zd Zed        Zd Zd Zed	        Zdd
Zd Zd Zd Zd Zd Zd Zd Zd ZeZd Zd ZeZd ZeZ y)
Expressionzh
    Abstract base class for all boolean expressions, including functions and
    variable symbols.
    Nc                 L    d | _         t               | _        d| _        d| _        y NF)
sort_orderrM   r'   r   iscanonicalrJ   s    r   r    zExpression.__init__  s'     G	  !r!   c                 :    t        d | j                  D              S )z
        Return a set of all associated objects with this expression symbols.
        Include recursively subexpressions objects.
        c              3   4   K   | ]  }|j                     y wr   rF   )r   ss     r   r   z%Expression.objects.<locals>.<genexpr>  s     /Q155/s   )setrP   rJ   s    r   objectszExpression.objects  s     /$,,///r!   c                     | j                   r| gS | j                  sg S t        t        j                  j                  d | j                  D                    S )z
        Return a list of all the literals contained in this expression.
        Include recursively subexpressions symbols.
        This includes duplicates.
        c              3   <   K   | ]  }|j                           y wr   )get_literalsr   s     r   r   z*Expression.get_literals.<locals>.<genexpr>  s     1Z#2B2B2D1Zr   )r   r'   rg   	itertoolschainfrom_iterablerJ   s    r   r   zExpression.get_literals  sE     >>6MyyIIOO111ZPTPYPY1ZZ[[r!   c                 4    t        | j                               S )z
        Return a set of all literals contained in this expression.
        Include recursively subexpressions literals.
        )r   r   rJ   s    r   literalszExpression.literals  s     4$$&''r!   c                       j                   r S t        d  j                  D              }t         fdt	        |      D              r S   j
                  | S )z
        Return an expression where NOTs are only occurring as literals.
        Applied recursively to subexpressions.
        c              3   <   K   | ]  }|j                           y wr   )r   r   s     r   r   z(Expression.literalize.<locals>.<genexpr>  s     ;#S^^%;r   c              3   F   K   | ]  \  }}|j                   |   u   y wr   r'   )r   ir   r   s      r   r   z(Expression.literalize.<locals>.<genexpr>  s"     Avq#sdiil"As   !)r   rM   r'   all	enumerater   rO   s   ` r   r   zExpression.literalize  sM    
 >>K;;;A4AAKt~~t$$r!   c                     | j                         D cg c]#  }t        |t              r|n|j                  d   % c}S c c}w )
        Return a list of all the symbols contained in this expression.
        Include subexpressions symbols recursively.
        This includes duplicates.
        r   )r   rV   r7   r'   )r   r   s     r   get_symbolszExpression.get_symbols  s9     DHCTCTCVWaZ6*q	9WWWs   (>c                 4    t        | j                               S )r   )r   r   rJ   s    r   rP   zExpression.symbols  s     4##%&&r!   c                 v    |j                         D ]  \  }}|| k(  s|c S  | j                  |||      }|| S |S )aj  
        Return an expression where all subterms of this expression are
        by the new expression using a `substitutions` mapping of:
        {expr: replacement}

        Return the provided `default` value if this expression has no elements,
        e.g. is empty.

        Simplify the results if `simplify` is True.

        Return this expression unmodified if nothing could be substituted. Note
        that a possible usage of this function is to check for expression
        containment as the expression will be returned unmodified if if does not
        contain any of the provided substitutions.
        )r<   _subs)r   substitutionsdefaultr{   r|   substitutions         r   subszExpression.subs  sQ    $ #0"5"5"7 	$D,t|##	$
 zz-(;|t--r!   c                    g }d}| | j                   u s| | j                  u r| S | j                  s|S | j                  D ]n  }|j                         D ]  \  }}||k(  s|j	                  |       d} 4 |j                  |||      }	|	|j	                  |       \|j	                  |	       d}p |sy | j                  | }
|r|
j                         S |
S )z
        Return an expression where all subterms are substituted by the new
        expression using a `substitutions` mapping of: {expr: replacement}
        FTN)r   r   r'   r<   rm   r   r   r{   )r   r   r   r{   new_argumentschanged_somethingr   r|   r   new_argnewexprs              r   r   zExpression._subs  s     ! 499

 2K yyN 99 	-C '4&9&9&; -"l$;!((6(,%	- ))M7HE? "((- "((1(,%/	-2 ! !$..-0%-w!:7:r!   c                     | S )a  
        Return a new simplified expression in canonical form built from this
        expression. The simplified expression may be exactly the same as this
        expression.

        Subclasses override this method to compute actual simplification.
        r2   rJ   s    r   r{   zExpression.simplify(  s	     r!   c                     | j                   st        |       }n,t        t        t	        t        | j                                     }t        | j
                  j                        |z  S )ab  
        Expressions are immutable and hashable. The hash of Functions is
        computed by respecting the structure of the whole expression by mixing
        the class name hash and the recursive hash of a frozenset of arguments.
        Hash of elements is based on their boolean equivalent. Hash of symbols
        is based on their object.
        )r'   idhash	frozensetrN   r   r.   )r   arghashs     r   __hash__zExpression.__hash__2  sH     yyhG9Styy%9:;GDNN++,w66r!   c                     | |u ryt        || j                        r+t        | j                        t        |j                        k(  S t        S )a_  
        Test if other element is structurally the same as itself.

        This method does not make any simplification or transformation, so it
        will return False although the expression terms may be mathematically
        equal. Use simplify() before testing equality to check the mathematical
        equality.

        For literals, plain equality is used.

        For functions, equality uses the facts that operations are:

        - commutative: order does not matter and different orders are equal.
        - idempotent: so args can appear more often in one term than in the other.
        T)rV   r   r   r'   NotImplementedr   others     r   __eq__zExpression.__eq__@  s?      5=eT^^,TYY'9UZZ+@@@r!   c                     | |k(   S r   r2   r  s     r   __ne__zExpression.__ne__X  s    5=  r!   c                     | j                   D|j                   8| j                   |j                   k(  rt        S | j                   |j                   k  S t        S r   )r   r  r  s     r   __lt__zExpression.__lt__[  sJ    ??&5+;+;+G%"2"22%%??U%5%555r!   c                 r    |j                  |       }|t        u r| j                  |      }|t        u ry| S |S r   )r	  r  )r   r  ltself_lts       r   __gt__zExpression.__gt__b  s>    \\$kk%(G.("{"	r!   c                 &    | j                  | |      S r   )r   r  s     r   __and__zExpression.__and__m  s    xxe$$r!   c                 $    | j                  |       S r   )r   rJ   s    r   
__invert__zExpression.__invert__r  s    xx~r!   c                 &    | j                  | |      S r   )r   r  s     r   __or__zExpression.__or__u  s    wwtU##r!   c                     t        d      )Nz/Cannot evaluate expression as a Python Boolean.)rz   rJ   s    r   __bool__zExpression.__bool__z  s    IJJr!   r   )!r.   r/   r0   r1   r   r   r   r   r   r7   r    propertyr   r   r   r   r   rP   r   r   r{   r   r  r  r	  r  r  __mul__r  r  __add__r  __nonzero__r2   r!   r   r   r     s     DE
C
C	BF! 0 0
\ ( (%X ' '.43;j70!	% G$ GK Kr!   r   c                   :     e Zd ZdZ fdZd Zd xZZddZ xZ	S )BaseElementz^
    Abstract base class for the base elements TRUE and FALSE of the boolean
    algebra.
    c                 T    t         t        |           d| _        d| _        d | _        y )Nr   T)superr  r    r   r   r:   r   r   s    r   r    zBaseElement.__init__  s(    k4)+ 	r!   c                 L    t        |t              r| | j                  k(  S t        S r   )rV   r  r   r  r  s     r   r	  zBaseElement.__lt__  s!    e[)4::%%r!   c                      y r   r2   r   s    r   <lambda>zBaseElement.<lambda>      r!   c                 $    d|z  t        |       z   S )C
        Return a pretty formatted representation of self.
        r   )ri   )r   indentdebugs      r   prettyzBaseElement.pretty  s     fT
**r!   r   F)
r.   r/   r0   r1   r    r	  r  r  r(  __classcell__r   s   @r   r  r    s#    

 ,+K(+r!   r  c                   J     e Zd ZdZ fdZd Zd Zd Zd Zd Z	d xZ
Z xZS )	r8   z^
    Boolean base element TRUE.
    Not meant to be subclassed nor instantiated directly.
    c                 *    t         t        |           y r   )r  r8   r    r  s    r   r    z_TRUE.__init__  s    eT#%r!   c                     t        d      S NTr   rJ   s    r   r   z_TRUE.__hash__  s    Dzr!   c                 :    | |u xs |du xs t        |t              S r/  )rV   r8   r  s     r   r  z_TRUE.__eq__  s!    u}IIE51IIr!   c                      y)Nr   r2   rJ   s    r   r,   z_TRUE.__str__      r!   c                      y)Nr   r2   rJ   s    r   __repr__z_TRUE.__repr__  s    r!   c                     | S r   r2   rJ   s    r   __call__z_TRUE.__call__      r!   c                      yr/  r2   r!  s    r   r"  z_TRUE.<lambda>  r#  r!   r.   r/   r0   r1   r    r   r  r,   r5  r7  r  r  r*  r+  s   @r   r8   r8     s3    
&J ,+K(r!   r8   c                   J     e Zd ZdZ fdZd Zd Zd Zd Zd Z	d xZ
Z xZS )	r9   z_
    Boolean base element FALSE.
    Not meant to be subclassed nor instantiated directly.
    c                 *    t         t        |           y r   )r  r9   r    r  s    r   r    z_FALSE.__init__  s    fd$&r!   c                     t        d      S r   r0  rJ   s    r   r   z_FALSE.__hash__  s    E{r!   c                 :    | |u xs |du xs t        |t              S r   )rV   r9   r  s     r   r  z_FALSE.__eq__  s!    u}KK*UF2KKr!   c                      y)Nr   r2   rJ   s    r   r,   z_FALSE.__str__  r3  r!   c                      y)Nr   r2   rJ   s    r   r5  z_FALSE.__repr__  s    r!   c                     | S r   r2   rJ   s    r   r7  z_FALSE.__call__  r8  r!   c                      yr   r2   r!  s    r   r"  z_FALSE.<lambda>  r#  r!   r:  r+  s   @r   r9   r9     s3    
'L -,K(r!   r9   c                   N     e Zd ZdZ fdZd Zd Zd Zd Zd Z	d Z
d
d	Z xZS )r7   zh
    Boolean variable.

    A Symbol can hold an object used to determine equality between symbols.
    c                 b    t         t        |           d| _        || _        d| _        d| _        y )Nr
   T)r  r7   r    r   rF   r   r   )r   rF   r   s     r   r    zSymbol.__init__  s-    fd$&r!   c                      || j                      S )zH
        Return the evaluated value for this symbol from kwargs
        r   r   r(   s     r   r7  zSymbol.__call__  s     dhhr!   c                 Z    | j                   t        |       S t        | j                         S r   )rF   r   r   rJ   s    r   r   zSymbol.__hash__  s#    88d8ODHH~r!   c                 v    | |u ryt        || j                        r| j                  |j                  k(  S t        S r/  )rV   r   rF   r  r  s     r   r  zSymbol.__eq__  s3    5=eT^^,88uyy((r!   c                     t         j                  | |      }|t        ur|S t        |t              r| j
                  |j
                  k  S t        S r   )r   r	  r  rV   r7   rF   )r   r  
comparators      r   r	  zSymbol.__lt__  sE    &&tU3
^+eV$88eii''r!   c                 ,    t        | j                        S r   )rc   rF   rJ   s    r   r,   zSymbol.__str__  s    488}r!   c                     t        | j                  t              rd| j                   dnt        | j                        }| j                  j
                   d| dS )N'r   r   )rV   rF   rc   ri   r   r.   )r   rF   s     r   r5  zSymbol.__repr__  sH    !+DHHc!:$((1oTXX..))*!C522r!   c                    d}|r |d| j                   d| j                  dz  }t        | j                  t              rd| j                   dnt        | j                        }d|z  | j                  j                   d| | dz   S )	r%  r#   <isliteral=, iscanonical=>rM  r   r   r   )r   r   rV   rF   rc   ri   r   r.   )r   r&  r'  debug_detailsrF   s        r   r(  zSymbol.pretty  s     {4>>*<N4K[K[J^^_``M!+DHHc!:$((1oTXXf4>>#:#:";1]OC5PQ RRRr!   r)  )r.   r/   r0   r1   r    r7  r   r  r	  r,   r5  r(  r*  r+  s   @r   r7   r7     s1     
3	Sr!   r7   c                   6     e Zd ZdZ fdZd Zd ZddZ xZS )rv   a  
    Boolean function.

    A boolean function takes n (one or more) boolean expressions as arguments
    where n is called the order of the function and maps them to one of the base
    elements TRUE or FALSE. Implemented functions are AND, OR and NOT.
    c                     t         t        |           d | _        t	        d |D              s
J d|       t        |      | _        y )Nc              3   <   K   | ]  }t        |t                y wr   )rV   r   r   s     r   r   z$Function.__init__.<locals>.<genexpr>   s      
,/JsJ'
r   z4Bad arguments: all arguments must be an Expression: )r  rv   r    operatorr   rM   r'   )r   r'   r   s     r   r    zFunction.__init__  sV    h&(  
37
 
 	KA$J	K 
 $K	r!   c                 ^   | j                   }t        |      dk(  r4| j                  r| j                   |d    S | j                   d|d    dS g }|D ]>  }|j                  r|j	                  t        |             *|j	                  d| d       @ | j                  j                  |      S )Nr   r   r   r   )r'   rx   r   rV  rm   rc   join)r   r'   args_strr   s       r   r,   zFunction.__str__%  s    yyt9>~~--a	22mm_Ad1gYa00 	,C}}C)!C5
+		, }}!!(++r!   c                     dj                  t        t        | j                              }| j                  j
                   d| dS )Nz, r   r   )rX  rN   ri   r'   r   r.   rO   s     r   r5  zFunction.__repr__5  s9    yyT499-...))*!D633r!   c           	         d}|ri|d| j                   d| j                  z  }t        | dd      }||d|z  }t        | dd      }||d|z  }t        | d	d      }||d
|z  }|dz  }| j                  j                  }| j
                  D cg c]  }|j                  |dz   |       }	}dj                  |	      }
d|z  }| j                   rdnd}| | d| | |
 d| d	S c c}w )aq  
        Return a pretty formatted representation of self as an indented tree.

        If debug is True, also prints debug information for each expression arg.

        For example:

        >>> print(BooleanAlgebra().parse(
        ...    u'not a and not b and not (a and ba and c) and c or c').pretty())
        OR(
          AND(
            NOT(Symbol('a')),
            NOT(Symbol('b')),
            NOT(
              AND(
                Symbol('a'),
                Symbol('ba'),
                Symbol('c')
              )
            ),
            Symbol('c')
          ),
          Symbol('c')
        )
        r#   rO  rP  identityNz, identity=annihilatorz, annihilator=r:   z, dual=rQ  r   r&  r'  z,
r   r   r   r   )r   r   getattrr   r.   r'   r(  rX  )r   r&  r'  rR  r\  r]  r:   clsr   r'   pfargs
cur_indentnew_lines                r   r(  zFunction.pretty9  s/   4 {4>>*<N4K[K[J^__MtZ6H#;xl!;;!$t<K&>+!AA4.D74(!33S Mnn%%BF))LQ
%8LLD!6\
2TcU!M?8*VHBzlRSTT	 Ms   C(r)  )	r.   r/   r0   r1   r    r,   r5  r(  r*  r+  s   @r   rv   rv     s    	 , 4.Ur!   rv   c                   R     e Zd ZdZ fdZd Zd Zd Zd Zd Z	d Z
d
 fd		Z xZS )r   aZ  
    Boolean NOT operation.

    The NOT operation takes exactly one argument. If this argument is a Symbol
    the resulting expression is also called a literal.

    The operator "~" can be used as abbreviation for NOT, e.g. instead of NOT(x)
    one can write ~x (where x is some boolean expression). Also for printing "~"
    is used for better readability.

    You can subclass to define alternative string representation.

    For example:

    >>> class NOT2(NOT):
    ...     def __init__(self, *args):
    ...         super(NOT2, self).__init__(*args)
    ...         self.operator = '!'
    c                 ~    t         t        |   |       t        | j                  d   t
              | _        d| _        y )Nr   r   )r  r   r    rV   r'   r7   r   rV  )r   arg1r   s     r   r    zNOT.__init__  s/    c4!$'#DIIaL&9r!   c                 r    | j                         }t        || j                        r|S |j                         S )zQ
        Return an expression where NOTs are only occurring as literals.
        )demorganrV   r   r   r   s     r   r   zNOT.literalize  s/     }}dDNN+K  r!   c                 t   | j                   r| S | j                         }t        || j                        s|j	                         S |j
                  d   | j                  | j                  fv r|j
                  d   j                  S | j                  |j
                  d   j	                               }d|_         |S )z
        Return a simplified expr in canonical form.

        This means double negations are canceled out and all contained boolean
        objects are in their canonical form.
        r   T)	r   cancelrV   r   r{   r'   r   r   r:   r   s     r   r{   zNOT.simplify  s     K{{}$/==?"99Q<IIJJ
 
 99Q<$$$~~diil3356r!   c                     | }	 |j                   d   }t        || j                        s|S |j                   d   }t        || j                        s|S O)zq
        Cancel itself and following NOTs as far as possible.
        Returns the simplified expression.
        r   )r'   rV   r   )r   r|   r   s      r   rj  z
NOT.cancel  sQ    
 ))A,Cc4>>288A;DdDNN3 r!   c                       j                         }|j                  st        | j                        s|S |j                  d   } |j
                   fd|j                  D         S )z
        Return a expr where the NOT function is moved inward.
        This is achieved by canceling double NOTs and using De Morgan laws.
        r   c              3   \   K   | ]#  }j                  |      j                          % y wr   )r   rj  r   r   r   s     r   r   zNOT.demorgan.<locals>.<genexpr>  s#     I#,335Is   ),)rj  r   rV   r   r'   r:   )r   r|   ops   `  r   rh  zNOT.demorgan  sQ    
 {{}>>D$((!;KYYq\rwwIIJJr!   c                 .     | j                   d   di | S )zI
        Return the evaluated (negated) value for this function.
        r   r2   r   rF  s     r   r7  zNOT.__call__  s      499Q<)&)))r!   c                 &    | j                   d   |k  S )Nr   r   r  s     r   r	  z
NOT.__lt__  s    yy|e##r!   c                    d}|r |d| j                   d| j                  dz  }| j                   rD| j                  d   j                  d|      }d|z  | j                  j
                   d| | d	z   S t        t        |   ||      S )
z
        Return a pretty formatted representation of self.
        Include additional debug details if `debug` is True.
        r#   rO  rP  rQ  r   r^  r   r   r   )r   r   r'   r(  r   r.   r  r   )r   r&  r'  rR  pretty_literalr   s        r   r(  z
NOT.pretty  s    
 {4>>*<N4K[K[J^^_``M>>!YYq\000GN&Lt~~'>'>&?qP^O__`$aaad*&*FFr!   )r   F)r.   r/   r0   r1   r    r   r{   rj  rh  r7  r	  r(  r*  r+  s   @r   r   r   j  s8    (
!0	K*$G Gr!   c                   X     e Zd ZdZdZ fdZd ZddZd Zd Z	d Z
d	 Zd
 Zd Z xZS )DualBasez
    Base class for AND and OR function.

    This class uses the duality principle to combine similar methods of AND
    and OR. Both operations take two or more arguments and can be created using
    "|" for OR and "&" for AND.
    Nc                 Z    t        t        | 
  ||g|  d | _        d | _        d | _        y r   )r  ru  r    r\  r]  r:   r   rf  arg2r'   r   s       r   r    zDualBase.__init__  s5    h&tT9D9    	r!   c                      | j                   v ryt        | j                        rt         fd|j                   D              S y)z?
        Test if expr is a subterm of this expression.
        Tc              3   :   K   | ]  }|j                   v   y wr   r   rn  s     r   r   z(DualBase.__contains__.<locals>.<genexpr>  s     =Csdii'=   N)r'   rV   r   r   r   s   ` r   __contains__zDualBase.__contains__  s<     499dDNN+=499=== ,r!   c                 6   | j                   r| S | j                  D cg c]  }|j                          }} | j                  | }|j	                         }|j                         }| j                  |j                  v r| j                  S g }|j                  D ]  }||vs|j                  |        t        |      dk(  r|d   S | j                  |v r.|j                  | j                         t        |      dk(  r|d   S |D ]#  }| j                  |      |v s| j                  c S  d}|t        |      dz
  k  r|dz   }||   }t        || j                        s|dz  }8|t        |      k  r>||   }t        || j                        r+t        |j                        t        |j                        k7  r|dz  }[d}	|j                  D ]H  }||j                  v r| j                  |      j                         |j                  v r	|	|}	Bd}	 nd}	 n |	||= t        |j                        }
|
j                  |	       t        |
      dk(  r	|
d   ||<   n | j                  |
 ||<   t        |      dk(  r|d   S  | j                  | j                         S |dz  }|t        |      k  r>|dz  }|t        |      dz
  k  r| j!                  |      }t        |      dk(  r|d   S |r|j#                           | j                  | }d|_         |S c c}w )a-  
        Return a new simplified expression in canonical form from this
        expression.

        For simplification of AND and OR fthe ollowing rules are used
        recursively bottom up:

        - Associativity (output does not contain same operations nested)::

            (A & B) & C = A & (B & C) = A & B & C
            (A | B) | C = A | (B | C) = A | B | C
         
         
        - Annihilation::

            A & 0 = 0, A | 1 = 1

        - Idempotence (e.g. removing duplicates)::

            A & A = A, A | A = A

        - Identity::

            A & 1 = A, A | 0 = A

        - Complementation::

            A & ~A = 0, A | ~A = 1

        - Elimination::

            (A & B) | (A & ~B) = A, (A | B) & (A | ~B) = A

        - Absorption::

            A & (A | B) = A, A | (A & B) = A

        - Negative absorption::

            A & (~A | B) = A & B, A | (~A & B) = A | B

        - Commutativity (output is always sorted)::

            A & B = B & A, A | B = B | A

        Other boolean objects are also in their canonical form.
        r   r   NT)r   r'   r{   r   r   flattenr]  rm   rx   r\  remover   rV   r:   rj  rg   absorbsort)r   r  r   r'   r|   r   jaiajnegatedaiargss              r   r{   zDualBase.simplify  s   f K +/))4344 t~~t$  
 ||~ tyy(### 99 	!C$C 	! t9>7N ==D KK&4yA~Aw  	(Cxx}$'''	(
 #d)a-AAaBb$)),Qc$i-!W!"dii0CLCL4PFA 77 Cbgg~#--/277:"?&)G&*G!"& &Q!"'']FMM'*6{a'"()Q"+$))V"4Q4yA~#Aw  .t~~t4==??QM c$i-N FA[ #d)a-b {{4 t9>7N IIK t~~t$I 5s   Lc                     t        | j                        }d}| j                  D ]H  }t        || j                        r+|j                  |||dz    |t	        |j                        z  }D|dz  }J  | j                  | S )z
        Return a new expression where nested terms of this expression are
        flattened as far as possible.

        E.g.::

            A & (B & C) becomes A & B & C.
        r   r   )rg   r'   rV   r   rx   )r   r'   r   r   s       r   r~  zDualBase.flatten  sx     DII99 	C#t~~."%((QQS]"Q	 t~~t$$r!   c                 @   t        |      }|st        | j                        }d}|t        |      k  rj||   }d}|t        |      k  r?||k(  r|dz  }||   }t        || j                        s|dz  };||v r||= ||k  r|dz  }M| j                  |      j                         }||v r@|j                  |d      }|||= ||k  r|dz  }||v r||= ||k  r|dz  }n|||<   |dz  }t        || j                        rsd}|j                  D ]J  }	| j                  |	      j                         }
|	|j                  v r1|
|j                  v r	||
}Dd} nd} n ||j                  |d      ||<   |dz  }|t        |      k  r?|dz  }|t        |      k  rj|S )ad  
        Given an `args` sequence of expressions, return a new list of expression
        applying absorption and negative absorption.

        See https://en.wikipedia.org/wiki/Absorption_law

        Absorption::

            A & (A | B) = A, A | (A & B) = A

        Negative absorption::

            A & (~A | B) = A & B, A | (~A & B) = A | B
        r   r   Fr   NT)rg   r'   rx   rV   r:   r   rj  subtract)r   r'   r   absorberr  targetneg_absorberbr  r   nargs              r   r  zDualBase.absorb  s    Dz		?D#d)mAwHAc$i-6FAa!&$))4FA v%Q1uQ  $xx188:6)uEAy Gq5FA 9 $Q 1u !Q&'DGQ h		2!F'}} "#xx}335&++- !V[[0%~)-)- %%)F!" )"(//&4/"HQQi c$i-j FAq #d)mt r!   c                      j                   } j                   v r't         j                         }|j                         nRt         j                        r<t         fdj                   D              rt        fd j                   D              }t        |      dk(  ryt        |      dk(  r|d   S   j                  | }|r|j                         }|S )z
        Return a new expression where the `expr` expression has been removed
        from this expression if it exists.
        c              3   :   K   | ]  }|j                   v   y wr   r   rn  s     r   r   z$DualBase.subtract.<locals>.<genexpr>  s     93$))#9r{  c              3   ,   K   | ]  }|vs|  y wr   r2   )r   r   r|   s     r   r   z$DualBase.subtract.<locals>.<genexpr>  s     ISDSIs   	r   Nr   )	r'   rg   r  rV   r   r   rM   rx   r{   )r   r|   r{   r'   r   s   ``   r   r  zDualBase.subtract  s    
 yy499		?DKKdnn-9tyy99IDIIIIt9>t9>7N $..$'&&(Gr!   c                 ,     j                   }t         j                        }t        |      D ]'  \  }}t	        ||      r|j                  ||<   "|f||<   ) t        j                  | }t         fd|D              }t        |      dk(  r|d   S  || S )z
        Return a term where the leading AND or OR terms are switched.

        This is done by applying the distributive laws::

            A & (B|C) = (A&B) | (A&C)
            A | (B&C) = (A|B) & (A|C)
        c              3   X   K   | ]!  } j                   | j                          # y wr   )r   r{   rn  s     r   r   z(DualBase.distributive.<locals>.<genexpr>"  s%     E^T^^S)224Es   '*r   r   )	r:   rg   r'   r   rV   r   productrM   rx   )r   r:   r'   r   r   prods   `     r   r   zDualBase.distributive  s     yyDIIo 	!FAs#t$((Q&Q		!   $'EEEt9>7N;r!   c                    t         j                  | |      }|t        ur|S t        || j                        rt        | j                        }t        |j                        }t        t        ||            D ]M  }| j                  |   |j                  |   k(  r#| j                  |   |j                  |   k  }|t        usK|c S  ||k7  r||k  S t        S r   )	r   r	  r  rV   r   rx   r'   rangemin)r   r  rJ  lenselflenotherr   s         r   r	  zDualBase.__lt__)  s    &&tU3
^+eT^^,$))nG5::H3w12 &99Q<5::a=0!YYq\EJJqM9
^3%%& ("))r!   c                 V    t        | j                  fd| j                  D              S )aI  
        Return the evaluation of this expression by calling each of its arg as
        arg(**kwargs) and applying its corresponding Python operator (and or or)
        to the results.

        Reduce is used as in e.g. AND(a, b, c, d) == AND(a, AND(b, AND(c, d)))
        ore.g. OR(a, b, c, d) == OR(a, OR(b, OR(c, d)))
        c              3   .   K   | ]  } |di   y w)Nr2   r2   )r   r   r(   s     r   r   z$DualBase.__call__.<locals>.<genexpr>F  s     (HV(Hs   )r   _pyoperatorr'   rF  s    `r   r7  zDualBase.__call__=  s"     d&&(Hdii(HIIr!   )T)r.   r/   r0   r1   r  r    r|  r{   r~  r  r  r   r	  r7  r*  r+  s   @r   ru  ru    sA     K>[z%(M^,2(	Jr!   ru  c                   &     e Zd ZdZeZ fdZ xZS )r   a  
    Boolean AND operation, taking two or more arguments.

    It can also be created by using "&" between two boolean expressions.

    You can subclass to define alternative string representation by overriding
    self.operator.
    
    For example:

    >>> class AND2(AND):
    ...     def __init__(self, *args):
    ...         super(AND2, self).__init__(*args)
    ...         self.operator = 'AND'
    c                     t        t        | 
  ||g|  d| _        | j                  | _        | j                  | _        | j                  | _	        d| _
        y )NrR   r   )r  r   r    r   r   r\  r   r]  r   r:   rV  rw  s       r   r    zAND.__init__\  sI    c4!$4t4		::GG	r!   )r.   r/   r0   r1   and_operatorr  r    r*  r+  s   @r   r   r   I  s      K r!   c                   &     e Zd ZdZeZ fdZ xZS )r   a  
    Boolean OR operation, taking two or more arguments

    It can also be created by using "|" between two boolean expressions.

    You can subclass to define alternative string representation by overriding
    self.operator.

    For example:

    >>> class OR2(OR):
    ...     def __init__(self, *args):
    ...         super(OR2, self).__init__(*args)
    ...         self.operator = 'OR'
    c                     t        t        | 
  ||g|  d| _        | j                  | _        | j                  | _        | j                  | _	        d| _
        y )N   r   )r  r   r    r   r   r\  r   r]  r   r:   rV  rw  s       r   r    zOR.__init__x  sI    b$ t3d3

99HH	r!   )r.   r/   r0   r1   or_operatorr  r    r*  r+  s   @r   r   r   e  s      K r!   ))r1   rs   r   	functoolsr   rV  r   r  r   r  rf   r^   r_   rn   rb   rk   rW   rX   rY   TOKEN_TYPESrw   rq   ry   rp   rj   rl   r%   	Exceptionr   objectr4   r   r  r8   r9   r7   rv   r   ru  r   r   r2   r!   r   <module>r     sw  .    ) '  		


 udu(	  "#    ! "#  #%=2H!#K#%h$ $8Z"V Z"z| |~+* +:,K ,8-[ -89SZ 9SxVUz VUriG( iGXpJx pJf( 8 r!   