Added Python port.

This commit is contained in:
Teal Dulcet 2022-02-01 07:20:23 -08:00
parent e3c5ab871c
commit 36f2f057d8
7 changed files with 1195 additions and 61 deletions

View File

@ -3,7 +3,7 @@
# Tables and Graphs # Tables and Graphs
C++ Console Table and Graph/Plot Libraries Console Table and Graph/Plot Libraries
Copyright © 2018 Teal Dulcet Copyright © 2018 Teal Dulcet
@ -526,16 +526,22 @@ When graphing multiple functions, colors `2` - `14` are used inorder. Color `0`
Pull requests welcome! Ideas for contributions: Pull requests welcome! Ideas for contributions:
Both:
* Add more options * Add more options
* Add an option to print a border around graphs/plots * Add an option to print a border around graphs/plots
* Add options to word wrap and truncate long text in table cells * Add options to word wrap and truncate long text in table cells
* Add option to center text in table cells
* Add more examples * Add more examples
* Improve the performance * Improve the performance
* Handle newlines, tabs and formatted text in the tables * Handle newlines and tabs in the tables
* Handle formatted text in the table and graph/plot titles * Handle formatted text in the table and graph/plot titles
* Support more graph/plot colors * Support more graph/plot colors
* Support 24-bit color
* Support combining colors when functions cross * Support combining colors when functions cross
* Support plotting multiple arrays of different sizes
* Update the `-t, --table` options of column command from [util-linux](https://en.wikipedia.org/wiki/Util-linux) to use the Table library * Update the `-t, --table` options of column command from [util-linux](https://en.wikipedia.org/wiki/Util-linux) to use the Table library
* Create a new CLI tool that uses the Graph library * Create a new CLI tool that uses the Graph library
* Port to other languages (C, Java, Rust, etc.) * Port to other languages (C, Java, Rust, etc.)
C++:
* Handle formatted text in the tables
* Support plotting multiple arrays of different sizes

351
python/README.md Normal file
View File

@ -0,0 +1,351 @@
## Tables
### Usage
Requires Python 3.5 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`.
Complete versions of all of the examples below and more can be found in the [test.py](test.py) file.
Run with: `python3 test.py`.
#### Output char array as table
```py
import tables
# Set array
tables.array(array, None, None, headerrow=True, headercolumn=True)
```
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
```py
import tables
headerrow = ["Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"]
headercolumn = ["Header column 2", "Header column 3", "Header column 4", "Header column 5"]
# Set array
tables.array(array, headerrow, headercolumn, headerrow=True, headercolumn=True)
```
Output same as example above.
#### Output array as table
```py
import tables
# Set array
tables.array(array, None, None)
```
![](../images/array%20to%20table.png)
#### Output sorted array as table
```py
import tables
# Set array
sortdimension = 0 # Column to sort by
array = sorted(array, key=lambda x: x[sortdimension])
tables.array(array, None, None)
```
![](../images/sorted%20array%20to%20table.png)
#### Output single function as table
```py
import tables
def afunction(x):
return x + 1
xmin = -10
xmax = 10
xscl = 2
tables.function(xmin, xmax, xscl, afunction, headerrow=True)
```
![](../images/function%20to%20table.png)
#### Output multiple functions as table
```py
import tables
def function1(x):
return 2 * x
def function2(x):
return x ** 2
xmin = -10
xmax = 10
xscl = 2
# Function parameter and return value can be any data type, as long as they are the same
functions = [function1, function2]
tables.functions(xmin, xmax, xscl, functions, headerrow=True)
```
![](../images/multiple%20functions%20to%20table.png)
### Options
#### Header row
Option: `headerrow`\
Default value: `False`
Header rows are bolded, centered and have a border.
#### Header column
Option: `headercolumn`\
Default value: `False`
Header columns are bolded, centered and have a border.
#### Table border
Option: `tableborder`\
Default value: `False`
#### Cell border
Option: `cellborder`\
Default value: `False`
#### Cell padding
Option: `padding`\
Default value: `1`
#### Alignment
Option: `alignment`\
Values:
* `False` (left, default)
* `True` (right)
#### Title
Option: `title`\
Default value: `None`
The title is word wrapped based on the current width of the terminal. 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)
## Graphs/Plots
### Usage
Requires Python 3.5 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`.
Complete versions of all of the examples below and more can be found in the [test.py](test.py) file.
Run with: `python3 test.py`.
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
```py
import graphs
height = 160
width = 160
xmin = -20
xmax = 20
ymin = -20
ymax = 20
# Set array
graphs.array(height, width, xmin, xmax, ymin, ymax, array)
```
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
```py
import graphs
def afunction(x):
return x + 1
height = 160
width = 160
xmin = -20
xmax = 20
ymin = -20
ymax = 20
graphs.function(height, width, xmin, xmax, ymin, ymax, afunction)
```
![](../images/function%20to%20graph.png)
#### Output multiple functions as graph
```py
import graphs
def function1(x):
return 2 * x
def function2(x):
return x ** 2
height = 160
width = 160
xmin = -20
xmax = 20
ymin = -20
ymax = 20
# Function parameter and return value can be any data type, as long as they are the same
functions = [function1, function2]
graphs.functions(height, width, xmin, xmax, ymin, ymax, functions)
```
![](../images/multiple%20functions%20to%20graph.png)
### Options
#### Border/Axis
Option: `border`\
Default value: `False`
#### Axis labels
Option: `axislabel`\
Default value: `False`
Requires `border` to be `False`.
#### Axis units labels
Option: `axisunitslabel`\
Default value: `False`
Requires `border` and `axislabel` to be `False`.
#### Title
Option: `title`\
Default value: `None`
The title is word wrapped based on the current width of the terminal. 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)

458
python/graphs.py Normal file
View File

@ -0,0 +1,458 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Teal Dulcet, CS546
from __future__ import division, print_function, unicode_literals
import sys
import math
import shutil
import textwrap
from wcwidth import wcswidth
from typing import List, Tuple, Optional, Sequence, Callable
import locale
locale.setlocale(locale.LC_ALL, '')
styles = [
["-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # ASCII
["", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # Basic
["", "", "", "", "", "", "", "", "", "", ""], # Light
["", "", "", "", "", "", "", "", "", "", ""], # Heavy
["", "", "", "", "", "", "", "", "", "", ""], # Double
["", "", "", "", "", "", "", "", "", "", ""], # Light Dashed
["", "", "", "", "", "", "", "", "", "", ""] # Heavy Dashed
]
# [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]] #No border
colors = ["\033[39m", "\033[30m", "\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m",
"\033[37m", "\033[90m", "\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m", "\033[96m", "\033[97m"]
dots = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
values = [[0x1, 0x2, 0x4, 0x40], [0x8, 0x10, 0x20, 0x80]]
fractions = {
"¼": 1.0 / 4.0,
"½": 1.0 / 2.0,
"¾": 3.0 / 4.0,
"": 1.0 / 7.0,
"": 1.0 / 9.0,
"": 1.0 / 10.0,
"": 1.0 / 3.0,
"": 2.0 / 3.0,
"": 1.0 / 5.0,
"": 2.0 / 5.0,
"": 3.0 / 5.0,
"": 4.0 / 5.0,
"": 1.0 / 6.0,
"": 5.0 / 6.0,
"": 1.0 / 8.0,
"": 3.0 / 8.0,
"": 5.0 / 8.0,
"": 7.0 / 8.0
}
constants = {
"π": math.pi,
"e": math.e
}
def strcol(str: str) -> int:
"""Returns the number of columns that the given string would take up if printed."""
width = wcswidth(str)
if width == -1:
print("\nError! wcswidth failed. Nonprintable wide character.", file=sys.stderr)
sys.exit(1)
return width
# return len(str)
def outputlabel(label: float) -> Tuple[int, str]:
"""Outputs a label in a nice, human readable format."""
"""Convert fractions and constants to Unicode characters"""
output = False
fractionpart, intpart = math.modf(label)
fractionpart = abs(fractionpart)
strm = ""
for fraction in fractions:
if abs(fractionpart - fractions[fraction]) < sys.float_info.epsilon:
if intpart != 0:
strm += str(intpart)
strm += fraction
output = True
break
if abs(label) >= sys.float_info.epsilon and not output:
for constant in constants:
if not output and label % constants[constant] == 0:
intpart = label / constants[constant]
if intpart == -1:
strm += "-"
elif intpart != 1:
strm += str(intpart)
strm += constant
output = True
break
if not output:
strm += "{0:n}".format(label)
length = strcol(strm)
return length, strm
def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, array: List[List[int]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, title: Optional[str]=None) -> int:
"""Output graph"""
if not array:
return 1
if not (0 >= style > len(styles)):
return 1
if height == 0:
return 1
if width == 0:
return 1
w = shutil.get_terminal_size()
aheight = height // 4
awidth = width // 2
if aheight > w.lines:
print("The height of the graph ({0}) is greater then the height of the terminal ({1}).".format(
aheight, w.lines), file=sys.stderr)
return 1
if awidth > w.columns:
print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format(
awidth, w.columns), file=sys.stderr)
return 1
if xmin >= xmax:
print("xmin must be less than xmax.", file=sys.stderr)
return 1
if ymin >= ymax:
print("ymin must be less than ymax.", file=sys.stderr)
return 1
xscl = width / (xmax - xmin)
yscl = height / (ymax - ymin)
xaxis = width - (xmax * xscl)
yaxis = ymax * yscl
divisor = 2 * 4 * ((width // 160) + 1 if (width / 160.0) > 1 else 1)
if title:
print(textwrap.fill(title, width=w.columns))
strm = ""
i = 0
while i < height:
ayaxis = i <= yaxis and (i + 4) > yaxis
yaxislabel = i <= (yaxis + 4) and (i + 4) > (yaxis + 4)
ylabelstrm = ""
ylabellength = 0
if border and axislabel and axisunitslabel:
output = False
label = 0.0
adivisor = -divisor if i < yaxis else divisor
k = yaxis + adivisor
while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or not axislabel) and not output:
if (i <= k and (i + 4) > k):
label = ymax - (k / yscl)
output = True
k += adivisor
if (output):
ylabellength, ylabelstrm = outputlabel(label)
ylabellength *= 2
j = 0
while j < width:
axaxis = j <= xaxis and (j + 2) > xaxis
xaxislabel = j <= (xaxis - 2) and (j + 2) > (xaxis - 2)
output = False
if border:
if axaxis and ayaxis:
strm += styles[style][6]
output = True
elif axaxis:
if axislabel and axisunitslabel:
adivisor = -divisor if i < yaxis else divisor
k = yaxis + adivisor
while ((i < yaxis and k >= i) or (i > yaxis and k < (i + 4))) and (i >= 4 or not axislabel) and not output:
if i <= k and (i + 4) > k:
strm += styles[style][7]
output = True
k += adivisor
if not output:
if i == 0:
strm += styles[style][4]
elif i >= (height - 4):
strm += styles[style][10]
else:
strm += styles[style][1]
output = True
elif ayaxis:
if axislabel and axisunitslabel:
adivisor = -divisor if j < xaxis else divisor
k = xaxis + adivisor
while ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and (j < (width - 4) or not axislabel) and not output:
if j <= k and (j + 2) > k:
strm += styles[style][3]
output = True
k += adivisor
if not output:
if j == 0:
strm += styles[style][2]
elif j >= (width - 2):
strm += styles[style][4]
else:
strm += styles[style][0]
output = True
elif yaxislabel and xaxislabel and axislabel and axisunitslabel:
strm += "0"
output = True
elif j >= (width - 2) and yaxislabel and axislabel:
strm += "x"
output = True
elif yaxislabel and axislabel and axisunitslabel:
label = 0.0
adivisor = -divisor if j < xaxis else divisor
if j < xaxis:
j += 2
k = xaxis + adivisor
while ((j < xaxis and k >= j) or (j > xaxis and k < (j + 2))) and j < (width - 2) and not output:
if j <= k and (j + 2) > k:
label = (k / xscl) + xmin
output = True
k += adivisor
if adivisor < 0:
j -= 2
if output:
output = False
length, astrm = outputlabel(label)
length *= 2
if (j >= xaxis or (j + length) < (xaxis - 4)) and (j + length) < (width - 2):
strm += astrm
if length > 2:
j += length - 2
if adivisor < 0:
output = True
else:
j += 2
elif i == 0 and xaxislabel and axislabel:
strm += "y"
output = True
elif (j <= (xaxis - ylabellength) and (j + 2) > (xaxis - ylabellength)) and axislabel and axisunitslabel:
strm += ylabelstrm
output = True
if ylabellength > 2:
j += ylabellength - 2
if not output:
dot = 0
color = 0
for k in range(min(2, width - j)):
for l in range(min(4, height - i)):
dot += (1 if array[j + k][i + l] else 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 -= 1
if color:
strm += colors[color]
strm += "\033[1m" + dots[dot] + "\033[22m"
if color:
strm += colors[0]
j += 2
strm += "\n"
i += 4
print(strm)
return 0
def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarrays: Sequence[Sequence[Sequence[float]]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int:
"""Convert one or more arrays to graph and output"""
if not aarrays:
return 1
if not all(len(x) == 2 for aarray in aarrays for x in aarray):
print("Error: The arrays must have two columns.")
return 1
if color < 0 or color >= len(colors):
return 1
w = shutil.get_terminal_size()
aheight = height // 4
awidth = width // 2
if aheight > w.lines:
print("The height of the graph ({0}) is greater then the height of the terminal ({1}).".format(
aheight, w.lines), file=sys.stderr)
return 1
if awidth > w.columns:
print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format(
awidth, w.columns), file=sys.stderr)
return 1
if xmin == 0 and xmax == 0:
xmin = min(i[0] for aarray in aarrays for i in aarray)
xmax = max(i[0] for aarray in aarrays for i in aarray)
if ymin == 0 and ymax == 0:
ymin = min(i[1] for aarray in aarrays for i in aarray)
ymax = max(i[1] for aarray in aarrays for i in aarray)
if xmin >= xmax:
print("xmin must be less than xmax.", file=sys.stderr)
return 1
if ymin >= ymax:
print("ymin must be less than ymax.", file=sys.stderr)
return 1
xscl = width / (xmax - xmin)
yscl = height / (ymax - ymin)
xaxis = width - (xmax * xscl)
yaxis = ymax * yscl
aaarray = [[0 for j in range(height)] for i in range(width)]
for j, aarray in enumerate(aarrays):
acolor = color + 1 if len(aarrays) == 1 else (j % (len(colors) - 2)) + 3
for i in aarray:
if i[0] >= xmin and i[0] < xmax and i[1] >= ymin and i[1] < ymax:
x = int((i[0] * xscl) + xaxis)
y = int((yaxis - (i[1] * yscl)) - 1)
if aaarray[x][y]:
if aaarray[x][y] != acolor:
aaarray[x][y] = 1
else:
aaarray[x][y] = acolor
return graph(height, width, xmin, xmax, ymin, ymax, aaarray, border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, title=title)
def array(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarray: Sequence[Sequence[float]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int:
"""Convert single array to graph and output"""
return arrays(height, width, xmin, xmax, ymin, ymax, [aarray], border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, color=color, title=title)
def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, afunctions: Sequence[Callable[[float], float]], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int:
"""Convert one or more functions to graph and output"""
if color < 0 or color >= len(colors):
return 1
if len(afunctions) == 0:
return 1
w = shutil.get_terminal_size()
if height == 0:
height = w.lines * 4
if width == 0:
width = w.columns * 2
aheight = height / 4
awidth = width / 2
if aheight > w.lines:
print("The height of the graph ({0}) is greater then the height of the terminal ({1}).".format(
aheight, w.lines), file=sys.stderr)
return 1
if awidth > w.columns:
print("The width of the graph ({0}) is greater then the width of the terminal ({1}).".format(
awidth, w.columns), file=sys.stderr)
return 1
if xmin >= xmax:
print("xmin must be less than xmax.", file=sys.stderr)
return 1
if ymin >= ymax:
print("ymin must be less than ymax.", file=sys.stderr)
return 1
rows = width
xscl = width / (xmax - xmin)
yscl = height / (ymax - ymin)
xaxis = width - (xmax * xscl)
yaxis = ymax * yscl
array = [[0 for j in range(height)] for i in range(width)]
for j, function in enumerate(afunctions):
acolor = color + 1 if len(afunctions) == 1 else (j %
(len(colors) - 2)) + 3
for i in (x / 2 for x in range(rows * 2)):
x = (i / xscl) + xmin
y = function(x)
if x >= xmin and x < xmax and y >= ymin and y < ymax:
ax = int((x * xscl) + xaxis)
ay = int((yaxis - (y * yscl)) - 1)
if array[ax][ay]:
if array[ax][ay] != acolor:
array[ax][ay] = 1
else:
array[ax][ay] = acolor
return graph(height, width, xmin, xmax, ymin, ymax, array, border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, title=title)
def function(height, width, xmin: float, xmax: float, ymin: float, ymax: float, afunction: Callable[[float], float], border: bool=True, axislabel: bool=True, axisunitslabel: bool=True, style: int=2, color: int=2, title: Optional[str]=None) -> int:
"""Convert single function to function array and output"""
return functions(height, width, xmin, xmax, ymin, ymax, [afunction], border=border, axislabel=axislabel, axisunitslabel=axisunitslabel, style=style, color=color, title=title)

263
python/tables.py Normal file
View File

@ -0,0 +1,263 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Teal Dulcet, CS546
from __future__ import division, print_function, unicode_literals
import sys
import shutil
import re
import textwrap
from wcwidth import wcswidth
from typing import List, Optional, Any, Sequence, Callable
import locale
locale.setlocale(locale.LC_ALL, '')
styles = [
["-", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # ASCII
["", "|", "+", "+", "+", "+", "+", "+", "+", "+", "+"], # Basic
["", "", "", "", "", "", "", "", "", "", ""], # Light
["", "", "", "", "", "", "", "", "", "", ""], # Heavy
["", "", "", "", "", "", "", "", "", "", ""], # Double
["", "", "", "", "", "", "", "", "", "", ""], # Light Dashed
["", "", "", "", "", "", "", "", "", "", ""] # Heavy Dashed
]
# [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]] #No border
ansi = re.compile(r'\x1B\[(?:[0-9]+(?:;[0-9]+)*)?m')
def strcol(str: str) -> int:
"""Returns the number of columns that the given string would take up if printed."""
str = ansi.sub('', str)
width = wcswidth(str)
if width == -1:
print("\nError! wcswidth failed. Nonprintable wide character.", file=sys.stderr)
sys.exit(1)
return width
# return len(str)
def table(array: List[List[str]], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int:
"""Output char array as table"""
if not array:
return 1
if not (0 >= style > len(styles)):
return 1
rows = len(array)
columns = len(array[0])
columnwidth = [max(strcol(i) for i in j) for j in zip(*array)]
width = sum(columnwidth)
w = shutil.get_terminal_size()
if tableborder or cellborder or headerrow or headercolumn:
width += (((2 * padding) + 1) * columns) + (1 if tableborder else -1)
else:
width += (2 * padding) * columns
if width > w.columns:
print("The width of the table ({0}) is greater then the width of the terminal ({1}).".format(
width, w.columns), file=sys.stderr)
return 1
if title:
print(textwrap.fill(title, width=w.columns))
strm = ""
if tableborder:
strm += styles[style][2]
for j in range(columns):
strm += styles[style][0] * ((2 * padding) + columnwidth[j])
if j == (columns - 1):
strm += styles[style][4] + "\n"
elif cellborder or headerrow or (j == 0 and headercolumn):
strm += styles[style][3]
else:
strm += styles[style][0]
for i in range(rows):
for j in range(columns):
if (j == 0 and tableborder) or (j > 0 and cellborder) or (i == 0 and j > 0 and headerrow) or (j == 1 and headercolumn):
strm += styles[style][1]
elif tableborder or (i > 0 and j > 0 and headerrow) or (j > 1 and headercolumn):
strm += " "
awidth = columnwidth[j] - (strcol(array[i][j]) - len(array[i][j]))
if (i == 0 and headerrow) or (j == 0 and headercolumn):
strm += " " * padding
strm += "\033[1m" + array[i][j].center(awidth) + "\033[22m"
strm += " " * padding
else:
strm += " " * padding
if alignment:
strm += array[i][j].rjust(awidth)
else:
strm += array[i][j].ljust(awidth)
strm += " " * padding
if tableborder:
strm += styles[style][1]
strm += "\n"
if tableborder:
if i == (rows - 1):
strm += styles[style][8]
elif cellborder or (i == 0 and headerrow) or headercolumn:
strm += styles[style][5]
if (i == (rows - 1) and tableborder) or (i < (rows - 1) and cellborder) or (i == 0 and headerrow) or (i < (rows - 1) and headercolumn):
for j in range(columns):
if (i == (rows - 1) and tableborder) or (i < (rows - 1) and cellborder) or (i == 0 and headerrow) or (i < (rows - 1) and j == 0 and headercolumn):
strm += styles[style][0] * ((2 * padding) + columnwidth[j])
elif i < (rows - 1) and headercolumn:
strm += " " * ((2 * padding) + columnwidth[j])
if j == (columns - 1):
if tableborder:
if i == (rows - 1):
strm += styles[style][10]
elif cellborder or (i == 0 and headerrow):
strm += styles[style][7]
elif headercolumn:
strm += styles[style][1]
strm += "\n"
else:
if i == (rows - 1) and tableborder:
if cellborder or (j == 0 and headercolumn):
strm += styles[style][9]
else:
strm += styles[style][0]
elif (i < (rows - 1) and cellborder) or ((i == 0 and headerrow) and (j == 0 and headercolumn)):
strm += styles[style][6]
elif i == 0 and headerrow:
strm += styles[style][9]
elif i < (rows - 1) and headercolumn:
if j == 0:
strm += styles[style][7]
else:
strm += " "
print(strm)
return 0
def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]], aheadercolumn: Optional[Sequence[Any]], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int:
"""Convert array to char array and output as table"""
if not aarray:
return 1
rows = len(aarray)
columns = len(aarray[0])
if not all(len(x) == columns for x in aarray):
print("Error: The rows of the array must have the same number of columns.", file=sys.stderr)
return 1
if aheaderrow:
rows += 1
if aheadercolumn:
columns += 1
if aheaderrow and len(aheaderrow) != columns:
print("Error: The header row must have the same number of columns as the array.", file=sys.stderr)
return 1
if aheadercolumn and len(aheadercolumn) != (rows - 1 if aheaderrow else rows):
print("Error: The header column must have the same number of rows as the array.", file=sys.stderr)
return 1
aaarray = [["" for j in range(columns)] for i in range(rows)]
i = 0
if aheaderrow:
for j in range(columns):
aaarray[i][j] = aheaderrow[j]
i += 1
j = 0
ii = 0
for i in range(i, rows):
if aheadercolumn:
aii = i
if aheaderrow:
aii -= 1
aaarray[i][j] = aheadercolumn[aii]
j += 1
jj = 0
for j in range(j, columns):
aaarray[i][j] = str(aarray[ii][jj])
jj += 1
j = 0
ii += 1
return table(aaarray, headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style)
def functions(xmin: float, xmax: float, xscl: float, afunctions: Sequence[Callable[[float], float]], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int:
"""Convert one or more functions to array and output as table"""
if len(afunctions) == 0:
return 1
if xmin >= xmax:
print("xmin must be less than xmax.", file=sys.stderr)
return 1
if xscl <= 0:
print("xscl must be greater than zero.", file=sys.stderr)
return 1
rows = int(((xmax - xmin) * xscl)) + 1
columns = len(afunctions) + 1
aaheaderrow = ["x", "y"]
length = len(aaheaderrow)
aheaderrow = [""] * columns
for j in range(columns):
if j < (length - 1) or len(afunctions) == 1:
aheaderrow[j] = aaheaderrow[j]
else:
aheaderrow[j] = aaheaderrow[length - 1] + str(j - length + 2)
aarray = [[0 for j in range(columns)] for i in range(rows)]
for i in range(rows):
aarray[i][0] = (i / xscl) + xmin
for j, function in enumerate(afunctions):
aarray[i][j + 1] = function(aarray[i][0])
return array(aarray, aheaderrow, None, headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style)
def function(xmin: float, xmax: float, xscl: float, afunction: Callable[[float], float], headerrow: bool=False, headercolumn: bool=False, tableborder: bool=True, cellborder: bool=False, padding: int=1, alignment: bool=False, title: Optional[str]=None, style: int=2) -> int:
"""Convert single function to array and output as table"""
return functions(xmin, xmax, xscl, [afunction], headerrow=headerrow, headercolumn=headercolumn, tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style)

111
python/test.py Normal file
View File

@ -0,0 +1,111 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Teal Dulcet, CS546
from __future__ import division, print_function, unicode_literals
import random
import sys
import math
import graphs
import tables
def afunction(x): return x + 1
def function1(x): return 2 * x
def function2(x): return x ** 2
rows = 5
columns = 5
xmin = -10
xmax = 10
xscl = 2
print("\nOutput array as table\n")
array = [[random.randint(0, sys.maxsize)
for j in range(columns)] for i in range(rows)]
for k in range(len(tables.styles)):
tables.array(array, None, None, style=k)
array = [[random.random() for j in range(columns)] for i in range(rows)]
for k in range(len(tables.styles)):
tables.array(array, None, None, style=k)
print("\nOutput char array as table\n")
array = [["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"]]
for k in range(len(tables.styles)):
tables.array(array, None, None, headerrow=True, headercolumn=True, style=k)
print("\nOutput array as table with separate header row and column\n")
array = [["Data {0:n}".format(i + j) for j in range(4)]
for i in range(1, 4 * 4 + 1, 4)]
headerrow = ["Header row/column 1", "Header row 2",
"Header row 3", "Header row 4", "Header row 5"]
headercolumn = ["Header column 2", "Header column 3",
"Header column 4", "Header column 5"]
for k in range(len(tables.styles)):
tables.array(array, headerrow, headercolumn, headerrow=True, headercolumn=True, cellborder=True, style=k)
tables.array(array, headerrow, headercolumn, headerrow=True, headercolumn=True, style=k)
tables.array(array, headerrow[:-1], None, headerrow=True, style=k)
tables.array(array, None, [headerrow[0]] + headercolumn[:-1], headercolumn=True, style=k)
tables.array(array, None, None, cellborder=True, style=k)
tables.array(array, None, None, tableborder=False, style=k)
tables.array(array, headerrow, headercolumn, tableborder=False, headerrow=True, headercolumn=True, style=k)
tables.array(array, headerrow[:-1], None, tableborder=False, headerrow=True, style=k)
tables.array(array, None, [headerrow[0]] + headercolumn[:-1], tableborder=False, headercolumn=True, style=k)
tables.array(array, None, None, tableborder=False, cellborder=True, style=k)
array = [[bool(random.getrandbits(1)) for j in range(columns)]
for i in range(rows)]
for k in range(len(tables.styles)):
tables.array(array, None, None, style=k)
print("\nOutput sorted array as table\n")
array = ([random.randint(0, sys.maxsize)
for j in range(columns)] for i in range(rows))
sortdimension = 0
array = sorted(array, key=lambda x: x[sortdimension])
for k in range(len(tables.styles)):
tables.array(array, None, None, style=k)
print("\nOutput single function as table\n")
for k in range(len(tables.styles)):
tables.function(xmin, xmax, xscl, afunction, headerrow=True, style=k)
print("\nOutput multiple functions as table\n")
for k in range(len(tables.styles)):
tables.functions(xmin, xmax, xscl, [
function1, function2], headerrow=True, style=k)
height = 160
width = 160
xmin = -20
xmax = 20
ymin = -20
ymax = 20
print("\nOutput array as plot\n")
array = [[i + j for j in range(2)] for i in range(10)]
for k in range(len(graphs.styles)):
graphs.array(height, width, xmin, xmax, ymin, ymax, array, style=k)
print("\nOutput single function as graph\n")
for k in range(len(graphs.styles)):
graphs.function(height, width, xmin, xmax, ymin, ymax, afunction, style=k)
print("\nOutput multiple functions as graph\n")
for k in range(len(graphs.styles)):
graphs.functions(height, width, xmin, xmax, ymin,
ymax, [function1, function2], style=k)
for k in range(len(graphs.styles)):
graphs.functions(height, width, -(2 * math.pi), 2 *
math.pi, -4, 4, [math.sin, math.cos, math.tan], style=k)

View File

@ -136,27 +136,13 @@ int main()
// Output char array as table // Output char array as table
cout << "\nOutput char array as table\n\n"; cout << "\nOutput char array as table\n\n";
{ {
const char *const aarray[rows][columns] = { const char *const array[rows][columns] = {
{"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"}, {"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 2", "Data 1", "Data 2", "Data 3", "Data 4"},
{"Header column 3", "Data 5", "Data 6", "Data 7", "Data 8"}, {"Header column 3", "Data 5", "Data 6", "Data 7", "Data 8"},
{"Header column 4", "Data 9", "Data 10", "Data 11", "Data 12"}, {"Header column 4", "Data 9", "Data 10", "Data 11", "Data 12"},
{"Header column 5", "Data 13", "Data 14", "Data 15", "Data 16"}}; {"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; tableoptions aoptions;
aoptions.headerrow = true; aoptions.headerrow = true;
aoptions.headercolumn = true; aoptions.headercolumn = true;
@ -167,18 +153,6 @@ int main()
table(rows, columns, array, NULL, NULL, aoptions); 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 // Output array as table with separate header row and column
cout << "\nOutput array as table with separate header row and column\n\n"; cout << "\nOutput array as table with separate header row and column\n\n";
@ -186,7 +160,7 @@ int main()
const size_t rows = 4; const size_t rows = 4;
const size_t columns = 4; const size_t columns = 4;
const char *const aarray[rows][columns] = { const char *const array[rows][columns] = {
{"Data 1", "Data 2", "Data 3", "Data 4"}, {"Data 1", "Data 2", "Data 3", "Data 4"},
{"Data 5", "Data 6", "Data 7", "Data 8"}, {"Data 5", "Data 6", "Data 7", "Data 8"},
{"Data 9", "Data 10", "Data 11", "Data 12"}, {"Data 9", "Data 10", "Data 11", "Data 12"},
@ -195,20 +169,6 @@ int main()
const char *const headerrow[] = {"Header row/column 1", "Header row 2", "Header row 3", "Header row 4", "Header row 5"}; 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"}; 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; tableoptions aoptions;
aoptions.headerrow = true; aoptions.headerrow = true;
aoptions.headercolumn = true; aoptions.headercolumn = true;
@ -219,18 +179,6 @@ int main()
table(rows, columns, array, headerrow, headercolumn, aoptions); 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; bool **array;

View File

@ -330,11 +330,8 @@ int table(const size_t rows, const size_t columns, char ***array, const tableopt
// Convert array to char array and output as table // Convert array to char array and output as table
template <typename T> template <typename T>
int table(size_t rows, size_t columns, T **array, const char *const headerrow[], const char *const headercolumn[], const tableoptions &aoptions) int table(size_t rows, size_t columns, const T &array, const char *const headerrow[], const char *const headercolumn[], const tableoptions &aoptions)
{ {
if (array == NULL)
return 1;
unsigned int i = 0; unsigned int i = 0;
unsigned int j = 0; unsigned int j = 0;