From c81720e7a9f4269704273e00e96ffcf7d090bbff Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 26 Jun 2026 23:16:03 -0400 Subject: [PATCH] Initial commit of the C++ version. --- README.md | 4 ++- cpp/.vimrc_proj | 1 + cpp/Makefile | 42 ++++++++++++++++++++++++ cpp/meson.build | 38 ++++++++++++++++++++++ cpp/scripts/reset_build.ps1 | 7 ++++ cpp/scripts/reset_build.sh | 10 ++++++ cpp/src/stats.cpp | 41 +++++++++++++++++++++++ cpp/src/stats.hpp | 65 +++++++++++++++++++++++++++++++++++++ cpp/tests/main.cpp | 13 ++++++++ cpp/tests/stats_tests.cpp | 20 ++++++++++++ cpp/wraps/fmt.wrap | 13 ++++++++ cpp/wraps/fuc2.wrap | 9 +++++ 12 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 cpp/.vimrc_proj create mode 100644 cpp/Makefile create mode 100644 cpp/meson.build create mode 100644 cpp/scripts/reset_build.ps1 create mode 100644 cpp/scripts/reset_build.sh create mode 100644 cpp/src/stats.cpp create mode 100644 cpp/src/stats.hpp create mode 100644 cpp/tests/main.cpp create mode 100644 cpp/tests/stats_tests.cpp create mode 100644 cpp/wraps/fmt.wrap create mode 100644 cpp/wraps/fuc2.wrap diff --git a/README.md b/README.md index b2efc2d..ddc0af1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # magic_stats -A simple bit of code in multiple languages that lets you calculate useful statistics without storing every sample. \ No newline at end of file +A simple bit of code in multiple languages that lets you calculate useful statistics without storing every sample. + +Look in cpp/ for a C++ version. More to come. diff --git a/cpp/.vimrc_proj b/cpp/.vimrc_proj new file mode 100644 index 0000000..2b745b4 --- /dev/null +++ b/cpp/.vimrc_proj @@ -0,0 +1 @@ +set makeprg=meson\ compile\ -C\ . diff --git a/cpp/Makefile b/cpp/Makefile new file mode 100644 index 0000000..cd6dd60 --- /dev/null +++ b/cpp/Makefile @@ -0,0 +1,42 @@ +all: build + +reset: +ifeq '$(OS)' 'Windows_NT' + powershell -executionpolicy bypass .\scripts\reset_build.ps1 +else + sh -x ./scripts/reset_build.sh +endif + +build: + meson compile -j 10 -C builddir + +release_build: + meson --wipe builddir -Db_ndebug=true --buildtype release + meson compile -j 10 -C builddir + +debug_build: + meson setup --wipe builddir -Db_ndebug=true --buildtype debugoptimized + meson compile -j 10 -C builddir + +test: build + ./builddir/fuc2it + +run: build test +ifeq '$(OS)' 'Windows_NT' + powershell "cp ./builddir/fuc2it.exe ." + ./fuc2it +else + ./builddir/fuc2it +endif + +debug: build + gdb --nx -x .gdbinit --ex run --args builddir/fuc2it + +debug_run: build + gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/fuc2it + +clean: + meson compile --clean -C builddir + +debug_test: build + gdb --nx -x .gdbinit --ex run --args builddir/fuc2it -e diff --git a/cpp/meson.build b/cpp/meson.build new file mode 100644 index 0000000..9e17ab8 --- /dev/null +++ b/cpp/meson.build @@ -0,0 +1,38 @@ +project('sol2_test', 'cpp', + version: '0.1.0', + default_options: [ + 'cpp_std=c++23', + # 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', + ]) + +# use this for common options only for our executables +cpp_args=[ + '-Wno-unused-parameter', + '-Wno-unused-function', + '-Wno-unused-variable', + '-Wno-unused-but-set-variable', + '-Wno-deprecated-declarations', + '-Wno-maybe-uninitialized', # only because of error in release_build + ] +link_args=[] +# these are passed as override_defaults +exe_defaults = [ 'warning_level=2', 'werror=false'] + +fmt = subproject('fmt').get_variable('fmt_dep') +fuc2 = subproject('fuc2').get_variable('fuc2_dep') + +dependencies = [fmt, fuc2] + +sources = [ +] + +executable('fuc2it', [ + 'src/stats.cpp', + 'tests/stats_tests.cpp', + 'tests/main.cpp', + ], + cpp_args: cpp_args, + link_args: link_args, + include_directories: include_directories('src'), + override_options: exe_defaults, + dependencies: dependencies) diff --git a/cpp/scripts/reset_build.ps1 b/cpp/scripts/reset_build.ps1 new file mode 100644 index 0000000..975852d --- /dev/null +++ b/cpp/scripts/reset_build.ps1 @@ -0,0 +1,7 @@ +mv .\subprojects\packagecache . +rm -recurse -force .\subprojects\,.\builddir\ +mkdir subprojects +mv .\packagecache .\subprojects\ +mkdir builddir +cp wraps\*.wrap subprojects\ +meson setup --default-library=static --prefer-static builddir diff --git a/cpp/scripts/reset_build.sh b/cpp/scripts/reset_build.sh new file mode 100644 index 0000000..89931e7 --- /dev/null +++ b/cpp/scripts/reset_build.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +mv -f ./subprojects/packagecache . +rm -rf subprojects builddir +mkdir subprojects +mv -f packagecache ./subprojects/ && true +mkdir builddir +cp wraps/*.wrap subprojects/ +# on OSX you can't do this with static +meson setup --default-library=static --prefer-static builddir diff --git a/cpp/src/stats.cpp b/cpp/src/stats.cpp new file mode 100644 index 0000000..3bd0039 --- /dev/null +++ b/cpp/src/stats.cpp @@ -0,0 +1,41 @@ +#include "stats.hpp" +#include +#include +#include + +void Stats::dump(std::string msg) +{ + fmt::println("{}: sum: {}, sumsq: {}, n: {}, " + "min: {}, max: {}, mean: {}, stddev: {}", + msg, sum, sumsq, n, min, max, mean(), + stddev()); +} + +// WARNING: got this code from google AI. PROBABLY BULLSHIT +void Stats::t_test(Stats& other) { + double n1 = this->n; + double n2 = other.n; + + double mean1 = this->mean(); + double mean2 = other.mean(); + + double var1 = this->variance(); + double var2 = other.variance(); + + // calculate t-stat + double delta_mean = mean1 - mean2; + double pooled_se = std::sqrt((var1 / n1) + (var2 / n2)); + double t_stat = delta_mean / pooled_se; + + // calculate degrees of freedom + double num = std::pow((var1 / n1) + (var2 / n2), 2); + double den = (std::pow(var1 / n1, 2) / (n1 - 1)) + (std::pow(var2 / n2, 2) / (n2 - 1)); + double df = num / den; + + double x = df / (df + t_stat * t_stat); + + double p_val = std::tgamma((df + 1.0) / 2.0) / (std::sqrt(df * std::numbers::pi) * std::tgamma(df / 2.0)) * std::pow(1.0 + (t_stat * t_stat) / df, -(df + 1.0) / 2.0); + + fmt::println("t_stat={}, df={}, p_val={}", + t_stat, df, p_val); +} diff --git a/cpp/src/stats.hpp b/cpp/src/stats.hpp new file mode 100644 index 0000000..5d819a4 --- /dev/null +++ b/cpp/src/stats.hpp @@ -0,0 +1,65 @@ +#pragma once +#include +#include + +struct Stats { + using TimeBullshit = std::chrono::time_point; + + double sum = 0.0; + double sumsq = 0.0; + double n = 0.0; + double min = 0.0; + double max = 0.0; + + inline void reset() { + sum = 0.0; + sumsq = 0.0; + n = 0.0; + min = 0.0; + max = 0.0; + } + + inline double mean() { + return sum / n; + } + + inline double stddev() { + return std::sqrt(variance()); + } + + inline double variance() { + return (sumsq - (sum * sum / n)) / (n - 1); + } + + inline void sample(double s) { + sum += s; + sumsq += s * s; + + if (n == 0) { + min = s; + max = s; + } else { + if (min > s) min = s; + if (max < s) max = s; + } + + n += 1; + } + + inline TimeBullshit time_start() { + return std::chrono::high_resolution_clock::now(); + } + + inline void sample_time(TimeBullshit start) { + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration(end - start); + + if(elapsed.count() > 0.0) { + sample(1.0/elapsed.count()); + } + } + + void dump(std::string msg=""); + + void t_test(Stats& other); +}; diff --git a/cpp/tests/main.cpp b/cpp/tests/main.cpp new file mode 100644 index 0000000..36e7ff4 --- /dev/null +++ b/cpp/tests/main.cpp @@ -0,0 +1,13 @@ +#include + +TEST_SET(stats_tests); + +using namespace fuc2; + +int main(int argc, char* argv[]) { + std::vector tests{ + stats_tests::TESTS, + }; + + return run_tests(tests, argc, argv); +} diff --git a/cpp/tests/stats_tests.cpp b/cpp/tests/stats_tests.cpp new file mode 100644 index 0000000..0787632 --- /dev/null +++ b/cpp/tests/stats_tests.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +using namespace fuc2; + +namespace stats_tests { + + void test_stats_stuff() { + CHECK(1 == 1); + } + + fuc2::Set TESTS{ + .name="stats", + .tests={ + TEST(test_stats_stuff), + } + }; +} diff --git a/cpp/wraps/fmt.wrap b/cpp/wraps/fmt.wrap new file mode 100644 index 0000000..7e9c5c4 --- /dev/null +++ b/cpp/wraps/fmt.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = fmt-12.0.0 +source_url = https://github.com/fmtlib/fmt/archive/12.0.0.tar.gz +source_filename = fmt-12.0.0.tar.gz +source_hash = aa3e8fbb6a0066c03454434add1f1fc23299e85758ceec0d7d2d974431481e40 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_12.0.0-1/fmt-12.0.0.tar.gz +patch_filename = fmt_12.0.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/fmt_12.0.0-1/get_patch +patch_hash = 307f288ebf3850abf2f0c50ac1fb07de97df9538d39146d802f3c0d6cada8998 +wrapdb_version = 12.0.0-1 + +[provide] +dependency_names = fmt diff --git a/cpp/wraps/fuc2.wrap b/cpp/wraps/fuc2.wrap new file mode 100644 index 0000000..50fe0df --- /dev/null +++ b/cpp/wraps/fuc2.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=fuc2-0.1.0 +url=https://lcthw.dev/cpp/fuc2.git +revision=HEAD +depth=1 +method=meson + +[provide] +fuc2 = fuc2_dep