42 Network | C++ Series β Module 09 The final C++ module, focused entirely on the Standard Template Library (STL). Each exercise enforces deliberate container selection, robust input handling, and rigorous adherence to the C++98 standard.
cpp09/
βββ ex00/ Bitcoin Exchange β std::map + CSV parsing
βββ ex01/ Reverse Polish Notation β std::stack + expression evaluation
βββ ex02/ PmergeMe (Ford-Johnson) β std::vector + std::deque benchmarking
# Exercise 00
cd ex00 && make && ./btc input.txt
# Exercise 01
cd ex01 && make && ./RPN "8 9 * 9 - 9 - 9 - 4 - 1 +"
# Exercise 02
cd ex02 && make && ./PmergeMe 3 5 9 7 4 1 8 2Given a CSV database of historical BTC exchange rates and an input file of date | amount pairs, output the computed value of each entry using the closest available past date.
std::map<std::string, float> _database;std::map provides O(log n) lookup via a balanced BST. lower_bound(date) efficiently finds the closest prior date when an exact match doesn't exist.
loadDatabase()β Readsdata.csv, parsesdate,ratepairs and populates the map.processInput()β Readsinput.txt, validates eachdate | valueline, then calls_database.lower_bound(date)to find the nearest matching rate and prints the result.
date | value
2011-01-03 | 3
2015-01-01 | 10
| Condition | Error Output |
|---|---|
| Negative value | Error: not a positive number. |
| Value > 1000 | Error: too large a number. |
| Invalid/malformed date | Error: bad input => <date> |
| Date before earliest DB entry | Error: date too early => <date> |
Missing | separator |
Error: bad input => <line> |
| Leap year edge cases | Validated (e.g. 2011-02-29 is rejected) |
| Overflow / non-numeric value | Error: bad input => <value> |
2011-01-03 => 3 = 0.9
2015-01-01 => 10 = 2790.6
Evaluate a mathematical expression in Postfix (RPN) notation passed as a single command-line argument, printing the final result.
std::stack<double> _stack;A stack is the natural structure for RPN β numbers are pushed, operators pop two operands, compute, and push the result back.
myCalculator() tokenizes the input string with std::stringstream, then for each token:
- Number β validated as a single digit (
0β9), then pushed onto_stack - Operator β pops two values, applies the operation via
cCalculation(), pushes result
Input: "8 9 * 9 - 9 - 9 - 4 - 1 +"
push 8 β [8]
push 9 β [8, 9]
* β [72]
push 9 β [72, 9]
- β [63]
... continues ...
Result β 42
| Operator | Operation |
|---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division (throws on zero divisor) |
| Condition | Thrown Exception |
|---|---|
| Multi-digit number | Only single digit numbers (0 - 9) are allowed! |
| Insufficient operands on stack | Invalid Input: Not enough operands |
| Division by zero | Division by zero. |
| Stack doesn't resolve to 1 value | Invalid Inputs. |
./RPN "8 9 * 9 - 9 - 9 - 4 - 1 +"
# Output: 42
./RPN "9 8 * 9 -"
# Output: 63Sort a sequence of positive integers from the command-line using the Ford-Johnson Merge-Insertion Sort, implemented independently for both std::vector and std::deque, then display the time taken by each.
std::vector<int> _vector;
std::deque<int> _deque;This algorithm is designed to minimise the total number of comparisons required to sort a sequence:
Step 1 β Pairing: Elements are grouped into pairs. Each pair is sorted: the larger becomes the "winner" (main chain candidate), the smaller becomes the "loser" (pend element).
Step 2 β Recursive Sort: All winners are recursively sorted using the same algorithm, forming the sorted main chain.
Step 3 β Jacobsthal Insertion:
Losers are reinserted into the main chain via std::lower_bound (binary search). The order of insertion follows Jacobsthal numbers (0, 1, 1, 3, 5, 11, 21, 43β¦) to ensure each binary search operates over the smallest possible prefix of the chain β minimising total comparisons.
Step 4 β Struggler: If the original sequence had an odd number of elements, the leftover "struggler" is binary-searched into the final sorted chain last.
// Formula: J(n) = J(n-1) + 2 * J(n-2)
// Sequence: 0, 1, 1, 3, 5, 11, 21, 43, 85, 171 ...
size_t next = jacobsthal[i-1] + 2 * jacobsthal[i-2];Uses gettimeofday() for microsecond-precision benchmarking of both containers:
gettimeofday(&start, NULL);
std::vector<int> sortedVec = recursiveSortVec(vecCopy);
gettimeofday(&end, NULL);
double vecTime = (end.tv_sec - start.tv_sec) * 1e6 + (end.tv_usec - start.tv_usec);| Condition | Error Thrown |
|---|---|
| Non-digit characters in argument | Error: invalid input (only positive integers allowed) |
Value exceeds INT_MAX |
Error: number too large (exceeds INT_MAX) |
| Duplicate values | Error: duplicate value found |
./PmergeMe 3 5 9 7 4 1 8 2
Before: 3 5 9 7 [...]
After: 1 2 3 4 [...]
Time to process a range of 8 elements with std::vector : 9 us
Time to process a range of 8 elements with std::deque : 14 us| Feature | std::vector |
std::deque |
|---|---|---|
| Memory layout | Single contiguous block | Segmented fixed-size blocks |
| Random access | O(1), highly cache-friendly | O(1), slightly slower |
| Insert at middle | O(n) β shifts elements | O(n) β slightly more overhead |
| Cache performance | β Excellent | |
| Best for | Read-heavy, random access | Front/back insertions |
- Standard: C++98 (
-std=c++98) - Flags:
-Wall -Wextra -Werror - Memory check:
valgrind --leak-check=full ./btc input.txt
| Concept | What Was Learnt |
|---|---|
| Container Selection | Choosing map, stack, vector, deque based on access patterns |
| Algorithm Design | Implementing Ford-Johnson from scratch; understanding why Jacobsthal ordering minimises comparisons |
| Robust Parsing | Handling every edge case: whitespace, overflow, bad formats, leap years, invalid dates |
| Performance Benchmarking | Using gettimeofday() to measure real-world STL container differences in microseconds |
| C++98 Constraints | Working without auto, lambdas, range-based loops, or any C++11+ features |
Developed by shkaruna as part of the 42 Network C++ curriculum.