Skip to content
This repository was archived by the owner on Oct 9, 2026. It is now read-only.

Restore 2019 coursework: bug fix, no size limit, input checks, tests, CI - #1

Merged
urtuba merged 9 commits into
masterfrom
restore
Oct 9, 2026
Merged

urtuba merged 9 commits into
masterfrom
restore

Conversation

@urtuba

@urtuba urtuba commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Restores this 2019 ITU data structures coursework. The original code is kept at tag original-2019.

What changes

  • The two examples from the assignment figures are added as examples/figure-1.txt and examples/figure-2.txt.
  • Makefile and characterization tests: the expected outputs were recorded from the unchanged 2019 program, so later commits prove they keep its behaviour.
  • CI: make test with g++ and clang++.
  • Tidy-up: a Tree class that frees its nodes; the left/right side is tracked during the search instead of the pow/log2 formula; builds with -Werror.
  • The hidden limit of 2047 nodes is removed. The old code wrote past a fixed array above it.
  • Bug fix: a target equal to the root's value printed the root alone as the left path. The root alone is in neither subtree, so a path now needs at least one node below the root. The tests recorded the old output first, so this commit shows the change.
  • Input checks: bad values, a missing target or extra data stop with error: line N: ... (exit status 1). Path sums use long long, so large values no longer produce false paths.
  • Reads the input from stdin when no file is given.
  • README rewritten with text diagrams of the task and both examples instead of the handout images, plus a one-click Compiler Explorer playground.

Checks

  • make test passes at every commit (55 checks on the last one, including stdin re-runs).
  • Random trees against the 2019 program: identical output before the fix; after it, the only differences are the root-alone cases. An independent Python reference agrees on trees of up to 30000 nodes.
  • The final code was compiled and run on Compiler Explorer (gcc 16.2, Linux, -Wpedantic) with no warnings; it prints the handout's expected paths.

🤖 Generated with Claude Code — changes by AI agent Claude Sonnet 5.5, reviewed by Claude Opus 5.5.

urtuba added 9 commits October 9, 2026 18:32
The two examples from the assignment handout, as input files for
b_tree. They are the first inputs the tests and the README will use.

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
Pin down what the 2019 program does before any change, so every later
commit can show that valid inputs still give the same output.

The Makefile builds b_tree and runs the tests with `make test`. The
runner tests/run.sh follows the one in cpp-deck-game: each case
tests/cases/NAME.txt is run with the file as the argument, and the
output is compared with NAME.out (exit status with NAME.code, stderr
with NAME.err when these files exist). A .gitignore keeps the built
binary out of the repository.

Every .out file was produced by running the unchanged original program,
not written by hand. The cases stay inside defined behaviour: a file
argument, at most 2047 nodes, values that cannot overflow. They cover
both figure examples, one node, no path, a path on one side only,
negative and duplicate values, several paths per side (preorder wins),
a path ending at an inner node and a tree of 1000 nodes.

root_alone records a known bug on purpose: a target equal to the root's
value prints the root alone as the left path. The fix commit will
change this output.

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
Run `make test` on every push and pull request, with both g++ and
clang++, so a change that breaks the build or the tests shows up on
GitHub. Same workflow as in cpp-deck-game.

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
No change in behaviour. The tests are untouched and still pass, and the
output matched the 2019 build on 3000 random valid inputs.

The loose structs and the create() initialiser become a Tree class: the
constructor sets the defaults, the destructor frees all nodes, and
copying is disabled because the tree owns its nodes. The left/right
side is now passed down the search as an enum. This replaces the
pow/log2 formula that guessed the side from the node number. The
formula counted the root as left, so the root is still counted as left;
this known bug is fixed in a later commit.

Also: clearer names, short comments, 4-space indentation, no
`using namespace std`, the author header lines like the sibling repos,
and -Werror in the Makefile, since the code builds without warnings.

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
The 2019 program found the place of a new node with a route array of 10
entries. A tree of 2048 nodes or more needs 11 steps, so it wrote past
the end of the array (undefined behaviour). The limit was never
checked or documented.

Keep a vector of node pointers where entry k-1 is node number k. The
parent of node k is node k/2, so adding a node needs no route at all.
The vector also owns the nodes, so the destructor is a plain loop.

New case large_5000: 5000 nodes, with a left path of 13 nodes and a
right path of 12 nodes, both ending below the old limit. Its expected
output was cross-checked with an independent Python version of the
search. Up to 2047 nodes the output still matches the 2019 build (3000
random inputs); above that the 2019 build is undefined, so a Python
reference was used instead (400 random trees of 2048 to 30000 nodes).

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
The task asks for one path in the left subtree and one in the right
subtree. The root is in neither, so a path needs at least one node below
it. The 2019 program checked the root's own sum too, and treated the root
as part of the left subtree. A target equal to the root's value printed
the root alone as the left path, even when no real left path existed.

The search now starts at the root's two children, one for each side, and
the root only adds its value to the sum.

Output for the case root_alone (`10 5 7`, target 10):

    before:  Path Found: 10      after:  No Path Found
             No Path Found               No Path Found

Its .out file is updated. New case root_and_longer_path (`10 5 -10 -5 1
10 1`, target 10): the 2019 program printed the root alone for the left
path, now it prints `Path Found: 10 5 -5`.

On 3000 random inputs, the output is the same as the 2019 build except
where the old left line was the root alone. A Python reference (also
checked on trees of 2048 to 30000 nodes) agrees with the new program on
every input.

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
The 2019 program trusted the file. A missing file, a word like "abc" or
an empty line gave silent wrong output, and a value that did not fit
in an int was cut or misread.

Now the input must follow these rules. Line 1 has one or more whole
numbers in the int range, separated by spaces or tabs. Line 2 has
exactly one whole number in the int range. Only whitespace may follow.
A missing final newline and Windows line endings are fine.

A broken rule prints `error: line N: <what is wrong>` on stderr and
exits with status 1 (the offending word is quoted, long ones are cut).
The errors are: empty input, no values on line 1, a word that is not a
whole number, a number out of int range, a missing target, a second
number on line 2 and extra data after line 2. A missing file prints
`error: cannot open FILE`. The program checks the whole input before it
prints anything. A wrong number of arguments prints a usage line and
exits with status 2, instead of crashing with no argument.

Sums are now long long. A path of big values could pass the int range
on the way down, which is undefined behaviour and could even give a
false match. The case int_overflow_no_match (2147483647 twice, target -2)
printed a path before this change and prints "No Path Found" now.

New cases: values near the int limits, the format variations that must
still work (CRLF, no final newline, tabs and spaces, blank lines at the
end) and one case for each error above. For the valid cases the 2019
build gives the same output. On 3000 random inputs the new program still
matches the 2019 build (apart from the root-alone fix).

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
The online playground can only pass the input as standard input, and
piping a file in is handy anyway. With no argument the program now
reads stdin; with one argument it reads that file, as before. More than
one argument is still a usage error (exit status 2); the usage line now
shows the optional file: "usage: b_tree [INPUT_FILE]".

The same code and the same checks run for both sources, so the error
messages are identical. In the tests, the runner now also feeds every
valid case on stdin with no arguments and expects the same output. A new
case, stdin_value_text, checks an error message on the stdin path.

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
The old README showed the task with three images from the assignment
handout and described the old build and the pow/log2 trick. Delete
img/ and draw the same examples as text: the node numbering 1 to 7 and
both figure examples with their output.

The new README follows the sibling repos: a note that this is 2019
coursework restored in 2026 (original code at tag original-2019), a
"Run it online" link, the task and the input rules, how to build, run
and test, how the program works, the changes made in 2026 and the
license (MIT, the LICENSE file is unchanged).

The online link opens b_tree.cpp in Compiler Explorer with
examples/figure-2.txt as stdin. It holds a snapshot of the code, so it
must be regenerated if b_tree.cpp changes.

Made by AI agent: Claude Sonnet 5.5 (claude-sonnet-5-5).
@urtuba
urtuba merged commit 3679d33 into master Oct 9, 2026
4 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant