diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1e2927c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,140 @@ +# CLAUDE.md — Sorting Algorithm Project + +## Project Overview + +This is a C++ academic project (ECE 4050 / CSC 5050 - Project 3) that implements and compares four classic sorting algorithms across three STL container types (`vector`, `list`, `set`). It tracks operation counts (accesses and comparisons) to measure and compare algorithm efficiency. + +--- + +## Repository Structure + +``` +Sorting-Algorithm/ +├── main.cpp # Top-level entry point (root copy) +├── sorting.cpp # All sorting algorithm implementations (root copy) +├── sort.h # Central header: SortMetrics struct + all function declarations +├── random.h / random.cpp # Random data generation utilities +├── operators.h / operators.cpp # Stream insertion operator overloads for STL containers +├── sort_metrics.cpp # Stream insertion operator for SortMetrics +├── sort/ # MSVC Debug build artifacts (compiled object files, logs) +├── sorting/ # Visual Studio project folder (canonical source of truth) +│ ├── main.cpp # Entry point (duplicate of root main.cpp) +│ ├── sorting.cpp # Algorithm implementations (duplicate of root sorting.cpp) +│ ├── sort.h # Header (duplicate of root sort.h) +│ ├── random.h / random.cpp +│ ├── operators.h / operators.cpp +│ ├── sort_metrics.cpp +│ ├── sorting.sln # Visual Studio solution file +│ ├── sorting.vcxproj # Visual Studio project file +│ └── Debug/ # MSVC build output (sorting.exe, .obj files, .pdb) +└── README.md +``` + +> **Note:** Source files exist in both the repository root and under `sorting/`. The `sorting/` directory is the Visual Studio project and contains the compiled executable (`sorting/Debug/sorting.exe`). The root-level `.cpp`/`.h` files appear to be duplicates uploaded alongside the VS project. + +--- + +## Source File Roles + +| File | Purpose | +|---|---| +| `sort.h` | Declares `SortMetrics` struct and all sort function signatures | +| `sorting.cpp` | Implements all sort algorithms and helper functions | +| `main.cpp` | Driver: generates random data, calls each sort, prints results | +| `random.cpp` | `random_vector(n)`, `vector_to_list()`, `vector_to_set()` | +| `operators.cpp` | `operator<<` overloads for `vector`, `list`, `set` | +| `sort_metrics.cpp` | `operator<<` overload for `SortMetrics` | + +--- + +## Key Data Structures + +### `SortMetrics` (defined in `sort.h`) +```cpp +struct SortMetrics { + int accesses{ 0 }; // Count of array element accesses + int comparisons{ 0 }; // Count of element comparisons +}; +``` +Every sort function resets both fields to `0` at entry, then increments them during execution. This enables post-sort efficiency analysis. + +--- + +## Implemented Algorithms + +All sort functions have the signature `bool func(..., SortMetrics& metrics)` and return `true` on success, `false` on error (or stub `0`). + +### Bubble Sort (`bubble_sort`) +- **`vector`**: Standard optimized bubble sort using swap-flag early exit. Uses arithmetic swap (no `std::swap` or temp variable). +- **`list`**: Selection-sort-style nested iterator loop (note: not a true bubble sort). +- **`set`**: **Stub** — returns `0`, no implementation (sets are always ordered by invariant). + +### Insertion Sort (`insertion_sort`) +- **`vector`**: Standard insertion sort with key-based shifting. +- **`list`**: **Not implemented** (commented out in header and main). +- **`set`**: **Stub** — returns `0`. + +### Merge Sort (`merge_sort`) +- **`vector`**: Recursive merge sort. Uses two internal helpers: + - `mergeSort(vector, metrics)` — recursive splitter + - `merge(left, right, metrics)` — merge step using `erase`/`push_back` +- **`list`**: **Not implemented**. +- **`set`**: **Stub** — returns `0`. + +### Quicksort (`quicksort`) +- **`vector`**: In-place quicksort with middle-element pivot. Signature requires explicit `left` and `right` indices: `quicksort(vec, metrics, 0, n-1)`. +- **`list`**: **Not implemented**. +- **`set`**: **Stub** — returns `0`. + +--- + +## Known Issues and Incomplete Implementations + +- **`set` stubs**: `bubble_sort(set)`, `insertion_sort(set)`, `merge_sort(set)`, and `quicksort(set)` all return `0` with no logic. `std::set` maintains sorted order automatically, so sorting is a no-op — these stubs exist to satisfy the interface. +- **`list` for insertion/merge/quicksort**: Commented out in `sort.h` and `main.cpp`. A partial pseudocode stub exists in `sorting.cpp` (lines 98–111). +- **Metrics reset inside quicksort**: Quicksort resets `metrics.accesses` and `metrics.comparisons` to `0` at the start of each recursive call, meaning the final reported metrics only reflect the last recursive frame, not the total operation count. +- **Quicksort swaps indices, not values**: `swap(i, j)` swaps the index variables, not `vec[i]` and `vec[j]`. This is a bug — it should be `swap(vec[i], vec[j])`. + +--- + +## Build System + +The project targets **Visual Studio 2019** (MSVC v142 toolchain) on Windows. Build artifacts are in `sorting/Debug/`. + +### Building on Linux (g++) +There is no Makefile. To compile manually: +```bash +g++ -std=c++17 -o sorting main.cpp sorting.cpp random.cpp operators.cpp sort_metrics.cpp +./sorting +``` + +All source files must be compiled together in a single translation unit group since they share declarations via headers but have no separate library setup. + +--- + +## Conventions + +- **`using namespace std;`** is used globally (declared at the top of `sort.h` and each `.cpp`). +- Headers use `#pragma once` for include guards. +- All functions that take a container take it by reference (`&`). +- `SortMetrics` is always passed by reference and reset at the start of each sort call. +- Internal/helper functions (`merge`, `mergeSort`) are defined in `sorting.cpp` but not declared in any header — they are file-scoped. +- The dataset size `n` is hardcoded to `10` in `main.cpp`. Change this value directly to test different sizes. + +--- + +## Development Workflow + +1. Edit source files (prefer files under `sorting/` as the VS project root). +2. Build with Visual Studio on Windows, or `g++` on Linux (see above). +3. Run the executable: `sorting/Debug/sorting.exe` (Windows) or `./sorting` (Linux). +4. Output shows the container contents before and after each sort, followed by `SortMetrics`. + +--- + +## What NOT to Change Without Care + +- **`sort.h` function signatures**: All implementations in `sorting.cpp` must match exactly. Adding or removing parameters breaks compilation. +- **`SortMetrics` struct fields**: `main.cpp` and `sort_metrics.cpp` depend on `accesses` and `comparisons` by name. +- **`operator<<` overloads in `operators.cpp`**: Used implicitly by `std::cout << vect` in `main.cpp`. Removing them breaks output. +- **Root-level source files vs `sorting/`**: Keep both in sync if edits are made, or consolidate to one location. diff --git a/sorting.cpp b/sorting.cpp index 6170108..07bef4f 100644 --- a/sorting.cpp +++ b/sorting.cpp @@ -23,12 +23,9 @@ bool bubble_sort(vector& vec, SortMetrics& metrics) { metrics.comparisons++; //if if (vec[i] > vec[i + 1]) { - metrics.accesses += 3; - //vec[i] being accessed two extra times and vec[i+1] one extra time - //swap - vec[i] += vec[i + 1]; - vec[i + 1] = vec[i] - vec[i + 1]; - vec[i] -= vec[i + 1]; + metrics.accesses += 2; + //two writes: vec[i] and vec[i+1] (reads already counted above) + swap(vec[i], vec[i + 1]); swap_ = true; } @@ -42,24 +39,24 @@ bool bubble_sort(list& list, SortMetrics& metrics) { metrics.accesses = 0; metrics.comparisons = 0; - for (auto i = begin(list); i != end(list); i++) - { - auto key = begin(list); //setting the new first element of the list - metrics.accesses++; + bool swapped = true; + while (swapped) { + swapped = false; + for (auto it = begin(list); it != end(list); ++it) { + auto next = std::next(it); + if (next == end(list)) break; + + metrics.accesses += 2; //read *it and *next + metrics.comparisons++; - for (auto itr = key; itr != end(list); itr++) - { - metrics.accesses += 2; //i and itr - metrics.comparisons++ ; - if (*i < *(itr)) - { - int temp = *i; - *i = *itr; - *itr = temp; + if (*it > *next) { + swap(*it, *next); + metrics.accesses += 2; //two writes for the swap + swapped = true; } } } - return 0; + return true; } //Bubble sort with set @@ -75,18 +72,19 @@ bool insertion_sort(vector& vec, SortMetrics& metrics) { metrics.comparisons = 0; for (size_t j = 1; j < vec.size(); j++) { - metrics.accesses++; //vec[j] + metrics.accesses++; //read vec[j] int key = vec[j]; - int i = j - 1; - - metrics.accesses += 2; //vec[i] and vec[i+1] - metrics.comparisons++;//vec[i] and key(=vec[j]) + int i = (int)j - 1; - while (i >= 0 && vec[i] > key) { + while (i >= 0) { + metrics.accesses++; //read vec[i] + metrics.comparisons++; //compare vec[i] > key (counted whether true or false) + if (vec[i] <= key) break; + metrics.accesses++; //write vec[i+1] vec[i + 1] = vec[i]; i--; } - metrics.accesses++; //vec[i+1] + metrics.accesses++; //write vec[i+1] = key vec[i + 1] = key; } @@ -126,14 +124,15 @@ vector merge(vector l, vector r,SortMetrics& metrics) { while (l.size() > 0 || r.size() > 0) { if (l.size() > 0 && r.size() > 0) { + metrics.comparisons++; + metrics.accesses += 2; //read l[0] and r[0] for comparison if (l.at(0) <= r.at(0)) { - metrics.comparisons++; - metrics.accesses += 2; //accessing and comparing the first elements in both halves + metrics.accesses++; //read l[0] for push_back re.push_back(l.at(0)); //organizing the vector behind the smallest element in the portion - l.erase(l.begin()); //resseting the vector to use push_back again + l.erase(l.begin()); //resetting the vector to use push_back again } else { - metrics.accesses++; //accessing the element in r + metrics.accesses++; //read r[0] for push_back re.push_back(r.at(0)); r.erase(r.begin()); } @@ -183,6 +182,8 @@ vector mergeSort(vector vec, SortMetrics& metrics) { } bool merge_sort(vector& vec, SortMetrics& metrics) { + metrics.accesses = 0; + metrics.comparisons = 0; vec = mergeSort(vec, metrics); //recursively sorting the vector by calling the sorting function which uses the helper function to sort the vector return 1; @@ -198,21 +199,19 @@ bool merge_sort(set& set, SortMetrics& metrics){ } //quicksort with vectors -bool quicksort(vector& vec, SortMetrics& metrics, int left, int right) { - metrics.accesses = 0; - metrics.comparisons = 0; - +// Internal recursive helper — metrics are NOT reset here so totals accumulate +static void quicksort_helper(vector& vec, SortMetrics& metrics, int left, int right) { int i, j, mid, pivot; i = left; // left partition of the vector j = right; //right partition of the vector - mid = left + (right - left) / 2; - //pivot (chosen by using the middle element of the vector) + mid = left + (right - left) / 2; + //pivot (chosen by using the middle element of the vector) pivot = vec[mid]; - metrics.accesses++; //acccessing the vector element at mid + metrics.accesses++; //accessing the vector element at mid while (j > left || i < right) { metrics.comparisons += 2; //vector elements at i and j being compared with the pivot - + while (vec[i] < pivot) { metrics.accesses++; i++; //incrementing i to find the element larger than the pivot @@ -220,23 +219,31 @@ bool quicksort(vector& vec, SortMetrics& metrics, int left, int right) { while (vec[j] > pivot) { metrics.accesses++; j--; //decrementing j to find the element smaller than the pivot - } + } if (i <= j) { - swap(i, j); //with the two elements found from the while loops above, if i side elements is smaller than the j side elements, swtich. if not, no action taken as they are already in the right order + swap(vec[i], vec[j]); //swap the actual elements, not the indices + metrics.accesses += 2; //two writes for the swap i++; j--; } else { // right side is larger if (i < right) - quicksort(vec, metrics, i, right); + quicksort_helper(vec, metrics, i, right); // left is larger if (j > left) - quicksort(vec, metrics, left, j); - return 1; + quicksort_helper(vec, metrics, left, j); + return; } } +} + +// Public entry point — resets metrics once, then delegates to helper +bool quicksort(vector& vec, SortMetrics& metrics, int left, int right) { + metrics.accesses = 0; + metrics.comparisons = 0; + quicksort_helper(vec, metrics, left, right); return 1; } diff --git a/sorting/sorting.cpp b/sorting/sorting.cpp index 6170108..07bef4f 100644 --- a/sorting/sorting.cpp +++ b/sorting/sorting.cpp @@ -23,12 +23,9 @@ bool bubble_sort(vector& vec, SortMetrics& metrics) { metrics.comparisons++; //if if (vec[i] > vec[i + 1]) { - metrics.accesses += 3; - //vec[i] being accessed two extra times and vec[i+1] one extra time - //swap - vec[i] += vec[i + 1]; - vec[i + 1] = vec[i] - vec[i + 1]; - vec[i] -= vec[i + 1]; + metrics.accesses += 2; + //two writes: vec[i] and vec[i+1] (reads already counted above) + swap(vec[i], vec[i + 1]); swap_ = true; } @@ -42,24 +39,24 @@ bool bubble_sort(list& list, SortMetrics& metrics) { metrics.accesses = 0; metrics.comparisons = 0; - for (auto i = begin(list); i != end(list); i++) - { - auto key = begin(list); //setting the new first element of the list - metrics.accesses++; + bool swapped = true; + while (swapped) { + swapped = false; + for (auto it = begin(list); it != end(list); ++it) { + auto next = std::next(it); + if (next == end(list)) break; + + metrics.accesses += 2; //read *it and *next + metrics.comparisons++; - for (auto itr = key; itr != end(list); itr++) - { - metrics.accesses += 2; //i and itr - metrics.comparisons++ ; - if (*i < *(itr)) - { - int temp = *i; - *i = *itr; - *itr = temp; + if (*it > *next) { + swap(*it, *next); + metrics.accesses += 2; //two writes for the swap + swapped = true; } } } - return 0; + return true; } //Bubble sort with set @@ -75,18 +72,19 @@ bool insertion_sort(vector& vec, SortMetrics& metrics) { metrics.comparisons = 0; for (size_t j = 1; j < vec.size(); j++) { - metrics.accesses++; //vec[j] + metrics.accesses++; //read vec[j] int key = vec[j]; - int i = j - 1; - - metrics.accesses += 2; //vec[i] and vec[i+1] - metrics.comparisons++;//vec[i] and key(=vec[j]) + int i = (int)j - 1; - while (i >= 0 && vec[i] > key) { + while (i >= 0) { + metrics.accesses++; //read vec[i] + metrics.comparisons++; //compare vec[i] > key (counted whether true or false) + if (vec[i] <= key) break; + metrics.accesses++; //write vec[i+1] vec[i + 1] = vec[i]; i--; } - metrics.accesses++; //vec[i+1] + metrics.accesses++; //write vec[i+1] = key vec[i + 1] = key; } @@ -126,14 +124,15 @@ vector merge(vector l, vector r,SortMetrics& metrics) { while (l.size() > 0 || r.size() > 0) { if (l.size() > 0 && r.size() > 0) { + metrics.comparisons++; + metrics.accesses += 2; //read l[0] and r[0] for comparison if (l.at(0) <= r.at(0)) { - metrics.comparisons++; - metrics.accesses += 2; //accessing and comparing the first elements in both halves + metrics.accesses++; //read l[0] for push_back re.push_back(l.at(0)); //organizing the vector behind the smallest element in the portion - l.erase(l.begin()); //resseting the vector to use push_back again + l.erase(l.begin()); //resetting the vector to use push_back again } else { - metrics.accesses++; //accessing the element in r + metrics.accesses++; //read r[0] for push_back re.push_back(r.at(0)); r.erase(r.begin()); } @@ -183,6 +182,8 @@ vector mergeSort(vector vec, SortMetrics& metrics) { } bool merge_sort(vector& vec, SortMetrics& metrics) { + metrics.accesses = 0; + metrics.comparisons = 0; vec = mergeSort(vec, metrics); //recursively sorting the vector by calling the sorting function which uses the helper function to sort the vector return 1; @@ -198,21 +199,19 @@ bool merge_sort(set& set, SortMetrics& metrics){ } //quicksort with vectors -bool quicksort(vector& vec, SortMetrics& metrics, int left, int right) { - metrics.accesses = 0; - metrics.comparisons = 0; - +// Internal recursive helper — metrics are NOT reset here so totals accumulate +static void quicksort_helper(vector& vec, SortMetrics& metrics, int left, int right) { int i, j, mid, pivot; i = left; // left partition of the vector j = right; //right partition of the vector - mid = left + (right - left) / 2; - //pivot (chosen by using the middle element of the vector) + mid = left + (right - left) / 2; + //pivot (chosen by using the middle element of the vector) pivot = vec[mid]; - metrics.accesses++; //acccessing the vector element at mid + metrics.accesses++; //accessing the vector element at mid while (j > left || i < right) { metrics.comparisons += 2; //vector elements at i and j being compared with the pivot - + while (vec[i] < pivot) { metrics.accesses++; i++; //incrementing i to find the element larger than the pivot @@ -220,23 +219,31 @@ bool quicksort(vector& vec, SortMetrics& metrics, int left, int right) { while (vec[j] > pivot) { metrics.accesses++; j--; //decrementing j to find the element smaller than the pivot - } + } if (i <= j) { - swap(i, j); //with the two elements found from the while loops above, if i side elements is smaller than the j side elements, swtich. if not, no action taken as they are already in the right order + swap(vec[i], vec[j]); //swap the actual elements, not the indices + metrics.accesses += 2; //two writes for the swap i++; j--; } else { // right side is larger if (i < right) - quicksort(vec, metrics, i, right); + quicksort_helper(vec, metrics, i, right); // left is larger if (j > left) - quicksort(vec, metrics, left, j); - return 1; + quicksort_helper(vec, metrics, left, j); + return; } } +} + +// Public entry point — resets metrics once, then delegates to helper +bool quicksort(vector& vec, SortMetrics& metrics, int left, int right) { + metrics.accesses = 0; + metrics.comparisons = 0; + quicksort_helper(vec, metrics, left, right); return 1; }