Files
docker-finance/container/src/root/macro/web/internal/crypto.C
Aaron Fiore 07d3bbc41f container: root: macro: web: internal: fix Crypto++ canvas
ROOT apparently no longer likes ++ in canvas title (thus, not rendering)
2026-01-09 11:59:12 -08:00

390 lines
11 KiB
C

// docker-finance | modern accounting for the power-user
//
// Copyright (C) 2021-2026 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 dfi
//! \since docker-finance 1.0.0
namespace dfi
{
//! \namespace dfi::macro
//! \brief ROOT macros
//! \since docker-finance 1.0.0
namespace macro
{
//! \namespace dfi::macro::web
//! \brief ROOT web-based macros
//! \since docker-finance 1.0.0
namespace web
{
//! \namespace dfi::macro::web::internal
//! \brief ROOT web server dfi macros for internal use only
//! \details Primarily implementation for public-consuming macros
//! \since docker-finance 1.0.0
namespace internal
{
//! \ingroup cpp_macro_impl
//! \brief RNG analysis macro
//! \since docker-finance 1.0.0
class Random final
{
public:
Random() = default;
~Random() = default;
Random(const Random&) = default;
Random& operator=(const Random&) = default;
Random(Random&&) = default;
Random& operator=(Random&&) = 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 Random::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 Random::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 Random::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 Random::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 Random::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 Random::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)
{
Random::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 =
([](Random::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();
}
Random::graph_value(c1, data);
Random::graph_frequency(c1, data);
Random::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);
Random::histo_gauss<decltype(generator)>(c2, data, generator);
Random::fun_facts(c2, data);
});
namespace common = ::dfi::macro::common;
const std::string timestamp{common::make_timestamp()};
#ifdef __DFI_PLUGIN_BITCOIN__
data.title = "Bitcoin_FastRandomContext_32_RNG_" + timestamp;
random(data, []() -> uint32_t {
return common::crypto::bitcoin::g_Random->generate();
});
// TODO(afiore): uint64_t (requires canvas-related refactor)
#endif
data.title = "Botan_RNG_" + timestamp;
random(data, []() -> uint32_t {
return common::crypto::botan::g_Random->generate();
});
data.title = "CryptoPP_RNG_" + timestamp;
random(data, []() -> uint32_t {
return common::crypto::cryptopp::g_Random->generate();
});
data.title = "libsodium_RNG_" + timestamp;
random(data, []() -> uint32_t {
return common::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 web
} // namespace macro
} // namespace dfi
#endif // CONTAINER_SRC_ROOT_MACRO_WEB_INTERNAL_CRYPTO_C_
// # vim: sw=2 sts=2 si ai et