Autism (Ascii trees)

making ascii trees because fun
v1
Brett 2023-11-29 21:36:22 -05:00
parent d589056a8d
commit da32f9b98a
3 changed files with 339 additions and 63 deletions

View File

@ -13,40 +13,66 @@
#include <blt/math/math.h> #include <blt/math/math.h>
#include <algorithm> #include <algorithm>
namespace blt::string { namespace blt::string
{
template<typename T> template<typename T>
static inline std::string withGrouping(T t, size_t group = 3){ static inline std::string withGrouping(T t, size_t group = 3)
{
// TODO: all this + make it faster // TODO: all this + make it faster
static_assert(std::is_integral_v<T>, "Must be integer type! (Floats currently not supported!)"); static_assert(std::is_integral_v<T>, "Must be integer type! (Floats currently not supported!)");
auto str = std::to_string(t); auto str = std::to_string(t);
std::string ret; std::string ret;
ret.reserve(str.size()); ret.reserve(str.size());
size_t count = 0; size_t count = 0;
for (int64_t i = str.size() - 1; i >= 0; i--){ for (int64_t i = str.size() - 1; i >= 0; i--)
{
ret += str[i]; ret += str[i];
if (count++ % (group) == group-1 && i != 0) if (count++ % (group) == group - 1 && i != 0)
ret += ','; ret += ',';
} }
std::reverse(ret.begin(), ret.end()); std::reverse(ret.begin(), ret.end());
return ret; return ret;
} }
static inline std::string fromBytes(unsigned long bytes){ static inline std::string fromBytes(unsigned long bytes)
if (bytes > 1073741824) { {
if (bytes > 1073741824)
{
// gigabyte // gigabyte
return std::to_string(round_up<3>((double) bytes / 1024.0 / 1024.0 / 1024.0)) += "gb"; return std::to_string(round_up<3>((double) bytes / 1024.0 / 1024.0 / 1024.0)) += "gb";
} else if (bytes > 1048576) { } else if (bytes > 1048576)
{
// megabyte // megabyte
return std::to_string(round_up<3>((double)bytes / 1024.0 / 1024.0)) += "mb"; return std::to_string(round_up<3>((double) bytes / 1024.0 / 1024.0)) += "mb";
} else if (bytes > 1024) { } else if (bytes > 1024)
{
// kilobyte // kilobyte
return std::to_string(round_up<3>((double)bytes / 1024.0)) += "kb"; return std::to_string(round_up<3>((double) bytes / 1024.0)) += "kb";
} else { } else
{
return std::to_string(bytes) += "b"; return std::to_string(bytes) += "b";
} }
} }
// TODO: update table formatter to use these!
/**
* creates a line starting/ending with ending char filled between with spacing char
* @param totalLength total length to generate
* @param endingChar beginning and ending char to use
* @param spacingChar char to use for spacing
* @return a generated line string eg: +--------+
*/
std::string createLine(size_t totalLength, char endingChar, char spacingChar);
/**
* Create a padding string using length and spacing char
* @param length length of string to generate
* @param spacing char to use to generate padding
* @return a padding string
*/
std::string createPadding(size_t length, char spacing = ' ');
// TODO template the padding functions: // TODO template the padding functions:
/** /**
@ -55,13 +81,13 @@ namespace blt::string {
* @param expectedLength expected length of the string. * @param expectedLength expected length of the string.
* @return a space padded string * @return a space padded string
*/ */
static inline std::string postPadWithSpaces(const std::string& str, size_t expectedLength) { static inline std::string postPadWithSpaces(const std::string& str, size_t expectedLength)
{
auto currentSize = (int) (str.length() - 1); auto currentSize = (int) (str.length() - 1);
if ((int) expectedLength - currentSize <= 0) if ((int) expectedLength - currentSize <= 0)
return str; return str;
auto paddedString = str; auto paddedString = str;
for (size_t i = 0; i < expectedLength - currentSize; i++) paddedString += createPadding(expectedLength - currentSize);
paddedString += " ";
return paddedString; return paddedString;
} }
@ -71,26 +97,29 @@ namespace blt::string {
* @param expectedLength expected length of the string. * @param expectedLength expected length of the string.
* @return a space padded string * @return a space padded string
*/ */
static inline std::string prePadWithSpaces(const std::string& str, size_t expectedLength) { static inline std::string prePadWithSpaces(const std::string& str, size_t expectedLength)
{
auto currentSize = str.length() - 1; auto currentSize = str.length() - 1;
auto paddedString = std::string(); auto paddedString = std::string();
for (unsigned int i = 0; i < expectedLength - currentSize; i++) paddedString += createPadding(expectedLength - currentSize);
paddedString += " ";
paddedString += str; paddedString += str;
return paddedString; return paddedString;
} }
struct utf8_string { struct utf8_string
{
char* characters; char* characters;
unsigned int size; unsigned int size;
}; };
// taken from java, adapted for c++. // taken from java, adapted for c++.
static inline utf8_string createUTFString(const std::string& str) { static inline utf8_string createUTFString(const std::string& str)
{
const auto strlen = (unsigned int) str.size(); const auto strlen = (unsigned int) str.size();
unsigned int utflen = strlen; unsigned int utflen = strlen;
for (unsigned int i = 0; i < strlen; i++) { for (unsigned int i = 0; i < strlen; i++)
{
unsigned char c = str[i]; unsigned char c = str[i];
if (c >= 0x80 || c == 0) if (c >= 0x80 || c == 0)
utflen += 1; utflen += 1;
@ -108,21 +137,26 @@ namespace blt::string {
chars.characters[count++] = (char) ((utflen >> 0) & 0xFF); chars.characters[count++] = (char) ((utflen >> 0) & 0xFF);
unsigned int i = 0; unsigned int i = 0;
for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII for (i = 0; i < strlen; i++)
{ // optimized for initial run of ASCII
int c = (unsigned char) str[i]; int c = (unsigned char) str[i];
if (c >= 0x80 || c == 0) break; if (c >= 0x80 || c == 0) break;
chars.characters[count++] = (char) c; chars.characters[count++] = (char) c;
} }
for (; i < strlen; i++) { for (; i < strlen; i++)
{
int c = (unsigned char) str[i]; int c = (unsigned char) str[i];
if (c < 0x80 && c != 0) { if (c < 0x80 && c != 0)
{
chars.characters[count++] = (char) c; chars.characters[count++] = (char) c;
} else if (c >= 0x800) { } else if (c >= 0x800)
{
chars.characters[count++] = (char) (0xE0 | ((c >> 12) & 0x0F)); chars.characters[count++] = (char) (0xE0 | ((c >> 12) & 0x0F));
chars.characters[count++] = (char) (0x80 | ((c >> 6) & 0x3F)); chars.characters[count++] = (char) (0x80 | ((c >> 6) & 0x3F));
chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F)); chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F));
} else { } else
{
chars.characters[count++] = (char) (0xC0 | ((c >> 6) & 0x1F)); chars.characters[count++] = (char) (0xC0 | ((c >> 6) & 0x1F));
chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F)); chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F));
} }
@ -130,7 +164,8 @@ namespace blt::string {
return chars; return chars;
} }
static inline std::string getStringFromUTF8(const utf8_string& str) { static inline std::string getStringFromUTF8(const utf8_string& str)
{
int utflen = (int) str.size; int utflen = (int) str.size;
int c, char2, char3; int c, char2, char3;
int count = 0; int count = 0;
@ -138,16 +173,19 @@ namespace blt::string {
auto chararr = new char[utflen + 1]; auto chararr = new char[utflen + 1];
while (count < utflen) { while (count < utflen)
{
c = (int) str.characters[count] & 0xff; c = (int) str.characters[count] & 0xff;
if (c > 127) break; if (c > 127) break;
count++; count++;
chararr[chararr_count++] = (char) c; chararr[chararr_count++] = (char) c;
} }
while (count < utflen) { while (count < utflen)
{
c = (int) str.characters[count] & 0xff; c = (int) str.characters[count] & 0xff;
switch (c >> 4) { switch (c >> 4)
{
case 0: case 0:
case 1: case 1:
case 2: case 2:
@ -192,23 +230,27 @@ namespace blt::string {
} }
} }
chararr[utflen] = '\0'; chararr[utflen] = '\0';
std::string strs {chararr}; std::string strs{chararr};
delete[] chararr; delete[] chararr;
return strs; return strs;
} }
struct TableColumn { struct TableColumn
{
std::string columnName; std::string columnName;
size_t maxColumnLength = 0; size_t maxColumnLength = 0;
TableColumn(std::string columnName): columnName(std::move(columnName)) {} TableColumn(std::string columnName): columnName(std::move(columnName))
{}
}; };
struct TableRow { struct TableRow
{
std::vector<std::string> rowValues; std::vector<std::string> rowValues;
}; };
class TableFormatter { class TableFormatter
{
private: private:
std::string m_tableName; std::string m_tableName;
int m_columnPadding; int m_columnPadding;
@ -224,19 +266,23 @@ namespace blt::string {
void updateMaxColumnLengths(); void updateMaxColumnLengths();
[[nodiscard]] inline size_t columnSize(const TableColumn& column) const { [[nodiscard]] inline size_t columnSize(const TableColumn& column) const
{
return column.columnName.size() + m_columnPadding * 2; return column.columnName.size() + m_columnPadding * 2;
} }
public: public:
explicit TableFormatter(std::string tableName = "", int columnPadding = 2, int maxColumnWidth = 500): explicit TableFormatter(std::string tableName = "", int columnPadding = 2, int maxColumnWidth = 500):
m_tableName(std::move(tableName)), m_columnPadding(columnPadding), m_maxColumnWidth(maxColumnWidth) {} m_tableName(std::move(tableName)), m_columnPadding(columnPadding), m_maxColumnWidth(maxColumnWidth)
{}
inline void addColumn(const TableColumn& column) { inline void addColumn(const TableColumn& column)
{
columns.push_back(column); columns.push_back(column);
} }
inline void addRow(TableRow row) { inline void addRow(TableRow row)
{
if (row.rowValues.size() > columns.size()) if (row.rowValues.size() > columns.size())
throw "Cannot insert more rows than columns!\n"; throw "Cannot insert more rows than columns!\n";
// ensure every row populates every column. This is important as the table generator assumes that all rows are complete! // ensure every row populates every column. This is important as the table generator assumes that all rows are complete!
@ -246,7 +292,8 @@ namespace blt::string {
rows.push_back(std::move(row)); rows.push_back(std::move(row));
} }
inline void addRow(const std::initializer_list<std::string>& values) { inline void addRow(const std::initializer_list<std::string>& values)
{
TableRow row; TableRow row;
for (const auto& value : values) for (const auto& value : values)
row.rowValues.push_back(value); row.rowValues.push_back(value);
@ -256,5 +303,65 @@ namespace blt::string {
std::vector<std::string> createTable(bool top = false, bool bottom = false); std::vector<std::string> createTable(bool top = false, bool bottom = false);
}; };
class TreeFormatter
{
public:
struct Node
{
std::string data;
Node* left = nullptr;
Node* right = nullptr;
explicit Node(std::string data): data(std::move(data))
{}
Node* with(Node* l, Node* r = nullptr)
{
left = l;
right = r;
return this;
}
~Node()
{
delete left;
delete right;
}
};
private:
// data classes
struct TreeFormat
{
int verticalSpacing;
int horizontalSpacing;
int verticalPadding;
int horizontalPadding;
TreeFormat(): verticalSpacing(2), horizontalSpacing(4), verticalPadding(1), horizontalPadding(4)
{}
} format;
Node* root;
public:
explicit TreeFormatter(std::string rootData, TreeFormat format = {}): format(format), root(new Node(std::move(rootData)))
{}
std::vector<std::string> generateBox(Node* node) const;
inline Node* getRoot()
{
return root;
}
std::vector<std::string> construct();
~TreeFormatter()
{
delete root;
}
};
} }
#endif //BLT_TESTS_FORMAT_H #endif //BLT_TESTS_FORMAT_H

View File

@ -6,15 +6,19 @@
#include <blt/std/format.h> #include <blt/std/format.h>
#include <cmath> #include <cmath>
#include "blt/std/logging.h" #include "blt/std/logging.h"
#include <stack>
#include <queue>
std::string createPadding(int padAmount) { std::string createPadding(int padAmount)
{
std::string padStr; std::string padStr;
for (int i = 0; i < padAmount; i++) for (int i = 0; i < padAmount; i++)
padStr += " "; padStr += " ";
return padStr; return padStr;
} }
std::vector<std::string> blt::string::TableFormatter::createTable(bool top, bool bottom) { std::vector<std::string> blt::string::TableFormatter::createTable(bool top, bool bottom)
{
std::vector<std::string> table; std::vector<std::string> table;
const auto& tableHeader = generateColumnHeader(); const auto& tableHeader = generateColumnHeader();
const auto& topSeparator = generateTopSeparator(tableHeader.size()); const auto& topSeparator = generateTopSeparator(tableHeader.size());
@ -26,16 +30,18 @@ std::vector<std::string> blt::string::TableFormatter::createTable(bool top, bool
table.push_back(tableHeader); table.push_back(tableHeader);
table.push_back(lineSeparator); table.push_back(lineSeparator);
for (const auto& row : rows) { for (const auto& row : rows)
{
std::string rowString = "|"; std::string rowString = "|";
for (unsigned int i = 0; i < row.rowValues.size(); i++) { for (unsigned int i = 0; i < row.rowValues.size(); i++)
{
const auto& rowValue = row.rowValues[i]; const auto& rowValue = row.rowValues[i];
const auto& column = columns[i]; const auto& column = columns[i];
const int spaceLeft = int(column.maxColumnLength) - int(rowValue.size()); const int spaceLeft = int(column.maxColumnLength) - int(rowValue.size());
// we want to prefer putting the space on the right size, flooring left and ceiling right ensures this. // we want to prefer putting the space on the right size, flooring left and ceiling right ensures this.
rowString += createPadding((int)std::floor(spaceLeft/2.0) + m_columnPadding); rowString += createPadding((int) std::floor(spaceLeft / 2.0) + m_columnPadding);
rowString += rowValue; rowString += rowValue;
rowString += createPadding((int)std::ceil(spaceLeft/2.0) + m_columnPadding); rowString += createPadding((int) std::ceil(spaceLeft / 2.0) + m_columnPadding);
rowString += "|"; rowString += "|";
} }
table.push_back(rowString); table.push_back(rowString);
@ -47,19 +53,21 @@ std::vector<std::string> blt::string::TableFormatter::createTable(bool top, bool
return table; return table;
} }
std::string blt::string::TableFormatter::generateColumnHeader() { std::string blt::string::TableFormatter::generateColumnHeader()
{
updateMaxColumnLengths(); updateMaxColumnLengths();
std::string header = "|"; std::string header = "|";
for (unsigned int i = 0; i < columns.size(); i++) { for (unsigned int i = 0; i < columns.size(); i++)
{
const auto& column = columns[i]; const auto& column = columns[i];
auto columnPaddingLength = (int(column.maxColumnLength) - int(column.columnName.size()))/2.0; auto columnPaddingLength = (int(column.maxColumnLength) - int(column.columnName.size())) / 2.0;
header += createPadding(int(m_columnPadding + (int)std::floor(columnPaddingLength))); header += createPadding(int(m_columnPadding + (int) std::floor(columnPaddingLength)));
header += column.columnName; header += column.columnName;
header += createPadding(int(m_columnPadding + (int)std::ceil(columnPaddingLength))); header += createPadding(int(m_columnPadding + (int) std::ceil(columnPaddingLength)));
if (i < columns.size()-1) if (i < columns.size() - 1)
header += "|"; header += "|";
} }
@ -67,15 +75,16 @@ std::string blt::string::TableFormatter::generateColumnHeader() {
return header; return header;
} }
std::string blt::string::TableFormatter::generateTopSeparator(size_t size) { std::string blt::string::TableFormatter::generateTopSeparator(size_t size)
{
auto sizeOfName = m_tableName.empty() ? 0 : m_tableName.size() + 4; auto sizeOfName = m_tableName.empty() ? 0 : m_tableName.size() + 4;
auto sizeNameRemoved = size - sizeOfName; auto sizeNameRemoved = size - sizeOfName;
std::string halfWidthLeftSeparator; std::string halfWidthLeftSeparator;
std::string halfWidthRightSeparator; std::string halfWidthRightSeparator;
auto sizeNameFloor = (size_t) std::floor((double)sizeNameRemoved/2.0); auto sizeNameFloor = (size_t) std::floor((double) sizeNameRemoved / 2.0);
auto sizeNameCeil = (size_t) std::ceil((double)sizeNameRemoved/2.0); auto sizeNameCeil = (size_t) std::ceil((double) sizeNameRemoved / 2.0);
halfWidthLeftSeparator.reserve(sizeNameCeil); halfWidthLeftSeparator.reserve(sizeNameCeil);
halfWidthRightSeparator.reserve(sizeNameFloor); halfWidthRightSeparator.reserve(sizeNameFloor);
@ -92,7 +101,8 @@ std::string blt::string::TableFormatter::generateTopSeparator(size_t size) {
std::string separator; std::string separator;
separator += halfWidthLeftSeparator; separator += halfWidthLeftSeparator;
if (sizeOfName != 0) { if (sizeOfName != 0)
{
separator += "{ "; separator += "{ ";
separator += m_tableName; separator += m_tableName;
separator += " }"; separator += " }";
@ -101,13 +111,16 @@ std::string blt::string::TableFormatter::generateTopSeparator(size_t size) {
return separator; return separator;
} }
std::string blt::string::TableFormatter::generateSeparator(size_t size) { std::string blt::string::TableFormatter::generateSeparator(size_t size)
{
size_t nextIndex = 0; size_t nextIndex = 0;
size_t currentColumnIndex = 0; size_t currentColumnIndex = 0;
std::string wholeWidthSeparator; std::string wholeWidthSeparator;
for (unsigned int i = 0; i < size; i++) { for (unsigned int i = 0; i < size; i++)
if (i == nextIndex) { {
auto currentColumnSize = columns[currentColumnIndex++].maxColumnLength + m_columnPadding*2; if (i == nextIndex)
{
auto currentColumnSize = columns[currentColumnIndex++].maxColumnLength + m_columnPadding * 2;
nextIndex += currentColumnSize + 1; nextIndex += currentColumnSize + 1;
wholeWidthSeparator += "+"; wholeWidthSeparator += "+";
} else } else
@ -117,13 +130,113 @@ std::string blt::string::TableFormatter::generateSeparator(size_t size) {
return wholeWidthSeparator; return wholeWidthSeparator;
} }
void blt::string::TableFormatter::updateMaxColumnLengths() { void blt::string::TableFormatter::updateMaxColumnLengths()
for (unsigned int i = 0; i < columns.size(); i++) { {
for (unsigned int i = 0; i < columns.size(); i++)
{
auto& column = columns[i]; auto& column = columns[i];
column.maxColumnLength = column.columnName.size(); column.maxColumnLength = column.columnName.size();
for (const auto& row : rows) { for (const auto& row : rows)
{
column.maxColumnLength = std::max(column.maxColumnLength, row.rowValues[i].size()); column.maxColumnLength = std::max(column.maxColumnLength, row.rowValues[i].size());
} }
} }
} }
/*
* -----------------------
* Tree Formatter
* -----------------------
*/
struct node_data
{
blt::string::TreeFormatter::Node* node;
size_t level;
node_data(blt::string::TreeFormatter::Node* node, size_t level): node(node), level(level)
{}
};
std::vector<std::string> blt::string::TreeFormatter::construct()
{
std::vector<std::string> lines;
std::stack<node_data> nodes;
std::queue<node_data> bfs;
bfs.emplace(root, 0);
while (!bfs.empty())
{
auto n = bfs.front();
nodes.push(n);
std::cout << "Node at level " << n.level << " " << bfs.size() << "\n";
if (n.node->left != nullptr)
bfs.emplace(n.node->left, n.level + 1);
if (n.node->right != nullptr)
bfs.emplace(n.node->right, n.level + 1);
bfs.pop();
}
return lines;
}
std::vector<std::string> blt::string::TreeFormatter::generateBox(blt::string::TreeFormatter::Node* node) const
{
if (node == nullptr)
return {};
std::vector<std::string> lines;
auto& data = node->data;
auto dataLength = data.length();
auto paddingLeft = format.horizontalPadding;
auto paddingRight = format.horizontalPadding;
auto totalLength = paddingLeft + paddingRight + dataLength;
if (totalLength % 2 != 0)
{
paddingRight += 1;
totalLength += 1;
}
// create horizontal line based on the calculated length
std::string hline = createLine(totalLength, '+', '-');
std::string paddedLine = createLine(totalLength, '|', ' ');
std::string dataStr;
dataStr.reserve(totalLength);
dataStr += '|';
dataStr += createPadding(paddingLeft - 1);
dataStr += data;
dataStr += createPadding(paddingRight - 1);
dataStr += '|';
lines.push_back(hline);
for (int i = 0; i < format.verticalPadding; i++)
lines.push_back(paddedLine);
lines.push_back(std::move(dataStr));
for (int i = 0; i < format.verticalPadding; i++)
lines.push_back(paddedLine);
lines.push_back(std::move(hline));
return lines;
}
std::string blt::string::createLine(size_t totalLength, char endingChar, char spacingChar)
{
std::string line;
line.reserve(totalLength);
line += endingChar;
line += createPadding(totalLength - 2, spacingChar);
line += endingChar;
return line;
}
std::string blt::string::createPadding(size_t length, char spacing)
{
std::string padding;
padding.reserve(length);
for (size_t i = 0; i < length; i++)
padding += spacing;
return padding;
}

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <blt/std/utility.h> #include <blt/std/utility.h>
#include <blt/std/format.h>
#include <blt/std/logging.h> #include <blt/std/logging.h>
#include <utility_test.h> #include <utility_test.h>
#include <vector> #include <vector>
@ -23,12 +24,17 @@
#include <iostream> #include <iostream>
#include <limits> #include <limits>
std::optional<int> get() std::optional<int> get()
{ {
return 10; return 10;
} }
void printLines(const std::vector<std::string>& lines)
{
for (const auto& v : lines)
std::cout << v << "\n";
}
void blt::test::utility::run() void blt::test::utility::run()
{ {
//std::vector<int> temp; //std::vector<int> temp;
@ -37,4 +43,54 @@ void blt::test::utility::run()
{ {
} }
blt::string::TableFormatter parkerLove("Intrinsic Action Value Table");
parkerLove.addColumn({"Thing"});
parkerLove.addColumn({"Value"});
parkerLove.addRow({"Cuddles", "1 / minute"});
parkerLove.addRow({"Hand Job", "10"});
parkerLove.addRow({"Head", "100"});
parkerLove.addRow({"Sleeping Together (Non-Sexual)", "1,000"});
parkerLove.addRow({"Actual Sex", "5,000"});
parkerLove.addRow({"Sleeping Together (Sexual)", "10,000"});
parkerLove.addRow({"Relationship (I would do anything for you)", "1,000,000,000,000"});
printLines(parkerLove.createTable(true, true));
blt::string::TreeFormatter treeFormatter("I love Men");
treeFormatter.getRoot()->with(
new string::TreeFormatter::Node("Guys"),
new string::TreeFormatter::Node("Femboys"));
printLines(treeFormatter.construct());
} }