root: macro: layout reorg, related refactor

This commit is contained in:
2024-07-20 16:44:12 -07:00
parent d2788cc511
commit 09c45c794b
8 changed files with 36 additions and 36 deletions

View File

@@ -0,0 +1,366 @@
// docker-finance | modern accounting for the power-user
//
// Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! \file
//! \author Aaron Fiore (Founder, Evergreen Crypto LLC)
//! \note File intended to be loaded into ROOT.cern framework / Cling interpreter
//! \since docker-finance 1.0.0
#ifndef CONTAINER_SRC_ROOT_MACRO_WEB_INTERNAL_CRYPTO_C_
#define CONTAINER_SRC_ROOT_MACRO_WEB_INTERNAL_CRYPTO_C_
#include <TCanvas.h>
#include <TGraph.h>
#include <cmath>
#include <iomanip>
#include <random>
#include <string>
#include <utility>
#include <vector>
#include "../../common/crypto.hh"
#include "../../common/utility.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
{
//! \namespace docker_finance::macro
//! \brief ROOT macros
//! \since docker-finance 1.0.0
namespace macro
{
//! \namespace docker_finance::macro::internal
//! \brief ROOT macros for internal use only
//! \since docker-finance 1.0.0
namespace internal
{
//! \ingroup cpp_macro_impl
//! \brief Cryptographical macro class
//! \since docker-finance 1.0.0
class Crypto final
{
public:
Crypto() = default;
~Crypto() = default;
Crypto(const Crypto&) = default;
Crypto& operator=(const Crypto&) = default;
Crypto(Crypto&&) = default;
Crypto& operator=(Crypto&&) = default;
private:
//! \since docker-finance 1.0.0
struct CanvasData
{
::Int_t n{}; // points
std::vector<::Double_t> x, y;
std::string title, label;
};
private:
//! \brief The "random device" implementation
//! \since docker-finance 1.0.0
template <typename t_gen, typename t_num>
struct GeneratorImpl
{
explicit GeneratorImpl(const t_gen& gen) : m_gen(gen) {}
//! \brief As expected from std::random_device except secure numbers are generated
t_num operator()() const { return m_gen(); }
//! \brief As implemented in std::random_device
t_num min() const { return std::numeric_limits<t_num>::min(); }
//! \brief As implemented in std::random_device
t_num max() const { return std::numeric_limits<t_num>::max(); }
t_gen m_gen;
};
//! \brief The "random device" facade
//! \since docker-finance 1.0.0
template <typename t_gen, typename t_num>
struct Generator
{
explicit Generator(const t_gen& gen) : m_impl(gen) {}
const auto& operator()() const { return m_impl; }
GeneratorImpl<t_gen, t_num> m_impl;
};
protected:
//! \brief Make graph of values from given canvas data
//! \param canvas Previously instantiated canvas
//! \param data Populated canvas data for graph
static void graph_value(::TCanvas* canvas, const Crypto::CanvasData& data)
{
// Graph: w/ lines
auto* gr1 = new ::TGraph(data.n, data.x.data(), data.y.data());
gr1->SetTitle(std::string(data.title + data.label).c_str());
gr1->SetMarkerColor(kBlue);
gr1->SetMarkerStyle(kFullDotSmall);
gr1->GetXaxis()->SetTitle("Count");
gr1->GetXaxis()->SetRangeUser(
0, *std::max_element(data.x.cbegin(), data.x.cend()));
gr1->GetYaxis()->SetTitle("Value [0..0xffffffff]");
gr1->GetYaxis()->SetRangeUser(
0, *std::max_element(data.y.cbegin(), data.y.cend()));
canvas->cd(1)->SetGridx();
canvas->cd(1)->SetGridy();
gStyle->SetLineStyle(kDotted);
gStyle->SetLineWidth(1);
gr1->Draw("APL");
// Graph: w/ bar
canvas->cd(2)->SetGridy();
gr1->Draw("AB");
}
//! \brief Make graph of value occurrences from given canvas data
//! \param canvas Previously instantiated canvas
//! \param data Populated canvas data for graph
static void graph_frequency(::TCanvas* canvas, const Crypto::CanvasData& data)
{
// x = [0..0xffffffff]
// y = frequency of number (max = nbins)
std::vector<::Double_t> x1;
std::vector<::Double_t> y1;
std::map<::Double_t, ::Double_t> map;
for (const auto& y : data.y)
{
++map[y]; // y = x
}
// .first = rand value
// .second = frequency
for (const auto& [x, y] : map)
{
x1.push_back(x);
y1.push_back(y);
}
auto* gr2 = new ::TGraph(data.n, x1.data(), y1.data());
gr2->SetTitle(std::string(data.title + data.label).c_str());
gr2->GetXaxis()->SetTitle("Value [0..0xffffffff]");
gr2->GetXaxis()->SetRangeUser(0, *std::max_element(x1.cbegin(), x1.cend()));
gr2->GetYaxis()->SetTitle("Count");
gr2->GetYaxis()->SetTickLength(0);
gr2->GetYaxis()->SetRangeUser(0, *std::max_element(y1.cbegin(), y1.cend()));
canvas->cd(3);
gr2->Draw("AB");
}
//! \brief Make 2D graph of prime numbers within given canvas data
//! \param canvas Previously instantiated canvas
//! \param data Populated canvas data for graph
static void graph_prime(::TCanvas* canvas, const Crypto::CanvasData& data)
{
canvas->cd(4);
auto* gr1 = new ::TGraph2D();
size_t primes{};
bool is_prime{true};
for (size_t n{}; n < data.n; n++)
{
const auto y = static_cast<uint32_t>(data.y.at(n));
is_prime = true;
if (y < 2)
is_prime = false;
for (size_t i{2}; i <= std::sqrt(y); ++i)
{
if (!(y % i))
{
is_prime = false;
break;
}
}
if (is_prime)
{
auto x = n;
auto z = y;
gr1->SetPoint(n, x, y, z);
primes++;
}
}
gr1->SetTitle(
std::string(data.title + data.label + " (" + primes + " primes)")
.c_str());
gStyle->SetPalette(1);
gr1->SetMarkerStyle(20);
gr1->Draw("pcol");
}
//! \brief Make histogram of values from given canvas data / random number generator
//! \param canvas Previously instantiated canvas
//! \param data Populated canvas data for graph
//! \param generator Random number generator to use
template <typename t_generator>
static void histo_gauss(
::TCanvas* canvas,
const Crypto::CanvasData& data,
const t_generator& generator)
{
canvas->cd(1);
canvas->SetGridx();
canvas->SetGridy();
auto* h1 = new ::TH1D(
data.title.c_str(),
std::string(data.title + data.label).c_str(),
data.n,
-5.0,
5.0); // TODO(unassigned): looks the best, would prefer more mathematical soundness
// Shove a CSPRNG into a Gaussian distribution
const Crypto::Generator<decltype(generator), uint32_t> gen(generator);
std::normal_distribution dist{0.0, 1.0};
for (size_t i{}; i < data.n; ++i)
{
h1->Fill(dist(gen()));
}
h1->Draw();
}
static void fun_facts(::TCanvas* canvas, const Crypto::CanvasData& data)
{
// TODO(unassigned): percent of prime / composite
canvas->cd(2);
auto* pt = new ::TPaveText(.05, .1, .95, .9);
// odd/even
size_t is_odd{};
for (const auto& y : data.y)
{
if (static_cast<uint32_t>(y) % 2)
{
is_odd++;
}
}
// miles
uint64_t miles{};
for (const auto& y : data.y)
{
miles += y;
}
miles /= 24901;
// TODO(unassigned): would be nice to have some RNG-related info
pt->AddText(std::string{"Fun facts"}
.c_str()); // TODO(unassigned): make this small text
pt->AddText(std::string{
"There are " + std::to_string(is_odd) + " odd numbers and "
+ std::to_string(data.y.size() - is_odd) + " even numbers"}
.c_str());
pt->AddText(std::string{
"If the total values generated were miles, you could circle the earth "
+ std::to_string(miles) + " times!"}
.c_str());
pt->Draw();
}
public:
// TODO(unassigned): put in a `Random` subdir to separate from other functions
static void rng_sample(const std::string& size)
{
Crypto::CanvasData data{};
// TODO(unassigned): sanitize because:
// 1. only unsigned is desired
// 2. server will crash if too large...
data.n = std::stoi(size);
data.x.resize(data.n);
data.y.resize(data.n);
auto random =
([](Crypto::CanvasData /* make copy */ data, const auto& generator) {
auto* c1 = new ::TCanvas(
std::string{"c1_" + data.title}.c_str(), "RNG analysis");
// TODO(unassigned): shift plots to the right so there's more space on the left and less on the right
c1->Divide(2, 2);
c1->SetFillColorAlpha(kBlue, 0.35);
// Create graph data
for (size_t i{}; i < data.n; i++)
{
data.x.at(i) = i;
data.y.at(i) = generator();
}
Crypto::graph_value(c1, data);
Crypto::graph_frequency(c1, data);
Crypto::graph_prime(c1, data);
auto* c2 = new ::TCanvas(
std::string{"c2_" + data.title}.c_str(), "RNG analysis");
// TODO(unassigned): shift plots to the right so there's more space on the left and less on the right
c2->Divide(0, 2);
Crypto::histo_gauss<decltype(generator)>(c2, data, generator);
Crypto::fun_facts(c2, data);
});
const std::string timestamp{utility::make_timestamp()};
data.title = "Botan_RNG_" + timestamp;
random(
data, []() -> uint32_t { return crypto::botan::g_Random->generate(); });
data.title = "Crypto++_RNG_" + timestamp;
random(data, []() -> uint32_t {
return crypto::cryptopp::g_Random->generate();
});
data.title = "libsodium_RNG_" + timestamp;
random(data, []() -> uint32_t {
return crypto::libsodium::g_Random->generate();
});
// TODO(unassigned): when clicking reload to create another sample, it doesn't do a clean refresh of the canvas
}
};
} // namespace internal
} // namespace macro
} // namespace docker_finance
#endif // CONTAINER_SRC_ROOT_MACRO_WEB_INTERNAL_CRYPTO_C_
// # vim: sw=2 sts=2 si ai et

View File

@@ -0,0 +1,230 @@
// docker-finance | modern accounting for the power-user
//
// Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! \file
//! \author Aaron Fiore (Founder, Evergreen Crypto LLC)
//! \note File intended to be loaded into ROOT.cern framework / Cling interpreter
//! \since docker-finance 1.0.0
#ifndef CONTAINER_SRC_ROOT_MACRO_WEB_INTERNAL_META_C_
#define CONTAINER_SRC_ROOT_MACRO_WEB_INTERNAL_META_C_
#include <TCanvas.h>
#include <TGraph.h>
#include <TPie.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "../../common/utility.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
{
//! \namespace docker_finance::macro
//! \brief ROOT macros
//! \since docker-finance 1.0.0
namespace macro
{
//! \namespace docker_finance::macro::internal
//! \brief ROOT macros for internal use only
//! \since docker-finance 1.0.0
namespace internal
{
//! \brief Meta processing macro class
//! \ingroup cpp_macro_impl
//! \since docker-finance 1.0.0
class Meta final
{
public:
Meta() = default;
~Meta() = default;
Meta(const Meta&) = default;
Meta& operator=(const Meta&) = default;
Meta(Meta&&) = default;
Meta& operator=(Meta&&) = default;
private:
//! \since docker-finance 1.0.0
struct CanvasData
{
std::string column;
std::pair<std::string, std::string> title; // canvas, object
std::map<std::string, uint64_t> row;
};
private:
//! \brief Make pie graph from given CSV data
//! \param csv Imported CSV data
//! \param data Populated canvas data for canvas creation
static void make_pie(
std::shared_ptr<ROOT::RDataFrame> csv,
const Meta::CanvasData& data)
{
// Setup pie data
std::vector<const char*> labels;
std::vector<::Float_t> values;
std::vector<::Int_t> colors;
// Populate all pie data from map
for (auto it = data.row.cbegin(); it != data.row.cend(); ++it)
{
const auto& label = it->first;
const auto& frequency = it->second;
std::cout << "label (row's value) = " << label
<< " | frequency (row's value frequency) = " << frequency
<< "\n";
labels.push_back(label.c_str());
values.push_back(frequency);
colors.push_back(std::distance(data.row.cbegin(), it));
}
// Make canvas and graph
auto* canvas = new ::TCanvas(
std::string{data.title.first + "_tpie"}.c_str(),
std::string{"Metadata (" + data.column + ")"}.c_str());
canvas->Draw();
auto* pie = new ::TPie(
"pie",
data.title.second.c_str(),
values.size(),
values.data(),
colors.data());
pie->SetLabels(labels.data());
pie->SetLabelsOffset(-.1);
// TODO(unassigned): why does changing these options have no apparent effect?
// pie->Draw("3DTNOL");
// pie->Draw("TNOL");
pie->Draw();
// TODO(unassigned): put count and percentages over the slices
// Make legend
auto* legend = pie->MakeLegend();
legend->SetHeader(data.column.c_str(), "C");
legend->Draw();
}
//! \brief Make graph from given CSV data
//! \param csv Imported CSV data
//! \param data Populated canvas data for canvas creation
static void make_graph(
std::shared_ptr<ROOT::RDataFrame> csv,
const Meta::CanvasData& data)
{
// Graph data
auto defined = csv->Define(
"DateAdded",
"std::tm time{}; std::istringstream ss(date_added);"
"ss >> std::get_time(&time, \"%Y-%m-%d %H:%M:%S\");"
"return std::mktime(&time);"); // Seconds from epoch
// TODO(unassigned): empty row will return "nan"
auto date = defined.Take<std::time_t>("DateAdded");
auto col = defined.Take<std::string>(data.column);
// Canvas
auto* canvas = new ::TCanvas(
std::string{data.title.first + "_tgraph"}.c_str(),
std::string{"Metadata (" + data.column + ")"}.c_str());
canvas->Draw();
// Graph
std::vector<::Float_t> x, y;
for (size_t i{}; i < data.row.size(); i++)
x.push_back(date->at(i));
for (const auto& [label, frequency] : data.row)
y.push_back(frequency);
auto* graph = new ::TGraph(x.size(), x.data(), y.data());
graph->SetTitle(data.title.second.c_str());
graph->SetMinimum(0);
graph->SetFillColor(kBlue);
graph->GetXaxis()->SetTimeDisplay(1);
graph->GetXaxis()->SetTimeFormat("%Y-%m-%d %H:%M:%S");
graph->GetXaxis()->SetTimeOffset(0, "local");
graph->Draw("B");
}
public:
//! \brief Sample metadata from given CSV column
//! \param column Existing CSV metadata column
static void meta_sample(const std::string& column)
{
// Import meta file
const std::string path{utility::get_env("global_conf_meta")};
auto csv =
std::make_shared<ROOT::RDataFrame>(ROOT::RDF::FromCSV(path.c_str()));
// Ensure viable header
std::string available{"\n\t"};
for (const auto& col : csv->GetColumnNames())
available += "\n\t" + col;
available += "\n";
if (!csv->HasColumn(column))
throw std::runtime_error(
std::string{"\n\nAvailable columns: " + available}.c_str());
// Aggregate all rows of given column
auto count = csv->Count();
if (!*count)
throw std::runtime_error("Empty CSV");
// Prepare canvas data
Meta::CanvasData data;
const std::string timestamp{utility::make_timestamp()},
parent{utility::get_env("global_parent_profile")},
child{utility::get_env("global_child_profile")};
data.column = column;
data.title.first = timestamp + "_meta_" + data.column;
data.title.second =
std::string{"Meta [" + parent + "/" + child + "] | " + timestamp};
// Map row values and frequency
auto col = csv->Take<std::string>(data.column);
for (size_t i{}; i < *count; i++)
++data.row[col->at(i)];
// Execute
Meta::make_pie(csv, data);
Meta::make_graph(csv, data);
}
};
} // namespace internal
} // namespace macro
} // namespace docker_finance
#endif // CONTAINER_SRC_ROOT_MACRO_WEB_INTERNAL_META_C_
// # vim: sw=2 sts=2 si ai et

View File

@@ -0,0 +1,92 @@
// docker-finance | modern accounting for the power-user
//
// Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! \file
//! \author Aaron Fiore (Founder, Evergreen Crypto LLC)
//! \note File intended to be loaded into ROOT.cern framework / Cling interpreter
//! \since docker-finance 1.0.0
#ifndef CONTAINER_SRC_ROOT_MACRO_WEB_SERVER_C_
#define CONTAINER_SRC_ROOT_MACRO_WEB_SERVER_C_
#include <THttpServer.h>
#include <memory>
#include "../common/common.hh"
//! \namespace docker_finance
//! \since docker-finance 1.0.0
namespace docker_finance
{
//! \namespace docker_finance::macro
//! \brief ROOT macros
//! \since docker-finance 1.0.0
namespace macro
{
//! \namespace docker_finance::macro::internal
//! \brief ROOT macros for internal use only
//! \since docker-finance 1.0.0
namespace internal
{
//! \brief HTTP Server instance
//! \note In namespace scope because of ROOT's static functions requirement
//! \since docker-finance 1.0.0
auto g_HTTPServer = std::make_unique<::THttpServer>("http:8080");
} // namespace internal
//! \brief Web server managing class
//! \ingroup cpp_macro
//! \since docker-finance 1.0.0
class Web final
{
public:
Web() = default;
~Web() = default;
Web(const Web&) = delete;
Web& operator=(const Web&) = delete;
Web(Web&&) = default;
Web& operator=(Web&&) = default;
private:
//! \brief ROOT HTTP server command registrar
//! \details Registers internal macros
static void register_commands()
{
::docker_finance::macro::internal::Command::load({"web/internal/crypto.C"});
internal::g_HTTPServer->RegisterCommand(
"/rng_sample",
"::docker_finance::macro::internal::Crypto::rng_sample(\"%arg1%\")");
::docker_finance::macro::internal::Command::load({"web/internal/meta.C"});
internal::g_HTTPServer->RegisterCommand(
"/meta_sample",
"::docker_finance::macro::internal::Meta::meta_sample(\"%arg1%\")");
}
public:
//! \brief Start webserver
static void run() { Web::register_commands(); }
};
} // namespace macro
} // namespace docker_finance
#endif // CONTAINER_SRC_ROOT_MACRO_WEB_SERVER_C_
// # vim: sw=2 sts=2 si ai et