COSC_3P95_Assignment_1/COSC3P95_A1Q2/main.cpp

138 lines
4.9 KiB
C++
Executable File

/**
* Email: bt19ex@brocku.ca
* Student Number: 6920201
* Licence: GPL3.0 see https://www.gnu.org/licenses/gpl-3.0.en.html for more detail
* Basic random test case generator and tester with partially formatted output. Requires terminal unicode and ANSI escape code support for full effect.
*
* Please use Linux to run this software, tested on Debian stable using CLion 2023.2.2 // GCC version 12.2
* This should work on any linux distro with a C++20 compiler.
* CMake is optional as this file can be compiled via g++
* An compile command would be:
* `g++ -O3 -o main.run -Wall -Wpedantic -Wextra -Werror main.cpp && ./main.run`
* If you use CMake please make sure CMAKE_BUILD_TYPE is release otherwise the program will take a long time to run.
*/
#include <iostream>
#include <random>
#include <vector>
#include <cstdint>
#include <algorithm>
/**
* Config Variables
* ---------------------------------------
*/
static constexpr size_t DEFAULT_TEST_COUNT = 10; // Default: 10 tests
static constexpr size_t MAX_RAM_USAGE = 1ul * 1024ul * 1024ul; // Default: 1mb per test
/**
* Do not change anything below this line!
* ---------------------------------------
*/
static constexpr auto maxSize = MAX_RAM_USAGE / sizeof(std::int32_t);
static const auto maxChars = std::to_string(maxSize).size();
struct failPoint
{
size_t i, j;
};
// fp has no meaning if the test doesn't fail.
bool validateTest(const std::vector<std::int32_t>& in, failPoint& fp)
{
// empty sets are sorted. if you have a problem with this take it up with god
if (in.empty())
return true;
for (size_t i = 0; i < in.size(); i++)
{
for (size_t j = i; j < in.size(); j++)
{
// if any value after the current value is less, then we have failed to sort.
if (in[i] > in[j])
{
fp = {i, j};
return false;
}
}
}
return true;
}
std::vector<std::int32_t> generateRandomData()
{
// setup random numbers
static std::random_device dev;
static std::mt19937_64 engine(dev());
// don't want the int array to be too big otherwise we'll run out of ram. you can change this for your machine but im on my laptop rn
// if it mattered I'd test it on my desktop with 4gb array allocations, but it doesn't
std::uniform_int_distribution dist(std::numeric_limits<std::int32_t>::min(), std::numeric_limits<std::int32_t>::max());
std::uniform_int_distribution dist_size(0ul, maxSize);
// pick a random size
size_t size = dist_size(engine);
// populate the array
std::vector<std::int32_t> ret;
for (size_t i = 0; i < size; i++)
ret.push_back(dist(engine));
return ret;
}
template<typename T>
void runTest(T t, std::string name)
{
auto data = generateRandomData();
// little extra formatting goes a long way =3
name += "; Data Size: ";
auto dataSizeStr = std::to_string(data.size());
name += dataSizeStr;
// run the sort on the data
t(data);
// add padding for nice output :P
std::string padding;
for (size_t i = 0; i < maxChars - dataSizeStr.size(); i++)
padding += ' ';
failPoint fp{};
// input and output arrays cannot be shown due to their potential size. Instead, the software will output the failure value and locations.
// sometimes they are neighbours sometimes they are far apart. it depends on the randomness
if (validateTest(data, fp))
std::cout << "\033[36m[\033[32m✓\033[36m]:\033[0m Test '" << name << "'" << padding << "\t\033[32mPASSED\033[0m" << std::endl;
else
std::cout << "\033[36m[\033[31mX\033[36m]:\033[0m Test '" << name << "'" << padding << "\t\033[31mFAILED\t\t" << "Value '" << data[fp.i]
<< "' @ " << fp.i << " is greater than '" << data[fp.j] << "' @ " << fp.j << "\033[0m" << std::endl;
}
void brettSort(std::vector<std::int32_t>& v)
{
if (v.empty())
return;
static std::random_device dev;
static std::mt19937_64 engine(dev());
// since the STL won't fail (it's pretty good as long as you follow the rules)
std::sort(v.begin(), v.end());
// we can add some errors (sometimes) that will cause our test to fail (also sometimes)!
std::uniform_int_distribution<int> dist(0, (int) v.size() - 1);
std::uniform_real_distribution realDist(0.0, 1.0);
int p1 = dist(engine);
int p2 = dist(engine);
if (!v.empty() && realDist(engine) > 0.85)
v[p1] = v[p2];
}
int main(int argc, char** argv)
{
std::cout << "You can provide a test count via ./program [test count]" << std::endl;
auto testCount = DEFAULT_TEST_COUNT;
if (argc > 1)
testCount = std::stoi(argv[1]);
std::cout << "Running " << testCount << " tests" << std::endl;
// the brett sort should fail at some point
for (size_t i = 0; i < testCount; i++)
runTest(brettSort, "brettSort");
return 0;
}