# CA_Functions.py
# File of Basic Functions for 1D CA's

# import modules
from numpy import *

# Define ReverseArray(Array, N)
# Function to reverse the order of elements in a 1d array of integers, i.e. [1,0,3,4] --> [4,3,0,1]
def ReverseArray(Array, N):
    ReversedArray = zeros(N, int)
    
    for n in xrange(N):
        m = N - 1 - n
        ReversedArray[n] = Array[m]
        
    return ReversedArray        


# Define DetermineRule(Range, RuleNumber)
# Function to determine the lookup table for a CA with a binary alphabet {0,1}
# based on its neighborhood range (i.e. 1 for an ECA) and Rule Number (i.e. 54).

def DetermineRule(Range, RuleNumber):
    NumInputs = 2**(2*Range + 1)
    lookup_table = []

    for n in xrange(NumInputs):
        m = (RuleNumber*1.0)/(2**n)

        if mod(m,2) == 1.0:
            lookup_table.append(1)
            RuleNumber = RuleNumber - 2**n
    
        else:
            lookup_table.append(0)
        
    return lookup_table



# Define IterateState(state, N, Range, lookup_table).
# Updates the current state of the 1D lattice with N cells using the given CA lookup table. 

def IterateState(state, N, Range, lookup_table):
    nextstate = 7*ones(N,int)
    
    # loop over all cells
    for n in xrange(N):
        ConvertedValue = 0

        # loop over the cell's "neighborhood"
        for k in xrange(2*Range + 1):
            cell = mod( n - Range + k, N)
            scale_factor = 2**(2*Range - k)
            ConvertedValue = ConvertedValue + scale_factor*state[cell]
            ConvertedValue = int(ConvertedValue)
        
        if lookup_table[ConvertedValue] == 0: nextstate[n] = 0
        elif lookup_table[ConvertedValue] == 1: nextstate[n] = 1

    return nextstate


# Define construct_update_transducer(RuleNumber) - only works for Range 1 CAs (i.e. ECAs)
# The Transducer is given as list of states of the from

#                     IS 0        IS 1        
#
#    [accept?, [ [[OS],TSN], [[OS],TSN] ]    ]      <-- Transducer State X
def construct_update_transducer(RuleNumber):
    lookup_table = DetermineRule(1,RuleNumber)
    print 'lookup_table = ', lookup_table
    
    s_000 = lookup_table[0]
    s_001 = lookup_table[1]
    s_010 = lookup_table[2]
    s_011 = lookup_table[3]
    s_100 = lookup_table[4]
    s_101 = lookup_table[5]
    s_110 = lookup_table[6]
    s_111 = lookup_table[7]

    T = [    [1,[ [[2],1],[[2],2] ]], [1,[ [[2],3],[[2],4] ]], [1,[ [[2],5],[[2],6] ]], [1,[ [[s_000],3],[[s_001],4] ]], [1,[ [[s_010],5],[[s_011],6] ]], [1,[ [[s_100],3],[[s_101],4] ]], [1,[ [[s_110],5],[[s_111],6] ]]    ]
    return T


# Define FilterState(state, N, Filter)
# Takes in the current state of a 1D CA with N cells (i.e. a length N string) and applies a transducer to it
# to produce a filtered length N string, in which each cell is identified with a particle domain or wall type.
# The transducer is called 'Filter'. It is a list of lists of lists with the following form:

# The Filter Format: 
#
#           IS 0        IS 1        IS 2 
#
# [    [ [OS,MSN,W], [OS,MSN,W], [OS,MSN,W] ]       MS 0
#      [ [OS,MSN,W], [OS,MSN,W], [OS,MSN,W] ]       MS 1
#      [ [OS,MSN,W], [OS,MSN,W], [OS,MSN,W] ]       MS 2
#      [ [OS,MSN,W], [OS,MSN,W], [OS,MSN,W] ]   ]   MS 3

# Here  IS = input symbol
#       OS = output symbol(s)
#       MS = Machine State (current)
#       MSN = Machine State Next
#       W = wall (0 if did not just write a wall, 1 if did just write a wall)


# Filter[a][b][c] = Filter[MS][IS][OS = 0, MNS = 1, wall = 2]
# Note: Output Symbol is actually itself a list of 0 or more elements.

def FilterState(state, N, Filter):
    
    FilteredOutput = zeros(N, int)
    LengthFO = 0
    CurrentOutputPosition = -1
    machine_state = 0
    n = -1
    m = 0
    go = 1
        
    while go == 1:

        n = mod(n + 1,N)        # the cell we are at
        m = m + 1               # the counter, if goes to long must end loop
        if m > 2*N:
            go = 0
            print 'Error: did not filter in 2 passes of data'

        # (0) get the input symbol
        input_symbol = state[n]
        
        # (1) the output symbol(s)
        output_symbols = Filter[machine_state][input_symbol][0]
        
        if output_symbols == []:
            CurrentOutputPosition = mod(CurrentOutputPosition + 1, N)

        if output_symbols != []:
            L = len(output_symbols)
            LengthFO = LengthFO + L
        
            for k in range(L):
                CurrentOutputPosition = mod(CurrentOutputPosition + 1, N) 
                FilteredOutput[CurrentOutputPosition] = output_symbols[k]
        
        # (2) the next machine state
        machine_state = Filter[machine_state][input_symbol][1]

        # (3) did we just write a wall as part of the output ?
        wall = Filter[machine_state][input_symbol][2]

        # (4) should we stop, are we done 'tranducing' output?
        if LengthFO >= N and wall == 1: go = 0


    return FilteredOutput


# NOTE TO SELF : This program uses output symbols as a list eventhough all the filters currently only
# have 1 or 0 output symbols for each MS-IS pair. This makes the function slightly more complicated,
# but allows for easier integration later if we have a transducer that wants to write multiple output
# symbols at one time. BUT, must use different 'blank list notation' for before and after the transducer
# is synchronized. Currently [] --> skip to next position in FilteredOutput array. This is what we want
# for synchronization states, but not for intermediate states at which the transducer writes no output.


#Range = 1
#RuleNumber = 54
#RuleNumber = 18

#lookup_table = DetermineRule(Range, RuleNumber)
#print 'lookup_table =', lookup_table
        
#T = construct_update_transducer(RuleNumber)
#print 'T =', T
            

        

        
        
        
    
    
    


        
    



