In [ ]:
# #Logic simulator requirements:
# 
# * Must be able to simulate simple sequential logic circuits.
# * Must be able to handle feedback (implied by above)


# #Circuit design architecture
# 
# * Circuits are made up of components.
# * Each component depends on some other components (inputs)
# 
#  e.g
#ource(1) -------,
#                      v
#                     Adder  Here adder relies on two inputs. They provide a constant '1' and '0' respectively.
#                      ^
#ource(0) -------'
# * The component, Output simply relays the output of its input component.
# 
#ource(1) ----> Output ---> '1'
# 
#   The purpose of output is to be the end of the cipher stream chain of components.
#   We 'clock' by running the function 'step' of the output Component with some time t. This will automatically update all the other components to the current time step, t
# * Below is the definition of the component class and a few subclasses.
# * Below that there are a couple of examples of circuits made using this premise.
# 
# * Todo:
# 
#     - Write extra components for the other elements of the E0 cipher stream generator.
#     - Convert components written so far to use the python bitstring module instead of integers.
#     - Links components together to form the E0 cipher stream generator.
#     
# 
#     

from bitstring import *

class Component(object):
    """
    Component Class:
        Each component rerpresents some digital logic component.
        We arrange them together as neccesary.
    """
    step = -1
    def __init__(self, args):
        self.inputs = list(args)
        self.outputs=[]
    
    def step(self,t):
        #print "Running step " + str(t) + "of class " + self.__class__.__name__
        for component in self.inputs:
            while(len(component.outputs) <= t):
                component.step(len(component.outputs))
        self.outputs.append(self.stepfunc(t))
    
    def stepfunc(self, t):
        pass
    
    def __str__(self):
        return str(self.outputs)
    
    def addInput(self, input):
        self.inputs.append(input)
        
class Slice(Component):
    """
    Slice Class
    """
    
    def __init__(self,start,end,input):
        self.start =start
        self.end= end
        super(Slice,self).__init__([input])
        
    def stepfunc(self,t):
        return self.inputs[0].outputs[t][self.start:self.end]
    
class Source(Component):
    """
ource Class:
        A component with no dependants. Always outputs a fixed value.
    """
    
    def __init__(self, value):
        self.inputs =[]
        self.outputs = []
        self.value = value
        
    def stepfunc(self, t):
        return self.value

class Output(Component):
    """
    Output Class:
        A component that depends(eventually) on all other components, but will typically depend on only one directly.
        The 'answer' is read from this component.
    """
    def __init__(self,*args):
        super(Output,self).__init__(args)
        
    def stepfunc(self, t):
        return self.inputs[0].outputs[t] # Return what its dependant  outputs
    
class Adder(Component):
    """
    Adder Class:
        Adds up its components.m
    """
    
    def __init__(self, outputLength, *inputs):
        self.outputLength = outputLength
        super(Adder,self).__init__(inputs)
      
    def stepfunc(self, t):
        #print [x.outputs[t] for x in self.inputs]
        total = 0
        for i in self.inputs:
            total+= i.outputs[t].uint
        return Bits(uint = total % 2**self.outputLength, length= self.outputLength)
    
class Delay(Component):
    """
    Delay Class:
        Output[t] = input.output[t-1]
    """
    #def __init__(self):
     #   super(Delay, self).__init__([])
      #  self.outputs.append(0)

    def __init__(self, initialValue, arg):
        super(Delay, self).__init__([arg])
        self.outputs = [initialValue]  
    
    def step(self,t):
        while(len(self.inputs[0].outputs) <= t-1):
            self.inputs[0].step(len(self.inputs[0].outputs))
        self.outputs.append(self.stepfunc(t))
            
    def stepfunc(self, t):
        return self.inputs[0].outputs[t-1]

class ExclusiveOR(Component):
    """
    Exclusive_OR Class:
        Performs Exclusive-OR operation on inputs
    """
    
    def __init__(self, *args):
        super(ExclusiveOR,self).__init__(args)

    def xor(self, sequence):
        return reduce(lambda a,b: a^b, sequence)
      
    def stepfunc(self, t):
        return self.xor([x.outputs[t] for x in self.inputs])

class Divider(Component):
    """
    Divider Class:
        Performs division by 2 operation on input
    """
    
    def __init__(self,outputLength, *arg):
        self.outputLength = outputLength
        super(Divider, self).__init__(arg)
      
    def stepfunc(self, t):        
        div = self.inputs[0].outputs[t] >> 1
        return Bits(uint = div.uint % 2**self.outputLength, length = self.outputLength)

class Bijection(Component):
    """
    Bijection Class:
        A bijection on some input
    """

    #Static class variables
    T1_values = [Bits('0b00'), Bits('0b01'), Bits('0b10'), Bits('0b11')]
    T2_values = [Bits('0b00'), Bits('0b11'), Bits('0b01'), Bits('0b10')]
    
    def __init__(self,b, arg):
        self.bijection = b
        super(Bijection, self).__init__([arg])
      
    def stepfunc(self, t):   
        return self.bijection[self.inputs[0].outputs[t].uint]
       

class DynamicSource(Component):
    """
    A dynamic Source class, returns a bit from dynamic until it runs out, then returns the same bit, static repeatedly
    """
    def __init__(self, dynamic, static):
        self.dynamic = dynamic
        self.static = static
        super(DynamicSource, self).__init__([])
        
    def stepfunc(self,t):
        return Bits(uint= int(self.dynamic[t]), length= 1) if t < len(self.dynamic) else self.static
        

class LFSR(Component):
    """LFSR Class """
    
    def __init__(self, dsource, mask, output_bit):
        self.mask = mask
        self.register = BitArray(uint = 0, length = len(mask))
        self.outputBit = output_bit
        super(LFSR, self).__init__([dsource])
        
    def stepfunc(self, t):
        inputBit = self.inputs[0].outputs[t]
        taps = list(self.mask.findall([1]))
        xorInputs = [self.register[tap] for tap in taps]
        
        retval = self.register[self.outputBit:self.outputBit+1]
        if(t >= len(self.mask)):
           xorInputs.append(bool(inputBit))
           input = Bits(uint = int(reduce(lambda x,y: x^y, xorInputs)), length = 1)
        else:
            input = inputBit
        self.register >>=1
        self.register = self.register | (input * len(self.register)) << (len(self.register) -1)
        return retval
    
class LFSRA(Component):
    """
    LFSR Class:
        Models an LFSR
    """

    #Static class variables
    LFSR1_mask = Bits(uint = 139297, length = 25)        #Bits('0b0000000100010000000100001')                L1
    LFSR2_mask = Bits(uint = 557185, length = 31)        #Bits('0b0000000000010001000000010000001')          L2
    LFSR3_mask = Bits(uint = 536871457, length = 33)     #Bits('0b000100000000000000000001000100001')        L3
    LFSR4_mask = Bits(uint = 34359740425, length = 39)   #Bits('0b000100000000000000000000000100000001001')  L4
    INITIALISATION = 0
    NORMAL = 1
    OPERATION_MODE = NORMAL                             # default operation mode is normal operation
    MASK_TYPE = 0
    
    def __init__(self, seed, LFSR_type):
        super(LFSR,self).__init__([])
        self.result = self.LFSR_func(seed, LFSR_type)
        self.counter = 0

    @classmethod
    def lfsr1(cls, seed):
        cls.MASK_TYPE = 1
        return cls(seed, LFSR.LFSR1_mask)

    @classmethod
    def lfsr2(cls, seed):
        cls.MASK_TYPE = 2
        return cls(seed, LFSR.LFSR2_mask)   

    @classmethod
    def lfsr3(cls, seed):
        cls.MASK_TYPE = 3
        return cls(seed, LFSR.LFSR3_mask)

    @classmethod
    def lfsr4(cls, seed):
        cls.MASK_TYPE = 4
        return cls(seed, LFSR.LFSR4_mask)

    def initialise(self, arg):        
        self.input_bit = arg.value        

    def LFSR_func(self, shift_register, tap_mask):
        MSB_bit = 0
        #counter = 0
        while True:
            taps = tap_mask.findall([1])
            list_of_taps = list(taps)                                   #get indices of taps
            xor_inputs = [shift_register[tap] for tap in list_of_taps]  #use tap indices for XOR operation
            def xor_operation(a,b): return a^b

            if not (self.OPERATION_MODE):
                # In initialisation operation, input bit is used to update LFSR MSB
                # Switch is closed when first input bit reaches right most position of LFSR
                # When switch is closed, input bit is XOR'd with xor operation result to give
                # new MSB_bit (not sure why I'm commenting this; seems obvious from code already!)
                if (self.counter < tap_mask.len):
                    MSB_bit = self.input_bit.int
                elif (self.counter >= tap_mask.len):
                    xor_result_bit = reduce(xor_operation, xor_inputs)
                    MSB_bit = self.input_bit.int ^ xor_result_bit
                
            elif (self.OPERATION_MODE):
                # In normal operation, XOR operation is used to update MSB bit of LFSR
                #xor_inputs = [shift_register[tap] for tap in list_of_taps]  #use tap indices for XOR operation
                #def xor_operation(a,b): return a^b
                xor_result_bit = reduce(xor_operation, xor_inputs)
                MSB_bit = xor_result_bit

            #MSB bit has to be made same length as shift register to enable OR operation
            xor_result_bit_shifted = Bits(uint = MSB_bit, length = shift_register.len)

            #Concatenate MSB and shift register (right shift LFSR)
            shift_register_update = (shift_register >> 1) | (xor_result_bit_shifted << (shift_register.len - 1))
            
            shift_register = shift_register_update
            
            #Select output bit depending on LFSR type
            if (self.MASK_TYPE == 1) | (self.MASK_TYPE == 2):
                X = shift_register[23]
            elif (self.MASK_TYPE == 3) | (self.MASK_TYPE == 4):
                X = shift_register[31]

            if (X):
                X_bit = Bits('0b01')
            elif not (X):
                X_bit = Bits('0b00')
            yield int(MSB_bit)#, X_bit.int , shift_register.bin
            self.counter += 1

    def step(self,t):
        if(len(self.outputs) == t):
            self.outputs.append(self.result.next())
      
    def stepfunc(self, t):
        return self.outputs.append(self.result.next())