"""
 even_1_automata.py

 A python implementation of an automaton which accepts 
 strings of 0's and 1's which have an even number of 1's.

 $ python even.py 
 -- testing the evens.py DFA -- 
 ''           => accept    PASS    
 '1'          => reject    PASS    
 '0'          => accept    PASS    
 '000000'     => accept    PASS    
 '010101010'  => accept    PASS    
 '110001'     => reject    PASS    
 '111'        => reject    PASS    
 '1110111'    => accept    PASS 

 Tested with python 3.5.

Jim Mahoney | cs.marlboro.college | Sep 2019 | MIT License
"""

class Automaton (object):

    def __init__(self):
    
        self.alphabet = ('0', '1')
        self.start_state = 'even'
        self.accept_states = [ 'even' ]
    
        self.transitions = { ('1', 'even') : 'odd',
                             ('0', 'even') : 'even',
                             ('1', 'odd')  : 'even',
                             ('0', 'odd')  : 'odd',
                             }

        self.tests = [ ('', True),
                       ('1', False),
                       ('0', True),
                       ('000000', True),
                       ('010101010', True),
                       ('110001', False),
                       ('111', False),
                       ('1110111', True),
                       ]

    def simulate(self, word):
        """ Return True/False when the word is accepts/rejected. """
        state = self.start_state
        for symbol in word:
            if symbol not in self.alphabet:
                return False
            if (symbol, state) not in self.transitions:
                return False
            state = self.transitions[ (symbol, state) ]
        return state in self.accept_states

    def print_tests(self):
        print(" -- testing the automaton -- ")
        for (word, outcome) in self.tests:
            accepts = 'accept' if self.simulate(word) else 'reject'
            result = 'PASS' if outcome == self.simulate(word) else 'FAIL'
            word = "'{}'".format(word)
            print(' {:12} => {:8}  {:8}'.format(word, accepts, result))

if __name__ == '__main__':
    Automaton().print_tests()