Added files.

This commit is contained in:
Teal Dulcet 2019-01-04 01:29:55 -08:00
parent 4aca350c08
commit 858dbafac0
30 changed files with 2290 additions and 1 deletions

23
.travis.yml Normal file
View 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
View File

@ -1,2 +1,554 @@
# Tables-and-Graphs
[![Build Status](https://travis-ci.org/tdulcet/Tables-and-Graphs.svg?branch=master)](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.
![](images/char%20array%20to%20table.png)
#### 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;
}
```
![](images/array%20to%20table.png)
#### 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;
}
```
![](images/sorted%20array%20to%20table.png)
#### 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;
}
```
![](images/function%20to%20table.png)
#### 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;
}
```
![](images/multiple%20functions%20to%20table.png)
### 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
![](images/ASCII%20table.png)
1. Basic
![](images/basic%20table.png)
2. Light (default)
![](images/light%20table.png)
3. Heavy
![](images/heavy%20table.png)
4. Double
![](images/double%20table.png)
5. Light Dashed
![](images/light%20dashed%20table.png)
6. Heavy Dashed
![](images/heavy%20dashed%20table.png)
### 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.
![](images/array%20to%20plot.png)
#### 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;
}
```
![](images/function%20to%20graph.png)
#### 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;
}
```
![](images/multiple%20functions%20to%20graph.png)
### 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
![](images/ASCII%20graph.png)
1. Basic
![](images/basic%20graph.png)
2. Light (default)
![](images/light%20graph.png)
3. Heavy
![](images/heavy%20graph.png)
4. Double
![](images/double%20graph.png)
5. Light Dashed
![](images/light%20dashed%20graph.png)
6. Heavy Dashed
![](images/heavy%20dashed%20graph.png)
#### 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
![](images/plot%20colors.png)
##### Graph
![](images/graph%20colors.png)
### 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
images/ASCII table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
images/array to plot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
images/array to table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
images/basic graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
images/basic table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
images/double graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
images/double table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
images/graph colors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
images/heavy graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
images/heavy table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
images/light graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
images/light table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
images/plot colors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

329
tables.cpp Normal file
View 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
View 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);
}