truth table: Binary Operations 2


requirements

Binary Operations 1


preview

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

 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(
19            logical_conjunction(True, True)
20        )
21        self.assertFalse(
22            logical_conjunction(True, False)
23        )
24        self.assertFalse(
25            logical_conjunction(False, True)
26        )
27        self.assertFalse(
28            logical_conjunction(False, False)
29        )
30
31    def test_project_second(self):
32        project_second = src.truth_table.project_second
33        self.assertTrue(project_second(True, True))
34        self.assertFalse(project_second(True, False))
35        self.assertTrue(project_second(False, True))
36        self.assertFalse(project_second(False, False))
37
38    def test_converse_non_implication(self):
39        converse_non_implication = (
40            src.truth_table.converse_non_implication
41        )
42        self.assertFalse(
43            converse_non_implication(True, True)
44        )
45        self.assertFalse(
46            converse_non_implication(True, False)
47        )
48        self.assertTrue(
49            converse_non_implication(False, True)
50        )
51        self.assertFalse(
52            converse_non_implication(False, False)
53        )
54
55    def test_negate_first(self):
56        negate_first = src.truth_table.negate_first
57        self.assertFalse(negate_first(True, True))
58        self.assertFalse(negate_first(True, False))
59        self.assertTrue(negate_first(False, True))
60        self.assertTrue(negate_first(False, False))
61
62    def test_logical_nand(self):
63        logical_nand = src.truth_table.logical_nand
64        self.assertFalse(logical_nand(True, True))
65        self.assertTrue(logical_nand(True, False))
66        self.assertTrue(logical_nand(False, True))
67        self.assertTrue(logical_nand(False, False))
68
69    def test_tautology(self):
70        tautology = src.truth_table.tautology
71        self.assertTrue(tautology(True, True))
72        self.assertTrue(tautology(True, False))
73        self.assertTrue(tautology(False, True))
74        self.assertTrue(tautology(False, False))
75
76    def test_logical_disjunction(self):
77        logical_disjunction = (
78            src.truth_table.logical_disjunction
79        )
80        self.assertTrue(logical_disjunction(True, True))
81        self.assertTrue(logical_disjunction(True, False))
82        self.assertTrue(logical_disjunction(False, True))
83        self.assertFalse(logical_disjunction(False, False))
84
85
86# Exceptions seen
87# AttributeError
88# TypeError
89# AssertionError
90# SyntaxError

continue 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 8 items
    
    tests/test_binary.py ....                         [ 50%]
    tests/test_nullary_unary.py ....                  [100%]
    
    ================== 8 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

  • So far I have tested


test_negate_first

The truth table for negate_first is

first input

second input

return

True

True

False

True

False

False

False

True

True

False

False

True


RED: make it fail


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

first input

second input

return

True

False

False

38      def test_converse_non_implication(self):
39          converse_non_implication = (
40              src.truth_table.converse_non_implication
41          )
42          self.assertFalse(
43              converse_non_implication(True, True)
44          )
45          self.assertFalse(
46              converse_non_implication(True, False)
47          )
48          self.assertTrue(
49              converse_non_implication(False, True)
50          )
51          self.assertFalse(
52              converse_non_implication(False, False)
53          )
54
55      def test_negate_first(self):
56          negate_first = src.truth_table.negate_first
57          self.assertFalse(negate_first(True, True))
58
59
60  # Exceptions seen

the terminal is my friend, and shows AttributeError

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

there is no definition for negate_first in truth_table.py


GREEN: make it pass


  • I open truth_table.py from the src folder

  • I add the function to truth_table.py

    29def converse_non_implication(first_input, second_input):
    30    return logical_conjunction(
    31        logical_negation(first_input),
    32        second_input
    33    )
    34    return not first_input and second_input
    35
    36
    37def negate_first(first_input, second_input):
    38    return False
    

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


REFACTOR: make it better


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

    first input

    second input

    return

    True

    False

    False

    55    def test_negate_first(self):
    56        negate_first = src.truth_table.negate_first
    57        self.assertFalse(negate_first(True, True))
    58        self.assertFalse(negate_first(True, False))
    59
    60
    61# Exceptions seen
    

    the test is still green. negate_first returns

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

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

    • False, if the first input is True

  • I add an assertion for the next case, which is when the first input is False and the second input is True

    first input

    second input

    return

    False

    True

    True

    55    def test_negate_first(self):
    56        negate_first = src.truth_table.negate_first
    57        self.assertFalse(negate_first(True, True))
    58        self.assertFalse(negate_first(True, False))
    59        self.assertTrue(negate_first(False, True))
    60
    61
    62# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: False is not true
    

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

  • I add an if statement for this case to negate_first in truth_table.py

    37def negate_first(first_input, second_input):
    38    if first_input == False:
    39        return True
    40    return False
    

    the test passes. negate_first returns

    • True, if the first input is False

    • False, if the above condition is not met

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

    first input

    second input

    return

    False

    False

    True

    55    def test_negate_first(self):
    56        negate_first = src.truth_table.negate_first
    57        self.assertFalse(negate_first(True, True))
    58        self.assertFalse(negate_first(True, False))
    59        self.assertTrue(negate_first(False, True))
    60        self.assertTrue(negate_first(False, False))
    61
    62
    63# Exceptions seen
    

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

  • I add the bool built-in function to the if statement in the negate_first function in truth_table.py

    37def negate_first(first_input, second_input):
    38    # if first_input == False:
    39    if bool(first_input) == False:
    40        return True
    41    return False
    

    still green.

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

    37def negate_first(first_input, second_input):
    38    # if first_input == False:
    39    # if bool(first_input) == False:
    40    if bool(not first_input) == True:
    41        return True
    42    return False
    

    green.

  • I remove == True

    37def negate_first(first_input, second_input):
    38    # if first_input == False:
    39    # if bool(first_input) == False:
    40    # if bool(not first_input) == True:
    41    if bool(not first_input):
    42        return True
    43    return False
    

    still green.

  • I remove bool

    37def negate_first(first_input, second_input):
    38    # if first_input == False:
    39    # if bool(first_input) == False:
    40    # if bool(not first_input) == True:
    41    # if bool(not first_input):
    42    if not first_input:
    43        return True
    44    return False
    

    the test is still green because when if first_input == False: runs, Python checks if first_input is equal to False. I can assume the following substitutions

    • if the value of something is False

      if something           == False
      if bool(something    ) == False
      if bool(False        ) == False
      if False               == False
      if True                == True  # use equality
      if True                         # remove '== True'
      if not False                    # change to terms of False
      if not something
      
    • if the value of something is True

      if something           == False
      if bool(something    ) == False
      if bool(True         ) == False
      if True                == False
      if False               == True  # use equality
      if False                        # remove '== True'
      if not True                     # change to terms of True
      if not something
      

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

  • I add an else clause to be clearer

    37def negate_first(first_input, second_input):
    38    # if first_input == False:
    39    # if bool(first_input) == False:
    40    # if bool(not first_input) == True:
    41    # if bool(not first_input):
    42    if not first_input:
    43        return True
    44    else:
    45        return False
    

    still green.

  • I use a conditional expression (ternary operator) for the if statements

    37def negate_first(first_input, second_input):
    38    # if first_input == False:
    39    # if bool(first_input) == False:
    40    # if bool(not first_input) == True:
    41    # if bool(not first_input):
    42    # if not first_input:
    43    #     return True
    44    # else:
    45    #     return False
    46    return True if not first_input else False
    

    still green.

  • I remove True if and else False

    37def negate_first(first_input, second_input):
    38    # if first_input == False:
    39    # if bool(first_input) == False:
    40    # if bool(not first_input) == True:
    41    # if bool(not first_input):
    42    # if not first_input:
    43    #     return True
    44    # else:
    45    #     return False
    46    # return True if not first_input else False
    47    return not first_input
    

    green.

  • I remove the commented lines

    37def negate_first(first_input, second_input):
    38    return not first_input
    
  • I add a git commit message in another terminal

    git commit -am 'add negate_first'
    

Negate First always returns

  • not first_input

  • True, if the first input is False

  • False, if the first input is True

  • the Logical Negation (NOT) of the first input in all cases, it does not care about the second input


examples of Negate First


  • approval to do something based on risk and reason, if the inputs are

    • is it risky?

    • has a good reason?

    is risky?

    has reason?

    approved

    yes

    good

    no

    yes

    bad

    no

    no

    good

    yes

    no

    bad

    yes

  • going for a walk, if the inputs are

    • is it raining?

    • do I feel like walking?

    is raining?

    feel like walking?

    should walk

    yes

    yes

    no

    yes

    no

    no

    no

    yes

    yes

    no

    no

    yes

  • smoke detector battery test, if the inputs are

    • is the battery dead?

    • is there smoke?

    battery dead?

    is there smoke?

    beep

    yes

    yes

    no

    yes

    no

    no

    no

    yes

    yes

    no

    no

    yes

  • can the system be used while it is being updated, if the inputs are

    • is update running?

    • is user admin?

    update running?

    admin?

    system can be used

    yes

    yes

    no

    yes

    no

    no

    no

    yes

    yes

    no

    no

    yes


test_logical_nand

The truth table for logical_nand is

first input

second input

return

True

True

False

True

False

True

False

True

True

False

False

True


RED: make it fail


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

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

    first input

    second input

    return

    True

    True

    False

    55    def test_negate_first(self):
    56        negate_first = src.truth_table.negate_first
    57        self.assertFalse(negate_first(True, True))
    58        self.assertFalse(negate_first(True, False))
    59        self.assertTrue(negate_first(False, True))
    60        self.assertTrue(negate_first(False, False))
    61
    62    def test_logical_nand(self):
    63        logical_nand = src.truth_table.logical_nand
    64        self.assertFalse(logical_nand(True, True))
    65
    66
    67# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.truth_table'
                    has no attribute 'logical_nand'.
                    Did you mean: 'logical_false'?
    

    because there is no definition for logical_nand in truth_table.py


GREEN: make it pass


I add the function to truth_table.py

37def negate_first(first_input, second_input):
38    return not first_input
39
40
41def logical_nand(first_input, second_input):
42    return False

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


REFACTOR: make it better


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

    first input

    second input

    return

    True

    False

    True

    62    def test_logical_nand(self):
    63        logical_nand = src.truth_table.logical_nand
    64        self.assertFalse(logical_nand(True, True))
    65        self.assertTrue(logical_nand(True, False))
    66
    67
    68# 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 logical_nand function in truth_table.py

    41def logical_nand(first_input, second_input):
    42    if second_input == False:
    43        return True
    44    return False
    

    the test passes. logical_nand returns

    • True, if the second input is False

    • False, if the above condition is NOT met

    • the logical negation of the second input so far

  • I add another assertion, for the case when the first input is False and the second input is True, to test_logical_nand in test_binary.py

    first input

    second input

    return

    False

    True

    True

    62    def test_logical_nand(self):
    63        logical_nand = src.truth_table.logical_nand
    64        self.assertFalse(logical_nand(True, True))
    65        self.assertTrue(logical_nand(True, False))
    66        self.assertTrue(logical_nand(False, True))
    67
    68
    69# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: False is not true
    

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

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

    41def logical_nand(first_input, second_input):
    42    if first_input == False:
    43        return True
    44    if second_input == False:
    45        return True
    46    return False
    

    the test passes. logical_nand returns

    • True, if the first input is False

    • True, if the second input is False

    • False, if none of the above conditions are met

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

    first input

    second input

    return

    False

    False

    True

    62    def test_logical_nand(self):
    63        logical_nand = src.truth_table.logical_nand
    64        self.assertFalse(logical_nand(True, True))
    65        self.assertTrue(logical_nand(True, False))
    66        self.assertTrue(logical_nand(False, True))
    67        self.assertTrue(logical_nand(False, False))
    68
    69
    70# Exceptions seen
    

    the test is still green because this happens when logical_nand is called - it runs if first_input == False:, where Python checks if first_input is equal to False

  • There is only one case where logical_nand returns False (the first case, when the first input is True and the second input is True). I add if statements for it, in truth_table.py

    41def logical_nand(first_input, second_input):
    42    # if first_input == False:
    43    #     return True
    44    # if second_input == False:
    45    #     return True
    46    # return False
    47    if first_input == True:
    48        if second_input == True:
    49            return False
    50    return True
    

    still green because logical_nand returns

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

    • True if the above condition is NOT met

    this happens when if first_input == True: runs, Python checks if first_input is equal to True

  • I use the bool built-in function

    41def logical_nand(first_input, second_input):
    42    # if first_input == False:
    43    #     return True
    44    # if second_input == False:
    45    #     return True
    46    # return False
    47    # if first_input == True:
    48    if bool(first_input) == True:
    49        # if second_input == True:
    50        if bool(second_input) == True:
    51            return False
    52    return True
    

    green.

  • I remove == True

    41def logical_nand(first_input, second_input):
    42    # if first_input == False:
    43    #     return True
    44    # if second_input == False:
    45    #     return True
    46    # return False
    47    # if first_input == True:
    48    # if bool(first_input) == True:
    49    if bool(first_input):
    50        # if second_input == True:
    51        # if bool(second_input) == True:
    52        if bool(second_input):
    53            return False
    54    return True
    

    still green.

  • I remove bool

    41def logical_nand(first_input, second_input):
    42    # if first_input == False:
    43    #     return True
    44    # if second_input == False:
    45    #     return True
    46    # return False
    47    # if first_input == True:
    48    # if bool(first_input) == True:
    49    # if bool(first_input):
    50    if first_input:
    51        # if second_input == True:
    52        # if bool(second_input) == True:
    53        # if bool(second_input):
    54        if second_input:
    55            return False
    56    return True
    

    the test is still green, because I can assume the following substitutions for if something == True:

    • if the value of something is False

      if something           == True
      if bool(something    ) == True
      if bool(False        ) == True
      if False               == True
      if not True            == True  # change to terms of True
      if not True                     # remove '==True'
      if False
      if something
      
    • if the value of something is True

      if something           == True
      if bool(something    ) == True
      if bool(True         ) == True
      if True                == True  # remove '==True'
      if True
      if 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

    41def logical_nand(first_input, second_input):
    42    # if first_input == False:
    43    #     return True
    44    # if second_input == False:
    45    #     return True
    46    # return False
    47    # if first_input == True:
    48    # if bool(first_input) == True:
    49    # if bool(first_input):
    50    # if first_input:
    51        # if second_input == True:
    52        # if bool(second_input) == True:
    53        # if bool(second_input):
    54        # if second_input:
    55    if first_input and second_input:
    56            return False
    57    return True
    

    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

    41def logical_nand(first_input, second_input):
    42    # if first_input == False:
    43    #     return True
    44    # if second_input == False:
    45    #     return True
    46    # return False
    47    # if first_input == True:
    48    # if bool(first_input) == True:
    49    # if bool(first_input):
    50    # if first_input:
    51        # if second_input == True:
    52        # if bool(second_input) == True:
    53        # if bool(second_input):
    54        # if second_input:
    55    if first_input and second_input:
    56        return False
    57    else:
    58        return True
    

    green.

  • I rewrite the else clause with the Logical Negation (NOT) of first_input and second_input so I can write the statements with a conditional expression (ternary operator)

    55def logical_nand(first_input, second_input):
    56    # if first_input == False:
    57    #     return True
    58    # if second_input == False:
    59    #     return True
    60    # return False
    61    # if first_input == True:
    62    # if bool(first_input) == True:
    63    # if bool(first_input):
    64    # if first_input:
    65        # if second_input == True:
    66        # if bool(second_input) == True:
    67        # if bool(second_input):
    68        # if second_input:
    69    if first_input and second_input:
    70        return False
    71    # else:
    72    if not (first_input and second_input):
    73        return True
    

    still green.

  • I move the first if statement below this new one

    41def logical_nand(first_input, second_input):
    42    # if first_input == False:
    43    #     return True
    44    # if second_input == False:
    45    #     return True
    46    # return False
    47    # if first_input == True:
    48    # if bool(first_input) == True:
    49    # if bool(first_input):
    50    # if first_input:
    51        # if second_input == True:
    52        # if bool(second_input) == True:
    53        # if bool(second_input):
    54        # if second_input:
    55    # else:
    56    if not (first_input and second_input):
    57        return True
    58    if first_input and second_input:
    59        return False
    

    the test is still green.

  • I change if first_input and second_input to an else clause

    55def logical_nand(first_input, second_input):
    56    # if first_input == False:
    57    #     return True
    58    # if second_input == False:
    59    #     return True
    60    # return False
    61    # if first_input == True:
    62    # if bool(first_input) == True:
    63    # if bool(first_input):
    64    # if first_input:
    65        # if second_input == True:
    66        # if bool(second_input) == True:
    67        # if bool(second_input):
    68        # if second_input:
    69    # else:
    70    if not (first_input and second_input):
    71        return True
    72    # if first_input and second_input:
    73    else:
    74        return False
    

    still green.

  • I add a conditional expression

    55    # else:
    56    # if not (first_input and second_input):
    57    #     return True
    58    # if first_input and second_input:
    59    # else:
    60    #     return False
    61    return (
    62        True if
    63        not (first_input and second_input)
    64        else False
    65    )
    

    green.

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

    55    # else:
    56    # if not (first_input and second_input):
    57    #     return True
    58    # if first_input and second_input:
    59    # else:
    60    #     return False
    61    return (
    62        # True if
    63        not (first_input and second_input)
    64        # else False
    65    )
    

    still green because when there is only one if statement that returns False with an else clause

    if something:
        return False
    else:
        return True
    

    since else is the logical_negation (not) of the if statement

    if something:
        return False
    if not something:
        return True
    

    I can use its logical negation (not) to return True

    if not something:
        return True
    else:
        return False
    

    I can then write it with a ternary operator (conditional expression)

    return True if not (something) else False
    

    which can be made simpler as

    return not (something)
    

    this means if something: return False is the same as return not (something) since Python groups objects as False or True

  • logical_nand returns not (first_input and second_input) which is the Logical Negation (NOT) of the Logical Conjunction (AND) of the first input and second input, which means that in the four cases

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

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

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

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

      not (first and second)
      not (False and False)
      not (False) # not logical_conjunction(False, False)
      True
      

    first

    second

    first and second

    not (first and second)

    True

    True

    True

    False

    True

    False

    False

    True

    False

    True

    False

    True

    False

    False

    False

    True

    I add a return statement to show this

    55    # else:
    56    # if not (first_input and second_input):
    57    #     return True
    58    # if first_input and second_input:
    59    # else:
    60    #     return
    61    return logical_negation(
    62        logical_conjunction(
    63            first_input, second_input
    64        )
    65    )
    66    return (
    67        # True if
    68        not (first_input and second_input)
    69        # else False
    70    )
    

    the test is still green.

  • I remove the comments

    41def logical_nand(first_input, second_input):
    42    return logical_negation(
    43        logical_conjunction(
    44            first_input, second_input
    45        )
    46    )
    47    return not (first_input and second_input)
    

    I can use any of these two `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 logical_nand'
    

Logical NAND


examples of Logical NAND


  • can the website be reached, if the inputs are

    • is main server down?

    • is backup server down?

    is main down?

    is backup down?

    can reach website?

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • safety when mixing household cleaning products, if the inputs are

    • is bleach?

    • is soap?

    is bleach?

    is soap?

    is safe when mixed?

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • fridge alarm, if the inputs are

    • door open?

    • temperature rose?

    door open?

    temperature rose?

    silent

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • approve a transaction, if the inputs are

    • amount is larger than normal?

    • the recipient is new?

    large amount?

    new recipient?

    approve?

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • show content, if the inputs are

    • is user younger than 18?

    • is the content flagged as adult?

    less than 18?

    adult content?

    show content

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • allow 2 processes writing to the same place, if the inputs are

    • is process A writing?

    • is process B writing?

    process A write?

    process B write?

    allow?

    yes

    yes

    no

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes


test_tautology

The truth table for tautology is

first input

second input

return

True

True

True

True

False

True

False

True

True

False

False

True


RED: make it fail


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

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

    first input

    second input

    return

    True

    True

    True

    62    def test_logical_nand(self):
    63        logical_nand = src.truth_table.logical_nand
    64        self.assertFalse(logical_nand(True, True))
    65        self.assertTrue(logical_nand(True, False))
    66        self.assertTrue(logical_nand(False, True))
    67        self.assertTrue(logical_nand(False, False))
    68
    69    def test_tautology(self):
    70        tautology = src.truth_table.tautology
    71        self.assertTrue(tautology(True, True))
    72
    73
    74# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

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

    truth_table.py does not have tautology in it.


GREEN: make it pass


I add the function to truth_table.py

41  def logical_nand(first_input, second_input):
42      return logical_negation(
43          logical_conjunction(
44              first_input, second_input
45          )
46      )
47      return not (first_input and second_input)
48
49
50  def tautology(first_input, second_input):
51      return True

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


REFACTOR: make it better


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

    first input

    second input

    return

    True

    False

    True

    69    def test_tautology(self):
    70        tautology = src.truth_table.tautology
    71        self.assertTrue(tautology(True, True))
    72        self.assertTrue(tautology(True, False))
    73
    74
    75# Exceptions seen
    

    the test is still green. tautology 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

  • I add an assertion for the next case, which is when the first input is False and the second input is True

    first input

    second input

    return

    False

    True

    True

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

    the test is still green. tautology returns True

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

    • if the first input is True

  • I add an assertion for the last case, which is when the first input is False and the second input is False

    first input

    second input

    return

    False

    False

    True

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

    still green, there is only one result for this operation.

  • I add a git commit message in the other terminal

    git commit -am 'add tautology'
    

Tautology always returns True, it does not care about the inputs. It is the opposite of contradiction which always returns False.


examples of Tautology


  • work, if the inputs are

    • do I have work?

    • do I feel like working?

    have work?

    feel like work?

    work?

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • the customer is always right, if the inputs are

    • did customer complain?

    • is customer new?

    complain?

    new?

    is customer king?

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • the sun is real, if the inputs are

    • am I awake?

    • is it bright or dark outside?

    awake?

    bright/dark?

    is sun real?

    yes

    bright

    yes

    yes

    dark

    yes

    no

    bright

    yes

    no

    dark

    yes

  • I can get better, if the inputs are

    • am I good?

    • have I done this before?

    good?

    done before?

    can I get better?

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes

  • a noun is the name of a person, place or thing, if the inputs are

    • is a person?

    • is a place or a thing?

    person?

    place/thing?

    noun is a name

    yes

    place

    yes

    yes

    thing

    yes

    no

    place

    yes

    no

    thing

    yes

  • unconditional love, if the inputs are

    • has the person been good?

    • does the person deserve love?

    been good?

    deserving?

    love?

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    yes


test_logical_disjunction

The truth table for logical_disjunction is

first input

second input

return

True

True

True

True

False

True

False

True

True

False

False

False


RED: make it fail


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

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

    first input

    second input

    return

    True

    True

    True

    69    def test_tautology(self):
    70        tautology = src.truth_table.tautology
    71        self.assertTrue(tautology(True, True))
    72        self.assertTrue(tautology(True, False))
    73        self.assertTrue(tautology(False, True))
    74        self.assertTrue(tautology(False, False))
    75
    76    def test_logical_disjunction(self):
    77        logical_disjunction = (
    78            src.truth_table.logical_disjunction
    79        )
    80        self.assertTrue(logical_disjunction(True, True))
    81
    82
    83# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.truth_table'
                    has no attribute 'logical_disjunction'.
                    Did you mean: 'logical_conjunction'?
    

    there is no logical_disjunction in truth_table.py in the src folder, yet.


GREEN: make it pass


I add the function to truth_table.py

50def tautology(first_input, second_input):
51    return True
52
53
54def logical_disjunction(first_input, second_input):
55    return True

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


REFACTOR: make it better


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

    first input

    second input

    return

    True

    False

    True

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

    the test is still green. logical_disjunction 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

    so far this is the same as tautology.

  • I add an assertion for the next case, which is when the first input is False and the second input is True

    first input

    second input

    return

    False

    True

    True

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

    the test is still green. logical_disjunction still looks like Tautology, it returns

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

    • True, if the first input is True

  • I add an assertion for the fourth case, which is when the first input is False and the second input is False

    first input

    second input

    return

    False

    False

    False

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

    the terminal is my friend, and shows AssertionError

    AssertionError: True is not false
    

    because the logical_disjunction function returns True and this assertion expects False.

  • I add if statements for the new case to logical_disjunction in truth_table.py

    54def logical_disjunction(first_input, second_input):
    55    if first_input == False:
    56        if second_input == False:
    57            return False
    58    return True
    

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

  • I add the bool built-in function

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    if bool(first_input) == False:
    57        # if second_input == False:
    58        if bool(second_input) == False:
    59            return False
    60    return True
    

    the test is still green.

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

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    if bool(not first_input) == True:
    58        # if second_input == False:
    59        # if bool(second_input) == False:
    60        if bool(not second_input) == True:
    61            return False
    62    return True
    

    still green.

  • I remove == True

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    # if bool(not first_input) == True:
    58    if bool(not first_input):
    59        # if second_input == False:
    60        # if bool(second_input) == False:
    61        # if bool(not second_input) == True:
    62        if bool(not second_input):
    63            return False
    64    return True
    

    green.

  • I remove bool

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    # if bool(not first_input) == True:
    58    # if bool(not first_input):
    59    if not first_input:
    60        # if second_input == False:
    61        # if bool(second_input) == False:
    62        # if bool(not second_input) == True:
    63        # if bool(not second_input):
    64        if not second_input:
    65            return False
    66    return True
    

    still green because I can assume the following substitutions for if something == False:

    • if the value of something is False

      if something           == False
      if bool(something    ) == False
      if bool(False        ) == False
      if False               == False
      if True                == True  # use equality
      if True                         # remove '== True'
      if not False                    # change to terms of False
      if not something
      
    • if the value of something is True

      if something           == False
      if bool(something    ) == False
      if bool(True         ) == False
      if True                == False
      if False               == True  # use equality
      if False                        # remove '== True'
      if not True                     # change to terms of True
      if not something
      

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

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

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    # if bool(not first_input) == True:
    58    # if bool(not first_input):
    59    # if not first_input:
    60        # if second_input == False:
    61        # if bool(second_input) == False:
    62        # if bool(not second_input) == True:
    63        # if bool(not second_input):
    64        # if not second_input:
    65    if not first_input and not second_input:
    66            return False
    67    return True
    

    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

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    # if bool(not first_input) == True:
    58    # if bool(not first_input):
    59    # if not first_input:
    60        # if second_input == False:
    61        # if bool(second_input) == False:
    62        # if bool(not second_input) == True:
    63        # if bool(not second_input):
    64        # if not second_input:
    65    if not first_input and not second_input:
    66        return False
    67    else:
    68        return True
    

    green.

  • I use Logical Negation (NOT) to change the else clause to the opposite of the if statement

    65    if not first_input and not second_input:
    66        return False
    67    # else:
    68    if not (not first_input and not second_input):
    69        return True
    

    still green.

  • I move the first if statement below the new one

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    # if bool(not first_input) == True:
    58    # if bool(not first_input):
    59    # if not first_input:
    60        # if second_input == False:
    61        # if bool(second_input) == False:
    62        # if bool(not second_input) == True:
    63        # if bool(not second_input):
    64        # if not second_input:
    65    # else:
    66    if not (not first_input and not second_input):
    67        return True
    68    if not first_input and not second_input:
    69        return False
    

    the test is still green.

  • I change it to an else clause

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    # if bool(not first_input) == True:
    58    # if bool(not first_input):
    59    # if not first_input:
    60        # if second_input == False:
    61        # if bool(second_input) == False:
    62        # if bool(not second_input) == True:
    63        # if bool(not second_input):
    64        # if not second_input:
    65    # else:
    66    if not (not first_input and not second_input):
    67        return True
    68    # if not first_input and not second_input:
    69    else:
    70        return False
    

    still green.

  • I add a conditional expression (ternary operator)

    65    # else:
    66    # if not (not first_input and not second_input):
    67    #     return True
    68    # if not first_input and not second_input:
    69    # else:
    70    #     return False
    71    return (
    72        True if
    73        not (not first_input and not second_input)
    74        else False
    75    )
    

    green.

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

    65    # else:
    66    # if not (not first_input and not second_input):
    67    #     return True
    68    # if not first_input and not second_input:
    69    # else:
    70    #     return False
    71    return (
    72        # True if
    73        not (not first_input and not second_input)
    74        # else False
    75    )
    

    still green.

  • not happens 3 times in this statement. I “multiply” it by every symbol in the statement to try to make it simpler

    65    # else:
    66    # if not (not first_input and not second_input):
    67    #     return True
    68    # if not first_input and not second_input:
    69    # else:
    70    #     return False
    71    # return (
    72        # True if
    73        # not (not first_input and not second_input)
    74        # else False
    75    # )
    76    return (
    77        (not not first_input)
    78        (not and)
    79        (not not second_input)
    80    )
    

    the terminal is my friend and shows SyntaxError

    SyntaxError: invalid syntax
    

    I cannot negate and this way

  • I add SyntaxError to the list of Exceptions seen in test_binary.py

    86# Exceptions seen
    87# AttributeError
    88# TypeError
    89# AssertionError
    90# SyntaxError
    
  • I change not and to or in the logical_disjunction function in truth_table.py

    76    return (
    77        (not not first_input)
    78        # (not and)
    79        or
    80        (not not second_input)
    81    )
    

    the test is green again

  • I remove not not because they cancel out

    76    return (
    77        # (not not first_input)
    78        first_input
    79        # (not and)
    80        or
    81        # (not not second_input)
    82        second_input
    83    )
    

    the test is still green. Do two nots make a right?

  • logical_disjunction returns not ((not first_input) and (not second_input)) which is the Logical Negation (NOT) of the Logical Conjunction of the Logical Negation of first_input, and the Logical Negation of second_input.

    logical_negation(
        logical_conjunction(
            logical_negation(first_input),
            logical_negation(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 and exits the function

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

    • 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

    This means that in the four cases

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

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

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

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

      not ((not first) and (not second))
      not ((not False) and (not False) )
      not (True        and True        )
      not  True        # not logical_conjunction(True, True)
      False
      

    first

    second

    not first

    not second

    ((not first) and (not second))

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

    True

    True

    False

    False

    False

    True

    True

    False

    False

    True

    False

    True

    False

    True

    True

    False

    False

    True

    False

    False

    True

    True

    True

    False

    I add a return statement to show this

    54def logical_disjunction(first_input, second_input):
    55    # if first_input == False:
    56    # if bool(first_input) == False:
    57    # if bool(not first_input) == True:
    58    # if bool(not first_input):
    59    # if not first_input:
    60        # if second_input == False:
    61        # if bool(second_input) == False:
    62        # if bool(not second_input) == True:
    63        # if bool(not second_input):
    64        # if not second_input:
    65    # else:
    66    # if not (not first_input and not second_input):
    67    #     return True
    68    # if not first_input and not second_input:
    69    # else:
    70    #     return False
    71    # return (
    72        # True if
    73        # not (not first_input and not second_input)
    74        # else False
    75    # )
    76    return logical_negation(
    77        logical_conjunction(
    78            logical_negation(first_input),
    79            logical_negation(second_input)
    80        )
    81    )
    82    return (
    83        # (not not first_input)
    84        first_input
    85        # (not and)
    86        or
    87        # (not not second_input)
    88        second_input
    89    )
    

    still green.

  • I remove the commented lines

    54def logical_disjunction(first_input, second_input):
    55    return logical_negation(
    56        logical_conjunction(
    57            logical_negation(first_input),
    58            logical_negation(second_input)
    59        )
    60    )
    61    return first_input or second_input
    

    I can use any of these two `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 logical_disjunction'
    

Logical Disjunction also known as “OR” returns

  • first_input or second_input

  • False, if the first input is False and the 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


examples of Logical Disjunction


  • the system works, if the inputs are

    • is the main system running?

    • is the backup system running?

    is main running?

    is backup running?

    is system working?

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    no

  • emergency room triage, if the inputs are

    • is it life threatening?

    • is patient a child?

    life threat?

    child?

    treat immediately

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    no

  • automatic door that opens, if the inputs are

    • has keycard?

    • entered code?

    has keycard?

    entered code?

    open door

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    no

  • give discount if the person is already a customer or has a coupon, if the inputs are

    • is already a customer?

    • has coupon code?

    already customer

    has coupon

    give discount

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    no

  • elevator moves, if the inputs are

    • did I push the button?

    • did someone else push the button?

    I pushed

    someone else pushed

    elevator moves

    yes

    yes

    yes

    yes

    no

    yes

    no

    yes

    yes

    no

    no

    no


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

  • 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

and

Three binary operations were written with AND, three with NOT and one was written with OR.

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


code from the chapter

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


what is next?

Would you like test even more 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.