Reasoning Toolkit & Manuscript Database
Apologetic question: "Can faith be reasoned about — and is the Bible reliable?"
Project 3 — Reasoning Toolkit & Manuscript Database
“Come now, let us reason together, says the LORD.” — Isaiah 1:18
“The grass withers, the flower fades, but the word of our God will stand forever.” — Isaiah 40:8
Chapter: 3 — Functions and Collections
Due: End of Week 3
Submit: A link to your code — an OnlineGDB project URL or a public GitHub repo URL — with p3_toolkit.cpp as the main source file. See Appendix A for the full workflow.
Allowed tools: Everything through Chapter 3 — all of Chapters 1–2 (types, arithmetic, cin/cout, conditionals, loops), plus functions you define yourself (parameters, return values, prototypes, pass-by-reference), fixed-size arrays, parallel arrays, std::string operations, linear search, and one sort.
Not yet allowed: Structs, classes, pointers, dynamic memory, vector or any other STL container. If you find yourself reaching for one of those, you have wandered into Weeks 4–6. Come back.
Estimated time: Normal 5–7 hrs · Medium 7–10 hrs · Hard 10–13 hrs
The Setup
Week 3 was one week with two halves, so Project 3 is one program with two halves.
The first half is a reasoning toolkit. The Christian intellectual tradition has used formal reasoning — logic, probability, evidence-weighing — for as long as there has been a tradition. Augustine reasoned about time and creation. Aquinas built his Five Ways on formal logical structure. Pascal’s Wager is an expected-value calculation. Plantinga’s modal arguments are multi-step logical proofs. That tradition is, structurally, a library of named, reusable arguments — which is exactly what a set of functions is. Nobody rebuilds the cosmological argument from scratch each time they need it; they name it, structure it, and reuse it. This project asks you to make that move in code: write the Bayesian update formula once, by hand, and then call it by name.
The second half is a manuscript database. There is a question people your age get asked, sometimes politely and sometimes not: “Is the Bible reliable?” One layer of that question is textual transmission — do we have a credible record of what was originally written? The empirical answer is unusual. For the New Testament there are roughly 5,800 known Greek manuscripts, plus tens of thousands in Latin, Coptic, Syriac, Armenian, and Ge’ez. The earliest fragments, such as P52 (a scrap of John 18), sit within a handful of generations of the originals; P52’s traditional dating is around AD 125, though palaeographers such as Nongbri argue the evidence supports anywhere in AD 125–225. Homer’s Iliad, the next best-attested classical work, survives in roughly 1,700 manuscripts, the earliest more than four centuries after composition. After Homer the counts fall into the hundreds.
You are not arguing that case in this program. You are making a small slice of that dataset legible to a human being who runs your code — searchable, sortable, summarizable. That is a real and modest thing, and it is enough for Week 3.
Here is the design rule that welds the halves together, and it is the whole point of the project:
Every operation is its own function.
mainis a menu, a dispatcher, and I/O. Nothing else.
The toolkit is functions with no collections. The database is collections that would be miserable without functions. One program, one menu, two families of operations — and a main that decides what while the functions know how. If a calculation or a loop happens between a cin and a cout in main, it is in the wrong place, and the rubric will say so.
Coach’s Note — A warning this project inherits from the sixteen-week course and repeats on purpose: the toolkit does not settle anything. A Bayesian update fed sloppy priors produces confident garbage, and five manuscripts prove nothing about a tradition of thousands. What writing this code buys you is the felt sense of how much every conclusion depends on its inputs. Humility is part of the lesson, and it is the part that transfers outside of programming.
Learning Targets
By completing this project, you will demonstrate that you can:
- Define many functions with parameters, return values, and correct return types, including
void. - Compose functions — write one function whose body is mostly calls to others.
- Use pass-by-reference (
int& count) when a function genuinely must change the caller’s variable. - Use
constarray parameters for functions that only read, and understand why the non-constones can rewrite your data. - Declare fixed-size arrays, track a logical size separately from capacity, and traverse with bounds that never run off the end.
- Keep parallel arrays in lockstep across an add and across a sort.
- Implement linear search — exact and substring, case-insensitively — and selection sort, by hand.
- Write a menu-driven
mainthat dispatches to functions and contains no business logic. - Handle the
cin >>/getlinenewline problem with exactly onecin.ignore().
Normal Tier
Goal: One menu-driven C++ program, in one file, with eleven operations — five reasoning operations and six manuscript-roster operations — each implemented as its own function.
Required features
1. One source file. Everything lives in p3_toolkit.cpp. No second file, no headers of your own.
2. The five reasoning functions, with exactly these signatures:
double bayes_update(double prior, double L_H, double L_notH);
double complement(double p);
double conjunction_probability(double p1, double p2);
bool modus_ponens(bool premise_A, bool premise_A_implies_B);
bool is_valid_probability(double p);
bayes_updatereturns(L_H * prior) / (L_H * prior + L_notH * (1 - prior)). Write the math yourself; do not copy it out of the chapter without reading it.complementreturns1 - p.conjunction_probabilityreturnsp1 * p2, assuming independence — say so in a comment above the function, because the assumption is doing real work.modus_ponensreturnstrueonly when both premises aretrue. It is the simplest possible validity checker: if A, then B; A is true; therefore B.is_valid_probabilityreturnstruewhenpis between 0 and 1 inclusive.
Booleans must print as true / false, not 1 / 0. One cout << boolalpha; at the top of main does it.
3. The database: three parallel arrays and a count.
const int MAX_MANUSCRIPTS = 20;
string names[MAX_MANUSCRIPTS];
int centuries[MAX_MANUSCRIPTS];
string types[MAX_MANUSCRIPTS];
int count = 0;
names[i], centuries[i], and types[i] describe the same manuscript. That contract is unenforced by the compiler and entirely your problem — which is the lesson.
4. Seed data. At start-up, load these five real manuscripts. Centuries are approximate; the four standard manuscript types are papyrus, uncial, minuscule, and lectionary.
| Name | Century | Type |
|---|---|---|
| Codex Sinaiticus | 4 | uncial |
| P52 | 2 | papyrus |
| Codex Bezae | 5 | uncial |
| Bodmer Papyri | 3 | papyrus |
| Codex Vaticanus | 4 | uncial |
Seed them in that order — not in century order. If your database starts out already sorted, your “Sort by century” option will appear to do nothing and you will not be able to tell whether it works.
Seed by calling your own add_manuscript, not by writing an initializer list. That way the seeding is itself a test of the add path.
5. The six roster functions, plus their helpers. Suggested signatures — you may rename, but keep the shape (collection first, then its size, then the arguments, the parameter order from §3.16):
void add_manuscript(string names[], int centuries[], string types[], int& count,
string name, int century, string type);
void list_manuscripts(const string names[], const int centuries[],
const string types[], int count);
int search_by_name(const string names[], const int centuries[],
const string types[], int count, string query);
int search_by_century_range(const string names[], const int centuries[],
const string types[], int count, int low, int high);
void sort_by_century(string names[], int centuries[], string types[], int count);
void print_summary(const int centuries[], const string types[], int count);
Behaviour, one at a time:
add_manuscripttakescountby reference so the caller’s count actually grows. Assign into slotcount, then increment — that order, not the other one. Ifcount >= MAX_MANUSCRIPTS, printDatabase fulland return without writing anything. This is the one place in the project where a missing&produces a program that compiles, runs, and silently does nothing.list_manuscriptsprints every entry numbered1.throughN.for the human reader while indexing0throughN-1, with century and type alongside. Ifcount == 0it prints(empty)and returns.search_by_nameis a case-insensitive substring search: typingcodex,Codex, orCODEXmust all find the three codices, andp52must findP52. Print each match and return the number of matches so the caller can report it.search_by_century_rangetakes a low and a high century and prints every manuscript in[low, high], inclusive at both ends. Return the number of matches. Guard the nonsense case wherelow > high— print something honest and return0rather than looping zero times and reporting nothing.sort_by_centuryis selection sort, written by hand, ascending. Every swap must move all three arrays. Miss one block and your names will describe other manuscripts’ centuries: nothing crashes, nothing warns, and every report from then on is quietly wrong.print_summaryprints, at minimum: the number of entries, the earliest century, the latest century, the average century, and how many manuscripts are of typepapyrusand how many areuncial.
print_summary must compose — it does the printing and delegates the arithmetic to four small functions you also write:
int earliest_century(const int centuries[], int count);
int latest_century(const int centuries[], int count);
double average_century(const int centuries[], int count);
int count_by_type(const string types[], int count, string wanted);
Each of those gets a guard clause for count == 0, and average_century must return a double computed with static_cast<double> — the Chapter 1 integer-division trap is still hunting you in Week 3.
You will also need string to_lower(string s);. Write it once, take its parameter by value, and use it in both the name search and the type count. That single reuse is the entire thesis of the chapter.
6. The menu loop. Number the operations 1–11 with 12 as Exit:
| # | Operation | # | Operation | |
|---|---|---|---|---|
| 1 | Bayesian update | 7 | Search by name | |
| 2 | Complement | 8 | Search by century range | |
| 3 | Conjunction | 9 | Sort by century | |
| 4 | Modus ponens check | 10 | Summary statistics | |
| 5 | Is this a valid probability? | 11 | Add a manuscript | |
| 6 | List all manuscripts | 12 | Exit |
The loop reads a choice, prompts for whatever that operation needs, calls the function, prints the result, and comes back for another choice until the user picks 12. An unrecognized number gets a polite message, not a crash. Whether you reprint the whole menu every iteration or print it once and offer a “show the menu again” option (the reference solution uses 0 for that) is your call — both are fine.
7. Exactly one cin.ignore(). You read the choice with cin >> choice and manuscript names with getline, so you need it — once, immediately after reading the choice, before the dispatch. Read manuscript types with cin >> type (they are single words), and you will never need a second one. If a getline seems to be skipped, this is why (§3.15, Bug 11).
8. main contains no business logic. No arithmetic. No search loops. No sorting. No comparisons except the dispatch itself and the loop’s exit test. main may declare the arrays, print prompts, read input, call functions, and print what they return. That is the list.
9. Compiles cleanly with -Wall -Wextra turned on in your OnlineGDB compiler settings (Appendix A shows where the setting lives). Zero warnings, zero errors. A warning is not a style opinion; in Week 3 it is usually §3.20 telling you about a bug you have not noticed yet.
Example run
This is a real session, captured from a working Normal-tier solution. It exercises six of the eleven operations — the Bayesian update, name search, century-range search, the sort, a listing, and the summary. Your prompt wording may differ; the numbers may not.
Seeding the manuscript database...
Added Codex Sinaiticus. Database now holds 1.
Added P52. Database now holds 2.
Added Codex Bezae. Database now holds 3.
Added Bodmer Papyri. Database now holds 4.
Added Codex Vaticanus. Database now holds 5.
=== Reasoning Toolkit & Manuscript Database (5 manuscripts) ===
Reasoning 1 Bayesian update 2 Complement 3 Conjunction
4 Modus ponens 5 Valid probability?
Manuscripts 6 List all 7 Search by name
8 Search by century range 9 Sort by century
10 Summary statistics 11 Add a manuscript
0 Show this menu again 12 Exit
Choice (0 = menu, 12 = exit): 1
Prior P(H): 0.01
P(E|H): 0.99
P(E|~H): 0.01
Posterior P(H|E): 0.5
Choice (0 = menu, 12 = exit): 7
Search term: codex
Matches:
Codex Sinaiticus (century 4, uncial)
Codex Bezae (century 5, uncial)
Codex Vaticanus (century 4, uncial)
Found 3.
Choice (0 = menu, 12 = exit): 8
Earliest century: 2
Latest century: 3
Matches:
P52 (century 2, papyrus)
Bodmer Papyri (century 3, papyrus)
Found 2.
Choice (0 = menu, 12 = exit): 9
Sorted by century.
Choice (0 = menu, 12 = exit): 6
All manuscripts:
1. P52 (century 2, papyrus)
2. Bodmer Papyri (century 3, papyrus)
3. Codex Sinaiticus (century 4, uncial)
4. Codex Vaticanus (century 4, uncial)
5. Codex Bezae (century 5, uncial)
Choice (0 = menu, 12 = exit): 10
Summary:
entries: 5
earliest century: 2
latest century: 5
average century: 3.6
papyri: 2
uncials: 3
Choice (0 = menu, 12 = exit): 12
Goodbye.
Two things in there deserve a second look, because noticing them is the actual education.
The Bayesian update returned 0.5. A hypothesis you gave a one-percent prior, tested by evidence that shows up 99% of the time when the hypothesis is true and only 1% of the time when it is false, comes out at a coin flip. Not 99%. That is not a bug in the arithmetic; it is what the arithmetic of evidence does when the prior is low, and it is why careful people are slow to be moved by a single striking argument in either direction.
After the sort, P52 still says century 2 and papyrus. All three arrays moved together. Had one block of the lockstep swap been missing, that table would still print, still look plausible, and be wrong — the silent, permanent bug of §3.18.
Grading rubric — Normal (out of 100)
| Criterion | Points |
|---|---|
Compiles cleanly with -Wall -Wextra — zero warnings, zero errors | 8 |
Five reasoning functions, exact signatures, correct math; booleans print as true/false | 14 |
Parallel arrays names / centuries / types plus a count, seeded with the five manuscripts | 8 |
add_manuscript takes int& count, assigns then increments, refuses to exceed MAX_MANUSCRIPTS | 7 |
list_manuscripts numbers entries from 1 and prints (empty) when the count is 0 | 6 |
search_by_name is case-insensitive substring search and reports the match count | 10 |
search_by_century_range is inclusive at both ends and survives low > high | 8 |
sort_by_century is a hand-written selection sort that swaps all three arrays in lockstep | 12 |
print_summary composes earliest_century, latest_century, average_century, count_by_type; no integer division in the average | 8 |
Menu loop dispatches all eleven operations plus exit, handles a bad choice, uses exactly one cin.ignore() | 8 |
main contains no business logic — declarations, prompts, dispatch, and printing only | 6 |
Working OnlineGDB/GitHub link plus the reflection comment block at the top of p3_toolkit.cpp | 5 |
Medium Tier (+up to 25% extra credit)
Build the Normal tier first and confirm it works. Then add these on top of it.
M1. Remove by shift
Add a Remove a manuscript option backed by:
void remove_manuscript(string names[], int centuries[], string types[], int& count,
string target);
Find the target with a case-insensitive exact-match linear search (find_index, returning -1 when absent). If it is not there, print Not found: <name> and return — a guard clause, not a nested if. If it is there, slide every later entry down over the hole in all three arrays, then decrement count. The loop bound is j < count - 1, not j < count; §3.13 explains exactly why, and getting it wrong is an out-of-bounds read.
M2. Refuse duplicates
Modify add_manuscript so that adding a name already in the database is rejected with a clear message. Case-insensitive: adding codex sinaiticus when Codex Sinaiticus is present must be refused. Reuse find_index from M1 — do not write a second search.
M3. Filter by type
Add a Filter by type option that prompts for one of papyrus, uncial, minuscule, lectionary and prints every manuscript of that type, case-insensitively, with a count. You already have count_by_type; this is its printing sibling, and the two should share to_lower rather than each lowering a string their own way.
M4. Guard clauses and a sentinel across the reasoning family
Give every probability-taking function a guard clause that returns the sentinel -1.0 on invalid input:
double bayes_update(double prior, double L_H, double L_notH) {
if (!is_valid_probability(prior) || !is_valid_probability(L_H)
|| !is_valid_probability(L_notH)) {
return -1.0; // sentinel: no real probability is negative
}
// ... the math
}
Then check it in the caller — print Invalid input - probabilities must be in [0, 1]. instead of the number. A sentinel returned to a caller who prints it verbatim has helped nobody (§3.7). Test it: enter a prior of 1.5 and you must get the message, not -1.
Hard Tier (+up to 25% additional extra credit)
H1. Composition and a session log through a reference parameter
Add three functions:
double chained_bayes(double prior, double L1_H, double L1_notH,
double L2_H, double L2_notH);
bool modus_tollens(bool premise_not_B, bool premise_A_implies_B);
void log_operation(string operation_name, double result, int& step_counter);
chained_bayes weighs two pieces of evidence in succession and must call bayes_update twice — it contains no arithmetic of its own. Verify it: prior 0.01 with likelihoods 0.99 / 0.01 twice in a row gives 0.5 after the first update and 0.99 after the second (that is the run in §3.6, and yours must match).
log_operation prints [Step N] bayes_update -> 0.5 using the counter’s value before incrementing, then increments it. Declare int step = 0; in main and pass it to every completed operation. Because the parameter is int& step_counter, main’s copy is the one that grows — this is the payoff of §3.5, and if your step number is stuck at 0 you dropped the &.
On exit, print a session summary: how many operations ran and the last numeric result produced.
H2. Citation builder
Add a Generate citations option that prints a canonical citation for every manuscript, by hand:
- A name that is
Pfollowed by digits becomesPapyrus <digits>—P52prints asPapyrus 52. - A name beginning with
Codexprints unchanged. - Anything else prints unchanged.
Do the parsing yourself with .substr(), .find(), .length(), and character comparison. No <algorithm> shortcuts — the string-manipulation rep is the assignment. Watch the substr trap from Bug 10: check your position before you slice.
H3. The flex move
Find one C++ feature for functions, arrays, or strings that this book has not covered, use it somewhere it genuinely helps, and document it. Strong candidates:
- Function overloading — two
print_resultfunctions, one taking adoubleand one taking abool, so the caller stops caring which it has. - Default parameter values —
void list_manuscripts(..., int count, bool numbered = true);. - A second sort that shares the selection-sort shape but orders by name alphabetically, still in lockstep. String comparison with
<sorts lexicographically, so decide what you want to do about capitalization before you start. std::sortfrom<algorithm>, orstd::vector<string>— the containers we have been deliberately avoiding. If you go here, first make sure your hand-written versions work, because Week 4’s midterm expects the hand-written ones.
Document it in your reflection block the way the flex move has been documented since Project 1: what you used, why you chose it, where you learned it, and what it replaced.
Submission
Submit one URL:
- OnlineGDB project link (recommended). Create the project at onlinegdb.com, set the compiler flags to
-Wall -Wextrain project settings, build, and share the link. Appendix A walks through this end to end. - GitHub repo link (optional). If you have set up local development on your own, push the source to a public repo and submit that URL. Making sure it compiles when the grader opens it is on you.
What the linked project must contain
- The main source file —
p3_toolkit.cpp— with your full solution. - A reflection comment block at the very top of that file:
/*
* Tier targeted: Normal / Medium / Hard
* Features done: list each feature you completed
* What I learned: one short paragraph (no bullets)
* What I'd change: one sentence
* AI usage: where and how, if any. Be honest.
*/
- The program left in a demonstrable state. When the grader presses Run, the features for your tier should be reachable without guesswork. Pre-fill OnlineGDB’s Stdin panel with a session that exercises them — the input for the example run above is nothing more than the choices and values, one per line:
1
0.01
0.99
0.01
7
codex
8
2
3
9
6
10
12
No separate demo file. No screenshots. The grader opens the link, reads the comment block, runs the program, and grades against the rubric.
On AI: the course rule from the README applies unchanged. Attempt it yourself first, use AI afterward to review or explain, and never submit code you cannot explain line by line. Week 3 is the single worst week to outsource, because functions and arrays are the load-bearing skills for Weeks 4 through 8. Code you did not write will cost you the midterm, and the midterm is next week.
Hints
Check your arithmetic against these before you blame your code. All of them come from a working solution:
bayes_update(0.01, 0.99, 0.01)= 0.5. By hand: numerator0.99 * 0.01 = 0.0099; denominator0.0099 + 0.01 * (1 - 0.01) = 0.0099 + 0.0099 = 0.0198; result0.0099 / 0.0198 = 0.5. If you got something else, you have a parenthesis bug or you wrote0.99where you meant(1.0 - prior).complement(0.3)= 0.7.conjunction_probability(0.5, 0.5)= 0.25.modus_ponens(true, true)= true;modus_ponens(true, false)= false.is_valid_probability(1.5)= false.- Search
codex,Codex, andCODEX— all three must find exactly 3 manuscripts. Searchp52— exactly 1. If uppercase finds nothing, you lowered one side and not the other. - Century range
2to3→ P52 and Bodmer Papyri, 2 matches. Range4to4→ Codex Sinaiticus and Codex Vaticanus, 2 matches (inclusive means a single-century range works). Range5to2→ 0 matches and a message, not a crash. - On the seed data, the summary must read: entries 5, earliest 2, latest 5, average 3.6, papyri 2, uncials 3. If your average prints
3, you did integer division —(4+2+5+3+4)/5is18/5, and inintarithmetic that truncates.static_cast<double>(sum) / count. - One end-to-end check of the reference parameter: add a manuscript named
Test Entry, century9, typeminuscule. The count must go from 5 to 6, and the summary must then read entries 6, latest 9, average 4.5 — because(2+3+4+4+5+9)/6 = 27/6. If the count is still 5, yourcountparameter is missing its&. Restart the program to clear the test entry (removal is Medium tier).
“My getline is being skipped and the manuscript name comes back empty.” You read the menu choice with cin >> choice, which left the newline in the buffer, and getline obediently read the zero characters before it. One cin.ignore(); immediately after reading the choice fixes every branch at once (§3.15, Bug 11).
“My sort scrambled the data — names have other manuscripts’ centuries.” You swapped one or two arrays and not all three (§3.18, Bug 14). Every swap is nine lines: three for the century, three for the name, three for the type, each with its own temp. Print all three columns after every pass while you are debugging and the drift is obvious in a glance.
“My swap turned two different values into the same value twice.” You wrote a = b; b = a;. The first assignment destroyed a before you saved it. You need the temp (§3.17).
“My list is missing the last manuscript” — or prints a blank line first. Bounds. Loop i < count, never i <= count, and in add_manuscript assign into names[count] then do count++, never the other order (Bug 13).
“The match count prints in the wrong place.” You wrote cout << "Found " << search_by_name(...) << endl; and the function’s own output landed in the middle of your sentence. Call it first, store the result, then print (§3.19). This bites everyone once.
“My main is getting long.” With eleven operations, an honest dispatch is 70–100 lines and that is correct. Long is fine; smart is not. If a line in main computes something, move it into a function. If main has a loop that walks an array, it is in the wrong file section.
“Should I use if / else if or switch for the dispatch?” Either. switch needs break on every case and cannot declare a variable in a case without braces around the body; else if avoids both potholes. The reference solution uses else if for exactly that reason.
“How long should this take me?” Session 4 of the chapter’s Week at a Glance sets aside three hours of your twelve for the build. That is the hands-on-keys number for someone who did all sixteen reps and can write a function that walks an array from memory. The 5–7 hours in the header is the honest total for everyone else — including the debugging, which is where the hours actually go. If you are past seven hours on Normal, you skipped the reps; go back to §3.22, take the Checkpoint, and drill what you missed. That is faster than continuing, every time.
Build it in this order. It is the difference between a smooth evening and a long one:
mainwith the menu, the loop, thecin.ignore(), and dispatch that only prints"chose N". Run it. Every option, including a bad one.- The five reasoning functions, one at a time, checked against the numbers above as you go.
- The arrays,
add_manuscript,list_manuscripts, and the seed. Run it. Your five manuscripts should list in seed order. to_lower, thensearch_by_name, thensearch_by_century_range.- The four statistics functions, then
print_summaryon top of them. sort_by_centurylast, because it is the one most likely to eat an evening — and list the database immediately after sorting, every single time you test it.
What Mastery Looks Like
A great Project 3 has functions that each do one thing well. Read each one out loud: the name is a verb phrase, the parameters are sensibly named and consistently ordered, the return type is right, and the body fits in a dozen lines. Array parameters that are only read are marked const, and the one parameter that must change the caller — int& count — is the only reference in the file.
A great Project 3 composes. print_summary does no arithmetic; it calls four functions that do. search_by_name and count_by_type both call the same to_lower. Nothing is written twice, so nothing can be fixed in only one of two places.
A great Project 3 has a main that reads like a menu, not a calculator. Somebody skimming it should learn what the program offers without learning how anything works.
A great Project 3 is bounds-safe. The grader can list an empty database, add past capacity, search for a term that matches nothing, ask for the range 5 to 2, and sort a one-element database, and the program stays polite through all of it. Nothing crashes, nothing prints garbage.
A great Project 3 makes the parallel-array pain visible. Nine lines to move one row; three arrays to remember on every operation. Say so in your reflection block. Chapter 4 replaces all of it with a struct in a single move, and it will feel like a gift in exact proportion to how much this week annoyed you.
When You’re Done
- Read
p3_toolkit.cppfrom top to bottom, out loud. Each function should be a short, named idea you can state in one sentence with no “and” in it. Any function that needs an “and” is two functions. - Exercise all eleven options plus exit, in one run, and check the values against the Hints list. The example run above covers six of them; the other five — complement, conjunction, modus ponens, the probability check, and add — are yours to verify.
- Then try to break it. Sort twice in a row. Search for
zzz. Ask for century range 5 to 2. List after adding. Add a sixth manuscript and re-run the summary; the average must change. A program that survives its author trying to break it usually survives the grader. - Recompile with
-Wall -Wextraone last time and confirm the output pane is empty above your program’s first line. Zero warnings is the bar, not “only two warnings.” - Fill in the reflection block honestly, pre-fill the Stdin panel, save, share, and submit the link.
Coach’s Note — This is the hinge project of the course. A struct with functions attached is a class; a function inside a class is a method; all of Weeks 5 through 8 are the question of which functions belong with which data. Everything after this week is built out of what you just did. So the real measure of tonight is not the grade — it is whether you can now sit at a blank file and write a function that walks an array, from memory, without hesitating. If you can, the rest of this course gets easier even as the ideas get bigger.
Nobody is going to walk you through this one, and that is exactly why finishing it counts. If it is late and the sort is still wrong, go to Rung 3 in §3.23 — pull the function into a new file with a three-element array and watch it — then sleep on it. This material genuinely consolidates overnight; a twenty-minute morning fix is common and an eleven-p.m. rewrite almost never is.
When it runs clean, submit it, close the laptop, and take the win. Then open Chapter 4, where structs delete the parallel-array problem in one move — and where the midterm is waiting.