From 274f9490795baf0d84ddf1d7d91d7f925b63d1cd Mon Sep 17 00:00:00 2001 From: Teal Dulcet Date: Thu, 14 Jul 2022 01:33:38 -0700 Subject: [PATCH] Updated to support and use modern C++ features and added more examples. --- .github/workflows/ci.yml | 4 +- .travis.yml | 8 +- README.md | 380 ++++++++++++++++++++++++++++++++++----- graphs.cpp | 106 ++++++++--- graphs.hpp | 235 ++++++++++++------------ python/README.md | 94 +++++++++- python/graphs.py | 33 ++-- python/tables.py | 12 +- python/test.py | 21 ++- tables.cpp | 352 +++++++++++++++++++++++++++++++++--- tables.hpp | 258 +++++++++++++------------- 11 files changed, 1123 insertions(+), 380 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b212a5..c6f32ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,9 +27,9 @@ jobs: $CXX --version - name: Script run: | - $CXX -Wall -g -fsanitize=address,undefined tables.cpp -o tables + $CXX -Wall -g -Og -fsanitize=address,undefined tables.cpp -o tables ./tables - $CXX -Wall -g -fsanitize=address,undefined graphs.cpp -o graphs + $CXX -Wall -g -Og -fsanitize=address,undefined graphs.cpp -o graphs ./graphs - name: Cppcheck run: cppcheck --enable=all . diff --git a/.travis.yml b/.travis.yml index 5aca9e6..9a5e281 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,12 +13,12 @@ install: - sudo apt-get -yqq update - sudo apt-get -yqq install cppcheck script: - - g++ -Wall -g -fsanitize=address,undefined tables.cpp -o gcc_tables + - g++ -Wall -g -Og -fsanitize=address,undefined tables.cpp -o gcc_tables - ./gcc_tables - - g++ -Wall -g -fsanitize=address,undefined graphs.cpp -o gcc_graphs + - g++ -Wall -g -Og -fsanitize=address,undefined graphs.cpp -o gcc_graphs - ./gcc_graphs - - clang++ -Wall -g -fsanitize=address,undefined,integer tables.cpp -o clang_tables + - clang++ -Wall -g -Og -fsanitize=address,undefined,integer tables.cpp -o clang_tables - ./clang_tables - - clang++ -Wall -g -fsanitize=address,undefined,integer graphs.cpp -o clang_graphs + - clang++ -Wall -g -Og -fsanitize=address,undefined,integer graphs.cpp -o clang_graphs - ./clang_graphs - cppcheck --enable=all . diff --git a/README.md b/README.md index 476ee48..d096fe1 100644 --- a/README.md +++ b/README.md @@ -9,20 +9,29 @@ Copyright © 2018 Teal Dulcet These header only libraries use [box-drawing](https://en.wikipedia.org/wiki/Box-drawing_character#Unicode), [Braille](https://en.wikipedia.org/wiki/Braille_Patterns), [fraction](https://en.wikipedia.org/wiki/Number_Forms) and other Unicode characters and [terminal colors and formatting](https://misc.flogisoft.com/bash/tip_colors_and_formatting) to output tables and graphs/plots to the console. All the tables and graphs are created with a single (one) function call and they do not require any special data structures. +See the [python](python) directory for Python ports of the libraries. + ❤️ Please visit [tealdulcet.com](https://www.tealdulcet.com/) to support these libraries and my other software development. ## Tables ### Usage +Requires support for C++17. See the [tables.hpp](tables.hpp) file for full usage information. + Complete versions of all of the examples below and more can be found in the [tables.cpp](tables.cpp) file. -Compile with: `g++ -Wall -g -O3 tables.cpp -o tables`. +Compile with: -Run with: `./tables`. +GCC: `g++ -std=c++17 -Wall -g -O3 tables.cpp -o tables`\ +Clang: `clang++ -std=c++17 -Wall -g -O3 tables.cpp -o tables` + +Run with: `./tables` #### Output char array as table +##### C style char array + ```cpp #include "tables.hpp" @@ -36,15 +45,44 @@ int main() char ***array; // Allocate and set array - + tableoptions aoptions; aoptions.headerrow = true; aoptions.headercolumn = true; - table(rows, columns, array, NULL, NULL, aoptions); + table(rows, columns, array, nullptr, nullptr, aoptions); // Deallocate array - + + return 0; +} +``` + +##### C++ string array + +```cpp +#include "tables.hpp" + +using namespace std; + +int main() +{ + size_t rows = 5; + size_t columns = 5; + + vector> array(rows, vector(columns)); + + // Set array + + string *headerrow = nullptr; + string *headercolumn = nullptr; + + tableoptions aoptions; + aoptions.headerrow = true; + aoptions.headercolumn = true; + + table(array, headerrow, headercolumn, aoptions); + return 0; } ``` @@ -55,6 +93,8 @@ Table cells can contain [Unicode characters](https://en.wikipedia.org/wiki/List_ #### Output array as table with separate header row and column +##### C style char array + ```cpp #include "tables.hpp" @@ -64,14 +104,14 @@ int main() { size_t rows = 4; size_t columns = 4; - + const char* headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"}; const char* headercolumn[] = {"Header column 2", "Header column 3", "Header column 4", "Header column 5"}; char ***array; // Allocate and set array - + tableoptions aoptions; aoptions.headerrow = true; aoptions.headercolumn = true; @@ -79,7 +119,38 @@ int main() table(rows, columns, array, headerrow, headercolumn, aoptions); // Deallocate array - + + return 0; +} +``` + +##### C++ string array + +```cpp +#include "tables.hpp" + +using namespace std; + +int main() +{ + size_t rows = 5; + size_t columns = 5; + + string headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"}; + string headercolumn[] = {"Header column 2", "Header column 3", "Header column 4", "Header column 5"}; + + vector> array(rows, vector(columns)); + + // Set array + + tableoptions aoptions; + aoptions.headerrow = true; + aoptions.headercolumn = true; + // or with C++20: + // tableoptions aoptions{.headerrow = true, .headercolumn = true}; + + table(array, headerrow, headercolumn, aoptions); + return 0; } ``` @@ -88,6 +159,8 @@ Output same as example above. #### Output array as table +##### C style pointer + ```cpp #include "tables.hpp" @@ -102,10 +175,32 @@ int main() // Allocate and set array - table(rows, columns, array, NULL, NULL, tabledefaultoptions); + table(rows, columns, array); // Deallocate array - + + return 0; +} +``` + +##### C++ array/vector + +```cpp +#include "tables.hpp" + +using namespace std; + +int main() +{ + size_t rows = 5; + size_t columns = 5; + + vector> array(rows, vector(columns)); // array can be any data type + + // Set array + + table(array); + return 0; } ``` @@ -114,6 +209,8 @@ int main() #### Output sorted array as table +##### C style pointer + ```cpp #include #include "tables.hpp" @@ -124,7 +221,7 @@ int dimensions; // Number of columns int sortdimension; // Column to sort by template -bool compare(const T *a, const T *b) +bool compare(const T &a, const T &b) { if (a[sortdimension] == b[sortdimension]) for (int i = 0; i < dimensions; ++i) @@ -142,16 +239,56 @@ int main() int **array; // array can be any data type // Allocate and set array - + dimensions = columns; sortdimension = 0; - - sort(array, array + rows, compare); - table(rows, columns, array, NULL, NULL, tabledefaultoptions); + sort(array, array + rows, compare); + + table(rows, columns, array); // Deallocate array - + + return 0; +} +``` + +##### C++ array/vector + +```cpp +#include +#include "tables.hpp" + +using namespace std; + +int sortdimension; // Column to sort by + +template +bool compare(const T &a, const T &b) +{ + if (a[sortdimension] == b[sortdimension]) + for (int i = 0; i < size(a); ++i) + if (sortdimension != i and a[i] != b[i]) + return a[i] < b[i]; + + return a[sortdimension] < b[sortdimension]; +} + +int main() +{ + size_t rows = 5; + size_t columns = 5; + + vector> array(rows, vector(columns)); // array can be any data type + + // Set array + + sortdimension = 0; + + sort(array.begin(), array.end(), compare>); + + table(array); + return 0; } ``` @@ -160,6 +297,8 @@ int main() #### Output single function as table +##### C style function pointer + ```cpp #include "tables.hpp" @@ -175,12 +314,37 @@ int main() double xmin = -10; double xmax = 10; double xscl = 2; - + tableoptions aoptions; aoptions.headerrow = true; - + table(xmin, xmax, xscl, afunction, aoptions); - + + return 0; +} +``` + +##### C++ lambda function + +```cpp +#include "tables.hpp" + +using namespace std; + +int main() +{ + double xmin = -10; + double xmax = 10; + double xscl = 2; + + function afunction = [](auto x) + { return x + 1; }; + + tableoptions aoptions; + aoptions.headerrow = true; + + table(xmin, xmax, xscl, afunction, aoptions); + return 0; } ``` @@ -189,6 +353,8 @@ int main() #### Output multiple functions as table +##### C style function pointer + ```cpp #include #include "tables.hpp" @@ -210,17 +376,48 @@ int main() double xmin = -10; double xmax = 10; double xscl = 2; - + size_t numfunctions = 2; - + // Function parameter and return value can be any data type, as long as they are the same - double (*functions[])(double) = {function1, function2}; - + function functions[] = {function1, function2}; + tableoptions aoptions; aoptions.headerrow = true; - + table(xmin, xmax, xscl, numfunctions, functions, aoptions); - + + return 0; +} +``` + +##### C++ lambda function + +```cpp +#include +#include "tables.hpp" + +using namespace std; + +int main() +{ + double xmin = -10; + double xmax = 10; + double xscl = 2; + + size_t numfunctions = 2; + + // Function parameter and return value can be any data type, as long as they are the same + function functions[] = {[](auto x) + { return 2 * x; }, + [](auto x) + { return pow(x, 2); }}; + + tableoptions aoptions; + aoptions.headerrow = true; + + table(xmin, xmax, xscl, numfunctions, functions, aoptions); + return 0; } ``` @@ -274,7 +471,7 @@ Default value: `false` #### Title Option: `title`\ -Default value: `NULL` +Default value: `nullptr` The title is word wrapped based on the current width of the terminal, using [this](https://gist.github.com/tdulcet/819821ca69501822ad3f84a060c640a0) solution. Handles newlines, tabs and [Unicode characters](https://en.wikipedia.org/wiki/List_of_Unicode_characters). @@ -315,15 +512,21 @@ Values: ### Usage +Requires support for C++17. See the [graphs.hpp](graphs.hpp) file for full usage information. + Complete versions of all of the examples below and more can be found in the [graphs.cpp](graphs.cpp) file. -Compile with: `g++ -Wall -g -O3 graphs.cpp -o graphs`. +Compile with: +GCC: `g++ -std=c++17 -Wall -g -O3 graphs.cpp -o graphs`\ +Clang: `clang++ -std=c++17 -Wall -g -O3 graphs.cpp -o graphs` -Run with: `./graphs`. +Run with: `./graphs` If `height` is `0`, it will be set to the current height of the terminal (number of rows times four). If `width` is `0`, it will be set to the current width of the terminal (number of columns times two). -#### Output array as plot +#### Output single array as plot + +##### C style pointer ```cpp #include "graphs.hpp" @@ -346,10 +549,39 @@ int main() // Allocate and set array - graph(height, width, xmin, xmax, ymin, ymax, rows, array, graphdefaultoptions); + graph(height, width, xmin, xmax, ymin, ymax, rows, array); // Deallocate array - + + return 0; +} +``` + +##### C++ array/vector + +```cpp +#include "graphs.hpp" + +using namespace std; + +int main() +{ + size_t height = 160; + size_t width = 160; + + long double xmin = -20; + long double xmax = 20; + long double ymin = -20; + long double ymax = 20; + + size_t rows = 10; + + vector> array(rows, vector(2)); // array can be any data type, but must have exactly two columns + + // Set array + + graph(height, width, xmin, xmax, ymin, ymax, array); + return 0; } ``` @@ -358,8 +590,12 @@ If `xmin` and `xmax` are both `0`, they will be set to the respective minimum an ![](images/array%20to%20plot.png) +Use `graph()` to plot multiple arrays, which can be of different sizes. + #### Output single function as graph +##### C style function pointer + ```cpp #include "graphs.hpp" @@ -380,8 +616,34 @@ int main() long double ymin = -20; long double ymax = 20; - graph(height, width, xmin, xmax, ymin, ymax, afunction, graphdefaultoptions); - + graph(height, width, xmin, xmax, ymin, ymax, afunction); + + return 0; +} +``` + +##### C++ lambda function + +```cpp +#include "graphs.hpp" + +using namespace std; + +int main() +{ + size_t height = 160; + size_t width = 160; + + long double xmin = -20; + long double xmax = 20; + long double ymin = -20; + long double ymax = 20; + + function afunction = [](auto x) + { return x + 1; }; + + graph(height, width, xmin, xmax, ymin, ymax, afunction); + return 0; } ``` @@ -390,6 +652,8 @@ int main() #### Output multiple functions as graph +##### C style function pointer + ```cpp #include "graphs.hpp" @@ -414,14 +678,45 @@ int main() long double xmax = 20; long double ymin = -20; long double ymax = 20; - - size_t numfunctions = 2; - - // Function parameter and return value can be any data type, as long as they are the same - double (*functions[])(double) = {function1, function2}; - graph(height, width, xmin, xmax, ymin, ymax, numfunctions, functions, graphdefaultoptions); - + size_t numfunctions = 2; + + // Function parameter and return value can be any data type, as long as they are the same + function functions[] = {function1, function2}; + + graph(height, width, xmin, xmax, ymin, ymax, numfunctions, functions); + + return 0; +} +``` + +##### C++ lambda function + +```cpp +#include "graphs.hpp" + +using namespace std; + +int main() +{ + size_t height = 160; + size_t width = 160; + + long double xmin = -20; + long double xmax = 20; + long double ymin = -20; + long double ymax = 20; + + size_t numfunctions = 2; + + // Function parameter and return value can be any data type, as long as they are the same + function functions[] = {[](auto x) + { return 2 * x; }, + [](auto x) + { return pow(x, 2); }}; + + graph(height, width, xmin, xmax, ymin, ymax, numfunctions, functions); + return 0; } ``` @@ -452,7 +747,7 @@ Requires `border` and `axislabel` to be `true`. #### Title Option: `title`\ -Default value: `NULL` +Default value: `nullptr` The title is word wrapped based on the current width of the terminal, using [this](https://gist.github.com/tdulcet/819821ca69501822ad3f84a060c640a0) solution. Handles newlines, tabs and [Unicode characters](https://en.wikipedia.org/wiki/List_of_Unicode_characters). @@ -543,5 +838,4 @@ Both: * Port to other languages (C, Java, Rust, etc.) C++: -* Handle formatted text in the tables -* Support plotting multiple arrays of different sizes +* Support tables with the `wchar_t`, `char16_t` and `char32_t` C data types and the `wstring`, `u16string` and `u32string` C++ data types. diff --git a/graphs.cpp b/graphs.cpp index cd733e6..9fc67c4 100644 --- a/graphs.cpp +++ b/graphs.cpp @@ -1,40 +1,42 @@ // Teal Dulcet, CS546 -// Compile: g++ -Wall -g -O3 graphs.cpp -o graphs +// Compile: g++ -Wall -g -O3 -std=c++17 graphs.cpp -o graphs // Run: ./graphs #include +#include +#include #include "graphs.hpp" using namespace std; -long double afunction(long double x) +constexpr long double afunction(long double x) { return x + 1; } -long double function1(long double x) +constexpr long double function1(long double x) { return 2 * x; } -long double function2(long double x) +constexpr long double function2(long double x) { return pow(x, 2); } -long double function3(long double x) +constexpr long double function3(long double x) { return sin(x); } -long double function4(long double x) +constexpr long double function4(long double x) { return cos(x); } -long double function5(long double x) +constexpr long double function5(long double x) { return tan(x); } @@ -52,7 +54,7 @@ int main() const size_t rows = 10; const size_t columns = 2; - // Output array as plot + // Output single array as plot cout << "\nOutput array as plot\n\n"; { long double **array; @@ -62,18 +64,18 @@ int main() for (unsigned int i = 0; i < rows; ++i) for (unsigned int j = 0; j < columns; ++j) - array[i][j] = i + j; //rand(); + array[i][j] = i + j; // rand(); graphoptions aoptions; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; graph(height, width, xmin, xmax, ymin, ymax, rows, array, aoptions); } - if (array != NULL) + if (array != nullptr) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -81,12 +83,57 @@ int main() delete[] array; } } + { + array, rows> aarray; + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + aarray[i][j] = i + j; // rand(); + + graphoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + graph(height, width, xmin, xmax, ymin, ymax, aarray, aoptions); + } + } + { + vector> array(rows, vector(columns)); + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + array[i][j] = i + j; // rand(); + + graphoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + graph(height, width, xmin, xmax, ymin, ymax, array, aoptions); + } + } // Output single function as graph cout << "\nOutput single function as graph\n\n"; { graphoptions aoptions; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + graph(height, width, xmin, xmax, ymin, ymax, afunction, aoptions); + } + } + { + function afunction = [](auto x) + { return x + 1; }; + + graphoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; @@ -96,11 +143,26 @@ int main() // Output multiple functions as graph cout << "\nOutput multiple functions as graph\n\n"; { - long double (*functions[])(long double) = {function1, function2}; + function functions[] = {function1, function2}; graphoptions aoptions; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + graph(height, width, xmin, xmax, ymin, ymax, 2, functions, aoptions); + } + } + { + function functions[] = {[](auto x) + { return 2 * x; }, + [](auto x) + { return pow(x, 2); }}; + + graphoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; @@ -113,28 +175,30 @@ int main() const long double ymin = -4; const long double ymax = 4; - long double (*functions[])(long double) = {function3, function4, function5}; + function functions[] = {function3, function4, function5}; graphoptions aoptions; aoptions.axisunitslabel = false; + // graphoptions aoptions{.axisunitslabel = false}; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; graph(height, width, xmin, xmax, ymin, ymax, 3, functions, aoptions); } - /*aoptions.style = 2; - + /* aoptions.style = 2; + for (unsigned int k = 10; k < 300; ++k) { - cout << "\e[1;1H" << "\e[2J"; + cout << "\e[1;1H" + << "\e[2J"; graph(k, k, xmin, xmax, ymin, ymax, 3, functions, aoptions); - + usleep(200000); - }*/ + } */ } return 0; diff --git a/graphs.hpp b/graphs.hpp index 4277c73..cb22961 100644 --- a/graphs.hpp +++ b/graphs.hpp @@ -4,25 +4,29 @@ #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include +#include #include #include using namespace std; const char *const styles[][11] = { - {"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //ASCII - {"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //Basic - {"─", "│", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light - {"━", "┃", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"}, //Heavy - {"═", "║", "╔", "╦", "╗", "╠", "╬", "╣", "╚", "╩", "╝"}, //Double - {"╌", "╎", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light Dashed - {"╍", "╏", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"} //Heavy Dashed + {"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // ASCII + {"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // Basic + {"─", "│", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, // Light + {"━", "┃", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"}, // Heavy + {"═", "║", "╔", "╦", "╗", "╠", "╬", "╣", "╚", "╩", "╝"}, // Double + {"╌", "╎", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, // Light Dashed + {"╍", "╏", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"} // Heavy Dashed }; // {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}};//No border @@ -38,31 +42,15 @@ const long double fractionvalues[] = {1.0L / 4.0L, 1.0L / 2.0L, 3.0L / 4.0L, 1.0 struct graphoptions { - bool border; - bool axislabel; - bool axisunitslabel; - char *title; - unsigned int style; - unsigned int color; - graphoptions(void); - ~graphoptions(void); + bool border = true; + bool axislabel = true; + bool axisunitslabel = true; + const char *title = nullptr; + unsigned int style = 2; + unsigned int color = 2; }; -graphoptions::graphoptions(void) -{ - border = true; - axislabel = true; - axisunitslabel = true; - style = 2; - color = 2; - title = NULL; -} - -graphoptions::~graphoptions(void) -{ -} - -const graphoptions graphdefaultoptions; +const graphoptions defaultoptions; // Number of columns needed to represent the string // Adapted from: https://stackoverflow.com/a/31124065 @@ -76,8 +64,8 @@ int strcol(const char *const str) cout << "Control character: " << (int)str[i] << "\n"; } - length = mbstowcs(NULL, str, 0); - if (length == (size_t)-1) + length = mbstowcs(nullptr, str, 0); + if (length == static_cast(-1)) { cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n"; exit(1); @@ -86,9 +74,9 @@ int strcol(const char *const str) wchar_t *wcstring = new wchar_t[length]; - if (mbstowcs(wcstring, str, length) == (size_t)-1) + if (mbstowcs(wcstring, str, length) == static_cast(-1)) { - if (wcstring != NULL) + if (wcstring != nullptr) delete[] wcstring; cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n"; @@ -102,7 +90,7 @@ int strcol(const char *const str) exit(1); } - if (wcstring != NULL) + if (wcstring != nullptr) delete[] wcstring; return width; @@ -168,7 +156,7 @@ size_t outputlabel(const long double label, ostringstream &strm) long double intpart = 0; long double fractionpart = abs(modf(label, &intpart)); - for (unsigned int i = 0; i < (sizeof fractions / sizeof fractions[0]) and !output; ++i) + for (unsigned int i = 0; i < size(fractions) and !output; ++i) { if (abs(fractionpart - fractionvalues[i]) < DBL_EPSILON) { @@ -224,9 +212,9 @@ size_t outputlabel(const long double label, ostringstream &strm) } // Output graph -int graph(const size_t height, const size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, unsigned short **array, const graphoptions &aoptions) +int graph(const size_t height, const size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, const vector> &array, const graphoptions &aoptions) { - if (array == NULL) + if (!size(array)) return 1; const bool border = aoptions.border; @@ -235,7 +223,7 @@ int graph(const size_t height, const size_t width, const long double xmin, const const char *const title = aoptions.title; const unsigned int style = aoptions.style; - if (style >= (sizeof styles / sizeof styles[0])) + if (style >= size(styles)) return 1; if (height == 0) @@ -282,7 +270,7 @@ int graph(const size_t height, const size_t width, const long double xmin, const setlocale(LC_CTYPE, ""); - if (title != NULL and title[0] != '\0') + if (title != nullptr and title[0] != '\0') cout << wrap(title, w.ws_col) << "\n"; for (unsigned int i = 0; i < height; i += 4) @@ -296,12 +284,12 @@ int graph(const size_t height, const size_t width, const long double xmin, const if (border and axislabel and axisunitslabel) { bool output = false; - long double label; + long double label = 0; int adivisor = divisor; if (i < yaxis) adivisor = -adivisor; - for (long double k = yaxis + adivisor; ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or !axislabel) and !output; k += adivisor) + for (long double k = yaxis + adivisor; ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and i >= 4 and !output; k += adivisor) { if (i <= k and (i + 4) > k) { @@ -340,7 +328,7 @@ int graph(const size_t height, const size_t width, const long double xmin, const if (i < yaxis) adivisor = -adivisor; - for (long double k = yaxis + adivisor; ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or !axislabel) and !output; k += adivisor) + for (long double k = yaxis + adivisor; ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and i >= 4 and !output; k += adivisor) { if (i <= k and (i + 4) > k) { @@ -368,7 +356,7 @@ int graph(const size_t height, const size_t width, const long double xmin, const if (j < xaxis) adivisor = -adivisor; - for (long double k = xaxis + adivisor; ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and (j < (width - 4) or !axislabel) and !output; k += adivisor) + for (long double k = xaxis + adivisor; ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and j < (width - 4) and !output; k += adivisor) { if (j <= k and (j + 2) > k) { @@ -400,7 +388,7 @@ int graph(const size_t height, const size_t width, const long double xmin, const } else if (yaxislabel and axislabel and axisunitslabel) { - long double label; + long double label = 0; int adivisor = divisor; if (j < xaxis) { @@ -497,19 +485,24 @@ int graph(const size_t height, const size_t width, const long double xmin, const return 0; } -// Convert array to graph and output +// Convert one or more arrays to graph and output template -int graph(size_t height, size_t width, long double xmin, long double xmax, long double ymin, long double ymax, const size_t rows, T **array, const graphoptions &aoptions) +int graphs(size_t height, size_t width, long double xmin, long double xmax, long double ymin, long double ymax, const T &arrays, const graphoptions &aoptions = defaultoptions) { - if (rows == 0) + if (!size(arrays)) return 1; - if (array == NULL) + if (!all_of(begin(arrays), end(arrays), [](const auto &array) + { return all_of(begin(array), end(array), [](const auto &x) + { return size(x) == 2; }); })) + { + cerr << "Error: The arrays must have two columns."; return 1; + } const unsigned int color = aoptions.color; - if (color >= (sizeof colors / sizeof colors[0])) + if (color >= size(colors)) return 1; struct winsize w; @@ -538,32 +531,22 @@ int graph(size_t height, size_t width, long double xmin, long double xmax, long if (xmin == 0 and xmax == 0) { - xmin = numeric_limits::max(); - xmax = numeric_limits::min(); - - for (unsigned int i = 0; i < rows; ++i) - { - if (array[i][0] < xmin) - xmin = array[i][0]; - - if (array[i][0] > xmax) - xmax = array[i][0]; - } + const auto compare = [](const auto &a, const auto &b) + { return a[0] < b[0]; }; + const auto result = accumulate(begin(arrays), end(arrays), make_pair(arrays[0][0], arrays[0][0]), [compare](const auto ¤t, const auto &array) + { const auto minmax = minmax_element(begin(array), end(array), compare); return make_pair(min(current.first, *minmax.first, compare), max(current.second, *minmax.second, compare)); }); + xmin = result.first[0]; + xmax = result.second[0]; } if (ymin == 0 and ymax == 0) { - ymin = numeric_limits::max(); - ymax = numeric_limits::min(); - - for (unsigned int i = 0; i < rows; ++i) - { - if (array[i][1] < ymin) - ymin = array[i][1]; - - if (array[i][1] > ymax) - ymax = array[i][1]; - } + const auto compare = [](const auto &a, const auto &b) + { return a[1] < b[1]; }; + const auto result = accumulate(begin(arrays), end(arrays), make_pair(arrays[0][0], arrays[0][0]), [compare](const auto ¤t, const auto &array) + { const auto minmax = minmax_element(begin(array), end(array), compare); return make_pair(min(current.first, *minmax.first, compare), max(current.second, *minmax.second, compare)); }); + ymin = result.first[1]; + ymax = result.second[1]; } if (xmin >= xmax) @@ -583,46 +566,65 @@ int graph(size_t height, size_t width, long double xmin, long double xmax, long const long double xaxis = width - (xmax * xscl); const long double yaxis = ymax * yscl; - unsigned short **aarray; - aarray = new unsigned short *[width]; - for (unsigned int i = 0; i < width; ++i) - aarray[i] = new unsigned short[height]; + vector> aarray(width, vector(height, 0)); - for (unsigned int i = 0; i < width; ++i) - for (unsigned int j = 0; j < height; ++j) - aarray[i][j] = 0; - - for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < size(arrays); ++j) { - if (array[i][0] >= xmin and array[i][0] < xmax and array[i][1] >= ymin and array[i][1] < ymax) - { - const long long x = (array[i][0] * xscl) + xaxis; - const long long y = (yaxis - (array[i][1] * yscl)) - 1; + auto array = arrays[j]; + const unsigned int color = (j % (size(colors) - 2)) + 3; - aarray[x][y] = color + 1; + for (unsigned int i = 0; i < size(array); ++i) + { + if (array[i][0] >= xmin and array[i][0] < xmax and array[i][1] >= ymin and array[i][1] < ymax) + { + const long long x = (array[i][0] * xscl) + xaxis; + const long long y = (yaxis - (array[i][1] * yscl)) - 1; + + if (aarray[x][y]) + { + if (aarray[x][y] != color) + aarray[x][y] = 1; + } + else + aarray[x][y] = color; + } } } - int code = graph(height, width, xmin, xmax, ymin, ymax, aarray, aoptions); + return graph(height, width, xmin, xmax, ymin, ymax, aarray, aoptions); +} - if (aarray != NULL) - { - for (unsigned int i = 0; i < width; ++i) - delete[] aarray[i]; +// Convert single array to graph and output +template +int graph(size_t height, size_t width, long double xmin, long double xmax, long double ymin, long double ymax, const T &aarray, const graphoptions &aoptions = defaultoptions) +{ + const std::array aaarray = {aarray}; - delete[] aarray; - } + return graphs(height, width, xmin, xmax, ymin, ymax, aaarray, aoptions); +} - return code; +// Convert single array to graph and output +template +int graph(size_t height, size_t width, long double xmin, long double xmax, long double ymin, long double ymax, const size_t rows, T **aarray, const graphoptions &aoptions = defaultoptions) +{ + if (rows == 0) + return 1; + + const size_t columns = 2; + vector> aaarray(rows); + for (unsigned int i = 0; i < rows; ++i) + copy(aarray[i], aarray[i] + columns, aaarray[i].begin()); + + return graph(height, width, xmin, xmax, ymin, ymax, aaarray, aoptions); } // Convert one or more functions to graph and output template -int graph(size_t height, size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, const size_t numfunctions, T (*functions[])(T), const graphoptions &aoptions) +int graph(size_t height, size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, const size_t numfunctions, function functions[], const graphoptions &aoptions = defaultoptions) { const unsigned int color = aoptions.color; - if (color >= (sizeof colors / sizeof colors[0])) + if (color >= size(colors)) return 1; if (numfunctions == 0) @@ -671,18 +673,11 @@ int graph(size_t height, size_t width, const long double xmin, const long double const long double xaxis = width - (xmax * xscl); const long double yaxis = ymax * yscl; - unsigned short **array; - array = new unsigned short *[width]; - for (unsigned int i = 0; i < width; ++i) - array[i] = new unsigned short[height]; - - for (unsigned int i = 0; i < width; ++i) - for (unsigned int j = 0; j < height; ++j) - array[i][j] = 0; + vector> array(width, vector(height, 0)); for (unsigned int j = 0; j < numfunctions; ++j) { - unsigned short acolor = numfunctions == 1 ? color + 1 : (j % ((sizeof colors / sizeof colors[0]) - 2)) + 3; + unsigned short acolor = numfunctions == 1 ? color + 1 : (j % (size(colors) - 2)) + 3; for (long double i = 0; i < rows; i += 0.5) { @@ -705,25 +700,23 @@ int graph(size_t height, size_t width, const long double xmin, const long double } } - int code = graph(height, width, xmin, xmax, ymin, ymax, array, aoptions); - - if (array != NULL) - { - for (unsigned int i = 0; i < width; ++i) - delete[] array[i]; - - delete[] array; - } - - return code; + return graph(height, width, xmin, xmax, ymin, ymax, array, aoptions); } // Convert single function to function array and output template -int graph(size_t height, size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, T afunction(T), const graphoptions &aoptions) +int graph(size_t height, size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, const function &afunction, const graphoptions &aoptions = defaultoptions) { - T(*functions[]) - (T) = {afunction}; + std::function afunctions[] = {afunction}; - return graph(height, width, xmin, xmax, ymin, ymax, 1, functions, aoptions); + return graph(height, width, xmin, xmax, ymin, ymax, 1, afunctions, aoptions); +} + +// Convert single function to function array and output +template +int graph(size_t height, size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, T afunction(T), const graphoptions &aoptions = defaultoptions) +{ + std::function afunctions[] = {afunction}; + + return graph(height, width, xmin, xmax, ymin, ymax, 1, afunctions, aoptions); } diff --git a/python/README.md b/python/README.md index 9a4278b..c4e5f14 100644 --- a/python/README.md +++ b/python/README.md @@ -2,20 +2,20 @@ ### Usage -Requires Python 3.5 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`. +Requires Python 3.5 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`. See the [tables.py](tables.py) file for full usage information. Complete versions of all of the examples below and more can be found in the [test.py](test.py) file. Run with: `python3 test.py`. -#### Output char array as table +#### Output str array as table ```py import tables # Set array -tables.array(array, None, None, headerrow=True, headercolumn=True) +tables.array(array, headerrow=True, headercolumn=True) ``` Table cells can contain [Unicode characters](https://en.wikipedia.org/wiki/List_of_Unicode_characters), but not newlines and tabs. @@ -42,9 +42,9 @@ Output same as example above. ```py import tables -# Set array +# Set array, can be any sequence data type -tables.array(array, None, None) +tables.array(array) ``` ![](../images/array%20to%20table.png) @@ -60,7 +60,7 @@ sortdimension = 0 # Column to sort by array = sorted(array, key=lambda x: x[sortdimension]) -tables.array(array, None, None) +tables.array(array) ``` ![](../images/sorted%20array%20to%20table.png) @@ -82,6 +82,22 @@ tables.function(xmin, xmax, xscl, afunction, headerrow=True) ![](../images/function%20to%20table.png) +#### Output lambda function as table + +```py +import tables + +xmin = -10 +xmax = 10 +xscl = 2 + +afunction = lambda x: x + 1 + +tables.function(xmin, xmax, xscl, afunction, headerrow=True) +``` + +Output same as example above. + #### Output multiple functions as table ```py @@ -105,6 +121,23 @@ tables.functions(xmin, xmax, xscl, functions, headerrow=True) ![](../images/multiple%20functions%20to%20table.png) +#### Output multiple lambda functions as table + +```py +import tables + +xmin = -10 +xmax = 10 +xscl = 2 + +# Function parameter and return value can be any data type, as long as they are the same +functions = [lambda x: 2 * x, lambda x: x ** 2] + +tables.functions(xmin, xmax, xscl, functions, headerrow=True) +``` + +Output same as example above. + ### Options #### Header row @@ -182,7 +215,7 @@ Values: ### Usage -Requires Python 3.5 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`. +Requires Python 3.5 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`. See the [graphs.py](graphs.py) file for full usage information. Complete versions of all of the examples below and more can be found in the [test.py](test.py) file. @@ -190,7 +223,7 @@ Run with: `python3 test.py`. If `height` is `0`, it will be set to the current height of the terminal (number of rows times four). If `width` is `0`, it will be set to the current width of the terminal (number of columns times two). -#### Output array as plot +#### Output single array as plot ```py import graphs @@ -203,7 +236,7 @@ xmax = 20 ymin = -20 ymax = 20 -# Set array +# Set array, can be any sequence data type, but must have exactly two columns graphs.array(height, width, xmin, xmax, ymin, ymax, array) ``` @@ -212,6 +245,8 @@ If `xmin` and `xmax` are both `0`, they will be set to the respective minimum an ![](../images/array%20to%20plot.png) +Use `graphs.arrays()` to plot multiple arrays, which can be of different sizes. + #### Output single function as graph ```py @@ -233,6 +268,26 @@ graphs.function(height, width, xmin, xmax, ymin, ymax, afunction) ![](../images/function%20to%20graph.png) +#### Output lambda function as graph + +```py +import graphs + +height = 160 +width = 160 + +xmin = -20 +xmax = 20 +ymin = -20 +ymax = 20 + +afunction = lambda x: x + 1 + +graphs.function(height, width, xmin, xmax, ymin, ymax, afunction) +``` + +Output same as example above. + #### Output multiple functions as graph ```py @@ -260,6 +315,27 @@ graphs.functions(height, width, xmin, xmax, ymin, ymax, functions) ![](../images/multiple%20functions%20to%20graph.png) +#### Output multiple lambda functions as graph + +```py +import graphs + +height = 160 +width = 160 + +xmin = -20 +xmax = 20 +ymin = -20 +ymax = 20 + +# Function parameter and return value can be any data type, as long as they are the same +functions = [lambda x: 2 * x, lambda x: x ** 2] + +graphs.functions(height, width, xmin, xmax, ymin, ymax, functions) +``` + +Output same as example above. + ### Options #### Border/Axis diff --git a/python/graphs.py b/python/graphs.py index a2b75a4..08b83f9 100644 --- a/python/graphs.py +++ b/python/graphs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Teal Dulcet, CS546 @@ -113,7 +113,7 @@ def outputlabel(label: float) -> Tuple[int, str]: return length, strm -def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, array: List[List[int]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, title: Optional[str]=None) -> int: +def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, array: List[List[int]], border: bool = True, axislabel: bool = True, axisunitslabel: bool = True, style: int = 2, title: Optional[str] = None) -> int: """Output graph""" if not array: return 1 @@ -175,7 +175,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: adivisor = -divisor if i < yaxis else divisor k = yaxis + adivisor - while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or not axislabel) and not output: + while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and i >= 4 and not output: if (i <= k and (i + 4) > k): label = ymax - (k / yscl) @@ -202,7 +202,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: adivisor = -divisor if i < yaxis else divisor k = yaxis + adivisor - while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or not axislabel) and not output: + while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and i >= 4 and not output: if i <= k and (i + 4) > k: strm += styles[style][7] output = True @@ -220,7 +220,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: adivisor = -divisor if j < xaxis else divisor k = xaxis + adivisor - while ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and (j < (width - 4) or not axislabel) and not output: + while ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and j < (width - 4) and not output: if j <= k and (j + 2) > k: strm += styles[style][3] output = True @@ -314,7 +314,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: return 0 -def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarrays: Sequence[Sequence[Sequence[float]]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int: +def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarrays: Sequence[Sequence[Sequence[float]]], border: bool = True, axislabel: bool = True, axisunitslabel: bool = True, style: int = 2, color: int = 2, title: Optional[str] = None) -> int: """Convert one or more arrays to graph and output""" if not aarrays: return 1 @@ -328,6 +328,12 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: w = shutil.get_terminal_size() + if height == 0: + height = w.lines * 4 + + if width == 0: + width = w.columns * 2 + aheight = height // 4 awidth = width // 2 @@ -365,7 +371,8 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: aaarray = [[0 for j in range(height)] for i in range(width)] for j, aarray in enumerate(aarrays): - acolor = color + 1 if len(aarrays) == 1 else (j % (len(colors) - 2)) + 3 + acolor = color + 1 if len(aarrays) == 1 else (j % + (len(colors) - 2)) + 3 for i in aarray: if i[0] >= xmin and i[0] < xmax and i[1] >= ymin and i[1] < ymax: @@ -381,17 +388,17 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: return graph(height, width, xmin, xmax, ymin, ymax, aaarray, border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, title=title) -def array(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarray: Sequence[Sequence[float]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int: +def array(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarray: Sequence[Sequence[float]], border: bool = True, axislabel: bool = True, axisunitslabel: bool = True, style: int = 2, color: int = 2, title: Optional[str] = None) -> int: """Convert single array to graph and output""" return arrays(height, width, xmin, xmax, ymin, ymax, [aarray], border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, color=color, title=title) -def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, afunctions: Sequence[Callable[[float], float]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int: +def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, afunctions: Sequence[Callable[[float], float]], border: bool = True, axislabel: bool = True, axisunitslabel: bool = True, style: int = 2, color: int = 2, title: Optional[str] = None) -> int: """Convert one or more functions to graph and output""" if color < 0 or color >= len(colors): return 1 - if len(afunctions) == 0: + if not afunctions: return 1 w = shutil.get_terminal_size() @@ -402,8 +409,8 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym if width == 0: width = w.columns * 2 - aheight = height / 4 - awidth = width / 2 + aheight = height // 4 + awidth = width // 2 if aheight > w.lines: print("The height of the graph ({0}) is greater then the height of the terminal ({1}).".format( @@ -453,6 +460,6 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym return graph(height, width, xmin, xmax, ymin, ymax, array, border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, title=title) -def function(height, width, xmin: float, xmax: float, ymin: float, ymax: float, afunction: Callable[[float], float], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int: +def function(height, width, xmin: float, xmax: float, ymin: float, ymax: float, afunction: Callable[[float], float], border: bool = True, axislabel: bool = True, axisunitslabel: bool = True, style: int = 2, color: int = 2, title: Optional[str] = None) -> int: """Convert single function to function array and output""" return functions(height, width, xmin, xmax, ymin, ymax, [afunction], border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, color=color, title=title) diff --git a/python/tables.py b/python/tables.py index 7e659c1..6e18495 100644 --- a/python/tables.py +++ b/python/tables.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Teal Dulcet, CS546 @@ -39,7 +39,7 @@ def strcol(str: str) -> int: # return len(str) -def table(array: List[List[str]], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int: +def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True, cellborder: bool = False, padding: int = 1, alignment: bool = False, title: Optional[str] = None, style: int = 2) -> int: """Output char array as table""" if not array: return 1 @@ -158,7 +158,7 @@ def table(array: List[List[str]], headerrow: bool=False, headercolumn: bool=Fals return 0 -def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]], aheadercolumn: Optional[Sequence[Any]], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int: +def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]] = None, aheadercolumn: Optional[Sequence[Any]] = None, headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True, cellborder: bool = False, padding: int = 1, alignment: bool = False, title: Optional[str] = None, style: int = 2) -> int: """Convert array to char array and output as table""" if not aarray: return 1 @@ -220,9 +220,9 @@ def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]], return table(aaarray, headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style) -def functions(xmin: float, xmax: float, xscl: float, afunctions: Sequence[Callable[[float], float]], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int: +def functions(xmin: float, xmax: float, xscl: float, afunctions: Sequence[Callable[[float], float]], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True, cellborder: bool = False, padding: int = 1, alignment: bool = False, title: Optional[str] = None, style: int = 2) -> int: """Convert one or more functions to array and output as table""" - if len(afunctions) == 0: + if not afunctions: return 1 if xmin >= xmax: @@ -258,6 +258,6 @@ def functions(xmin: float, xmax: float, xscl: float, afunctions: Sequence[Callab return array(aarray, aheaderrow, None, headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style) -def function(xmin: float, xmax: float, xscl: float, afunction: Callable[[float], float], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int: +def function(xmin: float, xmax: float, xscl: float, afunction: Callable[[float], float], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True, cellborder: bool = False, padding: int = 1, alignment: bool = False, title: Optional[str] = None, style: int = 2) -> int: """Convert single function to array and output as table""" return functions(xmin, xmax, xscl, [afunction], headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style) diff --git a/python/test.py b/python/test.py index 3f9c2cf..fcaed36 100644 --- a/python/test.py +++ b/python/test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Teal Dulcet, CS546 @@ -78,11 +78,16 @@ for k in range(len(tables.styles)): print("\nOutput single function as table\n") for k in range(len(tables.styles)): tables.function(xmin, xmax, xscl, afunction, headerrow=True, style=k) +for k in range(len(tables.styles)): + tables.function(xmin, xmax, xscl, lambda x: x + 1, headerrow=True, style=k) print("\nOutput multiple functions as table\n") for k in range(len(tables.styles)): tables.functions(xmin, xmax, xscl, [ function1, function2], headerrow=True, style=k) +for k in range(len(tables.styles)): + tables.functions(xmin, xmax, xscl, [ + lambda x: 2 * x, lambda x: x ** 2], headerrow=True, style=k) height = 160 width = 160 @@ -92,20 +97,26 @@ xmax = 20 ymin = -20 ymax = 20 -print("\nOutput array as plot\n") -array = [[i + j for j in range(2)] for i in range(10)] +print("\nOutput single array as plot\n") +array = [range(i, i + 2) for i in range(10)] for k in range(len(graphs.styles)): graphs.array(height, width, xmin, xmax, ymin, ymax, array, style=k) print("\nOutput single function as graph\n") for k in range(len(graphs.styles)): graphs.function(height, width, xmin, xmax, ymin, ymax, afunction, style=k) +for k in range(len(graphs.styles)): + graphs.function(height, width, xmin, xmax, ymin, + ymax, lambda x: x + 1, style=k) print("\nOutput multiple functions as graph\n") for k in range(len(graphs.styles)): graphs.functions(height, width, xmin, xmax, ymin, ymax, [function1, function2], style=k) +for k in range(len(graphs.styles)): + graphs.functions(height, width, xmin, xmax, ymin, ymax, [ + lambda x: 2 * x, lambda x: x ** 2], style=k) for k in range(len(graphs.styles)): - graphs.functions(height, width, -(2 * math.pi), 2 * - math.pi, -4, 4, [math.sin, math.cos, math.tan], axisunitslabel=False, style=k) + graphs.functions(height, width, -(2 * math.pi), 2 * math.pi, -4, + 4, [math.sin, math.cos, math.tan], axisunitslabel=False, style=k) diff --git a/tables.cpp b/tables.cpp index 3594218..fcebae8 100644 --- a/tables.cpp +++ b/tables.cpp @@ -1,27 +1,29 @@ // Teal Dulcet, CS546 -// Compile: g++ -Wall -g -O3 tables.cpp -o tables +// Compile: g++ -Wall -g -O3 -std=c++17 tables.cpp -o tables // Run: ./tables #include #include #include +#include +#include #include "tables.hpp" using namespace std; -long double afunction(long double x) +constexpr long double afunction(long double x) { return x + 1; } -long double function1(long double x) +constexpr long double function1(long double x) { return 2 * x; } -long double function2(long double x) +constexpr long double function2(long double x) { return pow(x, 2); } @@ -32,8 +34,8 @@ int sortdimension = 0; /* template int compare(const void *pa, const void *pb) { - const T *a = *(const T **)pa; - const T *b = *(const T **)pb; + const T a = *(const T *)pa; + const T b = *(const T *)pb; if (a[sortdimension] == b[sortdimension]) { @@ -58,7 +60,7 @@ int compare(const void *pa, const void *pb) } */ template -bool compare(const T *a, const T *b) +bool compare(const T &a, const T &b) { if (a[sortdimension] == b[sortdimension]) for (int i = 0; i < dimensions; ++i) @@ -77,6 +79,9 @@ int main() const long double xmax = 10; const long double xscl = 2; // 80 / (xmax - xmin); + string *headerrow = nullptr; + string *headercolumn = nullptr; + // Output array as table cout << "\nOutput array as table\n\n"; { @@ -91,14 +96,14 @@ int main() tableoptions aoptions; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; - table(rows, columns, array, NULL, NULL, aoptions); + table(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != NULL) + if (array != nullptr) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -106,6 +111,38 @@ int main() delete[] array; } } + { + array, rows> aarray; + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + aarray[i][j] = rand(); + + tableoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(aarray, headerrow, headercolumn, aoptions); + } + } + { + vector> array(rows, vector(columns)); + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + array[i][j] = rand(); + + tableoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(array, headerrow, headercolumn, aoptions); + } + } { long double **array; array = new long double *[rows]; @@ -118,14 +155,14 @@ int main() tableoptions aoptions; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; - table(rows, columns, array, NULL, NULL, aoptions); + table(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != NULL) + if (array != nullptr) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -133,6 +170,38 @@ int main() delete[] array; } } + { + array, rows> aarray; + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + aarray[i][j] = static_cast(rand()) / static_cast(RAND_MAX); + + tableoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(aarray, headerrow, headercolumn, aoptions); + } + } + { + vector> array(rows, vector(columns)); + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + array[i][j] = static_cast(rand()) / static_cast(RAND_MAX); + + tableoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(array, headerrow, headercolumn, aoptions); + } + } // Output char array as table cout << "\nOutput char array as table\n\n"; { @@ -146,12 +215,32 @@ int main() tableoptions aoptions; aoptions.headerrow = true; aoptions.headercolumn = true; + // tableoptions aoptions{.headerrow = true, .headercolumn = true}; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; - table(rows, columns, array, NULL, NULL, aoptions); + table(array, headerrow, headercolumn, aoptions); + } + } + { + const array, rows> aarray = {{{"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"}, + {"Header column 2", "Data 1", "Data 2", "Data 3", "Data 4"}, + {"Header column 3", "Data 5", "Data 6", "Data 7", "Data 8"}, + {"Header column 4", "Data 9", "Data 10", "Data 11", "Data 12"}, + {"Header column 5", "Data 13", "Data 14", "Data 15", "Data 16"}}}; + + tableoptions aoptions; + aoptions.headerrow = true; + aoptions.headercolumn = true; + // tableoptions aoptions{.headerrow = true, .headercolumn = true}; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(aarray, headerrow, headercolumn, aoptions); } } // Output array as table with separate header row and column @@ -172,12 +261,142 @@ int main() tableoptions aoptions; aoptions.headerrow = true; aoptions.headercolumn = true; + // tableoptions aoptions{.headerrow = true, .headercolumn = true}; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; - table(rows, columns, array, headerrow, headercolumn, aoptions); + table(array, headerrow, headercolumn, aoptions); + } + } + { + const size_t rows = 4; + const size_t columns = 4; + + const array, rows> aarray = {{{"Data 1", "Data 2", "Data 3", "Data 4"}, + {"Data 5", "Data 6", "Data 7", "Data 8"}, + {"Data 9", "Data 10", "Data 11", "Data 12"}, + {"Data 13", "Data 14", "Data 15", "Data 16"}}}; + + const string headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"}; + const string headercolumn[] = {"Header column 2", "Header column 3", "Header column 4", "Header column 5"}; + + vector aheaderrow(headerrow, headerrow + (rows + 1) - 1); + vector aheadercolumn(headerrow, headerrow + 1); + aheadercolumn.insert(aheadercolumn.end(), headercolumn, headercolumn + columns - 1); + + for (unsigned int k = 0; k < size(styles); ++k) + { + { + tableoptions aoptions; + aoptions.headerrow = true; + aoptions.headercolumn = true; + aoptions.cellborder = true; + aoptions.style = k; + // tableoptions aoptions{.headerrow = true, .headercolumn = true, .cellborder = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + tableoptions aoptions; + aoptions.headerrow = true; + aoptions.headercolumn = true; + aoptions.style = k; + // tableoptions aoptions{.headerrow = true, .headercolumn = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + string *headerrow = aheaderrow.data(); + string *headercolumn = nullptr; + + tableoptions aoptions; + aoptions.headerrow = true; + aoptions.style = k; + // tableoptions aoptions{.headerrow = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + string *headerrow = nullptr; + string *headercolumn = aheadercolumn.data(); + + tableoptions aoptions; + aoptions.headercolumn = true; + aoptions.style = k; + // tableoptions aoptions{.headercolumn = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + string *headerrow = nullptr; + string *headercolumn = nullptr; + + tableoptions aoptions; + aoptions.cellborder = true; + aoptions.style = k; + // tableoptions aoptions{.cellborder = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + string *headerrow = nullptr; + string *headercolumn = nullptr; + + tableoptions aoptions; + aoptions.tableborder = false; + aoptions.style = k; + // tableoptions aoptions{.tableborder = false, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + tableoptions aoptions; + aoptions.tableborder = false; + aoptions.headerrow = true; + aoptions.headercolumn = true; + aoptions.style = k; + // tableoptions aoptions{.tableborder = false, .headerrow = true, .headercolumn = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + string *headerrow = aheaderrow.data(); + string *headercolumn = nullptr; + + tableoptions aoptions; + aoptions.tableborder = false; + aoptions.headerrow = true; + aoptions.style = k; + // tableoptions aoptions{.tableborder = false, .headerrow = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + string *headerrow = nullptr; + string *headercolumn = aheadercolumn.data(); + + tableoptions aoptions; + aoptions.tableborder = false; + aoptions.headercolumn = true; + aoptions.style = k; + // tableoptions aoptions{.tableborder = false, .headercolumn = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } + { + string *headerrow = nullptr; + string *headercolumn = nullptr; + + tableoptions aoptions; + aoptions.tableborder = false; + aoptions.cellborder = true; + aoptions.style = k; + // tableoptions aoptions{.tableborder = false, .cellborder = true, .style = k}; + + table(aarray, headerrow, headercolumn, aoptions); + } } } { @@ -192,15 +411,16 @@ int main() tableoptions aoptions; aoptions.boolalpha = true; + // tableoptions aoptions{.boolalpha = true}; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; - table(rows, columns, array, NULL, NULL, aoptions); + table(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != NULL) + if (array != nullptr) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -223,19 +443,19 @@ int main() dimensions = columns; sortdimension = 0; - // qsort(array, rows, sizeof(array[0]), compare); - sort(array, array + rows, compare); + // qsort(array, rows, sizeof(array[0]), compare); + sort(array, array + rows, compare); tableoptions aoptions; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; - table(rows, columns, array, NULL, NULL, aoptions); + table(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != NULL) + if (array != nullptr) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -243,13 +463,71 @@ int main() delete[] array; } } + { + array, rows> aarray; + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + aarray[i][j] = rand(); + + dimensions = columns; + sortdimension = 0; + + sort(aarray.begin(), aarray.end(), compare>); + + tableoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(aarray, headerrow, headercolumn, aoptions); + } + } + { + vector> array(rows, vector(columns)); + + for (unsigned int i = 0; i < rows; ++i) + for (unsigned int j = 0; j < columns; ++j) + array[i][j] = rand(); + + dimensions = columns; + sortdimension = 0; + + sort(array.begin(), array.end(), compare>); + + tableoptions aoptions; + + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(array, headerrow, headercolumn, aoptions); + } + } // Output single function as table cout << "\nOutput single function as table\n\n"; { tableoptions aoptions; aoptions.headerrow = true; + // tableoptions aoptions{.headerrow = true}; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(xmin, xmax, xscl, afunction, aoptions); + } + } + { + function afunction = [](auto x) + { return x + 1; }; + + tableoptions aoptions; + aoptions.headerrow = true; + // tableoptions aoptions{.headerrow = true}; + + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; @@ -259,12 +537,30 @@ int main() // Output multiple functions as table cout << "\nOutput multiple functions as table\n\n"; { - long double (*functions[])(long double) = {function1, function2}; + function functions[] = {function1, function2}; tableoptions aoptions; aoptions.headerrow = true; + // tableoptions aoptions{.headerrow = true}; - for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k) + for (unsigned int k = 0; k < size(styles); ++k) + { + aoptions.style = k; + + table(xmin, xmax, xscl, 2, functions, aoptions); + } + } + { + function functions[] = {[](auto x) + { return 2 * x; }, + [](auto x) + { return pow(x, 2); }}; + + tableoptions aoptions; + aoptions.headerrow = true; + // tableoptions aoptions{.headerrow = true}; + + for (unsigned int k = 0; k < size(styles); ++k) { aoptions.style = k; diff --git a/tables.hpp b/tables.hpp index 7333739..b064026 100644 --- a/tables.hpp +++ b/tables.hpp @@ -7,60 +7,49 @@ #include #include #include +#include +#include #include #include +#include using namespace std; const char *const styles[][11] = { - {"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //ASCII - {"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //Basic - {"─", "│", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light - {"━", "┃", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"}, //Heavy - {"═", "║", "╔", "╦", "╗", "╠", "╬", "╣", "╚", "╩", "╝"}, //Double - {"╌", "╎", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light Dashed - {"╍", "╏", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"} //Heavy Dashed + {"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // ASCII + {"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // Basic + {"─", "│", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, // Light + {"━", "┃", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"}, // Heavy + {"═", "║", "╔", "╦", "╗", "╠", "╬", "╣", "╚", "╩", "╝"}, // Double + {"╌", "╎", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, // Light Dashed + {"╍", "╏", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"} // Heavy Dashed }; // {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}};//No border +regex ansi(R"(\x1B\[(?:[0-9]+(?:;[0-9]+)*)?m)"); + struct tableoptions { - bool headerrow; - bool headercolumn; - bool tableborder; - bool cellborder; - unsigned int padding; - ios_base &(*alignment)(ios_base &); - bool boolalpha; - char *title; - unsigned int style; - tableoptions(void); - ~tableoptions(void); + bool headerrow = false; + bool headercolumn = false; + bool tableborder = true; + bool cellborder = false; + unsigned int padding = 1; + ios_base &(*alignment)(ios_base &) = left; + bool boolalpha = false; + const char *title = nullptr; + unsigned int style = 2; }; -tableoptions::tableoptions(void) -{ - headerrow = false; - headercolumn = false; - tableborder = true; - cellborder = false; - padding = 1; - alignment = left; - boolalpha = false; - title = NULL; - style = 2; -} - -tableoptions::~tableoptions(void) -{ -} - -const tableoptions tabledefaultoptions; +const tableoptions defaultoptions; // Number of columns needed to represent the string // Adapted from: https://stackoverflow.com/a/31124065 -int strcol(const char *const str) +int strcol(const char *str) { + const string astr = regex_replace(str, ansi, ""); + str = astr.c_str(); + size_t length = strlen(str); for (size_t i = 0; i < length; ++i) if (iscntrl(str[i])) @@ -69,8 +58,8 @@ int strcol(const char *const str) cout << "Control character: " << (int)str[i] << "\n"; } - length = mbstowcs(NULL, str, 0); - if (length == (size_t)-1) + length = mbstowcs(nullptr, str, 0); + if (length == static_cast(-1)) { cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n"; exit(1); @@ -79,9 +68,9 @@ int strcol(const char *const str) wchar_t *wcstring = new wchar_t[length]; - if (mbstowcs(wcstring, str, length) == (size_t)-1) + if (mbstowcs(wcstring, str, length) == static_cast(-1)) { - if (wcstring != NULL) + if (wcstring != nullptr) delete[] wcstring; cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n"; @@ -95,7 +84,7 @@ int strcol(const char *const str) exit(1); } - if (wcstring != NULL) + if (wcstring != nullptr) delete[] wcstring; return width; @@ -154,9 +143,10 @@ string wrap(const char *const str, const size_t line_length) } // Output char array as table -int table(const size_t rows, const size_t columns, char ***array, const tableoptions &aoptions) +template +int table(const vector>> &array, const tableoptions &aoptions) { - if (array == NULL) + if (!size(array)) return 1; const bool headerrow = aoptions.headerrow; @@ -167,9 +157,12 @@ int table(const size_t rows, const size_t columns, char ***array, const tableopt const char *const title = aoptions.title; const unsigned int style = aoptions.style; - if (style >= (sizeof styles / sizeof styles[0])) + if (style >= size(styles)) return 1; + const size_t rows = array.size(); + const size_t columns = array[0].size(); + int columnwidth[columns]; for (unsigned int j = 0; j < columns; ++j) columnwidth[j] = 0; @@ -182,7 +175,7 @@ int table(const size_t rows, const size_t columns, char ***array, const tableopt { for (unsigned int i = 0; i < rows; ++i) { - int cellwidth = strcol(array[i][j]); + int cellwidth = strcol(array[i][j].c_str()); if (cellwidth > columnwidth[j]) columnwidth[j] = cellwidth; } @@ -204,7 +197,7 @@ int table(const size_t rows, const size_t columns, char ***array, const tableopt return 1; } - if (title != NULL and title[0] != '\0') + if (title != nullptr and title[0] != '\0') cout << wrap(title, w.ws_col) << "\n"; if (tableborder) @@ -234,29 +227,25 @@ int table(const size_t rows, const size_t columns, char ***array, const tableopt else if (tableborder or (i > 0 and j > 0 and headerrow) or (j > 1 and headercolumn)) cout << " "; - const int difference = columnwidth[j] - strcol(array[i][j]); + const int difference = columnwidth[j] - strcol(array[i][j].c_str()); if ((i == 0 and headerrow) or (j == 0 and headercolumn)) { const int apadding = (difference / 2); - for (unsigned int k = 0; k < padding + apadding; ++k) - cout << " "; + cout << string(padding + apadding, ' '); cout << "\e[1m" << array[i][j] << "\e[22m"; - for (unsigned int k = 0; k < padding + (difference - apadding); ++k) - cout << " "; + cout << string(padding + (difference - apadding), ' '); } else { - for (unsigned int k = 0; k < padding; ++k) - cout << " "; + cout << string(padding, ' '); - cout << aoptions.alignment << setw(difference + strlen(array[i][j])) << array[i][j]; + cout << aoptions.alignment << setw(difference + array[i][j].length()) << array[i][j]; - for (unsigned int k = 0; k < padding; ++k) - cout << " "; + cout << string(padding, ' '); } } @@ -281,8 +270,7 @@ int table(const size_t rows, const size_t columns, char ***array, const tableopt for (unsigned int k = 0; k < (2 * padding) + columnwidth[j]; ++k) cout << styles[style][0]; else if (i < (rows - 1) and headercolumn) - for (unsigned int k = 0; k < (2 * padding) + columnwidth[j]; ++k) - cout << " "; + cout << string((2 * padding) + columnwidth[j], ' '); if (j == (columns - 1)) { @@ -329,29 +317,38 @@ int table(const size_t rows, const size_t columns, char ***array, const tableopt } // Convert array to char array and output as table -template -int table(size_t rows, size_t columns, const T &array, const char *const headerrow[], const char *const headercolumn[], const tableoptions &aoptions) +template +int table(const T1 &aarray, T2 headerrow[] = nullptr, T2 headercolumn[] = nullptr, const tableoptions &aoptions = defaultoptions) { + if (!size(aarray)) + return 1; + unsigned int i = 0; unsigned int j = 0; - if (headerrow != NULL) + size_t rows = size(aarray); + size_t columns = size(aarray[0]); + + if (!all_of(begin(aarray), end(aarray), [columns](auto &x) + { return size(x) == columns; })) + { + cerr << "Error: The rows of the array must have the same number of columns."; + return 1; + } + + if (headerrow != nullptr) ++rows; - if (headercolumn != NULL) + if (headercolumn != nullptr) ++columns; - char ***aarray; - aarray = new char **[rows]; - for (unsigned int i = 0; i < rows; ++i) - aarray[i] = new char *[columns]; + vector> aaarray(rows, vector(columns)); - if (headerrow != NULL) + if (headerrow != nullptr) { for (unsigned int j = 0; j < columns; ++j) { - aarray[i][j] = new char[strlen(headerrow[j]) + 1]; - strcpy(aarray[i][j], headerrow[j]); + aaarray[i][j] = headerrow[j]; } ++i; @@ -359,15 +356,14 @@ int table(size_t rows, size_t columns, const T &array, const char *const headerr for (unsigned int ii = 0; i < rows; ++i) { - if (headercolumn != NULL) + if (headercolumn != nullptr) { unsigned int ii = i; - if (headerrow != NULL) + if (headerrow != nullptr) --ii; - aarray[i][j] = new char[strlen(headercolumn[ii]) + 1]; - strcpy(aarray[i][j], headercolumn[ii]); + aaarray[i][j] = headercolumn[ii]; ++j; } @@ -379,10 +375,8 @@ int table(size_t rows, size_t columns, const T &array, const char *const headerr if (aoptions.boolalpha) strm << boolalpha; - strm << array[ii][jj]; - string str = strm.str(); - aarray[i][j] = new char[str.length() + 1]; - strcpy(aarray[i][j], str.c_str()); + strm << aarray[ii][jj]; + aaarray[i][j] = strm.str(); ++jj; } @@ -391,26 +385,48 @@ int table(size_t rows, size_t columns, const T &array, const char *const headerr ++ii; } - int code = table(rows, columns, aarray, aoptions); + return table(aaarray, aoptions); +} - if (aarray != NULL) +template +int table(const size_t rows, const size_t columns, T **aarray, const char *const headerrow[] = nullptr, const char *const headercolumn[] = nullptr, const tableoptions &aoptions = defaultoptions) +{ + vector> aaarray(rows, vector(columns)); + for (unsigned int i = 0; i < rows; ++i) + copy(aarray[i], aarray[i] + columns, aaarray[i].begin()); + + string *aheaderrow = nullptr; + string *aheadercolumn = nullptr; + + if (headerrow and headercolumn) { - for (unsigned int i = 0; i < rows; ++i) - { - for (unsigned int j = 0; j < columns; ++j) - delete[] aarray[i][j]; + vector aaheaderrow(rows + 1); + copy(headerrow, headerrow + rows + 1, aaheaderrow.begin()); + aheaderrow = aaheaderrow.data(); - delete[] aarray[i]; - } - delete[] aarray; + vector aaheadercolumn(columns); + copy(headercolumn, headercolumn + columns, aaheadercolumn.begin()); + aheadercolumn = aaheadercolumn.data(); + } + else if (headerrow) + { + vector aaheaderrow(rows); + copy(headerrow, headerrow + rows, aaheaderrow.begin()); + aheaderrow = aaheaderrow.data(); + } + else if (headercolumn) + { + vector aaheadercolumn(columns); + copy(headercolumn, headercolumn + columns, aaheadercolumn.begin()); + aheadercolumn = aaheadercolumn.data(); } - return code; + return table(aaarray, aheaderrow, aheadercolumn, aoptions); } // Convert one or more functions to array and output as table template -int table(const long double xmin, const long double xmax, const long double xscl, const size_t numfunctions, T (*functions[])(T), const tableoptions &aoptions) +int table(const long double xmin, const long double xmax, const long double xscl, const size_t numfunctions, function functions[], const tableoptions &aoptions = defaultoptions) { if (numfunctions == 0) return 1; @@ -433,75 +449,53 @@ int table(const long double xmin, const long double xmax, const long double xscl const char *const aheaderrow[] = {"x", "y"}; // const char* const aheaderrow[] = {"", "x", "y"}; - const size_t length = (sizeof aheaderrow / sizeof aheaderrow[0]); + const size_t length = size(aheaderrow); - char **headerrow = new char *[columns]; + string *headerrow = new string[columns]; for (unsigned int j = 0; j < columns; ++j) { if (j < (length - 1) or numfunctions == 1) { - headerrow[j] = new char[strlen(aheaderrow[j]) + 1]; - strcpy(headerrow[j], aheaderrow[j]); + headerrow[j] = aheaderrow[j]; } else { ostringstream strm; strm << aheaderrow[length - 1] << j - length + 2; - string str = strm.str(); - headerrow[j] = new char[str.length() + 1]; - strcpy(headerrow[j], str.c_str()); + headerrow[j] = strm.str(); } } - char **headercolumn = NULL; - // headercolumn = new char *[rows + 1]; + string *headercolumn = nullptr; + // headercolumn = new string[rows + 1]; // for (unsigned int i = 0; i < rows + 1; ++i) // { // ostringstream strm; // strm << i + 1; - // string str = strm.str(); - // headercolumn[i] = new char[str.length() + 1]; - // strcpy(headercolumn[i], str.c_str()); + // headercolumn[i] = strm.str(); // } - T **array; - array = new T *[rows]; - for (unsigned int i = 0; i < rows; ++i) - array[i] = new T[columns]; + vector> aarray(rows, vector(columns)); for (unsigned int i = 0; i < rows; ++i) { - array[i][0] = (i / xscl) + xmin; + aarray[i][0] = (i / xscl) + xmin; for (unsigned int j = 0; j < numfunctions; ++j) - array[i][j + 1] = (functions[j])(array[i][0]); + aarray[i][j + 1] = (functions[j])(aarray[i][0]); } - int code = table(rows, columns, array, headerrow, headercolumn, aoptions); + int code = table(aarray, headerrow, headercolumn, aoptions); - if (array != NULL) + if (headerrow != nullptr) { - for (unsigned int i = 0; i < rows; ++i) - delete[] array[i]; - - delete[] array; - } - - if (headerrow != NULL) - { - for (unsigned int j = 0; j < columns; ++j) - delete[] headerrow[j]; - delete[] headerrow; } - // if (headercolumn != NULL) + // if (headercolumn != nullptr) // { - // for (unsigned int i = 0; i < rows + 1; ++i) - // delete[] headercolumn[i]; - // delete[] headercolumn; // } @@ -510,10 +504,18 @@ int table(const long double xmin, const long double xmax, const long double xscl // Convert single function to array and output as table template -int table(const long double xmin, const long double xmax, const long double xscl, T afunction(T), const tableoptions &aoptions) +int table(const long double xmin, const long double xmax, const long double xscl, const function &afunction, const tableoptions &aoptions = defaultoptions) { - T(*functions[]) - (T) = {afunction}; + std::function afunctions[] = {afunction}; - return table(xmin, xmax, xscl, 1, functions, aoptions); + return table(xmin, xmax, xscl, 1, afunctions, aoptions); +} + +// Convert single function to array and output as table +template +int table(const long double xmin, const long double xmax, const long double xscl, T afunction(T), const tableoptions &aoptions = defaultoptions) +{ + std::function afunctions[] = {afunction}; + + return table(xmin, xmax, xscl, 1, afunctions, aoptions); }