root: macro: layout reorg, related refactor
This commit is contained in:
366
container/src/root/macro/web/internal/crypto.C
Normal file
366
container/src/root/macro/web/internal/crypto.C
Normal 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
|
||||
Reference in New Issue
Block a user