Outline. Introduction. 2 Proof of Correctness. 3 Final Notes. Precondition P 1 : Inputs include

Similar documents
Outline. Computer Science 331. Three Classical Algorithms. The Sorting Problem. Classical Sorting Algorithms. Mike Jacobson. Description Analysis

Outline. Computer Science 331. Heap Shape. Binary Heaps. Heap Sort. Insertion Deletion. Mike Jacobson. HeapSort Priority Queues.

Mathematical Induction

Outline. Computer Science 331. Definitions: Paths and Their Costs. Computation of Minimum Cost Paths

Outline. Definition. 2 Height-Balance. 3 Searches. 4 Rotations. 5 Insertion. 6 Deletions. 7 Reference. 1 Every node is either red or black.

SEQUENCES, MATHEMATICAL INDUCTION, AND RECURSION

Computer Science 331

Lecture 1 Contracts : Principles of Imperative Computation (Fall 2018) Frank Pfenning

The divide and conquer strategy has three basic parts. For a given problem of size n,

Outline. Computer Science 331. Course Information. Assessment. Contact Information Assessment. Introduction to CPSC 331

1KOd17RMoURxjn2 CSE 20 DISCRETE MATH Fall

Reasoning and writing about algorithms: some tips

Solutions to the Second Midterm Exam

Lecture 1 Contracts. 1 A Mysterious Program : Principles of Imperative Computation (Spring 2018) Frank Pfenning

Dynamic Programming Algorithms

CSE 20 DISCRETE MATH WINTER

CS 161 Computer Security

Fundamental mathematical techniques reviewed: Mathematical induction Recursion. Typically taught in courses such as Calculus and Discrete Mathematics.

Complexity, Induction, and Recurrence Relations. CSE 373 Help Session 4/7/2016

Hardware versus software

COMP250: Loop invariants

MergeSort, Recurrences, Asymptotic Analysis Scribe: Michael P. Kim Date: September 28, 2016 Edited by Ofir Geri

Announcements. CS243: Discrete Structures. Strong Induction and Recursively Defined Structures. Review. Example (review) Example (review), cont.

Reasoning about programs

Last time. Reasoning about programs. Coming up. Project Final Presentations. This Thursday, Nov 30: 4 th in-class exercise

CS 506, Sect 002 Homework 5 Dr. David Nassimi Foundations of CS Due: Week 11, Mon. Apr. 7 Spring 2014

Outline. Computer Science 331. Analysis of Prim's Algorithm

SEQUENCES, MATHEMATICAL INDUCTION, AND RECURSION

CSE101: Design and Analysis of Algorithms. Ragesh Jaiswal, CSE, UCSD

Foundations, Reasoning About Algorithms, and Design By Contract CMPSC 122

An Annotated Language

Violations of the contract are exceptions, and are usually handled by special language constructs. Design by contract

Unit #3: Recursion, Induction, and Loop Invariants

How invariants help writing loops Author: Sander Kooijmans Document version: 1.0

Discrete Mathematics Lecture 4. Harper Langston New York University

Algorithms. Notations/pseudo-codes vs programs Algorithms for simple problems Analysis of algorithms

Recursion and Induction

introduction to Programming in C Department of Computer Science and Engineering Lecture No. #40 Recursion Linear Recursion

Reading 8 : Recursion

CS 3512, Spring Instructor: Doug Dunham. Textbook: James L. Hein, Discrete Structures, Logic, and Computability, 3rd Ed. Jones and Barlett, 2010

Overview. A mathema5cal proof technique Proves statements about natural numbers 0,1,2,... (or more generally, induc+vely defined objects) Merge Sort

CS2 Algorithms and Data Structures Note 1

Recall our recursive multiply algorithm:

Recursive Definitions Structural Induction Recursive Algorithms

Softwaretechnik. Program verification. Software Engineering Albert-Ludwigs-University Freiburg. June 30, 2011

Lecture 3 Notes Arrays

Chapter 3 (part 3) Describing Syntax and Semantics

12/30/2013 S. NALINI,AP/CSE

Recursion and Structural Induction

Dynamic Programming Algorithms

15 212: Principles of Programming. Some Notes on Induction

Lecture 3.4: Recursive Algorithms

1 Introduction. 2 InsertionSort. 2.1 Correctness of InsertionSort

Spark verification features

MergeSort, Recurrences, Asymptotic Analysis Scribe: Michael P. Kim Date: April 1, 2015

Introduction to Axiomatic Semantics

Lecture Notes on Contracts

Induction and Semantics in Dafny

Recursion. What is Recursion? Simple Example. Repeatedly Reduce the Problem Into Smaller Problems to Solve the Big Problem

Hoare Logic: Proving Programs Correct

Backward Reasoning: Rule for Assignment. Backward Reasoning: Rule for Sequence. Simple Example. Hoare Logic, continued Reasoning About Loops

Lecture Notes on Arrays

AXIOMS FOR THE INTEGERS

Lecture 15. Error-free variable length schemes: Shannon-Fano code

Lecture 1. 1 Notation

γ(ɛ) (a, b) (a, d) (d, a) (a, b) (c, d) (d, d) (e, e) (e, a) (e, e) (a) Draw a picture of G.

Analyze the obvious algorithm, 5 points Here is the most obvious algorithm for this problem: (LastLargerElement[A[1..n]:

Semantics. There is no single widely acceptable notation or formalism for describing semantics Operational Semantics

Fall Recursion and induction. Stephen Brookes. Lecture 4

Definition For vertices u, v V (G), the distance from u to v, denoted d(u, v), in G is the length of a shortest u, v-path. 1

Review: Hoare Logic Rules

Lectures 20, 21: Axiomatic Semantics

Outline. Binary Tree

Lecture 2: Getting Started

Recursively Enumerable Languages, Turing Machines, and Decidability

Reasoning About Imperative Programs. COS 441 Slides 10

CPSC 121: Models of Computation

Lecture 5 - Axiomatic semantics

Softwaretechnik. Program verification. Albert-Ludwigs-Universität Freiburg. June 28, Softwaretechnik June 28, / 24

Introduction to Axiomatic Semantics (1/2)

Exact Algorithms Lecture 7: FPT Hardness and the ETH

The divide-and-conquer paradigm involves three steps at each level of the recursion: Divide the problem into a number of subproblems.

Unit #2: Recursion, Induction, and Loop Invariants

CS Lecture 19: Loop invariants

Lecture 11: In-Place Sorting and Loop Invariants 10:00 AM, Feb 16, 2018

COMP 250 Fall Recursive algorithms 1 Oct. 2, 2017

Propositional Logic Formal Syntax and Semantics. Computability and Logic

6.001 Notes: Section 4.1

Introduction to Axiomatic Semantics (1/2)

Subset sum problem and dynamic programming

Assignment 1. Stefano Guerra. October 11, The following observation follows directly from the definition of in order and pre order traversal:

CSC212. Data Structure. Lecture 12 Reasoning about Recursion. Instructor: George Wolberg Department of Computer Science City College of New York

Arguing for program correctness and writing correct programs

CS 206 Introduction to Computer Science II

Integers and Mathematical Induction

Binary Search. Roland Backhouse February 5th, 2001

Induction and Recursion. CMPS/MATH 2170: Discrete Mathematics

Symbolic Execution and Proof of Properties

Incremental Proof Development in Dafny

CS173 Longest Increasing Substrings. Tandy Warnow

Transcription:

Outline Computer Science 331 Correctness of Algorithms Mike Jacobson Department of Computer Science University of Calgary Lectures #2-4 1 What is a? Applications 2 Recursive Algorithms 3 Final Notes Additional References Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 1 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 2 / 37 What is a? What is a? How Do We Specify a Computational Problem? Example: Specification of a Search Problem A computational problem is specified by one (or more) pairs of preconditions and postconditions. Precondition: A condition that must be satisfied when the execution of a program begins. This generally involves the algorithm s inputs as well as initial values of global variables. Postcondition: A condition that should be satisfied when the execution of a program ends. This might be A set of relationships between the values of inputs (and the values of global variables when execution started) and the values of outputs (and the values of global variables on a program s termination), or A description of output generated, or exception(s) raised. Precondition P 1 : Inputs include n: a positive integer A: an integer array of length n, with entries A[0], A[1],..., A[n-1] key: An integer found in the array (ie, such that A[i] = key for at least one integer i between 0 and n-1) Postcondition Q 1 : Output is the integer i such that 0 i < n, A[j] key for every integer j such that 0 j < i, and such that A[i] = key Inputs (and other variables) have not changed This describes what should happen for a successful search. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 3 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 4 / 37

What is a? What is a? Example: Specification of a Search Problem (cont.) Precondition P 2 : Inputs include n: a positive integer A: an integer array of length n, with entries A[0], A[1],..., A[n-1] key: An integer not found in the array (ie, such that A[i] key for every integer i between 0 and n-1) Postcondition Q 2 : A notfoundexception is thrown Inputs (and other variables) have not changed This describes what should happen for an unsuccessful search. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 5 / 37 Example: Specification of a Search Problem A problem can be specified by multiple precondition-postcondition pairs (P 1, Q 1 ); (P 2, Q 2 );..., ; (P k, Q k ) as long as it is not possible for more than one of the preconditions to be satisfied at the same time. P 1, P 2,..., P k For example, if P 1, Q 1, P 2, and Q 2 are as in the previous slides then the pair of precondition-postcondition pairs (P 1, Q 1 ); (P 2, Q 2 ) could specify a search problem in which the input is expected to be any positive integer n, integer array A of length n, and integer key. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 6 / 37 What is a? What is a? When is an Algorithm Correct? Suppose, first, that a problem is specified by a single precondition-postcondition pair (P, Q). An algorithm (that is supposed to solve this problem) is correct if it satisfies the following condition: If then inputs satisfy the given precondition P and the algorithm is executed the algorithm eventually halts, and the given postcondition Q is satisfied on termination. Note: This does not tell us anything about what happens if the algorithm is executed when P is not satisfied. When is an Algorithm Correct? Suppose, next, that k 2 and that a problem is specified by a sequence of k precondition-postcondition pairs (P 1, Q 1 ); (P 2, Q 2 );... ; (P k, Q k ) where it is impossible for more than one of the preconditions to be satisfied at the same time. An algorithm (that is supposed to solve this problem) is correct if the following is true for every integer i between 1 and k: If then inputs satisfy the given precondition P i and the algorithm is executed the algorithm eventually halts, and the given postcondition Q i is satisfied on termination. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 7 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 8 / 37

What is a? Applications When is an Algorithm Correct? Why are Proofs of Correctness Useful? A consequence of the previous definitions: Consider a problem specified by a sequence of k precondition-postcondition pairs (P 1, Q 1 ); (P 2, Q 2 );... ; (P k ; Q k ). Then an algorithm that is supposed to solve this problem is correct if and only if it is a correct solution for each of the k problems that are each specified by the single precondition-postcondition pair P i and Q i, for i between 1 and k. = It is sufficient to consider problems that are specified by a single precondition and postcondition (and we will do that, from now on). Who Generates Proofs of Correctness? Algorithm designers (whenever the algorithm is not obvious). Other people need to see evidence that this new algorithm really does solve the problem! Note that testing cannot do this (in general). Who Uses Proofs of Correctness? Anyone coding, debugging, testing, or otherwise maintaining software implementing any nontrivial algorithm need to know why (or how ) the algorithm does what it is supposed in order to do their jobs well. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 9 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 10 / 37 One Part of a : How to Prove of Algorithms? : If inputs satisfy the precondition P, and algorithm or program S is executed, then either S halts and its inputs and outputs satisfy the postcondition Q or S does not halt, at all. Generally written as {P} S {Q} Note: Detailed proofs rely heavily on discrete math and logic. Consider algorithm S: Divide S into sections S 1 ; S 2 ;... ; S K assignment statements loops control statements (i.e., if-then-else) (other programming constructs) Identify intermediate assertions R i so that {P} S 1 {R 1 } {R 1 } S 2 {R 2 }... {R K 1 } S K {Q} After proving each of these, we can then conclude that {P} S 1 ; S 2 ;... ; S K {Q} equivalently, {P} S {Q} Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 11 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 12 / 37

Example: Proof of Example: Pseudocode Problem Definition: Finding the largest entry in an integer array. Precondition P: Inputs include n: a positive integer A: an integer array of length n, with entries A[0],..., A[n-1] Postcondition Q: Output is an integer i such that 0 i < n, A[i] A[j] for every integer j such that 0 j < n Inputs (and other variables) have not changed int FindMax(A, n) i = 0 j = 1 while (j < n) do if A[j] > A[i] then i = j end if j = j + 1 end while return i Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 13 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 14 / 37 Example: Intermediate Assertion Intermediate Assertion I : n: a positive integer A: an integer array of length n i = 0 and j = 1 Divide into Sections: {P} i = 0, j = 1 {I } while (j < n) do if A[j] > A[i] then i = j end if j = j + 1 end while {Q} Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 15 / 37 Example: Proof of each Section Prove the correctness of each section of the algorithm using the intermediate assertion I : 1 First Section: {P} i = 0; j = 1 {I } correctness is trivial 2 Second Section: {I } while... end while {Q} proof is needed = In CPSC 331, we focus on proving correctness of simple loops and recursive programs. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 16 / 37

Correctness of Loops Example: Loop Invariant Problem: Show that {P} while G do S end while {Q} Observation: There is generally some condition that we expect to hold at the beginning of every execution of the body of the loop. Such a condition is called a loop invariant. A condition R is a Loop Invariant if: 1 Base Property: P implies that R is True before the first iteration of the loop and after testing G 2 Inductive Property: if R is satisfied at the beginning of the ith execution of the loop body and there is an i + 1st execution, then the loop invariant holds immediately before that execution. Claim: Assertion R is a loop invariant: 0 i < j 1 j < n A[i] A[k] for 0 k < j Prove correct by induction on the number of iterations of the loop body. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 17 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 18 / 37 Mathematical Induction Proof of the Loop Invariant Problem: For all integers k k 0, prove that property P(k) is True. Proof by Induction: 1 Base Case: Show that P(k 0 ) is True 2 Inductive Step: Show that if P(k) is True for some arbitrary integer k k 0 (the induction hypothesis), then P(k + 1) is True. choose an arbitrary k k 0 show that P(k + 1) is True if P(k) is True Exercise: Prove that 2 2n 1 is divisible by 3 for all integers n 1. Proof. 1 Base Property: Before the first iteration of the loop: i = 0 and j = 1; j < n; A[0] A[0] 2 Inductive Hypothesis: Assume that the loop body is executed l > 0 times and that R is satisfied at the beginning of the lth execution. i old, j old : values of i and j before the lth execution of the loop body. At the end of the lth execution, we have j = j old + 1 and thus: 1 j n (since j old < n) and 0 i < j (since i j old = j 1) If the if-condition was true, then A[j old ] > A[i old ] and i = j old. By IH A[i old ] > A[k] for 0 k < j old. But A[i] = A[j old ] > A[i old ] and j = j old + 1, so A[i] A[k] for 0 k < j. Otherwise, i = i old and A[i] A[j old ], so A[i] A[k] for 0 k < j (since j = j old + 1). If there is a l + 1st execution of the loop body, then the loop test must pass before it, so j < n and R holds. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 19 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 20 / 37

Correctness of Loops: Summary Problem: Prove that Solution: {P} while G do S end while {Q} 1 Identify a loop invariant R and prove: Base Property: P implies that R is True before the first iteration of the loop Inductive Property: if R is satisfied at the beginning of the ith execution of the loop body and there is an i + 1st execution, then the loop invariant holds immediately before that execution. Note: essentially a proof by induction that the loop invariant holds after zero or more executions of the loop body. 2 Prove the correctness of the postcondition: if the loop terminates after zero or more iterations, the Truth of R implies that Q is satisfied Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 21 / 37 Example: Last Step Problem: Prove that if the loop terminates after zero or more iterations, the Truth of implies that is satisfied. Solution: R : 0 i < j, 1 j < n, A[i] A[k] for 0 k < j Q : 0 i < n, A[i] A[j] for 0 j < n Upon termination, R holds except that j = n R implies that 0 i < j, so 0 i < n (part 1 of Q) R implies that A[i] A[k] for 0 k < j = n (part 2 of Q) Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 22 / 37 Example 2: of Loops Prove the correctness of the following algorithm. Precondition (P): n is a positive integer Postcondition (Q): n is unchanged and s = Sum(n) i = 1 s = 1 while i < n do i = i + 1 s = s + i end while n j j=1 Claim: Sum(n) is Partially Correct Proof. Proof that 1 i < n and s = i j=1 j is a loop invariant: 1 True before first iteration: 1 i = 1 < n and s = 1 2 Inductive Property: Assume that the loop body is executed k > 0 times and that LI is satisfied at the beginning of the kth execution. At the end of the kth execution, since i is increased by 1 : 1 i n s = ( i 1 j=1 j) + i = i j=1 j If there is a k + 1st execution of the loop body, then the loop test must pass before it, so i < n and LI holds. Proof of partial correctness: upon termination: LI holds except that i = n, so s = n j=1 j Q Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 23 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 24 / 37

Another Part: of Loops : If then inputs satisfy the precondition P, and algorithm or program S is executed, S is guaranteed to halt! Note: + Total Correctness! and are often (but not always) considered separately because... Different independent arguments are used for each Sometimes one condition holds, but not the other! Then the algorithm is not totally correct... but something interesting can still be established. Problem: Show that if the precondition P is satisfied and the loop while G do S end while is executed, then the loop eventually terminates. Suppose that a loop invariant R for the precondition P and the above loop has already been found. You should have done this when proving the partial correctness of this loop also useful to prove termination. Proof Rule: To establish the above termination property, prove each of the following. 1 If the loop invariant R is satisfied and the loop body S is executed then the loop body terminates. 2 The loop body is only executed a finite number of times. (Proof technique is based on the concept of a Loop Variant.) Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 25 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 26 / 37 of Loops, Continued Definition: A loop variant for a loop while G do S end while is a function f L of program variables that satisfies the following additional properties: 1 f L is integer-valued 2 The value of f L is decreased by at least one every time the loop body S is executed 3 If the value of f L is less than or equal to zero then the loop guard G is False (ie., the loop terminates) Note: The initial value of f L is an upper bound for the number of executions of the loop body before the loop terminates. of Loops, Continued Problem: Prove that if the precondition P is satisfied and the loop while G do S end while is executed, then the loop eventually terminates. Solution: 1 Show that if the loop invariant is satisfied and the loop body is executed then the loop body terminates 2 Identify a loop variant f L : f L is an integer valued function The value of f L is decreased by at least one every time the loop body is executed If the value of f L is less than or equal to zero then the loop guard is False Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 27 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 28 / 37

Recursive Algorithms Example: of Loops Correctness of Recursive Algorithms Claim: Sum(n) terminates. Proof. 1 Loop body always terminates (single statements only) 2 Loop variant: f (n, i) = n i f (n, i) is an integer valued function after every iteration, i increases by 1 and thus f (n, i) decreases by 1 if f (n, i) 0 then i n and the loop terminates (number of iterations = f (n, 1) = n 1) Suppose method A calls itself (but does not call any other methods). In this case, it is often possible to prove the correctness of this method using strong mathematical induction, proceeding by induction on the size of the inputs. Base Case: base cases of the recursive algorithm Inductive Step: algorithm is correct for all inputs of size up to n, show that it is correct for inputs of size n + 1 Proof proceeds by proving correctness while assuming the induction hypothesis (i.e., every recursive call returns the correct output). Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 29 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 30 / 37 Recursive Algorithms Recursive Algorithms Strong Mathematical Induction Example: of Recursive Algorithms Problem: For all integers k k 0, prove that property P(k) is True. Proof by Strong form of Induction: 1 Base Case: Show that P(k 0 ) is True 2 Inductive Step: Show that if P(i) is True for all integers k 0 i k then P(k + 1) is True. choose an arbitrary k k 0 show that P(k + 1) is True if P(k), P(k 1),..., P(k 0 ) are True Prove the correctness of the following algorithm. Precondition: i is a positive integer Postcondition: the value returned is the i th Fibonacci number, F i long Fib(i) if i == 0 then return 0 end if if i == 1 then return 1 end if return Fib(i-1) + Fib(i-2) Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 31 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 32 / 37

Example, Continued Claim: Fib(i) is correct. Recursive Algorithms Final Notes Applications to Software Development Proof. 1 Base Case: The algorithm is correct for i = 0 and i = 1 2 Inductive Step: Assume that Fib(i) for i = 0, 1,..., k (k 1) returns the i-th Fibonacci number denoted by F i. Show that Fib(k + 1) returns the (k + 1)-th Fibonacci number, F k+1. Since k + 1 > 1, we have: Fib(k + 1) = Fib(k) + Fib(k 1) Using the induction hypothesis, it follows that A proof of correctness of an algorithm includes detailed information about the expected state of inputs and variables at every step during the computation. This information can be included in documentation as an aid to other developers. It also facilitates effective testing and debugging. Self-study exercises can be used to learn more about this. Fib(k + 1) = F k + F k 1 = F k+1, i.e. computes the correct value and terminates. Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 33 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 34 / 37 Final Notes What You Need to Be Able to Do! Final Notes Can This All Be Automated? The following questions might come to mind. Understand proofs of correctness We will supply these throughout the course. Proofs are an aid to describing why and how and algorithm works. Provide your own proofs for simple iterative and recursive algorithms Iterative: loop invariants (partial correctness) and loop variants (termination) Recursive: induction Q: Is it possible to write a program that decides whether a given program is correct, providing a proof of correctness of the given program, if it is? A: No! the simpler problem of determining whether a given program halts on a given input is undecidable: It has been proved that no computer program can solve this problem! Q: Can a computer program be used to check a proof of correctness? A: See our courses in Artificial Intelligence for information about this! Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 35 / 37 Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 36 / 37

Final Notes Additional References References to Algorithms, Section 2.1 Recommended References: Susanna S. Epp Discrete Mathematics with Applications, Third Edition See Section 4.5 Michael Soltys An to the Analysis of Algorithms Chapter 1 contains an introduction to proofs of correctness and is freely available online! Mike Jacobson (University of Calgary) Computer Science 331 Lectures #2-4 37 / 37