From 73411c18cb51d9209478e1ca57b4fa39f92b2c3e Mon Sep 17 00:00:00 2001 From: Teal Dulcet Date: Fri, 10 Mar 2023 05:15:01 -0800 Subject: [PATCH] Updated the style and color options to use Enums. --- .github/workflows/ci.yml | 2 + README.md | 103 +++++----- graphs.cpp | 42 ++-- graphs.hpp | 411 +++++++++++++++++++++------------------ python/README.md | 99 ++++++---- python/graphs.py | 382 +++++++++++++++++++++--------------- python/tables.py | 166 ++++++++-------- python/test.py | 102 +++++----- tables.cpp | 128 ++++++------ tables.hpp | 194 +++++++++--------- 10 files changed, 903 insertions(+), 726 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcd4574..7fb4787 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,8 @@ jobs: ./graphs - name: Cppcheck run: cppcheck --enable=all . + - name: Clang-Tidy + run: clang-tidy -checks='bugprone-*,-bugprone-easily-swappable-parameters,cert-*,clang-analyzer-*,misc-const-correctness,misc-redundant-expression,misc-unused-*,modernize-*,-modernize-use-trailing-return-type,performance-*,portability-*,readability-const-return-type,readability-container-*,readability-duplicate-include,readability-else-after-return,readability-non-const-parameter,readability-redundant-*,readability-simplify-*,readability-string-compare,readability-use-anyofallof' -header-filter='.*' *.cpp -- -Wall -O3 -std=c++17 Python: name: Linux Python diff --git a/README.md b/README.md index 83198f4..657ada3 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,8 @@ Requires support for C++14. See the [tables.hpp](tables.hpp) file for full usage Complete versions of all of the examples below and more can be found in the [tables.cpp](tables.cpp) file. Compile with: - -GCC: `g++ -std=c++14 -Wall -g -O3 tables.cpp -o tables`\ -Clang: `clang++ -std=c++14 -Wall -g -O3 tables.cpp -o tables` +* GCC: `g++ -std=c++14 -Wall -g -O3 tables.cpp -o tables` +* Clang: `clang++ -std=c++14 -Wall -g -O3 tables.cpp -o tables` Run with: `./tables` @@ -313,12 +312,12 @@ int main() { double xmin = -10; double xmax = 10; - double xscl = 2; + double xstep = 0.5; tables::options aoptions; aoptions.headerrow = true; - tables::function(xmin, xmax, xscl, afunction, aoptions); + tables::function(xmin, xmax, xstep, afunction, aoptions); return 0; } @@ -335,7 +334,7 @@ int main() { double xmin = -10; double xmax = 10; - double xscl = 2; + double xstep = 0.5; function afunction = [](auto x) { return x + 1; }; @@ -343,7 +342,7 @@ int main() tables::options aoptions; aoptions.headerrow = true; - tables::function(xmin, xmax, xscl, afunction, aoptions); + tables::function(xmin, xmax, xstep, afunction, aoptions); return 0; } @@ -375,7 +374,7 @@ int main() { double xmin = -10; double xmax = 10; - double xscl = 2; + double xstep = 0.5; size_t numfunctions = 2; @@ -385,7 +384,7 @@ int main() tables::options aoptions; aoptions.headerrow = true; - tables::functions(xmin, xmax, xscl, numfunctions, functions, aoptions); + tables::functions(xmin, xmax, xstep, numfunctions, functions, aoptions); return 0; } @@ -403,7 +402,7 @@ int main() { double xmin = -10; double xmax = 10; - double xscl = 2; + double xstep = 0.5; size_t numfunctions = 2; @@ -416,7 +415,7 @@ int main() tables::options aoptions; aoptions.headerrow = true; - tables::functions(xmin, xmax, xscl, numfunctions, functions, aoptions); + tables::functions(xmin, xmax, xstep, numfunctions, functions, aoptions); return 0; } @@ -460,8 +459,10 @@ Default value: `1` Option: `alignment`\ Values: +* `nullptr` * `left` (default) * `right` +* `internal` (integer and floating-point types only) #### bool to alpha @@ -480,28 +481,35 @@ The title is word wrapped based on the current width of the terminal, using [thi Option: `style`\ Values: -0. ASCII +0. `style_ASCII`: ASCII ![](images/ASCII%20table.png) -1. Basic +1. `style_basic`: Basic ![](images/basic%20table.png) -2. Light (default) +2. `style_light`: Light (default) ![](images/light%20table.png) -3. Heavy +3. `style_heavy`: Heavy ![](images/heavy%20table.png) -4. Double +4. `style_double`: Double ![](images/double%20table.png) -5. Light Dashed +5. `style_light_dashed`: Light Dashed ![](images/light%20dashed%20table.png) -6. Heavy Dashed +6. `style_heavy_dashed`: Heavy Dashed ![](images/heavy%20dashed%20table.png) +#### Check size + +Option: `check`\ +Default value: `true` + +Check that the width of the table is not greater then the width of the terminal. + ### Other C++ Console Tables Libraries * [C++ Text Table](https://github.com/haarcuba/cpp-text-table) (must specify every cell individually in their data structure, limited options, no Unicode support, no header row or column support) @@ -517,8 +525,8 @@ Requires support for C++14. See the [graphs.hpp](graphs.hpp) file for full usage Complete versions of all of the examples below and more can be found in the [graphs.cpp](graphs.cpp) file. Compile with: -GCC: `g++ -std=c++14 -Wall -g -O3 graphs.cpp -o graphs`\ -Clang: `clang++ -std=c++14 -Wall -g -O3 graphs.cpp -o graphs` +* GCC: `g++ -std=c++14 -Wall -g -O3 graphs.cpp -o graphs` +* Clang: `clang++ -std=c++14 -Wall -g -O3 graphs.cpp -o graphs` Run with: `./graphs` @@ -756,25 +764,25 @@ The title is word wrapped based on the current width of the terminal, using [thi Option: `style`\ Values: -0. ASCII +0. `style_ASCII`: ASCII ![](images/ASCII%20graph.png) -1. Basic +1. `style_basic`: Basic ![](images/basic%20graph.png) -2. Light (default) +2. `style_light`: Light (default) ![](images/light%20graph.png) -3. Heavy +3. `style_heavy`: Heavy ![](images/heavy%20graph.png) -4. Double +4. `style_double`: Double ![](images/double%20graph.png) -5. Light Dashed +5. `style_light_dashed`: Light Dashed ![](images/light%20dashed%20graph.png) -6. Heavy Dashed +6. `style_heavy_dashed`: Heavy Dashed ![](images/heavy%20dashed%20graph.png) @@ -783,27 +791,25 @@ Values: Option: `color`\ Values: -0. System default -1. Black -2. Red (default) -3. Green -4. Yellow -5. Blue -6. Cyan -7. Light gray -8. Dark gray -9. Light red -10. Light green -11. Light yellow -12. Light blue -13. Light cyan -14. White +0. `color_default`: System default +1. `color_black`: Black +2. `color_red`: Red (default) +3. `color_green`: Green +4. `color_yellow`: Yellow +5. `color_blue`: Blue +6. `color_cyan`: Cyan +7. `color_light_gray`: Light gray +8. `color_dark_gray`: Dark gray +9. `color_light_red`: Light red +10. `color_light_green`: Light green +11. `color_light_yellow`: Light yellow +12. `color_light_blue`: Light blue +13. `color_light_cyan`: Light cyan +14. `color_white`: White See [here](https://misc.flogisoft.com/bash/tip_colors_and_formatting#foreground_text) for examples of the colors. -Only used for plots and when graphing a single function. - -When graphing multiple functions, colors `2` - `14` are used inorder. Color `0` is used where the functions cross. +Only used when plotting a single array and when graphing a single function. When plotting multiple arrays or graphing multiple functions, colors 2 - 14 are used inorder. The system default color is used where the plots cross. ##### Plot @@ -813,6 +819,13 @@ When graphing multiple functions, colors `2` - `14` are used inorder. Color `0` ![](images/graph%20colors.png) +#### Check size + +Option: `check`\ +Default value: `true` + +Check that the width and height of the graph are not greater then the respective width and height of the terminal. + ### Other C++ Console Graphs/Plots Libraries * [C++ terminal plotting library](https://github.com/fbbdev/plot) (requires C++14, based on [UnicodePlots.jl](https://github.com/Evizero/UnicodePlots.jl), no documentation and very difficult to use, although supports animations) diff --git a/graphs.cpp b/graphs.cpp index d60e350..269e3fa 100644 --- a/graphs.cpp +++ b/graphs.cpp @@ -68,14 +68,14 @@ int main() graphs::options aoptions; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; graphs::array(height, width, xmin, xmax, ymin, ymax, rows, array, aoptions); } - if (array != nullptr) + if (array) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -92,9 +92,9 @@ int main() graphs::options aoptions; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; graphs::array(height, width, xmin, xmax, ymin, ymax, aarray, aoptions); } @@ -108,9 +108,9 @@ int main() graphs::options aoptions; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; graphs::array(height, width, xmin, xmax, ymin, ymax, array, aoptions); } @@ -120,9 +120,9 @@ int main() { graphs::options aoptions; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; graphs::function(height, width, xmin, xmax, ymin, ymax, afunction, aoptions); } @@ -133,9 +133,9 @@ int main() graphs::options aoptions; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; graphs::function(height, width, xmin, xmax, ymin, ymax, afunction, aoptions); } @@ -147,11 +147,11 @@ int main() graphs::options aoptions; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; - graphs::functions(height, width, xmin, xmax, ymin, ymax, 2, functions, aoptions); + graphs::functions(height, width, xmin, xmax, ymin, ymax, graphs::size(functions), functions, aoptions); } } { @@ -162,11 +162,11 @@ int main() graphs::options aoptions; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; - graphs::functions(height, width, xmin, xmax, ymin, ymax, 2, functions, aoptions); + graphs::functions(height, width, xmin, xmax, ymin, ymax, graphs::size(functions), functions, aoptions); } } { @@ -181,11 +181,11 @@ int main() aoptions.axisunitslabel = false; // graphs::options aoptions{.axisunitslabel = false}; - for (unsigned int k = 0; k < graphs::size(graphs::styles); ++k) + for (const graphs::style_type style : graphs::style_types) { - aoptions.style = k; + aoptions.style = style; - graphs::functions(height, width, xmin, xmax, ymin, ymax, 3, functions, aoptions); + graphs::functions(height, width, xmin, xmax, ymin, ymax, graphs::size(functions), functions, aoptions); } /* aoptions.style = 2; @@ -195,7 +195,7 @@ int main() cout << "\e[1;1H" << "\e[2J"; - graphs::functions(k, k, xmin, xmax, ymin, ymax, 3, functions, aoptions); + graphs::functions(k, k, xmin, xmax, ymin, ymax, graphs::size(functions), functions, aoptions); usleep(200000); } */ diff --git a/graphs.hpp b/graphs.hpp index d64069b..f7aa95e 100644 --- a/graphs.hpp +++ b/graphs.hpp @@ -22,6 +22,19 @@ namespace graphs { using namespace std; + enum style_type + { + style_ASCII, + style_basic, + style_light, + style_heavy, + style_double, + style_light_dashed, + style_heavy_dashed + }; + + enum style_type const style_types[] = {style_ASCII, style_basic, style_light, style_heavy, style_double, style_light_dashed, style_heavy_dashed}; + const char *const styles[][11] = { {"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // ASCII {"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // Basic @@ -33,6 +46,27 @@ namespace graphs }; // {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}};//No border + enum color_type + { + color_default, + color_black, + color_red, + color_green, + color_yellow, + color_blue, + color_cyan, + color_light_gray, + color_dark_gray, + color_light_red, + color_light_green, + color_light_yellow, + color_light_blue, + color_light_cyan, + color_white + }; + + enum color_type const color_types[] = {color_default, color_black, color_red, color_green, color_yellow, color_blue, color_cyan, color_light_gray, color_dark_gray, color_light_red, color_light_green, color_light_yellow, color_light_blue, color_light_cyan, color_white}; + const char *const colors[] = {"\e[39m", "\e[30m", "\e[31m", "\e[32m", "\e[33m", "\e[34m", "\e[35m", "\e[36m", "\e[37m", "\e[90m", "\e[91m", "\e[92m", "\e[93m", "\e[94m", "\e[95m", "\e[96m", "\e[97m"}; const char *const dots[] = {"⠀", "⠁", "⠂", "⠃", "⠄", "⠅", "⠆", "⠇", "⠈", "⠉", "⠊", "⠋", "⠌", "⠍", "⠎", "⠏", "⠐", "⠑", "⠒", "⠓", "⠔", "⠕", "⠖", "⠗", "⠘", "⠙", "⠚", "⠛", "⠜", "⠝", "⠞", "⠟", "⠠", "⠡", "⠢", "⠣", "⠤", "⠥", "⠦", "⠧", "⠨", "⠩", "⠪", "⠫", "⠬", "⠭", "⠮", "⠯", "⠰", "⠱", "⠲", "⠳", "⠴", "⠵", "⠶", "⠷", "⠸", "⠹", "⠺", "⠻", "⠼", "⠽", "⠾", "⠿", "⡀", "⡁", "⡂", "⡃", "⡄", "⡅", "⡆", "⡇", "⡈", "⡉", "⡊", "⡋", "⡌", "⡍", "⡎", "⡏", "⡐", "⡑", "⡒", "⡓", "⡔", "⡕", "⡖", "⡗", "⡘", "⡙", "⡚", "⡛", "⡜", "⡝", "⡞", "⡟", "⡠", "⡡", "⡢", "⡣", "⡤", "⡥", "⡦", "⡧", "⡨", "⡩", "⡪", "⡫", "⡬", "⡭", "⡮", "⡯", "⡰", "⡱", "⡲", "⡳", "⡴", "⡵", "⡶", "⡷", "⡸", "⡹", "⡺", "⡻", "⡼", "⡽", "⡾", "⡿", "⢀", "⢁", "⢂", "⢃", "⢄", "⢅", "⢆", "⢇", "⢈", "⢉", "⢊", "⢋", "⢌", "⢍", "⢎", "⢏", "⢐", "⢑", "⢒", "⢓", "⢔", "⢕", "⢖", "⢗", "⢘", "⢙", "⢚", "⢛", "⢜", "⢝", "⢞", "⢟", "⢠", "⢡", "⢢", "⢣", "⢤", "⢥", "⢦", "⢧", "⢨", "⢩", "⢪", "⢫", "⢬", "⢭", "⢮", "⢯", "⢰", "⢱", "⢲", "⢳", "⢴", "⢵", "⢶", "⢷", "⢸", "⢹", "⢺", "⢻", "⢼", "⢽", "⢾", "⢿", "⣀", "⣁", "⣂", "⣃", "⣄", "⣅", "⣆", "⣇", "⣈", "⣉", "⣊", "⣋", "⣌", "⣍", "⣎", "⣏", "⣐", "⣑", "⣒", "⣓", "⣔", "⣕", "⣖", "⣗", "⣘", "⣙", "⣚", "⣛", "⣜", "⣝", "⣞", "⣟", "⣠", "⣡", "⣢", "⣣", "⣤", "⣥", "⣦", "⣧", "⣨", "⣩", "⣪", "⣫", "⣬", "⣭", "⣮", "⣯", "⣰", "⣱", "⣲", "⣳", "⣴", "⣵", "⣶", "⣷", "⣸", "⣹", "⣺", "⣻", "⣼", "⣽", "⣾", "⣿"}; @@ -40,23 +74,29 @@ namespace graphs const int values[][4] = {{0x1, 0x2, 0x4, 0x40}, {0x8, 0x10, 0x20, 0x80}}; const char *const fractions[] = {"¼", "½", "¾", "⅐", "⅑", "⅒", "⅓", "⅔", "⅕", "⅖", "⅗", "⅘", "⅙", "⅚", "⅛", "⅜", "⅝", "⅞"}; - const long double fractionvalues[] = {1.0L / 4.0L, 1.0L / 2.0L, 3.0L / 4.0L, 1.0L / 7.0L, 1.0L / 9.0L, 1.0L / 10.0L, 1.0L / 3.0L, 2.0L / 3.0L, 1.0L / 5.0L, 2.0L / 5.0L, 3.0L / 5.0L, 4.0L / 5.0L, 1.0L / 6.0L, 5.0L / 6.0L, 1.0L / 8.0L, 3.0L / 8.0L, 5.0L / 8.0L, 7.0L / 8.0L}; + const char *const constants[] = {"π", "e"}; + const long double constantvalues[] = {M_PI, M_E}; + + const long double max_bit = scalbn(1.0L, LDBL_MANT_DIG - 1); + const long double MAX = max_bit + (max_bit - 1); + struct options { bool border = true; bool axislabel = true; bool axisunitslabel = true; const char *title = nullptr; - unsigned int style = 2; - unsigned int color = 2; + style_type style = style_light; + color_type color = color_red; + bool check = true; }; const options defaultoptions; template - auto size(const T &array) + constexpr size_t size(const T &array) { return distance(begin(array), end(array)); } @@ -81,11 +121,11 @@ namespace graphs } ++length; - wchar_t *wcstring = new wchar_t[length]; + auto *wcstring = new wchar_t[length]; if (mbstowcs(wcstring, str, length) == static_cast(-1)) { - if (wcstring != nullptr) + if (wcstring) delete[] wcstring; cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n"; @@ -99,7 +139,7 @@ namespace graphs exit(1); } - if (wcstring != nullptr) + if (wcstring) delete[] wcstring; return width; @@ -110,8 +150,7 @@ namespace graphs // Adapted from: https://stackoverflow.com/a/42016346 and https://stackoverflow.com/a/13094734 string wrap(const char *const str, const size_t line_length) { - char words[strlen(str) + 1]; - strcpy(words, str); + string words = str; string wrapped; size_t index = 0; @@ -134,7 +173,7 @@ namespace graphs } char temp[templinelen + 1]; - strncpy(temp, words + (index - linelen), templinelen); + strncpy(temp, words.data() + (index - linelen), templinelen); temp[templinelen] = '\0'; size_t width = strcol(temp); @@ -158,62 +197,54 @@ namespace graphs } // Convert fractions and constants to Unicode characters - size_t outputlabel(const long double label, ostringstream &strm) + size_t outputfraction(const long double number, ostringstream &strm) { bool output = false; - long double intpart = 0; - long double fractionpart = abs(modf(label, &intpart)); - - for (unsigned int i = 0; i < graphs::size(fractions) and !output; ++i) + const long double n = abs(number); + if (n <= MAX) { - if (abs(fractionpart - fractionvalues[i]) < DBL_EPSILON) + long double intpart = 0; + long double fractionpart = abs(modf(number, &intpart)); + + for (size_t i = 0; i < graphs::size(fractions) and !output; ++i) { - if (intpart != 0) - strm << intpart; + if (abs(fractionpart - fractionvalues[i]) <= DBL_EPSILON * n) + { + if (intpart == 0 and number < 0) + strm << "-"; + else if (intpart != 0) + strm << setprecision(LDBL_DIG) << intpart; - strm << fractions[i]; + strm << fractions[i]; - output = true; + output = true; + } } - } - if (abs(label) >= DBL_EPSILON) - { - if (!output and fmod(label, M_PI) == 0) + if (n > DBL_EPSILON) { - const char symbol[] = "π"; + for (size_t i = 0; i < graphs::size(constants) and !output; ++i) + { + if (abs(fmod(number, constantvalues[i])) <= DBL_EPSILON * n) + { + intpart = number / constantvalues[i]; - intpart = label / M_PI; + if (intpart == -1) + strm << "-"; + else if (intpart != 1) + strm << setprecision(LDBL_DIG) << intpart; - if (intpart == -1) - strm << "-"; - else if (intpart != 1) - strm << intpart; + strm << constants[i]; - strm << symbol; - - output = true; - } - else if (!output and fmod(label, M_E) == 0) - { - const char symbol[] = "e"; - - intpart = label / M_E; - - if (intpart == -1) - strm << "-"; - else if (intpart != 1) - strm << intpart; - - strm << symbol; - - output = true; + output = true; + } + } } } if (!output) - strm << label; + strm << number; size_t length = strcol(strm.str().c_str()); @@ -230,10 +261,7 @@ namespace graphs const bool axislabel = aoptions.axislabel; const bool axisunitslabel = aoptions.axisunitslabel; const char *const title = aoptions.title; - const unsigned int style = aoptions.style; - - if (style >= graphs::size(styles)) - return 1; + const style_type style = aoptions.style; if (height == 0) return 1; @@ -244,19 +272,22 @@ namespace graphs struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - const int aheight = height / 4; - const int awidth = width / 2; - - if (aheight > w.ws_row) + if (aoptions.check) { - cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; - return 1; - } + const size_t aheight = height / 4; + const size_t awidth = width / 2; - if (awidth > w.ws_col) - { - cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; - return 1; + if (aheight > w.ws_row) + { + cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; + return 1; + } + + if (awidth > w.ws_col) + { + cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; + return 1; + } } if (xmin >= xmax) @@ -271,38 +302,39 @@ namespace graphs return 1; } - const long double xscl = width / (xmax - xmin); - const long double yscl = height / (ymax - ymin); - const long double xaxis = width - (xmax * xscl); - const long double yaxis = ymax * yscl; - const int divisor = 2 * 4 * ((width / 160.0) > 1 ? (width / 160) + 1 : 1); + const long double xstep = (xmax - xmin) / width; + const long double ystep = (ymax - ymin) / height; + const long double xaxis = xmin > 0 ? 0 : xmax < 0 ? width + : width - (xmax / xstep); + const long double yaxis = ymin > 0 ? height : ymax < 0 ? 0 + : ymax / ystep; + const int xdivisor = 2 * 4 * ((width / 160) + 2); + const int ydivisor = 2 * 4 * ((height / 160) + 2); - setlocale(LC_CTYPE, ""); + setlocale(LC_ALL, ""); - if (title != nullptr and title[0] != '\0') - cout << wrap(title, w.ws_col) << "\n"; + if (title and title[0] != '\0') + cout << wrap(title, width / 2) << "\n"; - for (unsigned int i = 0; i < height; i += 4) + for (size_t i = 0; i < height; i += 4) { - const bool ayaxis = (i <= yaxis and (i + 4) > yaxis); - const bool yaxislabel = (i <= (yaxis + 4) and (i + 4) > (yaxis + 4)); + const bool ayaxis = yaxis <= (height - 4) ? i <= yaxis and (i + 4) > yaxis : i < yaxis and (i + 4) >= yaxis; + const bool yaxislabel = yaxis <= (height - 4) ? i <= (yaxis + 4) and (i + 4) > (yaxis + 4) : i < (yaxis - 4) and (i + 4) >= (yaxis - 4); ostringstream ylabelstrm; size_t ylabellength = 0; - if (border and axislabel and axisunitslabel) + if (border and axislabel and axisunitslabel and yaxis >= 0 and yaxis <= height) { bool output = false; long double label = 0; - int adivisor = divisor; - if (i < yaxis) - adivisor = -adivisor; + int adivisor = i < yaxis ? -ydivisor : ydivisor; - 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) + for (long double k = yaxis + adivisor; (i < yaxis ? k >= i : k < (i + 4)) and i >= 4 and !output; k += adivisor) { if (i <= k and (i + 4) > k) { - label = ymax - (k / yscl); + label = ymax - ((k > height ? height : k) * ystep); output = true; } @@ -310,15 +342,15 @@ namespace graphs if (output) { - ylabellength = outputlabel(label, ylabelstrm); + ylabellength = outputfraction(label, ylabelstrm); ylabellength *= 2; } } - for (unsigned int j = 0; j < width; j += 2) + for (size_t j = 0; j < width; j += 2) { - const bool axaxis = (j <= xaxis and (j + 2) > xaxis); - const bool xaxislabel = (j <= (xaxis - 2) and (j + 2) > (xaxis - 2)); + const bool axaxis = xaxis >= 2 ? j < xaxis and (j + 2) >= xaxis : j <= xaxis and (j + 2) > xaxis; + const bool xaxislabel = xaxis >= 2 ? j < (xaxis - 2) and (j + 2) >= (xaxis - 2) : j <= (xaxis + 2) and (j + 2) > (xaxis + 2); bool output = false; @@ -331,66 +363,72 @@ namespace graphs } else if (axaxis) { - if (axislabel and axisunitslabel) + if (i == 0) { - int adivisor = divisor; - if (i < yaxis) - adivisor = -adivisor; + cout << styles[style][4]; + output = true; + } + else if (i >= (height - 4)) + { + cout << styles[style][10]; + output = true; + } + else if (axislabel and axisunitslabel) + { + int adivisor = i < yaxis ? -ydivisor : ydivisor; - 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) + for (long double k = yaxis + adivisor; (i < yaxis ? k >= i : k < (i + 4)) and i >= 4 and !output; k += adivisor) { if (i <= k and (i + 4) > k) { - cout << styles[style][7]; + cout << styles[style][xaxis >= 2 ? 7 : 5]; output = true; } } } if (!output) { - if (i == 0) - cout << styles[style][4]; - else if (i >= (height - 4)) - cout << styles[style][10]; - else - cout << styles[style][1]; + cout << styles[style][1]; output = true; } } else if (ayaxis) { - if (axislabel and axisunitslabel) + if (j == 0) { - int adivisor = divisor; - if (j < xaxis) - adivisor = -adivisor; + cout << styles[style][2]; + output = true; + } + else if (j >= (width - 2)) + { + cout << styles[style][4]; + output = true; + } + else if (axislabel and axisunitslabel) + { + int adivisor = j < xaxis ? -xdivisor : xdivisor; - 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) + for (long double k = xaxis + adivisor; (j < xaxis ? k >= j : k < (j + 2)) and j < (width - (2 * 2)) and !output; k += adivisor) { if (j <= k and (j + 2) > k) { - cout << styles[style][3]; + cout << styles[style][yaxis <= (height - 4) ? 3 : 9]; output = true; } } } if (!output) { - if (j == 0) - cout << styles[style][2]; - else if (j >= (width - 2)) - cout << styles[style][4]; - else - cout << styles[style][0]; + cout << styles[style][0]; output = true; } } - else if (yaxislabel and xaxislabel and axislabel and axisunitslabel) + else if (yaxislabel and xaxislabel and axislabel and axisunitslabel and ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0) { cout << "0"; output = true; } - else if (j >= (width - 2) and yaxislabel and axislabel) + else if ((xaxis <= (width - 2) ? j >= (width - 2) : j == 0) and yaxislabel and axislabel) { cout << "x"; output = true; @@ -398,18 +436,15 @@ namespace graphs else if (yaxislabel and axislabel and axisunitslabel) { long double label = 0; - int adivisor = divisor; + int adivisor = j < xaxis ? -xdivisor : xdivisor; if (j < xaxis) - { - adivisor = -adivisor; j += 2; - } - for (long double k = xaxis + adivisor; ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and j < (width - 2) and !output; k += adivisor) + for (long double k = xaxis + adivisor; (j < xaxis ? k >= j : k < (j + 2)) and j < (width - 2) and !output; k += adivisor) { if (j <= k and (j + 2) > k) { - label = (k / xscl) + xmin; + label = ((k > width ? width : k) * xstep) + xmin; output = true; } @@ -423,9 +458,9 @@ namespace graphs output = false; ostringstream strm; - size_t length = outputlabel(label, strm); + size_t length = outputfraction(label, strm); length *= 2; - if ((j >= xaxis or (j + length) < (xaxis - 4)) and (j + length) < (width - 2)) + if ((j >= xaxis or (j + length) < (ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0 ? xaxis - 4 : xaxis)) and (j + length) < (width - 2) and (xaxis <= (width - 2) or j > 2)) { cout << strm.str(); @@ -439,12 +474,12 @@ namespace graphs } } } - else if (i == 0 and xaxislabel and axislabel) + else if ((yaxis >= 4 ? i == 0 : i >= (height - 4)) and xaxislabel and axislabel) { cout << "y"; output = true; } - else if ((j <= (xaxis - ylabellength) and (j + 2) > (xaxis - ylabellength)) and axislabel and axisunitslabel) + else if (ylabellength and (xaxis < 2 ? xaxislabel : j < (xaxis - ylabellength) and (j + 2) >= (xaxis - ylabellength)) and (yaxis >= 4 or i < (height - 4)) and axislabel and axisunitslabel) { cout << ylabelstrm.str(); output = true; @@ -455,21 +490,23 @@ namespace graphs if (!output) { - unsigned int dot = 0; + size_t dot = 0; unsigned short color = 0; - for (unsigned int k = 0; k < 2 and k < (width - j); ++k) + for (size_t k = 0; k < 2 and k < (width - j); ++k) { - for (unsigned int l = 0; l < 4 and l < (height - i); ++l) + for (size_t l = 0; l < 4 and l < (height - i); ++l) { - dot += (array[j + k][i + l] ? 1 : 0) * values[k][l]; + const unsigned short value = array[j + k][i + l]; + if (value) + dot += values[k][l]; if (color) { - if (array[j + k][i + l] and color != array[j + k][i + l]) + if (value and color != value) color = 1; } else - color = array[j + k][i + l]; + color = value; } } @@ -479,14 +516,15 @@ namespace graphs if (color) cout << colors[color]; - cout << "\e[1m" << dots[dot] << "\e[22m"; + cout << dots[dot]; if (color) cout << colors[0]; } } - cout << "\n"; + if (i < (height - 4)) + cout << "\n"; } cout << endl; @@ -505,14 +543,11 @@ namespace graphs { return all_of(begin(array), end(array), [](const auto &x) { return graphs::size(x) == 2; }); })) { - cerr << "Error: The arrays must have two columns."; + cerr << "Error: The arrays must have two columns.\n"; return 1; } - const unsigned int color = aoptions.color; - - if (color >= graphs::size(colors)) - return 1; + const color_type color = aoptions.color; struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); @@ -523,19 +558,22 @@ namespace graphs if (width == 0) width = w.ws_col * 2; - const int aheight = height / 4; - const int awidth = width / 2; - - if (aheight > w.ws_row) + if (aoptions.check) { - cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; - return 1; - } + const size_t aheight = height / 4; + const size_t awidth = width / 2; - if (awidth > w.ws_col) - { - cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; - return 1; + if (aheight > w.ws_row) + { + cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; + return 1; + } + + if (awidth > w.ws_col) + { + cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; + return 1; + } } if (xmin == 0 and xmax == 0) @@ -570,32 +608,34 @@ namespace graphs return 1; } - const long double xscl = width / (xmax - xmin); - const long double yscl = height / (ymax - ymin); - const long double xaxis = width - (xmax * xscl); - const long double yaxis = ymax * yscl; + const long double xstep = (xmax - xmin) / width; + const long double ystep = (ymax - ymin) / height; + const long double xaxis = width - (xmax / xstep); + const long double yaxis = ymax / ystep; vector> aarray(width, vector(height, 0)); - for (unsigned int j = 0; j < graphs::size(arrays); ++j) + for (size_t j = 0; j < graphs::size(arrays); ++j) { - auto array = arrays[j]; - const unsigned int color = (j % (graphs::size(colors) - 2)) + 3; + const auto &array = arrays[j]; + const unsigned int acolor = graphs::size(arrays) == 1 ? color + 1 : (j % (graphs::size(colors) - 2)) + 3; - for (unsigned int i = 0; i < graphs::size(array); ++i) + for (size_t i = 0; i < graphs::size(array); ++i) { - if (array[i][0] >= xmin and array[i][0] < xmax and array[i][1] >= ymin and array[i][1] < ymax) + const auto &x = array[i][0], &y = array[i][1]; + + if (x >= xmin and x < xmax and y >= ymin and y < ymax) { - const long long x = (array[i][0] * xscl) + xaxis; - const long long y = (yaxis - (array[i][1] * yscl)) - 1; + const size_t ax = (x / xstep) + xaxis; + const size_t ay = (yaxis - (y / ystep)) - 1; - if (aarray[x][y]) + if (aarray[ax][ay]) { - if (aarray[x][y] != color) - aarray[x][y] = 1; + if (aarray[ax][ay] != acolor) + aarray[ax][ay] = 1; } else - aarray[x][y] = color; + aarray[ax][ay] = acolor; } } } @@ -621,7 +661,7 @@ namespace graphs const size_t columns = 2; vector> aaarray(rows); - for (unsigned int i = 0; i < rows; ++i) + for (size_t i = 0; i < rows; ++i) copy(aarray[i], aarray[i] + columns, aaarray[i].begin()); return array(height, width, xmin, xmax, ymin, ymax, aaarray, aoptions); @@ -631,10 +671,7 @@ namespace graphs template int functions(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 options &aoptions = defaultoptions) { - const unsigned int color = aoptions.color; - - if (color >= graphs::size(colors)) - return 1; + const color_type color = aoptions.color; if (numfunctions == 0) return 1; @@ -648,19 +685,22 @@ namespace graphs if (width == 0) width = w.ws_col * 2; - const int aheight = height / 4; - const int awidth = width / 2; - - if (aheight > w.ws_row) + if (aoptions.check) { - cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; - return 1; - } + const size_t aheight = height / 4; + const size_t awidth = width / 2; - if (awidth > w.ws_col) - { - cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; - return 1; + if (aheight > w.ws_row) + { + cerr << "The height of the graph (" << aheight << ") is greater then the height of the terminal (" << w.ws_row << ").\n"; + return 1; + } + + if (awidth > w.ws_col) + { + cerr << "The width of the graph (" << awidth << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; + return 1; + } } if (xmin >= xmax) @@ -677,26 +717,27 @@ namespace graphs const size_t rows = width; - const long double xscl = width / (xmax - xmin); - const long double yscl = height / (ymax - ymin); - const long double xaxis = width - (xmax * xscl); - const long double yaxis = ymax * yscl; + const long double xstep = (xmax - xmin) / width; + const long double ystep = (ymax - ymin) / height; + const long double xaxis = width - (xmax / xstep); + const long double yaxis = ymax / ystep; + const size_t xres = 2; vector> array(width, vector(height, 0)); - for (unsigned int j = 0; j < numfunctions; ++j) + for (size_t j = 0; j < numfunctions; ++j) { unsigned short acolor = numfunctions == 1 ? color + 1 : (j % (graphs::size(colors) - 2)) + 3; - for (long double i = 0; i < rows; i += 0.5) + for (size_t i = 0; i < rows * xres; ++i) { - T x = (i / xscl) + xmin; + T x = ((i / (long double)xres) * xstep) + xmin; T y = (functions[j])(x); if (x >= xmin and x < xmax and y >= ymin and y < ymax) { - const long long ax = (x * xscl) + xaxis; - const long long ay = (yaxis - (y * yscl)) - 1; + const size_t ax = (x / xstep) + xaxis; + const size_t ay = (yaxis - (y / ystep)) - 1; if (array[ax][ay]) { diff --git a/python/README.md b/python/README.md index c4e5f14..c7981c1 100644 --- a/python/README.md +++ b/python/README.md @@ -2,7 +2,7 @@ ### Usage -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. +Requires Python 3.6 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. @@ -75,9 +75,9 @@ def afunction(x): xmin = -10 xmax = 10 -xscl = 2 +xstep = 0.5 -tables.function(xmin, xmax, xscl, afunction, headerrow=True) +tables.function(xmin, xmax, xstep, afunction, headerrow=True) ``` ![](../images/function%20to%20table.png) @@ -89,11 +89,11 @@ import tables xmin = -10 xmax = 10 -xscl = 2 +xstep = 0.5 afunction = lambda x: x + 1 -tables.function(xmin, xmax, xscl, afunction, headerrow=True) +tables.function(xmin, xmax, xstep, afunction, headerrow=True) ``` Output same as example above. @@ -111,12 +111,12 @@ def function2(x): xmin = -10 xmax = 10 -xscl = 2 +xstep = 0.5 # Function parameter and return value can be any data type, as long as they are the same functions = [function1, function2] -tables.functions(xmin, xmax, xscl, functions, headerrow=True) +tables.functions(xmin, xmax, xstep, functions, headerrow=True) ``` ![](../images/multiple%20functions%20to%20table.png) @@ -128,12 +128,12 @@ import tables xmin = -10 xmax = 10 -xscl = 2 +xstep = 0.5 # 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) +tables.functions(xmin, xmax, xstep, functions, headerrow=True) ``` Output same as example above. @@ -174,7 +174,8 @@ Default value: `1` Option: `alignment`\ Values: -* `False` (left, default) +* `None` (default) +* `False` (left) * `True` (right) #### Title @@ -189,33 +190,40 @@ The title is word wrapped based on the current width of the terminal. Handles ne Option: `style`\ Values: -0. ASCII +0. `style_types.ASCII`: ASCII ![](../images/ASCII%20table.png) -1. Basic +1. `style_types.basic`: Basic ![](../images/basic%20table.png) -2. Light (default) +2. `style_types.light`: Light (default) ![](../images/light%20table.png) -3. Heavy +3. `style_types.heavy`: Heavy ![](../images/heavy%20table.png) -4. Double +4. `style_types.double`: Double ![](../images/double%20table.png) -5. Light Dashed +5. `style_types.light_dashed`: Light Dashed ![](../images/light%20dashed%20table.png) -6. Heavy Dashed +6. `style_types.heavy_dashed`: Heavy Dashed ![](../images/heavy%20dashed%20table.png) +#### Check size + +Option: `check`\ +Default value: `True` + +Check that the width of the table is not greater then the width of the terminal. + ## Graphs/Plots ### Usage -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. +Requires Python 3.6 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. @@ -369,25 +377,25 @@ The title is word wrapped based on the current width of the terminal. Handles ne Option: `style`\ Values: -0. ASCII +0. `style_types.ASCII`: ASCII ![](../images/ASCII%20graph.png) -1. Basic +1. `style_types.basic`: Basic ![](../images/basic%20graph.png) -2. Light (default) +2. `style_types.light`: Light (default) ![](../images/light%20graph.png) -3. Heavy +3. `style_types.heavy`: Heavy ![](../images/heavy%20graph.png) -4. Double +4. `style_types.double`: Double ![](../images/double%20graph.png) -5. Light Dashed +5. `style_types.light_dashed`: Light Dashed ![](../images/light%20dashed%20graph.png) -6. Heavy Dashed +6. `style_types.heavy_dashed`: Heavy Dashed ![](../images/heavy%20dashed%20graph.png) @@ -396,27 +404,25 @@ Values: Option: `color`\ Values: -0. System default -1. Black -2. Red (default) -3. Green -4. Yellow -5. Blue -6. Cyan -7. Light gray -8. Dark gray -9. Light red -10. Light green -11. Light yellow -12. Light blue -13. Light cyan -14. White +0. `color_types.default`: System default +1. `color_types.black`: Black +2. `color_types.red`: Red (default) +3. `color_types.green`: Green +4. `color_types.yellow`: Yellow +5. `color_types.blue`: Blue +6. `color_types.cyan`: Cyan +7. `color_types.dark_gray`: Light gray +8. `color_types.dark_gray`: Dark gray +9. `color_types.light_red`: Light red +10. `color_types.light_green`: Light green +11. `color_types.light_yellow`: Light yellow +12. `color_types.light_blue`: Light blue +13. `color_types.light_cyan`: Light cyan +14. `color_types.white`: White See [here](https://misc.flogisoft.com/bash/tip_colors_and_formatting#foreground_text) for examples of the colors. -Only used for plots and when graphing a single function. - -When graphing multiple functions, colors `2` - `14` are used inorder. Color `0` is used where the functions cross. +Only used when plotting a single array and when graphing a single function. When plotting multiple arrays or graphing multiple functions, colors 2 - 14 are used inorder. The system default color is used where the plots cross. ##### Plot @@ -425,3 +431,10 @@ When graphing multiple functions, colors `2` - `14` are used inorder. Color `0` ##### Graph ![](../images/graph%20colors.png) + +#### Check size + +Option: `check`\ +Default value: `True` + +Check that the width and height of the graph are not greater then the respective width and height of the terminal. diff --git a/python/graphs.py b/python/graphs.py index 08b83f9..9e74ebe 100644 --- a/python/graphs.py +++ b/python/graphs.py @@ -7,13 +7,26 @@ from __future__ import division, print_function, unicode_literals import sys import math import shutil +from fractions import Fraction import textwrap +from enum import Enum, IntEnum, auto from wcwidth import wcswidth from typing import List, Tuple, Optional, Sequence, Callable import locale locale.setlocale(locale.LC_ALL, '') + +class style_types(IntEnum): + ASCII = 0 + basic = auto() + light = auto() + heavy = auto() + double = auto() + light_dashed = auto() + heavy_dashed = auto() + + styles = [ ["-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # ASCII ["—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # Basic @@ -25,33 +38,71 @@ styles = [ ] # [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]] #No border -colors = ["\033[39m", "\033[30m", "\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m", - "\033[37m", "\033[90m", "\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m", "\033[96m", "\033[97m"] -dots = ["⠀", "⠁", "⠂", "⠃", "⠄", "⠅", "⠆", "⠇", "⠈", "⠉", "⠊", "⠋", "⠌", "⠍", "⠎", "⠏", "⠐", "⠑", "⠒", "⠓", "⠔", "⠕", "⠖", "⠗", "⠘", "⠙", "⠚", "⠛", "⠜", "⠝", "⠞", "⠟", "⠠", "⠡", "⠢", "⠣", "⠤", "⠥", "⠦", "⠧", "⠨", "⠩", "⠪", "⠫", "⠬", "⠭", "⠮", "⠯", "⠰", "⠱", "⠲", "⠳", "⠴", "⠵", "⠶", "⠷", "⠸", "⠹", "⠺", "⠻", "⠼", "⠽", "⠾", "⠿", "⡀", "⡁", "⡂", "⡃", "⡄", "⡅", "⡆", "⡇", "⡈", "⡉", "⡊", "⡋", "⡌", "⡍", "⡎", "⡏", "⡐", "⡑", "⡒", "⡓", "⡔", "⡕", "⡖", "⡗", "⡘", "⡙", "⡚", "⡛", "⡜", "⡝", "⡞", "⡟", "⡠", "⡡", "⡢", "⡣", "⡤", "⡥", "⡦", "⡧", "⡨", "⡩", "⡪", "⡫", "⡬", "⡭", "⡮", "⡯", "⡰", "⡱", "⡲", "⡳", "⡴", "⡵", "⡶", "⡷", "⡸", "⡹", "⡺", "⡻", "⡼", "⡽", "⡾", - "⡿", "⢀", "⢁", "⢂", "⢃", "⢄", "⢅", "⢆", "⢇", "⢈", "⢉", "⢊", "⢋", "⢌", "⢍", "⢎", "⢏", "⢐", "⢑", "⢒", "⢓", "⢔", "⢕", "⢖", "⢗", "⢘", "⢙", "⢚", "⢛", "⢜", "⢝", "⢞", "⢟", "⢠", "⢡", "⢢", "⢣", "⢤", "⢥", "⢦", "⢧", "⢨", "⢩", "⢪", "⢫", "⢬", "⢭", "⢮", "⢯", "⢰", "⢱", "⢲", "⢳", "⢴", "⢵", "⢶", "⢷", "⢸", "⢹", "⢺", "⢻", "⢼", "⢽", "⢾", "⢿", "⣀", "⣁", "⣂", "⣃", "⣄", "⣅", "⣆", "⣇", "⣈", "⣉", "⣊", "⣋", "⣌", "⣍", "⣎", "⣏", "⣐", "⣑", "⣒", "⣓", "⣔", "⣕", "⣖", "⣗", "⣘", "⣙", "⣚", "⣛", "⣜", "⣝", "⣞", "⣟", "⣠", "⣡", "⣢", "⣣", "⣤", "⣥", "⣦", "⣧", "⣨", "⣩", "⣪", "⣫", "⣬", "⣭", "⣮", "⣯", "⣰", "⣱", "⣲", "⣳", "⣴", "⣵", "⣶", "⣷", "⣸", "⣹", "⣺", "⣻", "⣼", "⣽", "⣾", "⣿"] +class color_types(IntEnum): + default = 0 + black = auto() + red = auto() + green = auto() + yellow = auto() + blue = auto() + cyan = auto() + light_gray = auto() + dark_gray = auto() + light_red = auto() + light_green = auto() + light_yellow = auto() + light_blue = auto() + light_cyan = auto() + white = auto() + + +colors = ["\033[39m", "\033[30m", "\033[31m", "\033[32m", "\033[33m", + "\033[34m", "\033[35m", "\033[36m", "\033[37m", "\033[90m", + "\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m", + "\033[96m", "\033[97m"] + +dots = [ + "⠀", "⠁", "⠂", "⠃", "⠄", "⠅", "⠆", "⠇", "⠈", "⠉", "⠊", "⠋", "⠌", "⠍", "⠎", + "⠏", "⠐", "⠑", "⠒", "⠓", "⠔", "⠕", "⠖", "⠗", "⠘", "⠙", "⠚", "⠛", "⠜", "⠝", + "⠞", "⠟", "⠠", "⠡", "⠢", "⠣", "⠤", "⠥", "⠦", "⠧", "⠨", "⠩", "⠪", "⠫", "⠬", + "⠭", "⠮", "⠯", "⠰", "⠱", "⠲", "⠳", "⠴", "⠵", "⠶", "⠷", "⠸", "⠹", "⠺", "⠻", + "⠼", "⠽", "⠾", "⠿", "⡀", "⡁", "⡂", "⡃", "⡄", "⡅", "⡆", "⡇", "⡈", "⡉", "⡊", + "⡋", "⡌", "⡍", "⡎", "⡏", "⡐", "⡑", "⡒", "⡓", "⡔", "⡕", "⡖", "⡗", "⡘", "⡙", + "⡚", "⡛", "⡜", "⡝", "⡞", "⡟", "⡠", "⡡", "⡢", "⡣", "⡤", "⡥", "⡦", "⡧", "⡨", + "⡩", "⡪", "⡫", "⡬", "⡭", "⡮", "⡯", "⡰", "⡱", "⡲", "⡳", "⡴", "⡵", "⡶", "⡷", + "⡸", "⡹", "⡺", "⡻", "⡼", "⡽", "⡾", "⡿", "⢀", "⢁", "⢂", "⢃", "⢄", "⢅", "⢆", + "⢇", "⢈", "⢉", "⢊", "⢋", "⢌", "⢍", "⢎", "⢏", "⢐", "⢑", "⢒", "⢓", "⢔", "⢕", + "⢖", "⢗", "⢘", "⢙", "⢚", "⢛", "⢜", "⢝", "⢞", "⢟", "⢠", "⢡", "⢢", "⢣", "⢤", + "⢥", "⢦", "⢧", "⢨", "⢩", "⢪", "⢫", "⢬", "⢭", "⢮", "⢯", "⢰", "⢱", "⢲", "⢳", + "⢴", "⢵", "⢶", "⢷", "⢸", "⢹", "⢺", "⢻", "⢼", "⢽", "⢾", "⢿", "⣀", "⣁", "⣂", + "⣃", "⣄", "⣅", "⣆", "⣇", "⣈", "⣉", "⣊", "⣋", "⣌", "⣍", "⣎", "⣏", "⣐", "⣑", + "⣒", "⣓", "⣔", "⣕", "⣖", "⣗", "⣘", "⣙", "⣚", "⣛", "⣜", "⣝", "⣞", "⣟", "⣠", + "⣡", "⣢", "⣣", "⣤", "⣥", "⣦", "⣧", "⣨", "⣩", "⣪", "⣫", "⣬", "⣭", "⣮", "⣯", + "⣰", "⣱", "⣲", "⣳", "⣴", "⣵", "⣶", "⣷", "⣸", "⣹", "⣺", "⣻", "⣼", "⣽", "⣾", + "⣿"] values = [[0x1, 0x2, 0x4, 0x40], [0x8, 0x10, 0x20, 0x80]] fractions = { - "¼": 1.0 / 4.0, - "½": 1.0 / 2.0, - "¾": 3.0 / 4.0, - "⅐": 1.0 / 7.0, - "⅑": 1.0 / 9.0, - "⅒": 1.0 / 10.0, - "⅓": 1.0 / 3.0, - "⅔": 2.0 / 3.0, - "⅕": 1.0 / 5.0, - "⅖": 2.0 / 5.0, - "⅗": 3.0 / 5.0, - "⅘": 4.0 / 5.0, - "⅙": 1.0 / 6.0, - "⅚": 5.0 / 6.0, - "⅛": 1.0 / 8.0, - "⅜": 3.0 / 8.0, - "⅝": 5.0 / 8.0, - "⅞": 7.0 / 8.0 + "¼": Fraction(1, 4), + "½": Fraction(1, 2), + "¾": Fraction(3, 4), + "⅐": Fraction(1, 7), + "⅑": Fraction(1, 9), + "⅒": Fraction(1, 10), + "⅓": Fraction(1, 3), + "⅔": Fraction(2, 3), + "⅕": Fraction(1, 5), + "⅖": Fraction(2, 5), + "⅗": Fraction(3, 5), + "⅘": Fraction(4, 5), + "⅙": Fraction(1, 6), + "⅚": Fraction(5, 6), + "⅛": Fraction(1, 8), + "⅜": Fraction(3, 8), + "⅝": Fraction(5, 8), + "⅞": Fraction(7, 8) } constants = { @@ -59,68 +110,75 @@ constants = { "e": math.e } +MAX = sys.float_info.radix ** sys.float_info.mant_dig - 1 -def strcol(str: str) -> int: + +def strcol(astr: str) -> int: """Returns the number of columns that the given string would take up if printed.""" - width = wcswidth(str) + width = wcswidth(astr) if width == -1: - print("\nError! wcswidth failed. Nonprintable wide character.", file=sys.stderr) + print( + "\nError! wcswidth failed. Nonprintable wide character.", + file=sys.stderr) sys.exit(1) return width - # return len(str) + # return len(astr) -def outputlabel(label: float) -> Tuple[int, str]: - """Outputs a label in a nice, human readable format.""" +def outputfraction(number: float) -> Tuple[int, str]: """Convert fractions and constants to Unicode characters""" output = False - fractionpart, intpart = math.modf(label) - fractionpart = abs(fractionpart) - strm = "" - for fraction in fractions: - if abs(fractionpart - fractions[fraction]) < sys.float_info.epsilon: - if intpart != 0: - strm += str(intpart) + n = abs(number) - strm += fraction + if n <= MAX: + # fractionpart, intpart = math.modf(number) + # fractionpart = abs(fractionpart) + intpart, fractionpart = divmod(Fraction(number).limit_denominator(), 1) - output = True - break - - if abs(label) >= sys.float_info.epsilon and not output: - for constant in constants: - if not output and label % constants[constant] == 0: - intpart = label / constants[constant] - - if intpart == -1: + for fraction, value in fractions.items(): + if abs(fractionpart - value) <= sys.float_info.epsilon * n: + if intpart == 0 and number < 0: strm += "-" - elif intpart != 1: - strm += str(intpart) + elif intpart != 0: + strm += "{0:n}".format(intpart) - strm += constant + strm += fraction output = True break + if n > sys.float_info.epsilon and not output: + for constant, value in constants.items(): + if not output and number % value <= sys.float_info.epsilon * n: + intpart = number / value + + if intpart == -1: + strm += "-" + elif intpart != 1: + strm += "{0:.{prec}n}".format(intpart, + prec=sys.float_info.dig) + + strm += constant + + output = True + break + if not output: - strm += "{0:n}".format(label) + strm += "{0:n}".format(number) length = strcol(strm) 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: style_types = style_types.light, title: Optional[str] = None, check: bool = True) -> int: """Output graph""" if not array: return 1 - if not (0 <= style < len(styles)): - return 1 - if height == 0: return 1 @@ -129,18 +187,19 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: w = shutil.get_terminal_size() - aheight = height // 4 - awidth = width // 2 + if check: + 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( - aheight, w.lines), file=sys.stderr) - return 1 + if aheight > w.lines: + print("The height of the graph ({0}) is greater then the height of the terminal ({1}).".format( + aheight, w.lines), file=sys.stderr) + return 1 - if awidth > w.columns: - print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format( - awidth, w.columns), file=sys.stderr) - return 1 + if awidth > w.columns: + print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format( + awidth, w.columns), file=sys.stderr) + return 1 if xmin >= xmax: print("xmin must be less than xmax.", file=sys.stderr) @@ -150,46 +209,51 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: print("ymin must be less than ymax.", file=sys.stderr) return 1 - xscl = width / (xmax - xmin) - yscl = height / (ymax - ymin) - xaxis = width - (xmax * xscl) - yaxis = ymax * yscl - divisor = 2 * 4 * ((width // 160) + 1 if (width / 160.0) > 1 else 1) + xstep = (xmax - xmin) / width + ystep = (ymax - ymin) / height + xaxis = 0 if xmin > 0 else width if xmax < 0 else width - (xmax / xstep) + yaxis = height if ymin > 0 else 0 if ymax < 0 else ymax / ystep + xdivisor = 2 * 4 * ((width // 160) + 2) + ydivisor = 2 * 4 * ((height // 160) + 2) if title: - print(textwrap.fill(title, width=w.columns)) + print(textwrap.fill(title, width=width // 2)) strm = "" i = 0 while i < height: - ayaxis = i <= yaxis and (i + 4) > yaxis - yaxislabel = i <= (yaxis + 4) and (i + 4) > (yaxis + 4) + ayaxis = i <= yaxis and ( + i + 4) > yaxis if yaxis <= (height - 4) else i < yaxis and (i + 4) >= yaxis + yaxislabel = i <= (yaxis + 4) and (i + 4) > (yaxis + 4) if yaxis <= ( + height - 4) else i < (yaxis - 4) and (i + 4) >= (yaxis - 4) ylabelstrm = "" ylabellength = 0 - if border and axislabel and axisunitslabel: + if border and axislabel and axisunitslabel and yaxis >= 0 and yaxis <= height: output = False label = 0.0 - adivisor = -divisor if i < yaxis else divisor + adivisor = -ydivisor if i < yaxis else ydivisor k = yaxis + adivisor - while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and i >= 4 and not output: + while (k >= i if i < yaxis else k < (i + 4)) and i >= 4 and not output: if (i <= k and (i + 4) > k): - label = ymax - (k / yscl) + label = ymax - ((height if k > height else k) * ystep) output = True k += adivisor if (output): - ylabellength, ylabelstrm = outputlabel(label) + ylabellength, ylabelstrm = outputfraction(label) ylabellength *= 2 j = 0 while j < width: - axaxis = j <= xaxis and (j + 2) > xaxis - xaxislabel = j <= (xaxis - 2) and (j + 2) > (xaxis - 2) + axaxis = j < xaxis and ( + j + 2) >= xaxis if xaxis >= 2 else j <= xaxis and (j + 2) > xaxis + xaxislabel = j < (xaxis - 2) and (j + 2) >= ( + xaxis - 2) if xaxis >= 2 else j <= (xaxis + 2) and (j + 2) > (xaxis + 2) output = False @@ -198,57 +262,61 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: strm += styles[style][6] output = True elif axaxis: - if axislabel and axisunitslabel: - adivisor = -divisor if i < yaxis else divisor + if i == 0: + strm += styles[style][4] + output = True + elif i >= (height - 4): + strm += styles[style][10] + output = True + elif axislabel and axisunitslabel: + adivisor = -ydivisor if i < yaxis else ydivisor k = yaxis + adivisor - while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and i >= 4 and not output: + while (k >= i if i < yaxis else k < (i + 4)) and i >= 4 and not output: if i <= k and (i + 4) > k: - strm += styles[style][7] + strm += styles[style][7 if xaxis >= 2 else 5] output = True k += adivisor if not output: - if i == 0: - strm += styles[style][4] - elif i >= (height - 4): - strm += styles[style][10] - else: - strm += styles[style][1] + strm += styles[style][1] output = True elif ayaxis: - if axislabel and axisunitslabel: - adivisor = -divisor if j < xaxis else divisor + if j == 0: + strm += styles[style][2] + output = True + elif j >= (width - 2): + strm += styles[style][4] + output = True + elif axislabel and axisunitslabel: + adivisor = -xdivisor if j < xaxis else xdivisor k = xaxis + adivisor - while ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and j < (width - 4) and not output: + while (k >= j if j < xaxis else k < (j + 2)) and j < (width - 4) and not output: if j <= k and (j + 2) > k: - strm += styles[style][3] + strm += styles[style][3 if yaxis <= + (height - 4) else 9] output = True k += adivisor if not output: - if j == 0: - strm += styles[style][2] - elif j >= (width - 2): - strm += styles[style][4] - else: - strm += styles[style][0] + strm += styles[style][0] output = True - elif yaxislabel and xaxislabel and axislabel and axisunitslabel: + elif yaxislabel and xaxislabel and axislabel and axisunitslabel and ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0: strm += "0" output = True - elif j >= (width - 2) and yaxislabel and axislabel: + elif (j >= (width - 2) if xaxis <= (width - 2) else j == 0) and yaxislabel and axislabel: strm += "x" output = True elif yaxislabel and axislabel and axisunitslabel: label = 0.0 - adivisor = -divisor if j < xaxis else divisor + adivisor = -xdivisor if j < xaxis else xdivisor if j < xaxis: j += 2 k = xaxis + adivisor - while ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and j < (width - 2) and not output: + while (k >= j if j < xaxis else k < (j + 2)) and j < (width - 2) and not output: if j <= k and (j + 2) > k: - label = (k / xscl) + xmin + label = ((width if k > width else k) + * xstep) + xmin output = True k += adivisor @@ -259,7 +327,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: if output: output = False - length, astrm = outputlabel(label) + length, astrm = outputfraction(label) length *= 2 if (j >= xaxis or (j + length) < (xaxis - 4)) and (j + length) < (width - 2): strm += astrm @@ -271,10 +339,10 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: output = True else: j += 2 - elif i == 0 and xaxislabel and axislabel: + elif (i == 0 if yaxis >= 4 else i >= (height - 4)) and xaxislabel and axislabel: strm += "y" output = True - elif (j <= (xaxis - ylabellength) and (j + 2) > (xaxis - ylabellength)) and axislabel and axisunitslabel: + elif ylabellength and (xaxislabel if xaxis < 2 else j < (xaxis - ylabellength) and (j + 2) >= (xaxis - ylabellength)) and (yaxis >= 4 or i < (height - 4)) and axislabel and axisunitslabel: strm += ylabelstrm output = True if ylabellength > 2: @@ -299,14 +367,15 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: if color: strm += colors[color] - strm += "\033[1m" + dots[dot] + "\033[22m" + strm += dots[dot] if color: strm += colors[0] j += 2 - strm += "\n" + if i < (height - 4): + strm += "\n" i += 4 print(strm) @@ -314,7 +383,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: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None, check: bool = True) -> int: """Convert one or more arrays to graph and output""" if not aarrays: return 1 @@ -323,9 +392,6 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: print("Error: The arrays must have two columns.") return 1 - if color < 0 or color >= len(colors): - return 1 - w = shutil.get_terminal_size() if height == 0: @@ -334,18 +400,19 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: if width == 0: width = w.columns * 2 - aheight = height // 4 - awidth = width // 2 + if check: + 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( - aheight, w.lines), file=sys.stderr) - return 1 + if aheight > w.lines: + print("The height of the graph ({0}) is greater then the height of the terminal ({1}).".format( + aheight, w.lines), file=sys.stderr) + return 1 - if awidth > w.columns: - print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format( - awidth, w.columns), file=sys.stderr) - return 1 + if awidth > w.columns: + print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format( + awidth, w.columns), file=sys.stderr) + return 1 if xmin == 0 and xmax == 0: xmin = min(i[0] for aarray in aarrays for i in aarray) @@ -363,10 +430,10 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: print("ymin must be less than ymax.", file=sys.stderr) return 1 - xscl = width / (xmax - xmin) - yscl = height / (ymax - ymin) - xaxis = width - (xmax * xscl) - yaxis = ymax * yscl + xstep = (xmax - xmin) / width + ystep = (ymax - ymin) / height + xaxis = width - (xmax / xstep) + yaxis = ymax / ystep aaarray = [[0 for j in range(height)] for i in range(width)] @@ -376,8 +443,8 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: for i in aarray: if i[0] >= xmin and i[0] < xmax and i[1] >= ymin and i[1] < ymax: - x = int((i[0] * xscl) + xaxis) - y = int((yaxis - (i[1] * yscl)) - 1) + x = int((i[0] / xstep) + xaxis) + y = int((yaxis - (i[1] / ystep)) - 1) if aaarray[x][y]: if aaarray[x][y] != acolor: @@ -385,19 +452,18 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: else: aaarray[x][y] = acolor - return graph(height, width, xmin, xmax, ymin, ymax, aaarray, border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, title=title) + 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: style_types = style_types.light, color: color_types = color_types.red, 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) + 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: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None, check: bool = True) -> int: """Convert one or more functions to graph and output""" - if color < 0 or color >= len(colors): - return 1 - if not afunctions: return 1 @@ -409,18 +475,19 @@ 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 + if check: + 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( - aheight, w.lines), file=sys.stderr) - return 1 + if aheight > w.lines: + print("The height of the graph ({0}) is greater then the height of the terminal ({1}).".format( + aheight, w.lines), file=sys.stderr) + return 1 - if awidth > w.columns: - print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format( - awidth, w.columns), file=sys.stderr) - return 1 + if awidth > w.columns: + print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format( + awidth, w.columns), file=sys.stderr) + return 1 if xmin >= xmax: print("xmin must be less than xmax.", file=sys.stderr) @@ -432,10 +499,11 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym rows = width - xscl = width / (xmax - xmin) - yscl = height / (ymax - ymin) - xaxis = width - (xmax * xscl) - yaxis = ymax * yscl + xstep = (xmax - xmin) / width + ystep = (ymax - ymin) / height + xaxis = width - (xmax / xstep) + yaxis = ymax / ystep + xres = 2 array = [[0 for j in range(height)] for i in range(width)] @@ -443,13 +511,13 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym acolor = color + 1 if len(afunctions) == 1 else (j % (len(colors) - 2)) + 3 - for i in (x / 2 for x in range(rows * 2)): - x = (i / xscl) + xmin + for i in (x / xres for x in range(rows * xres)): + x = (i * xstep) + xmin y = function(x) if x >= xmin and x < xmax and y >= ymin and y < ymax: - ax = int((x * xscl) + xaxis) - ay = int((yaxis - (y * yscl)) - 1) + ax = int((x / xstep) + xaxis) + ay = int((yaxis - (y / ystep)) - 1) if array[ax][ay]: if array[ax][ay] != acolor: @@ -457,9 +525,11 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym else: array[ax][ay] = acolor - return graph(height, width, xmin, xmax, ymin, ymax, array, border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, title=title) + 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: style_types = style_types.light, color: color_types = color_types.red, 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) + 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 6e18495..f984030 100644 --- a/python/tables.py +++ b/python/tables.py @@ -8,12 +8,24 @@ import sys import shutil import re import textwrap +from enum import IntEnum, auto from wcwidth import wcswidth from typing import List, Optional, Any, Sequence, Callable import locale locale.setlocale(locale.LC_ALL, '') + +class style_types(IntEnum): + ASCII = 0 + basic = auto() + light = auto() + heavy = auto() + double = auto() + light_dashed = auto() + heavy_dashed = auto() + + styles = [ ["-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # ASCII ["—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # Basic @@ -28,25 +40,24 @@ styles = [ ansi = re.compile(r'\x1B\[(?:[0-9]+(?:;[0-9]+)*)?m') -def strcol(str: str) -> int: +def strcol(astr: str) -> int: """Returns the number of columns that the given string would take up if printed.""" - str = ansi.sub('', str) - width = wcswidth(str) + astr = ansi.sub('', astr) + width = wcswidth(astr) if width == -1: - print("\nError! wcswidth failed. Nonprintable wide character.", file=sys.stderr) + print( + "\nError! wcswidth failed. Nonprintable wide character.", + file=sys.stderr) sys.exit(1) return width - # return len(str) + # return len(astr) -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: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light, check: bool = True) -> int: """Output char array as table""" if not array: return 1 - if not (0 <= style < len(styles)): - return 1 - rows = len(array) columns = len(array[0]) @@ -61,13 +72,14 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool = else: width += (2 * padding) * columns - if width > w.columns: - print("The width of the table ({0}) is greater then the width of the terminal ({1}).".format( - width, w.columns), file=sys.stderr) - return 1 + if check: + if width > w.columns: + print("The width of the table ({0}) is greater then the width of the terminal ({1}).".format( + width, w.columns), file=sys.stderr) + return 1 if title: - print(textwrap.fill(title, width=w.columns)) + print(textwrap.fill(title, width=width)) strm = "" @@ -77,18 +89,22 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool = for j in range(columns): strm += styles[style][0] * ((2 * padding) + columnwidth[j]) - if j == (columns - 1): - strm += styles[style][4] + "\n" - elif cellborder or headerrow or (j == 0 and headercolumn): - strm += styles[style][3] - else: - strm += styles[style][0] + if j < (columns - 1): + if cellborder or headerrow or (j == 0 and headercolumn): + strm += styles[style][3] + else: + strm += styles[style][0] + + strm += styles[style][4] + "\n" for i in range(rows): + if tableborder: + strm += styles[style][1] + for j in range(columns): - if (j == 0 and tableborder) or (j > 0 and cellborder) or (i == 0 and j > 0 and headerrow) or (j == 1 and headercolumn): + if (j > 0 and cellborder) or (i == 0 and j > 0 and headerrow) or (j == 1 and headercolumn): strm += styles[style][1] - elif tableborder or (i > 0 and j > 0 and headerrow) or (j > 1 and headercolumn): + elif j > 0 and (tableborder or (i > 0 and headerrow) or headercolumn): strm += " " awidth = columnwidth[j] - (strcol(array[i][j]) - len(array[i][j])) @@ -102,7 +118,9 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool = else: strm += " " * padding - if alignment: + if alignment is None: + strm += "{0:{width}}".format(array[i][j], width=awidth) + elif alignment: strm += array[i][j].rjust(awidth) else: strm += array[i][j].ljust(awidth) @@ -127,17 +145,7 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool = elif i < (rows - 1) and headercolumn: strm += " " * ((2 * padding) + columnwidth[j]) - if j == (columns - 1): - if tableborder: - if i == (rows - 1): - strm += styles[style][10] - elif cellborder or (i == 0 and headerrow): - strm += styles[style][7] - elif headercolumn: - strm += styles[style][1] - - strm += "\n" - else: + if j < (columns - 1): if i == (rows - 1) and tableborder: if cellborder or (j == 0 and headercolumn): strm += styles[style][9] @@ -153,12 +161,23 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool = else: strm += " " + if tableborder: + if i == (rows - 1): + strm += styles[style][10] + elif cellborder or (i == 0 and headerrow): + strm += styles[style][7] + elif headercolumn: + strm += styles[style][1] + + if i < (rows - 1): + strm += "\n" + print(strm) return 0 -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: +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: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light) -> int: """Convert array to char array and output as table""" if not aarray: return 1 @@ -167,7 +186,9 @@ def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]] = columns = len(aarray[0]) if not all(len(x) == columns for x in aarray): - print("Error: The rows of the array must have the same number of columns.", file=sys.stderr) + print( + "Error: The rows of the array must have the same number of columns.", + file=sys.stderr) return 1 if aheaderrow: @@ -177,50 +198,36 @@ def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]] = columns += 1 if aheaderrow and len(aheaderrow) != columns: - print("Error: The header row must have the same number of columns as the array.", file=sys.stderr) + print( + "Error: The header row must have the same number of columns as the array.", + file=sys.stderr) return 1 if aheadercolumn and len(aheadercolumn) != (rows - 1 if aheaderrow else rows): - print("Error: The header column must have the same number of rows as the array.", file=sys.stderr) + print( + "Error: The header column must have the same number of rows as the array.", + file=sys.stderr) return 1 aaarray = [["" for j in range(columns)] for i in range(rows)] - i = 0 - if aheaderrow: - for j in range(columns): - aaarray[i][j] = aheaderrow[j] + aaarray[0] = aheaderrow[:columns] - i += 1 + for i in range(1 if aheaderrow else 0, rows): + ii = i - 1 if aheaderrow else i - j = 0 - - ii = 0 - for i in range(i, rows): if aheadercolumn: - aii = i + aaarray[i][0] = aheadercolumn[ii] - if aheaderrow: - aii -= 1 + j = 1 if aheadercolumn else 0 + aaarray[i][j:] = map(str, aarray[ii][:columns - j]) - aaarray[i][j] = aheadercolumn[aii] - - j += 1 - - jj = 0 - for j in range(j, columns): - aaarray[i][j] = str(aarray[ii][jj]) - - jj += 1 - - j = 0 - ii += 1 - - return table(aaarray, headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style) + 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, xstep: float, afunctions: Sequence[Callable[[float], float]], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True, cellborder: bool = False, padding: int = 1, alignment: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light) -> int: """Convert one or more functions to array and output as table""" if not afunctions: return 1 @@ -229,11 +236,11 @@ def functions(xmin: float, xmax: float, xscl: float, afunctions: Sequence[Callab print("xmin must be less than xmax.", file=sys.stderr) return 1 - if xscl <= 0: - print("xscl must be greater than zero.", file=sys.stderr) + if xstep <= 0: + print("xstep must be greater than zero.", file=sys.stderr) return 1 - rows = int(((xmax - xmin) * xscl)) + 1 + rows = int(((xmax - xmin) / xstep)) + 1 columns = len(afunctions) + 1 aaheaderrow = ["x", "y"] @@ -241,23 +248,24 @@ def functions(xmin: float, xmax: float, xscl: float, afunctions: Sequence[Callab aheaderrow = [""] * columns - for j in range(columns): - if j < (length - 1) or len(afunctions) == 1: - aheaderrow[j] = aaheaderrow[j] - else: - aheaderrow[j] = aaheaderrow[length - 1] + str(j - length + 2) + if len(afunctions) == 1: + aheaderrow = aaheaderrow + else: + aheaderrow = aaheaderrow[:-1] + [aaheaderrow[-1] + + str(j - length + 2) for j in range(1, columns)] aarray = [[0 for j in range(columns)] for i in range(rows)] for i in range(rows): - aarray[i][0] = (i / xscl) + xmin + aarray[i][0] = (i * xstep) + xmin - for j, function in enumerate(afunctions): - aarray[i][j + 1] = function(aarray[i][0]) + aarray[i][1:] = [function(aarray[i][0]) for function in afunctions] - return array(aarray, aheaderrow, None, headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style) + 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, xstep: float, afunction: Callable[[float], float], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True, cellborder: bool = False, padding: int = 1, alignment: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light) -> 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) + return functions(xmin, xmax, xstep, [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 fcaed36..399bc82 100644 --- a/python/test.py +++ b/python/test.py @@ -21,17 +21,17 @@ columns = 5 xmin = -10 xmax = 10 -xscl = 2 +xstep = 0.5 print("\nOutput array as table\n") array = [[random.randint(0, sys.maxsize) for j in range(columns)] for i in range(rows)] -for k in range(len(tables.styles)): - tables.array(array, None, None, style=k) +for style in tables.style_types: + tables.array(array, None, None, style=style) array = [[random.random() for j in range(columns)] for i in range(rows)] -for k in range(len(tables.styles)): - tables.array(array, None, None, style=k) +for style in tables.style_types: + tables.array(array, None, None, style=style) print("\nOutput char array as table\n") array = [["Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"], @@ -39,8 +39,9 @@ array = [["Header row/column 1", "Header row 2", "Header row 3", "Header row 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"]] -for k in range(len(tables.styles)): - tables.array(array, None, None, headerrow=True, headercolumn=True, style=k) +for style in tables.style_types: + tables.array(array, None, None, headerrow=True, + headercolumn=True, style=style) print("\nOutput array as table with separate header row and column\n") array = [["Data {0:n}".format(i + j) for j in range(4)] @@ -50,44 +51,52 @@ headerrow = ["Header row/column 1", "Header row 2", headercolumn = ["Header column 2", "Header column 3", "Header column 4", "Header column 5"] -for k in range(len(tables.styles)): - tables.array(array, headerrow, headercolumn, headerrow=True, headercolumn=True, cellborder=True, style=k) - tables.array(array, headerrow, headercolumn, headerrow=True, headercolumn=True, style=k) - tables.array(array, headerrow[:-1], None, headerrow=True, style=k) - tables.array(array, None, [headerrow[0]] + headercolumn[:-1], headercolumn=True, style=k) - tables.array(array, None, None, cellborder=True, style=k) - tables.array(array, None, None, tableborder=False, style=k) - tables.array(array, headerrow, headercolumn, tableborder=False, headerrow=True, headercolumn=True, style=k) - tables.array(array, headerrow[:-1], None, tableborder=False, headerrow=True, style=k) - tables.array(array, None, [headerrow[0]] + headercolumn[:-1], tableborder=False, headercolumn=True, style=k) - tables.array(array, None, None, tableborder=False, cellborder=True, style=k) +for style in tables.style_types: + tables.array(array, headerrow, headercolumn, headerrow=True, + headercolumn=True, cellborder=True, style=style) + tables.array(array, headerrow, headercolumn, + headerrow=True, headercolumn=True, style=style) + tables.array(array, headerrow[:-1], None, headerrow=True, style=style) + tables.array(array, None, [headerrow[0]] + + headercolumn[:-1], headercolumn=True, style=style) + tables.array(array, None, None, cellborder=True, style=style) + tables.array(array, None, None, tableborder=False, style=style) + tables.array(array, headerrow, headercolumn, tableborder=False, + headerrow=True, headercolumn=True, style=style) + tables.array(array, headerrow[:-1], None, + tableborder=False, headerrow=True, style=style) + tables.array(array, None, [headerrow[0]] + headercolumn[:-1], + tableborder=False, headercolumn=True, style=style) + tables.array(array, None, None, tableborder=False, + cellborder=True, style=style) array = [[bool(random.getrandbits(1)) for j in range(columns)] for i in range(rows)] -for k in range(len(tables.styles)): - tables.array(array, None, None, style=k) +for style in tables.style_types: + tables.array(array, None, None, style=style) print("\nOutput sorted array as table\n") array = ([random.randint(0, sys.maxsize) for j in range(columns)] for i in range(rows)) sortdimension = 0 array = sorted(array, key=lambda x: x[sortdimension]) -for k in range(len(tables.styles)): - tables.array(array, None, None, style=k) +for style in tables.style_types: + tables.array(array, None, None, style=style) 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) +for style in tables.style_types: + tables.function(xmin, xmax, xstep, afunction, headerrow=True, style=style) +for style in tables.style_types: + tables.function(xmin, xmax, xstep, lambda x: x + + 1, headerrow=True, style=style) 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) +for style in tables.style_types: + tables.functions(xmin, xmax, xstep, [ + function1, function2], headerrow=True, style=style) +for style in tables.style_types: + tables.functions(xmin, xmax, xstep, [ + lambda x: 2 * x, lambda x: x ** 2], headerrow=True, style=style) height = 160 width = 160 @@ -99,24 +108,25 @@ ymax = 20 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) +for style in graphs.style_types: + graphs.array(height, width, xmin, xmax, ymin, ymax, array, style=style) 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)): +for style in graphs.style_types: graphs.function(height, width, xmin, xmax, ymin, - ymax, lambda x: x + 1, style=k) + ymax, afunction, style=style) +for style in graphs.style_types: + graphs.function(height, width, xmin, xmax, ymin, + ymax, lambda x: x + 1, style=style) 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)): +for style in graphs.style_types: + graphs.functions(height, width, xmin, xmax, ymin, ymax, + [function1, function2], style=style) +for style in graphs.style_types: graphs.functions(height, width, xmin, xmax, ymin, ymax, [ - lambda x: 2 * x, lambda x: x ** 2], style=k) + lambda x: 2 * x, lambda x: x ** 2], style=style) -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) +for style in graphs.style_types: + graphs.functions(height, width, -(2 * math.pi), 2 * math.pi, -4, 4, + [math.sin, math.cos, math.tan], axisunitslabel=False, style=style) diff --git a/tables.cpp b/tables.cpp index 104b9d9..974addc 100644 --- a/tables.cpp +++ b/tables.cpp @@ -77,7 +77,7 @@ int main() const long double xmin = -10; const long double xmax = 10; - const long double xscl = 2; // 80 / (xmax - xmin); + const long double xstep = 0.5; // (xmax - xmin) / 80; string *headerrow = nullptr; string *headercolumn = nullptr; @@ -96,14 +96,14 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != nullptr) + if (array) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -120,9 +120,9 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -136,9 +136,9 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(array, headerrow, headercolumn, aoptions); } @@ -155,14 +155,14 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != nullptr) + if (array) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -179,9 +179,9 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -195,9 +195,9 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(array, headerrow, headercolumn, aoptions); } @@ -217,9 +217,9 @@ int main() aoptions.headercolumn = true; // tables::options aoptions{.headerrow = true, .headercolumn = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(array, headerrow, headercolumn, aoptions); } @@ -236,9 +236,9 @@ int main() aoptions.headercolumn = true; // tables::options aoptions{.headerrow = true, .headercolumn = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -263,9 +263,9 @@ int main() aoptions.headercolumn = true; // tables::options aoptions{.headerrow = true, .headercolumn = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(array, headerrow, headercolumn, aoptions); } @@ -286,15 +286,15 @@ int main() vector aheadercolumn(headerrow, headerrow + 1); aheadercolumn.insert(aheadercolumn.end(), headercolumn, headercolumn + columns - 1); - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { { tables::options aoptions; aoptions.headerrow = true; aoptions.headercolumn = true; aoptions.cellborder = true; - aoptions.style = k; - // tables::options aoptions{.headerrow = true, .headercolumn = true, .cellborder = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.headerrow = true, .headercolumn = true, .cellborder = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -302,8 +302,8 @@ int main() tables::options aoptions; aoptions.headerrow = true; aoptions.headercolumn = true; - aoptions.style = k; - // tables::options aoptions{.headerrow = true, .headercolumn = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.headerrow = true, .headercolumn = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -313,8 +313,8 @@ int main() tables::options aoptions; aoptions.headerrow = true; - aoptions.style = k; - // tables::options aoptions{.headerrow = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.headerrow = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -324,8 +324,8 @@ int main() tables::options aoptions; aoptions.headercolumn = true; - aoptions.style = k; - // tables::options aoptions{.headercolumn = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.headercolumn = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -335,8 +335,8 @@ int main() tables::options aoptions; aoptions.cellborder = true; - aoptions.style = k; - // tables::options aoptions{.cellborder = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.cellborder = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -346,8 +346,8 @@ int main() tables::options aoptions; aoptions.tableborder = false; - aoptions.style = k; - // tables::options aoptions{.tableborder = false, .style = k}; + aoptions.style = style; + // tables::options aoptions{.tableborder = false, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -356,8 +356,8 @@ int main() aoptions.tableborder = false; aoptions.headerrow = true; aoptions.headercolumn = true; - aoptions.style = k; - // tables::options aoptions{.tableborder = false, .headerrow = true, .headercolumn = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.tableborder = false, .headerrow = true, .headercolumn = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -368,8 +368,8 @@ int main() tables::options aoptions; aoptions.tableborder = false; aoptions.headerrow = true; - aoptions.style = k; - // tables::options aoptions{.tableborder = false, .headerrow = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.tableborder = false, .headerrow = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -380,8 +380,8 @@ int main() tables::options aoptions; aoptions.tableborder = false; aoptions.headercolumn = true; - aoptions.style = k; - // tables::options aoptions{.tableborder = false, .headercolumn = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.tableborder = false, .headercolumn = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -392,8 +392,8 @@ int main() tables::options aoptions; aoptions.tableborder = false; aoptions.cellborder = true; - aoptions.style = k; - // tables::options aoptions{.tableborder = false, .cellborder = true, .style = k}; + aoptions.style = style; + // tables::options aoptions{.tableborder = false, .cellborder = true, .style = style}; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -413,14 +413,14 @@ int main() aoptions.boolalpha = true; // tables::options aoptions{.boolalpha = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != nullptr) + if (array) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -448,14 +448,14 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(rows, columns, array, nullptr, nullptr, aoptions); } - if (array != nullptr) + if (array) { for (unsigned int i = 0; i < rows; ++i) delete[] array[i]; @@ -477,9 +477,9 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(aarray, headerrow, headercolumn, aoptions); } @@ -498,9 +498,9 @@ int main() tables::options aoptions; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; tables::array(array, headerrow, headercolumn, aoptions); } @@ -512,11 +512,11 @@ int main() aoptions.headerrow = true; // tables::options aoptions{.headerrow = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; - tables::function(xmin, xmax, xscl, afunction, aoptions); + tables::function(xmin, xmax, xstep, afunction, aoptions); } } { @@ -527,11 +527,11 @@ int main() aoptions.headerrow = true; // tables::options aoptions{.headerrow = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; - tables::function(xmin, xmax, xscl, afunction, aoptions); + tables::function(xmin, xmax, xstep, afunction, aoptions); } } // Output multiple functions as table @@ -543,11 +543,11 @@ int main() aoptions.headerrow = true; // tables::options aoptions{.headerrow = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; - tables::functions(xmin, xmax, xscl, 2, functions, aoptions); + tables::functions(xmin, xmax, xstep, tables::size(functions), functions, aoptions); } } { @@ -560,11 +560,11 @@ int main() aoptions.headerrow = true; // tables::options aoptions{.headerrow = true}; - for (unsigned int k = 0; k < tables::size(tables::styles); ++k) + for (const tables::style_type style : tables::style_types) { - aoptions.style = k; + aoptions.style = style; - tables::functions(xmin, xmax, xscl, 2, functions, aoptions); + tables::functions(xmin, xmax, xstep, tables::size(functions), functions, aoptions); } } diff --git a/tables.hpp b/tables.hpp index f16dade..6c7c0f6 100644 --- a/tables.hpp +++ b/tables.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,19 @@ namespace tables { using namespace std; + enum style_type + { + style_ASCII, + style_basic, + style_light, + style_heavy, + style_double, + style_light_dashed, + style_heavy_dashed + }; + + enum style_type const style_types[] = {style_ASCII, style_basic, style_light, style_heavy, style_double, style_light_dashed, style_heavy_dashed}; + const char *const styles[][11] = { {"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // ASCII {"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, // Basic @@ -28,7 +42,7 @@ namespace tables }; // {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}};//No border - regex ansi(R"(\x1B\[(?:[0-9]+(?:;[0-9]+)*)?m)"); + const regex ansi(R"(\x1B\[(?:[0-9]+(?:;[0-9]+)*)?m)"); struct options { @@ -40,13 +54,14 @@ namespace tables ios_base &(*alignment)(ios_base &) = left; bool boolalpha = false; const char *title = nullptr; - unsigned int style = 2; + style_type style = style_light; + bool check = true; }; const options defaultoptions; template - auto size(const T &array) + constexpr size_t size(const T &array) { return distance(begin(array), end(array)); } @@ -74,11 +89,11 @@ namespace tables } ++length; - wchar_t *wcstring = new wchar_t[length]; + auto *wcstring = new wchar_t[length]; if (mbstowcs(wcstring, str, length) == static_cast(-1)) { - if (wcstring != nullptr) + if (wcstring) delete[] wcstring; cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n"; @@ -92,7 +107,7 @@ namespace tables exit(1); } - if (wcstring != nullptr) + if (wcstring) delete[] wcstring; return width; @@ -103,8 +118,7 @@ namespace tables // Adapted from: https://stackoverflow.com/a/42016346 and https://stackoverflow.com/a/13094734 string wrap(const char *const str, const size_t line_length) { - char words[strlen(str) + 1]; - strcpy(words, str); + string words = str; string wrapped; size_t index = 0; @@ -127,7 +141,7 @@ namespace tables } char temp[templinelen + 1]; - strncpy(temp, words + (index - linelen), templinelen); + strncpy(temp, words.data() + (index - linelen), templinelen); temp[templinelen] = '\0'; size_t width = strcol(temp); @@ -163,76 +177,83 @@ namespace tables const bool cellborder = aoptions.cellborder; const unsigned int padding = aoptions.padding; const char *const title = aoptions.title; - const unsigned int style = aoptions.style; - - if (style >= tables::size(styles)) - return 1; + const style_type style = aoptions.style; const size_t rows = array.size(); const size_t columns = array[0].size(); int columnwidth[columns]; - for (unsigned int j = 0; j < columns; ++j) + for (size_t j = 0; j < columns; ++j) columnwidth[j] = 0; - int width = 0; + setlocale(LC_ALL, ""); - setlocale(LC_CTYPE, ""); - - for (unsigned int j = 0; j < columns; ++j) + for (size_t j = 0; j < columns; ++j) { - for (unsigned int i = 0; i < rows; ++i) + for (size_t i = 0; i < rows; ++i) { - int cellwidth = strcol(array[i][j].c_str()); + const int cellwidth = strcol(array[i][j].c_str()); if (cellwidth > columnwidth[j]) columnwidth[j] = cellwidth; } - - width += columnwidth[j]; } struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + size_t width = accumulate(columnwidth, columnwidth + columns, 0ul); + if (tableborder or cellborder or headerrow or headercolumn) width += (((2 * padding) + 1) * columns) + (tableborder ? 1 : -1); else width += (2 * padding) * columns; - if (width > w.ws_col) + if (aoptions.check) { - cerr << "The width of the table (" << width << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; - return 1; + if (width > w.ws_col) + { + cerr << "The width of the table (" << width << ") is greater then the width of the terminal (" << w.ws_col << ").\n"; + return 1; + } } - if (title != nullptr and title[0] != '\0') - cout << wrap(title, w.ws_col) << "\n"; + if (title and title[0] != '\0') + cout << wrap(title, width) << "\n"; + + if (aoptions.alignment) + cout << aoptions.alignment; if (tableborder) { cout << styles[style][2]; - for (unsigned int j = 0; j < columns; ++j) + for (size_t j = 0; j < columns; ++j) { - for (unsigned int k = 0; k < (2 * padding) + columnwidth[j]; ++k) + for (size_t k = 0; k < (2 * padding) + columnwidth[j]; ++k) cout << styles[style][0]; - if (j == (columns - 1)) - cout << styles[style][4] << "\n"; - else if (cellborder or headerrow or (j == 0 and headercolumn)) - cout << styles[style][3]; - else - cout << styles[style][0]; + if (j < (columns - 1)) + { + if (cellborder or headerrow or (j == 0 and headercolumn)) + cout << styles[style][3]; + else + cout << styles[style][0]; + } } + + cout << styles[style][4] << "\n"; } - for (unsigned int i = 0; i < rows; ++i) + for (size_t i = 0; i < rows; ++i) { - for (unsigned int j = 0; j < columns; ++j) + if (tableborder) + cout << styles[style][1]; + + for (size_t j = 0; j < columns; ++j) { - if ((j == 0 and tableborder) or (j > 0 and cellborder) or (i == 0 and j > 0 and headerrow) or (j == 1 and headercolumn)) + if ((j and cellborder) or (i == 0 and j and headerrow) or (j == 1 and headercolumn)) cout << styles[style][1]; - else if (tableborder or (i > 0 and j > 0 and headerrow) or (j > 1 and headercolumn)) + else if (j and (tableborder or (i and headerrow) or headercolumn)) cout << " "; const int difference = columnwidth[j] - strcol(array[i][j].c_str()); @@ -251,7 +272,7 @@ namespace tables { cout << string(padding, ' '); - cout << aoptions.alignment << setw(difference + array[i][j].length()) << array[i][j]; + cout << setw(difference + array[i][j].length()) << array[i][j]; cout << string(padding, ' '); } @@ -272,29 +293,15 @@ namespace tables if ((i == (rows - 1) and tableborder) or (i < (rows - 1) and cellborder) or (i == 0 and headerrow) or (i < (rows - 1) and headercolumn)) { - for (unsigned int j = 0; j < columns; ++j) + for (size_t j = 0; j < columns; ++j) { if ((i == (rows - 1) and tableborder) or (i < (rows - 1) and cellborder) or (i == 0 and headerrow) or (i < (rows - 1) and j == 0 and headercolumn)) - for (unsigned int k = 0; k < (2 * padding) + columnwidth[j]; ++k) + for (size_t k = 0; k < (2 * padding) + columnwidth[j]; ++k) cout << styles[style][0]; else if (i < (rows - 1) and headercolumn) cout << string((2 * padding) + columnwidth[j], ' '); - if (j == (columns - 1)) - { - if (tableborder) - { - if (i == (rows - 1)) - cout << styles[style][10]; - else if (cellborder or (i == 0 and headerrow)) - cout << styles[style][7]; - else if (headercolumn) - cout << styles[style][1]; - } - - cout << "\n"; - } - else + if (j < (columns - 1)) { if (i == (rows - 1) and tableborder) { @@ -316,6 +323,19 @@ namespace tables } } } + + if (tableborder) + { + if (i == (rows - 1)) + cout << styles[style][10]; + else if (cellborder or (i == 0 and headerrow)) + cout << styles[style][7]; + else if (headercolumn) + cout << styles[style][1]; + } + + if (i < (rows - 1)) + cout << "\n"; } } @@ -331,30 +351,30 @@ namespace tables if (!tables::size(aarray)) return 1; - unsigned int i = 0; - unsigned int j = 0; + size_t i = 0; + size_t j = 0; size_t rows = tables::size(aarray); size_t columns = tables::size(aarray[0]); - if (!all_of(begin(aarray), end(aarray), [columns](auto &x) + if (!all_of(begin(aarray), end(aarray), [columns](const auto &x) { return tables::size(x) == columns; })) { - cerr << "Error: The rows of the array must have the same number of columns."; + cerr << "Error: The rows of the array must have the same number of columns.\n"; return 1; } - if (headerrow != nullptr) + if (headerrow) ++rows; - if (headercolumn != nullptr) + if (headercolumn) ++columns; vector> aaarray(rows, vector(columns)); - if (headerrow != nullptr) + if (headerrow) { - for (unsigned int j = 0; j < columns; ++j) + for (size_t j = 0; j < columns; ++j) { aaarray[i][j] = headerrow[j]; } @@ -362,13 +382,13 @@ namespace tables ++i; } - for (unsigned int ii = 0; i < rows; ++i) + for (size_t ii = 0; i < rows; ++i) { - if (headercolumn != nullptr) + if (headercolumn) { - unsigned int ii = i; + size_t ii = i; - if (headerrow != nullptr) + if (headerrow) --ii; aaarray[i][j] = headercolumn[ii]; @@ -376,7 +396,7 @@ namespace tables ++j; } - for (unsigned int jj = 0; j < columns; ++j) + for (size_t jj = 0; j < columns; ++j) { ostringstream strm; @@ -400,7 +420,7 @@ namespace tables int array(const size_t rows, const size_t columns, T **aarray, const char *const headerrow[] = nullptr, const char *const headercolumn[] = nullptr, const options &aoptions = defaultoptions) { vector> aaarray(rows, vector(columns)); - for (unsigned int i = 0; i < rows; ++i) + for (size_t i = 0; i < rows; ++i) copy(aarray[i], aarray[i] + columns, aaarray[i].begin()); string *aheaderrow = nullptr; @@ -434,7 +454,7 @@ namespace tables // Convert one or more functions to array and output as table template - int functions(const long double xmin, const long double xmax, const long double xscl, const size_t numfunctions, function functions[], const options &aoptions = defaultoptions) + int functions(const long double xmin, const long double xmax, const long double xstep, const size_t numfunctions, function functions[], const options &aoptions = defaultoptions) { if (numfunctions == 0) return 1; @@ -445,13 +465,13 @@ namespace tables return 1; } - if (xscl <= 0) + if (xstep <= 0) { - cerr << "xscl must be greater than zero.\n"; + cerr << "xstep must be greater than zero.\n"; return 1; } - const size_t rows = ((xmax - xmin) * xscl) + 1; + const size_t rows = ((xmax - xmin) / xstep) + 1; const size_t columns = numfunctions + 1; const char *const aheaderrow[] = {"x", "y"}; @@ -459,9 +479,9 @@ namespace tables const size_t length = tables::size(aheaderrow); - string *headerrow = new string[columns]; + auto *headerrow = new string[columns]; - for (unsigned int j = 0; j < columns; ++j) + for (size_t j = 0; j < columns; ++j) { if (j < (length - 1) or numfunctions == 1) { @@ -478,7 +498,7 @@ namespace tables string *headercolumn = nullptr; // headercolumn = new string[rows + 1]; - // for (unsigned int i = 0; i < rows + 1; ++i) + // for (size_t i = 0; i < rows + 1; ++i) // { // ostringstream strm; // strm << i + 1; @@ -487,22 +507,22 @@ namespace tables vector> aarray(rows, vector(columns)); - for (unsigned int i = 0; i < rows; ++i) + for (size_t i = 0; i < rows; ++i) { - aarray[i][0] = (i / xscl) + xmin; + aarray[i][0] = (i * xstep) + xmin; - for (unsigned int j = 0; j < numfunctions; ++j) + for (size_t j = 0; j < numfunctions; ++j) aarray[i][j + 1] = (functions[j])(aarray[i][0]); } int code = array(aarray, headerrow, headercolumn, aoptions); - if (headerrow != nullptr) + if (headerrow) { delete[] headerrow; } - // if (headercolumn != nullptr) + // if (headercolumn) // { // delete[] headercolumn; // } @@ -512,20 +532,20 @@ namespace tables // Convert single function to array and output as table template - int function(const long double xmin, const long double xmax, const long double xscl, const function &afunction, const options &aoptions = defaultoptions) + int function(const long double xmin, const long double xmax, const long double xstep, const function &afunction, const options &aoptions = defaultoptions) { std::function afunctions[] = {afunction}; - return functions(xmin, xmax, xscl, 1, afunctions, aoptions); + return functions(xmin, xmax, xstep, 1, afunctions, aoptions); } // Convert single function to array and output as table template - int function(const long double xmin, const long double xmax, const long double xscl, T afunction(T), const options &aoptions = defaultoptions) + int function(const long double xmin, const long double xmax, const long double xstep, T afunction(T), const options &aoptions = defaultoptions) { std::function afunctions[] = {afunction}; - return functions(xmin, xmax, xscl, 1, afunctions, aoptions); + return functions(xmin, xmax, xstep, 1, afunctions, aoptions); } }