| 
						
						
						
					 | 
					 | 
					@ -1,30 +1,23 @@ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					"""Data structures for 2-valued, 4-valued, and 8-valued logic computation. | 
					 | 
					 | 
					 | 
					"""This module contains definitions and data structures for 2-, 4-, and 8-valued logic operations. | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					Integer constants: ZERO, ONE, UNASSIGNED, UNKNOWN, RISING, FALLING, PPULSE, NPULSE. | 
					 | 
					 | 
					 | 
					8 logic values are defined as integer constants. | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					* The bits in the constants have the following meaning: | 
					 | 
					 | 
					 | 
					* For 2-valued logic: ``ZERO`` and ``ONE`` | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					* 4-valued logic adds: ``UNASSIGNED`` and ``UNKNOWN`` | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					* 8-valued logic adds: ``RISE``, ``FALL``, ``PPULSE``, and ``NPULSE``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					The bits in these constants have the following meaning: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * bit 0: Final/settled binary value of a signal | 
					 | 
					 | 
					 | 
					  * bit 0: Final/settled binary value of a signal | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * bit 1: Initial binary value of a signal | 
					 | 
					 | 
					 | 
					  * bit 1: Initial binary value of a signal | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * bit 2: 1, if activity or transitions are present on a signal | 
					 | 
					 | 
					 | 
					  * bit 2: Activity or transitions are present on a signal | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					Special meaning is given to values where bits 0 and 1 differ, but activity is 0. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					These values are interpreted as 'unknown' or 'unassigned' in 4-valued and 8-valued logic. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					* 4-valued logic: 2 bits for storage, the third bit is implicitly 0 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 0 (0b00) : '0', 0, False, logic-0 (kyupy.logic.ZERO) | 
					 | 
					 | 
					 | 
					Special meaning is given to values where bits 0 and 1 differ, but bit 2 (activity) is 0. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 1 (0b01) : '-', None, unassigned (kyupy.logic.UNASSIGNED) | 
					 | 
					 | 
					 | 
					These values are interpreted as ``UNKNOWN`` or ``UNASSIGNED`` in 4-valued and 8-valued logic. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 2 (0b10) : 'X', unknown (kyupy.logic.UNKNOWN) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 3 (0b11) : '1', 1, True, logic-1 (kyupy.logic.ONE) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					* 8-valued logic: 3 bits for storage, adds the following 4 interpretations | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 4 (0b100) : 'P', positive pulse 0 -> 1 -> 0 (kyupy.logic.PPULSE) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 5 (0b101) : 'R', rising transition (kyupy.logic.RISING) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 6 (0b110) : 'F', falling transition (kyupy.logic.FALLING) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  * 7 (0b111) : 'N', negative pulse 1 -> 0 -> 1 (kyupy.logic.NPULSE) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					In general, 2-valued logic only considers bit 0, 4-valued logic considers bits 0 and 1, and 8-valued logic | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					considers all 3 bits. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					The only exception is constant ``ONE=0b11`` which has two bits set for all logics including 2-valued logic. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					""" | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import math | 
					 | 
					 | 
					 | 
					import math | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -32,16 +25,36 @@ from collections.abc import Iterable | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import numpy as np | 
					 | 
					 | 
					 | 
					import numpy as np | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					from . import numba | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					ZERO = 0b000 | 
					 | 
					 | 
					 | 
					ZERO = 0b000 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					"""Integer constant ``0b000`` for logic-0. | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b000`` for logic-0. ``'0'``, ``0``, ``False``, ``'L'``, and ``'l'`` are interpreted as ``ZERO``. | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					UNKNOWN = 0b001 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b001`` for unknown or conflict. ``'X'``, or any other value is interpreted as ``UNKNOWN``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					UNASSIGNED = 0b010 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b010`` for unassigned or high-impedance. ``'-'``, ``None``, ``'Z'``, and ``'z'`` are | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					interpreted as ``UNASSIGNED``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					""" | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					UNASSIGNED = 0b001 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					UNKNOWN = 0b010 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					ONE = 0b011 | 
					 | 
					 | 
					 | 
					ONE = 0b011 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b011`` for logic-1. ``'1'``, ``1``, ``True``, ``'H'``, and ``'h'`` are interpreted as ``ONE``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					PPULSE = 0b100 | 
					 | 
					 | 
					 | 
					PPULSE = 0b100 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					RISING = 0b101 | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b100`` for positive pulse, meaning initial and final values are 0, but there is some activity | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					FALLING = 0b110 | 
					 | 
					 | 
					 | 
					on a signal. ``'P'``, ``'p'``, and ``'^'`` are interpreted as ``PPULSE``. | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					RISE = 0b101 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b110`` for a rising transition. ``'R'``, ``'r'``, and ``'/'`` are interpreted as ``RISE``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					FALL = 0b110 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b101`` for a falling transition. ``'F'``, ``'f'``, and ``'\\'`` are interpreted as ``FALL``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					NPULSE = 0b111 | 
					 | 
					 | 
					 | 
					NPULSE = 0b111 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					"""Integer constant ``0b111`` for negative pulse, meaning initial and final values are 1, but there is some activity | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					on a signal. ``'N'``, ``'n'``, and ``'v'`` are interpreted as ``NPULSE``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def interpret(value): | 
					 | 
					 | 
					 | 
					def interpret(value): | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -54,9 +67,9 @@ def interpret(value): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if value in [None, '-', 'Z', 'z']: | 
					 | 
					 | 
					 | 
					    if value in [None, '-', 'Z', 'z']: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return UNASSIGNED | 
					 | 
					 | 
					 | 
					        return UNASSIGNED | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if value in ['R', 'r', '/']: | 
					 | 
					 | 
					 | 
					    if value in ['R', 'r', '/']: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return RISING | 
					 | 
					 | 
					 | 
					        return RISE | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if value in ['F', 'f', '\\']: | 
					 | 
					 | 
					 | 
					    if value in ['F', 'f', '\\']: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return FALLING | 
					 | 
					 | 
					 | 
					        return FALL | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if value in ['P', 'p', '^']: | 
					 | 
					 | 
					 | 
					    if value in ['P', 'p', '^']: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return PPULSE | 
					 | 
					 | 
					 | 
					        return PPULSE | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if value in ['N', 'n', 'v']: | 
					 | 
					 | 
					 | 
					    if value in ['N', 'n', 'v']: | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -64,6 +77,110 @@ def interpret(value): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return UNKNOWN | 
					 | 
					 | 
					 | 
					    return UNKNOWN | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					_bit_in_lut = np.array([2 ** x for x in range(7, -1, -1)], dtype='uint8') | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					@numba.njit | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def bit_in(a, pos): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return a[pos >> 3] & _bit_in_lut[pos & 7] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def mv_cast(*args, m=8): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return [a if isinstance(a, MVArray) else MVArray(a, m=m) for a in args] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def mv_getm(*args): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return max([a.m for a in args if isinstance(a, MVArray)] + [0]) or 8 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def _mv_not(m, out, inp): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    np.bitwise_xor(inp, 0b11, out=out)  # this also exchanges UNASSIGNED <-> UNKNOWN | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if m > 2: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        np.putmask(out, (inp == UNKNOWN), UNKNOWN)  # restore UNKNOWN | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def mv_not(x1, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    m = mv_getm(x1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    x1 = mv_cast(x1, m=m)[0] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    out = out or MVArray(x1.data.shape, m=m) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    _mv_not(m, out.data, x1.data) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def _mv_or(m, out, *ins): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if m > 2: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        any_one = (ins[0] == ONE) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins[1:]: any_one |= (inp == ONE) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        out[...] = ZERO | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        np.putmask(out, any_one, ONE) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            np.bitwise_or(out, inp, out=out, where=~any_one) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        np.putmask(out, (any_unknown & ~any_one), UNKNOWN) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    else: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        out[...] = ZERO | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins: np.bitwise_or(out, inp, out=out) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def mv_or(x1, x2, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    m = mv_getm(x1, x2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    x1, x2 = mv_cast(x1, x2, m=m) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    out = out or MVArray(np.broadcast(x1.data, x2.data).shape, m=m) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    _mv_or(m, out.data, x1.data, x2.data) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def _mv_and(m, out, *ins): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if m > 2: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        any_zero = (ins[0] == ZERO) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins[1:]: any_zero |= (inp == ZERO) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        out[...] = ONE | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        np.putmask(out, any_zero, ZERO) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            np.bitwise_and(out, inp | 0b100, out=out, where=~any_zero) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            if m > 4: np.bitwise_or(out, inp & 0b100, out=out, where=~any_zero) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        np.putmask(out, (any_unknown & ~any_zero), UNKNOWN) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    else: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        out[...] = ONE | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins: np.bitwise_and(out, inp, out=out) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def mv_and(x1, x2, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    m = mv_getm(x1, x2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    x1, x2 = mv_cast(x1, x2, m=m) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    out = out or MVArray(np.broadcast(x1.data, x2.data).shape, m=m) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    _mv_and(m, out.data, x1.data, x2.data) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def _mv_xor(m, out, *ins): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if m > 2: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        out[...] = ZERO | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            np.bitwise_xor(out, inp & 0b011, out=out) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            if m > 4: np.bitwise_or(out, inp & 0b100, out=out) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        np.putmask(out, any_unknown, UNKNOWN) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    else: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        out[...] = ZERO | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        for inp in ins: np.bitwise_xor(out, inp, out=out) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def mv_xor(x1, x2, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    m = mv_getm(x1, x2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    x1, x2 = mv_cast(x1, x2, m=m) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    out = out or MVArray(np.broadcast(x1.data, x2.data).shape, m=m) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    _mv_xor(m, out.data, x1.data, x2.data) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					class MVArray: | 
					 | 
					 | 
					 | 
					class MVArray: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """An n-dimensional array of m-valued logic values. | 
					 | 
					 | 
					 | 
					    """An n-dimensional array of m-valued logic values. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -71,11 +188,7 @@ class MVArray: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    interpreting 2-valued, 4-valued, and 8-valued logic values. | 
					 | 
					 | 
					 | 
					    interpreting 2-valued, 4-valued, and 8-valued logic values. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Each logic value is stored as an uint8, value manipulations are cheaper than in BPArray. | 
					 | 
					 | 
					 | 
					    Each logic value is stored as an uint8, value manipulations are cheaper than in BPArray. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Axis convention (1 axis, a single vector/pattern): | 
					 | 
					 | 
					 | 
					    An MVArray always has 2 or more axes: | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    * Axis is PI/PO/FF position, the length of this axis is called "width". | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Axis convention for 2 and more axes is consistent with BPArray: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    * Second-last axis is PI/PO/FF position, the length of this axis is called "width". | 
					 | 
					 | 
					 | 
					    * Second-last axis is PI/PO/FF position, the length of this axis is called "width". | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    * Last axis is vector/pattern, the length of this axis is called "length". | 
					 | 
					 | 
					 | 
					    * Last axis is vector/pattern, the length of this axis is called "length". | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -83,33 +196,43 @@ class MVArray: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    def __init__(self, a, m=None): | 
					 | 
					 | 
					 | 
					    def __init__(self, a, m=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.m = m or 4 | 
					 | 
					 | 
					 | 
					        self.m = m or 8 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        assert self.m in range(2, 256) | 
					 | 
					 | 
					 | 
					        assert self.m in [2, 4, 8] | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        # Try our best to interpret given a. | 
					 | 
					 | 
					 | 
					        # Try our best to interpret given a. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if isinstance(a, MVArray): | 
					 | 
					 | 
					 | 
					        if isinstance(a, MVArray): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            self.data = a.data.copy() | 
					 | 
					 | 
					 | 
					            self.data = a.data.copy() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            self.m = m or a.m | 
					 | 
					 | 
					 | 
					            self.m = m or a.m | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        elif isinstance(a, int) or isinstance(a, tuple): | 
					 | 
					 | 
					 | 
					        elif isinstance(a, int): | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            self.data = np.full((a, 1), UNASSIGNED, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        elif isinstance(a, tuple): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            self.data = np.full(a, UNASSIGNED, dtype=np.uint8) | 
					 | 
					 | 
					 | 
					            self.data = np.full(a, UNASSIGNED, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        else: | 
					 | 
					 | 
					 | 
					        else: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            if isinstance(a, str): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                a = [a] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            self.data = np.asarray(interpret(a), dtype=np.uint8) | 
					 | 
					 | 
					 | 
					            self.data = np.asarray(interpret(a), dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            if self.data.ndim > 1: | 
					 | 
					 | 
					 | 
					            if self.data.ndim == 1: | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                self.data = self.data[:, np.newaxis] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					            else: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                self.data = np.moveaxis(self.data, -2, -1) | 
					 | 
					 | 
					 | 
					                self.data = np.moveaxis(self.data, -2, -1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        # Cast data to m-valued logic. | 
					 | 
					 | 
					 | 
					        # Cast data to m-valued logic. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if self.m == 2: | 
					 | 
					 | 
					 | 
					        if self.m == 2: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            self.data[...] = ((self.data & 0b001) & ((self.data >> 1) & 0b001) | (self.data == RISING)) * ONE | 
					 | 
					 | 
					 | 
					            self.data[...] = ((self.data & 0b001) & ((self.data >> 1) & 0b001) | (self.data == RISE)) * ONE | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        elif self.m == 4: | 
					 | 
					 | 
					 | 
					        elif self.m == 4: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            self.data[...] = (self.data & 0b011) & ((self.data != FALLING) * ONE) | ((self.data == RISING) * ONE) | 
					 | 
					 | 
					 | 
					            self.data[...] = (self.data & 0b011) & ((self.data != FALL) * ONE) | ((self.data == RISE) * ONE) | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        elif self.m == 8: | 
					 | 
					 | 
					 | 
					        elif self.m == 8: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            self.data[...] = self.data & 0b111 | 
					 | 
					 | 
					 | 
					            self.data[...] = self.data & 0b111 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.length = 1 if self.data.ndim == 1 else self.data.shape[-1] | 
					 | 
					 | 
					 | 
					        self.length = self.data.shape[-1] | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        self.width = len(self.data) if self.data.ndim == 1 else self.data.shape[-2] | 
					 | 
					 | 
					 | 
					        self.width = self.data.shape[-2] | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    def __repr__(self): | 
					 | 
					 | 
					 | 
					    def __repr__(self): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return f'<MVArray length={self.length} width={self.width} m={self.m} bytes={self.data.nbytes}>' | 
					 | 
					 | 
					 | 
					        return f'<MVArray length={self.length} width={self.width} m={self.m} nbytes={self.data.nbytes}>' | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    def __str__(self): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        chars = ["0", "X", "-", "1", "P", "R", "F", "N"] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        return str([''.join(chars[v] for v in self.data[:, idx]) for idx in range(self.length)]) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					class BPArray: | 
					 | 
					 | 
					 | 
					class BPArray: | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |