Added unit format options and updated the CI.

This commit is contained in:
Teal Dulcet 2023-05-11 07:03:53 -07:00
parent 3608b85963
commit cb2440f1ab
8 changed files with 482 additions and 156 deletions

View File

@ -36,6 +36,34 @@ jobs:
- name: Clang-Tidy
run: clang-tidy -checks='bugprone-*,-bugprone-easily-swappable-parameters,cert-*,clang-analyzer-*,misc-const-correctness,misc-redundant-expression,misc-unused-*,modernize-*,-modernize-use-trailing-return-type,performance-*,portability-*,readability-const-return-type,readability-container-*,readability-duplicate-include,readability-else-after-return,readability-non-const-parameter,readability-redundant-*,readability-simplify-*,readability-string-compare,readability-use-anyofallof' -header-filter='.*' *.cpp -- -Wall -O3 -std=c++17
Pylint:
name: Pylint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install pylint
- name: Script
run: pylint -f colorized --py-version 3.7 -d design,C0103,W0311,C0301,C0302,C0209 --load-plugins pylint.extensions.code_style,pylint.extensions.emptystring,pylint.extensions.comparetozero,pylint.extensions.comparison_placement,pylint.extensions.for_any_all,pylint.extensions.consider_refactoring_into_while_condition,pylint.extensions.consider_ternary_expression,pylint.extensions.dict_init_mutate,pylint.extensions.docstyle,pylint.extensions.check_elif,pylint.extensions.set_membership,pylint.extensions.typing -e R6104 -r y python/*.py
continue-on-error: true
Ruff:
name: Ruff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install ruff
- name: Script
run: ruff --format=github --target-version py37 --select F,E,W,I,D,UP,YTT,S,BLE,B,A,C4,T10,EM,EXE,ISC,ICN,G,PIE,PYI,Q,RSE,RET,SLF,SIM,TID,TCH,ARG,PGH,PL,RUF --ignore E101,E501,W191,D211,D213,D401,PLR09,PLR2004,RUF003 .
continue-on-error: true
Python:
name: Linux Python

View File

@ -1,7 +1,7 @@
[![Build Status](https://travis-ci.com/tdulcet/Tables-and-Graphs.svg?branch=master)](https://travis-ci.com/tdulcet/Tables-and-Graphs)
[![Actions Status](https://github.com/tdulcet/Table-and-Graph-Libs/workflows/CI/badge.svg?branch=master)](https://github.com/tdulcet/Table-and-Graph-Libs/actions)
# Tables and Graphs
# Table and Graph Libraries
Console Table and Graph/Plot Libraries
@ -759,19 +759,42 @@ Default value: `true`
Requires `axis` to be `true`.
#### Axis ticks
#### Axis tick marks
Option: `axistick`\
Default value: `true`
Requires `axis` and `axislabel` to be `true`.
Requires `axis` to be `true`.
#### Axis units labels
Option: `axisunitslabel`\
Default value: `true`
Requires `axis`, `axislabel` and `axistick` to be `true`.
Requires `axis` and `axistick` to be `true`.
#### X-axis units format
Option: `xunits`\
Values:
1. `units_number`: Locale number format
2. `units_scale_none`: Locale number format with full precision
3. `units_scale_SI`: Auto-scale to the SI standard
4. `units_scale_IEC`: Auto-scale to the IEC standard
5. `units_scale_IEC_I`: Auto-scale to the IEC standard
6. `units_fracts`: Locale number format, but convert fractions and mathematical constants to Unicode characters (default)
6. `units_percent`: Percentage format
7. `units_date`: Locale date format
8. `units_time`: Locale time format
9. `units_monetary`: Locale monetary/currency format
Formats 2-5 are similar to the respective `--to` options with the [numfmt](https://www.gnu.org/software/coreutils/manual/html_node/numfmt-invocation.html) command from GNU Coreutils, but with [more precision](https://github.com/tdulcet/Numbers-Tool#comparison-of---to-option).
#### Y-axis units format
Option: `yunits`\
Values: Same as above.
#### Title

View File

@ -15,6 +15,7 @@
#include <iterator>
#include <numeric>
#include <functional>
#include <chrono>
#include <sys/ioctl.h>
#include <unistd.h>
@ -81,6 +82,24 @@ namespace graphs
const char *const constants[] = {"π", "e"};
const long double constantvalues[] = {M_PI, M_E};
enum units_type
{
units_number,
units_scale_none,
units_scale_SI,
units_scale_IEC,
units_scale_IEC_I,
units_fracts,
units_percent,
units_date,
units_time,
units_monetary
};
enum units_type const units_types[] = {units_number, units_scale_SI, units_scale_IEC, units_scale_IEC_I, units_fracts, units_percent, units_date, units_time, units_monetary};
const char *const suffix_power_char[] = {"", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"};
const long double max_bit = scalbn(1.0L, LDBL_MANT_DIG - 1);
const long double MAX = max_bit + (max_bit - 1);
@ -91,6 +110,8 @@ namespace graphs
bool axislabel = true;
bool axistick = true;
bool axisunitslabel = true;
units_type xunits = units_fracts;
units_type yunits = units_fracts;
const char *title = nullptr;
style_type style = style_light;
color_type color = color_red;
@ -114,7 +135,7 @@ namespace graphs
if (iscntrl(str[i]))
{
cerr << "\nError! Control character in string.\n";
cout << "Control character: " << (int)str[i] << "\n";
cout << "Control character: " << (int)str[i] << '\n';
}
length = mbstowcs(nullptr, str, 0);
@ -200,8 +221,90 @@ namespace graphs
return wrapped;
}
// Auto-scale number to unit
// Adapted from: https://github.com/coreutils/coreutils/blob/master/src/numfmt.c
void outputunit(long double number, const units_type scale, ostringstream &strm)
{
unsigned int x = 0;
long double val = number;
if (val >= -LDBL_MAX and val <= LDBL_MAX)
{
while (abs(val) >= 10)
{
++x;
val /= 10;
}
}
if (scale == units_scale_none)
{
if (x > LDBL_DIG)
return;
strm << setprecision(LDBL_DIG) << number;
return;
}
if (x > 33 - 1)
return;
double scale_base;
switch (scale)
{
case units_scale_IEC:
case units_scale_IEC_I:
scale_base = 1024;
break;
case units_scale_none:
case units_scale_SI:
default:
scale_base = 1000;
break;
}
unsigned int power = 0;
if (number >= -LDBL_MAX and number <= LDBL_MAX)
{
while (abs(number) >= scale_base)
{
++power;
number /= scale_base;
}
}
long double anumber = abs(number);
anumber += anumber < 10 ? 0.0005 : anumber < 100 ? 0.005
: anumber < 1000 ? 0.05
: 0.5;
if (number != 0 and anumber < 1000 and power > 0)
{
strm << setprecision(LDBL_DIG) << number;
string str = strm.str();
const unsigned int length = 5 + (number < 0 ? 1 : 0);
if (str.length() > length)
{
const int prec = anumber < 10 ? 3 : anumber < 100 ? 2
: 1;
strm.str("");
strm << setprecision(prec) << fixed << number;
}
}
else
{
strm << setprecision(0) << fixed << number;
}
strm << (power < graphs::size(suffix_power_char) ? suffix_power_char[power] : "(error)");
if (scale == units_scale_IEC_I and power > 0)
strm << "i";
}
// Convert fractions and constants to Unicode characters
size_t outputfraction(const long double number, ostringstream &strm)
void outputfraction(const long double number, ostringstream &strm)
{
bool output = false;
@ -249,6 +352,49 @@ namespace graphs
if (!output)
strm << number;
}
size_t outputlabel(const long double label, const units_type units, ostringstream &strm)
{
strm.imbue(locale(""));
switch (units)
{
case units_number:
strm << label;
break;
case units_scale_none:
case units_scale_SI:
case units_scale_IEC:
case units_scale_IEC_I:
outputunit(label, units, strm);
break;
case units_fracts:
outputfraction(label, strm);
break;
case units_percent:
strm << label * 100 << '%';
break;
case units_date:
{
// const time_t t = chrono::system_clock::to_time_t(chrono::sys_seconds(chrono::duration_cast<chrono::seconds>(chrono::duration<double>(label))));
const time_t t = chrono::system_clock::to_time_t(chrono::system_clock::time_point(chrono::duration_cast<chrono::seconds>(chrono::duration<long double>(label))));
const tm atm = *localtime(&t);
strm << put_time(&atm, "%x");
break;
}
case units_time:
{
// const time_t t = chrono::system_clock::to_time_t(chrono::sys_seconds(chrono::duration_cast<chrono::seconds>(chrono::duration<double>(label))));
const time_t t = chrono::system_clock::to_time_t(chrono::system_clock::time_point(chrono::duration_cast<chrono::seconds>(chrono::duration<long double>(label))));
const tm atm = *localtime(&t);
strm << put_time(&atm, "%X");
break;
}
case units_monetary:
strm << showbase << put_money(label);
break;
}
size_t length = strcol(strm.str().c_str());
@ -320,7 +466,7 @@ namespace graphs
setlocale(LC_ALL, "");
if (title and title[0] != '\0')
cout << wrap(title, awidth) << "\n";
cout << wrap(title, awidth) << '\n';
if (border)
{
@ -329,7 +475,7 @@ namespace graphs
for (size_t k = 0; k < awidth; ++k)
cout << styles[style][0];
cout << styles[style][4] << "\n";
cout << styles[style][4] << '\n';
}
for (size_t i = 0; i < height; i += 4)
@ -340,7 +486,7 @@ namespace graphs
ostringstream ylabelstrm;
size_t ylabellength = 0;
if (axis and axislabel and axistick and axisunitslabel and yaxis >= 0 and yaxis <= height)
if (axis and axistick and axisunitslabel and yaxis >= 0 and yaxis <= height)
{
bool output = false;
long double label = 0;
@ -358,7 +504,7 @@ namespace graphs
if (output)
{
ylabellength = outputfraction(label, ylabelstrm);
ylabellength = outputlabel(label, aoptions.yunits, ylabelstrm);
ylabellength *= 2;
}
}
@ -392,7 +538,7 @@ namespace graphs
cout << styles[style][10];
output = true;
}
else if (axislabel and axistick)
else if (axistick)
{
int adivisor = i < yaxis ? -ydivisor : ydivisor;
@ -423,7 +569,7 @@ namespace graphs
cout << styles[style][4];
output = true;
}
else if (axislabel and axistick)
else if (axistick)
{
int adivisor = j < xaxis ? -xdivisor : xdivisor;
@ -442,7 +588,7 @@ namespace graphs
output = true;
}
}
else if (yaxislabel and xaxislabel and axislabel and axistick and axisunitslabel and ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0)
else if (yaxislabel and xaxislabel and axistick and axisunitslabel and ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0)
{
cout << "0";
output = true;
@ -452,7 +598,7 @@ namespace graphs
cout << "x";
output = true;
}
else if (yaxislabel and axislabel and axistick and axisunitslabel)
else if (yaxislabel and axistick and axisunitslabel)
{
long double label = 0;
int adivisor = j < xaxis ? -xdivisor : xdivisor;
@ -477,7 +623,7 @@ namespace graphs
output = false;
ostringstream strm;
size_t length = outputfraction(label, strm);
size_t length = outputlabel(label, aoptions.xunits, strm);
length *= 2;
if ((j >= xaxis or (j + length) < (ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0 ? xaxis - 4 : xaxis)) and (j + length) < (width - 2) and (xaxis <= (width - 2) or j > 2))
{
@ -498,7 +644,7 @@ namespace graphs
cout << "y";
output = true;
}
else if (ylabellength and (xaxis < 2 ? xaxislabel : j < (xaxis - ylabellength) and (j + 2) >= (xaxis - ylabellength)) and (yaxis >= 4 or i < (height - 4)) and axislabel and axistick and axisunitslabel)
else if (ylabellength and (xaxis < 2 ? xaxislabel : j < (xaxis - ylabellength) and (j + 2) >= (xaxis - ylabellength)) and (yaxis >= 4 or i < (height - 4)) and axistick and axisunitslabel)
{
cout << ylabelstrm.str();
output = true;
@ -546,7 +692,7 @@ namespace graphs
cout << styles[style][1];
if (i < (height - 4) or border)
cout << "\n";
cout << '\n';
}
if (border)

View File

@ -10,7 +10,13 @@
### Usage
Requires Python 3.6 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`. See the [tables.py](tables.py) file for full usage information.
Requires Python 3.6 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with:
```bash
pip3 install wcwidth
# or
python3 -m pip install wcwidth
```
See the [tables.py](tables.py) file for full usage information.
Complete versions of all of the examples below and more can be found in the [test.py](test.py) file.
@ -231,7 +237,13 @@ Check that the width of the table is not greater then the width of the terminal.
### Usage
Requires Python 3.6 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with: `pip3 install wcwidth`. See the [graphs.py](graphs.py) file for full usage information.
Requires Python 3.6 or greater and the [wcwidth library](https://pypi.org/project/wcwidth/), which users can install with:
```bash
pip3 install wcwidth
# or
python3 -m pip install wcwidth
```
See the [graphs.py](graphs.py) file for full usage information.
Complete versions of all of the examples below and more can be found in the [test.py](test.py) file.
@ -371,19 +383,42 @@ Default value: `True`
Requires `axis` to be `True`.
#### Axis ticks
#### Axis tick marks
Option: `axistick`\
Default value: `True`
Requires `axis` and `axislabel` to be `True`.
Requires `axis` to be `True`.
#### Axis units labels
Option: `axisunitslabel`\
Default value: `True`
Requires `axis`, `axislabel` and `axistick` to be `True`.
Requires `axis` and `axistick` to be `True`.
#### X-axis units format
Option: `xunits`\
Values:
1. `units_types.number`: Locale number format
2. `units_types.scale_none`: Locale number format with full precision
3. `units_types.scale_SI`: Auto-scale to the SI standard
4. `units_types.scale_IEC`: Auto-scale to the IEC standard
5. `units_types.scale_IEC_I`: Auto-scale to the IEC standard
6. `units_types.fracts`: Locale number format, but convert fractions and mathematical constants to Unicode characters (default)
7. `units_types.percent`: Percentage format
8. `units_types.date`: Locale date format
9. `units_types.time`: Locale time format
10. `units_types.monetary`: Locale monetary/currency format
Formats 2-5 are similar to the respective `--to` options with the [numfmt](https://www.gnu.org/software/coreutils/manual/html_node/numfmt-invocation.html) command from GNU Coreutils, but with [more precision](https://github.com/tdulcet/Numbers-Tool#comparison-of---to-option).
#### Y-axis units format
Option: `yunits`\
Values: Same as above.
#### Title

View File

@ -1,20 +1,20 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Teal Dulcet, CS546
from __future__ import division, print_function, unicode_literals
import sys
import locale
import math
import shutil
from fractions import Fraction
import sys
import textwrap
from datetime import datetime, timezone
from enum import Enum, IntEnum, auto
from wcwidth import wcswidth
from typing import List, Tuple, Optional, Sequence, Callable
import locale
from fractions import Fraction
from typing import Callable, List, Optional, Sequence, Tuple
locale.setlocale(locale.LC_ALL, '')
from wcwidth import wcswidth
locale.setlocale(locale.LC_ALL, "")
class style_types(IntEnum):
@ -112,6 +112,22 @@ constants = {
"e": math.e
}
class units_types(Enum):
number = auto()
scale_none = auto()
scale_SI = auto()
scale_IEC = auto()
scale_IEC_I = auto()
fracts = auto()
percent = auto()
date = auto()
time = auto()
monetary = auto()
suffix_power_char = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"]
MAX = sys.float_info.radix ** sys.float_info.mant_dig - 1
@ -127,8 +143,60 @@ def strcol(astr: str) -> int:
# return len(astr)
def outputfraction(number: float) -> Tuple[int, str]:
"""Convert fractions and constants to Unicode characters"""
def outputunit(number: float, scale: units_types) -> str:
x = 0
val = number
if -sys.float_info.max <= val <= sys.float_info.max:
while abs(val) >= 10:
x += 1
val /= 10
if scale == units_types.scale_none:
if x > sys.float_info.dig:
return ""
return f"{number:.{sys.float_info.dig}n}"
if x > 33 - 1:
return ""
if scale in {units_types.scale_IEC, units_types.scale_IEC_I}:
scale_base = 1024
elif scale == units_types.scale_SI:
scale_base = 1000
power = 0
if -sys.float_info.max <= number <= sys.float_info.max:
while abs(number) >= scale_base:
power += 1
number /= scale_base
anumber = abs(number)
anumber += 0.0005 if anumber < 10 else 0.005 if anumber < 100 else 0.05 if anumber < 1000 else 0.5
strm = ""
if number != 0 and anumber < 1000 and power > 0:
strm = f"{number:.{sys.float_info.dig}n}"
length = 5 + (number < 0)
if len(strm) > length:
prec = 3 if anumber < 10 else 2 if anumber < 100 else 1
strm = locale.format_string(f"%.{prec}f", number, grouping=True)
else:
strm = locale.format_string("%.0f", number, grouping=True)
strm += suffix_power_char[power] if power < len(
suffix_power_char) else "(error)"
if scale == units_types.scale_IEC_I and power > 0:
strm += "i"
return strm
def outputfraction(number: float) -> str:
"""Convert fractions and constants to Unicode characters."""
output = False
strm = ""
@ -145,7 +213,7 @@ def outputfraction(number: float) -> Tuple[int, str]:
if intpart == 0 and number < 0:
strm += "-"
elif intpart != 0:
strm += "{0:n}".format(intpart)
strm += f"{intpart:n}"
strm += fraction
@ -160,8 +228,7 @@ def outputfraction(number: float) -> Tuple[int, str]:
if intpart == -1:
strm += "-"
elif intpart != 1:
strm += "{0:.{prec}n}".format(intpart,
prec=sys.float_info.dig)
strm += f"{intpart:.{sys.float_info.dig}n}"
strm += constant
@ -169,7 +236,27 @@ def outputfraction(number: float) -> Tuple[int, str]:
break
if not output:
strm += "{0:n}".format(number)
strm += f"{number:n}"
return strm
def outputlabel(label: float, units: units_types) -> Tuple[int, str]:
"""Outputs a label in a nice, human readable format."""
if units == units_types.number:
strm = f"{label:n}"
elif units in {units_types.scale_none, units_types.scale_SI, units_types.scale_IEC, units_types.scale_IEC_I}:
strm = outputunit(label, units)
elif units == units_types.fracts:
strm = outputfraction(label)
elif units == units_types.percent:
strm = f"{label:%}"
elif units == units_types.date:
strm = datetime.fromtimestamp(label, timezone.utc).strftime("%x")
elif units == units_types.time:
strm = datetime.fromtimestamp(label, timezone.utc).strftime("%X")
elif units == units_types.monetary:
strm = locale.currency(label, grouping=True)
length = strcol(strm)
@ -177,8 +264,8 @@ def outputfraction(number: float) -> Tuple[int, str]:
def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, array: List[List[int]], border: bool = False, axis: bool = True, axislabel: bool = True, axistick: bool = True,
axisunitslabel: bool = True, style: style_types = style_types.light, title: Optional[str] = None, check: bool = True) -> int:
"""Output graph"""
axisunitslabel: bool = True, xunits: units_types = units_types.fracts, yunits: units_types = units_types.fracts, style: style_types = style_types.light, title: Optional[str] = None, check: bool = True) -> int:
"""Output graph."""
if not array:
return 1
@ -195,13 +282,13 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
if check:
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)
print(
f"The height of the graph ({aheight}) is greater then the height of the terminal ({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)
print(
f"The width of the graph ({awidth}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr)
return 1
if xmin >= xmax:
@ -233,29 +320,27 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
i = 0
while i < height:
ayaxis = i <= yaxis and i + 4 > yaxis if yaxis <= height - \
4 else i < yaxis and i + 4 >= yaxis
yaxislabel = i <= yaxis + 4 and i + 4 > yaxis + \
4 if yaxis <= height - 4 else i < yaxis - 4 and i + 4 >= yaxis
ayaxis = i <= yaxis < i + 4 if yaxis <= height - 4 else i < yaxis <= i + 4
yaxislabel = i <= yaxis + 4 < i + 4 if yaxis <= height - 4 else i < yaxis - 4 <= i + 4
ylabelstrm = ""
ylabellength = 0
if axis and axislabel and axistick and axisunitslabel and yaxis >= 0 and yaxis <= height:
if axis and axistick and axisunitslabel and 0 <= yaxis <= height:
output = False
label = 0.0
adivisor = -ydivisor if i < yaxis else ydivisor
k = yaxis + adivisor
while (k >= i if i < yaxis else k < i + 4) and i >= 4 and not output:
if i <= k and i + 4 > k:
if i <= k < i + 4:
label = ymax - (height if k > height else k) * ystep
output = True
k += adivisor
if output:
ylabellength, ylabelstrm = outputfraction(label)
ylabellength, ylabelstrm = outputlabel(label, yunits)
ylabellength *= 2
if border:
@ -263,9 +348,8 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
j = 0
while j < width:
axaxis = j < xaxis and j + 2 >= xaxis if xaxis >= 2 else j <= xaxis and j + 2 > xaxis
xaxislabel = j < xaxis - 2 and j + 2 >= xaxis - \
2 if xaxis >= 2 else j <= xaxis + 2 and j + 2 > xaxis + 2
axaxis = j < xaxis <= j + 2 if xaxis >= 2 else j <= xaxis < j + 2
xaxislabel = j < xaxis - 2 <= j + 2 if xaxis >= 2 else j <= xaxis + 2 < j + 2
output = False
@ -280,12 +364,12 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
elif i >= height - 4:
strm += styles[style][10]
output = True
elif axislabel and axistick:
elif axistick:
adivisor = -ydivisor if i < yaxis else ydivisor
k = yaxis + adivisor
while (k >= i if i < yaxis else k < i + 4) and i >= 4 and not output:
if i <= k and i + 4 > k:
if i <= k < i + 4:
strm += styles[style][7 if xaxis >= 2 else 5]
output = True
k += adivisor
@ -299,12 +383,12 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
elif j >= width - 2:
strm += styles[style][4]
output = True
elif axislabel and axistick:
elif axistick:
adivisor = -xdivisor if j < xaxis else xdivisor
k = xaxis + adivisor
while (k >= j if j < xaxis else k < j + 2) and j < width - 4 and not output:
if j <= k and j + 2 > k:
if j <= k < j + 2:
strm += styles[style][3 if yaxis <=
height - 4 else 9]
output = True
@ -312,13 +396,13 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
if not output:
strm += styles[style][0]
output = True
elif yaxislabel and xaxislabel and axislabel and axistick and axisunitslabel and ymin <= 0 and ymax >= 0 and xmin <= 0 and xmax >= 0:
elif yaxislabel and xaxislabel and axistick and axisunitslabel and ymin <= 0 <= ymax and xmin <= 0 <= xmax:
strm += "0"
output = True
elif (j >= width - 2 if xaxis <= width - 2 else j == 0) and yaxislabel and axislabel:
strm += "x"
output = True
elif yaxislabel and axislabel and axistick and axisunitslabel:
elif yaxislabel and axistick and axisunitslabel:
label = 0.0
adivisor = -xdivisor if j < xaxis else xdivisor
if j < xaxis:
@ -326,7 +410,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
k = xaxis + adivisor
while (k >= j if j < xaxis else k < j + 2) and j < width - 2 and not output:
if j <= k and j + 2 > k:
if j <= k < j + 2:
label = (width if k > width else k) * xstep + xmin
output = True
@ -338,7 +422,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
if output:
output = False
length, astrm = outputfraction(label)
length, astrm = outputlabel(label, xunits)
length *= 2
if (j >= xaxis or j + length < xaxis - 4) and j + length < width - 2:
strm += astrm
@ -353,7 +437,7 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
elif (i == 0 if yaxis >= 4 else i >= height - 4) and xaxislabel and axislabel:
strm += "y"
output = True
elif ylabellength and (xaxislabel if xaxis < 2 else j < xaxis - ylabellength and j + 2 >= xaxis - ylabellength) and (yaxis >= 4 or i < height - 4) and axislabel and axistick and axisunitslabel:
elif ylabellength and (xaxislabel if xaxis < 2 else j < xaxis - ylabellength and j + 2 >= xaxis - ylabellength) and (yaxis >= 4 or i < height - 4) and axistick and axisunitslabel:
strm += ylabelstrm
output = True
if ylabellength > 2:
@ -405,8 +489,8 @@ def graph(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarrays: Sequence[Sequence[Sequence[float]]], border: bool = False, axis: bool = True, axislabel: bool = True, axistick: bool = True, axisunitslabel: bool = True,
style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None, check: bool = True) -> int:
"""Convert one or more arrays to graph and output"""
xunits: units_types = units_types.fracts, yunits: units_types = units_types.fracts, style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None, check: bool = True) -> int:
"""Convert one or more arrays to graph and output."""
if not aarrays:
return 1
@ -427,13 +511,13 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
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)
print(
f"The height of the graph ({aheight}) is greater then the height of the terminal ({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)
print(
f"The width of the graph ({awidth}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr)
return 1
if xmin == 0 and xmax == 0:
@ -474,19 +558,19 @@ def arrays(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax:
aaarray[x][y] = acolor
return graph(height, width, xmin, xmax, ymin, ymax, aaarray, border=border, axis=axis, axislabel=axislabel,
axistick=axistick, axisunitslabel=axisunitslabel, style=style, title=title)
axistick=axistick, axisunitslabel=axisunitslabel, xunits=xunits, yunits=yunits, style=style, title=title)
def array(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, aarray: Sequence[Sequence[float]], border: bool = False, axis: bool = True, axislabel: bool = True, axistick: bool = True,
axisunitslabel: bool = True, style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None) -> int:
"""Convert single array to graph and output"""
axisunitslabel: bool = True, xunits: units_types = units_types.fracts, yunits: units_types = units_types.fracts, style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None) -> int:
"""Convert single array to graph and output."""
return arrays(height, width, xmin, xmax, ymin, ymax, [aarray], border=border, axis=axis, axislabel=axislabel,
axistick=axistick, axisunitslabel=axisunitslabel, style=style, color=color, title=title)
axistick=axistick, axisunitslabel=axisunitslabel, xunits=xunits, yunits=yunits, 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 = False, axis: bool = True, axislabel: bool = True, axistick: bool = True,
axisunitslabel: bool = True, style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None, check: bool = True) -> int:
"""Convert one or more functions to graph and output"""
axisunitslabel: bool = True, xunits: units_types = units_types.fracts, yunits: units_types = units_types.fracts, style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None, check: bool = True) -> int:
"""Convert one or more functions to graph and output."""
if not afunctions:
return 1
@ -503,13 +587,13 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym
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)
print(
f"The height of the graph ({aheight}) is greater then the height of the terminal ({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)
print(
f"The width of the graph ({awidth}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr)
return 1
if xmin >= xmax:
@ -538,7 +622,7 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym
x = i * xstep + xmin
y = function(x)
if x >= xmin and x < xmax and y >= ymin and y < ymax:
if xmin <= x < xmax and ymin <= y < ymax:
ax = int(x / xstep + xaxis)
ay = int(yaxis - y / ystep - 1)
@ -549,11 +633,11 @@ def functions(height: int, width: int, xmin: float, xmax: float, ymin: float, ym
array[ax][ay] = acolor
return graph(height, width, xmin, xmax, ymin, ymax, array, border=border, axis=axis, axislabel=axislabel,
axistick=axistick, axisunitslabel=axisunitslabel, style=style, title=title)
axistick=axistick, axisunitslabel=axisunitslabel, xunits=xunits, yunits=yunits, style=style, title=title)
def function(height, width, xmin: float, xmax: float, ymin: float, ymax: float, afunction: Callable[[float], float], border: bool = False, axis: bool = True, axislabel: bool = True, axistick: bool = True,
axisunitslabel: bool = True, style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None) -> int:
"""Convert single function to function array and output"""
def function(height: int, width: int, xmin: float, xmax: float, ymin: float, ymax: float, afunction: Callable[[float], float], border: bool = False, axis: bool = True, axislabel: bool = True, axistick: bool = True,
axisunitslabel: bool = True, xunits: units_types = units_types.fracts, yunits: units_types = units_types.fracts, style: style_types = style_types.light, color: color_types = color_types.red, title: Optional[str] = None) -> int:
"""Convert single function to function array and output."""
return functions(height, width, xmin, xmax, ymin, ymax, [afunction], border=border, axis=axis, axislabel=axislabel,
axistick=axistick, axisunitslabel=axisunitslabel, style=style, color=color, title=title)
axistick=axistick, axisunitslabel=axisunitslabel, xunits=xunits, yunits=yunits, style=style, color=color, title=title)

View File

@ -1,19 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Teal Dulcet, CS546
from __future__ import division, print_function, unicode_literals
import sys
import shutil
import locale
import re
import shutil
import sys
import textwrap
from enum import IntEnum, auto
from wcwidth import wcswidth
from typing import List, Optional, Any, Sequence, Callable
import locale
from typing import Any, Callable, List, Optional, Sequence
locale.setlocale(locale.LC_ALL, '')
from wcwidth import wcswidth
locale.setlocale(locale.LC_ALL, "")
class style_types(IntEnum):
@ -37,12 +36,12 @@ styles = [
# [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]] #No border
]
ansi = re.compile(r'\x1B\[(?:[0-9]+(?:;[0-9]+)*)?m')
ansi = re.compile(r"\x1B\[(?:[0-9]+(?:;[0-9]+)*)?m")
def strcol(astr: str) -> int:
"""Returns the number of columns that the given string would take up if printed."""
astr = ansi.sub('', astr)
astr = ansi.sub("", astr)
width = wcswidth(astr)
if width == -1:
print(
@ -55,7 +54,7 @@ def strcol(astr: str) -> int:
def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True, cellborder: bool = False,
padding: int = 1, alignment: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light, check: bool = True) -> int:
"""Output char array as table"""
"""Output char array as table."""
if not array:
return 1
@ -75,8 +74,8 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool =
if check:
if width > w.columns:
print("The width of the table ({0}) is greater then the width of the terminal ({1}).".format(
width, w.columns), file=sys.stderr)
print(
f"The width of the table ({width}) is greater then the width of the terminal ({w.columns}).", file=sys.stderr)
return 1
if title:
@ -120,7 +119,7 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool =
strm += " " * padding
if alignment is None:
strm += "{0:{width}}".format(array[i][j], width=awidth)
strm += f"{array[i][j]:{awidth}}"
elif alignment:
strm += array[i][j].rjust(awidth)
else:
@ -134,45 +133,48 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool =
if i < rows - 1 or tableborder:
strm += "\n"
if tableborder:
if i == rows - 1:
strm += styles[style][8]
elif cellborder or (i == 0 and headerrow) or headercolumn:
if (i < rows - 1 and cellborder) or (i == 0 and headerrow) or (i < rows - 1 and headercolumn):
if tableborder and (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):
if cellborder or (i == 0 and headerrow) or (j == 0 and headercolumn):
strm += styles[style][0] * (2 * padding + columnwidth[j])
elif i < rows - 1 and headercolumn:
elif headercolumn:
strm += " " * (2 * padding + columnwidth[j])
if j < columns - 1:
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)):
if 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:
elif headercolumn:
if j == 0:
strm += styles[style][7]
else:
strm += " "
if tableborder:
if i == rows - 1:
strm += styles[style][10]
elif cellborder or (i == 0 and headerrow):
if cellborder or (i == 0 and headerrow):
strm += styles[style][7]
elif headercolumn:
strm += styles[style][1]
if i < rows - 1:
strm += "\n"
strm += "\n"
if tableborder:
strm += styles[style][8]
for j in range(columns):
strm += styles[style][0] * (2 * padding + columnwidth[j])
if j < columns - 1:
if cellborder or (j == 0 and headercolumn):
strm += styles[style][9]
else:
strm += styles[style][0]
strm += styles[style][10]
print(strm)
@ -181,7 +183,7 @@ def table(array: List[List[str]], headerrow: bool = False, headercolumn: bool =
def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]] = None, aheadercolumn: Optional[Sequence[Any]] = None, headerrow: bool = False, headercolumn: bool = False,
tableborder: bool = True, cellborder: bool = False, padding: int = 1, alignment: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light) -> int:
"""Convert array to char array and output as table"""
"""Convert array to char array and output as table."""
if not aarray:
return 1
@ -232,7 +234,7 @@ def array(aarray: Sequence[Sequence[Any]], aheaderrow: Optional[Sequence[Any]] =
def functions(xmin: float, xmax: float, xstep: float, afunctions: Sequence[Callable[[float], float]], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True,
cellborder: bool = False, padding: int = 1, alignment: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light) -> int:
"""Convert one or more functions to array and output as table"""
"""Convert one or more functions to array and output as table."""
if not afunctions:
return 1
@ -271,6 +273,6 @@ def functions(xmin: float, xmax: float, xstep: float, afunctions: Sequence[Calla
def function(xmin: float, xmax: float, xstep: float, afunction: Callable[[float], float], headerrow: bool = False, headercolumn: bool = False, tableborder: bool = True,
cellborder: bool = False, padding: int = 1, alignment: Optional[bool] = None, title: Optional[str] = None, style: style_types = style_types.light) -> int:
"""Convert single function to array and output as table"""
"""Convert single function to array and output as table."""
return functions(xmin, xmax, xstep, [afunction], headerrow=headerrow, headercolumn=headercolumn,
tableborder=tableborder, cellborder=cellborder, padding=padding, alignment=alignment, title=title, style=style)

View File

@ -1,25 +1,24 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Teal Dulcet, CS546
from __future__ import division, print_function, unicode_literals
import math
import random
import sys
import math
import graphs
import tables
def afunction(x):
def afunction(x: float):
return x + 1
def function1(x):
def function1(x: float):
return 2 * x
def function2(x):
def function2(x: float):
return x ** 2
@ -51,7 +50,7 @@ for style in tables.style_types:
headercolumn=True, style=style)
print("\nOutput array as table with separate header row and column\n")
array = [["Data {0:n}".format(i + j) for j in range(4)]
array = [[f"Data {i + j:n}" 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"]

View File

@ -78,7 +78,7 @@ namespace tables
if (iscntrl(str[i]))
{
cerr << "\nError! Control character in string.\n";
cout << "Control character: " << (int)str[i] << "\n";
cout << "Control character: " << (int)str[i] << '\n';
}
length = mbstowcs(nullptr, str, 0);
@ -218,7 +218,7 @@ namespace tables
}
if (title and title[0] != '\0')
cout << wrap(title, width) << "\n";
cout << wrap(title, width) << '\n';
if (aoptions.alignment)
cout << aoptions.alignment;
@ -241,7 +241,7 @@ namespace tables
}
}
cout << styles[style][4] << "\n";
cout << styles[style][4] << '\n';
}
for (size_t i = 0; i < rows; ++i)
@ -254,7 +254,7 @@ namespace tables
if ((j and cellborder) or (i == 0 and j and headerrow) or (j == 1 and headercolumn))
cout << styles[style][1];
else if (j and (tableborder or (i and headerrow) or headercolumn))
cout << " ";
cout << ' ';
const int difference = columnwidth[j] - strcol(array[i][j].c_str());
@ -282,64 +282,73 @@ namespace tables
cout << styles[style][1];
if (i < (rows - 1) or tableborder)
cout << "\n";
cout << '\n';
if (tableborder)
if ((i < (rows - 1) and cellborder) or (i == 0 and headerrow) or (i < (rows - 1) and headercolumn))
{
if (i == (rows - 1))
cout << styles[style][8];
else if (cellborder or (i == 0 and headerrow) or headercolumn)
cout << styles[style][5];
}
if (tableborder)
{
if (cellborder or (i == 0 and headerrow) or headercolumn)
cout << 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 (size_t j = 0; j < columns; ++j)
{
if ((i == (rows - 1) and tableborder) or (i < (rows - 1) and cellborder) or (i == 0 and headerrow) or (i < (rows - 1) and j == 0 and headercolumn))
if (cellborder or (i == 0 and headerrow) or (j == 0 and headercolumn))
for (size_t k = 0; k < (2 * padding) + columnwidth[j]; ++k)
cout << styles[style][0];
else if (i < (rows - 1) and headercolumn)
else if (headercolumn)
cout << string((2 * padding) + columnwidth[j], ' ');
if (j < (columns - 1))
{
if (i == (rows - 1) and tableborder)
{
if (cellborder or (j == 0 and headercolumn))
cout << styles[style][9];
else
cout << styles[style][0];
}
else if ((i < (rows - 1) and cellborder) or ((i == 0 and headerrow) and (j == 0 and headercolumn)))
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 (i < (rows - 1) and headercolumn)
else if (headercolumn)
{
if (j == 0)
cout << styles[style][7];
else
cout << " ";
cout << ' ';
}
}
}
if (tableborder)
{
if (i == (rows - 1))
cout << styles[style][10];
else if (cellborder or (i == 0 and headerrow))
if (cellborder or (i == 0 and headerrow))
cout << styles[style][7];
else if (headercolumn)
cout << styles[style][1];
}
if (i < (rows - 1))
cout << "\n";
cout << '\n';
}
}
if (tableborder)
{
cout << styles[style][8];
for (size_t j = 0; j < columns; ++j)
{
for (size_t k = 0; k < (2 * padding) + columnwidth[j]; ++k)
cout << styles[style][0];
if (j < (columns - 1))
{
if (cellborder or (j == 0 and headercolumn))
cout << styles[style][9];
else
cout << styles[style][0];
}
}
cout << styles[style][10];
}
cout << endl;
return 0;