From a90aca8c5b8cefb28233a3c7643aa26d4073b64f Mon Sep 17 00:00:00 2001 From: saundersp Date: Sun, 7 May 2023 19:51:43 +0200 Subject: [PATCH] Added files --- .gitignore | 2 + Dockerfile | 22 +++ Makefile | 50 ++++++ data.cpp | 101 +++++++++++ data.hpp | 487 ++++++++++++++++++++++++++++++++++++++++++++++++++++ toolbox.cpp | 157 +++++++++++++++++ toolbox.hpp | 46 +++++ 7 files changed, 865 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 data.cpp create mode 100644 data.hpp create mode 100644 toolbox.cpp create mode 100644 toolbox.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebda65e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.ccls-cache +bin diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d396b18 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM alpine:3.17.1 + +RUN apk add make g++ + +WORKDIR /home/saundersp/sorting_algorithms + +RUN adduser \ + --disabled-password \ + --gecos '' \ + --home "$(pwd)" \ + --no-create-home \ + saundersp + +COPY . . + +RUN chown -R saundersp /home/saundersp/sorting_algorithms + +USER saundersp + +RUN make -j $NPROC + +ENTRYPOINT ["bin/data"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..02e9da7 --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +CC := g++ -m64 -std=c++17 +OBJ_DIR := bin +$(shell mkdir -p $(OBJ_DIR)) +SRC_DIR := . +#CFLAGS := -Og -g -Wall -Wextra -Wno-error=unused-function -pedantic -rdynamic +#CFLAGS := $(CFLAGS) -pg -ggdb3 +#CFLAGS := $(CFLAGS) -D__DEBUG +CFLAGS := -O4 -Wall -Wextra -Wno-error=unused-function +EXEC := $(OBJ_DIR)/data +SRC := $(wildcard $(SRC_DIR)/*.cpp) +HEADER := $(wildcard $(SRC_DIR)/*.hpp) +OBJ_EXT := o +ifeq ($(OS), Windows_NT) + EXEC:=$(EXEC).exe + OBJ_EXT:=obj +endif +OBJ := $(SRC:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.$(OBJ_EXT)) + +.PHONY: all start clean mrproper + +all: $(EXEC) + +$(OBJ_DIR)/%.$(OBJ_EXT): $(SRC_DIR)/%.cpp $(SRC_DIR)/%.hpp + @echo Compiling $< + @$(CC) $(CFLAGS) -c $< -o $@ + +$(EXEC): $(OBJ) + @echo Linking objects files to $@ + @$(CC) $(CFLAGS) $^ -o $@ + +start: $(EXEC) + @./$(EXEC) + +profile: start + @gprof $(EXEC) gmon.out | gprof2dot | dot -Tpng -o output.png + +debug: $(EXEC) + @gdb -q -tui $(EXEC) -x copies + +check: $(EXEC) + @valgrind -q -s --leak-check=full --show-leak-kinds=all $(EXEC) + +r2: $(EXEC) + @r2 $(EXEC) + +clean: + @rm $(EXEC) gmon.out output.png + +mrproper: + @rm -r $(OBJ_DIR) diff --git a/data.cpp b/data.cpp new file mode 100644 index 0000000..51e694e --- /dev/null +++ b/data.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include "toolbox.hpp" +#include "data.hpp" + +template +static bool is_arg_sorted(const asp::Array& a, const asp::Array& indices) noexcept { + for (size_t i = 1; i < a.length; ++i) + if (a[indices[i - 1]] > a[indices[i]]) + return false; + return true; +} + +template +static bool is_sorted(const asp::Array& a) noexcept { + for (size_t i = 1; i < a.length; ++i) + if (a[i - 1] > a[i]) + return false; + return true; +} + +template +static void test_sort(const asp::Array& original, void (* const fnc)(const asp::Array&), const char* title) noexcept { +#ifdef __DEBUG + printf("xxxxxxxxxxxxxxx INGORE COPY "); +#endif + const asp::Array a(original); + asp::measure_time_void(title, fnc, a); +#ifdef __DEBUG + // assert(asp::measure_time("=> Unit test", is_sorted, a)); + const bool result = asp::measure_time("=> Unit test", is_sorted, a); + printf("| %-" STR(W_NAME) "s | %" STR(W_TIME) "s | %-" STR(W_FTIME) "s |\n", result ? "Success" : "Failure", "-", "-"); +#else + assert(is_sorted(a)); +#endif +} + +template +static void test_argsort(const asp::Array& original, asp::Array(* const fnc)(const asp::Array&), const char* title) noexcept { +#ifdef __DEBUG + printf("xxxxxxxxxxxxxxx INGORE COPY "); +#endif + const asp::Array a(original); + const asp::Array indices = asp::measure_time>(title, fnc, a); +#ifdef __DEBUG + // assert(asp::measure_time("=> Unit test", is_arg_sorted, a, indices)); + const bool result = asp::measure_time("=> Unit test", is_arg_sorted, original, indices); + printf("| %-" STR(W_NAME) "s | %" STR(W_TIME) "s | %-" STR(W_FTIME) "s |\n", result ? "Success" : "Failure", "-", "-"); +#else + assert(is_arg_sorted(original, indices)); +#endif +} + +template +static asp::Array create_random_array(const size_t& n) noexcept { + asp::Array original(n); + std::random_device rd; + std::default_random_engine gen(rd()); + std::uniform_int_distribution distrib(std::numeric_limits::min(), std::numeric_limits::max()); + return std::move(asp::map(original, [& distrib, & gen](const size_t&, const T&) -> const T { return distrib(gen); })); +} + +int main(int argc, char** argv) { + asp::toolbox_unit_test(); + + using array_type = uint16_t; + size_t N = static_cast(1)<<16; + // size_t N = std::numeric_limits::max(); + if (argc > 2) { + fprintf(stderr, "Too many arguments\nUsage: ./data \n"); + return EXIT_FAILURE; + } else if (argc == 2) + N = std::strtoul(argv[1], argv + strlen(argv[1]) + 1, 10); + + asp::print("Estimating memory footprint at : " + asp::format_byte_size(2 * N * sizeof(array_type))); + + char buff[64]; + sprintf(buff, "Sorting algorithm for %s elements", asp::thousand_sep(N).c_str()); + asp::print_separator(buff); + const asp::Array original = asp::measure_time>("Creating random array", create_random_array, N); + // std::cout << asp::min(original) << "<=>" << asp::max(original) << std::endl;; + // asp::print(original); + + test_sort(original, asp::bubble_sort, "Bubble sort"); + test_argsort(original, asp::bubble_sort_arg, "Bubble sort (arg)"); + test_sort(original, asp::quicksort, "Quicksort"); + test_argsort(original, asp::quicksort_arg, "Quicksort (arg)"); + test_sort(original, asp::quicksort_iter, "Quicksort (iterative)"); + test_argsort(original, asp::quicksort_arg_iter, "Quicksort (iterative) (arg)"); + test_sort(original, asp::mergesort, "Mergesort"); + test_argsort(original, asp::mergesort_arg, "Mergesort (arg)"); + + // W.I.P + // test_sort(original, asp::counting_sort, "Counting sort"); + // test_argsort(original, asp::counting_sort_arg, "Counting sort (arg)"); + // test_sort(original, asp::radix_sort, "Radix sort"); + // test_argsort(original, asp::radix_sort_arg, "Radix sort (arg)"); + + return EXIT_SUCCESS; +} diff --git a/data.hpp b/data.hpp new file mode 100644 index 0000000..7a18716 --- /dev/null +++ b/data.hpp @@ -0,0 +1,487 @@ +#pragma once +#include +#include +#include +#include + +namespace asp { + template + struct Array { + std::shared_ptr data; + size_t length = 0; + + Array() noexcept = delete; + Array(const size_t& size) noexcept: data(std::shared_ptr(new T[size])), length(size) { +// #ifdef __DEBUG +// printf("Creating array of size %lu\n", size); +// #endif + } + Array(const Array& other) noexcept: data(std::shared_ptr(new T[other.length])), length(other.length) { +#ifdef __DEBUG + printf("Copying array of size %lu\n", other.length); +#endif + memcpy(data.get(), other.data.get(), length * sizeof(T)); + } + Array(Array&& other) noexcept { +// #ifdef __DEBUG +// printf("Moving array of size %lu\n", other.length); +// #endif + data = other.data; + length = other.length; + other.data = nullptr; + other.length = 0; + } + + constexpr T& operator[](const size_t& index) const { +#ifdef __DEBUG + if (index > length) { + fprintf(stderr, "Index %ld out of range in Array of length %ld !\n", index, length); + throw std::out_of_range("Index out of range !"); + } +#endif + return data.get()[index]; + } + }; + + template + int print(const Array& a, const char* format) noexcept { + int num_written = 0; + num_written += printf("["); + char formatter[BUFSIZ] = { 0 }; + sprintf(formatter, "%s,", format); + for (size_t i = 0; i < a.length; ++i) + num_written += printf(formatter, a[i]); + sprintf(formatter, "%s]\n", format); + num_written += printf(formatter, a[a.length - 1]); + return num_written; + } + + int print(const Array& a) noexcept { + return print(a, "%i"); + } + + int print(const Array& a) noexcept { + return print(a, "%lu"); + } + + int print(const Array& a) noexcept { + //printf("%i\n", a[0]); + return print(a, "%i"); + } + + int print(const std::string& s) noexcept { + return printf("%s\n", s.c_str()); + } + + int print(const char* s) noexcept { + return printf("%s\n", s); + } + + template + constexpr T& max(const Array& a) noexcept { + T& max_el = a[0]; + for (size_t i = 1; i < a.length; ++i) + if (a[i] > max_el) + max_el = a[i]; + return max_el; + } + + template + constexpr T& min(const Array& a) noexcept { + T& max_el = a[0]; + for (size_t i = 1; i < a.length; ++i) + if (a[i] < max_el) + max_el = a[i]; + return max_el; + } + + template + Array& map(Array& a, const F& fnc) noexcept { + for (size_t i = 0; i < a.length; ++i) + a[i] = fnc(i, a[i]); + return a; + } + + template + void foreach(const Array& a, const F& fnc) noexcept { + for (size_t i = 0; i < a.length; ++i) + fnc(i, a[i]); + } + + Array range(const size_t& n) noexcept { + Array a(n); + return std::move(map(a, [](const size_t& i, const size_t&) -> const size_t& { + return i; + })); + } + + template + constexpr inline static void swap(T* a, T* b) noexcept { + const T temp = *a; + *a = *b; + *b = temp; + } + + template + void bubble_sort(const Array& a) noexcept { + size_t j; + for (size_t i = 0; i < a.length; ++i) + for (j = i + 1; j < a.length; ++j) + if (a[i] > a[j]) + swap(&a[i], &a[j]); + } + + template + Array bubble_sort_arg(const Array& a) noexcept { + Array indices = range(a.length); + size_t j; + for (size_t i = 0; i < a.length; ++i) + for (j = i + 1; j < a.length; ++j) + if (a[i] > a[j]){ + swap(&indices[i], &indices[j]); + swap(&a[i], &a[j]); + + } + return indices; + } + + template + static size_t qs_partition(const Array& a, const size_t& l, const size_t& h) noexcept { + size_t i = l - 1; + for (size_t j = l; j <= h; ++j) + if (a[j] < a[h]) + swap(&a[++i], &a[j]); + swap(&a[++i], &a[h]); + return i; + } + + template + static void quicksort(const Array& a, const size_t& l, const size_t& h) noexcept { + if (l >= h) + return; + + const size_t p = qs_partition(a, l, h); + if (p - 1 <= h) + quicksort(a, l, p - 1); + quicksort(a, p + 1, h); + } + + template + void quicksort(const Array& a) noexcept { + quicksort(a, 0, a.length - 1); + } + + template + static void quicksort_iter(const Array& a, const size_t& l, const size_t& h) noexcept { + // Create an auxiliary stack + + const size_t total = h - l + 1; + // push initial values of l and h to stack + size_t* stack = new size_t[total]{l, h}; + + // initialize top of stack + size_t top = 1; + + size_t low = l, high = h; + + // Keep popping from stack while is not empty + while (top <= total) { + // Pop h and l + high = stack[top--]; + low = stack[top--]; + if(low >= high) + break; + + // Set pivot element at its correct position + // in sorted array + const size_t p = qs_partition(a, low, high); + + // If there are elements on left side of pivot, + // then push left side to stack + if (p - 1 > low && p - 1 < total) { + stack[++top] = low; + stack[++top] = p - 1; + } + + // If there are elements on right side of pivot, + // then push right side to stack + if (p + 1 < high) { + stack[++top] = p + 1; + stack[++top] = high; + } + } + + delete[] stack; + } + + template + void quicksort_iter(const Array& a) noexcept { + quicksort_iter(a, 0, a.length - 1); + } + + template + static size_t qs_arg_partition(const Array& a, const Array& indices, const size_t& l, const size_t& h) noexcept { + size_t i = l - 1; + for (size_t j = l; j <= h; ++j) + if (a[j] < a[h]){ + swap(&a[++i], &a[j]); + swap(&indices[i], &indices[j]); + } + swap(&indices[++i], &indices[h]); + swap(&a[i], &a[h]); + return i; + } + + template + static void quicksort_arg(const Array& a, const Array& indices, const size_t& l, const size_t& h) noexcept { + if (l >= h) + return; + + const size_t p = qs_arg_partition(a, indices, l, h); + if (p - 1 <= h) + quicksort_arg(a, indices, l, p - 1); + quicksort_arg(a, indices, p + 1, h); + } + + template + Array quicksort_arg(const Array& other, const size_t& l, const size_t& h) noexcept { + Array indices = range(other.length); + quicksort_arg(other, indices, l, h); + return indices; + } + + template + Array quicksort_arg(const Array& a) noexcept { + return quicksort_arg(a, 0, a.length - 1); + } + + template + void quicksort_arg_iter(const Array& a, const Array& indices, const size_t& l, const size_t& h) noexcept { + // Create an auxiliary stack + + const size_t total = h - l + 1; + // push initial values of l and h to stack + size_t* stack = new size_t[total]{l,h}; + + // initialize top of stack + size_t top = 1; + + size_t low = l, high = h; + + // Keep popping from stack while is not empty + while (top <= total) { + // Pop h and l + high = stack[top--]; + low = stack[top--]; + if(low >= high) + break; + + // Set pivot element at its correct position + // in sorted array + const size_t p = qs_arg_partition(a, indices, low, high); + + // If there are elements on left side of pivot, + // then push left side to stack + if (p - 1 > low && p - 1 < total) { + stack[++top] = low; + stack[++top] = p - 1; + } + + // If there are elements on right side of pivot, + // then push right side to stack + if (p + 1 < high) { + stack[++top] = p + 1; + stack[++top] = high; + } + } + + delete[] stack; + } + + template + Array quicksort_arg_iter(const Array& a) noexcept { + Array indices = range(a.length); + quicksort_arg_iter(a, indices, 0, a.length - 1); + return indices; + } + + template + struct ArgVal { + size_t indice; + T val; + + ArgVal() noexcept = default; + ArgVal(const size_t& _i, const T& _v) noexcept : indice(_i), val(_v) {} + + constexpr bool operator>(const ArgVal& other) const noexcept { + return std::move(val > other.val); + } + constexpr bool operator<(const ArgVal& other) const noexcept { + return std::move(val < other.val); + } + constexpr bool operator>=(const ArgVal& other) const noexcept { + return std::move(val >= other.val); + } + constexpr bool operator<=(const ArgVal& other) const noexcept { + return std::move(val <= other.val); + } + }; + + template + static void merge(const Array& a, const size_t& l, const size_t& m, const size_t& r) noexcept { + + Array left_arr(m - l + 1); + memcpy(&left_arr.data[0], &a[l], left_arr.length * sizeof(T)); + Array right_arr(r - m); + memcpy(&right_arr.data[0], &a[m + 1], right_arr.length * sizeof(T)); + + size_t i_a0 = 0, i_a1 = 0, i = l; + + // Merge the temp arrays back size_to array[left..right] + for (; i_a0 < left_arr.length && i_a1 < right_arr.length; ++i) + a[i] = left_arr[i_a0] <= right_arr[i_a1] ? left_arr[i_a0++] : right_arr[i_a1++]; + // + // Copy the remaining elements of left[], if there are any + const size_t leftover = left_arr.length - i_a0; + memcpy(&a[i], &left_arr[i_a0], leftover * sizeof(T)); + // Copy the remaining elements of right[], if there are any + memcpy(&a[i + leftover], &right_arr[i_a1], (right_arr.length - i_a1) * sizeof(T)); + } + + template + void mergesort(const Array& a, const size_t& l, const size_t& r) noexcept { + if (l >= r) + return; + + const size_t m = l + (r - l) / 2; + mergesort(a, l, m); + mergesort(a, m + 1, r); + merge(a, l, m, r); + } + + template + void mergesort(const Array& a) noexcept { + mergesort(a, 0, a.length - 1); + } + + template + Array mergesort_arg(const Array& a, const size_t& l, const size_t& r) noexcept { + + Array> temp_vals(a.length); + map(temp_vals, [&a](const size_t& i, const ArgVal&) -> const ArgVal { + return ArgVal(i, a[i]); + }); + + mergesort(temp_vals, l, r); + + Array indices(a.length); + return std::move(map(indices, [&temp_vals](const size_t& i, const size_t&) -> size_t { + return temp_vals[i].indice; + })); + } + + template + Array mergesort_arg(const Array& a) noexcept { + return mergesort_arg(a, 0, a.length - 1); + } + + //static void count_sort(const Array& a, const int& exp, const int& d) noexcept { + // Array output(a.length), count(d); + // memset(&count[0], 0, d * sizeof(int)); + + // foreach(a, [count, exp, d](const int&, const int& val) -> void { + // count[(val / exp) % d]++; + // }); + + // for (int i = 1; i <= d; ++i) + // count[i] += count[i - 1]; + + // for (int i = a.length - 1; i >= 0; --i) { + // output[count[(a[i] / exp) % d] - 1] = a[i]; + // count[(a[i] / exp) % d]--; + // } + + // memcpy(&a[0], &output[0], a.length * sizeof(int)); + //} + + template + void counting_sort(const Array& a) noexcept { + Array output(a); + + map(a, [output](const size_t& i, const T&) -> const T& { + return output[i]; + }); + } + + template + Array counting_sort_arg(const Array& a) noexcept { + Array indices = range(a.length); + + return indices; + } + + template + inline void radix_sort_256(T* a, const size_t& n) noexcept { + //template + //void radix_sort(const Array& a) noexcept { + if (n <= 1) + //if (a.length <= 1) + return; + + T* output = new T[n]; // output array + size_t* count = new size_t[256]; + T* originalArr = a; // So we know which was input + + for (size_t shift = 0, s = 0; shift < 4; shift++, s += 8) { + // Zero the counts + for (size_t i = 0; i < 256; i++) + count[i] = 0; + + // Store count of occurrences in count[] + for (size_t i = 0; i < n; i++) + count[(a[i] >> s) & 0xff]++; + + // Change count[i] so that count[i] now contains + // actual position of this digit in output[] + for (size_t i = 1; i < 256; i++) + count[i] += count[i - 1]; + + // Build the output array + for (int i = n - 1; i >= 0; i--) { + // precalculate the offset as it's a few instructions + const size_t idx = (a[i] >> s) & 0xff; + + // Subtract from the count and store the value + output[--count[idx]] = a[i]; + } + + // Copy the output array to input[], so that input[] + // is sorted according to current digit + + // We can just swap the pointers + swap(a, output); + } + + // If we switched posize_ters an odd number of times, + // make sure we copy before returning + if (originalArr == output) { + swap(a, output); + for (size_t i = 0; i < n; i++) + a[i] = output[i]; + } + + delete[] output, delete[] count; + } + + template + void radix_sort(const Array& a) noexcept { + radix_sort_256(a.data.get(), a.length); + } + + template + Array radix_sort_arg(const Array& a) noexcept { + Array indices = range(a.length); + + return indices; + } +}; diff --git a/toolbox.cpp b/toolbox.cpp new file mode 100644 index 0000000..13cbe17 --- /dev/null +++ b/toolbox.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include "toolbox.hpp" + +namespace asp { + static const constexpr size_t N_TIMES = 10; + static const constexpr std::array time_formats = { "ns", "us", "ms", "s", "m", "h", "j", "w", "M", "y" }; + static const constexpr std::array time_numbers = { 1, 1000, 1000, 1000, 60, 60, 24, 7, 4, 12 }; + static const uint64_t total_time = std::accumulate(time_numbers.begin(), time_numbers.end(), (uint64_t)1, std::multiplies()); + + static const constexpr size_t N_BYTES = 7; + static const constexpr std::array bytes_formats = { "", "K", "M", "G", "P", "E", "Z" }; //, "Y" }; + static const constexpr uint64_t total_bytes = static_cast(1)<<(10 * (N_BYTES - 1)); + + /** + * @brief Format the time in seconds in human readable format. + * + * @param time Time in seconds + * @return std::string The formatted human readable string. + */ + std::string format_time(const uint64_t time) noexcept { + return time < 2 ? std::to_string(time) + "s" : format_time_ns(time * (uint64_t)1e9); + } + + /** + * @brief Format the time in nanoseconds in human readable format. + * + * @param time Time in nanoseconds + * @return std::string The formatted human readable string. + */ + std::string format_byte_size(uint64_t bytes) noexcept { + if (bytes == 0) + return "0B"; + uint64_t prod = total_bytes; + + std::string s = ""; + uint64_t res; + for (size_t i = N_BYTES; i > 0; --i) { + if (bytes >= prod) { + res = bytes / prod; + bytes %= prod; + s += std::to_string(res) + bytes_formats[i - 1] + "B "; + } + prod /= static_cast(1)<<10; + } + + if (s.back() == ' ') + s.pop_back(); + + return s; + } + + /** + * @brief Format the time in nanoseconds in human readable format. + * + * @param time Time in nanoseconds + * @return std::string The formatted human readable string. + */ + std::string format_time_ns(uint64_t time) noexcept { + if (time == 0) + return "0ns"; + uint64_t prod = total_time; + + std::string s = ""; + uint64_t res; + for (size_t i = N_TIMES; i > 0; --i) { + if (time >= prod) { + res = time / prod; + time %= prod; + s += std::to_string(res) + time_formats[i - 1] + " "; + } + prod /= time_numbers[i - 1]; + } + + if (s.back() == ' ') + s.pop_back(); + + return s; + } + + /** + * @brief Run some unitary tests on format functions + * + */ + void toolbox_unit_test() noexcept { + assert(std::string("0B") == format_byte_size(static_cast(0))); + assert(std::string("1B") == format_byte_size(static_cast(1))); + assert(std::string("1KB") == format_byte_size(static_cast(1)<<10)); + assert(std::string("1MB") == format_byte_size(static_cast(1)<<20)); + assert(std::string("1GB") == format_byte_size(static_cast(1)<<30)); + assert(std::string("1PB") == format_byte_size(static_cast(1)<<40)); + assert(std::string("1EB") == format_byte_size(static_cast(1)<<50)); + assert(std::string("1ZB") == format_byte_size(static_cast(1)<<60)); + //assert(std::string("1YB") == format_byte_size(static_cast(1)<<70)); + // UINT64_MAX == 18446744073709551615I64u == -1 + assert(std::string("15ZB 1023EB 1023PB 1023GB 1023MB 1023KB 1023B") == format_byte_size(static_cast(-1))); + + assert(std::string("0s") == format_time(static_cast(0))); + assert(std::string("1s") == format_time(static_cast(1))); + assert(std::string("1m") == format_time(static_cast(60))); + assert(std::string("1h") == format_time(static_cast(3600))); + assert(std::string("1j") == format_time(static_cast(86400))); + assert(std::string("1w") == format_time(static_cast(604800))); + assert(std::string("1M") == format_time(static_cast(2419200))); + assert(std::string("1y") == format_time(static_cast(29030400))); + + assert(std::string("0ns") == format_time_ns(static_cast(0))); + assert(std::string("1ns") == format_time_ns(static_cast(1))); + assert(std::string("1us") == format_time_ns(static_cast(1e3))); + assert(std::string("1ms") == format_time_ns(static_cast(1e6))); + assert(std::string("1s") == format_time_ns(static_cast(1e9))); + assert(std::string("1m") == format_time_ns(static_cast(6e10))); + assert(std::string("1h") == format_time_ns(static_cast(36e11))); + assert(std::string("1j") == format_time_ns(static_cast(864e11))); + assert(std::string("1w") == format_time_ns(static_cast(6048e11))); + assert(std::string("1M") == format_time_ns(static_cast(24192e11))); + assert(std::string("1y") == format_time_ns(static_cast(290304e11))); + // UINT64_MAX == 18446744073709551615I64u == -1 + assert(std::string("635y 5M 3j 23h 34m 33s 709ms 551us 615ns") == format_time_ns(static_cast(-1))); + } + + std::string thousand_sep(const uint64_t& k, const char& sep) noexcept { + std::string s = "", n = std::to_string(k); + + int c = 0; + for (int i = static_cast(n.size()) - 1; i >= 0; --i) { + c++; + s.push_back(n[i]); + if (c == 3) { + s.push_back(sep); + c = 0; + } + } + + std::reverse(s.begin(), s.end()); + + if (s.size() % 4 == 0) + s.erase(s.begin()); + + return s; + } + + std::string thousand_sep(const uint64_t& k) noexcept { + return thousand_sep(k, ','); + } + + void print_separator(const char* title) noexcept { +#define S(N) std::string(N, '-').c_str() + const constexpr size_t SEPARATOR_SIZE = W_NAME + W_TIME + W_FTIME + 6 + 6; + char separator[SEPARATOR_SIZE]; + sprintf(separator, "|%s|%s|%s|\n", S(W_NAME + 2), S(W_TIME + 2), S(W_FTIME + 2)); + printf("| %-" STR(W_NAME) "s | %-" STR(W_TIME) "s | %-" STR(W_FTIME) "s | \n%s", title, "Time spent(ns)", "Formatted time spent", separator); + } +} diff --git a/toolbox.hpp b/toolbox.hpp new file mode 100644 index 0000000..70a9058 --- /dev/null +++ b/toolbox.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +#define W_NAME 49 +#define W_TIME 17 +#define W_FTIME 29 +// Trick to insert preprocessor into strings +#define STR_(X) #X +#define STR(X) STR_(X) + +#define duration_ns(a) std::chrono::duration_cast(a).count() +#define time() std::chrono::high_resolution_clock::now() + +namespace asp { + std::string format_byte_size(uint64_t) noexcept; + std::string format_time(const uint64_t) noexcept; + std::string format_time_ns(uint64_t) noexcept; + void toolbox_unit_test() noexcept; + std::string thousand_sep(const uint64_t&, const char&) noexcept; + std::string thousand_sep(const uint64_t&) noexcept; + void print_separator(const char*) noexcept; + + template + void measure_time_void(const char* step_name, const F& fnc, Args &&...args) noexcept { +#ifndef __DEBUG + printf("| %-" STR(W_NAME) "s | %" STR(W_TIME) "s | %-" STR(W_FTIME) "s |\r", step_name, "In progress", "In progress"); +#endif + const auto start = time(); + fnc(std::forward(args)...); + const long long timespent = duration_ns(time() - start); + printf("| %-" STR(W_NAME) "s | %" STR(W_TIME) "s | %-" STR(W_FTIME) "s |\n", step_name, thousand_sep(timespent).c_str(), format_time_ns(timespent).c_str()); + } + + template + T measure_time(const char* step_name, const F& fnc, Args &&...args) noexcept { +#ifndef __DEBUG + printf("| %-" STR(W_NAME) "s | %" STR(W_TIME) "s | %-" STR(W_FTIME) "s |\r", step_name, "In progress", "In progress"); +#endif + const auto start = time(); + const T res = fnc(std::forward(args)...); + const long long timespent = duration_ns(time() - start); + printf("| %-" STR(W_NAME) "s | %" STR(W_TIME) "s | %-" STR(W_FTIME) "s |\n", step_name, thousand_sep(timespent).c_str(), format_time_ns(timespent).c_str()); + return res; + } +};