Added files.
23
.travis.yml
Normal file
@ -0,0 +1,23 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
- os: linux
|
||||
dist: xenial
|
||||
install:
|
||||
- sudo apt-get -yqq update
|
||||
- sudo apt-get -yqq install cppcheck
|
||||
script:
|
||||
- g++ -Wall -g -fsanitize=address tables.cpp -o gcc_tables
|
||||
- ./gcc_tables
|
||||
- g++ -Wall -g -fsanitize=address graphs.cpp -o gcc_graphs
|
||||
- ./gcc_graphs
|
||||
- clang++ -Wall -g -fsanitize=address tables.cpp -o clang_tables
|
||||
- ./clang_tables
|
||||
- clang++ -Wall -g -fsanitize=address graphs.cpp -o clang_graphs
|
||||
- ./clang_graphs
|
||||
- cppcheck --error-exitcode=1 .
|
554
README.md
@ -1,2 +1,554 @@
|
||||
# Tables-and-Graphs
|
||||
[](https://travis-ci.org/tdulcet/Tables-and-Graphs)
|
||||
|
||||
# Tables and Graphs
|
||||
|
||||
C++ Console Table and Graph/Plot Libraries
|
||||
|
||||
Copyright © 2018 Teal Dulcet
|
||||
|
||||
These header only libraries use [box-drawing](https://en.wikipedia.org/wiki/Box-drawing_character#Unicode), [Braille](https://en.wikipedia.org/wiki/Braille_Patterns), [fraction](https://en.wikipedia.org/wiki/Number_Forms) and other Unicode characters and [terminal colors and formatting](https://misc.flogisoft.com/bash/tip_colors_and_formatting) to output tables and graphs/plots to the console.
|
||||
|
||||
Please visit [tealdulcet.com](https://www.tealdulcet.com/) to support these libraries and my other software development.
|
||||
|
||||
## Tables
|
||||
|
||||
### Usage
|
||||
|
||||
Complete versions of all of the examples below and more can be found in the [tables.cpp](tables.cpp) file.
|
||||
|
||||
Compile with: `g++ -Wall -g -O3 tables.cpp -o tables`.
|
||||
|
||||
Run with: `./tables`.
|
||||
|
||||
#### Output char array as table
|
||||
|
||||
```cpp
|
||||
#include "tables.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t rows = 5;
|
||||
size_t columns = 5;
|
||||
|
||||
char ***array;
|
||||
|
||||
// Allocate and set array
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
aoptions.headercolumn = true;
|
||||
|
||||
table(rows, columns, array, NULL, NULL, aoptions);
|
||||
|
||||
// Deallocate array
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Table cells can contain [Unicode characters](https://en.wikipedia.org/wiki/List_of_Unicode_characters), but not newlines and tabs.
|
||||
|
||||

|
||||
|
||||
#### Output array as table with separate header row and column
|
||||
|
||||
```cpp
|
||||
#include "tables.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t rows = 4;
|
||||
size_t columns = 4;
|
||||
|
||||
const char* headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"};
|
||||
const char* headercolumn[] = {"Header column 2", "Header column 3", "Header column 4", "Header column 5"};
|
||||
|
||||
char ***array;
|
||||
|
||||
// Allocate and set array
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
aoptions.headercolumn = true;
|
||||
|
||||
table(rows, columns, array, headerrow, headercolumn, aoptions);
|
||||
|
||||
// Deallocate array
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Output same as example above.
|
||||
|
||||
#### Output array as table
|
||||
|
||||
```cpp
|
||||
#include "tables.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t rows = 5;
|
||||
size_t columns = 5;
|
||||
|
||||
double **array; // array can be any data type
|
||||
|
||||
// Allocate and set array
|
||||
|
||||
table(rows, columns, array, NULL, NULL, tabledefaultoptions);
|
||||
|
||||
// Deallocate array
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### Output sorted array as table
|
||||
|
||||
```cpp
|
||||
#include <algorithm>
|
||||
#include "tables.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int dimensions; // Number of columns
|
||||
int sortdimension; // Column to sort by
|
||||
|
||||
template <typename T>
|
||||
bool compare(const T *a, const T *b)
|
||||
{
|
||||
if (a[sortdimension] == b[sortdimension])
|
||||
for (int i = 0; i < dimensions; ++i)
|
||||
if (sortdimension != i and a[i] != b[i])
|
||||
return a[i] < b[i];
|
||||
|
||||
return a[sortdimension] < b[sortdimension];
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t rows = 5;
|
||||
size_t columns = 5;
|
||||
|
||||
int **array; // array can be any data type
|
||||
|
||||
// Allocate and set array
|
||||
|
||||
dimensions = columns;
|
||||
sortdimension = 0;
|
||||
|
||||
sort(array, array + rows, compare<int>);
|
||||
|
||||
table(rows, columns, array, NULL, NULL, tabledefaultoptions);
|
||||
|
||||
// Deallocate array
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### Output single function as table
|
||||
|
||||
```cpp
|
||||
#include "tables.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
double afunction(double x)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
double xmin = -10;
|
||||
double xmax = 10;
|
||||
double xscl = 2;
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
|
||||
table(xmin, xmax, xscl, afunction, aoptions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### Output multiple functions as table
|
||||
|
||||
```cpp
|
||||
#include <cmath>
|
||||
#include "tables.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
double function1(double x)
|
||||
{
|
||||
return 2 * x;
|
||||
}
|
||||
|
||||
double function2(double x)
|
||||
{
|
||||
return pow(x, 2);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
double xmin = -10;
|
||||
double xmax = 10;
|
||||
double xscl = 2;
|
||||
|
||||
size_t numfunctions = 2;
|
||||
|
||||
// Function parameter and return value can be any data type, as long as they are the same
|
||||
double (*functions[])(double) = {function1, function2};
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
|
||||
table(xmin, xmax, xscl, numfunctions, functions, aoptions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Options
|
||||
|
||||
#### Header row
|
||||
|
||||
Option: `headerrow`
|
||||
|
||||
Default value: `false`
|
||||
|
||||
Header rows are bolded and centered.
|
||||
|
||||
#### Header column
|
||||
|
||||
Option: `headercolumn`
|
||||
|
||||
Default value: `false`
|
||||
|
||||
Header columns are bolded and centered.
|
||||
|
||||
#### Table border
|
||||
|
||||
Option: `tableborder`
|
||||
|
||||
Default value: `true`
|
||||
|
||||
#### Cell border
|
||||
|
||||
Option: `cellborder`
|
||||
|
||||
Default value: `false`
|
||||
|
||||
Requires `tableborder` to be `true`.
|
||||
|
||||
#### Cell padding
|
||||
|
||||
Option: `padding`
|
||||
|
||||
Default value: `1`
|
||||
|
||||
#### Alignment
|
||||
|
||||
Option: `alignment`
|
||||
|
||||
Values:
|
||||
|
||||
* `left` (default)
|
||||
* `right`
|
||||
|
||||
#### bool to alpha
|
||||
|
||||
Option: `boolalpha`
|
||||
|
||||
Default value: `false`
|
||||
|
||||
#### Title
|
||||
|
||||
Option: `title`
|
||||
|
||||
Default value: `NULL`
|
||||
|
||||
The title is word wraped based on the current width of the terminal, using [this](https://gist.github.com/tdulcet/819821ca69501822ad3f84a060c640a0) solution. Handles newlines, tabs and [Unicode characters](https://en.wikipedia.org/wiki/List_of_Unicode_characters).
|
||||
|
||||
#### Border style
|
||||
|
||||
Option: `style`
|
||||
|
||||
Values:
|
||||
|
||||
0. ASCII
|
||||
|
||||

|
||||
1. Basic
|
||||
|
||||

|
||||
2. Light (default)
|
||||
|
||||

|
||||
3. Heavy
|
||||
|
||||

|
||||
4. Double
|
||||
|
||||

|
||||
5. Light Dashed
|
||||
|
||||

|
||||
6. Heavy Dashed
|
||||
|
||||

|
||||
|
||||
### Other C++ Console Tables Libraries
|
||||
|
||||
* [C++ Text Table](https://github.com/haarcuba/cpp-text-table) (must specify every cell individually in there data structure, limited options, no Unicode support, no header row or column support)
|
||||
* [Cpp Console Table](https://github.com/Oradle/CppConsoleTable) (must specify every cell individually in there data structure, no Unicode support, no header row or column support)
|
||||
* [ConsoleTable](https://github.com/766F6964/ConsoleTable) (requires C++11, must specify entire row at once in there data structure, no header column support)
|
||||
|
||||
## Graphs/Plots
|
||||
|
||||
### Usage
|
||||
|
||||
Complete versions of all of the examples below and more can be found in the [graphs.cpp](graphs.cpp) file.
|
||||
|
||||
Compile with: `g++ -Wall -g -O3 graphs.cpp -o graphs`.
|
||||
|
||||
Run with: `./graphs`.
|
||||
|
||||
If `height` is `0`, it will be set to the current height of the terminal (number of rows times four). If `width` is `0`, it will be set to the current width of the terminal (number of columns times two).
|
||||
|
||||
#### Output array as plot
|
||||
|
||||
```cpp
|
||||
#include "graphs.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t height = 160;
|
||||
size_t width = 160;
|
||||
|
||||
long double xmin = -20;
|
||||
long double xmax = 20;
|
||||
long double ymin = -20;
|
||||
long double ymax = 20;
|
||||
|
||||
size_t rows = 10;
|
||||
|
||||
double **array; // array can be any data type, but must have exactly two columns
|
||||
|
||||
// Allocate and set array
|
||||
|
||||
graph(height, width, xmin, xmax, ymin, ymax, rows, array, graphdefaultoptions);
|
||||
|
||||
// Deallocate array
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
If `xmin` and `xmax` are both `0`, they will be set to the respective minimum and maximum values of x in the array. If `ymin` and `ymax` are both `0`, they will be set to the respective minimum and maximum values of y in the array.
|
||||
|
||||

|
||||
|
||||
#### Output single function as graph
|
||||
|
||||
```cpp
|
||||
#include "graphs.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
double afunction(double x)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t height = 160;
|
||||
size_t width = 160;
|
||||
|
||||
long double xmin = -20;
|
||||
long double xmax = 20;
|
||||
long double ymin = -20;
|
||||
long double ymax = 20;
|
||||
|
||||
graph(height, width, xmin, xmax, ymin, ymax, afunction, graphdefaultoptions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
#### Output multiple functions as graph
|
||||
|
||||
```cpp
|
||||
#include "graphs.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
double function1(double x)
|
||||
{
|
||||
return 2 * x;
|
||||
}
|
||||
|
||||
double function2(double x)
|
||||
{
|
||||
return pow(x, 2);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t height = 160;
|
||||
size_t width = 160;
|
||||
|
||||
long double xmin = -20;
|
||||
long double xmax = 20;
|
||||
long double ymin = -20;
|
||||
long double ymax = 20;
|
||||
|
||||
size_t numfunctions = 2;
|
||||
|
||||
// Function parameter and return value can be any data type, as long as they are the same
|
||||
double (*functions[])(double) = {function1, function2};
|
||||
|
||||
graph(height, width, xmin, xmax, ymin, ymax, numfunctions, functions, graphdefaultoptions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Options
|
||||
|
||||
#### Border/Axis
|
||||
|
||||
Option: `border`
|
||||
|
||||
Default value: `true`
|
||||
|
||||
#### Axis labels
|
||||
|
||||
Option: `axislabel`
|
||||
|
||||
Default value: `true`
|
||||
|
||||
Requires `border` to be `true`.
|
||||
|
||||
#### Axis units labels
|
||||
|
||||
Option: `axisunitslabel`
|
||||
|
||||
Default value: `true`
|
||||
|
||||
Requires `border` and `axislabel` to be `true`.
|
||||
|
||||
#### Title
|
||||
|
||||
Option: `title`
|
||||
|
||||
Default value: `NULL`
|
||||
|
||||
The title is word wraped based on the current width of the terminal, using [this](https://gist.github.com/tdulcet/819821ca69501822ad3f84a060c640a0) solution. Handles newlines, tabs and [Unicode characters](https://en.wikipedia.org/wiki/List_of_Unicode_characters).
|
||||
|
||||
#### Axis/Border style
|
||||
|
||||
Option: `style`
|
||||
|
||||
Values:
|
||||
|
||||
0. ASCII
|
||||
|
||||

|
||||
1. Basic
|
||||
|
||||

|
||||
2. Light (default)
|
||||
|
||||

|
||||
3. Heavy
|
||||
|
||||

|
||||
4. Double
|
||||
|
||||

|
||||
5. Light Dashed
|
||||
|
||||

|
||||
6. Heavy Dashed
|
||||
|
||||

|
||||
|
||||
#### Graph/Plot Color
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
##### Plot
|
||||
|
||||

|
||||
|
||||
##### Graph
|
||||
|
||||

|
||||
|
||||
### 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)
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests welcome! Ideas for contributions:
|
||||
|
||||
* Add more options
|
||||
* Add an option to print a border around graphs/plots
|
||||
* Add more examples
|
||||
* Improve the performance
|
||||
* Handle newlines, tabs and formatted text in the tables
|
||||
* Handle formatted text in the table and graph/plot titles
|
||||
* Support more graph/plot colors
|
||||
* Support combining colors when functions cross
|
||||
* Support plotting multiple arrays of different sizes
|
||||
* Port to other languages (C, Java, Rust, etc.)
|
||||
|
141
graphs.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
// Teal Dulcet, CS546
|
||||
|
||||
// Compile: g++ -Wall -g -O3 graphs.cpp -o graphs
|
||||
|
||||
// Run: ./graphs
|
||||
|
||||
#include <cctype>
|
||||
#include "graphs.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
long double afunction(long double x)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
long double function1(long double x)
|
||||
{
|
||||
return 2 * x;
|
||||
}
|
||||
|
||||
long double function2(long double x)
|
||||
{
|
||||
return pow(x, 2);
|
||||
}
|
||||
|
||||
long double function3(long double x)
|
||||
{
|
||||
return sin(x);
|
||||
}
|
||||
|
||||
long double function4(long double x)
|
||||
{
|
||||
return cos(x);
|
||||
}
|
||||
|
||||
long double function5(long double x)
|
||||
{
|
||||
return tan(x);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const size_t height = 160;
|
||||
const size_t width = 160;
|
||||
|
||||
const long double xmin = -20;
|
||||
const long double xmax = 20;
|
||||
const long double ymin = -20;
|
||||
const long double ymax = 20;
|
||||
|
||||
const size_t rows = 10;
|
||||
const size_t columns = 2;
|
||||
|
||||
// Output array as plot
|
||||
cout << "\nOutput array as plot\n\n";
|
||||
{
|
||||
long double **array;
|
||||
array = new long double *[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new long double[columns];
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
array[i][j] = i + j; //rand();
|
||||
|
||||
graphoptions aoptions;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
graph(height, width, xmin, xmax, ymin, ymax, rows, array, aoptions);
|
||||
}
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
delete[] array[i];
|
||||
|
||||
delete[] array;
|
||||
}
|
||||
}
|
||||
// Output single function as graph
|
||||
cout << "\nOutput single function as graph\n\n";
|
||||
{
|
||||
graphoptions aoptions;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
graph(height, width, xmin, xmax, ymin, ymax, afunction, aoptions);
|
||||
}
|
||||
}
|
||||
// Output multiple functions as graph
|
||||
cout << "\nOutput multiple functions as graph\n\n";
|
||||
{
|
||||
long double (*functions[])(long double) = {function1, function2};
|
||||
|
||||
graphoptions aoptions;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
graph(height, width, xmin, xmax, ymin, ymax, 2, functions, aoptions);
|
||||
}
|
||||
}
|
||||
{
|
||||
const long double xmin = -(2 * M_PI);
|
||||
const long double xmax = 2 * M_PI;
|
||||
const long double ymin = -4;
|
||||
const long double ymax = 4;
|
||||
|
||||
long double (*functions[])(long double) = {function3, function4, function5};
|
||||
|
||||
graphoptions aoptions;
|
||||
aoptions.axisunitslabel = false;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
graph(height, width, xmin, xmax, ymin, ymax, 3, functions, aoptions);
|
||||
}
|
||||
|
||||
/*aoptions.style = 2;
|
||||
|
||||
for (unsigned int k = 10; k < 300; ++k)
|
||||
{
|
||||
cout << "\e[1;1H" << "\e[2J";
|
||||
|
||||
graph(k, k, xmin, xmax, ymin, ymax, 3, functions, aoptions);
|
||||
|
||||
usleep(200000);
|
||||
}*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
725
graphs.hpp
Normal file
@ -0,0 +1,725 @@
|
||||
// Teal Dulcet, CS546
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <cfloat>
|
||||
#include <iomanip>
|
||||
#include <cwchar>
|
||||
#include <clocale>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char *const styles[][11] = {
|
||||
{"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //ASCII
|
||||
{"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //Basic
|
||||
{"─", "│", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light
|
||||
{"━", "┃", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"}, //Heavy
|
||||
{"═", "║", "╔", "╦", "╗", "╠", "╬", "╣", "╚", "╩", "╝"}, //Double
|
||||
{"╌", "╎", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light Dashed
|
||||
{"╍", "╏", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"} //Heavy Dashed
|
||||
};
|
||||
// {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}};//No border
|
||||
|
||||
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[] = {"⠀", "⠁", "⠂", "⠃", "⠄", "⠅", "⠆", "⠇", "⠈", "⠉", "⠊", "⠋", "⠌", "⠍", "⠎", "⠏", "⠐", "⠑", "⠒", "⠓", "⠔", "⠕", "⠖", "⠗", "⠘", "⠙", "⠚", "⠛", "⠜", "⠝", "⠞", "⠟", "⠠", "⠡", "⠢", "⠣", "⠤", "⠥", "⠦", "⠧", "⠨", "⠩", "⠪", "⠫", "⠬", "⠭", "⠮", "⠯", "⠰", "⠱", "⠲", "⠳", "⠴", "⠵", "⠶", "⠷", "⠸", "⠹", "⠺", "⠻", "⠼", "⠽", "⠾", "⠿", "⡀", "⡁", "⡂", "⡃", "⡄", "⡅", "⡆", "⡇", "⡈", "⡉", "⡊", "⡋", "⡌", "⡍", "⡎", "⡏", "⡐", "⡑", "⡒", "⡓", "⡔", "⡕", "⡖", "⡗", "⡘", "⡙", "⡚", "⡛", "⡜", "⡝", "⡞", "⡟", "⡠", "⡡", "⡢", "⡣", "⡤", "⡥", "⡦", "⡧", "⡨", "⡩", "⡪", "⡫", "⡬", "⡭", "⡮", "⡯", "⡰", "⡱", "⡲", "⡳", "⡴", "⡵", "⡶", "⡷", "⡸", "⡹", "⡺", "⡻", "⡼", "⡽", "⡾", "⡿", "⢀", "⢁", "⢂", "⢃", "⢄", "⢅", "⢆", "⢇", "⢈", "⢉", "⢊", "⢋", "⢌", "⢍", "⢎", "⢏", "⢐", "⢑", "⢒", "⢓", "⢔", "⢕", "⢖", "⢗", "⢘", "⢙", "⢚", "⢛", "⢜", "⢝", "⢞", "⢟", "⢠", "⢡", "⢢", "⢣", "⢤", "⢥", "⢦", "⢧", "⢨", "⢩", "⢪", "⢫", "⢬", "⢭", "⢮", "⢯", "⢰", "⢱", "⢲", "⢳", "⢴", "⢵", "⢶", "⢷", "⢸", "⢹", "⢺", "⢻", "⢼", "⢽", "⢾", "⢿", "⣀", "⣁", "⣂", "⣃", "⣄", "⣅", "⣆", "⣇", "⣈", "⣉", "⣊", "⣋", "⣌", "⣍", "⣎", "⣏", "⣐", "⣑", "⣒", "⣓", "⣔", "⣕", "⣖", "⣗", "⣘", "⣙", "⣚", "⣛", "⣜", "⣝", "⣞", "⣟", "⣠", "⣡", "⣢", "⣣", "⣤", "⣥", "⣦", "⣧", "⣨", "⣩", "⣪", "⣫", "⣬", "⣭", "⣮", "⣯", "⣰", "⣱", "⣲", "⣳", "⣴", "⣵", "⣶", "⣷", "⣸", "⣹", "⣺", "⣻", "⣼", "⣽", "⣾", "⣿"};
|
||||
|
||||
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};
|
||||
|
||||
struct graphoptions
|
||||
{
|
||||
bool border;
|
||||
bool axislabel;
|
||||
bool axisunitslabel;
|
||||
char *title;
|
||||
unsigned int style;
|
||||
unsigned int color;
|
||||
graphoptions(void);
|
||||
~graphoptions(void);
|
||||
};
|
||||
|
||||
graphoptions::graphoptions(void)
|
||||
{
|
||||
border = true;
|
||||
axislabel = true;
|
||||
axisunitslabel = true;
|
||||
style = 2;
|
||||
color = 2;
|
||||
title = NULL;
|
||||
}
|
||||
|
||||
graphoptions::~graphoptions(void)
|
||||
{
|
||||
}
|
||||
|
||||
const graphoptions graphdefaultoptions;
|
||||
|
||||
// Number of columns needed to represent the string
|
||||
// Adapted from: https://stackoverflow.com/a/31124065
|
||||
int strcol(const char *const str)
|
||||
{
|
||||
size_t length = strlen(str);
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
if (iscntrl(str[i]))
|
||||
{
|
||||
cerr << "\nError! Control character in string.\n";
|
||||
cout << "Control character: " << (int)str[i] << "\n";
|
||||
}
|
||||
|
||||
length = mbstowcs(NULL, str, 0);
|
||||
if (length == (size_t)-1)
|
||||
{
|
||||
cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n";
|
||||
exit(1);
|
||||
}
|
||||
++length;
|
||||
|
||||
wchar_t *wcstring = new wchar_t[length];
|
||||
|
||||
if (mbstowcs(wcstring, str, length) == (size_t)-1)
|
||||
{
|
||||
if (wcstring != NULL)
|
||||
delete[] wcstring;
|
||||
|
||||
cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int width = wcswidth(wcstring, length);
|
||||
if (width == -1)
|
||||
{
|
||||
cerr << "\nError! wcswidth failed. Nonprintable wide character.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (wcstring != NULL)
|
||||
delete[] wcstring;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
// Word wrap
|
||||
// Source: https://gist.github.com/tdulcet/819821ca69501822ad3f84a060c640a0
|
||||
// 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 wrapped;
|
||||
|
||||
size_t index = 0;
|
||||
size_t linelen = 0;
|
||||
while (words[index] != '\0')
|
||||
{
|
||||
if (words[index] == '\n')
|
||||
{
|
||||
linelen = 0;
|
||||
}
|
||||
else if (isspace(words[index]))
|
||||
{
|
||||
size_t tempindex = index + 1;
|
||||
size_t templinelen = linelen;
|
||||
while (!isspace(words[tempindex]) and words[tempindex] != '\0')
|
||||
{
|
||||
++templinelen;
|
||||
|
||||
++tempindex;
|
||||
}
|
||||
|
||||
char temp[templinelen + 1];
|
||||
strncpy(temp, words + (index - linelen), templinelen);
|
||||
temp[templinelen] = '\0';
|
||||
|
||||
size_t width = strcol(temp);
|
||||
|
||||
if (width >= line_length)
|
||||
{
|
||||
words[index] = '\n';
|
||||
linelen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (words[index] == '\t')
|
||||
linelen += 8 - (linelen % 8);
|
||||
else if (words[index] != '\n')
|
||||
++linelen;
|
||||
|
||||
++index;
|
||||
}
|
||||
wrapped = words;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
// Convert fractions and constants to Unicode characters
|
||||
size_t outputlabel(const long double label, ostringstream &strm)
|
||||
{
|
||||
bool output = false;
|
||||
|
||||
long double intpart = 0;
|
||||
long double fractionpart = abs(modf(label, &intpart));
|
||||
|
||||
for (unsigned int i = 0; i < (sizeof fractions / sizeof fractions[0]) and !output; ++i)
|
||||
{
|
||||
if (abs(fractionpart - fractionvalues[i]) < DBL_EPSILON)
|
||||
{
|
||||
if (intpart != 0)
|
||||
strm << intpart;
|
||||
|
||||
strm << fractions[i];
|
||||
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output and fmod(label, M_PI) == 0)
|
||||
{
|
||||
const char symbol[] = "π";
|
||||
|
||||
intpart = label / M_PI;
|
||||
|
||||
if (intpart == -1)
|
||||
strm << "-";
|
||||
else if (intpart != 1)
|
||||
strm << intpart;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!output)
|
||||
strm << label;
|
||||
|
||||
size_t length = strcol(strm.str().c_str());
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// Output graph
|
||||
int graph(const size_t height, const size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, unsigned short **array, const graphoptions &aoptions)
|
||||
{
|
||||
if (array == NULL)
|
||||
return 1;
|
||||
|
||||
const bool border = aoptions.border;
|
||||
const bool axislabel = aoptions.axislabel;
|
||||
const bool axisunitslabel = aoptions.axisunitslabel;
|
||||
const char *const title = aoptions.title;
|
||||
const unsigned int style = aoptions.style;
|
||||
|
||||
if (style >= (sizeof styles / sizeof styles[0]))
|
||||
return 1;
|
||||
|
||||
if (height == 0)
|
||||
return 1;
|
||||
|
||||
if (width == 0)
|
||||
return 1;
|
||||
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
const int aheight = height / 4;
|
||||
const int awidth = width / 2;
|
||||
|
||||
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)
|
||||
{
|
||||
cerr << "xmin must be less than xmax.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ymin >= ymax)
|
||||
{
|
||||
cerr << "ymin must be less than ymax.\n";
|
||||
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);
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
if (title != NULL and title[0] != '\0')
|
||||
cout << wrap(title, w.ws_col) << "\n";
|
||||
|
||||
for (unsigned int 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));
|
||||
|
||||
ostringstream ylabelstrm;
|
||||
size_t ylabellength = 0;
|
||||
|
||||
if (border and axislabel and axisunitslabel)
|
||||
{
|
||||
bool output = false;
|
||||
long double label;
|
||||
int adivisor = divisor;
|
||||
if (i < yaxis)
|
||||
adivisor = -adivisor;
|
||||
|
||||
for (long double k = yaxis + adivisor; ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or !axislabel) and !output; k += adivisor)
|
||||
{
|
||||
if (i <= k and (i + 4) > k)
|
||||
{
|
||||
label = ymax - (k / yscl);
|
||||
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (output)
|
||||
{
|
||||
ylabellength = outputlabel(label, ylabelstrm);
|
||||
ylabellength *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int 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));
|
||||
|
||||
bool output = false;
|
||||
|
||||
if (border)
|
||||
{
|
||||
if (axaxis and ayaxis)
|
||||
{
|
||||
cout << styles[style][6];
|
||||
output = true;
|
||||
}
|
||||
else if (axaxis)
|
||||
{
|
||||
if (axisunitslabel)
|
||||
{
|
||||
int adivisor = divisor;
|
||||
if (i < yaxis)
|
||||
adivisor = -adivisor;
|
||||
|
||||
for (long double k = yaxis + adivisor; ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or !axislabel) and !output; k += adivisor)
|
||||
{
|
||||
if (i <= k and (i + 4) > k)
|
||||
{
|
||||
cout << styles[style][7];
|
||||
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];
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
else if (ayaxis)
|
||||
{
|
||||
if (axisunitslabel)
|
||||
{
|
||||
int adivisor = divisor;
|
||||
if (j < xaxis)
|
||||
adivisor = -adivisor;
|
||||
|
||||
for (long double k = xaxis + adivisor; ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and (j < (width - 4) or !axislabel) and !output; k += adivisor)
|
||||
{
|
||||
if (j <= k and (j + 2) > k)
|
||||
{
|
||||
cout << styles[style][3];
|
||||
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];
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
else if (yaxislabel and xaxislabel and axisunitslabel)
|
||||
{
|
||||
cout << "0";
|
||||
output = true;
|
||||
}
|
||||
else if (j >= (width - 2) and yaxislabel and axislabel)
|
||||
{
|
||||
cout << "x";
|
||||
output = true;
|
||||
}
|
||||
else if (yaxislabel and axislabel and axisunitslabel)
|
||||
{
|
||||
long double label;
|
||||
int adivisor = divisor;
|
||||
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)
|
||||
{
|
||||
if (j <= k and (j + 2) > k)
|
||||
{
|
||||
label = (k / xscl) + xmin;
|
||||
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (adivisor < 0)
|
||||
j -= 2;
|
||||
|
||||
if (output)
|
||||
{
|
||||
output = false;
|
||||
|
||||
ostringstream strm;
|
||||
size_t length = outputlabel(label, strm);
|
||||
length *= 2;
|
||||
if ((j >= xaxis or (j + length) < (xaxis - 4)) and (j + length) < (width - 2))
|
||||
{
|
||||
cout << strm.str();
|
||||
|
||||
if (length > 2)
|
||||
j += length - 2;
|
||||
|
||||
if (adivisor < 0)
|
||||
output = true;
|
||||
else
|
||||
j += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (i == 0 and xaxislabel and axislabel)
|
||||
{
|
||||
cout << "y";
|
||||
output = true;
|
||||
}
|
||||
else if ((j <= (xaxis - ylabellength) and (j + 2) > (xaxis - ylabellength)) and axislabel and axisunitslabel)
|
||||
{
|
||||
cout << ylabelstrm.str();
|
||||
output = true;
|
||||
if (ylabellength > 2)
|
||||
j += ylabellength - 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output)
|
||||
{
|
||||
unsigned int dot = 0;
|
||||
unsigned short color = 0;
|
||||
|
||||
for (int k = 0; k < 2 and (j + k) < width; ++k)
|
||||
{
|
||||
for (int l = 0; l < 4 and (i + l) < height; ++l)
|
||||
{
|
||||
dot += (array[j + k][i + l] ? 1 : 0) * values[k][l];
|
||||
if (color)
|
||||
{
|
||||
if (array[j + k][i + l] and color != array[j + k][i + l])
|
||||
color = 1;
|
||||
}
|
||||
else
|
||||
color = array[j + k][i + l];
|
||||
}
|
||||
}
|
||||
|
||||
if (color)
|
||||
--color;
|
||||
|
||||
if (color)
|
||||
cout << colors[color];
|
||||
|
||||
cout << "\e[1m" << dots[dot] << "\e[22m";
|
||||
|
||||
if (color)
|
||||
cout << colors[0];
|
||||
}
|
||||
}
|
||||
|
||||
cout << "\n";
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert array to graph and output
|
||||
template <typename T>
|
||||
int graph(size_t height, size_t width, long double xmin, long double xmax, long double ymin, long double ymax, const size_t rows, T **array, const graphoptions &aoptions)
|
||||
{
|
||||
if (rows == 0)
|
||||
return 1;
|
||||
|
||||
if (array == NULL)
|
||||
return 1;
|
||||
|
||||
const unsigned int color = aoptions.color;
|
||||
|
||||
if (color >= (sizeof colors / sizeof colors[0]))
|
||||
return 1;
|
||||
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
if (height == 0)
|
||||
height = w.ws_row * 4;
|
||||
|
||||
if (width == 0)
|
||||
width = w.ws_col * 2;
|
||||
|
||||
const int aheight = height / 4;
|
||||
const int awidth = width / 2;
|
||||
|
||||
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)
|
||||
{
|
||||
xmin = numeric_limits<T>::max();
|
||||
xmax = numeric_limits<T>::min();
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
if (array[i][0] < xmin)
|
||||
xmin = array[i][0];
|
||||
|
||||
if (array[i][0] > xmax)
|
||||
xmax = array[i][0];
|
||||
}
|
||||
}
|
||||
|
||||
if (ymin == 0 and ymax == 0)
|
||||
{
|
||||
ymin = numeric_limits<T>::max();
|
||||
ymax = numeric_limits<T>::min();
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
if (array[i][1] < ymin)
|
||||
ymin = array[i][1];
|
||||
|
||||
if (array[i][1] > ymax)
|
||||
ymax = array[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
if (xmin >= xmax)
|
||||
{
|
||||
cerr << "xmin must be less than xmax.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ymin >= ymax)
|
||||
{
|
||||
cerr << "ymin must be less than ymax.\n";
|
||||
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;
|
||||
|
||||
unsigned short **aarray;
|
||||
aarray = new unsigned short *[width];
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
aarray[i] = new unsigned short[height];
|
||||
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
aarray[i][j] = 0;
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
if (array[i][0] >= xmin and array[i][0] < xmax and array[i][1] >= ymin and array[i][1] < ymax)
|
||||
{
|
||||
const long long x = (array[i][0] * xscl) + xaxis;
|
||||
const long long y = (yaxis - (array[i][1] * yscl)) - 1;
|
||||
|
||||
aarray[x][y] = color + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int code = graph(height, width, xmin, xmax, ymin, ymax, aarray, aoptions);
|
||||
|
||||
if (aarray != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
delete[] aarray[i];
|
||||
|
||||
delete[] aarray;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Convert one or more functions to graph and output
|
||||
template <typename T>
|
||||
int graph(size_t height, size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, const size_t numfunctions, T (*functions[])(T), const graphoptions &aoptions)
|
||||
{
|
||||
const unsigned int color = aoptions.color;
|
||||
|
||||
if (color >= (sizeof colors / sizeof colors[0]))
|
||||
return 1;
|
||||
|
||||
if (numfunctions == 0)
|
||||
return 1;
|
||||
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
if (height == 0)
|
||||
height = w.ws_row * 4;
|
||||
|
||||
if (width == 0)
|
||||
width = w.ws_col * 2;
|
||||
|
||||
const int aheight = height / 4;
|
||||
const int awidth = width / 2;
|
||||
|
||||
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)
|
||||
{
|
||||
cerr << "xmin must be less than xmax.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ymin >= ymax)
|
||||
{
|
||||
cerr << "ymin must be less than ymax.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
unsigned short **array;
|
||||
array = new unsigned short *[width];
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
array[i] = new unsigned short[height];
|
||||
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
array[i][j] = 0;
|
||||
|
||||
for (unsigned int j = 0; j < numfunctions; ++j)
|
||||
{
|
||||
unsigned short acolor = numfunctions == 1 ? color + 1 : (j % ((sizeof colors / sizeof colors[0]) - 2)) + 3;
|
||||
|
||||
for (long double i = 0; i < rows; i += 0.5)
|
||||
{
|
||||
T x = (i / xscl) + 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;
|
||||
|
||||
if (array[ax][ay])
|
||||
{
|
||||
if (array[ax][ay] != acolor)
|
||||
array[ax][ay] = 1;
|
||||
}
|
||||
else
|
||||
array[ax][ay] = acolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int code = graph(height, width, xmin, xmax, ymin, ymax, array, aoptions);
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
delete[] array[i];
|
||||
|
||||
delete[] array;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Convert single function to function array and output
|
||||
template <typename T>
|
||||
int graph(size_t height, size_t width, const long double xmin, const long double xmax, const long double ymin, const long double ymax, T afunction(T), const graphoptions &aoptions)
|
||||
{
|
||||
T(*functions[])
|
||||
(T) = {afunction};
|
||||
|
||||
return graph(height, width, xmin, xmax, ymin, ymax, 1, functions, aoptions);
|
||||
}
|
BIN
images/ASCII graph.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
images/ASCII table.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
images/array to plot.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
images/array to table.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
images/basic graph.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
images/basic table.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
images/char array to table.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
images/double graph.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
images/double table.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
images/function to graph.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
images/function to table.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
images/graph colors.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
images/heavy dashed graph.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
images/heavy dashed table.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
images/heavy graph.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
images/heavy table.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
images/light dashed graph.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
images/light dashed table.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
images/light graph.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
images/light table.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
images/multiple functions to graph.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
images/multiple functions to table.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
images/plot colors.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
images/sorted array to table.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
329
tables.cpp
Normal file
@ -0,0 +1,329 @@
|
||||
// Teal Dulcet, CS546
|
||||
|
||||
// Compile: g++ -Wall -g -O3 tables.cpp -o tables
|
||||
|
||||
// Run: ./tables
|
||||
|
||||
#include <cctype>
|
||||
// #include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include "tables.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
long double afunction(long double x)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
long double function1(long double x)
|
||||
{
|
||||
return 2 * x;
|
||||
}
|
||||
|
||||
long double function2(long double x)
|
||||
{
|
||||
return pow(x, 2);
|
||||
}
|
||||
|
||||
int dimensions = 0;
|
||||
int sortdimension = 0;
|
||||
|
||||
/* template <typename T>
|
||||
int compare(const void *pa, const void *pb)
|
||||
{
|
||||
const T *a = *(const T **)pa;
|
||||
const T *b = *(const T **)pb;
|
||||
|
||||
if (a[sortdimension] == b[sortdimension])
|
||||
{
|
||||
for (int i = 0; i < dimensions; ++i)
|
||||
{
|
||||
if (sortdimension != i and a[i] != b[i])
|
||||
{
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (a[sortdimension] > b[sortdimension])
|
||||
return 1;
|
||||
else if (a[sortdimension] == b[sortdimension])
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
} */
|
||||
|
||||
template <typename T>
|
||||
bool compare(const T *a, const T *b)
|
||||
{
|
||||
if (a[sortdimension] == b[sortdimension])
|
||||
for (int i = 0; i < dimensions; ++i)
|
||||
if (sortdimension != i and a[i] != b[i])
|
||||
return a[i] < b[i];
|
||||
|
||||
return a[sortdimension] < b[sortdimension];
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const size_t rows = 5;
|
||||
const size_t columns = 5;
|
||||
|
||||
const long double xmin = -10;
|
||||
const long double xmax = 10;
|
||||
const long double xscl = 2; // 80 / (xmax - xmin);
|
||||
|
||||
// Output array as table
|
||||
cout << "\nOutput array as table\n\n";
|
||||
{
|
||||
long long **array;
|
||||
array = new long long *[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new long long[columns];
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
array[i][j] = rand();
|
||||
|
||||
tableoptions aoptions;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(rows, columns, array, NULL, NULL, aoptions);
|
||||
}
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
delete[] array[i];
|
||||
|
||||
delete[] array;
|
||||
}
|
||||
}
|
||||
{
|
||||
long double **array;
|
||||
array = new long double *[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new long double[columns];
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
array[i][j] = static_cast<long double>(rand()) / static_cast<long double>(RAND_MAX);
|
||||
|
||||
tableoptions aoptions;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(rows, columns, array, NULL, NULL, aoptions);
|
||||
}
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
delete[] array[i];
|
||||
|
||||
delete[] array;
|
||||
}
|
||||
}
|
||||
// Output char array as table
|
||||
cout << "\nOutput char array as table\n\n";
|
||||
{
|
||||
const char *const aarray[rows][columns] = {
|
||||
{"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"},
|
||||
{"Header column 2", "Data 1", "Data 2", "Data 3", "Data 4"},
|
||||
{"Header column 3", "Data 5", "Data 6", "Data 7", "Data 8"},
|
||||
{"Header column 4", "Data 9", "Data 10", "Data 11", "Data 12"},
|
||||
{"Header column 5", "Data 13", "Data 14", "Data 15", "Data 16"}};
|
||||
|
||||
char ***array;
|
||||
array = new char **[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new char *[columns];
|
||||
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
array[i][j] = new char[strlen(aarray[i][j]) + 1];
|
||||
strcpy(array[i][j], aarray[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
aoptions.headercolumn = true;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(rows, columns, array, NULL, NULL, aoptions);
|
||||
}
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
delete[] array[i][j];
|
||||
|
||||
delete[] array[i];
|
||||
}
|
||||
delete[] array;
|
||||
}
|
||||
}
|
||||
// Output array as table with separate header row and column
|
||||
cout << "\nOutput array as table with separate header row and column\n\n";
|
||||
{
|
||||
const size_t rows = 4;
|
||||
const size_t columns = 4;
|
||||
|
||||
const char *const aarray[rows][columns] = {
|
||||
{"Data 1", "Data 2", "Data 3", "Data 4"},
|
||||
{"Data 5", "Data 6", "Data 7", "Data 8"},
|
||||
{"Data 9", "Data 10", "Data 11", "Data 12"},
|
||||
{"Data 13", "Data 14", "Data 15", "Data 16"}};
|
||||
|
||||
const char *const headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"};
|
||||
const char *const headercolumn[] = {"Header column 2", "Header column 3", "Header column 4", "Header column 5"};
|
||||
|
||||
char ***array;
|
||||
array = new char **[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new char *[columns];
|
||||
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
array[i][j] = new char[strlen(aarray[i][j]) + 1];
|
||||
strcpy(array[i][j], aarray[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
aoptions.headercolumn = true;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(rows, columns, array, headerrow, headercolumn, aoptions);
|
||||
}
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
delete[] array[i][j];
|
||||
|
||||
delete[] array[i];
|
||||
}
|
||||
delete[] array;
|
||||
}
|
||||
}
|
||||
{
|
||||
bool **array;
|
||||
array = new bool *[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new bool[columns];
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
array[i][j] = rand() % 2;
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.boolalpha = true;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(rows, columns, array, NULL, NULL, aoptions);
|
||||
}
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
delete[] array[i];
|
||||
|
||||
delete[] array;
|
||||
}
|
||||
}
|
||||
// Output sorted array as table
|
||||
cout << "\nOutput sorted array as table\n\n";
|
||||
{
|
||||
int **array;
|
||||
array = new int *[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new int[columns];
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
array[i][j] = rand();
|
||||
|
||||
dimensions = columns;
|
||||
sortdimension = 0;
|
||||
|
||||
// qsort(array, rows, sizeof(array[0]), compare<int>);
|
||||
sort(array, array + rows, compare<int>);
|
||||
|
||||
tableoptions aoptions;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(rows, columns, array, NULL, NULL, aoptions);
|
||||
}
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
delete[] array[i];
|
||||
|
||||
delete[] array;
|
||||
}
|
||||
}
|
||||
// Output single function as table
|
||||
cout << "\nOutput single function as table\n\n";
|
||||
{
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(xmin, xmax, xscl, afunction, aoptions);
|
||||
}
|
||||
}
|
||||
// Output multiple functions as table
|
||||
cout << "\nOutput multiple functions as table\n\n";
|
||||
{
|
||||
long double (*functions[])(long double) = {function1, function2};
|
||||
|
||||
tableoptions aoptions;
|
||||
aoptions.headerrow = true;
|
||||
|
||||
for (unsigned int k = 0; k < (sizeof styles / sizeof styles[0]); ++k)
|
||||
{
|
||||
aoptions.style = k;
|
||||
|
||||
table(xmin, xmax, xscl, 2, functions, aoptions);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
519
tables.hpp
Normal file
@ -0,0 +1,519 @@
|
||||
// Teal Dulcet, CS546
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <cwchar>
|
||||
#include <clocale>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char *const styles[][11] = {
|
||||
{"-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //ASCII
|
||||
{"—", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"}, //Basic
|
||||
{"─", "│", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light
|
||||
{"━", "┃", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"}, //Heavy
|
||||
{"═", "║", "╔", "╦", "╗", "╠", "╬", "╣", "╚", "╩", "╝"}, //Double
|
||||
{"╌", "╎", "┌", "┬", "┐", "├", "┼", "┤", "└", "┴", "┘"}, //Light Dashed
|
||||
{"╍", "╏", "┏", "┳", "┓", "┣", "╋", "┫", "┗", "┻", "┛"} //Heavy Dashed
|
||||
};
|
||||
// {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}};//No border
|
||||
|
||||
struct tableoptions
|
||||
{
|
||||
bool headerrow;
|
||||
bool headercolumn;
|
||||
bool tableborder;
|
||||
bool cellborder;
|
||||
unsigned int padding;
|
||||
ios_base &(*alignment)(ios_base &);
|
||||
bool boolalpha;
|
||||
char *title;
|
||||
unsigned int style;
|
||||
tableoptions(void);
|
||||
~tableoptions(void);
|
||||
};
|
||||
|
||||
tableoptions::tableoptions(void)
|
||||
{
|
||||
headerrow = false;
|
||||
headercolumn = false;
|
||||
tableborder = true;
|
||||
cellborder = false;
|
||||
padding = 1;
|
||||
alignment = left;
|
||||
boolalpha = false;
|
||||
title = NULL;
|
||||
style = 2;
|
||||
}
|
||||
|
||||
tableoptions::~tableoptions(void)
|
||||
{
|
||||
}
|
||||
|
||||
const tableoptions tabledefaultoptions;
|
||||
|
||||
// Number of columns needed to represent the string
|
||||
// Adapted from: https://stackoverflow.com/a/31124065
|
||||
int strcol(const char *const str)
|
||||
{
|
||||
size_t length = strlen(str);
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
if (iscntrl(str[i]))
|
||||
{
|
||||
cerr << "\nError! Control character in string.\n";
|
||||
cout << "Control character: " << (int)str[i] << "\n";
|
||||
}
|
||||
|
||||
length = mbstowcs(NULL, str, 0);
|
||||
if (length == (size_t)-1)
|
||||
{
|
||||
cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n";
|
||||
exit(1);
|
||||
}
|
||||
++length;
|
||||
|
||||
wchar_t *wcstring = new wchar_t[length];
|
||||
|
||||
if (mbstowcs(wcstring, str, length) == (size_t)-1)
|
||||
{
|
||||
if (wcstring != NULL)
|
||||
delete[] wcstring;
|
||||
|
||||
cerr << "\nError! mbstowcs failed. Invalid multibyte character.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int width = wcswidth(wcstring, length);
|
||||
if (width == -1)
|
||||
{
|
||||
cerr << "\nError! wcswidth failed. Nonprintable wide character.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (wcstring != NULL)
|
||||
delete[] wcstring;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
// Word wrap
|
||||
// Source: https://gist.github.com/tdulcet/819821ca69501822ad3f84a060c640a0
|
||||
// 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 wrapped;
|
||||
|
||||
size_t index = 0;
|
||||
size_t linelen = 0;
|
||||
while (words[index] != '\0')
|
||||
{
|
||||
if (words[index] == '\n')
|
||||
{
|
||||
linelen = 0;
|
||||
}
|
||||
else if (isspace(words[index]))
|
||||
{
|
||||
size_t tempindex = index + 1;
|
||||
size_t templinelen = linelen;
|
||||
while (!isspace(words[tempindex]) and words[tempindex] != '\0')
|
||||
{
|
||||
++templinelen;
|
||||
|
||||
++tempindex;
|
||||
}
|
||||
|
||||
char temp[templinelen + 1];
|
||||
strncpy(temp, words + (index - linelen), templinelen);
|
||||
temp[templinelen] = '\0';
|
||||
|
||||
size_t width = strcol(temp);
|
||||
|
||||
if (width >= line_length)
|
||||
{
|
||||
words[index] = '\n';
|
||||
linelen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (words[index] == '\t')
|
||||
linelen += 8 - (linelen % 8);
|
||||
else if (words[index] != '\n')
|
||||
++linelen;
|
||||
|
||||
++index;
|
||||
}
|
||||
wrapped = words;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
// Output char array as table
|
||||
int table(const size_t rows, const size_t columns, char ***array, const tableoptions &aoptions)
|
||||
{
|
||||
if (array == NULL)
|
||||
return 1;
|
||||
|
||||
const bool headerrow = aoptions.headerrow;
|
||||
const bool headercolumn = aoptions.headercolumn;
|
||||
const bool tableborder = aoptions.tableborder;
|
||||
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 >= (sizeof styles / sizeof styles[0]))
|
||||
return 1;
|
||||
|
||||
int columnwidth[columns];
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
columnwidth[j] = 0;
|
||||
|
||||
int width = 0;
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
int cellwidth = strcol(array[i][j]);
|
||||
if (cellwidth > columnwidth[j])
|
||||
columnwidth[j] = cellwidth;
|
||||
}
|
||||
|
||||
width += columnwidth[j];
|
||||
}
|
||||
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
if (tableborder and (cellborder or headerrow or headercolumn))
|
||||
width += (((2 * padding) + 1) * columns) + 1;
|
||||
else
|
||||
width += ((2 * padding) * columns) + 2;
|
||||
|
||||
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 != NULL and title[0] != '\0')
|
||||
cout << wrap(title, w.ws_col) << "\n";
|
||||
|
||||
if (tableborder)
|
||||
{
|
||||
cout << styles[style][2];
|
||||
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
for (unsigned int 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];
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
if (tableborder)
|
||||
{
|
||||
if (j == 0 or cellborder or (i == 0 and headerrow) or (j == 1 and headercolumn))
|
||||
cout << styles[style][1];
|
||||
else
|
||||
cout << " ";
|
||||
}
|
||||
|
||||
if ((i == 0 and headerrow) or (j == 0 and headercolumn))
|
||||
{
|
||||
int difference = columnwidth[j] - strcol(array[i][j]);
|
||||
int apadding = (difference / 2);
|
||||
|
||||
for (unsigned int k = 0; k < padding + apadding; ++k)
|
||||
cout << " ";
|
||||
|
||||
cout << "\e[1m" << array[i][j] << "\e[22m";
|
||||
|
||||
for (unsigned int k = 0; k < padding + (difference - apadding); ++k)
|
||||
cout << " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int k = 0; k < padding; ++k)
|
||||
cout << " ";
|
||||
|
||||
cout << aoptions.alignment << setw(columnwidth[j]) << array[i][j];
|
||||
|
||||
for (unsigned int k = 0; k < padding; ++k)
|
||||
cout << " ";
|
||||
}
|
||||
}
|
||||
|
||||
if (tableborder)
|
||||
cout << styles[style][1];
|
||||
|
||||
cout << "\n";
|
||||
|
||||
if (tableborder)
|
||||
{
|
||||
if (i == (rows - 1))
|
||||
cout << styles[style][8];
|
||||
else if (cellborder or (i == 0 and headerrow) or headercolumn)
|
||||
cout << styles[style][5];
|
||||
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
if (cellborder or i == (rows - 1) or (i == 0 and headerrow) or (j == 0 and headercolumn))
|
||||
for (unsigned int k = 0; k < (2 * padding) + columnwidth[j]; ++k)
|
||||
cout << styles[style][0];
|
||||
else if (headercolumn)
|
||||
for (unsigned int k = 0; k < (2 * padding) + columnwidth[j]; ++k)
|
||||
cout << " ";
|
||||
|
||||
if (j == (columns - 1))
|
||||
{
|
||||
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 (cellborder or (i == 0 and headerrow) or headercolumn)
|
||||
cout << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == (rows - 1))
|
||||
{
|
||||
if (cellborder or (j == 0 and headercolumn))
|
||||
cout << styles[style][9];
|
||||
else
|
||||
cout << styles[style][0];
|
||||
}
|
||||
else if (cellborder or ((i == 0 and headerrow) and (j == 0 and headercolumn)))
|
||||
cout << styles[style][6];
|
||||
else if (i == 0 and headerrow)
|
||||
cout << styles[style][9];
|
||||
else if (headercolumn)
|
||||
{
|
||||
if (j == 0)
|
||||
cout << styles[style][7];
|
||||
else
|
||||
cout << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert array to char array and output as table
|
||||
template <typename T>
|
||||
int table(size_t rows, size_t columns, T **array, const char *const headerrow[], const char *const headercolumn[], const tableoptions &aoptions)
|
||||
{
|
||||
if (array == NULL)
|
||||
return 1;
|
||||
|
||||
unsigned int i = 0;
|
||||
unsigned int j = 0;
|
||||
|
||||
if (headerrow != NULL)
|
||||
++rows;
|
||||
|
||||
if (headercolumn != NULL)
|
||||
++columns;
|
||||
|
||||
char ***aarray;
|
||||
aarray = new char **[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
aarray[i] = new char *[columns];
|
||||
|
||||
if (headerrow != NULL)
|
||||
{
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
aarray[i][j] = new char[strlen(headerrow[j]) + 1];
|
||||
strcpy(aarray[i][j], headerrow[j]);
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
for (unsigned int ii = 0; i < rows; ++i)
|
||||
{
|
||||
if (headercolumn != NULL)
|
||||
{
|
||||
unsigned int ii = i;
|
||||
|
||||
if (headerrow != NULL)
|
||||
--ii;
|
||||
|
||||
aarray[i][j] = new char[strlen(headercolumn[ii]) + 1];
|
||||
strcpy(aarray[i][j], headercolumn[ii]);
|
||||
|
||||
++j;
|
||||
}
|
||||
|
||||
for (unsigned int jj = 0; j < columns; ++j)
|
||||
{
|
||||
ostringstream strm;
|
||||
|
||||
if (aoptions.boolalpha)
|
||||
strm << boolalpha;
|
||||
|
||||
strm << array[ii][jj];
|
||||
string str = strm.str();
|
||||
aarray[i][j] = new char[str.length() + 1];
|
||||
strcpy(aarray[i][j], str.c_str());
|
||||
|
||||
++jj;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
++ii;
|
||||
}
|
||||
|
||||
int code = table(rows, columns, aarray, aoptions);
|
||||
|
||||
if (aarray != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
delete[] aarray[i][j];
|
||||
|
||||
delete[] aarray[i];
|
||||
}
|
||||
delete[] aarray;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Convert one or more functions to array and output as table
|
||||
template <typename T>
|
||||
int table(const long double xmin, const long double xmax, const long double xscl, const size_t numfunctions, T (*functions[])(T), const tableoptions &aoptions)
|
||||
{
|
||||
if (numfunctions == 0)
|
||||
return 1;
|
||||
|
||||
if (xmin >= xmax)
|
||||
{
|
||||
cerr << "xmin must be less than xmax.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (xscl <= 0)
|
||||
{
|
||||
cerr << "xscl must be greater than zero.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const size_t rows = ((xmax - xmin) * xscl) + 1;
|
||||
const size_t columns = numfunctions + 1;
|
||||
|
||||
const char *const aheaderrow[] = {"x", "y"};
|
||||
// const char* const aheaderrow[] = {"", "x", "y"};
|
||||
|
||||
char **headerrow = NULL;
|
||||
headerrow = new char *[columns];
|
||||
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
{
|
||||
const size_t length = (sizeof aheaderrow / sizeof aheaderrow[0]);
|
||||
|
||||
if (j < (length - 1) or numfunctions == 1)
|
||||
{
|
||||
headerrow[j] = new char[strlen(aheaderrow[j]) + 1];
|
||||
strcpy(headerrow[j], aheaderrow[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ostringstream strm;
|
||||
strm << aheaderrow[length - 1] << j - length + 2;
|
||||
string str = strm.str();
|
||||
headerrow[j] = new char[str.length() + 1];
|
||||
strcpy(headerrow[j], str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
char **headercolumn = NULL;
|
||||
// headercolumn = new char *[rows + 1];
|
||||
|
||||
// for (unsigned int i = 0; i < rows + 1; ++i)
|
||||
// {
|
||||
// ostringstream strm;
|
||||
// strm << i + 1;
|
||||
// string str = strm.str();
|
||||
// headercolumn[i] = new char[str.length() + 1];
|
||||
// strcpy(headercolumn[i], str.c_str());
|
||||
// }
|
||||
|
||||
T **array;
|
||||
array = new T *[rows];
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
array[i] = new T[columns];
|
||||
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
{
|
||||
array[i][0] = (i / xscl) + xmin;
|
||||
|
||||
for (unsigned int j = 0; j < numfunctions; ++j)
|
||||
array[i][j + 1] = (functions[j])(array[i][0]);
|
||||
}
|
||||
|
||||
int code = table(rows, columns, array, headerrow, headercolumn, aoptions);
|
||||
|
||||
if (array != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < rows; ++i)
|
||||
delete[] array[i];
|
||||
|
||||
delete[] array;
|
||||
}
|
||||
|
||||
if (headerrow != NULL)
|
||||
{
|
||||
for (unsigned int j = 0; j < columns; ++j)
|
||||
delete[] headerrow[j];
|
||||
|
||||
delete[] headerrow;
|
||||
}
|
||||
|
||||
// if (headercolumn != NULL)
|
||||
// {
|
||||
// for (unsigned int i = 0; i < rows + 1; ++i)
|
||||
// delete[] headercolumn[i];
|
||||
|
||||
// delete[] headercolumn;
|
||||
// }
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Convert single function to array and output as table
|
||||
template <typename T>
|
||||
int table(const long double xmin, const long double xmax, const long double xscl, T afunction(T), const tableoptions &aoptions)
|
||||
{
|
||||
T(*functions[])
|
||||
(T) = {afunction};
|
||||
|
||||
return table(xmin, xmax, xscl, 1, functions, aoptions);
|
||||
}
|