From 3c3c17b618dcc6fde776fb6b6c7dbdfbda81ed3e Mon Sep 17 00:00:00 2001 From: Boris Reuderink Date: Mon, 1 Sep 2025 12:10:07 +0200 Subject: [PATCH 1/6] Initial test for CSV parser. --- meson.build | 87 ++++++++++++++++++++++++++++-------------------- tests/csv_test.c | 48 ++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 tests/csv_test.c diff --git a/meson.build b/meson.build index 938c80b..a606044 100644 --- a/meson.build +++ b/meson.build @@ -3,19 +3,28 @@ project('epsilon', 'c', default_options: ['c_std=c99', 'warning_level=3', 'werror=true'] ) +# Dependencies cc = meson.get_compiler('c') math_dep = cc.find_library('m', required: false) unity_dep = dependency('unity', required: true) -# Library sources -epsilon_sources = [ +# Library configuration +epsilon_sources = files( 'epsilon/rng.c', 'epsilon/stats.c', 'epsilon/hash.c', 'epsilon/transform.c' -] +) + +epsilon_headers = files( + 'epsilon/rng.h', + 'epsilon/stats.h', + 'epsilon/hash.h', + 'epsilon/transform.h', + 'epsilon/pa.h' +) -# Build the library +# Build library epsilon_lib = library('epsilon', sources: epsilon_sources, dependencies: math_dep, @@ -23,51 +32,55 @@ epsilon_lib = library('epsilon', version: meson.project_version() ) -# Dependency object for downstream use +# Declare dependency for downstream use epsilon_dep = declare_dependency( link_with: epsilon_lib, + include_directories: include_directories('epsilon'), dependencies: math_dep ) # Install headers -epsilon_headers = [ - 'epsilon/rng.h', - 'epsilon/stats.h', - 'epsilon/hash.h', - 'epsilon/transform.h', - 'epsilon/pa.h' -] install_headers(epsilon_headers, subdir: 'epsilon') -# Examples -example_exes = [ - ['example_hash', 'examples/example_hash.c'], - ['example_transform', 'examples/example_transform.c'], - ['example_stats', 'examples/example_stats.c'], - ['example_rng', 'examples/example_rng.c'] -] +# Examples and tests configuration +examples = { + 'example_hash': 'examples/example_hash.c', + 'example_transform': 'examples/example_transform.c', + 'example_stats': 'examples/example_stats.c', + 'example_rng': 'examples/example_rng.c' +} -foreach pair : example_exes - exe = executable(pair[0], pair[1], dependencies: epsilon_dep, install: false) - test(pair[0], exe) -endforeach +tests = { + 'hash_test': 'tests/hash_test.c', + 'rng_test': 'tests/rng_test.c', + 'stats_test': 'tests/stats_test.c', + 'transform_test': 'tests/transform_test.c', + 'csv_test': 'tests/csv_test.c' +} -# Tests -test_exes = [ - ['hash_test', 'tests/hash_test.c'], - ['rng_test', 'tests/rng_test.c'], - ['stats_test', 'tests/stats_test.c'], - ['transform_test', 'tests/transform_test.c'] -] +# Build and register examples +foreach name, source : examples + exe = executable(name, source, + dependencies: epsilon_dep, + install: false + ) + test(name, exe) +endforeach -foreach pair : test_exes - exe = executable(pair[0], pair[1], dependencies: [epsilon_dep, unity_dep], install: false) - test(pair[0], exe) +# Build and register tests +foreach name, source : tests + exe = executable(name, source, + dependencies: [epsilon_dep, unity_dep], + install: false + ) + test(name, exe) endforeach -# Documentation -install_data('README.md', install_dir: 'share/doc/epsilon') -install_data('LICENSE', install_dir: 'share/licenses/epsilon') +# Documentation installation +install_data( + files('README.md', 'LICENSE'), + install_dir: 'share/doc/epsilon' +) -# Optional metadata +# Distribution metadata meson.add_dist_script('echo', 'Package contact: boris@cortext.nl') diff --git a/tests/csv_test.c b/tests/csv_test.c new file mode 100644 index 0000000..06844f3 --- /dev/null +++ b/tests/csv_test.c @@ -0,0 +1,48 @@ +#include +#include +#include + +#define CSV_RECORD_SIZE 4096 +#define CSV_FIELDS 512 + +typedef struct { + char buffer[CSV_RECORD_SIZE]; + char *fields[CSV_FIELDS]; + size_t num_fields; +} csv_record_t; + +size_t csv_parse_record(const char *s, size_t len, csv_record_t *r) { + (void)s; + (void)len; + r->num_fields = 0; + return 0; +} + +typedef struct { + const char *input, *expected[10]; + size_t expected_count; +} csv_test_case; + +void test_parse_field(void) { + csv_test_case cases[] = { + {"single", {"single"}, 1}, + {"first, second, third", {"first", "second", "third"}, 3}, + }; + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); ++i) { + csv_test_case c = cases[i]; + + csv_record_t r = {0}; + csv_parse_record(c.input, strlen(c.input), &r); + + TEST_ASSERT_EQUAL_STRING_ARRAY(c.expected, r.fields, c.expected_count); + } +} + +void setUp(void) {} +void tearDown(void) {} +int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_parse_field); + return UNITY_END(); +} From 463dee7329972b0c7c73a9137263d0f68054063d Mon Sep 17 00:00:00 2001 From: Boris Reuderink Date: Mon, 1 Sep 2025 12:54:18 +0200 Subject: [PATCH 2/6] Clean up with Copilot. --- README.md | 50 +++++++++++++++++++++--------------- epsilon/hash.h | 13 ---------- epsilon/pa.h | 14 ---------- epsilon/rng.h | 12 --------- epsilon/stats.h | 29 --------------------- examples/example_hash.c | 2 +- examples/example_rng.c | 2 +- examples/example_stats.c | 2 +- examples/example_transform.c | 2 +- meson.build | 24 +++++++++-------- {epsilon => src}/hash.c | 2 ++ src/hash.h | 7 +++++ src/pa.h | 6 +++++ {epsilon => src}/rng.c | 6 ++--- src/rng.h | 5 ++++ {epsilon => src}/stats.c | 9 +++---- src/stats.h | 26 +++++++++++++++++++ {epsilon => src}/transform.c | 8 +++--- {epsilon => src}/transform.h | 3 ++- tests/hash_test.c | 2 +- tests/rng_test.c | 2 +- tests/stats_test.c | 2 +- tests/transform_test.c | 2 +- 23 files changed, 108 insertions(+), 122 deletions(-) delete mode 100644 epsilon/hash.h delete mode 100644 epsilon/pa.h delete mode 100644 epsilon/rng.h delete mode 100644 epsilon/stats.h rename {epsilon => src}/hash.c (91%) create mode 100644 src/hash.h create mode 100644 src/pa.h rename {epsilon => src}/rng.c (50%) create mode 100644 src/rng.h rename {epsilon => src}/stats.c (76%) create mode 100644 src/stats.h rename {epsilon => src}/transform.c (74%) rename {epsilon => src}/transform.h (51%) diff --git a/README.md b/README.md index e9250e6..6796f5e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Epsilon -Epsilon is a library with small functions for machine learning and statistics -written in plain C. The functions are decoupled and well tested. + +Epsilon is a C library of small, decoupled, and well-tested functions for machine learning and statistics. It is designed for microcontrollers and resource-constrained environments. [![CI Meson](https://github.com/breuderink/epsilon/actions/workflows/ci_meson.yml/badge.svg)](https://github.com/breuderink/epsilon/actions/workflows/ci_meson.yml) @@ -31,40 +31,50 @@ To allow machine learning to run on microcontrollers, the implementations: - work with fixed-point math when realistic, and - are easy to tune. -# Building -Epsilon uses Meson for building. Install Meson and Ninja, create a build directory, and configure the project. In the repository root, configure and build the project, then run the unit tests and examples: +# Folder Structure + +``` +/ # Project root +├── LICENSE +├── README.md +├── meson.build +├── docs/ # Documentation and references +├── src/ # Source code (.c/.h) +├── tests/ # Unit tests +├── examples/ # Example programs +├── subprojects/ # External dependencies (e.g., Unity) +``` + +# Building & Testing +Epsilon uses Meson for building. In the repository root: + ```bash -$ meson setup builddir -$ meson test -C builddir +meson setup builddir +meson compile -C builddir +meson test -C builddir ``` + # Algorithms + ## Pseudo-random number generation -- [Xorshift](docs/marsaglia2003xrn.pdf) is a fast and simple -pseudo-random number generator by George Marsaglia that has good statistical -properties. See the [xorshift example](examples/example_xorshift.c). +- [Xorshift](docs/marsaglia2003xrn.pdf): Fast, simple PRNG with good statistical properties. See `examples/example_rng.c`. ## Hashing -- The [FNV hash](https://tools.ietf.org/html/draft-eastlake-fnv-17) is a fast -hash function that maps variable length input to a fixed output -([example](examples/example_FNV_hash.c)). It can be used for [feature -hashing](https://en.wikipedia.org/wiki/Feature_hashing). +- [FNV hash](https://tools.ietf.org/html/draft-eastlake-fnv-17): Fast hash for feature hashing. See `examples/example_hash.c`. ## Statistics -- Welfords method computes mean and variance in a single pass. See the -[example of Welford's method](examples/example_Welfords_method.c). +- Welford's method: Online mean and variance in one pass. See `examples/example_stats.c`. ## Transformations -- Fast Walsh-Hadamard transform (FWHT) implements the Walsh-Hardamard -transform in O(n log n) time. The FWHT similar to the fast Fourier transform -and the Haar transform. See the [FWHT example](examples/example_FWHT.c). +- Fast Walsh-Hadamard Transform (FWHT): O(n log n) transform, similar to FFT. See `examples/example_transform.c`. ## Passive-aggressive learning -- [Online passive-aggressive (PA)](docs/crammer2006opa.pdf) regression solves a -regression problem by only updating the model on prediction mistakes. +- [Online passive-aggressive (PA)](docs/crammer2006opa.pdf): Regression with updates only on mistakes. + # Other solutions for Tiny ML or Edge AI - [TensorFlow Lite](https://www.tensorflow.org/lite/) diff --git a/epsilon/hash.h b/epsilon/hash.h deleted file mode 100644 index 7da4afa..0000000 --- a/epsilon/hash.h +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -// Fowler-Noll-Vo 1a hash [1]. -uint32_t FNV1a32_update(uint32_t hash, uint8_t data); -uint32_t FNV1a32_hash(const void *data, size_t n); - -/* -# References - -[1] Fowler, Glenn, et al. “The FNV Non-Cryptographic Hash Algorithm.” IETF - Tools, Network Working Group, tools.ietf.org/html/draft-eastlake-fnv-03. -*/ diff --git a/epsilon/pa.h b/epsilon/pa.h deleted file mode 100644 index aad1a99..0000000 --- a/epsilon/pa.h +++ /dev/null @@ -1,14 +0,0 @@ - -// Passive-aggressive parameters, see [1]. -typedef struct { - float C; // Perform aggressive updates with high C. - float eps; // Size of insensitive band for regression. -} PA_t; - -/* -# References - - -[1] Crammer, K. et al. “Online Passive-Aggressive Algorithms.” J. Mach. Learn. - Res. (2003). -*/ \ No newline at end of file diff --git a/epsilon/rng.h b/epsilon/rng.h deleted file mode 100644 index 58c1821..0000000 --- a/epsilon/rng.h +++ /dev/null @@ -1,12 +0,0 @@ -#include - -// Marsaglia's xorshift random number generator [1]; -uint32_t xorshift32(uint32_t y); - -/* -# References - -[1] Marsaglia, George. "Xorshift RNGs." Journal of Statistical Software 8.14 - (2003): 1-6. - -*/ diff --git a/epsilon/stats.h b/epsilon/stats.h deleted file mode 100644 index eec54a8..0000000 --- a/epsilon/stats.h +++ /dev/null @@ -1,29 +0,0 @@ -#include - -// Structure for computing the variance in one pass using Welford's method [1]. -typedef struct { - float mean, squared_diff; -} Welfords_method_t; - -typedef struct { - size_t n; - Welfords_method_t Welford; -} online_stats_t; - -// Update online statistics with observation. -void observe(online_stats_t *s, float x); - -// Current mean from online statistics. -float mean(const online_stats_t *s); - -// Current (biased) population variance. -float pvariance(const online_stats_t *s); - -// Current sample variance. -float variance(const online_stats_t *s); - -/* -# References -[1] Welford, B. P. "Note on a method for calculating corrected sums of - squares and products." Technometrics 4.3 (1962): 419-420. -*/ diff --git a/examples/example_hash.c b/examples/example_hash.c index ec05019..2755cae 100644 --- a/examples/example_hash.c +++ b/examples/example_hash.c @@ -1,4 +1,4 @@ -#include "epsilon/hash.h" +#include "hash.h" #include #include #include diff --git a/examples/example_rng.c b/examples/example_rng.c index 7254368..f1228a9 100644 --- a/examples/example_rng.c +++ b/examples/example_rng.c @@ -1,4 +1,4 @@ -#include "epsilon/rng.h" +#include "rng.h" #include #include diff --git a/examples/example_stats.c b/examples/example_stats.c index c63fd9b..819ed94 100644 --- a/examples/example_stats.c +++ b/examples/example_stats.c @@ -1,4 +1,4 @@ -#include "epsilon/stats.h" +#include "stats.h" #include int main(void) { diff --git a/examples/example_transform.c b/examples/example_transform.c index 8fe77b5..08afeb7 100644 --- a/examples/example_transform.c +++ b/examples/example_transform.c @@ -1,4 +1,4 @@ -#include "epsilon/transform.h" +#include "transform.h" #include #define LOG2_DIMS 3 diff --git a/meson.build b/meson.build index a606044..a2a8288 100644 --- a/meson.build +++ b/meson.build @@ -9,19 +9,21 @@ math_dep = cc.find_library('m', required: false) unity_dep = dependency('unity', required: true) # Library configuration + epsilon_sources = files( - 'epsilon/rng.c', - 'epsilon/stats.c', - 'epsilon/hash.c', - 'epsilon/transform.c' + 'src/rng.c', + 'src/stats.c', + 'src/hash.c', + 'src/transform.c' ) + epsilon_headers = files( - 'epsilon/rng.h', - 'epsilon/stats.h', - 'epsilon/hash.h', - 'epsilon/transform.h', - 'epsilon/pa.h' + 'src/rng.h', + 'src/stats.h', + 'src/hash.h', + 'src/transform.h', + 'src/pa.h' ) # Build library @@ -33,13 +35,15 @@ epsilon_lib = library('epsilon', ) # Declare dependency for downstream use + epsilon_dep = declare_dependency( link_with: epsilon_lib, - include_directories: include_directories('epsilon'), + include_directories: include_directories('src'), dependencies: math_dep ) # Install headers + install_headers(epsilon_headers, subdir: 'epsilon') # Examples and tests configuration diff --git a/epsilon/hash.c b/src/hash.c similarity index 91% rename from epsilon/hash.c rename to src/hash.c index e4c7923..03542ab 100644 --- a/epsilon/hash.c +++ b/src/hash.c @@ -1,3 +1,5 @@ + +// FNV-1a 32-bit hash implementation. #include #include diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..d6bcae3 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,7 @@ + +// FNV-1a 32-bit hash interface. +#include +#include + +uint32_t FNV1a32_update(uint32_t hash, uint8_t data); +uint32_t FNV1a32_hash(const void *data, size_t n); diff --git a/src/pa.h b/src/pa.h new file mode 100644 index 0000000..9d06bed --- /dev/null +++ b/src/pa.h @@ -0,0 +1,6 @@ + +// Passive-aggressive regression parameters. +typedef struct { + float C; + float eps; +} PA_t; diff --git a/epsilon/rng.c b/src/rng.c similarity index 50% rename from epsilon/rng.c rename to src/rng.c index 9a8419f..98745d4 100644 --- a/epsilon/rng.c +++ b/src/rng.c @@ -1,9 +1,7 @@ + +// Simple xorshift32 random number generator. #include -// Algorithm `xor` on page 4 of [1]. -// -// [1] Marsaglia, George. "Xorshift RNGs." Journal of Statistical Software 8.14 -// (2003): 1-6. uint32_t xorshift32(uint32_t y) { if (y == 0) y = 1; diff --git a/src/rng.h b/src/rng.h new file mode 100644 index 0000000..70e746d --- /dev/null +++ b/src/rng.h @@ -0,0 +1,5 @@ + +// xorshift32 random number generator interface. +#include + +uint32_t xorshift32(uint32_t y); diff --git a/epsilon/stats.c b/src/stats.c similarity index 76% rename from epsilon/stats.c rename to src/stats.c index 0274d3f..fef6bb4 100644 --- a/epsilon/stats.c +++ b/src/stats.c @@ -1,4 +1,6 @@ -#include "epsilon/stats.h" + +// Online mean and variance (Welford's method). +#include "stats.h" #include #include #include @@ -12,10 +14,6 @@ void Welford_observe(Welfords_method_t *w, size_t n, float x) { float delta_after = (x - w->mean); w->squared_diff += delta_before * delta_after; assert(isfinite(w->squared_diff)); - - // Note that we can track the population variance instead of the squared - // difference. That seems more symmetrical, and would prevent squared_diff - // from getting to big for a fixed-point implementation. } void observe(online_stats_t *s, float x) { @@ -23,7 +21,6 @@ void observe(online_stats_t *s, float x) { if (s->n < SIZE_MAX) { s->n++; } - Welford_observe(&s->Welford, s->n, x); } diff --git a/src/stats.h b/src/stats.h new file mode 100644 index 0000000..ae37ef6 --- /dev/null +++ b/src/stats.h @@ -0,0 +1,26 @@ + +#include + +// Online mean and variance (Welford's method) interface. +typedef struct { + float mean, squared_diff; +} Welfords_method_t; + +typedef struct { + size_t n; + Welfords_method_t Welford; +} online_stats_t; + +void observe(online_stats_t *s, float x); + +float mean(const online_stats_t *s); + +float pvariance(const online_stats_t *s); + +float variance(const online_stats_t *s); + +/* + * References + * [1] Welford, B. P. "Note on a method for calculating corrected sums of + * squares and products." Technometrics 4.3 (1962): 419-420. + */ diff --git a/epsilon/transform.c b/src/transform.c similarity index 74% rename from epsilon/transform.c rename to src/transform.c index 1d9040d..e5a6580 100644 --- a/epsilon/transform.c +++ b/src/transform.c @@ -1,4 +1,6 @@ -#include "epsilon/transform.h" + +// In-place Fast Walsh-Hadamard transform. +#include "transform.h" #include #include #include @@ -10,15 +12,11 @@ inline static void WHT_butterfly(float *const s, float *const d) { *d = temp - *d; } -// Perform in-place Fast Walsh-Hadamard transform. void FWHT(float *const x, const uint8_t nbits) { const size_t n = 1 << nbits; for (size_t width = n; width > 1; width >>= 1) { - // width halves each iteration. for (size_t block = 0; block < n; block += width) { - // block shifts by with. for (size_t i = 0; i < (width >> 1); ++i) { - // i loops to half a block. WHT_butterfly(x + block + i, x + (width >> 1) + block + i); } } diff --git a/epsilon/transform.h b/src/transform.h similarity index 51% rename from epsilon/transform.h rename to src/transform.h index 98b41d8..23da335 100644 --- a/epsilon/transform.h +++ b/src/transform.h @@ -1,4 +1,5 @@ + +// In-place Fast Walsh-Hadamard transform interface. #include -// Perform in-place Fast Walsh-Hadamard transform. void FWHT(float *x, uint8_t nbits); diff --git a/tests/hash_test.c b/tests/hash_test.c index fc4adde..8349ff3 100644 --- a/tests/hash_test.c +++ b/tests/hash_test.c @@ -1,4 +1,4 @@ -#include "epsilon/hash.h" +#include "hash.h" #include #include diff --git a/tests/rng_test.c b/tests/rng_test.c index 5606455..1970195 100644 --- a/tests/rng_test.c +++ b/tests/rng_test.c @@ -1,4 +1,4 @@ -#include "epsilon/rng.h" +#include "rng.h" #include #include #include diff --git a/tests/stats_test.c b/tests/stats_test.c index ae98596..e2447dd 100644 --- a/tests/stats_test.c +++ b/tests/stats_test.c @@ -1,4 +1,4 @@ -#include "epsilon/stats.h" +#include "stats.h" #include #include diff --git a/tests/transform_test.c b/tests/transform_test.c index c076c46..0ea8db8 100644 --- a/tests/transform_test.c +++ b/tests/transform_test.c @@ -1,4 +1,4 @@ -#include "epsilon/transform.h" +#include "transform.h" #include #include #include From d9740f303fc6ada8a608de1ce40c54a3e2ae5319 Mon Sep 17 00:00:00 2001 From: Boris Reuderink Date: Mon, 1 Sep 2025 13:47:48 +0200 Subject: [PATCH 3/6] Remove CSV files. --- meson.build | 1 - tests/csv_test.c | 48 ------------------------------------------------ 2 files changed, 49 deletions(-) delete mode 100644 tests/csv_test.c diff --git a/meson.build b/meson.build index a2a8288..5406d93 100644 --- a/meson.build +++ b/meson.build @@ -59,7 +59,6 @@ tests = { 'rng_test': 'tests/rng_test.c', 'stats_test': 'tests/stats_test.c', 'transform_test': 'tests/transform_test.c', - 'csv_test': 'tests/csv_test.c' } # Build and register examples diff --git a/tests/csv_test.c b/tests/csv_test.c deleted file mode 100644 index 06844f3..0000000 --- a/tests/csv_test.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include - -#define CSV_RECORD_SIZE 4096 -#define CSV_FIELDS 512 - -typedef struct { - char buffer[CSV_RECORD_SIZE]; - char *fields[CSV_FIELDS]; - size_t num_fields; -} csv_record_t; - -size_t csv_parse_record(const char *s, size_t len, csv_record_t *r) { - (void)s; - (void)len; - r->num_fields = 0; - return 0; -} - -typedef struct { - const char *input, *expected[10]; - size_t expected_count; -} csv_test_case; - -void test_parse_field(void) { - csv_test_case cases[] = { - {"single", {"single"}, 1}, - {"first, second, third", {"first", "second", "third"}, 3}, - }; - - for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); ++i) { - csv_test_case c = cases[i]; - - csv_record_t r = {0}; - csv_parse_record(c.input, strlen(c.input), &r); - - TEST_ASSERT_EQUAL_STRING_ARRAY(c.expected, r.fields, c.expected_count); - } -} - -void setUp(void) {} -void tearDown(void) {} -int main(void) { - UNITY_BEGIN(); - RUN_TEST(test_parse_field); - return UNITY_END(); -} From af497661bfa4cfd568cb24bdd08326dbbcb4aba6 Mon Sep 17 00:00:00 2001 From: Boris Reuderink Date: Mon, 1 Sep 2025 13:57:00 +0200 Subject: [PATCH 4/6] Simplify CI workflow. --- .github/workflows/ci_meson.yml | 41 ++++++++-------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci_meson.yml b/.github/workflows/ci_meson.yml index a58a3ce..6b61a3f 100644 --- a/.github/workflows/ci_meson.yml +++ b/.github/workflows/ci_meson.yml @@ -1,48 +1,25 @@ name: CI Meson on: - push: - branches: - - main - paths: - - '**.c' - - '**.h' - - 'meson.build' - - 'meson_options.txt' - - pull_request: - branches: - - main - paths: - - '**.c' - - '**.h' - - 'meson.build' - - 'meson_options.txt' - + [push, pull_request] jobs: build: - name: Build and Test on ${{ matrix.os }} with Meson v${{ matrix.meson_version }} + name: Build and Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - # meson_version: ["1.2.0", "1.3.0", "1.4.0"] meson_version: ["1.4.0"] steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: '3.x' - - name: Install dependencies - run: python -m pip install meson==${{ matrix.meson_version }} ninja - - name: Configure Project - run: meson setup builddir/ - - name: Run Tests - run: meson test -C builddir/ -v - - name: Upload Test Log - uses: actions/upload-artifact@v4 + - run: python -m pip install meson==${{ matrix.meson_version }} ninja + - run: meson setup builddir/ + - run: meson test -C builddir/ -v + - uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.os }}_Meson_Testlog path: builddir/meson-logs/testlog.txt + \ No newline at end of file From ac602e7e83a17e69da5cfd81308dc32738cc6edd Mon Sep 17 00:00:00 2001 From: Boris Reuderink Date: Mon, 1 Sep 2025 13:59:31 +0200 Subject: [PATCH 5/6] Fix CI trigger. --- .github/workflows/ci_meson.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_meson.yml b/.github/workflows/ci_meson.yml index 6b61a3f..d1e454b 100644 --- a/.github/workflows/ci_meson.yml +++ b/.github/workflows/ci_meson.yml @@ -1,6 +1,9 @@ name: CI Meson on: - [push, pull_request] + push: + branches: + - main + pull_request: jobs: build: name: Build and Test on ${{ matrix.os }} @@ -22,4 +25,3 @@ jobs: with: name: ${{ matrix.os }}_Meson_Testlog path: builddir/meson-logs/testlog.txt - \ No newline at end of file From cb6271efa146ff6342aabe9fc18476fc93fa16b3 Mon Sep 17 00:00:00 2001 From: Boris Reuderink Date: Mon, 1 Sep 2025 14:09:43 +0200 Subject: [PATCH 6/6] Clean up headers. --- src/hash.h | 7 ++++++- src/rng.h | 7 ++++++- src/stats.h | 15 ++++++--------- src/transform.h | 7 ++++++- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/hash.h b/src/hash.h index d6bcae3..e2af8c8 100644 --- a/src/hash.h +++ b/src/hash.h @@ -1,7 +1,12 @@ -// FNV-1a 32-bit hash interface. +#ifndef EPSILON_HASH_H +#define EPSILON_HASH_H + +// FNV-1a 32-bit hash interface #include #include uint32_t FNV1a32_update(uint32_t hash, uint8_t data); uint32_t FNV1a32_hash(const void *data, size_t n); + +#endif // EPSILON_HASH_H diff --git a/src/rng.h b/src/rng.h index 70e746d..7227b37 100644 --- a/src/rng.h +++ b/src/rng.h @@ -1,5 +1,10 @@ -// xorshift32 random number generator interface. +#ifndef EPSILON_RNG_H +#define EPSILON_RNG_H + +// xorshift32 random number generator interface #include uint32_t xorshift32(uint32_t y); + +#endif // EPSILON_RNG_H diff --git a/src/stats.h b/src/stats.h index ae37ef6..4f922e4 100644 --- a/src/stats.h +++ b/src/stats.h @@ -1,7 +1,10 @@ +#ifndef EPSILON_STATS_H +#define EPSILON_STATS_H + #include -// Online mean and variance (Welford's method) interface. +// Online mean and variance (Welford's method) interface typedef struct { float mean, squared_diff; } Welfords_method_t; @@ -12,15 +15,9 @@ typedef struct { } online_stats_t; void observe(online_stats_t *s, float x); - float mean(const online_stats_t *s); - float pvariance(const online_stats_t *s); - float variance(const online_stats_t *s); -/* - * References - * [1] Welford, B. P. "Note on a method for calculating corrected sums of - * squares and products." Technometrics 4.3 (1962): 419-420. - */ +#endif // EPSILON_STATS_H + diff --git a/src/transform.h b/src/transform.h index 23da335..c581eff 100644 --- a/src/transform.h +++ b/src/transform.h @@ -1,5 +1,10 @@ -// In-place Fast Walsh-Hadamard transform interface. +#ifndef EPSILON_TRANSFORM_H +#define EPSILON_TRANSFORM_H + +// In-place Fast Walsh-Hadamard transform interface #include void FWHT(float *x, uint8_t nbits); + +#endif // EPSILON_TRANSFORM_H