truth table: Binary Operations 3


requirements

Binary Operations 2


preview

These are the tests I have at the end of the chapter

truth_table/tests/test_binary.py
 1import src.truth_table
 2import unittest
 3
 4
 5class TestBinaryOperations(unittest.TestCase):
 6
 7    def test_contradiction(self):
 8        contradiction = src.truth_table.contradiction
 9        self.assertFalse(contradiction(True, True))
10        self.assertFalse(contradiction(True, False))
11        self.assertFalse(contradiction(False, True))
12        self.assertFalse(contradiction(False, False))
13
14    def test_logical_conjunction(self):
15        logical_conjunction = (
16            src.truth_table.logical_conjunction
17        )
18        self.assertTrue(logical_conjunction(True, True))
19        self.assertFalse(logical_conjunction(True, False))
20        self.assertFalse(logical_conjunction(False, True))
21        self.assertFalse(logical_conjunction(False, False))
truth_table/tests/test_binary.py
23    def test_project_second(self):
24        project_second = src.truth_table.project_second
25        self.assertTrue(project_second(True, True))
26        self.assertFalse(project_second(True, False))
27        self.assertTrue(project_second(False, True))
28        self.assertFalse(project_second(False, False))
29
30    def test_converse_non_implication(self):
31        converse_non_implication = (
32            src.truth_table.converse_non_implication
33        )
34        self.assertFalse(converse_non_implication(True, True))
35        self.assertFalse(converse_non_implication(True, False))
36        self.assertTrue(converse_non_implication(False, True))
37        self.assertFalse(converse_non_implication(False, False))
truth_table/tests/test_binary.py
39    def test_negate_first(self):
40        negate_first = src.truth_table.negate_first
41        self.assertFalse(negate_first(True, True))
42        self.assertFalse(negate_first(True, False))
43        self.assertTrue(negate_first(False, True))
44        self.assertTrue(negate_first(False, False))
45
46    def test_logical_nand(self):
47        nand = src.truth_table.logical_nand
48        self.assertFalse(nand(True, True))
49        self.assertTrue(nand(True, False))
50        self.assertTrue(nand(False, True))
51        self.assertTrue(nand(False, False))
truth_table/tests/test_binary.py
53    def test_tautology(self):
54        tautology = src.truth_table.tautology
55        self.assertTrue(tautology(True, True))
56        self.assertTrue(tautology(True, False))
57        self.assertTrue(tautology(False, True))
58        self.assertTrue(tautology(False, False))
59
60    def test_logical_disjunction(self):
61        logical_disjunction = (
62            src.truth_table.logical_disjunction
63        )
64        self.assertTrue(logical_disjunction(True, True))
65        self.assertTrue(logical_disjunction(True, False))
66        self.assertTrue(logical_disjunction(False, True))
67        self.assertFalse(logical_disjunction(False, False))
truth_table/tests/test_binary.py
69    def test_exclusive_disjunction(self):
70        xor = src.truth_table.exclusive_disjunction
71        self.assertFalse(xor(True, True))
72        self.assertTrue(xor(True, False))
73        self.assertTrue(xor(False, True))
74        self.assertFalse(xor(False, False))
75
76    def test_material_non_implication(self):
77        material_non_implication = (
78            src.truth_table.material_non_implication
79        )
80        self.assertFalse(material_non_implication(True, True))
81        self.assertTrue(material_non_implication(True, False))
82        self.assertFalse(material_non_implication(False, True))
83        self.assertFalse(material_non_implication(False, False))
truth_table/tests/test_binary.py
 85    def test_project_first(self):
 86        project_first = src.truth_table.project_first
 87        self.assertTrue(project_first(True, True))
 88        self.assertTrue(project_first(True, False))
 89        self.assertFalse(project_first(False, True))
 90        self.assertFalse(project_first(False, False))
 91
 92    def test_converse_implication(self):
 93        converse_implication = (
 94            src.truth_table.converse_implication
 95        )
 96        self.assertTrue(converse_implication(True, True))
 97        self.assertTrue(converse_implication(True, False))
 98        self.assertFalse(converse_implication(False, True))
 99        self.assertTrue(converse_implication(False, False))
100
101
102# Exceptions seen
103# AttributeError
104# TypeError
105# AssertionError
106# SyntaxError

open the project

  • Make sure you are in the pumping_python folder with pwd in the terminal

    pwd
    

    if the terminal does not show

    .../pumping_python
    

    change directory to the pumping_python folder

  • Once in pumping_python, change directory to the project

    cd truth_table
    

    the terminal shows

    .../pumping_python/truth_table
    
  • I run the tests with pytest-watcher

    uv run pytest-watcher . --now
    

    the terminal is my friend, and shows

    rootdir: .../pumping_python/truth_table
    configfile: pyproject.toml
    collected 12 items
    
    tests/test_binary.py ........                      [ 66%]
    tests/test_nullary_unary.py ....                   [100%]
    
    ================== 12 passed in G.HIs ===================
    
  • I hold ctrl (Windows) or option (MacOS) on the keyboard, then click on tests/test_binary.py with the mouse to open it

  • Up to this point I have tested


test_exclusive_disjunction

The truth table for exclusive_disjunction is

first input

second input

return

True

True

False

True

False

True

False

True

True

False

False

False


RED: make it fail


I add a test for exclusive_disjunction with an assertion for the case when the first input is True and the second input is True, to test_binary.py

first input

second input

return

True

True

False

67        self.assertFalse(logical_disjunction(False, False))
68
69    def test_exclusive_disjunction(self):
70        xor = src.truth_table.exclusive_disjunction
71        self.assertFalse(xor(True, True))
72
73
74# Exceptions seen

the terminal is my friend, and shows AttributeError

AttributeError: module 'src.truth_table'
                has no attribute 'exclusive_disjunction'

because truth_table.py does not have anything in it with that name.


GREEN: make it pass


  • I open truth_table/__init__.py from the src folder

  • I add exclusive_disjunction to truth_table.py

    52def logical_disjunction(first_input, second_input):
    53    return logical_negation(
    54        logical_conjunction(
    55            logical_negation(first_input),
    56            logical_negation(second_input)
    57        )
    58    )
    59    return first_input or second_input
    60
    61
    62def exclusive_disjunction(first_input, second_input):
    63    return False
    

    the test passes. exclusive_disjunction returns False, if the first input is True and the second input is True.

    exclusive_disjunction(True , True ) -> False
    

REFACTOR: make it better


  • I add an assertion for the next case, which is if the first input is True and the second input is False, to test_exclusive_disjunction in test_binary.py

    first input

    second input

    return

    True

    False

    True

    69    def test_exclusive_disjunction(self):
    70        xor = src.truth_table.exclusive_disjunction
    71        self.assertFalse(xor(True, True))
    72        self.assertTrue(xor(True, False))
    73
    74
    75# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: False is not true
    

    because the function returns False and the assertion expects True.

  • I add an if statement to the exclusive_disjunction function in truth_table.py

    62def exclusive_disjunction(first_input, second_input):
    63    if second_input == False:
    64        return True
    65    return False
    

    the test passes. exclusive_disjunction returns

    • True, if the second input is False

    • False, if the above condition is NOT met

    exclusive_disjunction(True , False) -> True
    exclusive_disjunction(True , True ) -> False
    
  • I add an assertion for the third case, which is if the first input is False and the second input is True, to test_binary.py

    first input

    second input

    return

    False

    True

    True

    69    def test_exclusive_disjunction(self):
    70        xor = src.truth_table.exclusive_disjunction
    71        self.assertFalse(xor(True, True))
    72        self.assertTrue(xor(True, False))
    73        self.assertTrue(xor(False, True))
    74
    75
    76# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: False is not true
    

    because Python checks if second_input is equal to False when if second_input == False: runs

  • I add an if statement to exclusive_disjunction in truth_table.py

    62def exclusive_disjunction(first_input, second_input):
    63    if first_input == False:
    64        return True
    65    if second_input == False:
    66        return True
    67    return False
    

    the test passes because Python checks if first_input is equal to False when if first_input == False: runs

  • I add an assertion for the last case, which is if the first input is False and the second input is False to test_exclusive_disjunction, in test_binary.py

    first input

    second input

    return

    False

    False

    False

    69    def test_exclusive_disjunction(self):
    70        xor = src.truth_table.exclusive_disjunction
    71        self.assertFalse(xor(True, True))
    72        self.assertTrue(xor(True, False))
    73        self.assertTrue(xor(False, True))
    74        self.assertFalse(xor(False, False))
    75
    76
    77# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: True is not false
    

    because Python checks if first_input is equal to False when if first_input == False: runs

    • if first_input is equal to False, it goes to the next line - return True

    • if first_input is NOT equal to False, it leaves the if statement and continues to the next if statement at the same indentation level in the function - if second_input == False:, which checks if second_input is equal to False

      • if second_input is NOT equal to False, it leaves the if statement and continues to run the rest of the function - return False

      • if second_input is equal to False, it goes to the next line - return True

      • second_input is False in this case, which raises AssertionError since the function returns True and the assertion expects False

  • I add an if statement for this case, to the one for when the first input is False in the exclusive_disjunction function in truth_table.py

    62def exclusive_disjunction(first_input, second_input):
    63    if first_input == False:
    64        if second_input == False:
    65            return False
    66        return True
    67    if second_input == False:
    68        return True
    69    return False
    

    the test passes because Python checks if first_input is equal to False when if first_input == False: runs

    • if first_input is NOT equal to False, it leaves the if statement and continues to the next if statement at the same indentation level in the function - if second_input == False:, which checks if second_input is equal to False

      • if second_input is NOT equal to False, it leaves the if statement and continues to run the rest of the function - return False

        exclusive_disjunction(True , True ) -> False
        └── def exclusive_disjunction(first_input, second_input):
            ├── first_input  == True
            ├── second_input == True
            ├── if first_input == False:
                   if second_input == False:
                       return False
                   return True
            ├── if second_input == False:
                   return True
            └── return False
        
      • if second_input is equal to False, it goes to the next line - return True

        exclusive_disjunction(True , False) -> True
        └── def exclusive_disjunction(first_input, second_input):
            ├── first_input  == True
            ├── second_input == False
            ├── if first_input == False:
                   if second_input == False:
                       return False
                   return True
            └── if second_input == False:
                └── return True
                return False
        
    • if first_input is equal to False, it goes to the next line - if second_input == False, which checks if if second_input is equal to False

      • if second_input is NOT equal to False, it leaves the if statement and continues to the next line for if first_input == False: - return True

        exclusive_disjunction(False, True ) -> True
        └── def exclusive_disjunction(first_input, second_input):
            ├── first_input  == False
            ├── second_input == True
            └── if first_input == False:
                ├── if second_input == False:
                       return False
                └── return True
                if second_input == False:
                    return True
                return False
        
      • if second_input is equal to False, it goes to the next line - return False

        exclusive_disjunction(False, False) -> False
        └── def exclusive_disjunction(first_input, second_input):
            ├── first_input  == False
            ├── second_input == False
            └── if first_input == False:
                └── if second_input == False:
                    └── return False
                    return True
                if second_input == False:
                    return True
                return False
        
  • there are two cases where exclusive_disjunction returns False and two cases where it returns True. I add an if statement for the other case where it returns True, to make it clearer

    62def exclusive_disjunction(first_input, second_input):
    63    if first_input == False:
    64        if second_input == False:
    65            return False
    66        return True
    67    if first_input == True:
    68        if second_input == False:
    69            return True
    70    return False
    

    the test is still green because Python checks if first_input is equal to False when if first_input == False: runs

  • I add the bool built-in function

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    if bool(first_input) == False:
    65        # if second_input == False:
    66        if bool(second_input) == False:
    67            return False
    68        return True
    69    # if first_input == True:
    70    if bool(first_input) == True:
    71        # if second_input == False:
    72        if bool(second_input) == False:
    73            return True
    74    return False
    

    still green.

  • I use Logical Negation (NOT) to write three of the if statements in terms of True

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    # if bool(first_input) == False:
    65    if not bool(first_input) == True:
    66        # if second_input == False:
    67        # if bool(second_input) == False:
    68        if not bool(second_input) == True:
    69            return False
    70        return True
    71    # if first_input == True:
    72    if bool(first_input) == True:
    73        # if second_input == False:
    74        # if bool(second_input) == False:
    75        if not bool(second_input) == True:
    76            return True
    77    return False
    

    green.

  • I remove == True

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    # if bool(first_input) == False:
    65    # if not bool(first_input) == True:
    66    if not bool(first_input):
    67        # if second_input == False:
    68        # if bool(second_input) == False:
    69        # if not bool(second_input) == True:
    70        if not bool(second_input):
    71            return False
    72        return True
    73    # if first_input == True:
    74    # if bool(first_input) == True:
    75    if bool(first_input):
    76        # if second_input == False:
    77        # if bool(second_input) == False:
    78        # if not bool(second_input) == True:
    79        if not bool(second_input):
    80            return True
    81    return False
    

    still green.

  • I remove bool

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    # if bool(first_input) == False:
    65    # if not bool(first_input) == True:
    66    # if not bool(first_input):
    67    if not first_input:
    68        # if second_input == False:
    69        # if bool(second_input) == False:
    70        # if not bool(second_input) == True:
    71        # if not bool(second_input):
    72        if not second_input:
    73            return False
    74        return True
    75    # if first_input == True:
    76    # if bool(first_input) == True:
    77    # if bool(first_input):
    78    if first_input:
    79        # if second_input == False:
    80        # if bool(second_input) == False:
    81        # if not bool(second_input) == True:
    82        # if not bool(second_input):
    83        if not second_input:
    84            return True
    85    return False
    

    the test is still green, because

    • Python checks if something is equal to False when if something == False: runs. I can assume the following substitutions

      • if the value of something is False

        something = False
        
        if something       == False
        if bool(something) == False
        if bool(False    ) == False # use the value
        if False           == False # bool(False) returns False
        if True            == True  # write in terms of True
        if True                     # remove '== True'
        if not False                # not False == True
        if not something            # use the name for the value
        
      • if the value of something is True

        something = True
        
        if something       == False
        if bool(something) == False
        if bool(True     ) == False # use the value
        if True            == False # bool(True) returns True
        if False           == True  # write in terms of True
        if not True        == True  # write in terms of True
        if not True                 # remove '== True'
        if not something            # use the name for the value
        
    • Python checks if (something) is equal to True when if something == True: runs. I can assume the following substitutions

      • if the value of something is False

        something = False
        
        if something       == True
        if bool(something) == True
        if bool(False    ) == True # use the value
        if False           == True # bool(False) returns False
        if not True        == True  # write in terms of True
        if not True                 # remove '== True'
        if False                    # not True == False
        if something                # use the name for the value
        
      • if the value of something is True

        something = True
        
        if something       == True
        if bool(something) == True
        if bool(True     ) == True # use the value
        if True            == True # bool(True) returns True
        if True                    # remove '== True'
        if something               # use the name for the value
        
    • if bool(something) == False is the same as if not bool(something) == True is the same as if not bool(something) is the same as if not something.

    • if bool(something) == True is the same as if bool(something) is the same as if something.

  • I move the if statements to put them together

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    # if bool(first_input) == False:
    65    # if not bool(first_input) == True:
    66    # if not bool(first_input):
    67        # if second_input == False:
    68        # if bool(second_input) == False:
    69        # if not bool(second_input) == True:
    70        # if not bool(second_input):
    71    # if first_input == True:
    72    # if bool(first_input) == True:
    73    # if bool(first_input):
    74        # if second_input == False:
    75        # if bool(second_input) == False:
    76        # if not bool(second_input) == True:
    77        # if not bool(second_input):
    78    if not first_input:
    79        if not second_input:
    80            return False
    81        return True
    82    if first_input:
    83        if not second_input:
    84            return True
    85    return False
    

    still green.

  • I add if statements for the other two cases to make it clearer

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    # if bool(first_input) == False:
    65    # if not bool(first_input) == True:
    66    # if not bool(first_input):
    67        # if second_input == False:
    68        # if bool(second_input) == False:
    69        # if not bool(second_input) == True:
    70        # if not bool(second_input):
    71    # if first_input == True:
    72    # if bool(first_input) == True:
    73    # if bool(first_input):
    74        # if second_input == False:
    75        # if bool(second_input) == False:
    76        # if not bool(second_input) == True:
    77        # if not bool(second_input):
    78    if not first_input:
    79        if not second_input:
    80            return False
    81        if second_input:
    82            return True
    83    if first_input:
    84        if not second_input:
    85            return True
    86        if second_input:
    87            return False
    

    green.

  • I use Logical Conjunction (AND) to write the if statements for all the cases

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    # if bool(first_input) == False:
    65    # if not bool(first_input) == True:
    66    # if not bool(first_input):
    67        # if second_input == False:
    68        # if bool(second_input) == False:
    69        # if not bool(second_input) == True:
    70        # if not bool(second_input):
    71    # if first_input == True:
    72    # if bool(first_input) == True:
    73    # if bool(first_input):
    74        # if second_input == False:
    75        # if bool(second_input) == False:
    76        # if not bool(second_input) == True:
    77        # if not bool(second_input):
    78    # if not first_input:
    79    #     if not second_input:
    80    #         return False
    81    #     if second_input:
    82    #         return True
    83    # if first_input:
    84    #     if not second_input:
    85    #         return True
    86    #     if second_input:
    87    #         return False
    88    if not first_input and not second_input:
    89        return False
    90    if not first_input and second_input:
    91        return True
    92    if first_input and not second_input:
    93        return True
    94    if first_input and second_input:
    95        return False
    

    still green, because I can put two if statements together when one is indented under the other

    if something:
        if something_else:
    

    can also be written as

    if something and something_else:
    
  • I move the first if statement to the bottom to be with the other statement that returns the same thing (False)

    62def exclusive_disjunction(first_input, second_input):
    63    # if first_input == False:
    64    # if bool(first_input) == False:
    65    # if not bool(first_input) == True:
    66    # if not bool(first_input):
    67        # if second_input == False:
    68        # if bool(second_input) == False:
    69        # if not bool(second_input) == True:
    70        # if not bool(second_input):
    71    # if first_input == True:
    72    # if bool(first_input) == True:
    73    # if bool(first_input):
    74        # if second_input == False:
    75        # if bool(second_input) == False:
    76        # if not bool(second_input) == True:
    77        # if not bool(second_input):
    78    # if not first_input:
    79    #     if not second_input:
    80    #         return False
    81    #     if second_input:
    82    #         return True
    83    # if first_input:
    84    #     if not second_input:
    85    #         return True
    86    #     if second_input:
    87    #         return False
    88    if not first_input and second_input:
    89        return True
    90    if first_input and not second_input:
    91        return True
    92    if first_input and second_input:
    93        return False
    94    if not first_input and not second_input:
    95        return False
    

    the test is still green.

  • I use Logical Disjunction (OR) to put the if statements that return the same thing together

     62def exclusive_disjunction(first_input, second_input):
     63    # if first_input == False:
     64    # if bool(first_input) == False:
     65    # if not bool(first_input) == True:
     66    # if not bool(first_input):
     67        # if second_input == False:
     68        # if bool(second_input) == False:
     69        # if not bool(second_input) == True:
     70        # if not bool(second_input):
     71    # if first_input == True:
     72    # if bool(first_input) == True:
     73    # if bool(first_input):
     74        # if second_input == False:
     75        # if bool(second_input) == False:
     76        # if not bool(second_input) == True:
     77        # if not bool(second_input):
     78    # if not first_input:
     79    #     if not second_input:
     80    #         return False
     81    #     if second_input:
     82    #         return True
     83    # if first_input:
     84    #     if not second_input:
     85    #         return True
     86    #     if second_input:
     87    #         return False
     88    # if not first_input and second_input:
     89    #     return True
     90    # if first_input and not second_input:
     91    if (
     92        (not first_input and second_input)
     93        or
     94        (first_input and not second_input)
     95    ):
     96        return True
     97    # if first_input and second_input:
     98    #     return False
     99    # if not first_input and not second_input:
    100    if (
    101        (first_input and second_input)
    102        or
    103        (not first_input and not second_input)
    104    ):
    105        return False
    

    still green, because I can put two if statements together if they both return the same thing and are at the same indentation level

    if something:
        return this
    if something_else:
        return this
    

    can also be written as

    if something or something_else:
        return this
    
  • I change the second if statement to an else clause

     91    if (
     92        (not first_input and second_input)
     93        or
     94        (first_input and not second_input)
     95    ):
     96        return True
     97    # if first_input and second_input:
     98    #     return False
     99    # if not first_input and not second_input:
    100    # if (
    101    #     (first_input and second_input)
    102    #     or
    103    #     (not first_input and not second_input)
    104    # ):
    105    else:
    106        return False
    

    green.

  • I add a conditional expression

     91    # if (
     92    #     (not first_input and second_input)
     93    #     or
     94    #     (first_input and not second_input)
     95    # ):
     96    #     return True
     97    # if first_input and second_input:
     98    #     return False
     99    # if not first_input and not second_input:
    100    # if (
    101    #     (first_input and second_input)
    102    #     or
    103    #     (not first_input and not second_input)
    104    # ):
    105    # else:
    106    #     return False
    107    return True if (
    108        (not first_input and second_input)
    109        or
    110        (first_input and not second_input)
    111    ) else False
    

    still green.

  • I remove True if and else False to make it simpler

    105    # else:
    106    #     return False
    107    # return True if (
    108    return (
    109        (not first_input and second_input)
    110        or
    111        (first_input and not second_input)
    112    # ) else False
    113    )
    

    the test is still green.

  • exclusive_disjunction returns ((not first_input and second_input) or (first_input and not second_input))

    • not first_input and second_input is the Logical Conjunction (AND), of the Logical Negation (NOT) of the first input, and the second input

      logical_conjunction(
          logical_negation(first_input),
          second_input
      )
      
    • not first_input is the Logical Negation (NOT) of first_input

      • if the first input is True, this part of the statement is False

      • if the first input is False, this part of the statement is True

    • first_input and not second_input is the Logical Conjunction (AND), of the first input, and the Logical Negation (NOT) of the second input

      logical_conjunction(
          first_input,
          logical_negation(second_input)
      )
      
    • not second_input is the Logical Negation (NOT) of second_input

      • if the second input is True, this part of the statement is False

      • if the second input is False, this part of the statement is True

    • ((not first_input and second_input) or (first_input and not second_input)) can be thought of as the Logical Disjunction (OR), of the Logical Conjunction (AND), of the Logical Negation (NOT) of the first input, and the second input, and the Logical Conjunction (AND) of the first input and the Logical Negation (NOT) of the second input. Confusing when written in English. If we use the function names we get

      logical_disjunction(
          logical_conjunction(
              logical_negation(first_input),
              second_input
          ),
          logical_conjunction(
              first_input,
              logical_negation(second_input)
          )
      )
      

      Exclusive Disjunction can also be thought of as the Logical Disjunction (OR), of the Converse Non-Implication of the first input and the second input, and the Logical Conjunction (AND) of the first input, and the Logical Negation (NOT) of the second input

      logical_disjunction(
          converse_non_implication(
              first_input, second_input
          ),
          logical_conjunction(
              first_input,
              logical_negation(second_input)
          )
      )
      

      because converse_non_implication returns not first_input and second_input

      logical_disjunction(first_input, second_input)
      
                   first_input == (not first_input and second_input)
      converse_non_implication == (not first_input and second_input)
      
                  second_input == (first_input and not second_input)
      

    This means that in the four cases

    • if the first input is True and the second input is True, exclusive_disjunction returns

      (not first and second) or (first and not second)
      (not True  and True  ) or (True  and not True  )
      (False     and True  ) or (True  and False     )
       False                 or  False
       False                 # logical_disjunction(False, False)
      
    • if the first input is True and the second input is False, exclusive_disjunction returns

      (not first and second) or (first and not second)
      (not True  and False ) or (True  and not False )
      (False     and False ) or (True  and True      )
       False                 or  True
       True                  # logical_disjunction(False, True)
      
    • if the first input is False and the second input is True, exclusive_disjunction returns

      (not first and second) or (first and not second)
      (not False and True  ) or (False and not True  )
      (True      and True  ) or (False and False     )
       True                  or  False
       True                  # logical_disjunction(True, False)
      
    • if the first input is False and the second input is False, exclusive_disjunction returns

      (not first and second) or (first and not second)
      (not False and False ) or (False and not False )
      (True      and False ) or (False and True      )
       False                 or  False
       False                 # logical_disjunction(False, False)
      

    first

    second

    not first

    not second

    ((not first) and second)

    (first and (not second))

    ((not first) and second) or (first and (not second))

    True

    True

    False

    False

    False

    False

    False

    True

    False

    False

    True

    False

    True

    True

    False

    True

    True

    False

    True

    False

    True

    False

    False

    True

    True

    False

    False

    False

    I add a return statement to show this

    107    # return True if (
    108    return logical_disjunction(
    109        converse_non_implication(
    110            first_input, second_input
    111        ),
    112        logical_conjunction(
    113            first_input,
    114            logical_negation(second_input)
    115        )
    116    )
    117    return (
    118        (not first_input and second_input)
    119        or
    120        (first_input and not second_input)
    121    # ) else False
    122    )
    

    the test is still green.

  • exclusive_disjunction returns False when the two inputs are equal, it returns True when the two inputs are NOT equal. I add an if statement to show this

    107    # return True if (
    108    if first_input == second_input:
    109        return False
    110    else:
    111        return True
    112    return logical_disjunction(
    113        converse_non_implication(
    114            first_input, second_input
    115        ),
    116        logical_conjunction(
    117            first_input,
    118            logical_negation(second_input)
    119        )
    120    )
    121    return (
    122        (not first_input and second_input)
    123        or
    124        (first_input and not second_input)
    125    # ) else False
    126    )
    

    green.

  • I change the else clause to the Logical Negation (NOT) of the new if statement so I can write a conditional expression

    107    # return True if (
    108    if first_input == second_input:
    109        return False
    110    # else:
    111    if not (first_input == second_input):
    112        return True
    113    return logical_disjunction(
    114        converse_non_implication(
    115            first_input, second_input
    116        ),
    117        logical_conjunction(
    118            first_input,
    119            logical_negation(second_input)
    120        )
    121    )
    122    return (
    123        (not first_input and second_input)
    124        or
    125        (first_input and not second_input)
    126    # ) else False
    127    )
    

    still green.

  • I add a conditional expression

    107    # return True if (
    108    # if first_input == second_input:
    109    #     return False
    110    # else:
    111    # if not (first_input == second_input):
    112    #     return True
    113    return (
    114        True if
    115        not (first_input == second_input)
    116        else False
    117    )
    118    return logical_disjunction(
    119        converse_non_implication(
    120            first_input, second_input
    121        ),
    122        logical_conjunction(
    123            first_input,
    124            logical_negation(second_input)
    125        )
    126    )
    127    return (
    128        (not first_input and second_input)
    129        or
    130        (first_input and not second_input)
    131    # ) else False
    132    )
    

    the test is still green.

  • I remove True if and else False to make the statement simpler

    107    # return True if (
    108    # if first_input == second_input:
    109    #     return False
    110    # else:
    111    # if not (first_input == second_input):
    112    #     return True
    113    return (
    114        # True if
    115        not (first_input == second_input)
    116        # else False
    117    )
    118    return logical_disjunction(
    119        converse_non_implication(
    120            first_input, second_input
    121        ),
    122        logical_conjunction(
    123            first_input,
    124            logical_negation(second_input)
    125        )
    126    )
    127    return (
    128        (not first_input and second_input)
    129        or
    130        (first_input and not second_input)
    131    # ) else False
    132    )
    

    still green.

  • I can also write the conditional expression with the NOT equal symbol (!=) (exclamation mark and equal symbol !+= on the keyboard)

    107    # return True if (
    108    # if first_input == second_input:
    109    #     return False
    110    # else:
    111    # if not (first_input == second_input):
    112    #     return True
    113    return first_input != second_input
    114    return (
    115        # True if
    116        not (first_input == second_input)
    117        # else False
    118    )
    119    return logical_disjunction(
    120        converse_non_implication(
    121            first_input, second_input
    122        ),
    123        logical_conjunction(
    124            first_input,
    125            logical_negation(second_input)
    126        )
    127    )
    128    return (
    129        (not first_input and second_input)
    130        or
    131        (first_input and not second_input)
    132    # ) else False
    133    )
    

    green.

  • I remove the commented lines

    62def exclusive_disjunction(first_input, second_input):
    63    return first_input != second_input
    64    return not (first_input == second_input)
    65    return logical_disjunction(
    66        converse_non_implication(
    67            first_input, second_input
    68        ),
    69        logical_conjunction(
    70            first_input,
    71            logical_negation(second_input)
    72        )
    73    )
    74    return (
    75        (not first_input and second_input)
    76        or
    77        (first_input and not second_input)
    78    )
    

    I can use any of these return statements. The first return statement is the only one that runs in this case, because the return statement is the last thing to run in a function.

  • I add a git commit message in the other terminal

    git commit -am 'add exclusive_disjunction'
    

Exclusive Disjunction returns

  • True, if first_input is NOT EQUAL to second_input.

  • False, if first_input is EQUAL to the second_input.

  • first_input != second_input - which reads as first_input is NOT equal to second_input.

  • not (first_input == second_input) - which can be read as the Logical Negation of the Logical Equality of the first input and the second input.

  • (not first_input and second_input) or (first_input and not second_input) which is the Logical Disjunction of the Logical Conjunction of the Logical Negation of the first input, and the second input, and the Logical Conjunction of the first input and the Logical Negation of the second input. Wow! That’s a lot.

Exclusive Disjunction is also known as Exclusive OR or XOR. Would “Logical Inequality” be a better name?


examples of Exclusive Disjunction


  • two light switches for one bulb, if the inputs are

    • is switch A on?

    • is switch B on?

    switch A?

    switch B?

    bulb on?

    on

    on

    no

    on

    off

    yes

    off

    on

    yes

    off

    off

    no

  • magnets attract, if the inputs are

    • what is the direction of magnet A?

    • what is the direction of magnet B?

    magnet A?

    magnet B?

    attract?

    north

    north

    no

    north

    south

    yes

    south

    north

    yes

    south

    south

    no

  • a coin toss, if the inputs are

    • my choice

    • your choice

    my choice

    your choice

    there is a winner

    heads

    heads

    no

    heads

    tails

    yes

    tails

    heads

    yes

    tails

    tails

    no

  • two people meet at a door that can only take one person at a time, if the inputs are

    • I go

    • You go

    I go

    you go

    we both go

    go

    go

    no

    go

    stop

    yes

    stop

    go

    yes

    stop

    stop

    no

  • A broken vending machine, if the inputs are

    • did I put money in the machine?

    • did I get what I picked?

    I paid

    I got what I want

    machine is broken

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    no

    should I complain when I did not pay and I got what I want?


test_material_non_implication

The truth table for material_non_implication is

first input

second input

return

True

True

False

True

False

True

False

True

False

False

False

False


RED: make it fail


  • I go back to the terminal where the tests are running

  • I add a test for material_non_implication with an assertion for if the first input is True and the second input is True, to test_binary.py

    first input

    second input

    return

    True

    True

    False

    74        self.assertFalse(xor(False, False))
    75
    76    def test_material_non_implication(self):
    77        material_non_implication = (
    78            src.truth_table.material_non_implication
    79        )
    80        self.assertFalse(material_non_implication(True, True))
    81
    82
    83# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.truth_table'
                    has no attribute 'material_non_implication'.
                    Did you mean: 'converse_non_implication'?
    

    material_non_implication is not in truth_table.py.


GREEN: make it pass


I add material_non_implication to truth_table.py

74    return (
75        (not first_input and second_input)
76        or
77        (first_input and not second_input)
78    )
79
80
81def material_non_implication(first_input, second_input):
82    return False

the test passes. material_non_implication returns False, if the first input is True and the second input is True.

material_non_implication(True , True ) -> False

REFACTOR: make it better


  • I add an assertion for the second case, which is if the first input is True and the second input is False to test_material_non_implication in test_binary.py

    first input

    second input

    return

    True

    False

    True

    76    def test_material_non_implication(self):
    77        material_non_implication = (
    78            src.truth_table.material_non_implication
    79        )
    80        self.assertFalse(material_non_implication(True, True))
    81        self.assertTrue(material_non_implication(True, False))
    82
    83
    84# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: False is not true
    

    because the function returns False and the assertion expects True.

  • I add an if statement to material_non_implication in truth_table.py

    81def material_non_implication(first_input, second_input):
    82    if second_input == False:
    83        return True
    84    return False
    

    the test passes because material_non_implication is called, it runs if second_input == False:

    • if second_input is NOT equal to False, it leaves the if statement then runs return False

    • if second_input is equal to False, it runs return True

    material_non_implication(True , False) -> True
    material_non_implication(True , True ) -> False
    
  • I add an assertion for the third case to test_material_non_implication, for when the first input is False and the second input is True, in test_binary.py

    first input

    second input

    return

    False

    True

    False

    76    def test_material_non_implication(self):
    77        material_non_implication = (
    78            src.truth_table.material_non_implication
    79        )
    80        self.assertFalse(material_non_implication(True, True))
    81        self.assertTrue(material_non_implication(True, False))
    82        self.assertFalse(material_non_implication(False, True))
    83
    84
    85# Exceptions seen
    

    the test is still green.

    material_non_implication(False, True ) -> False
    material_non_implication(True , False) -> True
    material_non_implication(True , True ) -> False
    
  • I add an assertion for the fourth case, which is if the first input is False and the second input is False

    first input

    second input

    return

    False

    False

    False

    76    def test_material_non_implication(self):
    77        material_non_implication = (
    78            src.truth_table.material_non_implication
    79        )
    80        self.assertFalse(material_non_implication(True, True))
    81        self.assertTrue(material_non_implication(True, False))
    82        self.assertFalse(material_non_implication(False, True))
    83        self.assertFalse(material_non_implication(False, False))
    84
    85
    86# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: True is not false
    

    because when material_non_implication is called, it runs if second_input == False:

    • if second_input is NOT equal to False, it leaves the if statement then runs return False

    • if second_input is equal to False, it runs return True

    • second_input is False in this case, which raises AssertionError since the assertion expects False and the function returns True

  • I add an if statement for the one case that returns True, to the material_non_implication function in truth_table.py

    81def material_non_implication(first_input, second_input):
    82    if first_input == True:
    83        if second_input == False:
    84            return True
    85    return False
    

    the test passes.

    material_non_implication(False, False) -> False
    material_non_implication(False, True ) -> False
    material_non_implication(True , False) -> True
    material_non_implication(True , True ) -> False
    

    When material_non_implication is called Python checks if first_input == True:

    • if first_input is NOT equal to True, it leaves the if statement then runs return False

      material_non_implication(False, False) -> False
      └── def material_non_implication(first_input, second_input):
          ├── first_input  == False
          ├── second_input == False
          ├── if first_input == True:
                 if second_input == False:
                     return True
          └── return False
      
      material_non_implication(False, True ) -> False
      └── def material_non_implication(first_input, second_input):
          ├── first_input  == False
          ├── second_input == True
          ├── if first_input == True:
                 if second_input == False:
                     return True
          └── return False
      
    • if first_input is equal to True, it runs if second_input == False:

      • if second_input is NOT equal to False, it leaves if first_input == True: then runs the next line in the function - return False

        material_non_implication(True , True ) -> False
        └── def material_non_implication(first_input, second_input):
            ├── first_input  == False
            ├── second_input == True
            └── if first_input == True:
            ┌───┴── if second_input == False:
                       return True
            └── return False
        
      • if second_input is equal to False, it runs return True

        material_non_implication(True , True ) -> False
        └── def material_non_implication(first_input, second_input):
            ├── first_input  == False
            ├── second_input == True
            └── if first_input == True:
                └── if second_input == False:
                    └── return True
                return False
        
    • it only checks the second input if the first input is True

  • I use the bool built-in function

    81def material_non_implication(first_input, second_input):
    82    # if first_input == True:
    83    if bool(first_input) == True:
    84        # if second_input == False:
    85        if bool(second_input) == False:
    86            return True
    87    return False
    

    the test is still green.

  • I use Logical Negation (NOT) to write the second if statement in terms of True

    81def material_non_implication(first_input, second_input):
    82    # if first_input == True:
    83    if bool(first_input) == True:
    84        # if second_input == False:
    85        # if bool(second_input) == False:
    86        if not bool(second_input) == True:
    87            return True
    88    return False
    

    still green.

  • I remove == True

    81def material_non_implication(first_input, second_input):
    82    # if first_input == True:
    83    # if bool(first_input) == True:
    84    if bool(first_input):
    85        # if second_input == False:
    86        # if bool(second_input) == False:
    87        # if not bool(second_input) == True:
    88        if not bool(second_input):
    89            return True
    90    return False
    

    green.

  • I remove bool

    81def material_non_implication(first_input, second_input):
    82    # if first_input == True:
    83    # if bool(first_input) == True:
    84    # if bool(first_input):
    85    if first_input:
    86        # if second_input == False:
    87        # if bool(second_input) == False:
    88        # if not bool(second_input) == True:
    89        # if not bool(second_input):
    90        if not second_input:
    91            return True
    92    return False
    

    still green, because

    • if bool(something) == True is the same as if bool(something) is the same as if something.

    • if bool(something) == False is the same as if not bool(something) == True is the same as if not bool(something) is the same as if not something.

  • I use Logical Conjunction (AND) to put the two if statements together

    81def material_non_implication(first_input, second_input):
    82    # if first_input == True:
    83    # if bool(first_input) == True:
    84    # if bool(first_input):
    85    # if first_input:
    86        # if second_input == False:
    87        # if bool(second_input) == False:
    88        # if not bool(second_input) == True:
    89        # if not bool(second_input):
    90        # if not second_input:
    91    if first_input and not second_input:
    92            return True
    93    return False
    

    the test is still green, because I can put two if statements together when one is indented under the other

    if something:
        if something_else:
    

    can also be written as

    if something and something_else:
    
  • I add a conditional expression

    81def material_non_implication(first_input, second_input):
    82    # if first_input == True:
    83    # if bool(first_input) == True:
    84    # if bool(first_input):
    85    # if first_input:
    86        # if second_input == False:
    87        # if bool(second_input) == False:
    88        # if not bool(second_input) == True:
    89        # if not bool(second_input):
    90        # if not second_input:
    91    # if first_input and not second_input:
    92    #         return True
    93    # return False
    94    return (
    95        True if
    96        first_input and not second_input
    97        else False
    98    )
    

    still green.

    if (                  vs return True
        first_input and
        not second_input
    ):
        return True       vs if first_input and not second_input
    return False          vs else False
    
  • I remove True if and else False to make the statement simpler

    94    return (
    95        # True if
    96        first_input and not second_input
    97        # else False
    98    )
    

    green.

  • material_non_implication returns first_input and not second_input

    • not second_input is the Logical Negation (NOT) of second_input

      • if the second input is True, this part of the statement is False

      • if the second input is False, this part of the statement is True

    • first_input and not second_input is the Logical Conjunction (AND) of the first input and the Logical Negation (NOT) of the second input

      logical_conjunction(
          first_input,
          logical_negation(second_input)
      )
      

    this means that in the four cases

    • if the first input is True and the second input is True, material_non_implication returns

      first_input and not second_input
      True        and not True
      True        and False # logical_conjunction(True, False)
      False
      
    • if the first input is True and the second input is False, material_non_implication returns

      first_input and not second_input
      True        and not False
      True        and True  # logical_conjunction(True, True)
      True
      
    • if the first input is False and the second input is True, material_non_implication returns

      first_input and not second_input
      False       and not True
      False       and False # logical_conjunction(False, False)
      False
      
    • if the first input is False and the second input is False, material_non_implication returns

      first_input and not second_input
      False       and not False
      False       and True  # logical_conjunction(False, True)
      False
      

    first

    second

    not second

    (first and not second)

    True

    True

    False

    False

    True

    False

    True

    True

    False

    True

    False

    False

    False

    False

    True

    False

    I add a return statement to show this

     81def material_non_implication(first_input, second_input):
     82    # if first_input == True:
     83    # if bool(first_input) == True:
     84    # if bool(first_input):
     85    # if first_input:
     86        # if second_input == False:
     87        # if bool(second_input) == False:
     88        # if not bool(second_input) == True:
     89        # if not bool(second_input):
     90        # if not second_input:
     91    # if first_input and not second_input:
     92    #         return True
     93    # return False
     94    return logical_conjunction(
     95        first_input,
     96        logical_negation(second_input)
     97    )
     98    return (
     99        # True if
    100        first_input and not second_input
    101        # else False
    102    )
    

    still green.

    material_non_implication(False, False) -> False
     └── logical_conjunction(False, True ) -> False
    
    material_non_implication(False, True ) -> False
     └── logical_conjunction(False, False) -> False
    
    material_non_implication(True , False) -> True
     └── logical_conjunction(True , True ) -> True
    
    material_non_implication(True , True ) -> False
     └── logical_conjunction(True , False) -> False
    
  • I remove the comments

    81def material_non_implication(first_input, second_input):
    82    return logical_conjunction(
    83        first_input,
    84        logical_negation(second_input)
    85    )
    86    return first_input and not second_input
    

    I can use either of these return statements. The first return statement is the only one that runs in this case, because the return statement is the last thing to run in a function.

  • I add a git commit message in the other terminal

    git commit -am \
    'add material_non_implication'
    

Material Non-Implication

  • returns first_input and not second_input which is the Logical Conjunction of the first input and the Logical Negation of the second input.

  • returns True, only if the first input is True and the second input is False.

  • is the Logical Negation of Material/Logical Implication which returns False only if the first input is True and the second input is False.

  • can be thought of as a claim that does not deliver.


examples of Material Non-Implication


  • A broken promise, if the inputs are

    • did I make a promise?

    • did I do something?

    I promised

    I did something

    broken promise

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    no

    no

    no

    no

  • A false alarm, if the inputs are

    • did alarm ring?

    • was there a bad event?

    alarm rang

    bad event

    false alarm

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    no (alarm is broken)

    no

    no

    no

  • the boy who cried wolf, if the inputs are

    • did the boy cry?

    • was there a wolf?

    boy cried

    wolf

    false claim

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    no

    no

    no

    no


more about Exclusive Disjunction

I can also think of Exclusive Disjunction which returns (not first_input and second_input) or (first_input and not second_input) as

return logical_disjunction(
    converse_non_implication(
        first_input, second_input
    ),
    material_non_implication(
        first_input, second_input
    )
)

because logical_disjunction returns first_input or second_input

  • first_input in this case is (not first_input and second_input)

  • converse_non_implication returns not first_input and second_input

  • second_input in this case is (first_input and not second_input)

  • material_non_implication returns first_input and not second_input

logical_disjunction(first_input, second_input)

             first_input == (not first_input and second_input)
converse_non_implication == (not first_input and second_input)

            second_input == (first_input and not second_input)
material_non_implication == (first_input and not second_input)
  • I change the return statement in the exclusive_disjunction function in truth_table.py

    62def exclusive_disjunction(first_input, second_input):
    63    # return first_input != second_input
    64    # return not (first_input == second_input)
    65    return logical_disjunction(
    66        converse_non_implication(
    67            first_input, second_input
    68        ),
    69        # logical_conjunction(
    70        #     first_input,
    71        #     logical_negation(second_input)
    72        # )
    73        material_non_implication(
    74            first_input, second_input
    75        )
    76    )
    77    return (
    78        (not first_input and second_input)
    79        or
    80        (first_input and not second_input)
    81    )
    

    the test is still green.

  • I put the first two return statements back, then remove the commented lines

    62def exclusive_disjunction(first_input, second_input):
    63    return first_input != second_input
    64    return not (first_input == second_input)
    65    return logical_disjunction(
    66        converse_non_implication(
    67            first_input, second_input
    68        ),
    69        material_non_implication(
    70            first_input, second_input
    71        )
    72    )
    73    return (
    74        (not first_input and second_input)
    75        or
    76        (first_input and not second_input)
    77    )
    78
    79
    80def material_non_implication(first_input, second_input):
    81    return logical_conjunction(
    82        first_input,
    83        logical_negation(second_input)
    84    )
    85    return first_input and not second_input
    

    still green.

  • I add a git commit message in the other terminal

    git commit -am \
    'refactor exclusive_disjunction'
    

test_project_first

The truth table for project_first is

first input

second input

return

True

True

True

True

False

True

False

True

False

False

False

False


RED: make it fail


  • I go back to the terminal where the tests are running

  • I add a test for project_first with an assertion for if the first input is True and the second input is True, in test_binary.py

    first input

    second input

    return

    True

    True

    True

    83        self.assertFalse(material_non_implication(False, False))
    84
    85    def test_project_first(self):
    86        project_first = src.truth_table.project_first
    87        self.assertTrue(project_first(True, True))
    88
    89
    90# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.truth_table'
                    has no attribute 'project_first'
    

    there is nothing named project_first in truth_table.py


GREEN: make it pass


I add a function for project_first to truth_table.py

80def material_non_implication(first_input, second_input):
81    return logical_conjunction(
82        first_input,
83        logical_negation(second_input)
84    )
85    return first_input and not second_input
86
87
88def project_first(first_input, second_input):
89    return True

the test passes. project_first returns True, if the first input is True and the second input is True.

project_first(True , True ) -> True

REFACTOR: make it better


  • I add an assertion for the second case, which is if the first input is True and the second input is False, to test_project_first in test_binary.py

    first input

    second input

    return

    True

    False

    True

    85    def test_project_first(self):
    86        project_first = src.truth_table.project_first
    87        self.assertTrue(project_first(True, True))
    88        self.assertTrue(project_first(True, False))
    89
    90
    91# Exceptions seen
    

    the test is still green. project_first returns

    • True, if the first input is True and the second input is False

    • True, if the first input is True and the second input is True

    • True, if the first input is True

    project_first(True , False) -> True
    project_first(True , True ) -> True
    
  • on to the next case, which is if the first input is False and the second input is True

    first input

    second input

    return

    False

    True

    False

    85    def test_project_first(self):
    86        project_first = src.truth_table.project_first
    87        self.assertTrue(project_first(True, True))
    88        self.assertTrue(project_first(True, False))
    89        self.assertFalse(project_first(False, True))
    90
    91
    92# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: True is not false
    

    because the function returns True and the assertion expects False.

  • I add an if statement for this case to the project_first function in truth_table.py

    88def project_first(first_input, second_input):
    89    if first_input == False:
    90        return False
    91    return True
    

    the test passes because when project_first is called, Python checks if first_input == False:

    • if first_input is NOT equal to False, it leaves the if statement then runs return True

      project_first(True , True ) -> True
      └── def project_first(first_input, second_input):
          ├── first_input  == True
          ├── second_input == True
          ├── if first_input == False:
                return False
          └── return True
      
      project_first(True , False) -> True
      └── def project_first(first_input, second_input):
          ├── first_input  == True
          ├── second_input == False
          ├── if first_input == False:
                return False
          └── return True
      
    • if first_input is equal to False, it runs return False

      project_first(False, False) -> False
      └── def project_first(first_input, second_input):
          ├── first_input  == False
          ├── second_input == False
          └── if first_input == False:
              └── return False
              return True
      
      project_first(False, True ) -> False
      └── def project_first(first_input, second_input):
          ├── first_input  == False
          ├── second_input == True
          └── if first_input == False:
              └── return False
              return True
      
  • I add an assertion for the last case, which is if the first input is False and the second input is False, to test_project_first in test_binary.py

    first input

    second input

    return

    False

    False

    False

    85    def test_project_first(self):
    86        project_first = src.truth_table.project_first
    87        self.assertTrue(project_first(True, True))
    88        self.assertTrue(project_first(True, False))
    89        self.assertFalse(project_first(False, True))
    90        self.assertFalse(project_first(False, False))
    91
    92
    93# Exceptions seen
    

    the test is still green.

    project_first(False, False) -> False
    project_first(False, True ) -> False
    project_first(True , False) -> True
    project_first(True , True ) -> True
    
  • I add the bool built-in function to the if statement in the project_first function in truth_table.py

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    if bool(first_input) == False:
    91        return False
    92    return True
    

    still green.

  • I use Logical Negation (NOT) to write it in terms of True

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    # if bool(first_input) == False:
    91    if not bool(first_input) == True:
    92        return False
    93    return True
    

    green.

  • I remove == True

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    # if bool(first_input) == False:
    91    # if not bool(first_input) == True:
    92    if not bool(first_input):
    93        return False
    94    return True
    

    still green.

  • I remove bool

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    # if bool(first_input) == False:
    91    # if not bool(first_input) == True:
    92    # if not bool(first_input):
    93    if not first_input:
    94        return False
    95    return True
    

    the test is still green, because if bool(something) == False is the same as if not bool(something) == True is the same as if not bool(something) is the same as if not something.

  • I add an else clause to make it clearer

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    # if bool(first_input) == False:
    91    # if not bool(first_input) == True:
    92    # if not bool(first_input):
    93    if not first_input:
    94        return False
    95    else:
    96        return True
    

    still green.

  • I use Logical Negation (NOT) for the else clause

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    # if bool(first_input) == False:
    91    # if not bool(first_input) == True:
    92    # if not bool(first_input):
    93    if not first_input:
    94        return False
    95    # else:
    96    if not (not first_input):
    97        return True
    

    green.

  • I remove the nots because they cancel out

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    # if bool(first_input) == False:
    91    # if not bool(first_input) == True:
    92    # if not bool(first_input):
    93    if not first_input:
    94        return False
    95    # else:
    96    # if not (not first_input):
    97    if first_input:
    98        return True
    

    still green.

  • I add a conditional expression

    88def project_first(first_input, second_input):
    89    # if first_input == False:
    90    # if bool(first_input) == False:
    91    # if not bool(first_input) == True:
    92    # if not bool(first_input):
    93    # if not first_input:
    94    #     return False
    95    # else:
    96    # if not (not first_input):
    97    # if first_input:
    98    #     return True
    99    return True if first_input else False
    

    the test is still green.

    if first_input:   vs return True
        return True   vs if first_input
    else:             vs else
        return False  vs False
    
  • I remove True if and else False

     88def project_first(first_input, second_input):
     89    # if first_input == False:
     90    # if bool(first_input) == False:
     91    # if not bool(first_input) == True:
     92    # if not bool(first_input):
     93    # if not first_input:
     94    #     return False
     95    # else:
     96    # if not (not first_input):
     97    # if first_input:
     98    #     return True
     99    # return True if first_input else False
    100    return first_input
    

    still green.

  • I remove the other statements

    88def project_first(first_input, second_input):
    89    return first_input
    
  • I add a git commit message in the other terminal

    git commit -am 'add project_first'
    

Project First always returns the first input it does not care about the second input, like Project Second which always returns the second input and does not care about the first input.


examples of Project First


  • did the event happen, if the inputs are

    • did the event happen?

    • did I get a notification?

    event

    notification

    event happened

    yes

    yes

    yes

    yes

    no

    yes (notification is broken)

    no

    yes

    no (notification is broken)

    no

    no

    no

    “if a tree falls in the forest, does it make a sound?”

  • able to login with or without Multi Factor Authentication, if the inputs are

    • did user provide the right password?

    • does user have MFA?

    right password

    right MFA code

    log in

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    no (how did user get the MFA code?)

    no

    no

    no

  • my actions, if the inputs are

    • what I want to do?

    • what you think I should do?

    what I want

    your thoughts

    my actions

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    no

    no

    no

    no

  • autocorrect, if the inputs are

    • autocorrect suggests a word

    • I write what I want

    autocorrect

    what I want

    what I get

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    no

    no

    no

    no


test_converse_implication

The truth table for converse_implication is

first input

second input

return

True

True

True

True

False

True

False

True

False

False

False

True


RED: make it fail


  • I go back to the terminal where the tests are running

  • I add a test for converse_implication with an assertion for if the first input is True and the second input is True, to test_binary.py

    first input

    second input

    return

    True

    True

    True

    90        self.assertFalse(project_first(False, False))
    91
    92    def test_converse_implication(self):
    93        converse_implication = (
    94            src.truth_table.converse_implication
    95        )
    96        self.assertTrue(converse_implication(True, True))
    97
    98
    99# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.truth_table'
                    has no attribute 'converse_implication'.
                    Did you mean: 'converse_non_implication'?
    

    because truth_table.py does not have anything named converse_implication in it.


GREEN: make it pass


I add a function definition for converse_implication to truth_table.py

88def project_first(first_input, second_input):
89    return first_input
90
91
92def converse_implication(first_input, second_input):
93    return True

the test passes. converse_implication returns True, if the first input is True and the second input is True.

converse_implication(True , True ) -> True

REFACTOR: make it better


  • I add an assertion for the second case, which is if the first input is True and the second input is False, to test_converse_implication in test_binary.py

    first input

    second input

    return

    True

    False

    True

     92    def test_converse_implication(self):
     93        converse_implication = (
     94            src.truth_table.converse_implication
     95        )
     96        self.assertTrue(converse_implication(True, True))
     97        self.assertTrue(converse_implication(True, False))
     98
     99
    100# Exceptions seen
    

    the test is still green. converse_implication returns True

    • if the first input is True and the second input is False

    • if the first input is True and the second input is True

    • if the first input is True

    converse_implication(True , False) -> True
    converse_implication(True , True ) -> True
    
  • time for the next case, which is if the first input is False and the second input is True

     92    def test_converse_implication(self):
     93        converse_implication = (
     94            src.truth_table.converse_implication
     95        )
     96        self.assertTrue(converse_implication(True, True))
     97        self.assertTrue(converse_implication(True, False))
     98        self.assertFalse(converse_implication(False, True))
     99
    100
    101# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: True is not false
    

    because the converse_implication function returns True and the assertion expects False.

  • I add an if statement to converse_implication in truth_table.py

    92def converse_implication(first_input, second_input):
    93    if first_input == False:
    94        return False
    95    return True
    

    the test passes because when converse_implication is called, it runs if first_input == False:

    • if first_input is NOT equal to False, it leaves the if statement then runs return True

    • if first_input is equal to False, it runs return False

    converse_implication(False, True ) -> False
    converse_implication(True , False) -> True
    converse_implication(True , True ) -> True
    
  • I add an assertion for the last case, which is if the first input is False and the second input is False, to test_converse_implication in test_binary.py

    first input

    second input

    return

    False

    False

    True

     92    def test_converse_implication(self):
     93        converse_implication = (
     94            src.truth_table.converse_implication
     95        )
     96        self.assertTrue(converse_implication(True, True))
     97        self.assertTrue(converse_implication(True, False))
     98        self.assertFalse(converse_implication(False, True))
     99        self.assertTrue(converse_implication(False, False))
    100
    101
    102# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: False is not true
    

    because when converse_implication is called, Python checks if first_input == False:

    • if first_input is NOT equal to False, it leaves the if statement then runs return True

    • if first_input is equal to False, it runs return False

    • first_input is False in this case, which raises AssertionError since the assertion expects True and the function returns False

  • I add an if statement for the only case that returns False

    92def converse_implication(first_input, second_input):
    93    if first_input == False:
    94        if second_input == True:
    95            return False
    96    return True
    

    the test passes.

    converse_implication(False, False) -> True
    converse_implication(False, True ) -> False
    converse_implication(True , False) -> True
    converse_implication(True , True ) -> True
    

    Python if first_input == False: when converse_implication is called

    • if first_input is NOT equal to False, it leaves the if statement then runs return True

      converse_implication(True , True ) -> True
      └── def converse_implication(first_input, second_input):
          ├── first_input  == True
          ├── second_input == True
          ├── if first_input == False:
                 if second_input == True:
                     return False
          └── return True
      
      converse_implication(True , False) -> True
      └── def converse_implication(first_input, second_input):
          ├── first_input  == True
          ├── second_input == False
          ├── if first_input == False:
                 if second_input == True:
                     return False
          └── return True
      
    • if first_input is equal to False, Python checks if second_input == True:

      • if second_input is NOT equal to True, it leaves if first_input == False: and runs return True

        converse_implication(False, False) -> True
        └── def converse_implication(first_input, second_input):
            ├── first_input  == False
            ├── second_input == False
            └── if first_input == False:
            ┌───┴── if second_input == True:
                       return False
            └── return True
        
      • if second_input is equal to True, it runs return False

        converse_implication(False, True ) -> False
        └── def converse_implication(first_input, second_input):
            ├── first_input  == False
            ├── second_input == True
            └── if first_input == False:
                └── if second_input == True:
                    └── return False
                return True
        
    • it only checks the second input if the first input is True.

  • I add the bool built-in function

    92def converse_implication(first_input, second_input):
    93    # if first_input == False:
    94    if bool(first_input) == False:
    95        # if second_input == True:
    96        if bool(second_input) == True:
    97            return False
    98    return True
    

    the test is still green.

  • I use Logical Negation (NOT) to write the first if statement in terms of True

    92def converse_implication(first_input, second_input):
    93    # if first_input == False:
    94    # if bool(first_input) == False:
    95    if not bool(first_input) == True:
    96        # if second_input == True:
    97        if bool(second_input) == True:
    98            return False
    99    return True
    

    still green.

  • I remove == True

     92def converse_implication(first_input, second_input):
     93    # if first_input == False:
     94    # if bool(first_input) == False:
     95    # if not bool(first_input) == True:
     96    if not bool(first_input):
     97        # if second_input == True:
     98        # if bool(second_input) == True:
     99        if bool(second_input):
    100            return False
    101    return True
    

    green.

  • I remove bool

     92def converse_implication(first_input, second_input):
     93    # if first_input == False:
     94    # if bool(first_input) == False:
     95    # if not bool(first_input) == True:
     96    # if not bool(first_input):
     97    if not first_input:
     98        # if second_input == True:
     99        # if bool(second_input) == True:
    100        # if bool(second_input):
    101        if second_input:
    102            return False
    103    return True
    

    still green, because

    • if bool(something) == False is the same as if not bool(something) == True is the same as if not bool(something) is the same as if not something.

    • if bool(something) == True is the same as if bool(something) is the same as if something.

  • I use Logical Conjunction (AND) to put the two if statements together

     92def converse_implication(first_input, second_input):
     93    # if first_input == False:
     94    # if bool(first_input) == False:
     95    # if not bool(first_input) == True:
     96    # if not bool(first_input):
     97    # if not first_input:
     98        # if second_input == True:
     99        # if bool(second_input) == True:
    100        # if bool(second_input):
    101        # if second_input:
    102    if not first_input and second_input:
    103            return False
    104    return True
    

    the test is still green, because I can put two if statements together when one is indented under the other

    if something:
        if something_else:
    

    can also be written as

    if something and something_else:
    
  • I add an else clause to make it clearer

     92def converse_implication(first_input, second_input):
     93    # if first_input == False:
     94    # if bool(first_input) == False:
     95    # if not bool(first_input) == True:
     96    # if not bool(first_input):
     97    # if not first_input:
     98        # if second_input == True:
     99        # if bool(second_input) == True:
    100        # if bool(second_input):
    101        # if second_input:
    102    if not first_input and second_input:
    103        return False
    104    else:
    105        return True
    

    still green.

  • I use the Logical Negation (NOT) of the if statement for the else clause

    102    if not first_input and second_input:
    103        return False
    104    # else:
    105    if not (not first_input and second_input):
    106        return True
    

    green.

  • I “multiply” the not by every symbol in the parentheses

    102    if not first_input and second_input:
    103        return False
    104    # else:
    105    # if not (not first_input and second_input):
    106    if (
    107        (not not first_input)
    108        (not and)
    109        (not second_input)
    110    ):
    111        return True
    

    the terminal is my friend, and shows SyntaxError

    SyntaxError: invalid syntax
    

    I cannot negate and this way.

  • I change not and to or

    102    if not first_input and second_input:
    103        return False
    104    # else:
    105    # if not (not first_input and second_input):
    106    if (
    107        (not not first_input)
    108        # (not and)
    109        or
    110        (not second_input)
    111    ):
    112        return True
    

    the test is green again.

  • I remove not not

    102    if not first_input and second_input:
    103        return False
    104    # else:
    105    # if not (not first_input and second_input):
    106    if (
    107        # (not not first_input)
    108        first_input
    109        # (not and)
    110        or
    111        (not second_input)
    112    ):
    113        return True
    

    the test is still green because two nots cancel out.

  • I add a conditional expression

    102    # if not first_input and second_input:
    103    #     return False
    104    # else:
    105    # if not (not first_input and second_input):
    106    # if (
    107        # (not not first_input)
    108        # first_input
    109        # (not and)
    110        # or
    111        # (not second_input)
    112    # ):
    113        # return True
    114    return (
    115        True if
    116        first_input or not second_input
    117        else False
    118    )
    

    still green.

  • I remove True if and else False

    102    # if not first_input and second_input:
    103    #     return False
    104    # else:
    105    # if not (not first_input and second_input):
    106    # if (
    107        # (not not first_input)
    108        # first_input
    109        # (not and)
    110        # or
    111        # (not second_input)
    112    # ):
    113        # return True
    114    return (
    115        # True if
    116        first_input or not second_input
    117        # else False
    118    )
    

    green.

  • converse_implication returns first_input or not second_input

    • not second_input is the Logical Negation (NOT) of second_input

      • if the second input is True, this part of the statement is False

      • if the second input is False, this part of the statement is True

    • first_input or not second_input is the Logical Disjunction (OR) of the first input and the Logical Negation (NOT) of the second input

      logical_disjunction(
          first_input,
          logical_negation(second_input)
      )
      

    this means that in the four cases

    • if the first input is True and the second input is True, converse_implication returns

      first_input or not second_input
      True        or not True
      True        or False  # logical_disjunction(True, False)
      True
      
    • if the first input is True and the second input is False, converse_implication returns

      first_input or not second_input
      True        or not False
      True        or True   # logical_disjunction(True, True)
      True
      
    • if the first input is False and the second input is True, converse_implication returns

      first_input or not second_input
      False       or not True
      False       or False  # logical_disjunction(False, False)
      False
      
    • if the first input is False and the second input is False, converse_implication returns

      first_input or not second_input
      False       or not False
      False       or True  # logical_disjunction(False, True)
      True
      

    first

    second

    not second

    (first or not second)

    True

    True

    False

    True

    True

    False

    True

    True

    False

    True

    False

    False

    False

    False

    True

    True

    I add a return statement to show this

    106    # if (
    107        # (not not first_input)
    108        # (not and)
    109    #     first_input
    110    #     or
    111    #     (not second_input)
    112    # ):
    113    #     return True
    114    return logical_disjunction(
    115        first_input,
    116        logical_negation(second_input)
    117    )
    118    return (
    119        # True if
    120        first_input or not second_input
    121        # else False
    122    )
    

    still green.

    converse_implication(False, False) -> True
    └── logical_disjunction(False, True ) -> True
    
    converse_implication(False, True ) -> False
    └── logical_disjunction(False, False) -> False
    
    converse_implication(True , False) -> True
    └── logical_disjunction(True , True ) -> True
    
    converse_implication(True , True ) -> True
    └── logical_disjunction(True , False) -> True
    
  • I remove the comments

    92def converse_implication(first_input, second_input):
    93    return logical_disjunction(
    94        first_input,
    95        logical_negation(second_input)
    96    )
    97    return first_input or not second_input
    

    I can use either of these return statements. The first return statement is the only one that runs in this case, because the return statement is the last thing to run in a function.

  • I add a git commit message in the other terminal

    git commit -am 'add converse_implication'
    

Converse Implication returns

  • first_input or not second_input.

  • False, only if the first input is False and the second input is True.

  • the Logical Disjunction of the first input and the Logical Negation of the second input.

  • It is the opposite of Converse Non-Implication which always returns not first_input and second_input or True, only if the first input is False and the second input is True.


examples of Converse Implication


  • I cannot get a table at a restaurant, if the inputs are

    • do I have a reservation?

    • is the place full?

    reservation

    place is full

    I get a table

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    no

    no

    no

    yes

  • sleep through alarm, if the inputs are

    • am I too deep in sleep to hear the alarm?

    • is the alarm ringing?

    too tired

    alarm rings

    I sleep

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    no

    no

    no

    yes

  • staying dry outside, if the inputs are

    • do I have an umbrella?

    • is it raining?

    umbrella?

    raining?

    dry

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    no

    no

    no

    yes


close the project

  • I close test_binary.py and truth_table.py

  • I click in the terminal where the tests are running

  • I use q on the keyboard to leave the tests. The terminal goes back to the command line.

  • I change directory to the parent of truth_table

    cd ..
    

    the terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory.


review

Binary Operations take two inputs, each input can be True or False. If I name the first input first_input and the second input second_input, the tests show that

  • Converse Implication

    • returns first_input or not second_input

    • returns False if first_input is False and second_input is True

    • is the Logical Negation (NOT) of Converse Non-Implication which returns True only if first_input is False and second_input is True

    first input

    second input

    return

    True

    True

    True

    True

    False

    True

    False

    True

    False

    False

    False

    True

  • Project First

    first input

    second input

    return

    True

    True

    True

    True

    False

    True

    False

    True

    False

    False

    False

    False

  • Material Non-Implication

    first input

    second input

    return

    True

    True

    False

    True

    False

    True

    False

    True

    False

    False

    False

    False

  • Exclusive Disjunction

    • returns first_input != second_input

    • returns True only if first_input and second_input are NOT equal

    • is the Logical Negation (NOT) of Logical Equality which returns True only if first_input and second_input are equal

    first input

    second input

    return

    True

    True

    False

    True

    False

    True

    False

    True

    True

    False

    False

    False

  • Logical Disjunction

    • returns first_input or second_input

    • returns False only if first_input is False and second_input is False

    • is the Logical Negation (NOT) of Logical NOR which returns True only if first_input is False and second_input is False

    first input

    second input

    return

    True

    True

    True

    True

    False

    True

    False

    True

    True

    False

    False

    False

  • Tautology

    first input

    second input

    return

    True

    True

    True

    True

    False

    True

    False

    True

    True

    False

    False

    True

  • Logical NAND

    • returns not (first_input and second_input)

    • returns False only if first_input is True and second_input is True

    • is the Logical Negation (NOT) of Logical Conjunction (AND) which returns True only if first_input is True and second_input is True

    first input

    second input

    return

    True

    True

    False

    True

    False

    True

    False

    True

    True

    False

    False

    True

  • Negate First

    first input

    second input

    return

    True

    True

    False

    True

    False

    False

    False

    True

    True

    False

    False

    True

  • Converse Non-Implication

    • returns not first_input and second_input

    • returns True only if first_input is False and second_input is True

    • is the Logical Negation (NOT) of Converse Implication which returns False if first_input is False and second_input is True

    first input

    second input

    return

    True

    True

    False

    True

    False

    False

    False

    True

    True

    False

    False

    False

  • Project Second

    first input

    second input

    return

    True

    True

    True

    True

    False

    False

    False

    True

    True

    False

    False

    False

  • Logical Conjunction returns

    • returns first_input and second_input

    • returns True only if first_input is True and second_input is True

    • is the Logical Negation (NOT) of Logical NAND which returns False only if first_input is True and second_input is True

    first input

    second input

    return

    True

    True

    True

    True

    False

    False

    False

    True

    False

    False

    False

    False

  • Contradiction

    first input

    second input

    return

    True

    True

    False

    True

    False

    False

    False

    True

    False

    False

    False

    False

All the binary operations or conditions have been written with some combination of some or all of these three

return

True, True

True, False

False, True

False, False

name of operation

False

False

False

False

False

contradiction

first and second

True

False

False

False

logical_conjunction

second

True

False

True

False

project_second

(not first) and second

False

False

True

False

converse_non_implication

not first

False

False

True

True

negate_first

not (first and second)

False

True

True

True

logical_nand

True

True

True

True

True

tautology

first or second

True

True

True

False

logical_disjunction

(not (first and second)) and (first or second)

False

True

True

False

exclusive_disjunction

first and (not second)

False

True

False

False

material_non_implication

first

True

True

False

False

project_first

first or (not second)

True

True

False

True

converse_implication


code from the chapter

Do you want to see all the CODE I typed for the Truth Table?


what is next?

Would you like to test the last binary operations?


rate pumping python

If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too.