summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore32
-rw-r--r--LICENSE21
-rw-r--r--Makefile63
-rwxr-xr-xREADME.md52
-rw-r--r--example/Makefile20
-rw-r--r--[-rwxr-xr-x]example/ann_xor.c41
-rw-r--r--[-rwxr-xr-x]example/bin.c44
-rw-r--r--example/buffer.c22
-rwxr-xr-xexample/build6
-rw-r--r--example/direct.c19
-rw-r--r--[-rwxr-xr-x]example/escape.c81
-rw-r--r--[-rwxr-xr-x]example/genetic_ann.c102
-rw-r--r--[-rwxr-xr-x]example/genetic_char.c64
-rw-r--r--example/genetic_pushdown_btc.c173
-rw-r--r--example/genetic_pushdown_btc.txt366
-rw-r--r--[-rwxr-xr-x]example/pool.c18
-rw-r--r--example/pushdown.c16
-rw-r--r--example/stack.c29
-rwxr-xr-xexample/ufo.c22
-rwxr-xr-xinclude/ann.h604
-rwxr-xr-xinclude/bin.h315
-rw-r--r--include/buffer.h232
-rw-r--r--include/direct.h313
-rwxr-xr-xinclude/escape.h209
-rwxr-xr-xinclude/flat.h293
-rwxr-xr-xinclude/genetic.h148
-rwxr-xr-xinclude/interpolate.h205
-rwxr-xr-xinclude/pool.h123
-rw-r--r--include/pushdown.h915
-rw-r--r--include/stack.h211
-rw-r--r--include/test.h57
-rw-r--r--include/type.h11
-rwxr-xr-xinclude/ufo.h730
-rw-r--r--[-rwxr-xr-x]include/uri.h499
-rw-r--r--working/buffer.h67
-rwxr-xr-xworking/ufo.c15
-rwxr-xr-xworking/ufo.h217
37 files changed, 4911 insertions, 1444 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8f21e0c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,32 @@
+# Compiled binaries
+*.o
+*.exe
+
+# Build output
+bin/
+test.bin
+
+# Test binaries
+tests/test_*
+
+# Compiled example binaries
+example/ann_xor
+example/bin
+example/buffer
+example/direct
+example/escape
+example/genetic_ann
+example/genetic_char
+example/genetic_pushdown_btc
+example/pool
+example/pushdown
+example/stack
+example/ufo
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+*~
+*.code-workspace
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..30aad98
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Ethan Werner
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a4ad567
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,63 @@
+CC = gcc
+CFLAGS = -Wall -Wextra -Werror -pedantic -std=c99 -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat-security -Wnull-dereference -I include
+LDFLAGS = -lm
+
+CFLAGS_DEBUG = -Wall -Wextra -Werror -pedantic -std=c99 -g -O0 -fstack-protector-strong -Wformat-security -Wnull-dereference -I include
+LDFLAGS_DEBUG = $(LDFLAGS)
+
+EXAMPLES = ann_xor buffer bin direct escape genetic_ann genetic_char \
+ genetic_pushdown_btc pool pushdown stack ufo
+
+TESTS = test_stack test_pool test_buffer test_bin test_pushdown \
+ test_escape test_direct test_ann test_genetic test_flat \
+ test_uri test_ufo test_interpolate
+
+EXAMPLE_BINS = $(addprefix bin/, $(EXAMPLES))
+TEST_BINS = $(addprefix tests/, $(TESTS))
+
+.PHONY: all clean examples tests run run-tests debug debug-tests valgrind-tests
+
+all: examples tests
+
+examples: $(EXAMPLE_BINS)
+
+tests: $(TEST_BINS)
+
+$(EXAMPLE_BINS): bin/%: example/%.c
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+$(TEST_BINS): tests/%: tests/%.c
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+debug:
+ $(MAKE) examples CFLAGS="$(CFLAGS_DEBUG)" LDFLAGS="$(LDFLAGS_DEBUG)"
+
+debug-tests:
+ $(MAKE) tests CFLAGS="$(CFLAGS_DEBUG)" LDFLAGS="$(LDFLAGS_DEBUG)"
+
+valgrind-tests: debug-tests
+ @fail=0; \
+ for t in $(TEST_BINS); do \
+ echo ">>> $$t"; valgrind --error-exitcode=1 ./$$t; \
+ if [ $$? -ne 0 ]; then fail=1; fi; \
+ echo; \
+ done; \
+ if [ $$fail -ne 0 ]; then echo "SOME TESTS FAILED"; exit 1; \
+ else echo "ALL TESTS PASSED"; fi
+
+clean:
+ rm -f $(EXAMPLE_BINS) $(TEST_BINS)
+
+run: examples
+ @for ex in $(EXAMPLE_BINS); do \
+ echo ">>> $$ex"; ./$$ex; echo; done
+
+run-tests: tests
+ @fail=0; \
+ for t in $(TEST_BINS); do \
+ echo ">>> $$t"; ./$$t; \
+ if [ $$? -ne 0 ]; then fail=1; fi; \
+ echo; \
+ done; \
+ if [ $$fail -ne 0 ]; then echo "SOME TESTS FAILED"; exit 1; \
+ else echo "ALL TESTS PASSED"; fi
diff --git a/README.md b/README.md
index a3f2732..bb62aa4 100755
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@ The following libraries are *highly* experimental and should be treated as such.
Please feel free to create issues, and pull requests are always welcome.
-## ann.h - Artifical Neural Network
+## ann.h - Artificial Neural Network
-This library provides an implementation of a fully connected, artifical neural network with backpropagation. The entirety of the ANN is stored in a single memory allocation, allowing for ease in persisting models and training via external methods.
+This library provides an implementation of a fully connected, artificial neural network with backpropagation. The entirety of the ANN is stored in a single memory allocation, allowing for ease in persisting models and training via external methods.
### Activation Functions
@@ -22,11 +22,11 @@ The activation function for hidden layers can be chosen independently from that
## bin.h - Flat File Block Storage
-This library is an experiment in storing ordered numerical data as binary "flat files". The contained data must be of a fixed block size. The primary goal of the library is to provide a simple method of persisting and caching data that is time-series in nature. The resulting files should be short lived.
+This library is an experiment in storing ordered numerical data as binary "flat files". The contained data must be of a fixed block size. The primary goal of the library is to provide a simple method of persisting and caching data that is time-series in nature. The resulting files should be short lived.
### Format
-The file header contains two 64 bit unsigned integers, block count and block size. The rest of the file contains the block data.
+The file header contains two 64 bit unsigned integers, block count and block size. The rest of the file contains the block data.
Each block must possess a numerical key/index as the first element. Prior to using the library, a ```typedef [numerical_type] bin_key_t``` must be declared in the implementation file.
@@ -34,32 +34,44 @@ Each block must possess a numerical key/index as the first element. Prior to usi
This is a naive library, and as such, does not provide input validation. If data is placed out of order, *there it will remain.*
+## buffer.h - Dynamic Buffer
+
+A dynamically resizing buffer with embedded metadata. Stores capacity, element count, and element size preceding the data pointer. Automatically doubles capacity when usage exceeds 80%, and halves when below 20% (minimum capacity of 32).
+
+## direct.h - Computation Graph
+
+A computation graph library with topological update ordering. Nodes are connected via edges and evaluated in dependency order using DFS. Uses a generation counter to avoid re-evaluating unchanged nodes. Provides basic arithmetic node functions (add, subtract) as examples.
+
## escape.h - ANSI Escape Codes
-This library provides utility functions/macros for an assortment of ANSI escape codes.
+This library provides utility functions and macros for an assortment of ANSI escape codes, including cursor movement, screen clearing, text formatting, and color output. Each operation is available as both a macro (string literal) and a function (writes to stdout or a given FILE*).
## flat.h - Directory Traversal
-This library provides a simple wrapper for traversing POSIX file systems. The intention for this library is to be used alongside bin.h, for the purpose of traversing hierarchial directory structures.
+This library provides a simple wrapper for traversing POSIX file systems. The intention for this library is to be used alongside bin.h, for the purpose of traversing hierarchical directory structures. Provides both POSIX (dirent.h) and Windows (FindFirstFileA) implementations.
## genetic.h - Genetic Algorithm
-An implementation of a genetic algorithm. The library exposes a single function for creating a new generation of individuals. The function is "generic", meaning that the respective operations for crossover, mutation, and fitness are passed as as function pointers.
+An implementation of a genetic algorithm. The library exposes a single function for creating a new generation of individuals. The function is "generic", meaning that the respective operations for crossover, mutation, and fitness are passed as function pointers.
### Algorithm Overview
**1. Crossover**
- Determine the selection cutoff for the population.
-- Replace all unfit individuals by preforming a crossover operation with two randomly selected fit indivuals.
+- Replace all unfit individuals by performing a crossover operation with two randomly selected fit individuals.
**2. Mutation**
-- Determine the number of mutation operations, and perform those on a randomly selected individual.
+- Determine the number of mutation operations, and perform those on randomly selected individuals.
**3. Fitness**
-- Sort the population by descending fitness, in preparation for the next generation.
+- Sort the population by ascending fitness, in preparation for the next generation.
+
+## interpolate.h - 1D Interpolation Macros
+
+A collection of interpolation and easing macros inspired by "Math for Game Programmers: Fast and Funky 1D Nonlinear Transformations". Provides namespaced `INTERP_*` macros for collision safety, legacy unprefixed aliases guarded by `#ifndef`, and function wrappers for debuggability.
## pool.h - Memory Pool
@@ -67,20 +79,26 @@ A free-list based memory pool. The pool has a fixed size after allocation.
**Note:** The size of a block must be greater than or equal to ```sizeof(void *)```
+## pushdown.h - Pushdown Automata
+
+An implementation of a stack-based pushdown automata with 65+ operations. Instructions execute sequentially until an END marker, supporting arithmetic, trigonometry, stack manipulation (Forth-style), and comparison operations. Designed for use with stack.h.
+
## stack.h - Stack Implementation
-A stack implementation to be used with pushdown.h
+A generic dynamic stack with automatic resizing. Requires the user to define ```typedef <type> stack_value_t``` before inclusion. Provides both simple variants (return zero on empty) and ```_ok``` variants (return status code) for pop and peek operations.
+
+## test.h - Test Utilities
-**Note:** A pop operation on an empty stack will return 0/NULL.
+A minimal test framework providing ```ASSERT```, ```ASSERT_FLOAT_EQ```, and ```ASSERT_STR_EQ``` macros. Tracks failure count and prints results. Not a library for distribution — intended for the test suite only.
-## pushdown.h - A Pushdown/Stack Automata
+## type.h - Type Aliases
-This file provides an implementation of a pushdown/stack automata.
+Common type aliases used across the libraries: ```i8```, ```i16```, ```i32```, ```i64```, ```u8```, ```u16```, ```u32```, ```u64```, ```f32```, ```f64```.
-## ufo.h - Universal Functions Originator
+## ufo.h - Universal Function Originator
- An implementation of the symbolic regression technique known as a Universal Functions Originator. The originating paper can be found [here](https://doi.org/10.1016/j.asoc.2020.106417).
+An implementation of the symbolic regression technique known as a Universal Function Originator. The originating paper can be found [here](https://doi.org/10.1016/j.asoc.2020.106417). Uses a single-allocation design for easy persistence and serialization.
## uri.h - URI Builder
-A simple, and likely incomplete, implementation of a "builder" pattern to generate URIs as defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986). The URIs are stored interally as separate blocks, enabling sequential URIs to be generated with ease.
+A simple, and likely incomplete, implementation of a "builder" pattern to generate URIs as defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986). The URIs are stored internally as separate components, enabling sequential URIs to be generated with ease.
diff --git a/example/Makefile b/example/Makefile
new file mode 100644
index 0000000..67a7746
--- /dev/null
+++ b/example/Makefile
@@ -0,0 +1,20 @@
+CC = gcc
+CFLAGS = -Wall -Wextra -Werror -pedantic -std=c99 -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat-security -Wnull-dereference -I ../include
+LDFLAGS = -lm
+
+EXAMPLES = ann_xor buffer bin direct escape genetic_ann genetic_char \
+ genetic_pushdown_btc pool pushdown stack ufo
+
+.PHONY: all clean run
+
+all: $(EXAMPLES)
+
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+clean:
+ rm -f $(EXAMPLES)
+
+run: all
+ @for ex in $(EXAMPLES); do \
+ echo ">>> $$ex"; ./$$ex; echo; done
diff --git a/example/ann_xor.c b/example/ann_xor.c
index a39264c..ac1623e 100755..100644
--- a/example/ann_xor.c
+++ b/example/ann_xor.c
@@ -1,4 +1,4 @@
-// example_xor.c - Example XOR training
+// ann_xor.c - Example: training an ANN to solve XOR using backpropagation
#define ANN_IMPLEMENTATION
#include "../include/ann.h"
@@ -10,39 +10,44 @@
int main(void) {
srand(time(0));
- ann_t *ann = ann_init(3, (uint_t[]){2, 2, 1});
- ann_set_activation(ann, SIGMOID, SIGMOID);
- ann_random(ann);
+ ann_t ann = {0};
+ if (!ann_init(&ann, 3, (u64[]){2, 2, 1})) {
+ fprintf(stderr, "Failed to init ann\n");
+ return 1;
+ }
+ ann_set_activation(&ann, SIGMOID, SIGMOID);
+ ann_random(&ann);
- fp_t input[4][2] = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};
- fp_t output[4] = {0, 0, 0, 0};
- fp_t expected[4] = {0, 1, 1, 0};
- fp_t error = 0;
+ f64 input[4][2] = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};
+ f64 output[4] = {0, 0, 0, 0};
+ f64 expected[4] = {0, 1, 1, 0};
- for (int_t iteration = 0; iteration < 100000; iteration++) {
- for (int_t training = 0; training < 4; training++) {
- ann_propagation_forward(ann, (fp_t const *const)&input[training],
+ for (i64 iteration = 0; iteration < 100000; iteration++) {
+ for (i64 training = 0; training < 4; training++) {
+ ann_propagation_forward(&ann, (f64 const *const)&input[training],
&output[training]);
- error += ann_error_total(&output[training], &expected[training],
- ann->layer_neuron_n[ann->layer_n - 1]);
+ (void)ann_error_total(&output[training], &expected[training],
+ ann.layer_neuron_n[ann.layer_n - 1]);
// Train using backpropagation
- ann_propagation_backward(ann, (fp_t const *const)&input[training],
+ ann_propagation_backward(&ann, (f64 const *const)&input[training],
&output[training], &expected[training], 0.4);
// Train using numeric integration
- // ann_train_numeric( ann, ( fp_t const * const ) &i[b], &t[b], 0.03 );
+ // ann_train_numeric( ann, ( f64 const * const ) &i[b], &t[b], 0.03 );
}
}
- ann_print_weight(ann);
+ ann_print_weight(&ann);
fputs("------------------------------------------------------------\n\n",
stderr);
for (int i = 0; i < 4; i++) {
- ann_propagation_forward(ann, (fp_t const *const)&input[i], &output[i]);
- ann_print_neuron(ann, (fp_t const *const)&input[i], &output[i]);
+ ann_propagation_forward(&ann, (f64 const *const)&input[i], &output[i]);
+ ann_print_neuron(&ann, (f64 const *const)&input[i], &output[i]);
}
+
+ ann_free(&ann);
}
diff --git a/example/bin.c b/example/bin.c
index 40a7f77..7be43c1 100755..100644
--- a/example/bin.c
+++ b/example/bin.c
@@ -1,47 +1,67 @@
+// bin.c - Example usage of bin.h flat file block storage
+
+#include <assert.h>
+#include <math.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
typedef int32_t bin_key_t;
+#define BIN_IMPLEMENTATION
+#include "../include/bin.h"
+
#define TEST_LENGTH 10
#define TEST_START -2
-#define TEST_STEP 0.5;
struct sample_t {
bin_key_t time;
double data;
};
-#define BIN_IMPLEMENTATION
-#include "../include/bin.h"
-
-int main() {
+int main(void) {
char const *path = "./test.bin";
FILE *bin = bin_init(path, sizeof(struct sample_t));
+ if (!bin) {
+ fprintf(stderr, "Failed to initialize binary file\n");
+ return 1;
+ }
bin_key_t half = (bin_key_t)((TEST_START + TEST_LENGTH) / 2.0);
// Insert half of the values using bin_insert()
for (bin_key_t i = half - 1; i >= TEST_START; i--) {
- bin_insert(bin, 0, &(struct sample_t){.time = i, .data = 2.0 * i}, 1);
+ struct sample_t sample = {.time = i, .data = 2.0 * i};
+ bin_insert(bin, 0, &sample, 1);
}
// Append half of the values to the end
for (bin_key_t i = half; i < TEST_START + TEST_LENGTH; i++) {
- bin_append(bin, &(struct sample_t){.time = i, .data = 2.0 * i}, 1);
+ struct sample_t sample = {.time = i, .data = 2.0 * i};
+ bin_append(bin, &sample, 1);
}
uint64_t l = bin_length(bin);
- uint64_t bs = bin_block_size(bin);
+ (void)bin_block_size(bin);
- struct sample_t x[l];
+ assert(l < 1000000);
+ struct sample_t *x = calloc(l, sizeof(struct sample_t));
+ if (!x) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ bin_close(bin);
+ return 1;
+ }
bin_read(bin, 0, x, l);
// Test that all values are correct
- for (int32_t i = 0; i < TEST_LENGTH; i++) {
- bin_key_t key = TEST_START + i;
- assert(x[i].time == key && x[i].data == 2.0 * key);
+ for (uint32_t i = 0; i < TEST_LENGTH; i++) {
+ bin_key_t key = TEST_START + (int32_t)i;
+ assert(x[i].time == key);
+ assert(fabs(x[i].data - 2.0 * key) < 1e-9);
}
+ free(x);
bin_close(bin);
+ remove(path);
+ return 0;
} \ No newline at end of file
diff --git a/example/buffer.c b/example/buffer.c
new file mode 100644
index 0000000..35237b2
--- /dev/null
+++ b/example/buffer.c
@@ -0,0 +1,22 @@
+// buffer.c - Example usage of buffer.h dynamic buffer
+
+#define BUFFER_IMPLEMENTATION
+#include "../include/buffer.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ void *buf = buffer_malloc(10, sizeof(int));
+ if (!buf) {
+ fprintf(stderr, "Failed to allocate buffer\n");
+ return 1;
+ }
+
+ printf("capacity: %" PRIu64 "\n", buffer_capacity(buf));
+ printf("size: %" PRIu64 "\n", buffer_count(buf));
+
+ buffer_free(buf);
+ return 0;
+}
diff --git a/example/build b/example/build
deleted file mode 100755
index b6c600f..0000000
--- a/example/build
+++ /dev/null
@@ -1,6 +0,0 @@
-EXAMPLES=$(ls -1 --color=never *.c | sed 's/\.c//')
-
-for EXAMPLE in $EXAMPLES
-do
- gcc -g -fno-fast-math -O0 -o $EXAMPLE $EXAMPLE.c -lm
-done
diff --git a/example/direct.c b/example/direct.c
index 102c0c3..29da5d3 100644
--- a/example/direct.c
+++ b/example/direct.c
@@ -1,7 +1,12 @@
-typedef double value_t;
+
+// direct.c - Example usage of direct.h computation graph
+
+#include <stdio.h>
+
+typedef double direct_value_t;
+
#define DT_IMPLEMENTATION
#include "../include/direct.h"
-#include <stdio.h>
int main() {
node_t x0 = {.x = 0.0, .function = dt_add};
@@ -20,7 +25,7 @@ int main() {
dt_connect(&x3, &x4);
dt_connect(&x4, &x5);
dt_connect(&x4, &x6);
- dt_connect(&x5, &x2);
+ dt_connect(&x5, &x6);
dt_update_trace(&x0);
printf("data: %lf\n", x0.x);
@@ -29,5 +34,13 @@ int main() {
dt_update_trace(&x0);
printf("data: %lf\n", x0.x);
+ dt_clear_edges(&x0);
+ dt_clear_edges(&x1);
+ dt_clear_edges(&x2);
+ dt_clear_edges(&x3);
+ dt_clear_edges(&x4);
+ dt_clear_edges(&x5);
+ dt_clear_edges(&x6);
+
return 0;
} \ No newline at end of file
diff --git a/example/escape.c b/example/escape.c
index c5e71ac..99f686a 100755..100644
--- a/example/escape.c
+++ b/example/escape.c
@@ -1,6 +1,5 @@
// escape.c - Simple test print code
-
#include <stdint.h>
#include <tgmath.h>
@@ -9,54 +8,56 @@
#define TEST_STRING "HELLO"
-void hsv_rgb( float h, float s, float v, uint8_t *r, uint8_t *g, uint8_t *b )
-{
- float h0 = h * 6.0;
+void hsv_rgb(float h, float s, float v, uint8_t *r, uint8_t *g, uint8_t *b) {
+ float h0 = h * 6.0f;
- float k = fmod( 5.0 + h0, 6.0 );
- *r = 255 * ( v - v * s * fmax( 0.0, fmin( k, fmin( 4.0 - k, 1.0 ) ) ) );
+ float k = fmodf(5.0f + h0, 6.0f);
+ *r = (uint8_t)(255.0f * (v - v * s * fmaxf(0.0f, fminf(k, fminf(4.0f - k, 1.0f)))));
- k = fmod( 3.0 + h0, 6.0 );
- *g = 255 * ( v - v * s * fmax( 0.0, fmin( k, fmin( 4.0 - k, 1.0 ) ) ) );
+ k = fmodf(3.0f + h0, 6.0f);
+ *g = (uint8_t)(255.0f * (v - v * s * fmaxf(0.0f, fminf(k, fminf(4.0f - k, 1.0f)))));
- k = fmod( 1.0 + h0, 6.0 );
- *b = 255 * ( v - v * s * fmax( 0.0, fmin( k, fmin( 4.0 - k, 1.0 ) ) ) );
+ k = fmodf(1.0f + h0, 6.0f);
+ *b = (uint8_t)(255.0f * (v - v * s * fmaxf(0.0f, fminf(k, fminf(4.0f - k, 1.0f)))));
}
+int main(void) {
+ // Clear screen
+ fputs(ESC_CLEAR_SCREEN, stdout);
-int main( void )
-{
- // Clear screen
- fputs( ESC_CLEAR_SCREEN, stdout );
-
- // Print a rectangle of Xs with positional coordinates
- fputs( ESC_CURSOR_POSITION( 1, 1 ), stdout ); fflush( stdout );
- fputs( ESC_CURSOR_POSITION( 3, 2 ) "X", stdout ); fflush( stdout );
- fputs( ESC_CURSOR_POSITION( 10, 2 ) "X", stdout ); fflush( stdout );
- fputs( ESC_CURSOR_POSITION( 10, 4 ) "X", stdout ); fflush( stdout );
- fputs( ESC_CURSOR_POSITION( 3, 4 ) "X", stdout ); fflush( stdout );
- fputs( "\n", stdout );
+ // Print a rectangle of Xs with positional coordinates
+ fputs(ESC_CURSOR_POSITION(1, 1), stdout);
+ fflush(stdout);
+ fputs(ESC_CURSOR_POSITION(3, 2) "X", stdout);
+ fflush(stdout);
+ fputs(ESC_CURSOR_POSITION(10, 2) "X", stdout);
+ fflush(stdout);
+ fputs(ESC_CURSOR_POSITION(10, 4) "X", stdout);
+ fflush(stdout);
+ fputs(ESC_CURSOR_POSITION(3, 4) "X", stdout);
+ fflush(stdout);
+ fputs("\n", stdout);
- fputs( "\n" ESC_RESET TEST_STRING "\n\n", stdout );
- fputs( ESC_BOLD TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_OVERLINE TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_UNDERLINE TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_STRIKETHROUGH TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_COLOR_FG_RGB( 0, 128, 128 ) TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_COLOR_BG_RGB( 128, 0, 128 ) TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_BLINK TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_REVERSE TEST_STRING ESC_RESET "\n\n", stdout );
- fputs( ESC_CONCEAL TEST_STRING ESC_REVEAL TEST_STRING ESC_RESET "\n\n", stdout );
+ fputs("\n" ESC_RESET TEST_STRING "\n\n", stdout);
+ fputs(ESC_BOLD TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_OVERLINE TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_UNDERLINE TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_STRIKETHROUGH TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_COLOR_FG_RGB(0, 128, 128) TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_COLOR_BG_RGB(128, 0, 128) TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_BLINK TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_REVERSE TEST_STRING ESC_RESET "\n\n", stdout);
+ fputs(ESC_CONCEAL TEST_STRING ESC_REVEAL TEST_STRING ESC_RESET "\n\n",
+ stdout);
- uint8_t r,g,b;
+ uint8_t r, g, b;
- for( float i = 0; i < 1; i += 0.01 )
- {
- hsv_rgb( i, 1.0, 1.0, &r, &g, &b );
+ for (float i = 0; i < 1; i += 0.01f) {
+ hsv_rgb(i, 1.0, 1.0, &r, &g, &b);
- esc_color_fg_rgb( r, g, b );
- fputs( "█", stdout );
- }
+ esc_color_fg_rgb(r, g, b);
+ fputs("█", stdout);
+ }
- fputs( "\n", stdout );
+ fputs("\n", stdout);
}
diff --git a/example/genetic_ann.c b/example/genetic_ann.c
index 071ccf5..85dd520 100755..100644
--- a/example/genetic_ann.c
+++ b/example/genetic_ann.c
@@ -1,5 +1,8 @@
+// genetic_ann.c - Example training of an XOR neural network by genetic algorithm
+
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -8,7 +11,7 @@
#define POPULATION_N 100
#define RATE_SELECTION 0.2
-#define RATE_MUTATON 10
+#define RATE_MUTATION 0.2
#define MAGNITUDE_MUTATION 0.01
#define ERROR 0.00001
@@ -18,12 +21,12 @@ typedef ann_t *ga_individual_t;
#include "../include/genetic.h"
double fitness(ann_t *const x, void const *c) {
- fp_t *i = *((fp_t **)c);
- fp_t *t = *((fp_t **)c + 1);
- fp_t o[4];
+ f64 *i = *((f64 **)c);
+ f64 *t = *((f64 **)c + 1);
+ f64 o[4];
- fp_t e = 0, sum = 0;
- for (uint_t b = 0; b < 4; b++) {
+ f64 e = 0, sum = 0;
+ for (u64 b = 0; b < 4; b++) {
ann_propagation_forward(x, &i[2 * b], &o[b]);
e = ann_error_total(&o[b], &t[b], x->layer_neuron_n[x->layer_n - 1]);
sum += e;
@@ -33,46 +36,76 @@ double fitness(ann_t *const x, void const *c) {
}
void crossover(ann_t **y, ann_t *x0, ann_t *x1) {
- uint32_t i_crossover = rand() % x0->weight_n;
- memcpy((*y)->weight, x0->weight, i_crossover * sizeof(fp_t));
- memcpy((*y)->weight + i_crossover, x1->weight + i_crossover,
- (x0->weight_n - i_crossover) * sizeof(fp_t));
+ if (!y || !x0 || !x1 || !*y)
+ return;
+ if (x0->weight_n == 0 || x1->weight_n == 0)
+ return;
+ ann_t *child = (ann_t *)*y;
+ if (child->weight_n == 0)
+ return;
+ uint32_t i = rand() % x0->weight_n;
+ u64 max_copy = child->weight_n < x0->weight_n ? child->weight_n : x0->weight_n;
+ max_copy = max_copy < x1->weight_n ? max_copy : x1->weight_n;
+ u64 safe_i = i >= max_copy ? max_copy - 1 : (u64)i;
+
+ memcpy(child->weight, x0->weight, safe_i * sizeof(f64));
+ memcpy(child->weight + safe_i, x1->weight + safe_i, (max_copy - safe_i) * sizeof(f64));
+ if (child->weight_n > max_copy) {
+ memset(child->weight + max_copy, 0, (child->weight_n - max_copy) * sizeof(f64));
+ }
}
-void mutate(ann_t *x) {
- uint32_t i_mutation = rand() % x->weight_n;
- fp_t r =
- (2 * (MAGNITUDE_MUTATION * (fp_t)rand() / RAND_MAX)) - MAGNITUDE_MUTATION;
- x->weight[i_mutation] += r;
+void mutate(ann_t **x) {
+ if (!x || !*x || (*x)->weight_n == 0)
+ return;
+ uint32_t i_mutation = rand() % (*x)->weight_n;
+ f64 r =
+ (2 * (MAGNITUDE_MUTATION * (f64)rand() / RAND_MAX)) - MAGNITUDE_MUTATION;
+ (*x)->weight[i_mutation] += r;
}
int main(void) {
srand(time(0));
- fp_t input[12] = {0, 0, 0, 1, 1, 0, 1, 1};
- fp_t output;
- fp_t target[4] = {0, 1, 1, 0};
+ f64 input[12] = {0, 0, 0, 1, 1, 0, 1, 1};
+ f64 output;
+ f64 target[4] = {0, 1, 1, 0};
- ann_t **networks = malloc(sizeof(ann_t *) * POPULATION_N);
+ ann_t *networks[POPULATION_N] = {0};
- for (uint_t i = 0; i < POPULATION_N; i++) {
- networks[i] = ann_init(3, (uint_t[]){2, 2, 1});
- ann_set_activation(networks[i], SIGMOID, IDENTITY);
- ann_random(networks[i]);
+ for (u64 i = 0; i < POPULATION_N; i++) {
+ ann_t *ann = malloc(sizeof(ann_t));
+ if (!ann) {
+ fprintf(stderr, "Failed to allocate ann_t\n");
+ for (u64 k = 0; k < i; k++) { ann_free(networks[k]); free(networks[k]); }
+ exit(1);
+ }
+ if (!ann_init(ann, 3, (u64[]){2, 2, 1})) {
+ free(ann);
+ fprintf(stderr, "Failed to init ann_t\n");
+ for (u64 k = 0; k < i; k++) { ann_free(networks[k]); free(networks[k]); }
+ exit(1);
+ }
+ ann_set_activation(ann, SIGMOID, IDENTITY);
+ ann_random(ann);
+ networks[i] = ann;
}
- fp_t e = ERROR + 1;
- uint32_t n = 0, i = 0;
- while (e > ERROR) {
- e = ga_generation(networks, POPULATION_N, RATE_SELECTION, RATE_MUTATON,
- fitness, (fp_t *[2]){input, target}, crossover, mutate);
+ f64 e = ERROR + 1;
+ uint32_t n = 0;
+ uint32_t max_iterations = 1000000;
+ uint32_t print_counter = 0;
+ f64 *fitness_data[2] = {input, target};
+ while (e > ERROR && n < max_iterations) {
+ e = ga_generation(networks, POPULATION_N, RATE_SELECTION, RATE_MUTATION,
+ fitness, fitness_data, crossover, mutate);
n++;
- i++;
+ print_counter++;
- if (i > 1000) {
+ if (print_counter > 1000) {
printf("\rERROR: %10.5lf", e);
- i = 0;
+ print_counter = 0;
}
}
@@ -81,11 +114,16 @@ int main(void) {
for (int i = 0; i < 1; i++) {
for (int j = 0; j < 4; j++) {
- ann_propagation_forward(networks[i], (fp_t const *const)&input[2 * j],
+ ann_propagation_forward(networks[i], (f64 const *const)&input[2 * j],
&output);
ann_print_neuron(networks[i], &input[2 * j], &output);
}
}
printf("\nITERATIONS: %u\n", n);
+
+ for (u64 i = 0; i < POPULATION_N; i++) {
+ ann_free(networks[i]);
+ free(networks[i]);
+ }
}
diff --git a/example/genetic_char.c b/example/genetic_char.c
index 7dc24d4..d0b08ae 100755..100644
--- a/example/genetic_char.c
+++ b/example/genetic_char.c
@@ -1,7 +1,8 @@
-// example_string.c - Example training of ann.h ANN, with genetic algorithm
+// genetic_char.c - Example: evolving a string to match a target
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -12,7 +13,7 @@ typedef char *ga_individual_t;
#define POPULATION_N 100
#define RATE_SELECTION 0.5
-#define RATE_MUTATON 2.0
+#define RATE_MUTATION 0.2
#define ERROR 0
#define TARGET_STRING \
@@ -23,43 +24,58 @@ typedef char *ga_individual_t;
#define generate() ((char)(rand() % (127 - 32) + 32))
void crossover(char **y, char *x0, char *x1) {
+ if (!y || !x0 || !x1)
+ return;
uint32_t lx0 = strlen(x0);
uint32_t lx1 = strlen(x1);
+ if (lx0 == 0 || lx1 == 0)
+ return;
+
uint32_t ix0 = rand() % lx0;
uint32_t ix1 = rand() % lx1;
uint32_t ly = ix0 + (lx1 - ix1);
char *sy = malloc(ly + 1);
- strncpy(sy, x0, ix0);
- strncpy(sy + ix0, x1 + ix1, lx1 - ix1);
+ if (!sy) {
+ free(*y);
+ *y = NULL;
+ return;
+ }
+ memcpy(sy, x0, ix0);
+ memcpy(sy + ix0, x1 + ix1, lx1 - ix1);
sy[ly] = 0;
free(*y);
*y = sy;
}
-void mutation(char *x) {
- uint32_t l = strlen(x);
+void mutation(char **x) {
+ if (!x || !*x) return;
+ uint32_t l = strlen(*x);
+ if (l == 0) return;
uint32_t i = rand() % l;
- x[i] = generate();
+ (*x)[i] = generate();
}
double fitness(char *individual, void const *data) {
+ if (!individual) return 1e30;
double error = 0;
char const *target = (char const *)data;
while (*individual && *target) {
- error += abs(*target++ - *individual++);
+ error += abs((unsigned char)*target - (unsigned char)*individual);
+ target++;
+ individual++;
}
while (*individual) {
- error += *individual++;
+ error += (unsigned char)*individual++;
}
while (*target) {
- error += *target++;
+ error += (unsigned char)*target++;
}
return error;
@@ -71,10 +87,15 @@ int main(void) {
srand(time(0));
- // Initialize poplation
+ // Initialize population
for (uint64_t i = 0; i < POPULATION_N; i++) {
uint64_t n = rand() % 5 + 20;
char *str = malloc(n + 1);
+ if (!str) {
+ fprintf(stderr, "Failed to allocate string\n");
+ for (uint64_t k = 0; k < i; k++) free(population[k]);
+ exit(1);
+ }
str[n] = (uint8_t)0;
for (uint64_t j = 0; j < n; j++) {
@@ -84,27 +105,28 @@ int main(void) {
population[i] = str;
}
- printf("TARGET: %s\n", TARGET_STRING);
+ printf("TARGET: %s\n", TARGET_STRING);
fflush(stdout);
- time_t t0 = clock();
+ clock_t t0 = clock();
uint32_t n = 0;
- // Conduct opitmization
+ // Conduct optimization
double error = 1;
- while (error > ERROR) {
- error =
- ga_generation(population, POPULATION_N, RATE_SELECTION, RATE_MUTATON,
- fitness, (void const *)target, crossover, mutation);
+ uint32_t max_iterations = 1000000;
+ while (error > ERROR && n < max_iterations) {
+ error =
+ ga_generation(population, POPULATION_N, RATE_SELECTION, RATE_MUTATION,
+ fitness, (void const *)target, crossover, mutation);
n++;
+ printf("\rSOLUTION: %s", population[0]);
}
- time_t t1 = clock();
+ clock_t t1 = clock();
- printf("\nSOLUTION: %s", population[0]);
- printf("\nITERATIONS: %d\nTIME: %f ms\n", n,
+ printf("\nSOLUTION: %s\nITERATIONS: %u\nTIME: %f ms\n", population[0], n,
1000.0 * (t1 - t0) / CLOCKS_PER_SEC);
for (int i = 0; i < POPULATION_N; i++) {
diff --git a/example/genetic_pushdown_btc.c b/example/genetic_pushdown_btc.c
new file mode 100644
index 0000000..1261413
--- /dev/null
+++ b/example/genetic_pushdown_btc.c
@@ -0,0 +1,173 @@
+// genetic_pushdown_btc.c - Example training of a pushdown automaton by genetic algorithm
+
+#include "../include/escape.h"
+#include <float.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <tgmath.h>
+#include <time.h>
+
+typedef double stack_value_t;
+#define STACK_IMPLEMENTATION
+#include "../include/stack.h"
+
+typedef stack_value_t pushdown_value_t;
+#define PUSHDOWN_IMPLEMENTATION
+#include "../include/pushdown.h"
+
+typedef struct {
+ size_t size;
+ pushdown_instruction_t *op;
+} program_t;
+
+typedef program_t ga_individual_t;
+#define GENETIC_IMPLEMENTATION
+#include "../include/genetic.h"
+
+#define POPULATION_N 128
+#define RATE_SELECTION 0.25
+#define RATE_MUTATION 0.2
+#define ERROR 1000
+
+#define generate() ((pushdown_instruction_t)(rand() % END))
+
+void crossover(program_t *y, program_t x0, program_t x1) {
+ if (!y || x0.size == 0 || x1.size == 0) {
+ return;
+ }
+
+ uint32_t x0_crossover = rand() % x0.size;
+ uint32_t x1_crossover = rand() % x1.size;
+
+ uint32_t size = x0_crossover + (x1.size - x1_crossover);
+
+ pushdown_instruction_t *instructions =
+ calloc(size + 1, sizeof(pushdown_instruction_t));
+ if (!instructions) {
+ free(y->op);
+ y->op = NULL;
+ y->size = 0;
+ return;
+ }
+ memcpy(instructions, x0.op, x0_crossover * sizeof(pushdown_instruction_t));
+ memcpy(instructions + x0_crossover, x1.op + x1_crossover,
+ (x1.size - x1_crossover) * sizeof(pushdown_instruction_t));
+ instructions[size] = END;
+
+ free(y->op);
+ y->op = instructions;
+ y->size = size;
+}
+
+void mutation(program_t *x) {
+ if (!x || x->size == 0) {
+ return;
+ }
+ uint32_t i = rand() % x->size;
+ x->op[i] = generate();
+}
+
+double BTC[] = {
+#include "./genetic_pushdown_btc.txt"
+};
+
+double fitness(program_t individual, void const *data) {
+ (void)data;
+ if (!individual.op)
+ return DBL_MAX;
+
+ double error = 0;
+ const uint32_t K = 16;
+ size_t btc_n = sizeof(BTC) / sizeof(*BTC);
+ if (btc_n < K)
+ return 0;
+
+ for (size_t i = 0; i < btc_n - K; i++) {
+ stack_t input = {0};
+ stack_t output = {0};
+
+ stack_from_array(&input, &BTC[i], K);
+ stack_init(&output);
+ pushdown_run(&output, &input, individual.op);
+ double e = fabs(stack_peek(&output) - BTC[i + K]) + individual.size;
+ error += e;
+
+ stack_free(&input);
+ stack_free(&output);
+ }
+
+ return error;
+}
+
+// void kill(int signal) { (void)signal; } // removed: shadows POSIX kill(2)
+
+int main(void) {
+ program_t population[POPULATION_N] = {0};
+
+ srand(time(0));
+
+ // Initialize population
+ for (uint64_t i = 0; i < POPULATION_N; i++) {
+ uint64_t n = rand() % 10 + 10;
+ pushdown_instruction_t *program =
+ calloc(n + 1, sizeof(pushdown_instruction_t));
+ if (!program) {
+ fprintf(stderr, "Failed to allocate program\n");
+ for (uint64_t k = 0; k < i; k++) free(population[k].op);
+ return 1;
+ }
+ program[n] = END;
+
+ for (uint64_t j = 0; j < n; j++) {
+ program[j] = generate();
+ }
+
+ population[i].op = program;
+ population[i].size = n;
+ }
+
+ uint32_t n = 0;
+
+ double error = ERROR + 1, error_last = 0;
+ double rate_mutation = RATE_MUTATION;
+ double rate_selection = RATE_SELECTION;
+
+ // Conduct optimization
+ uint32_t max_iterations = 1000000;
+ while (error > ERROR && n < max_iterations) {
+ error_last = error;
+ error = ga_generation(population, POPULATION_N, rate_selection,
+ rate_mutation, fitness, NULL, crossover, mutation);
+
+ if (error == error_last) {
+ rate_mutation = fmin(1.1 * rate_mutation, 0.95);
+ } else {
+ rate_mutation = RATE_MUTATION;
+ }
+
+ printf("%u\t%zu\t%lf\t%lf\n", n, population[0].size, error, error / POPULATION_N);
+ n++;
+ }
+
+ clock_t t1 = clock();
+ (void)t1;
+
+ pushdown_instruction_t *op = population[0].op;
+ if (!op) {
+ fprintf(stderr, "Best individual has no program\n");
+ return 1;
+ }
+ while (*op != END) {
+ printf("%d\n", *op);
+ op++;
+ }
+
+ for (uint64_t i = 0; i < POPULATION_N; i++) {
+ free(population[i].op);
+ }
+
+ return 0;
+}
diff --git a/example/genetic_pushdown_btc.txt b/example/genetic_pushdown_btc.txt
new file mode 100644
index 0000000..7dfbc2c
--- /dev/null
+++ b/example/genetic_pushdown_btc.txt
@@ -0,0 +1,366 @@
+16625.080000,
+16688.470000,
+16679.860000,
+16863.240000,
+16836.740000,
+16951.970000,
+16955.080000,
+17091.140000,
+17196.550000,
+17446.290000,
+17934.900000,
+18869.590000,
+19909.570000,
+20976.300000,
+20880.800000,
+21169.630000,
+21161.520000,
+20688.780000,
+21086.790000,
+22676.550000,
+22777.630000,
+22720.420000,
+22934.430000,
+22636.470000,
+23117.860000,
+23032.780000,
+23078.730000,
+23031.090000,
+23774.570000,
+22840.140000,
+23139.280000,
+23723.770000,
+23471.870000,
+23449.320000,
+23331.850000,
+22955.670000,
+22760.110000,
+23264.290000,
+22939.400000,
+21819.040000,
+21651.180000,
+21870.880000,
+21788.200000,
+21808.100000,
+22220.800000,
+24307.840000,
+23623.470000,
+24565.600000,
+24641.280000,
+24327.640000,
+24829.150000,
+24436.350000,
+24188.840000,
+23947.490000,
+23198.130000,
+23175.380000,
+23561.210000,
+23522.870000,
+23147.350000,
+23646.550000,
+23475.470000,
+22362.680000,
+22353.350000,
+22435.510000,
+22429.760000,
+22219.770000,
+21718.080000,
+20363.020000,
+20187.240000,
+20632.410000,
+22163.950000,
+24197.530000,
+24746.070000,
+24375.960000,
+25052.790000,
+27423.930000,
+26965.880000,
+28038.680000,
+27767.240000,
+28175.820000,
+27307.440000,
+28333.970000,
+27493.290000,
+27494.710000,
+27994.330000,
+27139.890000,
+27268.130000,
+28348.440000,
+28033.560000,
+28478.480000,
+28411.040000,
+28199.310000,
+27790.220000,
+28168.090000,
+28177.980000,
+28044.140000,
+27925.860000,
+27947.790000,
+28333.050000,
+29652.980000,
+30235.060000,
+30139.050000,
+30399.070000,
+30485.700000,
+30318.500000,
+30315.360000,
+29445.040000,
+30397.550000,
+28822.680000,
+28245.990000,
+27276.910000,
+27817.500000,
+27591.380000,
+27525.340000,
+28307.600000,
+28422.700000,
+29473.790000,
+29340.260000,
+29248.490000,
+29268.810000,
+28091.570000,
+28680.540000,
+29006.310000,
+28847.710000,
+29534.380000,
+28904.620000,
+28454.980000,
+27694.270000,
+27658.780000,
+27621.760000,
+27000.790000,
+26804.990000,
+26784.080000,
+26930.640000,
+27192.690000,
+27036.650000,
+27398.800000,
+26832.210000,
+26890.130000,
+27129.590000,
+26753.830000,
+26851.280000,
+27225.730000,
+26334.820000,
+26476.210000,
+26719.290000,
+26868.350000,
+28085.650000,
+27745.880000,
+27702.350000,
+27219.660000,
+26819.970000,
+27249.590000,
+27075.130000,
+27119.070000,
+25760.100000,
+27238.780000,
+26346.000000,
+26508.220000,
+26480.380000,
+25851.240000,
+25940.170000,
+25902.500000,
+25918.730000,
+25124.680000,
+25576.390000,
+26327.460000,
+26510.680000,
+26336.210000,
+26851.030000,
+28327.490000,
+30027.300000,
+29912.280000,
+30695.470000,
+30548.700000,
+30480.260000,
+30271.130000,
+30688.160000,
+30086.250000,
+30445.350000,
+30477.250000,
+30590.080000,
+30620.770000,
+31156.440000,
+30777.580000,
+30514.170000,
+29909.340000,
+30342.270000,
+30292.540000,
+30171.230000,
+30414.470000,
+30620.950000,
+30391.650000,
+31476.050000,
+30334.070000,
+30295.810000,
+30249.130000,
+30145.890000,
+29856.560000,
+29913.920000,
+29792.020000,
+29908.740000,
+29771.800000,
+30084.540000,
+29176.920000,
+29227.390000,
+29354.970000,
+29210.690000,
+29319.250000,
+29356.920000,
+29275.310000,
+29230.110000,
+29675.730000,
+29151.960000,
+29178.680000,
+29074.090000,
+29042.130000,
+29041.860000,
+29180.580000,
+29765.490000,
+29561.490000,
+29429.590000,
+29397.710000,
+29415.960000,
+29282.910000,
+29408.440000,
+29170.350000,
+28701.780000,
+26664.550000,
+26049.560000,
+26096.210000,
+26189.580000,
+26124.140000,
+26031.660000,
+26431.640000,
+26162.370000,
+26047.670000,
+26008.460000,
+26089.690000,
+26106.150000,
+27727.390000,
+27297.270000,
+25931.470000,
+25800.720000,
+25868.800000,
+25969.570000,
+25812.420000,
+25779.980000,
+25753.240000,
+26240.200000,
+25905.650000,
+25895.680000,
+25832.230000,
+25162.650000,
+25833.340000,
+26228.320000,
+26539.670000,
+26608.690000,
+26568.280000,
+26534.190000,
+26754.280000,
+27211.120000,
+27132.010000,
+26567.630000,
+26579.570000,
+26579.390000,
+26256.830000,
+26298.480000,
+26217.250000,
+26352.720000,
+27021.550000,
+26911.720000,
+26967.920000,
+27983.750000,
+27530.790000,
+27429.980000,
+27799.390000,
+27415.910000,
+27946.600000,
+27968.840000,
+27935.090000,
+27583.680000,
+27391.020000,
+26873.320000,
+26756.800000,
+26862.380000,
+26861.710000,
+27159.650000,
+28519.470000,
+28415.750000,
+28328.340000,
+28719.810000,
+29682.950000,
+29918.410000,
+29993.900000,
+33086.230000,
+33901.530000,
+34502.820000,
+34156.650000,
+33909.800000,
+34089.570000,
+34538.480000,
+34502.360000,
+34667.780000,
+35437.250000,
+34938.240000,
+34732.320000,
+35082.200000,
+35049.360000,
+35037.370000,
+35443.560000,
+35655.280000,
+36693.130000,
+37313.970000,
+37138.050000,
+37054.520000,
+36502.360000,
+35537.640000,
+37880.580000,
+36154.770000,
+36596.680000,
+36585.700000,
+37386.550000,
+37476.960000,
+35813.810000,
+37432.340000,
+37289.620000,
+37720.280000,
+37796.790000,
+37479.120000,
+37254.170000,
+37831.090000,
+37858.490000,
+37712.750000,
+38688.750000,
+39476.330000,
+39978.390000,
+41980.100000,
+44080.650000,
+43746.450000,
+43292.660000,
+44166.600000,
+43725.980000,
+43779.700000,
+41243.830000,
+41450.220000,
+42890.740000,
+43023.970000,
+41929.760000,
+42240.120000,
+41364.660000,
+42623.540000,
+42270.530000,
+43652.250000,
+43869.150000,
+43997.900000,
+43739.540000,
+43016.120000,
+43613.140000,
+42520.400000,
+43442.860000,
+42627.860000,
+42099.400000,
+42156.900000,
+42265.190000,
+44167.330000,
diff --git a/example/pool.c b/example/pool.c
index 8b1fafd..5c985e4 100755..100644
--- a/example/pool.c
+++ b/example/pool.c
@@ -1,7 +1,19 @@
+// pool.c - Example usage of pool.h memory pool
+
#define POOL_IMPLEMENTATION
#include "../include/pool.h"
-int main() {
- pool_t*p = pool_init(10, 100);
- pool_print(p);
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ pool_t *p = pool_init(16, 100);
+ if (!p) {
+ fprintf(stderr, "Failed to initialize pool\n");
+ return 1;
+ }
+ printf("capacity: %u\n", pool_capacity(p));
+ printf("size: %u\n", pool_size(p));
+ pool_fini(&p);
+ return 0;
} \ No newline at end of file
diff --git a/example/pushdown.c b/example/pushdown.c
index ada8d73..bd34bb8 100644
--- a/example/pushdown.c
+++ b/example/pushdown.c
@@ -1,13 +1,20 @@
-typedef double pushdown_value_t;
+// pushdown.c - Example usage of pushdown.h stack automaton
+
+#include <stdio.h>
+
+typedef double stack_value_t;
#define STACK_IMPLEMENTATION
+#include "../include/stack.h"
+
+typedef double pushdown_value_t;
#define PUSHDOWN_IMPLEMENTATION
#include "../include/pushdown.h"
int main() {
pushdown_instruction_t instructions[] = {PUSH, COPY, MUL, END};
- stack_t input = {};
- stack_t output = {};
+ stack_t input = {0};
+ stack_t output = {0};
stack_init(&input);
stack_init(&output);
@@ -19,4 +26,7 @@ int main() {
while (output.size) {
printf("%lf\n", stack_pop(&output));
}
+
+ stack_free(&input);
+ stack_free(&output);
} \ No newline at end of file
diff --git a/example/stack.c b/example/stack.c
index 93128f4..d5d2e5a 100644
--- a/example/stack.c
+++ b/example/stack.c
@@ -1,20 +1,27 @@
+// stack.c - Example usage of stack.h dynamic stack
+
+#include <stdio.h>
+
typedef double stack_value_t;
#define STACK_IMPLEMENTATION
#include "../include/stack.h"
-void main() {
- stack_t stack = { .capacity = 10 };
- stack_init(&stack);
+int main(void) {
+ stack_t stack = {0};
+ stack.capacity = 10;
+ stack_init(&stack);
+
+ for (int i = 0; i < 15; i++) {
+ stack_push(&stack, (stack_value_t)i);
+ }
+
+ printf("%lf\n", stack_peek(&stack));
+ printf("%lf\n", stack_pop(&stack));
- for(int i = 0; i < 15; i++) {
- stack_push(&stack, (stack_value_t) i);
- }
-
- printf("%lf\n", stack_peek(&stack));
+ for (int i = 0; i < 14; i++) {
printf("%lf\n", stack_pop(&stack));
+ }
- for(int i = 0; i < 15; i++) {
- printf("%lf\n", stack_pop(&stack));
- }
+ stack_free(&stack);
} \ No newline at end of file
diff --git a/example/ufo.c b/example/ufo.c
new file mode 100755
index 0000000..fcd1b5c
--- /dev/null
+++ b/example/ufo.c
@@ -0,0 +1,22 @@
+// ufo.c - Example usage of ufo.h Universal Function Originator
+
+#define UFO_IMPLEMENTATION
+#include "../include/ufo.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int main(void) {
+ ufo_t *ufo = ufo_init(1, 100);
+ if (!ufo) {
+ fprintf(stderr, "Failed to initialize UFO\n");
+ return 1;
+ }
+ printf("%f\n", ufo_evaluate(ufo, (f64[]){1.0}));
+ ufo_free(ufo);
+
+ return 0;
+}
+
diff --git a/include/ann.h b/include/ann.h
index 087daa5..e34176a 100755
--- a/include/ann.h
+++ b/include/ann.h
@@ -1,10 +1,14 @@
-// ann.h - Artificial Neural Nework Libray
+// ann.h - Artificial Neural Network Library
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Memory: Single-allocation design — all neurons, weights, and deltas
+// stored in one contiguous buffer for easy persistence and cloning.
+// Use ann_copy for deep clone; ann_free to release.
#ifndef ANN_H
#define ANN_H
-#include <stdint.h>
-#include "./type.h"
+#include "type.h"
#ifdef __cplusplus
extern "C" {
@@ -21,64 +25,66 @@ typedef enum {
} ann_activation_t;
typedef struct {
+ void *buffer;
+
// The full size of the allocated structure
- uint_t n;
+ u64 n;
// The number of layers in the neural network
- uint_t layer_n;
+ u64 layer_n;
// The neuron count ( hidden )
- uint_t neuron_n;
+ u64 neuron_n;
// The total number of weights and biases
- uint_t weight_n;
+ u64 weight_n;
// The number of neurons in each layer
- uint_t *layer_neuron_n;
+ u64 *layer_neuron_n;
// The pointer to the neurons
- fp_t *neuron;
+ f64 *neuron;
// The weights and biases for each neuron
// - w_lji denotes the connection between the jth neuron in layer l, and
// the ith neuron in the previous layer
// - b_lj denotes the bias for the jth neuron in layer l
// { w_000, w_001, w_002, .... , b_00, ...., w_ijk, b_ij }`
- fp_t *weight;
+ f64 *weight;
// The delta between between the actual and the cost function
- fp_t *delta;
+ f64 *delta;
// The activation function used in the hidden layer neurons
- fp_t (*activation_hidden)(fp_t);
+ f64 (*activation_hidden)(f64);
// The partial derivative of the activation function used in the hidden
// layer neurons
- fp_t (*activation_hidden_partial)(fp_t);
+ f64 (*activation_hidden_partial)(f64);
// The activation function used in the ouput neurons
- fp_t (*activation_output)(fp_t);
+ f64 (*activation_output)(f64);
// The partial derivative of the activation function used in the output
// layer neurons
- fp_t (*activation_output_partial)(fp_t);
+ f64 (*activation_output_partial)(f64);
} ann_t;
-ann_t *ann_init(uint_t, uint_t *);
-ann_t *ann_copy(ann_t const *);
+int ann_init(ann_t *, u64, u64 *);
+void ann_copy(ann_t *, ann_t const *);
void ann_free(ann_t *);
void ann_random(ann_t *);
-void ann_propagation_forward(ann_t *, fp_t const *const, fp_t *);
-void ann_propagation_backward(ann_t *, fp_t const *, fp_t *, fp_t const *,
- fp_t);
-void ann_train_numeric(ann_t *, fp_t const *, fp_t const *, fp_t);
+void ann_propagation_forward(ann_t *, f64 const *const, f64 *);
+void ann_propagation_backward(ann_t *, f64 const *, f64 *, f64 const *,
+ f64);
+void ann_train_numeric(ann_t *, f64 const *, f64 const *, u64, f64);
-fp_t ann_error_total(fp_t const *, fp_t const *, uint_t);
+f64 ann_error_total(f64 const *, f64 const *, u64);
void ann_set_activation(ann_t *, ann_activation_t, ann_activation_t);
void ann_print_weight(ann_t *);
-void ann_print_neuron(ann_t *, fp_t const *const, fp_t const *const);
+void ann_print_neuron(ann_t *, f64 const *const, f64 const *const);
#ifdef __cplusplus
}
@@ -87,7 +93,7 @@ void ann_print_neuron(ann_t *, fp_t const *const, fp_t const *const);
#endif // ANN_H
////////////////////////////////////////////////////////////////////////////////
-// IMPELEMENTATION SECTION
+// IMPLEMENTATION SECTION
////////////////////////////////////////////////////////////////////////////////
#ifdef ANN_IMPLEMENTATION
@@ -108,27 +114,27 @@ void ann_print_neuron(ann_t *, fp_t const *const, fp_t const *const);
#define ELU_ALPHA 0.2
#define LRELU_ALPHA 0.2
-static fp_t ann_random_range(fp_t, fp_t);
-
-static fp_t ann_error(fp_t, fp_t);
-static fp_t ann_error_partial(fp_t, fp_t);
+static void ann_zero_weights(ann_t *);
+static f64 ann_random_range(f64, f64);
+static f64 ann_error(f64, f64);
+static f64 ann_error_partial(f64, f64);
-static fp_t ann_activation_identity(fp_t);
-static fp_t ann_activation_identity_partial(fp_t);
-static fp_t ann_activation_binary(fp_t);
-static fp_t ann_activation_binary_partial(fp_t);
-static fp_t ann_activation_sigmoid(fp_t);
-static fp_t ann_activation_sigmoid_partial(fp_t);
-static fp_t ann_activation_relu(fp_t);
-static fp_t ann_activation_relu_partial(fp_t);
-static fp_t ann_activation_elu(fp_t);
-static fp_t ann_activation_elu_partial(fp_t);
-static fp_t ann_activation_lrelu(fp_t);
-static fp_t ann_activation_lrelu_partial(fp_t);
-static fp_t ann_activation_tanh(fp_t);
-static fp_t ann_activation_tanh_partial(fp_t);
+static f64 ann_activation_identity(f64);
+static f64 ann_activation_identity_partial(f64);
+static f64 ann_activation_binary(f64);
+static f64 ann_activation_binary_partial(f64);
+static f64 ann_activation_sigmoid(f64);
+static f64 ann_activation_sigmoid_partial(f64);
+static f64 ann_activation_relu(f64);
+static f64 ann_activation_relu_partial(f64);
+static f64 ann_activation_elu(f64);
+static f64 ann_activation_elu_partial(f64);
+static f64 ann_activation_lrelu(f64);
+static f64 ann_activation_lrelu_partial(f64);
+static f64 ann_activation_tanh(f64);
+static f64 ann_activation_tanh_partial(f64);
-static fp_t (*ACTIVATION[][2])(fp_t) = {
+static f64 (*ACTIVATION[][2])(f64) = {
{ann_activation_identity, ann_activation_identity_partial}, // IDENTITY
{ann_activation_binary, ann_activation_binary_partial}, // BINARY
{ann_activation_sigmoid, ann_activation_sigmoid_partial}, // SIGMOID
@@ -138,103 +144,135 @@ static fp_t (*ACTIVATION[][2])(fp_t) = {
{ann_activation_tanh, ann_activation_tanh_partial}, // TANH
};
-// ann_init()
-//
-// Initialize the neural network
-//
-// layer_n - The layer count for the layer count
-// layer_neuron_n - An array of neuron counts for each layer
-//
-// return - The created ann_t instance
-
-ann_t *ann_init(uint_t layer_n, uint_t *layer_neuron_n) {
- assert(layer_n >= 2);
+int ann_init(ann_t *ann, u64 layer_n, u64 *layer_neuron_n) {
+ if (!ann || !layer_neuron_n || layer_n < 2)
+ return 0;
- uint_t neuron_n = 0;
- uint_t weight_n = 0;
+ u64 neuron_n = 0;
+ u64 weight_n = 0;
- // Calculate the number of neurons and weights / biases
- uint_t l = 1;
+ u64 l = 1;
for (; l < layer_n - 1; l++) {
+ if (neuron_n > UINT64_MAX - layer_neuron_n[l])
+ return 0;
neuron_n += layer_neuron_n[l];
- weight_n += layer_neuron_n[l] * (layer_neuron_n[l - 1] + 1);
+ if (layer_neuron_n[l - 1] == UINT64_MAX)
+ return 0;
+ {
+ u64 rhs = layer_neuron_n[l - 1] + 1;
+ if (layer_neuron_n[l] > UINT64_MAX / rhs)
+ return 0;
+ u64 prod = layer_neuron_n[l] * rhs;
+ if (weight_n > UINT64_MAX - prod)
+ return 0;
+ weight_n += prod;
+ }
+ }
+
+ if (layer_neuron_n[l - 1] == UINT64_MAX)
+ return 0;
+ {
+ u64 rhs = layer_neuron_n[l - 1] + 1;
+ if (layer_neuron_n[l] > UINT64_MAX / rhs)
+ return 0;
+ u64 prod = layer_neuron_n[l] * rhs;
+ if (weight_n > UINT64_MAX - prod)
+ return 0;
+ weight_n += prod;
}
- weight_n += layer_neuron_n[l] * (layer_neuron_n[l - 1] + 1);
+ if (layer_n > SIZE_MAX / sizeof(u64))
+ return 0;
+ size_t layer_neuron_sz = sizeof(u64) * layer_n;
+ size_t padding = (sizeof(f64) - (layer_neuron_sz % sizeof(f64))) % sizeof(f64);
- uint_t n =
- sizeof(ann_t) + // ANN
- (sizeof(uint_t) * layer_n) + // layer_neuron_n[]
- (sizeof(fp_t) * (neuron_n + // neuron[]
- weight_n + // weight[]
- neuron_n + layer_neuron_n[layer_n - 1])); // delta[]
+ u64 total_fp = neuron_n + weight_n;
+ if (total_fp < neuron_n || total_fp < weight_n)
+ return 0;
+ if (total_fp > UINT64_MAX - neuron_n)
+ return 0;
+ total_fp += neuron_n;
+ if (total_fp > UINT64_MAX - layer_neuron_n[layer_n - 1])
+ return 0;
+ total_fp += layer_neuron_n[layer_n - 1];
+ if (total_fp > SIZE_MAX / sizeof(f64))
+ return 0;
+ u64 n = (u64)(layer_neuron_sz + padding + sizeof(f64) * total_fp);
- // Allocate everything as one structure
- ann_t *ann = (ann_t *)malloc(n);
+ ann->buffer = malloc(n);
+ if (!ann->buffer)
+ return 0;
- // ann_t | layer_neuron_n[] | neuron[] | weight[] | delta[]
ann->n = n;
ann->layer_n = layer_n;
ann->weight_n = weight_n;
ann->neuron_n = neuron_n;
- ann->layer_neuron_n = (uint_t *)((uint8_t *)ann + sizeof(ann_t));
- memcpy(ann->layer_neuron_n, layer_neuron_n, sizeof(uint_t) * layer_n);
- ann->neuron = (fp_t *)(ann->layer_neuron_n + ann->layer_n);
+ ann->layer_neuron_n = (u64 *)ann->buffer;
+ memcpy(ann->layer_neuron_n, layer_neuron_n, sizeof(u64) * layer_n);
+ ann->neuron = (f64 *)((char *)ann->buffer + layer_neuron_sz + padding);
ann->weight = ann->neuron + ann->neuron_n;
ann->delta = ann->weight + ann->weight_n;
ann_set_activation(ann, SIGMOID, SIGMOID);
+ ann_zero_weights(ann);
+ return 1;
+}
- return ann;
+void ann_free(ann_t *ann) {
+ if (ann) {
+ free(ann->buffer);
+ ann->buffer = NULL;
+ ann->layer_neuron_n = NULL;
+ ann->neuron = NULL;
+ ann->weight = NULL;
+ ann->delta = NULL;
+ }
}
-// ann_free()
-//
-// Free the neural network's memory
-//
-// ann - The instance of ann_t to free
+void ann_copy(ann_t *dest, ann_t const *source) {
+ if (!dest || !source || !source->buffer)
+ return;
+ void *new_buffer = malloc(source->n);
+ if (!new_buffer)
+ return;
-void ann_free(ann_t *ann) { free(ann); }
+ memcpy(new_buffer, source->buffer, source->n);
-// ann_copy()
-//
-// Make a copy of the given ann_t instance
-//
-// ann - The instance of ann_t to copy
-//
-// return - The copied ann_t instance
+ if (dest->buffer)
+ free(dest->buffer);
-ann_t *ann_copy(ann_t const *ann) {
- ann_t *copy = (ann_t *)malloc(ann->n);
- memcpy(copy, ann, ann->n);
+ size_t layer_neuron_sz = sizeof(u64) * source->layer_n;
+ size_t padding = (sizeof(f64) - (layer_neuron_sz % sizeof(f64))) % sizeof(f64);
- return copy;
+ dest->buffer = new_buffer;
+ dest->n = source->n;
+ dest->layer_n = source->layer_n;
+ dest->weight_n = source->weight_n;
+ dest->neuron_n = source->neuron_n;
+ dest->layer_neuron_n = (u64 *)dest->buffer;
+ dest->neuron = (f64 *)((char *)dest->buffer + layer_neuron_sz + padding);
+ dest->weight = dest->neuron + source->neuron_n;
+ dest->delta = dest->weight + source->weight_n;
+ dest->activation_hidden = source->activation_hidden;
+ dest->activation_hidden_partial = source->activation_hidden_partial;
+ dest->activation_output = source->activation_output;
+ dest->activation_output_partial = source->activation_output_partial;
}
-// ann_propagation_forward()
-//
-// Perform forward propagation on the ann_t instance
-//
-// ann - The ann_t instance to perform the propagation on
-// input - An array containing the input vector
-// output - The destination array for the output vector
-
-void ann_propagation_forward(ann_t *ann, fp_t const *const input,
- fp_t *output) {
- // printf("ann:\t\t%p\n", ann);
- // printf("output:\t\t%p\n", output);
- fp_t *w_ij = ann->weight;
- // printf("ann->weight:\t%p\n", w_ij);
- fp_t const *x = input; // Input neuron into y
- fp_t *y = ann->neuron; // The current neuron being calculated
+void ann_propagation_forward(ann_t *ann, f64 const *const input,
+ f64 *output) {
+ if (!ann || !input || !output)
+ return;
+ f64 *w_ij = ann->weight;
+ f64 const *x = input;
+ f64 *y = ann->neuron;
- uint_t l = 1;
+ u64 l = 1;
for (; l < ann->layer_n - 1; l++) {
- for (uint_t j = 0; j < ann->layer_neuron_n[l]; j++) {
- // printf("neuron[%u]:\t%p\n", j, &(y[j]));
+ for (u64 j = 0; j < ann->layer_neuron_n[l]; j++) {
y[j] = 0;
- for (uint_t i = 0; i < ann->layer_neuron_n[l - 1]; i++) {
+ for (u64 i = 0; i < ann->layer_neuron_n[l - 1]; i++) {
y[j] += x[i] * *w_ij++;
}
@@ -246,12 +284,10 @@ void ann_propagation_forward(ann_t *ann, fp_t const *const input,
y += ann->layer_neuron_n[l];
}
- // Last layer
- for (uint_t j = 0; j < ann->layer_neuron_n[l]; j++) {
- // printf("output[%u]:\t%p\n", j, &(output[j]));
+ for (u64 j = 0; j < ann->layer_neuron_n[l]; j++) {
output[j] = 0;
- for (uint_t i = 0; i < ann->layer_neuron_n[l - 1]; i++) {
+ for (u64 i = 0; i < ann->layer_neuron_n[l - 1]; i++) {
output[j] += x[i] * *w_ij++;
}
@@ -260,321 +296,253 @@ void ann_propagation_forward(ann_t *ann, fp_t const *const input,
}
}
-// ann_propagation_backward()
-//
-// Perform backpropagation on the ann_t instance
-//
-// ann - The ann_t instance to perform backpropagation upon
-// input - Input vector array
-// output - Output vector array
-// target - Target output vector array
-// rate - Learning rate
-
-void ann_propagation_backward(ann_t *ann, fp_t const *input, fp_t *output,
- fp_t const *target, fp_t rate) {
- int_t l = ann->layer_n - 1;
- uint_t i, j, q;
+void ann_propagation_backward(ann_t *ann, f64 const *input, f64 *output,
+ f64 const *target, f64 rate) {
+ if (!ann || !input || !output || !target)
+ return;
+ i64 l = (i64)ann->layer_n - 1;
+ u64 i = 0, j = 0, q = 0;
- // First output layer delta
- fp_t *d_j = ann->delta + ann->neuron_n;
+ f64 *d_j = ann->delta + ann->neuron_n;
- // Output Deltas
for (j = 0; j < ann->layer_neuron_n[l]; j++) {
- d_j[j] = ann->activation_output_partial(output[j]) *
- ann_error_partial(output[j], target[j]);
+ d_j[j] = ann_error_partial(output[j], target[j]) * ann->activation_output_partial(output[j]);
}
- // First weight in the set between the last layer and the current
- fp_t *w_jq = ann->weight + ann->weight_n -
- ann->layer_neuron_n[l] * ann->layer_neuron_n[l - 1] -
- ann->layer_neuron_n[l];
+ {
+ u64 last_layer_n = ann->layer_neuron_n[l];
+ u64 prev_layer_plus_bias = ann->layer_neuron_n[l - 1] + 1;
+ if (last_layer_n > UINT64_MAX / prev_layer_plus_bias)
+ return;
+ f64 *w_jq = ann->weight + ann->weight_n - last_layer_n * prev_layer_plus_bias;
- fp_t *o_j = ann->neuron + ann->neuron_n;
- fp_t *d_q;
+ f64 *o_j = ann->neuron + ann->neuron_n;
+ f64 *d_q;
- l--;
+ l--;
- // Hidden Deltas
- for (; l > 0; --l) {
- d_q = d_j;
- d_j -= ann->layer_neuron_n[l];
- o_j -= ann->layer_neuron_n[l];
+ for (; l > 0; --l) {
+ d_q = d_j;
+ d_j -= ann->layer_neuron_n[l];
+ o_j -= ann->layer_neuron_n[l];
- for (j = 0; j < ann->layer_neuron_n[l]; j++) {
- d_j[j] = 0;
-
- for (q = 0; q < ann->layer_neuron_n[l + 1]; q++) {
- d_j[j] += w_jq[q * (ann->layer_neuron_n[l] + 1) + j] * d_q[q];
+ for (j = 0; j < ann->layer_neuron_n[l]; j++) {
+ d_j[j] = 0;
+ for (q = 0; q < ann->layer_neuron_n[l + 1]; q++) {
+ d_j[j] += w_jq[q * (ann->layer_neuron_n[l] + 1) + j] * d_q[q];
+ }
+ d_j[j] *= ann->activation_hidden_partial(o_j[j]);
}
- d_j[j] *= ann->activation_hidden_partial(o_j[j]);
+ if (l > 1) {
+ u64 shift = ann->layer_neuron_n[l];
+ u64 prev_plus_bias = ann->layer_neuron_n[l - 1] + 1;
+ if (shift <= UINT64_MAX / prev_plus_bias) {
+ w_jq -= shift * prev_plus_bias;
+ } else {
+ return;
+ }
+ }
}
-
- w_jq -= (ann->layer_neuron_n[l - 1] * ann->layer_neuron_n[l] +
- ann->layer_neuron_n[l - 1]);
}
- fp_t *w_ij = ann->weight;
+ f64 *w_ij = ann->weight;
d_j = ann->delta;
l = 1;
- // Input training
for (j = 0; j < ann->layer_neuron_n[l]; j++) {
for (i = 0; i < ann->layer_neuron_n[l - 1]; i++) {
*w_ij -= rate * input[i] * d_j[j];
w_ij++;
}
-
*w_ij -= rate * d_j[j];
w_ij++;
}
l++;
- fp_t *i_i = ann->neuron;
+ f64 *i_i = ann->neuron;
- // Hidden training
- for (; l < (int_t)ann->layer_n; l++) {
+ for (; l < (i64)ann->layer_n; l++) {
d_j += ann->layer_neuron_n[l - 1];
-
for (j = 0; j < ann->layer_neuron_n[l]; j++) {
for (i = 0; i < ann->layer_neuron_n[l - 1]; i++) {
*w_ij -= rate * i_i[i] * d_j[j];
w_ij++;
}
-
*w_ij -= rate * d_j[j];
w_ij++;
}
-
i_i += ann->layer_neuron_n[l - 1];
}
}
-// ann_activation_identity()
-//
-// y = x
-// dy/dx = 1
-
-static fp_t ann_activation_identity(fp_t x) { return x; }
-static fp_t ann_activation_identity_partial(fp_t x) { return 1; }
-
-// ann_activation_binary()
-//
-// y = { x < 0 -> 0, x > 0 -> 1 }
-// dy/dx = 0
+static f64 ann_activation_identity(f64 x) { return x; }
+static f64 ann_activation_identity_partial(f64 x) { (void)x; return 1.0; }
-static fp_t ann_activation_binary(fp_t x) {
- return (x > 0.0) ? 1.0 : (x < 0.0) ? -1.0 : 0.0;
+static f64 ann_activation_binary(f64 x) {
+ return (x >= 0.0) ? 1.0 : 0.0;
}
-static fp_t ann_activation_binary_partial(fp_t x) { return 0.0; }
-
-// ann_activation_sign()
-//
-// y = { x < 0 -> -1, x = 0 -> 0, x > 0 -> 1 }
-// dy/dx = 0
-
-static fp_t ann_activation_sign(fp_t x) { return (x > 0.0) ? 1.0 : 0.0; }
-static fp_t ann_activation_sign_partial(fp_t x) { return 0.0; }
-
-// ann_activation_sigmoid()
-//
-// y = 1.0 / ( 1.0 + e^-x )
-// dy/dx = f(x) * ( 1.0 - f(x) )
+static f64 ann_activation_binary_partial(f64 x) { (void)x; return 0.0; }
-static fp_t ann_activation_sigmoid(fp_t x) { return 1.0 / (1.0 + exp(-x)); }
-static fp_t ann_activation_sigmoid_partial(fp_t x) { return x * (1.0 - x); }
-
-// ann_activation_relu()
-//
-// y = { x < 0 -> 0, x >= 0 -> x }
-// dy/dx = { x < 0 -> 0, x >= 0 -> 1 }
+static f64 ann_activation_sigmoid(f64 x) {
+ return (x >= 0) ? (1.0 / (1.0 + exp(-x))) : (exp(x) / (1.0 + exp(x)));
+}
+static f64 ann_activation_sigmoid_partial(f64 x) {
+ // x is the already-activated sigmoid output σ(z), so σ'(z) = σ(z)(1-σ(z)) = x(1-x)
+ return x * (1.0 - x);
+}
-static fp_t ann_activation_relu(fp_t x) { return (x > 0.0) ? x : 0.0; }
-static fp_t ann_activation_relu_partial(fp_t x) {
+static f64 ann_activation_relu(f64 x) { return (x > 0.0) ? x : 0.0; }
+static f64 ann_activation_relu_partial(f64 x) {
return (x > 0.0) ? 1.0 : 0.0;
}
-// ann_activation_elu()
-//
-// y = { x < 0 -> a * ( e^x - 1 ), x >= 0 -> x }
-// dy/dx = { x < 0 -> x + a, x >= 0 -> 1 }
-
-static fp_t ann_activation_elu(fp_t x) {
+static f64 ann_activation_elu(f64 x) {
return (x > 0.0) ? x : ELU_ALPHA * (expm1(x));
}
-static fp_t ann_activation_elu_partial(fp_t x) {
+static f64 ann_activation_elu_partial(f64 x) {
return (x > 0.0) ? 1.0 : x + ELU_ALPHA;
}
-// ann_activation_lelu()
-//
-// y = { x < 0 -> a * x, x >= 0 -> x }
-// dy/dx = { x < 0 -> a, x >= 0 -> 1 }
-
-static fp_t ann_activation_lrelu(fp_t x) {
+static f64 ann_activation_lrelu(f64 x) {
return (x > 0.0) ? x : LRELU_ALPHA * x;
}
-static fp_t ann_activation_lrelu_partial(fp_t x) {
+static f64 ann_activation_lrelu_partial(f64 x) {
return (x > 0.0) ? 1.0 : LRELU_ALPHA;
}
-// ann_activation_tanh()
-//
-// y = sinh( x ) / cosh( x ) = ( e^2x - 1 ) / ( e^2x + 1 )
-// dy/dx = 1.0 - f(x)^2
-
-static fp_t ann_activation_tanh(fp_t x) { return tanh(x); }
-static fp_t ann_activation_tanh_partial(fp_t x) { return 1.0 - (x * x); }
+static f64 ann_activation_tanh(f64 x) { return tanh(x); }
+static f64 ann_activation_tanh_partial(f64 x) { return 1.0 - (x * x); }
-// ann_error()
-//
-// Implementation of mean squared error for calculating the difference between
-// the current and target outputs for the ann_t instance
-//
-// output - The current ann_t output
-// target - The target ann_t output
+static f64 ann_error(f64 output, f64 target) {
+ f64 error = output - target;
+ return 0.5 * error * error;
+}
-fp_t ann_error(fp_t output, fp_t target) {
- fp_t error = output - target;
- error = 0.5 * error * error;
+static f64 ann_error_partial(f64 output, f64 target) { return (output - target); }
+f64 ann_error_total(f64 const *output, f64 const *target, u64 n) {
+ if (!output || !target)
+ return 0;
+ f64 error = 0;
+ for (u64 i = 0; i < n; i++) {
+ error += ann_error(output[i], target[i]);
+ }
return error;
}
-// ann_error_partial()
-//
-// The partial derivative of mean squared error where n = 2
-//
-// output - The current ann_t output
-// target - The target ann_t output
-
-fp_t ann_error_partial(fp_t output, fp_t target) { return (output - target); }
-
-// ann_error_total()
-//
-//
-//
-// output - The array of current outputs
-// target - The array of target outputs
-// n - The size of the output and target arrays
+void ann_train_numeric(ann_t *ann, f64 const *input, f64 const *target,
+ u64 n, f64 rate) {
+ if (!ann || !input || !target || rate == 0)
+ return;
+ if (n > SIZE_MAX / sizeof(f64))
+ return;
+ f64 *output = malloc(n * sizeof(f64));
+ if (!output)
+ return;
-fp_t ann_error_total(fp_t const *output, fp_t const *target, uint_t n) {
- fp_t error = 0;
- for (uint_t i = 0; i < n; i++) {
- error += ann_error(output[i], target[i]);
+ for (u64 w = 0; w < ann->weight_n; w++) {
+ f64 original = ann->weight[w];
+ ann->weight[w] = original + rate;
+ ann_propagation_forward(ann, input, output);
+ f64 error_p = ann_error_total(output, target, n);
+ ann->weight[w] = original - rate;
+ ann_propagation_forward(ann, input, output);
+ f64 error_m = ann_error_total(output, target, n);
+ f64 gradient = (error_p - error_m) / (2.0 * rate);
+ ann->weight[w] = original - rate * gradient;
}
- return error;
+ free(output);
}
-// ann_set_activation()
-//
-// Set the activation functions for the hidden and output layers
-//
-// ann - The current ann_t instance
-// activation_hidden - The hidden layer activation function
-// activation_output - The output layer activation function
-
void ann_set_activation(ann_t *ann, ann_activation_t activation_hidden,
- ann_activation_t activation_output) {
+ ann_activation_t activation_output) {
+ if (!ann)
+ return;
+ if ((int)activation_hidden < 0 || (int)activation_output < 0)
+ return;
+ if (activation_hidden > TANH || activation_output > TANH)
+ return;
ann->activation_hidden = ACTIVATION[activation_hidden][0];
ann->activation_hidden_partial = ACTIVATION[activation_hidden][1];
ann->activation_output = ACTIVATION[activation_output][0];
ann->activation_output_partial = ACTIVATION[activation_output][1];
}
-// ann_random()
-//
-// Set the weights of the ann_t instance to random numbers in the [-1, 1] range
-//
-// ann - The current ann_t instance
+static void ann_zero_weights(ann_t *ann) {
+ if (!ann)
+ return;
+ memset(ann->weight, 0, ann->weight_n * sizeof(f64));
+}
void ann_random(ann_t *ann) {
- uint_t l, j, i;
- fp_t *wb = ann->weight;
+ if (!ann)
+ return;
+ u64 l, j, i;
+ f64 *wb = ann->weight;
for (l = 1; l < ann->layer_n; l++) {
+ if (ann->layer_neuron_n[l - 1] == 0)
+ continue;
+ if (ann->layer_neuron_n[l] == 0)
+ continue;
+ f64 fan_in = (f64)ann->layer_neuron_n[l - 1];
+ f64 fan_out = (f64)ann->layer_neuron_n[l];
+ f64 range = sqrt(6.0 / (fan_in + fan_out));
for (j = 0; j < ann->layer_neuron_n[l]; j++) {
- i = 0;
- for (; i < ann->layer_neuron_n[l - 1]; i++) {
- *wb++ = ann_random_range(-1.0, 1.0);
+ for (i = 0; i < ann->layer_neuron_n[l - 1]; i++) {
+ *wb++ = ann_random_range(-range, range);
}
-
*wb++ = 0;
}
}
}
-// ann_random_range()
-//
-// Remap a random value to a given range
-//
-// low - The lower bounds of the range
-// high - The upper bounds of the range
-
-static fp_t ann_random_range(fp_t low, fp_t high) {
- return (low + ((fp_t)rand()) * (high - low) / RAND_MAX);
+static f64 ann_random_range(f64 low, f64 high) {
+ return (low + ((f64)rand()) * (high - low) / (f64)RAND_MAX);
}
-// ann_print_neuron()
-//
-// Print a text representation of the neurons in a given ann_t instance
-//
-// ann - The current ann_t instance
-// input - The input used for the ann_t propagation being printed
-// output - The output used for the ann_t propagation being printed
-
-void ann_print_neuron(ann_t *ann, fp_t const *const input,
- fp_t const *const output) {
+void ann_print_neuron(ann_t *ann, f64 const *const input,
+ f64 const *const output) {
+ if (!ann || !input || !output)
+ return;
fputs("-> ", stderr);
- for (uint_t i = 0; i < ann->layer_neuron_n[0]; i++) {
+ for (u64 i = 0; i < ann->layer_neuron_n[0]; i++) {
fprintf(stderr, " %+.*f", PRINT_PRECISION, input[i]);
}
-
fputs("\n", stderr);
- fp_t *n = ann->neuron;
-
- for (uint_t l = 1; l < ann->layer_n - 1; l++) {
- fprintf(stderr, " ", l);
- for (uint_t i = 0; i < ann->layer_neuron_n[l]; i++) {
+ f64 *n = ann->neuron;
+ for (u64 l = 1; l < ann->layer_n - 1; l++) {
+ fprintf(stderr, " ");
+ for (u64 i = 0; i < ann->layer_neuron_n[l]; i++) {
fprintf(stderr, " %+.*f", PRINT_PRECISION, *n);
n++;
}
-
fputs("\n", stderr);
}
fputs("= ", stderr);
- for (uint_t i = 0; i < ann->layer_neuron_n[ann->layer_n - 1]; i++) {
+ for (u64 i = 0; i < ann->layer_neuron_n[ann->layer_n - 1]; i++) {
fprintf(stderr, " %+.*f", PRINT_PRECISION, output[i]);
}
-
fputs("\n\n", stderr);
}
-// ann_print_weight()
-//
-// Print a text representation of weights in the given ann_t instance
-//
-// ann - The current ann_t instance
-
void ann_print_weight(ann_t *ann) {
- fp_t *weight = ann->weight;
-
- for (uint_t i = 1; i < ann->layer_n; i++) {
- for (uint_t j = 0; j < ann->layer_neuron_n[i - 1] + 1; j++) {
- for (uint_t k = 0; k < ann->layer_neuron_n[i]; k++) {
+ if (!ann)
+ return;
+ f64 *weight = ann->weight;
+ for (u64 i = 1; i < ann->layer_n; i++) {
+ for (u64 j = 0; j < ann->layer_neuron_n[i - 1] + 1; j++) {
+ for (u64 k = 0; k < ann->layer_neuron_n[i]; k++) {
fprintf(stderr, " %+.*f", PRINT_PRECISION,
- *(weight + j + k * (ann->layer_neuron_n[i - 1] + 1)));
+ *(weight + k * (ann->layer_neuron_n[i - 1] + 1) + j));
}
-
fputs("\n", stderr);
}
-
- weight += ann->layer_neuron_n[i - 1] * ann->layer_neuron_n[i] +
- ann->layer_neuron_n[i];
-
+ weight += ann->layer_neuron_n[i] * (ann->layer_neuron_n[i - 1] + 1);
fputs("\n", stderr);
}
}
diff --git a/include/bin.h b/include/bin.h
index d764f09..d06b074 100755
--- a/include/bin.h
+++ b/include/bin.h
@@ -1,47 +1,77 @@
// bin.h - flat file, fixed block size, time series database
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Behavior: No input validation — out-of-order data persists as-is.
+// Unbuffered I/O by default (use _buffered variants for performance).
+// _ok variants return 1 on success, 0 on any error (invalid args, I/O fail,
+// overflow). Non-_ok variants remain for backward compatibility.
#ifndef BIN_H
#define BIN_H
+/* USER-DEFINED (define before including):
+ * typedef <numerical_type> bin_key_t;
+ */
+
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include "type.h"
typedef struct {
- uint_t length;
- uint_t block_size;
+ u64 length;
+ u64 block_size;
} bin_meta_t;
-FILE *bin_init(char const *, uint_t);
+FILE *bin_init(char const *, u64);
+FILE *bin_init_buffered(char const *, u64);
FILE *bin_open(char const *path);
-void bin_close(FILE *);
-void bin_read(FILE *, int_t, void *, uint_t);
-void bin_write(FILE *, int_t, void *, uint_t);
-void bin_insert(FILE *, int_t, void *, uint_t);
-void bin_append(FILE *, void *, uint_t);
+FILE *bin_open_buffered(char const *path);
+int bin_read(FILE *, i64, void *, u64);
+void bin_write(FILE *, i64, void *, u64);
+int bin_write_ok(FILE *, i64, void *, u64);
+void bin_insert(FILE *, i64, void *, u64);
+int bin_insert_ok(FILE *, i64, void *, u64);
+void bin_append(FILE *, void *, u64);
+int bin_append_ok(FILE *, void *, u64);
+int bin_close(FILE *);
-uint_t bin_length(FILE *);
-uint_t bin_block_size(FILE *);
+u64 bin_length(FILE *);
+u64 bin_block_size(FILE *);
-int_t bin_search(FILE *, bin_key_t);
-uint_t bin_fuzzy_index(int_t);
+i64 bin_search(FILE *, bin_key_t);
+u64 bin_fuzzy_index(i64);
#endif // BIN_H
#ifdef BIN_IMPLEMENTATION // IMPLEMENTATION
-#include <assert.h>
#include <stdlib.h>
-static void bin_write_length(FILE *, uint_t);
-static void bin_write_block_size(FILE *, uint_t);
+static void bin_write_length(FILE *, u64);
+static void bin_write_block_size(FILE *, u64);
-#define index_bytes(i, bs) ((i * bs) + sizeof(bin_meta_t))
+static inline i64 index_bytes_safe(i64 i, u64 bs, u64 *out) {
+ if (bs == 0 || i < 0 || (u64)i > (UINT64_MAX - sizeof(bin_meta_t)) / bs)
+ return -1;
+ *out = (u64)i * bs + sizeof(bin_meta_t);
+ return 0;
+}
#define member_sizeof(type, member) (sizeof(((type *)0)->member))
+static inline int fseek_safe(FILE *f, u64 offset) {
+#ifdef _WIN32
+ return _fseeki64(f, (long long)offset, SEEK_SET);
+#else
+ if ((u64)(long)offset != offset)
+ return -1;
+ return fseek(f, (long)offset, SEEK_SET);
+#endif
+}
+
// bin_init()
//
// Creates the desired binary file
@@ -50,7 +80,9 @@ static void bin_write_block_size(FILE *, uint_t);
//
// return - The newly created file
-FILE *bin_init(char const *path, uint_t block_size) {
+FILE *bin_init(char const *path, u64 block_size) {
+ if (!path)
+ return NULL;
FILE *f = fopen(path, "wb+");
if (!f)
@@ -64,6 +96,20 @@ FILE *bin_init(char const *path, uint_t block_size) {
return f;
}
+FILE *bin_init_buffered(char const *path, u64 block_size) {
+ if (!path)
+ return NULL;
+ FILE *f = fopen(path, "wb+");
+
+ if (!f)
+ return NULL;
+
+ bin_write_length(f, 0);
+ bin_write_block_size(f, block_size);
+
+ return f;
+}
+
// bin_open()
//
// Opens the desired binary file
@@ -73,6 +119,8 @@ FILE *bin_init(char const *path, uint_t block_size) {
// return - The newly opened file
FILE *bin_open(char const *path) {
+ if (!path)
+ return NULL;
FILE *f = fopen(path, "rb+");
if (!f)
@@ -83,13 +131,23 @@ FILE *bin_open(char const *path) {
return f;
}
+FILE *bin_open_buffered(char const *path) {
+ if (!path)
+ return NULL;
+ return fopen(path, "rb+");
+}
+
// bin_close()
//
// Closes the desired binary file
//
// f - The file descriptor to be closed
-void bin_close(FILE *f) { fclose(f); }
+int bin_close(FILE *f) {
+ if (!f) return 0;
+ int r = fclose(f);
+ return r == 0;
+}
// bin_read()
//
@@ -100,13 +158,33 @@ void bin_close(FILE *f) { fclose(f); }
// data - The block_t buffer to be read into
// n - The number of elements to read into the buffer
-void bin_read(FILE *f, int_t i, void *data, uint_t n) {
- uint_t bs = bin_block_size(f);
+int bin_read(FILE *f, i64 i, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
- assert(0 <= i && (i + n) <= bin_length(f));
+ if (i < 0)
+ return 0;
+ u64 len = bin_length(f);
+ if ((u64)i + n > len)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fread(data, bs, n, f);
+ u64 offset = 0;
+ if (index_bytes_safe(i, bs, &offset) != 0)
+ return 0;
+ if (bs > SIZE_MAX / n)
+ return 0;
+ fseek_safe(f, offset);
+ size_t read_n = fread(data, (size_t)bs, (size_t)n, f);
+ if (read_n != n) {
+ if ((n - read_n) > SIZE_MAX / bs)
+ return (int)read_n;
+ memset((char *)data + read_n * bs, 0, (n - read_n) * bs);
+ return (int)read_n;
+ }
+ return (int)n;
}
// bin_write()
@@ -118,20 +196,30 @@ void bin_read(FILE *f, int_t i, void *data, uint_t n) {
// data - The block_t buffer to write from
// n - The number of elements to write from the buffer
-void bin_write(FILE *f, int_t i, void *data, uint_t n) {
- uint_t l = bin_length(f);
- uint_t bs = bin_block_size(f);
+void bin_write(FILE *f, i64 i, void *data, u64 n) {
+ (void)bin_write_ok(f, i, data, n);
+}
- assert(0 <= i && i <= l);
+int bin_write_ok(FILE *f, i64 i, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 l = bin_length(f);
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fwrite(data, bs, n, f);
+ if (i < 0)
+ return 0;
+ if ((u64)i + n > l)
+ return 0;
- if ((i + n) >= l) {
- l += (i + n - l);
- }
-
- bin_write_length(f, l);
+ u64 offset = 0;
+ if (index_bytes_safe(i, bs, &offset) != 0)
+ return 0;
+ if (fseek_safe(f, offset) != 0)
+ return 0;
+ size_t written = fwrite(data, bs, n, f);
+ return written == (size_t)n;
}
// bin_append()
@@ -142,14 +230,31 @@ void bin_write(FILE *f, int_t i, void *data, uint_t n) {
// data - The blocks to append to the file
// n - The number of block to be appended
-void bin_append(FILE *f, void *data, uint_t n) {
- uint_t l = bin_length(f);
- uint_t bs = bin_block_size(f);
+void bin_append(FILE *f, void *data, u64 n) {
+ (void)bin_append_ok(f, data, n);
+}
- fseek(f, index_bytes(l, bs), SEEK_SET);
- fwrite(data, bs, n, f);
+int bin_append_ok(FILE *f, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 l = bin_length(f);
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
+ if (l > UINT64_MAX - n)
+ return 0;
+
+ u64 offset = 0;
+ if (index_bytes_safe((i64)l, bs, &offset) != 0)
+ return 0;
+ if (fseek_safe(f, offset) != 0)
+ return 0;
+ size_t written = fwrite(data, bs, n, f);
+ if (written != (size_t)n)
+ return 0;
bin_write_length(f, l + n);
+ return 1;
}
// bin_insert()
@@ -161,24 +266,59 @@ void bin_append(FILE *f, void *data, uint_t n) {
// data - The blocks to insert into the file
// n - The number of blocks to be inserted
-void bin_insert(FILE *f, int_t i, void *data, uint_t n) {
- uint_t l = bin_length(f);
- uint_t bs = bin_block_size(f);
+void bin_insert(FILE *f, i64 i, void *data, u64 n) {
+ (void)bin_insert_ok(f, i, data, n);
+}
+
+int bin_insert_ok(FILE *f, i64 i, void *data, u64 n) {
+ if (!f || !data || n == 0)
+ return 0;
+ u64 l = bin_length(f);
+ u64 bs = bin_block_size(f);
+ if (bs == 0)
+ return 0;
- assert(0 <= i && i <= l);
+ if (i < 0 || (u64)i > l)
+ return 0;
- uint_t tmp_n = (l - i) * bs;
+ u64 blocks_to_move = l - (u64)i;
+ if (blocks_to_move > UINT64_MAX / bs)
+ return 0;
- uint8_t tmp[tmp_n];
+ if (l > UINT64_MAX - n)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fread(tmp, bs, l - i, f);
+ u64 offset = 0;
+ if (index_bytes_safe(i, bs, &offset) != 0)
+ return 0;
+ if (fseek_safe(f, offset) != 0)
+ return 0;
- fseek(f, index_bytes(i, bs), SEEK_SET);
- fwrite(data, bs, n, f);
- fwrite(tmp, 1, tmp_n, f);
+ if (blocks_to_move > 0) {
+ u64 tmp_n = blocks_to_move * bs;
+ if (tmp_n > SIZE_MAX)
+ return 0;
+ u8 *tmp = malloc((size_t)tmp_n);
+ if (tmp == NULL)
+ return 0;
+ size_t rn = fread(tmp, (size_t)bs, (size_t)blocks_to_move, f);
+ if (rn != (size_t)blocks_to_move) {
+ memset((char *)tmp + rn * (size_t)bs, 0, ((size_t)blocks_to_move - rn) * (size_t)bs);
+ }
+
+ if (fseek_safe(f, offset) != 0) { free(tmp); return 0; }
+ size_t written = fwrite(data, bs, n, f);
+ if (written != (size_t)n) { free(tmp); return 0; }
+ fwrite(tmp, 1, (size_t)tmp_n, f);
+ free(tmp);
+ } else {
+ size_t written = fwrite(data, bs, n, f);
+ if (written != (size_t)n)
+ return 0;
+ }
bin_write_length(f, l + n);
+ return 1;
}
// bin_length()
@@ -189,11 +329,13 @@ void bin_insert(FILE *f, int_t i, void *data, uint_t n) {
//
// return - The length / number of entries in the file
-uint_t bin_length(FILE *f) {
- uint_t n = 0;
+u64 bin_length(FILE *f) {
+ if (!f) return 0;
+ u64 n = 0;
fseek(f, offsetof(bin_meta_t, length), SEEK_SET);
- fread(&n, member_sizeof(bin_meta_t, length), 1, f);
+ size_t rn = fread(&n, member_sizeof(bin_meta_t, length), 1, f);
+ if (rn != 1) return 0;
return n;
}
@@ -206,11 +348,13 @@ uint_t bin_length(FILE *f) {
//
// return - The block size for the entries in the file
-uint_t bin_block_size(FILE *f) {
- uint_t n = 0;
+u64 bin_block_size(FILE *f) {
+ if (!f) return 0;
+ u64 n = 0;
fseek(f, offsetof(bin_meta_t, block_size), SEEK_SET);
- fread(&n, member_sizeof(bin_meta_t, block_size), 1, f);
+ size_t rn = fread(&n, member_sizeof(bin_meta_t, block_size), 1, f);
+ if (rn != 1) return 0;
return n;
}
@@ -226,21 +370,38 @@ uint_t bin_block_size(FILE *f) {
// The returned value is -( index + 1 ) where index is that of the
// first element greater than k or bin_length().
-int_t bin_search(FILE *f, bin_key_t k) {
- uint_t origin = ftell(f);
+i64 bin_search(FILE *f, bin_key_t k) {
+ if (!f) return -1;
+ fpos_t origin;
+ fgetpos(f, &origin);
- int_t l = 0;
- int_t r = bin_length(f) - 1;
- int_t m;
+ u64 len = bin_length(f);
+ if (len == 0) {
+ fsetpos(f, &origin);
+ return -1;
+ }
+
+ i64 l = 0;
+ i64 r = (i64)len - 1;
+ i64 m;
- uint_t bs = bin_block_size(f);
+ u64 bs = bin_block_size(f);
- bin_key_t key;
+ bin_key_t key = 0;
+ int found = 0;
while (l <= r) {
- m = (l + r) / 2;
- fseek(f, index_bytes(m, bs), SEEK_SET);
- fread(&key, sizeof(k), 1, f);
+ m = l + (r - l) / 2;
+ u64 offset = 0;
+ if (index_bytes_safe(m, bs, &offset) != 0) {
+ fsetpos(f, &origin);
+ return -(l + 1);
+ }
+ fseek_safe(f, offset);
+ if (fread(&key, sizeof(k), 1, f) != 1) {
+ fsetpos(f, &origin);
+ return -(l + 1);
+ }
if (key < k)
l = m + 1;
@@ -248,13 +409,15 @@ int_t bin_search(FILE *f, bin_key_t k) {
else if (key > k)
r = m - 1;
- else
+ else {
+ found = 1;
break;
+ }
}
- fseek(f, origin, SEEK_SET);
+ fsetpos(f, &origin);
- if (key != k)
+ if (!found)
return -(l + 1);
return m;
@@ -270,7 +433,11 @@ int_t bin_search(FILE *f, bin_key_t k) {
// return - The index remapped the binary file. If the index is already
// positive, then no operation occurrs.
-uint_t bin_fuzzy_index(int_t i) { return (i < 0) ? -(i + 1) : i; }
+u64 bin_fuzzy_index(i64 i) {
+ if (i < 0)
+ return (u64)(-(i + 1));
+ return (u64)i;
+}
// bin_write_length()
//
@@ -279,9 +446,11 @@ uint_t bin_fuzzy_index(int_t i) { return (i < 0) ? -(i + 1) : i; }
// f - The file to be written to
// count - The length to be written to the file
-static void bin_write_length(FILE *f, uint_t length) {
+static void bin_write_length(FILE *f, u64 length) {
fseek(f, offsetof(bin_meta_t, length), SEEK_SET);
- fwrite(&length, member_sizeof(bin_meta_t, length), 1, f);
+ if (fwrite(&length, member_sizeof(bin_meta_t, length), 1, f) != 1) {
+ clearerr(f);
+ }
}
// bin_write_block_size()
@@ -291,7 +460,7 @@ static void bin_write_length(FILE *f, uint_t length) {
// f - The file to be written to
// count - The block size to be written to the file
-static void bin_write_block_size(FILE *f, uint_t block_size) {
+static void bin_write_block_size(FILE *f, u64 block_size) {
fseek(f, offsetof(bin_meta_t, block_size), SEEK_SET);
fwrite(&block_size, member_sizeof(bin_meta_t, block_size), 1, f);
}
diff --git a/include/buffer.h b/include/buffer.h
new file mode 100644
index 0000000..2365173
--- /dev/null
+++ b/include/buffer.h
@@ -0,0 +1,232 @@
+// buffer.h - Dynamic buffer with metadata
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Memory safety: NULL-safe on all operations. buffer_resize returns
+// original pointer on failure; use buffer_resize_ok for NULL on failure.
+// Resize policy: doubles when usage > 80%, halves when < 20% (min 32).
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include "type.h"
+
+typedef struct {
+ u64 n; // Array capacity
+ u64 i; // Element count
+ u64 s; // Element size
+} buffer_meta_t;
+
+#define _buffer_from_meta(x) ((void *)((u8 *)(x) + sizeof(buffer_meta_t)))
+#define _buffer_to_meta(x) ((void *)((u8 *)(x) - sizeof(buffer_meta_t)))
+
+void *buffer_malloc(u64 num, u64 size);
+void buffer_free(void *);
+void *buffer_resize(void *);
+void *buffer_resize_ok(void *);
+u64 buffer_count(const void *);
+u64 buffer_byte_count(const void *);
+u64 buffer_capacity(const void *);
+u64 buffer_element_size(const void *);
+
+#endif // BUFFER_H
+
+#ifdef BUFFER_IMPLEMENTATION
+#ifndef BUFFER_IMPLEMENTED
+#define BUFFER_IMPLEMENTED
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// buffer_malloc()
+//
+// Allocate a dynamic buffer with embedded metadata
+//
+// n - Initial element capacity
+// s - Element size in bytes
+//
+// return - Pointer to data region, or NULL on failure
+
+void *buffer_malloc(u64 n, u64 s) {
+ if (n == 0 || s == 0)
+ return NULL;
+ if (n > (SIZE_MAX - sizeof(buffer_meta_t)) / s)
+ return NULL;
+
+ void *x = malloc((n * s) + sizeof(buffer_meta_t));
+ if (!x)
+ return NULL;
+
+ buffer_meta_t *meta = (buffer_meta_t *)x;
+ meta->i = 0;
+ meta->n = n;
+ meta->s = s;
+
+ return _buffer_from_meta(x);
+}
+
+// buffer_free()
+//
+// Release a dynamic buffer and its metadata
+//
+// buffer - Pointer returned by buffer_malloc
+
+void buffer_free(void *buffer) {
+ if (buffer == NULL)
+ return;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ meta->n = 0;
+ meta->i = 0;
+ meta->s = 0;
+ free(_buffer_to_meta(buffer));
+}
+
+// buffer_count()
+//
+// Return the number of elements currently in use
+//
+// buffer - Pointer to buffer data region
+//
+// return - Element count, or 0 if buffer is NULL
+
+u64 buffer_count(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ return meta->i;
+}
+
+// buffer_byte_count()
+//
+// Return the number of bytes currently in use
+//
+// buffer - Pointer to buffer data region
+//
+// return - Byte count (count * element size), or 0 if buffer is NULL
+
+u64 buffer_byte_count(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ if (meta->s == 0 || meta->i > UINT64_MAX / meta->s)
+ return 0;
+ return meta->i * meta->s;
+}
+
+// buffer_element_size()
+//
+// Return the size of each element in bytes
+//
+// buffer - Pointer to buffer data region
+//
+// return - Element size, or 0 if buffer is NULL
+
+u64 buffer_element_size(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ return meta->s;
+}
+
+// buffer_capacity()
+//
+// Return the total element capacity
+//
+// buffer - Pointer to buffer data region
+//
+// return - Capacity, or 0 if buffer is NULL
+
+u64 buffer_capacity(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ return meta->n;
+}
+
+// buffer_resize()
+//
+// Automatically resize buffer based on usage ratio. Doubles capacity when
+// usage > 80%, halves when < 20% (min 32). Returns original pointer on
+// realloc failure — use buffer_resize_ok for NULL on failure.
+//
+// buffer - Pointer to buffer data region
+//
+// return - New data pointer (may differ from input), or original on failure
+
+void *buffer_resize(void *buffer) {
+ if (!buffer)
+ return NULL;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ u64 new_n = meta->n;
+ if (meta->n == 0)
+ return buffer;
+ f64 ratio = (f64)meta->i / (f64)meta->n;
+
+ if (ratio > 0.8) {
+ if (new_n > UINT64_MAX / 2)
+ return buffer;
+ new_n *= 2;
+ } else if (ratio < 0.2 && new_n > 32) {
+ new_n /= 2;
+ }
+
+ if (new_n == meta->n)
+ return buffer;
+
+ if (meta->s == 0 || new_n > (SIZE_MAX - sizeof(buffer_meta_t)) / meta->s)
+ return buffer;
+
+ void *new_meta = realloc(meta, sizeof(buffer_meta_t) + new_n * meta->s);
+ if (!new_meta)
+ return buffer;
+
+ meta = (buffer_meta_t *)new_meta;
+ meta->n = new_n;
+
+ return _buffer_from_meta(new_meta);
+}
+
+// buffer_resize_ok()
+//
+// Same as buffer_resize, but returns NULL on realloc failure instead of
+// the original pointer. Caller must check return value.
+//
+// buffer - Pointer to buffer data region
+//
+// return - New data pointer, original if no resize needed, or NULL on failure
+
+void *buffer_resize_ok(void *buffer) {
+ if (!buffer)
+ return NULL;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ u64 old_n = meta->n;
+ u64 new_n = meta->n;
+ if (meta->n == 0)
+ return buffer;
+ f64 ratio = (f64)meta->i / (f64)meta->n;
+
+ if (ratio > 0.8) {
+ if (new_n > UINT64_MAX / 2)
+ return buffer;
+ new_n *= 2;
+ } else if (ratio < 0.2 && new_n > 32) {
+ new_n /= 2;
+ }
+
+ if (new_n == old_n)
+ return buffer;
+
+ if (meta->s == 0 || new_n > (SIZE_MAX - sizeof(buffer_meta_t)) / meta->s)
+ return buffer;
+
+ void *new_meta = realloc(meta, sizeof(buffer_meta_t) + new_n * meta->s);
+ if (!new_meta)
+ return NULL;
+
+ meta = (buffer_meta_t *)new_meta;
+ meta->n = new_n;
+
+ return _buffer_from_meta(meta);
+}
+
+#endif /* BUFFER_IMPLEMENTED */
+#endif // BUFFER_IMPLEMENTATION
diff --git a/include/direct.h b/include/direct.h
index c26907e..4302db0 100644
--- a/include/direct.h
+++ b/include/direct.h
@@ -1,11 +1,21 @@
-#ifndef DT_H
-#define DT_H
+// direct.h — Computation graph with topological update
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Behavior: dt_update does DFS from origin, evaluates nodes in reverse
+// order (topological). Uses a global generation counter to avoid
+// re-visiting nodes. The counter is u64 and overflows after ~1.8e19
+// calls, at which point reachable node generations are reset.
+// Memory: Edges are heap-allocated per dt_connect. Use dt_clear_edges to
+// free all edges for a node.
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
+#ifndef DIRECT_H
+#define DIRECT_H
-struct value_t;
+/* USER-DEFINED (define before including):
+ * typedef <type> direct_value_t;
+ */
+
+#include "type.h"
typedef struct edge {
struct edge *next;
@@ -14,108 +24,303 @@ typedef struct edge {
typedef struct node {
struct edge *edge;
- value_t x;
- bool flag;
- value_t (*function)(struct node *node);
+ direct_value_t x;
+ u64 visited_generation;
+ direct_value_t (*function)(struct node *node);
} node_t;
-void dt_connect(node_t *, node_t *);
+int dt_connect(node_t *, node_t *);
+int dt_connect_ok(node_t *, node_t *);
void dt_disconnect(node_t *, node_t *);
void dt_update(node_t *);
+void dt_update_trace(node_t *);
+void dt_clear_edges(node_t *);
-value_t dt_one(node_t *node);
-value_t dt_add(node_t *node);
-value_t dt_subtract(node_t *node);
+direct_value_t dt_one(node_t *node);
+direct_value_t dt_add(node_t *node);
+direct_value_t dt_subtract(node_t *node);
#endif
#ifdef DT_IMPLEMENTATION
+#ifndef DT_IMPLEMENTED
+#define DT_IMPLEMENTED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+// dt_connect() / dt_connect_ok()
+//
+// Add an edge from 'to' to 'from'. When dt_update is called on a node,
+// it will recursively evaluate its connected dependents.
+//
+// to - Destination node (depends on 'from')
+// from - Source node
+//
+// return - 1 on success, 0 on failure
+
+int dt_connect(node_t *to, node_t *from) {
+ return dt_connect_ok(to, from);
+}
+
+int dt_connect_ok(node_t *to, node_t *from) {
+ if (!to || !from)
+ return 0;
-void dt_connect(node_t *to, node_t *from) {
edge_t *edge = malloc(sizeof(edge_t));
+ if (!edge)
+ return 0;
edge->node = from;
edge->next = to->edge;
to->edge = edge;
+ return 1;
}
+// dt_disconnect()
+//
+// Remove the edge from 'to' to 'from'
+
void dt_disconnect(node_t *to, node_t *from) {
+ if (!to || !from)
+ return;
+
edge_t *edge = to->edge;
- edge_t *last = NULL;
+ edge_t *prev = NULL;
while (edge) {
if (edge->node == from) {
- edge_t *tmp = edge;
- last->next = edge->next;
- free(tmp);
+ if (prev) {
+ prev->next = edge->next;
+ } else {
+ to->edge = edge->next;
+ }
+ free(edge);
+ break;
}
-
- last = edge;
+ prev = edge;
edge = edge->next;
}
}
-void dt_update(node_t *origin) {
- // printf(">: %p - %lf\n", origin, origin->x);
+static u64 _dt_generation = 1;
- edge_t *edge = origin->edge;
- origin->flag = !origin->flag;
+static int dt_grow(node_t ***s, node_t ***e, u32 *cap) {
+ if (*cap > UINT32_MAX / 2) return 0;
+ u32 nc = (*cap) * 2;
+ size_t grow_sz = (size_t)nc * sizeof(node_t *);
+ if (nc > 0 && grow_sz / nc != sizeof(node_t *)) return 0;
+ node_t **ns = realloc(*s, grow_sz);
+ if (!ns) return 0;
+ node_t **ne = realloc(*e, grow_sz);
+ if (!ne) {
+ node_t **shrunk = realloc(*s, (size_t)(*cap) * sizeof(node_t *));
+ if (!shrunk)
+ shrunk = ns;
+ else
+ free(ns);
+ *s = shrunk;
+ return 0;
+ }
+ *s = ns;
+ *e = ne;
+ *cap = nc;
+ return 1;
+}
- while (edge) {
- if (origin->flag != edge->node->flag) {
- dt_update(edge->node);
+static void dt_update_impl(node_t *origin) {
+ if (!origin || !origin->function)
+ return;
+ u32 cap = 64;
+ node_t **dfs = malloc(cap * sizeof(node_t *));
+ if (!dfs) return;
+ node_t **eval = malloc(cap * sizeof(node_t *));
+ if (!eval) { free(dfs); return; }
+ u32 sp = 0, ep = 0;
+ dfs[sp++] = origin;
+ while (sp > 0) {
+ node_t *c = dfs[--sp];
+ if (!c || !c->function) continue;
+ if (c->visited_generation == _dt_generation) continue;
+ c->visited_generation = _dt_generation;
+ if (!dt_grow(&dfs, &eval, &cap)) { free(dfs); free(eval); return; }
+ eval[ep++] = c;
+ edge_t *e = c->edge;
+ while (e) {
+ if (e->node && e->node->function && e->node->visited_generation != _dt_generation) {
+ if (!dt_grow(&dfs, &eval, &cap)) { free(dfs); free(eval); return; }
+ dfs[sp++] = e->node;
+ }
+ e = e->next;
}
-
- edge = edge->next;
}
-
- origin->x = origin->function(origin);
- // printf("<: %p - %lf\n", origin, origin->x);
+ for (u32 i = ep; i > 0; i--)
+ eval[i - 1]->x = eval[i - 1]->function(eval[i - 1]);
+ free(dfs);
+ free(eval);
}
-void dt_update_trace(node_t *origin) {
- printf(">: %p - %lf\n", origin, origin->x);
+static void dt_reset_generations(node_t *origin, node_t **dfs, u32 *dfs_sp,
+ u32 cap, u32 *stack_cap) {
+ (void)cap;
+ if (*stack_cap == 0) return;
+ node_t *c = origin;
+ if (!c || !c->function) return;
+ dfs[(*dfs_sp)++] = c;
+ c->visited_generation = 0;
+ while (*dfs_sp > 0) {
+ node_t *n = dfs[--(*dfs_sp)];
+ edge_t *e = n->edge;
+ while (e) {
+ if (e->node && e->node->function && e->node->visited_generation != 0) {
+ if (*dfs_sp >= *stack_cap) {
+ u32 nc = (*stack_cap) * 2;
+ node_t **nd = realloc(dfs, (size_t)nc * sizeof(node_t *));
+ if (nd) { dfs = nd; *stack_cap = nc; }
+ else return;
+ }
+ dfs[(*dfs_sp)++] = e->node;
+ e->node->visited_generation = 0;
+ }
+ e = e->next;
+ }
+ }
+}
- edge_t *edge = origin->edge;
- origin->flag = !origin->flag;
+// dt_update()
+//
+// Evaluate the computation graph starting from origin. Performs DFS to
+// collect all reachable nodes, then evaluates them in reverse (topological)
+// order. Uses generation counters to skip already-evaluated nodes.
- while (edge) {
- if (origin->flag != edge->node->flag) {
- dt_update_trace(edge->node);
+void dt_update(node_t *origin) {
+ if (!origin || !origin->function)
+ return;
+ if (_dt_generation >= UINT64_MAX) {
+ _dt_generation = 1;
+ u32 cap = 64;
+ u32 sp = 0;
+ node_t **dfs = malloc(cap * sizeof(node_t *));
+ if (dfs) {
+ dt_reset_generations(origin, dfs, &sp, cap, &cap);
+ free(dfs);
}
-
- edge = edge->next;
+ } else {
+ _dt_generation++;
}
+ dt_update_impl(origin);
+}
- origin->x = origin->function(origin);
- printf("<: %p - %lf\n", origin, origin->x);
+static void dt_update_trace_impl(node_t *origin) {
+ if (!origin || !origin->function)
+ return;
+ u32 cap = 64;
+ node_t **dfs = malloc(cap * sizeof(node_t *));
+ if (!dfs) return;
+ node_t **eval = malloc(cap * sizeof(node_t *));
+ if (!eval) { free(dfs); return; }
+ u32 sp = 0, ep = 0;
+ dfs[sp++] = origin;
+ while (sp > 0) {
+ node_t *c = dfs[--sp];
+ if (!c || !c->function) continue;
+ if (c->visited_generation == _dt_generation) continue;
+ c->visited_generation = _dt_generation;
+ if (!dt_grow(&dfs, &eval, &cap)) { free(dfs); free(eval); return; }
+ eval[ep++] = c;
+ fprintf(stderr, ">: %p\n", (void *)c);
+ edge_t *e = c->edge;
+ while (e) {
+ if (e->node && e->node->function && e->node->visited_generation != _dt_generation) {
+ if (!dt_grow(&dfs, &eval, &cap)) { free(dfs); free(eval); return; }
+ dfs[sp++] = e->node;
+ }
+ e = e->next;
+ }
+ }
+ for (u32 i = ep; i > 0; i--) {
+ node_t *n = eval[i - 1];
+ n->x = n->function(n);
+ fprintf(stderr, "<: %p\n", (void *)n);
+ }
+ free(dfs);
+ free(eval);
}
-value_t dt_one(node_t *node) {
- return 1;
+// dt_update_trace()
+//
+// Same as dt_update, but prints each node's address to stderr as it is
+// visited (">:") and evaluated ("<:") for debugging purposes.
+
+void dt_update_trace(node_t *origin) {
+ if (!origin || !origin->function)
+ return;
+ if (_dt_generation >= UINT64_MAX) {
+ _dt_generation = 1;
+ u32 cap = 64;
+ u32 sp = 0;
+ node_t **dfs = malloc(cap * sizeof(node_t *));
+ if (dfs) {
+ dt_reset_generations(origin, dfs, &sp, cap, &cap);
+ free(dfs);
+ }
+ } else {
+ _dt_generation++;
+ }
+ dt_update_trace_impl(origin);
}
-value_t dt_add(node_t *node) {
+// dt_one() - Constant node that always returns 1
+direct_value_t dt_one(node_t *node) { (void)node; return 1; }
+
+// dt_add() - Sums the values of all connected input nodes plus the node's own value
+direct_value_t dt_add(node_t *node) {
+ if (!node)
+ return 0;
edge_t *edge = node->edge;
- value_t x = node->x;
+ direct_value_t x = node->x;
- while(edge) {
- x += edge->node->x;
+ while (edge) {
+ if (edge->node)
+ x += edge->node->x;
edge = edge->next;
}
return x;
}
-value_t dt_subtract(node_t *node) {
+// dt_subtract() - Subtracts all connected input node values from the node's own value
+direct_value_t dt_subtract(node_t *node) {
+ if (!node)
+ return 0;
edge_t *edge = node->edge;
- value_t x = node->x;
+ direct_value_t x = node->x;
- while(edge) {
- x -= edge->node->x;
+ while (edge) {
+ if (edge->node)
+ x -= edge->node->x;
edge = edge->next;
}
return x;
}
-#endif
+// dt_clear_edges()
+//
+// Free all edges connected to a node
+
+void dt_clear_edges(node_t *node) {
+ if (!node)
+ return;
+ edge_t *edge = node->edge;
+ node->edge = NULL;
+
+ while (edge) {
+ edge_t *next = edge->next;
+ free(edge);
+ edge = next;
+ }
+}
+
+#endif /* DT_IMPLEMENTED */
+#endif /* DT_IMPLEMENTATION */
diff --git a/include/escape.h b/include/escape.h
index 13bd996..db46694 100755
--- a/include/escape.h
+++ b/include/escape.h
@@ -1,91 +1,110 @@
// escape.h - ESC escape code macros and functions
+//
+// Thread safety: Safe for concurrent use (no shared state). The macros
+// produce string literals; the functions write to the provided FILE*.
+// Portability: ANSI escape codes are not supported on Windows Console API
+// (use Windows 10+ VT mode or a translation layer).
#ifndef ESCAPE_H
#define ESCAPE_H
-
-#include <stdint.h>
-
+#include "type.h"
+#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
+#define CSI "\x1B["
-#define CSI "\x1B["
-
-
-#define ESC_CURSOR_UP( n ) CSI #n "A"
-void esc_cursor_up( uint32_t n );
+#define ESC_CURSOR_UP(n) CSI #n "A"
+void esc_cursor_up(u32 n);
+void esc_fcursor_up(FILE *, u32 n);
-#define ESC_CURSOR_DOWN( n ) CSI #n "B"
-void esc_cursor_down( uint32_t n );
+#define ESC_CURSOR_DOWN(n) CSI #n "B"
+void esc_cursor_down(u32 n);
+void esc_fcursor_down(FILE *, u32 n);
-#define ESC_CURSOR_LEFT( n ) CSI #n "C"
-void esc_cursor_left( uint32_t n );
+#define ESC_CURSOR_LEFT(n) CSI #n "D"
+void esc_cursor_left(u32 n);
+void esc_fcursor_left(FILE *, u32 n);
-#define ESC_CURSOR_RIGHT( n ) CSI #n "D"
-void esc_cursor_right( uint32_t n );
+#define ESC_CURSOR_RIGHT(n) CSI #n "C"
+void esc_cursor_right(u32 n);
+void esc_fcursor_right(FILE *, u32 n);
-#define ESC_CURSOR_LINE_NEXT( n ) CSI #n "E"
-void esc_cursor_line_next( uint32_t n );
+#define ESC_CURSOR_LINE_NEXT(n) CSI #n "E"
+void esc_cursor_line_next(u32 n);
+void esc_fcursor_line_next(FILE *, u32 n);
-#define ESC_CURSOR_POSITION( x, y ) CSI #y ";" #x "H"
-void esc_cursor_position( uint32_t x, uint32_t y );
+#define ESC_CURSOR_POSITION(col, row) CSI #row ";" #col "H"
+void esc_cursor_position(u32 col, u32 row);
+void esc_fcursor_position(FILE *, u32 col, u32 row);
-#define ESC_CLEAR_SCREEN CSI "2J"
-void esc_clear_screen( void );
+#define ESC_CLEAR_SCREEN CSI "2J"
+void esc_clear_screen(void);
+void esc_fclear_screen(FILE *);
#define ESC_CLEAR_SCREEN_START CSI "1J"
-void esc_clear_screen_start( void );
+void esc_clear_screen_start(void);
+void esc_fclear_screen_start(FILE *);
#define ESC_CLEAR_SCREEN_END CSI "0J"
-void esc_clear_screen_end( void );
+void esc_clear_screen_end(void);
+void esc_fclear_screen_end(FILE *);
#define ESC_CLEAR_LINE CSI "2K"
-void esc_clear_line( void );
+void esc_clear_line(void);
+void esc_fclear_line(FILE *);
-#define ESC_CLEAR_LINE_START CSI "1L"
-void esc_clear_line_start( void );
+#define ESC_CLEAR_LINE_START CSI "1K"
+void esc_clear_line_start(void);
+void esc_fclear_line_start(FILE *);
-#define ESC_CLEAR_LINE_END CSI "0L"
-void esc_clear_line_end( void );
+#define ESC_CLEAR_LINE_END CSI "0K"
+void esc_clear_line_end(void);
+void esc_fclear_line_end(FILE *);
-#define ESC_SCROLL_UP( n ) CSI #n "S"
-void esc_scroll_up( uint32_t n );
+#define ESC_SCROLL_UP(n) CSI #n "S"
+void esc_scroll_up(u32 n);
+void esc_fscroll_up(FILE *, u32 n);
-#define ESC_SCROLL_DOWN( n ) CSI #n "T"
-void esc_scroll_down( uint32_t n );
+#define ESC_SCROLL_DOWN(n) CSI #n "T"
+void esc_scroll_down(u32 n);
+void esc_fscroll_down(FILE *, u32 n);
-#define ESC_SGR "m"
+#define ESC_SGR "m"
-#define ESC_RESET CSI "0" ESC_SGR
-#define ESC_BOLD CSI "1" ESC_SGR
-#define ESC_UNDERLINE CSI "4" ESC_SGR
-#define ESC_BLINK CSI "5" ESC_SGR
-#define ESC_REVERSE CSI "7" ESC_SGR
-#define ESC_CONCEAL CSI "8" ESC_SGR
-#define ESC_REVEAL CSI "28" ESC_SGR
-#define ESC_STRIKETHROUGH CSI "9" ESC_SGR
-#define ESC_OVERLINE CSI "53" ESC_SGR
+#define ESC_RESET CSI "0" ESC_SGR
+#define ESC_BOLD CSI "1" ESC_SGR
+#define ESC_UNDERLINE CSI "4" ESC_SGR
+#define ESC_BLINK CSI "5" ESC_SGR
+#define ESC_REVERSE CSI "7" ESC_SGR
+#define ESC_CONCEAL CSI "8" ESC_SGR
+#define ESC_REVEAL CSI "28" ESC_SGR
+#define ESC_STRIKETHROUGH CSI "9" ESC_SGR
+#define ESC_OVERLINE CSI "53" ESC_SGR
-#define ESC_COLOR_FG_RGB( r, g, b ) CSI "38;2;" #r ";" #g ";" #b ESC_SGR
-void esc_color_fg_rgb( uint32_t r, uint32_t g, uint32_t b );
+#define ESC_COLOR_FG_RGB(r, g, b) CSI "38;2;" #r ";" #g ";" #b ESC_SGR
+void esc_color_fg_rgb(u32 r, u32 g, u32 b);
+void esc_fcolor_fg_rgb(FILE *, u32 r, u32 g, u32 b);
-#define ESC_COLOR_BG_RGB( r, g, b ) CSI "48;2;" #r ";" #g ";" #b ESC_SGR
-void esc_color_bg_rgb( uint32_t r, uint32_t g, uint32_t b );
+#define ESC_COLOR_BG_RGB(r, g, b) CSI "48;2;" #r ";" #g ";" #b ESC_SGR
+void esc_color_bg_rgb(u32 r, u32 g, u32 b);
+void esc_fcolor_bg_rgb(FILE *, u32 r, u32 g, u32 b);
-#define ESC_COLOR_FG_256( n ) CSI "38;5;" #n ESC_SGR
-void esc_color_fg_256( uint32_t r, uint32_t g, uint32_t b );
+#define ESC_COLOR_FG_256(n) CSI "38;5;" #n ESC_SGR
+void esc_color_fg_256(u32 n);
+void esc_fcolor_fg_256(FILE *, u32 n);
-#define ESC_COLOR_BG_256( n ) CSI "48;5;" #n ESC_SGR
-void esc_color_bg_256( uint32_t r, uint32_t g, uint32_t b );
-
-#define ESC_COLOR_FG( n ) CSI "3" #n ESC_SGR
-#define ESC_COLOR_BG( n ) CSI "4" #n ESC_SGR
-#define ESC_COLOR_FG_BRIGHT( n ) CSI "9" #n ESC_SGR
-#define ESC_COLOR_BG_BRIGHT( n ) CSI "10" #n ESC_SGR
+#define ESC_COLOR_BG_256(n) CSI "48;5;" #n ESC_SGR
+void esc_color_bg_256(u32 n);
+void esc_fcolor_bg_256(FILE *, u32 n);
+#define ESC_COLOR_FG(n) CSI "3" #n ESC_SGR
+#define ESC_COLOR_BG(n) CSI "4" #n ESC_SGR
+#define ESC_COLOR_FG_BRIGHT(n) CSI "9" #n ESC_SGR
+#define ESC_COLOR_BG_BRIGHT(n) CSI "10" #n ESC_SGR
#ifdef __cplusplus
}
@@ -93,29 +112,73 @@ void esc_color_bg_256( uint32_t r, uint32_t g, uint32_t b );
#endif // ESCAPE_H
-
#ifdef ESCAPE_IMPLEMENTATION
#include <stdio.h>
-void esc_cursor_up( uint32_t n ) { fprintf( stdout, CSI "%dA", n ); }
-void esc_cursor_down( uint32_t n ) { fprintf( stdout, CSI "%dB", n ); }
-void esc_cursor_left( uint32_t n ) { fprintf( stdout, CSI "%dC", n ); }
-void esc_cursor_right( uint32_t n ) { fprintf( stdout, CSI "%dD", n ); }
-void esc_cursor_line_next( uint32_t n ) { fprintf( stdout, CSI "%dE", n ); }
-void esc_cursor_position( uint32_t x, uint32_t y ) { fprintf( stdout, CSI "%d;%dH", y, x ); }
-void esc_clear_screen( void ) { fputs( CSI "2J", stdout ); }
-void esc_clear_screen_start( void ) { fputs( CSI "1J", stdout ); }
-void esc_clear_screen_end( void ) { fputs( CSI "0J", stdout ); }
-void esc_clear_line( void ) { fputs( CSI "2L", stdout ); }
-void esc_clear_line_start( void ) { fputs( CSI "1L", stdout ); }
-void esc_clear_line_end( void ) { fputs( CSI "0L", stdout ); }
-void esc_scroll_up( uint32_t n ) { fprintf( stdout, "%dS", n ); }
-void esc_scroll_down( uint32_t n ) { fprintf( stdout, "%dT", n ); }
+void esc_fcursor_up(FILE *f, u32 n) { if (!f) return; fprintf(f, CSI "%uA", (unsigned)n); }
+void esc_fcursor_down(FILE *f, u32 n) { if (!f) return; fprintf(f, CSI "%uB", (unsigned)n); }
+void esc_fcursor_left(FILE *f, u32 n) { if (!f) return; fprintf(f, CSI "%uD", (unsigned)n); }
+void esc_fcursor_right(FILE *f, u32 n) { if (!f) return; fprintf(f, CSI "%uC", (unsigned)n); }
+void esc_fcursor_line_next(FILE *f, u32 n) { if (!f) return; fprintf(f, CSI "%uE", (unsigned)n); }
+void esc_fcursor_position(FILE *f, u32 col, u32 row) {
+ if (!f) return;
+ fprintf(f, CSI "%u;%uH", (unsigned)row, (unsigned)col);
+}
+void esc_fclear_screen(FILE *f) { if (!f) return; fputs(CSI "2J", f); }
+void esc_fclear_screen_start(FILE *f) { if (!f) return; fputs(CSI "1J", f); }
+void esc_fclear_screen_end(FILE *f) { if (!f) return; fputs(CSI "0J", f); }
+void esc_fclear_line(FILE *f) { if (!f) return; fputs(CSI "2K", f); }
+void esc_fclear_line_start(FILE *f) { if (!f) return; fputs(CSI "1K", f); }
+void esc_fclear_line_end(FILE *f) { if (!f) return; fputs(CSI "0K", f); }
+void esc_fscroll_up(FILE *f, u32 n) { if (!f) return; fprintf(f, CSI "%uS", (unsigned)n); }
+void esc_fscroll_down(FILE *f, u32 n) { if (!f) return; fprintf(f, CSI "%uT", (unsigned)n); }
-void esc_color_fg_rgb( uint32_t r, uint32_t g, uint32_t b ) { fprintf( stdout, CSI "38;2;%d;%d;%d" ESC_SGR, r, g, b ); }
-void esc_color_bg_rgb( uint32_t r, uint32_t g, uint32_t b ) { fprintf( stdout, CSI "48;2;%d;%d;%d" ESC_SGR, r, g, b ); }
-void esc_color_fg_256( uint32_t r, uint32_t g, uint32_t b ) { fprintf( stdout, CSI "38;5;%d;%d;%d" ESC_SGR, r, g, b ); }
-void esc_color_bg_256( uint32_t r, uint32_t g, uint32_t b ) { fprintf( stdout, CSI "48;5;%d;%d;%d" ESC_SGR, r, g, b ); }
+void esc_fcolor_fg_rgb(FILE *f, u32 r, u32 g, u32 b) {
+ if (!f) return;
+ fprintf(f, CSI "38;2;%u;%u;%u" ESC_SGR, (unsigned)r, (unsigned)g, (unsigned)b);
+}
+void esc_fcolor_bg_rgb(FILE *f, u32 r, u32 g, u32 b) {
+ if (!f) return;
+ fprintf(f, CSI "48;2;%u;%u;%u" ESC_SGR, (unsigned)r, (unsigned)g, (unsigned)b);
+}
+void esc_fcolor_fg_256(FILE *f, u32 n) {
+ if (!f) return;
+ fprintf(f, CSI "38;5;%u" ESC_SGR, (unsigned)n);
+}
+void esc_fcolor_bg_256(FILE *f, u32 n) {
+ if (!f) return;
+ fprintf(f, CSI "48;5;%u" ESC_SGR, (unsigned)n);
+}
+
+void esc_cursor_up(u32 n) { esc_fcursor_up(stdout, n); }
+void esc_cursor_down(u32 n) { esc_fcursor_down(stdout, n); }
+void esc_cursor_left(u32 n) { esc_fcursor_left(stdout, n); }
+void esc_cursor_right(u32 n) { esc_fcursor_right(stdout, n); }
+void esc_cursor_line_next(u32 n) { esc_fcursor_line_next(stdout, n); }
+void esc_cursor_position(u32 col, u32 row) {
+ esc_fcursor_position(stdout, col, row);
+}
+void esc_clear_screen(void) { esc_fclear_screen(stdout); }
+void esc_clear_screen_start(void) { esc_fclear_screen_start(stdout); }
+void esc_clear_screen_end(void) { esc_fclear_screen_end(stdout); }
+void esc_clear_line(void) { esc_fclear_line(stdout); }
+void esc_clear_line_start(void) { esc_fclear_line_start(stdout); }
+void esc_clear_line_end(void) { esc_fclear_line_end(stdout); }
+void esc_scroll_up(u32 n) { esc_fscroll_up(stdout, n); }
+void esc_scroll_down(u32 n) { esc_fscroll_down(stdout, n); }
+
+void esc_color_fg_rgb(u32 r, u32 g, u32 b) {
+ esc_fcolor_fg_rgb(stdout, r, g, b);
+}
+void esc_color_bg_rgb(u32 r, u32 g, u32 b) {
+ esc_fcolor_bg_rgb(stdout, r, g, b);
+}
+void esc_color_fg_256(u32 n) {
+ esc_fcolor_fg_256(stdout, n);
+}
+void esc_color_bg_256(u32 n) {
+ esc_fcolor_bg_256(stdout, n);
+}
#endif // ESCAPE_IMPLEMENTATION \ No newline at end of file
diff --git a/include/flat.h b/include/flat.h
index 17a1cc4..9bbde7a 100755
--- a/include/flat.h
+++ b/include/flat.h
@@ -1,16 +1,30 @@
// flat.h - Directory traversal utility functions
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Memory: flat_directory_entries returns a NULL-terminated array of
+// heap-allocated strings. Use flat_free_entries to release both.
+// Portability: Provides POSIX (dirent.h) and Windows (FindFirstFileA) impls.
#ifndef FLAT_H
+#define FLAT_H
-#include <stdint.h>
+#include "type.h"
#include <sys/stat.h>
+#ifdef _WIN32
+#include <direct.h>
+#include <windows.h>
+#define MKDIR(x) (mkdir(x))
+#else
+#include <dirent.h>
+#define MKDIR(x) (mkdir(x, 0755))
+#endif
+
char *flat_path(int, ...);
-int64_t flat_directory_entry_count(char const *);
+void flat_free_entries(char **);
+i64 flat_directory_entry_count(char const *);
char **flat_directory_entries(char const *);
-#define MKDIR(x) (mkdir(x, 0777))
-
#endif // FLAT_H
#ifdef FLAT_IMPLEMENTATION
@@ -19,7 +33,16 @@ char **flat_directory_entries(char const *);
#include <stdlib.h>
#include <string.h>
-#include <dirent.h>
+#ifndef _WIN32
+
+// flat_path()
+//
+// Join path components into a single allocated string. Uses '/' separator
+// on POSIX, '\\' on Windows. Caller must free the result.
+//
+// s_n - Number of string components
+//
+// return - Allocated path string, or NULL on failure
char *flat_path(int s_n, ...) {
va_list list;
@@ -28,45 +51,55 @@ char *flat_path(int s_n, ...) {
if (s_n < 1)
return NULL;
- // First, measure the total length required.
va_start(list, s_n);
for (int i = 0; i < s_n; i++) {
const char *s = va_arg(list, char *);
- c_n += strlen(s);
+ if (s)
+ c_n += strlen(s);
}
va_end(list);
- // Allocate return buffer.
- char *r = malloc(c_n + 1);
+ size_t separators = (s_n > 1 ? (size_t)(s_n - 1) : 0);
+ if (c_n > SIZE_MAX - separators - 1)
+ return NULL;
+ size_t total = c_n + separators + 1;
+ char *r = malloc(total);
if (r == NULL)
return NULL;
- // Concatenate all the strings into the return buffer.
char *d = r;
va_start(list, s_n);
for (int i = 0; i < s_n; i++) {
const char *s = va_arg(list, char *);
- // This loop is a strcpy.
- while ((*d++ = *s++))
- ;
- d--;
+ // Add separator between components (not before the first).
+ if (i > 0)
+ *d++ = '/';
+
+ if (s) {
+ while ((*d++ = *s++))
+ ;
+ d--; // Step back over the null terminator.
+ }
}
va_end(list);
+ *d = '\0';
return r;
}
-int64_t flat_directory_entry_count(char const *path) {
+i64 flat_directory_entry_count(char const *path) {
DIR *dir;
struct dirent *ent;
+ if (path == NULL)
+ return 0;
if (!(dir = opendir(path))) {
return 0;
}
- int64_t n = 0;
+ i64 n = 0;
while ((ent = readdir(dir))) {
if (!strcmp(ent->d_name, "."))
@@ -83,38 +116,234 @@ int64_t flat_directory_entry_count(char const *path) {
return n;
}
-char **flat_directory_entries(char const *path) {
- int64_t n = flat_directory_entry_count(path);
- char **entries = calloc(n + 1, sizeof(char **));
+static int flat_skip_entry(struct dirent *ent) {
+ if (!ent) return 1;
+ if (!strcmp(ent->d_name, ".")) return 1;
+ if (!strcmp(ent->d_name, "..")) return 1;
+ return 0;
+}
+char **flat_directory_entries(char const *path) {
DIR *dir;
struct dirent *ent;
- int64_t i = 0;
- int64_t entry_n = 0;
-
+ if (path == NULL)
+ return NULL;
if (!(dir = opendir(path))) {
return NULL;
}
- while (i < n) {
- ent = readdir(dir);
- if (!strcmp(ent->d_name, "."))
- continue;
+ i64 capacity = 16;
+ char **entries = calloc((size_t)capacity, sizeof(char *));
+ if (!entries) {
+ closedir(dir);
+ return NULL;
+ }
- if (!strcmp(ent->d_name, ".."))
+ i64 count = 0;
+ while ((ent = readdir(dir))) {
+ if (flat_skip_entry(ent))
continue;
- entry_n = strlen(ent->d_name);
- entries[i] = malloc(entry_n + 1);
- strcpy(entries[i], ent->d_name);
+ if (count >= capacity - 1) {
+ if (capacity > INT64_MAX / 2) {
+ flat_free_entries(entries);
+ closedir(dir);
+ return NULL;
+ }
+ i64 new_capacity = capacity * 2;
+ if ((size_t)new_capacity > SIZE_MAX / sizeof(char *)) {
+ flat_free_entries(entries);
+ closedir(dir);
+ return NULL;
+ }
+ char **new_entries = realloc(entries, (size_t)new_capacity * sizeof(char *));
+ if (!new_entries) {
+ flat_free_entries(entries);
+ closedir(dir);
+ return NULL;
+ }
+ entries = new_entries;
+ capacity = new_capacity;
+ }
- i++;
+ size_t entry_n = strlen(ent->d_name);
+ entries[count] = malloc(entry_n + 1);
+ if (!entries[count]) {
+ flat_free_entries(entries);
+ closedir(dir);
+ return NULL;
+ }
+ strcpy(entries[count], ent->d_name);
+ count++;
}
+ entries[count] = NULL;
+
closedir(dir);
return entries;
}
-#endif // FLAT_IMPELEMTATION
+void flat_free_entries(char **entries) {
+ if (!entries)
+ return;
+ i64 i = 0;
+ while (entries[i]) {
+ free(entries[i]);
+ i++;
+ }
+ free(entries);
+}
+
+#else /* _WIN32 */
+
+#include <errno.h>
+#include <windows.h>
+
+static int flat_skip_entry_win(char const *name) {
+ if (!name) return 1;
+ if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
+ return 1;
+ return 0;
+}
+
+char *flat_path(int s_n, ...) {
+ va_list list;
+ size_t c_n = 0;
+
+ if (s_n < 1)
+ return NULL;
+
+ va_start(list, s_n);
+ for (int i = 0; i < s_n; i++) {
+ const char *s = va_arg(list, char *);
+ if (s)
+ c_n += strlen(s);
+ }
+ va_end(list);
+
+ size_t separators = (s_n > 1 ? (size_t)(s_n - 1) : 0);
+ if (c_n > SIZE_MAX - separators - 1)
+ return NULL;
+ size_t total = c_n + separators + 1;
+ char *r = malloc(total);
+ if (r == NULL)
+ return NULL;
+
+ char *d = r;
+ va_start(list, s_n);
+ for (int i = 0; i < s_n; i++) {
+ const char *s = va_arg(list, char *);
+ if (i > 0)
+ *d++ = '\\';
+ if (s) {
+ while ((*d++ = *s++))
+ ;
+ d--;
+ }
+ }
+ va_end(list);
+ *d = '\0';
+ return r;
+}
+
+i64 flat_directory_entry_count(char const *path) {
+ if (path == NULL)
+ return 0;
+ char pattern[4096];
+ if (strlen(path) > sizeof(pattern) - 3)
+ return 0;
+ sprintf(pattern, "%s\\*", path);
+
+ WIN32_FIND_DATAA fd;
+ HANDLE h = FindFirstFileA(pattern, &fd);
+ if (h == INVALID_HANDLE_VALUE)
+ return 0;
+
+ i64 n = 0;
+ do {
+ if (!flat_skip_entry_win(fd.cFileName))
+ n++;
+ } while (FindNextFileA(h, &fd));
+
+ FindClose(h);
+ return n;
+}
+
+char **flat_directory_entries(char const *path) {
+ if (path == NULL)
+ return NULL;
+ char pattern[4096];
+ if (strlen(path) > sizeof(pattern) - 3)
+ return NULL;
+ sprintf(pattern, "%s\\*", path);
+
+ WIN32_FIND_DATAA fd;
+ HANDLE h = FindFirstFileA(pattern, &fd);
+ if (h == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ i64 capacity = 16;
+ char **entries = calloc((size_t)capacity, sizeof(char *));
+ if (!entries) {
+ FindClose(h);
+ return NULL;
+ }
+
+ i64 count = 0;
+ do {
+ if (flat_skip_entry_win(fd.cFileName))
+ continue;
+
+ if (count >= capacity - 1) {
+ if (capacity > INT64_MAX / 2) {
+ flat_free_entries(entries);
+ FindClose(h);
+ return NULL;
+ }
+ i64 new_capacity = capacity * 2;
+ if ((size_t)new_capacity > SIZE_MAX / sizeof(char *)) {
+ flat_free_entries(entries);
+ FindClose(h);
+ return NULL;
+ }
+ char **new_entries = realloc(entries, (size_t)new_capacity * sizeof(char *));
+ if (!new_entries) {
+ flat_free_entries(entries);
+ FindClose(h);
+ return NULL;
+ }
+ entries = new_entries;
+ capacity = new_capacity;
+ }
+
+ size_t entry_n = strlen(fd.cFileName);
+ entries[count] = malloc(entry_n + 1);
+ if (!entries[count]) {
+ flat_free_entries(entries);
+ FindClose(h);
+ return NULL;
+ }
+ strcpy(entries[count], fd.cFileName);
+ count++;
+ } while (FindNextFileA(h, &fd));
+
+ entries[count] = NULL;
+ FindClose(h);
+ return entries;
+}
+
+void flat_free_entries(char **entries) {
+ if (!entries)
+ return;
+ i64 i = 0;
+ while (entries[i]) {
+ free(entries[i]);
+ i++;
+ }
+ free(entries);
+}
+
+#endif /* _WIN32 */
+#endif // FLAT_IMPLEMENTATION
diff --git a/include/genetic.h b/include/genetic.h
index eadcab5..2ebf985 100755
--- a/include/genetic.h
+++ b/include/genetic.h
@@ -1,31 +1,35 @@
// genetic.h - A simple genetic algorithm implementation
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Behavior: rate_selection must be in (0, 1). A value of 1.0 is rejected
+// because it results in no evolution (all individuals survive).
+// rate_mutation must be >= 0. Returns -1.0 on any parameter error.
#ifndef GENETIC_H
#define GENETIC_H
-#include <assert.h>
-#include <stdint.h>
+#include "type.h"
-struct ga_individual_t;
+// ga_individual_t must be typedef'd by the user before including this header.
// ga_crossover_t
//
// Type definition for a crossover function
//
-// uintptr_t * - The address for the child pointer
-// uintptr_t - Parent pointer
-// uintptr_t - Parent pointer
+// ga_individual_t * - The address for the child individual
+// ga_individual_t - Parent individual 0
+// ga_individual_t - Parent individual 1
typedef void (*ga_crossover_t)(ga_individual_t *, ga_individual_t,
- ga_individual_t);
+ ga_individual_t);
// ga_mutation_t
//
-// Type definition for a crossover function
+// Type definition for a mutation function
//
-// uintptr_t - The individual being mutated
+// ga_individual_t * - The individual being mutated
-typedef void (*ga_mutation_t)(ga_individual_t);
+typedef void (*ga_mutation_t)(ga_individual_t *);
// ga_fitness_t()
//
@@ -34,12 +38,12 @@ typedef void (*ga_mutation_t)(ga_individual_t);
// uintptr_t - The individual on which to run the fitness function
// void const * - The user defined data pointer
-typedef double (*ga_fitness_t)(ga_individual_t, void const *);
+typedef f64 (*ga_fitness_t)(ga_individual_t, void const *);
-double ga_generation(ga_individual_t *, uint32_t, double, double, ga_fitness_t,
- void const *, ga_crossover_t, ga_mutation_t);
+f64 ga_generation(ga_individual_t *, u32, f64, f64, ga_fitness_t,
+ void const *, ga_crossover_t, ga_mutation_t);
-#endif // ga_H
+#endif
#ifdef GENETIC_IMPLEMENTATION // IMPLEMENTATION
@@ -48,7 +52,7 @@ double ga_generation(ga_individual_t *, uint32_t, double, double, ga_fitness_t,
#include <string.h>
#include <tgmath.h>
-// ga_finess_sort()
+// ga_fitness_sort()
//
// Run the fitness function on
//
@@ -59,38 +63,61 @@ double ga_generation(ga_individual_t *, uint32_t, double, double, ga_fitness_t,
//
// return - The error for the best performing individual
-static double ga_fitness_sort(ga_individual_t *population,
- uint32_t population_n, ga_fitness_t fitness,
- void const *fitness_data) {
- double *fitness_buffer = calloc(population_n, sizeof(double));
- double fitness_current = 0;
- ga_individual_t individual;
+static f64 ga_fitness_sort(ga_individual_t *population,
+ u32 population_n, ga_fitness_t fitness,
+ void const *fitness_data) {
+ if (!population || !fitness || population_n == 0)
+ return -1;
- // Insertion sort
- uint32_t i, j, k;
- for (i = 0; i < population_n; i++) {
- fitness_current = fabs(fitness(population[i], fitness_data));
- individual = population[i];
- // for (j = i; j > 0 && fitness_current > fitness_buffer[j - 1]; j--) {
- for (j = i; j > 0 && fitness_current < fitness_buffer[j - 1]; j--) {
- }
+ f64 *fitness_buffer = calloc((size_t)population_n, sizeof(f64));
+ if (!fitness_buffer)
+ return -1;
- // Shift array back by one for sorting
- for (k = i; k > j; k--) {
- population[k] = population[k - 1];
- fitness_buffer[k] = fitness_buffer[k - 1];
- }
+ for (u32 i = 0; i < population_n; i++) {
+ fitness_buffer[i] = fabs(fitness(population[i], fitness_data));
+ }
- population[j] = individual;
- fitness_buffer[j] = fitness_current;
+ // Index array for indirect sort
+ u32 *indices = malloc((size_t)population_n * sizeof(u32));
+ if (!indices) {
+ free(fitness_buffer);
+ return -1;
}
+ for (u32 i = 0; i < population_n; i++)
+ indices[i] = i;
- // for (i = 0; i < population_n; i++) {
- // printf("%u - %lf\n", i, fitness_buffer[i]);
- // }
+ // Shell sort on indices by fitness value (indirect sort, only rearrange indices)
+ {
+ u32 gap = population_n / 2;
+ while (gap > 0) {
+ for (u32 i = gap; i < population_n; i++) {
+ u32 temp = indices[i];
+ f64 temp_f = fitness_buffer[temp];
+ u32 j;
+ for (j = i; j >= gap && fitness_buffer[indices[j - gap]] > temp_f; j -= gap) {
+ indices[j] = indices[j - gap];
+ }
+ indices[j] = temp;
+ }
+ gap /= 2;
+ }
+ }
+
+ // Copy sorted individuals back
+ ga_individual_t *sorted = calloc((size_t)population_n, sizeof(ga_individual_t));
+ if (!sorted) {
+ free(fitness_buffer);
+ free(indices);
+ return -1;
+ }
+ for (u32 i = 0; i < population_n; i++)
+ sorted[i] = population[indices[i]];
+ memcpy(population, sorted, (size_t)population_n * sizeof(ga_individual_t));
- double error = fitness_buffer[0];
+ f64 error = fitness_buffer[indices[0]];
+ free(sorted);
free(fitness_buffer);
+ free(indices);
return error;
}
@@ -113,30 +140,37 @@ static double ga_fitness_sort(ga_individual_t *population,
//
// return - The error of the best performing individual
-double ga_generation(ga_individual_t *population, uint32_t population_n,
- double rate_selection, double rate_mutation,
- ga_fitness_t fitness, void const *fitness_data,
- ga_crossover_t crossover, ga_mutation_t mutation) {
- // assert(0 < rate_selection && rate_selection <= 1.0);
- // assert(0 < rate_mutation && rate_mutation <= 1.0);
- assert(2 < population_n);
+f64 ga_generation(ga_individual_t *population, u32 population_n,
+ f64 rate_selection, f64 rate_mutation,
+ ga_fitness_t fitness, void const *fitness_data,
+ ga_crossover_t crossover, ga_mutation_t mutation) {
+ if (!population || !fitness || !crossover || !mutation)
+ return -1;
+ if (rate_selection <= 0 || rate_selection >= 1.0)
+ return -1;
+ if (rate_mutation < 0)
+ return -1;
+ if (population_n < 3)
+ return -1;
- uint32_t selection_n = rate_selection * population_n;
+ u32 selection_n = (u32)(rate_selection * (f64)population_n);
+ if (selection_n >= population_n)
+ return -1;
// Crossover
- for (uint32_t i = selection_n; i < population_n; i++) {
- uint32_t parent_i = rand() % selection_n;
- uint32_t parent_j = rand() % selection_n;
+ for (u32 i = selection_n; i < population_n; i++) {
+ u32 parent_i = (u32)((u64)rand() * (u64)selection_n / ((u64)RAND_MAX + 1));
+ u32 parent_j = (u32)((u64)rand() * (u64)selection_n / ((u64)RAND_MAX + 1));
- // printf("%p - %p\n", &population[i], population[i]);
crossover(&population[i], population[parent_i], population[parent_j]);
}
- // Mutation
- uint32_t mutation_n = rate_mutation * (population_n - selection_n);
- for (uint32_t i = 0; i < mutation_n; i++) {
- uint32_t mutation_i = rand() % (population_n - selection_n) + selection_n;
- mutation(population[mutation_i]);
+ // Mutation count
+ u32 mutation_n = (u32)(rate_mutation * (f64)(population_n - selection_n));
+ for (u32 i = 0; i < mutation_n; i++) {
+ u32 range = population_n - selection_n;
+ u32 mutation_i = selection_n + (u32)((u64)rand() * (u64)range / ((u64)RAND_MAX + 1));
+ mutation(&population[mutation_i]);
}
// Fitness
diff --git a/include/interpolate.h b/include/interpolate.h
index 1f79405..da23315 100755
--- a/include/interpolate.h
+++ b/include/interpolate.h
@@ -1,31 +1,194 @@
// Math for Game Programmers: Fast and Funky 1D Nonlinear Transformations
// https://www.youtube.com/watch?v=mr5xkf6zSzk
+//
+// Thread safety: Safe for concurrent use (no shared state, pure functions).
+// Macros: Namespaced INTERP_* variants are collision-safe. Legacy un-prefixed
+// names are provided via #ifndef guard — if your code already defines e.g.
+// "mix", the library will not override it.
#ifndef INTERPOLATE_H
-#define INTERPOLATE_H
+#define INTERPOLATE_H
-#define smooth_start_2( t ) ( t * t )
-#define smooth_start_3( t ) ( smooth_start_2( t ) * t )
-#define smooth_start_4( t ) ( smooth_start_3( t ) * t )
-#define smooth_start_5( t ) ( smooth_start_4( t ) * t )
-#define smooth_start_6( t ) ( smooth_start_5( t ) * t )
+// Namespaced macros (safe from collisions). Use these in new code.
+#ifndef INTERP_FLIP
+#define INTERP_FLIP(x) (1.0 - (double)(x))
+#endif
+#ifndef INTERP_SCALE
+#define INTERP_SCALE(a, t) ((double)(t) * (double)(a))
+#endif
+#ifndef INTERP_REVERSE_SCALE
+#define INTERP_REVERSE_SCALE(a, t) ((1.0 - (double)(t)) * (double)(a))
+#endif
+#ifndef INTERP_MIX
+#define INTERP_MIX(a, b, b_weight) ((double)(a) + (double)(b_weight) * ((double)(b) - (double)(a)))
+#endif
+#ifndef INTERP_CROSSFADE
+#define INTERP_CROSSFADE(a, b, t) ((double)(a) + (double)(t) * ((double)(b) - (double)(a)))
+#endif
-#define smooth_stop_2( t ) ( flip( smooth_start_2( flip( t ) ) ) )
-#define smooth_stop_3( t ) ( flip( smooth_start_3( flip( t ) ) ) )
-#define smooth_stop_4( t ) ( flip( smooth_start_4( flip( t ) ) ) )
-#define smooth_stop_5( t ) ( flip( smooth_start_5( flip( t ) ) ) )
-#define smooth_stop_6( t ) ( flip( smooth_start_6( flip( t ) ) ) )
+#ifndef INTERP_SMOOTH_START_2
+#define INTERP_SMOOTH_START_2(t) ((double)(t) * (double)(t))
+#endif
+#ifndef INTERP_SMOOTH_START_3
+#define INTERP_SMOOTH_START_3(t) (INTERP_SMOOTH_START_2(t) * (double)(t))
+#endif
+#ifndef INTERP_SMOOTH_START_4
+#define INTERP_SMOOTH_START_4(t) (INTERP_SMOOTH_START_3(t) * (double)(t))
+#endif
+#ifndef INTERP_SMOOTH_START_5
+#define INTERP_SMOOTH_START_5(t) (INTERP_SMOOTH_START_4(t) * (double)(t))
+#endif
+#ifndef INTERP_SMOOTH_START_6
+#define INTERP_SMOOTH_START_6(t) (INTERP_SMOOTH_START_5(t) * (double)(t))
+#endif
-#define smooth_step_3( t ) ( crossfade( smooth_start_2( t ), smooth_stop_2( t ), t ) )
-#define mix( a, b, b_weight ) ( a + b_weight * ( b - a ) )
-#define crossfade( a, b, t ) ( a + t * ( b - a ) )
-#define scale( a, t ) ( t * a )
-#define reverse_scale( a, t ) ( ( 1 - t ) * a )
+#ifndef INTERP_SMOOTH_STOP_2
+#define INTERP_SMOOTH_STOP_2(t) (INTERP_FLIP(INTERP_SMOOTH_START_2(INTERP_FLIP(t))))
+#endif
+#ifndef INTERP_SMOOTH_STOP_3
+#define INTERP_SMOOTH_STOP_3(t) (INTERP_FLIP(INTERP_SMOOTH_START_3(INTERP_FLIP(t))))
+#endif
+#ifndef INTERP_SMOOTH_STOP_4
+#define INTERP_SMOOTH_STOP_4(t) (INTERP_FLIP(INTERP_SMOOTH_START_4(INTERP_FLIP(t))))
+#endif
+#ifndef INTERP_SMOOTH_STOP_5
+#define INTERP_SMOOTH_STOP_5(t) (INTERP_FLIP(INTERP_SMOOTH_START_5(INTERP_FLIP(t))))
+#endif
+#ifndef INTERP_SMOOTH_STOP_6
+#define INTERP_SMOOTH_STOP_6(t) (INTERP_FLIP(INTERP_SMOOTH_START_6(INTERP_FLIP(t))))
+#endif
-#define arch_2( t ) ( scale( flip( t ), t ) )
-#define smooth_start_arch_3( t ) ( scale( arch_2( t ), t ) )
-#define smooth_stop_arch_3( t ) ( reverse_scale( arch_2( t ), t ) )
+#ifndef INTERP_SMOOTH_STEP_3
+#define INTERP_SMOOTH_STEP_3(t) (INTERP_CROSSFADE(INTERP_SMOOTH_START_2(t), INTERP_SMOOTH_STOP_2(t), (double)(t)))
+#endif
-#define flip( x ) ( 1 - x )
+#ifndef INTERP_ARCH_2
+#define INTERP_ARCH_2(t) (INTERP_SCALE(INTERP_FLIP(t), (double)(t)))
+#endif
+#ifndef INTERP_SMOOTH_START_ARCH_3
+#define INTERP_SMOOTH_START_ARCH_3(t) (INTERP_SCALE(INTERP_ARCH_2(t), (double)(t)))
+#endif
+#ifndef INTERP_SMOOTH_STOP_ARCH_3
+#define INTERP_SMOOTH_STOP_ARCH_3(t) (INTERP_REVERSE_SCALE(INTERP_ARCH_2(t), (double)(t)))
+#endif
-#endif \ No newline at end of file
+// Legacy macro names — alias to namespaced variants when not already defined by user.
+#ifndef flip
+#define flip(x) INTERP_FLIP(x)
+#endif
+#ifndef scale
+#define scale(a, t) INTERP_SCALE(a, t)
+#endif
+#ifndef reverse_scale
+#define reverse_scale(a, t) INTERP_REVERSE_SCALE(a, t)
+#endif
+#ifndef mix
+#define mix(a, b, b_weight) INTERP_MIX(a, b, b_weight)
+#endif
+#ifndef crossfade
+#define crossfade(a, b, t) INTERP_CROSSFADE(a, b, t)
+#endif
+
+#ifndef smooth_start_2
+#define smooth_start_2(t) INTERP_SMOOTH_START_2(t)
+#endif
+#ifndef smooth_start_3
+#define smooth_start_3(t) INTERP_SMOOTH_START_3(t)
+#endif
+#ifndef smooth_start_4
+#define smooth_start_4(t) INTERP_SMOOTH_START_4(t)
+#endif
+#ifndef smooth_start_5
+#define smooth_start_5(t) INTERP_SMOOTH_START_5(t)
+#endif
+#ifndef smooth_start_6
+#define smooth_start_6(t) INTERP_SMOOTH_START_6(t)
+#endif
+
+#ifndef smooth_stop_2
+#define smooth_stop_2(t) INTERP_SMOOTH_STOP_2(t)
+#endif
+#ifndef smooth_stop_3
+#define smooth_stop_3(t) INTERP_SMOOTH_STOP_3(t)
+#endif
+#ifndef smooth_stop_4
+#define smooth_stop_4(t) INTERP_SMOOTH_STOP_4(t)
+#endif
+#ifndef smooth_stop_5
+#define smooth_stop_5(t) INTERP_SMOOTH_STOP_5(t)
+#endif
+#ifndef smooth_stop_6
+#define smooth_stop_6(t) INTERP_SMOOTH_STOP_6(t)
+#endif
+
+#ifndef smooth_step_3
+#define smooth_step_3(t) INTERP_SMOOTH_STEP_3(t)
+#endif
+
+#ifndef arch_2
+#define arch_2(t) INTERP_ARCH_2(t)
+#endif
+#ifndef smooth_start_arch_3
+#define smooth_start_arch_3(t) INTERP_SMOOTH_START_ARCH_3(t)
+#endif
+#ifndef smooth_stop_arch_3
+#define smooth_stop_arch_3(t) INTERP_SMOOTH_STOP_ARCH_3(t)
+#endif
+
+/* Function wrappers for debuggability (use these instead of macros when debugging) */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+double interpolate_flip(double t);
+double interpolate_scale(double a, double t);
+double interpolate_reverse_scale(double a, double t);
+double interpolate_mix(double a, double b, double b_weight);
+double interpolate_crossfade(double a, double b, double t);
+double interpolate_smooth_start_2(double t);
+double interpolate_smooth_start_3(double t);
+double interpolate_smooth_start_4(double t);
+double interpolate_smooth_start_5(double t);
+double interpolate_smooth_start_6(double t);
+double interpolate_smooth_stop_2(double t);
+double interpolate_smooth_stop_3(double t);
+double interpolate_smooth_stop_4(double t);
+double interpolate_smooth_stop_5(double t);
+double interpolate_smooth_stop_6(double t);
+double interpolate_smooth_step_3(double t);
+double interpolate_arch_2(double t);
+double interpolate_smooth_start_arch_3(double t);
+double interpolate_smooth_stop_arch_3(double t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#ifdef INTERPOLATE_IMPLEMENTATION
+#ifndef INTERPOLATE_IMPLEMENTED
+#define INTERPOLATE_IMPLEMENTED
+
+double interpolate_flip(double t) { return flip(t); }
+double interpolate_scale(double a, double t) { return scale(a, t); }
+double interpolate_reverse_scale(double a, double t) { return reverse_scale(a, t); }
+double interpolate_mix(double a, double b, double b_weight) { return mix(a, b, b_weight); }
+double interpolate_crossfade(double a, double b, double t) { return crossfade(a, b, t); }
+double interpolate_smooth_start_2(double t) { return smooth_start_2(t); }
+double interpolate_smooth_start_3(double t) { return smooth_start_3(t); }
+double interpolate_smooth_start_4(double t) { return smooth_start_4(t); }
+double interpolate_smooth_start_5(double t) { return smooth_start_5(t); }
+double interpolate_smooth_start_6(double t) { return smooth_start_6(t); }
+double interpolate_smooth_stop_2(double t) { return smooth_stop_2(t); }
+double interpolate_smooth_stop_3(double t) { return smooth_stop_3(t); }
+double interpolate_smooth_stop_4(double t) { return smooth_stop_4(t); }
+double interpolate_smooth_stop_5(double t) { return smooth_stop_5(t); }
+double interpolate_smooth_stop_6(double t) { return smooth_stop_6(t); }
+double interpolate_smooth_step_3(double t) { return smooth_step_3(t); }
+double interpolate_arch_2(double t) { return arch_2(t); }
+double interpolate_smooth_start_arch_3(double t) { return smooth_start_arch_3(t); }
+double interpolate_smooth_stop_arch_3(double t) { return smooth_stop_arch_3(t); }
+
+#endif /* INTERPOLATE_IMPLEMENTED */
+#endif // INTERPOLATE_IMPLEMENTATION \ No newline at end of file
diff --git a/include/pool.h b/include/pool.h
index d418142..7121f18 100755
--- a/include/pool.h
+++ b/include/pool.h
@@ -1,38 +1,43 @@
// pool.h - Fixed block size, free list based, memory pool
+//
+// Note: block_s must be >= sizeof(void*) and aligned to maintain proper
+// pointer alignment for returned blocks.
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Memory safety: pool_free validates pointer range, alignment, and detects
+// double-free via free-list walk (O(n) in number of free blocks).
+// Invalid frees are silently ignored.
#ifndef POOL_H
#define POOL_H
-#include <complex.h>
-#include <setjmp.h>
-#include <stdint.h>
+#include "type.h"
typedef struct pool_t {
- uint32_t block_n; // Block capacity
- uint32_t block_i; // Block count
- uint32_t block_s; // Block size in bytes
- void *data; // Blocks
- void *free; // Free chunk
+ u32 block_n; // Block capacity
+ u32 block_i; // Block count
+ u32 block_s; // Block size in bytes
+ void *data; // Blocks
+ void *free; // Free chunk
} pool_t;
-pool_t *pool_init(uint32_t, uint32_t);
-void pool_fini(pool_t *);
+pool_t *pool_init(u32, u32);
+void pool_fini(pool_t **);
void *pool_malloc(pool_t *);
void pool_free(pool_t *, void *);
+u32 pool_capacity(const pool_t *);
+u32 pool_size(const pool_t *);
+
#endif // POOL_H
-#define POOL_IMPLEMENTATION
#ifdef POOL_IMPLEMENTATION // IMPLEMENTATION
-#define POOL_MULTIPLIER_RESIZE 2
-#define POOL_BLOCK_N 10
-
-#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
#include <stdlib.h>
-static void pool_print(pool_t *pool);
-static void pool_free_list(uintptr_t *, uint32_t, uint32_t);
+static void pool_free_list(uintptr_t *, u32, u32);
// pool_init()
//
@@ -41,16 +46,32 @@ static void pool_free_list(uintptr_t *, uint32_t, uint32_t);
// block_s - The size of a block in bytes
// block_n - The number of initial blocks
-pool_t *pool_init( uint32_t block_s, uint32_t block_n) {
+pool_t *pool_init(u32 block_s, u32 block_n) {
+ if (block_n == 0)
+ return NULL;
+ if (block_s < sizeof(void *) || block_s % sizeof(void *) != 0)
+ return NULL;
+
pool_t *pool = (pool_t *)malloc(sizeof(pool_t));
+ if (!pool)
+ return NULL;
// Set size and count
pool->block_n = block_n;
pool->block_s = block_s;
+ pool->block_i = 0;
+ if (block_n != 0 && block_s > SIZE_MAX / (size_t)block_n) {
+ free(pool);
+ return NULL;
+ }
pool->data = calloc(block_n, block_s);
- pool->free = pool->data;
+ if (!pool->data) {
+ free(pool);
+ return NULL;
+ }
+ pool->free = pool->data;
pool_free_list((uintptr_t *)pool->data, block_n, block_s);
return pool;
@@ -63,6 +84,8 @@ pool_t *pool_init( uint32_t block_s, uint32_t block_n) {
// pool - A pointer to the pool object
void *pool_malloc(pool_t *pool) {
+ if (!pool)
+ return NULL;
uintptr_t *free = (uintptr_t *)pool->free;
if (free) {
@@ -80,6 +103,31 @@ void *pool_malloc(pool_t *pool) {
// pool - A pointer to the pool object
void pool_free(pool_t *pool, void *p) {
+ if (!pool || !p || !pool->data)
+ return;
+ // Validate pointer is within pool range
+ u8 *base = (u8 *)pool->data;
+ u8 *ptr = (u8 *)p;
+ if ((u64)pool->block_n > UINT64_MAX / (u64)pool->block_s)
+ return;
+ u64 total = (u64)pool->block_n * pool->block_s;
+ if (total > SIZE_MAX)
+ return;
+ u8 *end = base + (size_t)total;
+ if (ptr < base || ptr >= end)
+ return;
+ // Validate pointer is aligned to block size
+ if ((ptr - base) % pool->block_s != 0)
+ return;
+ // Check for double-free: walk free list to detect already-freed block
+ {
+ uintptr_t *cur = (uintptr_t *)pool->free;
+ while (cur) {
+ if ((uintptr_t)cur == (uintptr_t)p)
+ return;
+ cur = (uintptr_t *)*cur;
+ }
+ }
// Convert the pointer into a usable type
uintptr_t *free = (uintptr_t *)p;
@@ -89,30 +137,21 @@ void pool_free(pool_t *pool, void *p) {
// Set the pool's free head to the newly freed pointer
pool->free = (void *)free;
- pool->block_i--;
+ if (pool->block_i > 0)
+ pool->block_i--;
}
-uint32_t pool_capacity(pool_t *pool) {
- return pool->block_n;
-}
+u32 pool_capacity(const pool_t *pool) { if (!pool) return 0; return pool->block_n; }
-uint32_t pool_size(pool_t *pool) {
- return pool->block_i;
-}
+u32 pool_size(const pool_t *pool) { if (!pool) return 0; return pool->block_i; }
-static void pool_print(pool_t *pool) {
- uintptr_t *ptr = (uintptr_t *)pool->free;
- while (((uintptr_t)ptr - (uintptr_t)pool->data) <
- pool->block_n * pool->block_s) {
- printf("%p %lx\n", ptr, *ptr);
- ptr = (uintptr_t *)(((uint8_t *)ptr) + pool->block_s);
- }
-}
+static void pool_free_list(uintptr_t *block, u32 block_n,
+ u32 block_s) {
+ if (block_n == 0)
+ return;
-static void pool_free_list(uintptr_t *block, uint32_t block_n,
- uint32_t block_s) {
- uint8_t *ptr = (uint8_t *)block;
- for (int i = 0; i < block_n - 1; i++) {
+ u8 *ptr = (u8 *)block;
+ for (u32 i = 0; i < block_n - 1; i++) {
uintptr_t *tmp = (uintptr_t *)ptr;
*tmp = (uintptr_t)ptr + block_s;
ptr = ptr + block_s;
@@ -121,4 +160,12 @@ static void pool_free_list(uintptr_t *block, uint32_t block_n,
*(uintptr_t *)ptr = (uintptr_t)0;
}
+void pool_fini(pool_t **pool) {
+ if (pool && *pool) {
+ free((*pool)->data);
+ free(*pool);
+ *pool = NULL;
+ }
+}
+
#endif // POOL_IMPLEMENTATION
diff --git a/include/pushdown.h b/include/pushdown.h
index aa7c308..5369cfc 100644
--- a/include/pushdown.h
+++ b/include/pushdown.h
@@ -1,11 +1,23 @@
+// pushdown.h — Stack-based pushdown automata with 65+ math/stack operations
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Behavior: Instructions execute sequentially until END. Invalid instruction
+// values cause immediate termination (pushdown_run) or validation failure
+// (pushdown_validate). Arithmetic on invalid input (div/0, sqrt negative,
+// log <= 0, etc.) returns 0 rather than signaling error.
+
#ifndef PUSHDOWN_H
#define PUSHDOWN_H
-typedef pushdown_value_t stack_value_t;
-#include "./stack.h"
+/* USER-DEFINED (define before including pushdown.h):
+ * typedef <type> stack_value_t; (required by stack.h)
+ * typedef <type> pushdown_value_t; (required by pushdown.h)
+ */
+
+#include "stack.h"
typedef enum {
- END,
+ NOOP,
PUSH,
POP,
SKIP,
@@ -14,12 +26,18 @@ typedef enum {
SUB,
MUL,
DIV,
+ RECIP,
+ INC,
+ DEC,
+ SIGN,
+ NEG,
SIN,
COS,
TAN,
ASIN,
ACOS,
ATAN,
+ ATAN2,
SINH,
COSH,
TANH,
@@ -27,111 +45,830 @@ typedef enum {
ACOSH,
ATANH,
EXP,
+ POW,
LOG,
SQRT,
- ABS
+ ABS,
+ CEIL,
+ FLOOR,
+ ERF,
+ GAMMA,
+ MIN,
+ MAX,
+ CLAMP,
+ ROUNDD,
+ FRACT,
+ MOD,
+ LOG10,
+ LOG2,
+ SIGNUM,
+ SWAP,
+ ROT3,
+ DEPTH,
+ OVER,
+ PICK,
+ DROP2,
+ NIP,
+ TUCK,
+ EQUAL,
+ LESS,
+ GREATER,
+ COMPARE,
+ PI_CONST,
+ E_CONST,
+ RAND,
+ SQUARE,
+ CUBE,
+ RSQRT,
+ SORT2,
+ SORT3,
+ MINMAX,
+ END
} pushdown_instruction_t;
void pushdown_run(stack_t *, stack_t *, pushdown_instruction_t *);
+u32 pushdown_instruction_count(pushdown_instruction_t *);
+int pushdown_validate(pushdown_instruction_t *);
+void pushdown_print(pushdown_instruction_t *);
#endif
#ifdef PUSHDOWN_IMPLEMENTATION
+#ifndef PUSHDOWN_IMPLEMENTED
+#define PUSHDOWN_IMPLEMENTED
#include <tgmath.h>
+static void _noop(stack_t *output, stack_t *input) { (void)output; (void)input; }
+
+static void _push(stack_t *output, stack_t *input) {
+ if (input->size < 1)
+ return;
+ pushdown_value_t x = stack_peek(input);
+ if (!stack_push(output, x))
+ return;
+ stack_pop(input);
+}
+
+static void _pop(stack_t *output, stack_t *input) { (void)input; stack_pop(output); }
+
+static void _skip(stack_t *output, stack_t *input) { (void)output; stack_pop(input); }
+
+static void _copy(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_peek(output);
+ if (!stack_push(output, x))
+ return;
+}
+
+static void _add(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = x + y;
+}
+
+static void _sub(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = x - y;
+}
+
+static void _mul(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = x * y;
+}
+
+static void _div(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (y == 0) ? 0 : x / y;
+}
+
+static void _recip(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x == 0) ? 0 : 1.0 / x;
+}
+
+static void _inc(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = x + 1;
+}
+
+static void _dec(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = x - 1;
+}
+
+static void _sign(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x > 0) ? 1 : ((x < 0) ? -1 : 0);
+}
+
+static void _neg(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = -x;
+}
+
+static void _sin(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = sin(x);
+}
+
+static void _cos(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = cos(x);
+}
+
+static void _tan(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = tan(x);
+}
+
+static void _asin(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x < -1 || x > 1) ? 0 : asin(x);
+}
+
+static void _acos(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x < -1 || x > 1) ? 0 : acos(x);
+}
+
+static void _atan(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = atan(x);
+}
+
+static void _sinh(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = sinh(x);
+}
+
+static void _cosh(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = cosh(x);
+}
+
+static void _tanh(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = tanh(x);
+}
+
+static void _asinh(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = asinh(x);
+}
+
+static void _acosh(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x < 1) ? 0 : acosh(x);
+}
+
+static void _atanh(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x <= -1 || x >= 1) ? 0 : atanh(x);
+}
+
+static void _atan2(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = atan2(x, y);
+}
+
+static void _exp(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = exp(x);
+}
+
+static void _pow(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (x < 0 && fmod(y, 1.0) != 0.0) ? 0 : pow(x, y);
+}
+
+static void _log(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x <= 0) ? 0 : log(x);
+}
+
+static void _sqrt(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x < 0) ? 0 : sqrt(x);
+}
+
+static void _abs(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = fabs(x);
+}
+
+static void _ceil(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = ceil(x);
+}
+
+static void _floor(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = floor(x);
+}
+
+static void _erf(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = erf(x);
+}
+
+static void _min(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (x < y) ? x : y;
+}
+
+static void _max(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (x > y) ? x : y;
+}
+
+static void _clamp(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 3)
+ return;
+ pushdown_value_t hi = stack_pop(output);
+ pushdown_value_t lo = stack_pop(output);
+ pushdown_value_t v = stack_pop(output);
+ output->data[output->size++] = (v < lo) ? lo : ((v > hi) ? hi : v);
+}
+
+static void _roundd(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = round(x);
+}
+
+static void _fract(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = x - floor(x);
+}
+
+static void _mod(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (y == 0) ? 0 : fmod(x, y);
+}
+
+static void _log10_fn(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x <= 0) ? 0 : log10(x);
+}
+
+static void _log2_fn(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x <= 0) ? 0 : log2(x);
+}
+
+static void _signum(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x > 0) ? 1 : ((x < 0) ? -1 : 0);
+}
+
+static void _swap(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = x;
+ output->data[output->size++] = y;
+}
+
+static void _rot3(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 3)
+ return;
+ pushdown_value_t t1 = stack_pop(output);
+ pushdown_value_t t2 = stack_pop(output);
+ pushdown_value_t t3 = stack_pop(output);
+ output->data[output->size++] = t2;
+ output->data[output->size++] = t3;
+ output->data[output->size++] = t1;
+}
+
+static void _depth(stack_t *output, stack_t *input) {
+ (void)input;
+ pushdown_value_t d = (pushdown_value_t)output->size;
+ output->data[output->size++] = d;
+}
+
+static void _over(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = output->data[output->size - 2];
+ if (!stack_push(output, x))
+ return;
+}
+
+static void _pick(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t n = stack_pop(output);
+ u32 idx = (u32)n;
+ if (idx >= output->size) {
+ output->data[output->size++] = n;
+ return;
+ }
+ pushdown_value_t x = output->data[output->size - 1 - idx];
+ if (!stack_push(output, x))
+ return;
+}
+
+static void _drop2(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ output->size -= 2;
+}
+
+static void _nip(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t top = stack_pop(output);
+ stack_pop(output);
+ output->data[output->size++] = top;
+}
+
+static void _tuck(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t top = stack_pop(output);
+ pushdown_value_t second = output->data[output->size - 1];
+ if (!stack_push(output, top))
+ return;
+ output->data[output->size++] = second;
+ output->data[output->size++] = top;
+}
+
+static void _equal(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (x == y) ? 1 : 0;
+}
+
+static void _less(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (y < x) ? 1 : 0;
+}
+
+static void _greater(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (y > x) ? 1 : 0;
+}
+
+static void _compare(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (y > x) ? -1 : ((y < x) ? 1 : 0);
+}
+
+static void _pi_const(stack_t *output, stack_t *input) {
+ (void)input;
+ if (!stack_push(output, 3.14159265358979323846))
+ return;
+}
+
+static void _e_const(stack_t *output, stack_t *input) {
+ (void)input;
+ if (!stack_push(output, 2.71828182845904523536))
+ return;
+}
+
+static void _rand(stack_t *output, stack_t *input) {
+ (void)input;
+ if (!stack_push(output, (pushdown_value_t)((f64)rand() / RAND_MAX)))
+ return;
+}
+
+static void _square(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = x * x;
+}
+
+static void _cube(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = x * x * x;
+}
+
+static void _rsqrt(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = (x == 0) ? 0 : 1.0 / sqrt(x < 0 ? -x : x);
+}
+
+static void _sort2(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ if (x < y) {
+ output->data[output->size++] = x;
+ output->data[output->size++] = y;
+ } else {
+ output->data[output->size++] = y;
+ output->data[output->size++] = x;
+ }
+}
+
+static void _sort3(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 3)
+ return;
+ pushdown_value_t a = stack_pop(output);
+ pushdown_value_t b = stack_pop(output);
+ pushdown_value_t c = stack_pop(output);
+ pushdown_value_t s[3] = {a, b, c};
+ for (u32 i = 0; i < 3; i++)
+ for (u32 j = i + 1; j < 3; j++)
+ if (s[j] < s[i]) {
+ pushdown_value_t tmp = s[i];
+ s[i] = s[j];
+ s[j] = tmp;
+ }
+ output->data[output->size++] = s[0];
+ output->data[output->size++] = s[1];
+ output->data[output->size++] = s[2];
+}
+
+static void _minmax(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 2)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ pushdown_value_t y = stack_pop(output);
+ output->data[output->size++] = (x < y) ? x : y;
+ output->data[output->size++] = (x > y) ? x : y;
+}
+
+static void _gamma(stack_t *output, stack_t *input) {
+ (void)input;
+ if (output->size < 1)
+ return;
+ pushdown_value_t x = stack_pop(output);
+ output->data[output->size++] = tgamma(x);
+}
+
+static void (*OPERATIONS[])(stack_t *output, stack_t *input) = {
+ [NOOP] = _noop,
+ [PUSH] = _push,
+ [POP] = _pop,
+ [SKIP] = _skip,
+ [COPY] = _copy,
+ [ADD] = _add,
+ [SUB] = _sub,
+ [MUL] = _mul,
+ [DIV] = _div,
+ [RECIP] = _recip,
+ [INC] = _inc,
+ [DEC] = _dec,
+ [SIGN] = _sign,
+ [NEG] = _neg,
+ [SIN] = _sin,
+ [COS] = _cos,
+ [TAN] = _tan,
+ [ASIN] = _asin,
+ [ACOS] = _acos,
+ [ATAN] = _atan,
+ [ATAN2] = _atan2,
+ [SINH] = _sinh,
+ [COSH] = _cosh,
+ [TANH] = _tanh,
+ [ASINH] = _asinh,
+ [ACOSH] = _acosh,
+ [ATANH] = _atanh,
+ [EXP] = _exp,
+ [POW] = _pow,
+ [LOG] = _log,
+ [SQRT] = _sqrt,
+ [ABS] = _abs,
+ [CEIL] = _ceil,
+ [FLOOR] = _floor,
+ [ERF] = _erf,
+ [GAMMA] = _gamma,
+ [MIN] = _min,
+ [MAX] = _max,
+ [CLAMP] = _clamp,
+ [ROUNDD] = _roundd,
+ [FRACT] = _fract,
+ [MOD] = _mod,
+ [LOG10] = _log10_fn,
+ [LOG2] = _log2_fn,
+ [SIGNUM] = _signum,
+ [SWAP] = _swap,
+ [ROT3] = _rot3,
+ [DEPTH] = _depth,
+ [OVER] = _over,
+ [PICK] = _pick,
+ [DROP2] = _drop2,
+ [NIP] = _nip,
+ [TUCK] = _tuck,
+ [EQUAL] = _equal,
+ [LESS] = _less,
+ [GREATER] = _greater,
+ [COMPARE] = _compare,
+ [PI_CONST] = _pi_const,
+ [E_CONST] = _e_const,
+ [RAND] = _rand,
+ [SQUARE] = _square,
+ [CUBE] = _cube,
+ [RSQRT] = _rsqrt,
+ [SORT2] = _sort2,
+ [SORT3] = _sort3,
+ [MINMAX] = _minmax,
+};
+
+// pushdown_run()
+//
+// Execute a sequence of instructions until END. Instructions operate on
+// the output stack, pulling values from the input stack via PUSH.
+
void pushdown_run(stack_t *output, stack_t *input,
- pushdown_instruction_t *instructions) {
+ pushdown_instruction_t *instructions) {
+ if (!output || !input || !instructions)
+ return;
+ pushdown_instruction_t *op = instructions;
+
+ while (*op != END) {
+ if (*op > MINMAX || *op < NOOP)
+ break;
+ OPERATIONS[*op](output, input);
+ op++;
+ }
+}
+
+// pushdown_instruction_count()
+//
+// Count the number of instructions before END marker.
+//
+// return - Instruction count, or 0 if NULL or invalid
+
+u32 pushdown_instruction_count(pushdown_instruction_t *instructions) {
+ if (!instructions)
+ return 0;
+ u32 count = 0;
+ pushdown_instruction_t *op = instructions;
+ while (*op != END) {
+ if (*op > MINMAX || *op < NOOP)
+ return count;
+ count++;
+ op++;
+ }
+ return count;
+}
+
+// pushdown_validate()
+//
+// Check that all instruction values in the sequence are valid enum members.
+//
+// return - 1 if valid, 0 if invalid
+
+int pushdown_validate(pushdown_instruction_t *instructions) {
+ if (!instructions)
+ return 0;
pushdown_instruction_t *op = instructions;
+ while (*op != END) {
+ if (*op < NOOP || *op > MINMAX)
+ return 0;
+ op++;
+ }
+ return 1;
+}
- pushdown_value_t x, y;
+// pushdown_print()
+//
+// Print the instruction sequence to stdout in readable format
- while (*op) {
+void pushdown_print(pushdown_instruction_t *instructions) {
+ if (!instructions)
+ return;
+ pushdown_instruction_t *op = instructions;
+ printf("[");
+ int first = 1;
+ while (*op != END) {
+ if (!first) printf(", ");
switch (*op) {
- case PUSH:
- stack_push(output, stack_pop(input));
- break;
- case POP:
- stack_pop(output);
- break;
- case SKIP:
- stack_pop(input);
- break;
- case COPY:
- stack_push(output, stack_peek(output));
- break;
- case ADD:
- x = stack_pop(output);
- y = stack_pop(output);
- stack_push(output, x + y);
- break;
- case SUB:
- x = stack_pop(output);
- y = stack_pop(output);
- stack_push(output, x - y);
- break;
- case MUL:
- x = stack_pop(output);
- y = stack_pop(output);
- stack_push(output, x * y);
- break;
- case DIV:
- x = stack_pop(output);
- y = stack_pop(output);
- stack_push(output, x / y);
- break;
- case SIN:
- stack_push(output, sin(stack_pop(output)));
- break;
- case COS:
- stack_push(output, cos(stack_pop(output)));
- break;
- case TAN:
- stack_push(output, tan(stack_pop(output)));
- break;
- case ASIN:
- stack_push(output, asin(stack_pop(output)));
- break;
- case ACOS:
- stack_push(output, acos(stack_pop(output)));
- break;
- case ATAN:
- stack_push(output, atan(stack_pop(output)));
- break;
- case SINH:
- stack_push(output, sinh(stack_pop(output)));
- break;
- case COSH:
- stack_push(output, cosh(stack_pop(output)));
- break;
- case TANH:
- stack_push(output, tanh(stack_pop(output)));
- break;
- case ASINH:
- stack_push(output, asinh(stack_pop(output)));
- break;
- case ACOSH:
- stack_push(output, acosh(stack_pop(output)));
- break;
- case ATANH:
- stack_push(output, atanh(stack_pop(output)));
- break;
- case EXP:
- stack_push(output, exp(stack_pop(output)));
- break;
- case LOG:
- stack_push(output, log(stack_pop(output)));
- break;
- case SQRT:
- stack_push(output, sqrt(stack_pop(output)));
- break;
- case ABS:
- stack_push(output, fabs(stack_pop(output)));
- break;
+ case NOOP: printf("NOOP"); break;
+ case PUSH: printf("PUSH"); break;
+ case POP: printf("POP"); break;
+ case SKIP: printf("SKIP"); break;
+ case COPY: printf("COPY"); break;
+ case ADD: printf("ADD"); break;
+ case SUB: printf("SUB"); break;
+ case MUL: printf("MUL"); break;
+ case DIV: printf("DIV"); break;
+ case RECIP: printf("RECIP"); break;
+ case INC: printf("INC"); break;
+ case DEC: printf("DEC"); break;
+ case SIGN: printf("SIGN"); break;
+ case NEG: printf("NEG"); break;
+ case SIN: printf("SIN"); break;
+ case COS: printf("COS"); break;
+ case TAN: printf("TAN"); break;
+ case ASIN: printf("ASIN"); break;
+ case ACOS: printf("ACOS"); break;
+ case ATAN: printf("ATAN"); break;
+ case ATAN2: printf("ATAN2"); break;
+ case SINH: printf("SINH"); break;
+ case COSH: printf("COSH"); break;
+ case TANH: printf("TANH"); break;
+ case ASINH: printf("ASINH"); break;
+ case ACOSH: printf("ACOSH"); break;
+ case ATANH: printf("ATANH"); break;
+ case EXP: printf("EXP"); break;
+ case POW: printf("POW"); break;
+ case LOG: printf("LOG"); break;
+ case SQRT: printf("SQRT"); break;
+ case ABS: printf("ABS"); break;
+ case CEIL: printf("CEIL"); break;
+ case FLOOR: printf("FLOOR"); break;
+ case ERF: printf("ERF"); break;
+ case GAMMA: printf("GAMMA"); break;
+ case MIN: printf("MIN"); break;
+ case MAX: printf("MAX"); break;
+ case CLAMP: printf("CLAMP"); break;
+ case ROUNDD: printf("ROUNDD"); break;
+ case FRACT: printf("FRACT"); break;
+ case MOD: printf("MOD"); break;
+ case LOG10: printf("LOG10"); break;
+ case LOG2: printf("LOG2"); break;
+ case SIGNUM: printf("SIGNUM"); break;
+ case SWAP: printf("SWAP"); break;
+ case ROT3: printf("ROT3"); break;
+ case DEPTH: printf("DEPTH"); break;
+ case OVER: printf("OVER"); break;
+ case PICK: printf("PICK"); break;
+ case DROP2: printf("DROP2"); break;
+ case NIP: printf("NIP"); break;
+ case TUCK: printf("TUCK"); break;
+ case EQUAL: printf("EQUAL"); break;
+ case LESS: printf("LESS"); break;
+ case GREATER: printf("GREATER"); break;
+ case COMPARE: printf("COMPARE"); break;
+ case PI_CONST: printf("PI"); break;
+ case E_CONST: printf("E"); break;
+ case RAND: printf("RAND"); break;
+ case SQUARE: printf("SQUARE"); break;
+ case CUBE: printf("CUBE"); break;
+ case RSQRT: printf("RSQRT"); break;
+ case SORT2: printf("SORT2"); break;
+ case SORT3: printf("SORT3"); break;
+ case MINMAX: printf("MINMAX"); break;
+ default: printf("?"); break;
}
-
+ first = 0;
op++;
}
+ printf(", END]\n");
}
-#endif \ No newline at end of file
+#endif /* PUSHDOWN_IMPLEMENTED */
+#endif /* PUSHDOWN_IMPLEMENTATION */ \ No newline at end of file
diff --git a/include/stack.h b/include/stack.h
index 7e0f711..659b013 100644
--- a/include/stack.h
+++ b/include/stack.h
@@ -1,84 +1,229 @@
+// stack.h — Generic dynamic stack
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Memory safety: Double-free and use-after-free protected via NULL reset.
+// Empty stack: pop/peek on empty stack returns zero-initialized value.
+// Use stack_pop_ok / stack_peek_ok for error detection.
+
#ifndef STACK_H
#define STACK_H
-#include "./type.h"
-#include <assert.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+/* USER-DEFINED (define before including):
+ * typedef <type> stack_value_t;
+ */
+
+#include "type.h"
typedef struct stack {
stack_value_t *data;
- stack_value_t *head;
- uint_t size;
- uint_t capacity;
+ u64 size;
+ u64 capacity;
} stack_t;
void stack_init(stack_t *);
-stack_t *stack_from_array(stack_value_t *, uint_t);
-void stack_push(stack_t *, stack_value_t);
+void stack_free(stack_t *);
+void stack_from_array(stack_t *, stack_value_t *, u64);
+int stack_push(stack_t *, stack_value_t);
stack_value_t stack_pop(stack_t *);
-stack_value_t stack_peek(stack_t *);
+int stack_pop_ok(stack_t *, stack_value_t *);
+stack_value_t stack_peek(const stack_t *);
+int stack_peek_ok(const stack_t *, stack_value_t *);
+int stack_empty(const stack_t *);
+void stack_clear(stack_t *);
#endif
#ifdef STACK_IMPLEMENTATION
+#ifndef STACK_IMPLEMENTED
+#define STACK_IMPLEMENTED
-const stack_value_t ZERO = 0;
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+static stack_value_t const ZERO = {0}; /* Requires stack_value_t to be an aggregate type. For scalar types (double, int, pointer), {0} zero-initializes the entire value. For struct types, C99 guarantees remaining members are zero-initialized. */
+
+// stack_init()
+//
+// Initialize or reset a stack. If stack->capacity is 0, defaults to 16.
+// Frees any existing data buffer before allocating new one.
void stack_init(stack_t *stack) {
+ if (!stack)
+ return;
+ if (stack->data != NULL) {
+ free(stack->data);
+ stack->data = NULL;
+ }
if (stack->capacity == 0) {
stack->capacity = 16;
}
-
+ if (stack->capacity > SIZE_MAX / sizeof(stack_value_t)) {
+ stack->capacity = 0;
+ stack->size = 0;
+ stack->data = NULL;
+ return;
+ }
stack->size = 0;
stack->data = calloc(stack->capacity, sizeof(stack_value_t));
- stack->head = stack->data;
+ if (!stack->data)
+ stack->capacity = 0;
}
-stack_t *stack_from_array(stack_value_t *array, uint_t size) {
- stack_t *stack = calloc(1, sizeof(stack_t));
+// stack_from_array()
+//
+// Initialize a stack from an existing array. Allocates exact capacity.
+void stack_from_array(stack_t *stack, stack_value_t *array, u64 size) {
+ if (!stack || !array || size == 0)
+ return;
+ if (size > SIZE_MAX / sizeof(stack_value_t))
+ return;
+ if (stack->data)
+ free(stack->data);
stack->size = size;
stack->capacity = size;
- stack->data = calloc(size, sizeof(stack_value_t));
+ stack->data = calloc((size_t)size, sizeof(stack_value_t));
+ if (!stack->data) {
+ stack->size = 0;
+ stack->capacity = 0;
+ stack->data = NULL;
+ return;
+ }
memcpy(stack->data, array, sizeof(stack_value_t) * size);
- stack->head = stack->data;
-
- return stack;
}
-void stack_push(stack_t *stack, stack_value_t x) {
+// stack_push()
+//
+// Push a value onto the stack. Automatically doubles capacity if full.
+//
+// return - 1 on success, 0 on failure
+
+int stack_push(stack_t *stack, stack_value_t x) {
+ if (!stack || !stack->data)
+ return 0;
if (stack->size == stack->capacity) {
+ if (stack->capacity > UINT64_MAX / 2)
+ return 0;
stack->capacity *= 2;
- stack->data = realloc(stack->data, sizeof(stack_value_t) * stack->capacity);
- stack->head = &(stack->data[stack->size - 1]);
+ if (stack->capacity > SIZE_MAX / sizeof(stack_value_t)) {
+ stack->capacity /= 2;
+ return 0;
+ }
+ void *tmp = realloc(stack->data, sizeof(stack_value_t) * stack->capacity);
+ if (!tmp) {
+ stack->capacity /= 2;
+ return 0;
+ }
+ stack->data = tmp;
}
- *stack->head = x;
- stack->head++;
+ stack->data[stack->size] = x;
stack->size++;
+ return 1;
}
+// stack_pop()
+//
+// Remove and return the top element. Returns zero-initialized value on
+// empty stack — use stack_pop_ok for error detection.
+
stack_value_t stack_pop(stack_t *stack) {
- if (stack->size == 0) {
+ if (!stack || !stack->data || stack->size == 0) {
return ZERO;
}
- stack_value_t x = stack->head[-1];
stack->size--;
- stack->head--;
+ stack_value_t x = stack->data[stack->size];
+ stack->data[stack->size] = ZERO;
return x;
}
-stack_value_t stack_peek(stack_t *stack) {
- if (stack->size == 0) {
+// stack_pop_ok()
+//
+// Same as stack_pop, but writes result to *out and returns status code.
+//
+// return - 1 on success, 0 on empty stack or invalid args
+
+int stack_pop_ok(stack_t *stack, stack_value_t *out) {
+ if (!stack || !stack->data || !out || stack->size == 0) {
+ return 0;
+ }
+
+ stack->size--;
+ *out = stack->data[stack->size];
+ stack->data[stack->size] = ZERO;
+
+ return 1;
+}
+
+// stack_peek()
+//
+// Return the top element without removing it. Returns zero on empty stack
+// — use stack_peek_ok for error detection.
+
+stack_value_t stack_peek(const stack_t *stack) {
+ if (!stack || !stack->data || stack->size == 0) {
return ZERO;
}
- return stack->head[-1];
+ return stack->data[stack->size - 1];
+}
+
+// stack_peek_ok()
+//
+// Same as stack_peek, but writes result to *out and returns status code.
+//
+// return - 1 on success, 0 on empty stack or invalid args
+
+int stack_peek_ok(const stack_t *stack, stack_value_t *out) {
+ if (!stack || !stack->data || !out || stack->size == 0) {
+ return 0;
+ }
+
+ *out = stack->data[stack->size - 1];
+ return 1;
+}
+
+// stack_empty()
+//
+// Check if the stack is empty.
+//
+// return - 1 if empty, 0 otherwise
+
+int stack_empty(const stack_t *stack) {
+ if (!stack)
+ return 1;
+ return stack->size == 0;
+}
+
+// stack_clear()
+//
+// Reset the stack to empty, zeroing all allocated slots
+
+void stack_clear(stack_t *stack) {
+ if (!stack || !stack->data)
+ return;
+ if (stack->capacity > SIZE_MAX / sizeof(stack_value_t))
+ return;
+ for (u64 i = 0; i < stack->capacity; i++)
+ stack->data[i] = ZERO;
+ stack->size = 0;
+}
+
+// stack_free()
+//
+// Release the stack's data buffer and reset all fields
+
+void stack_free(stack_t *stack) {
+ if (stack) {
+ free(stack->data);
+ stack->data = NULL;
+ stack->size = 0;
+ stack->capacity = 0;
+ }
}
-#endif \ No newline at end of file
+#endif /* STACK_IMPLEMENTED */
+#endif /* STACK_IMPLEMENTATION */ \ No newline at end of file
diff --git a/include/test.h b/include/test.h
new file mode 100644
index 0000000..9f40fd6
--- /dev/null
+++ b/include/test.h
@@ -0,0 +1,57 @@
+// test.h - Minimal test assertion macros
+//
+// Not a library for distribution. Intended for the test suite only.
+// Provides ASSERT, ASSERT_FLOAT_EQ, and ASSERT_STR_EQ macros with
+// failure tracking via a global test_failures counter.
+
+#ifndef TEST_H
+#define TEST_H
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "type.h"
+
+#ifndef TEST_EPS
+#define TEST_EPS 1e-9
+#endif
+
+#ifndef TEST_FAILURES_VAR
+#define TEST_FAILURES_VAR int test_failures = 0
+#endif
+
+#define ASSERT(cond, msg) \
+ do { \
+ if (!(cond)) { \
+ fprintf(stderr, "FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); \
+ test_failures++; \
+ } \
+ } while (0)
+
+#define ASSERT_FLOAT_EQ(a, b, msg) \
+ do { \
+ f64 _a = (a); \
+ f64 _b = (b); \
+ if (fabs(_a - _b) > TEST_EPS) { \
+ fprintf(stderr, "FAIL: %s (got %f, expected %f) (%s:%d)\n", \
+ msg, (double)_a, (double)_b, __FILE__, __LINE__); \
+ test_failures++; \
+ } \
+ } while (0)
+
+#define ASSERT_STR_EQ(a, b, msg) \
+ do { \
+ char const *_a = (a); \
+ char const *_b = (b); \
+ if (_a == NULL || _b == NULL || strcmp(_a, _b) != 0) { \
+ fprintf(stderr, "FAIL: %s (got '%s', expected '%s') (%s:%d)\n", \
+ msg, _a ? _a : "NULL", _b ? _b : "NULL", __FILE__, __LINE__); \
+ test_failures++; \
+ } \
+ } while (0)
+
+#define TEST_PRINT_RESULTS(name) \
+ printf("%s: %d failures\n", name, test_failures)
+
+#endif /* TEST_H */
diff --git a/include/type.h b/include/type.h
index 56819c8..1106832 100644
--- a/include/type.h
+++ b/include/type.h
@@ -1,3 +1,8 @@
+// type.h - Common type aliases
+//
+// Used across header libraries to provide consistent, concise type names
+// for fixed-width integers and floating point types.
+
#ifndef TYPE_H
#define TYPE_H
@@ -16,8 +21,4 @@ typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
-typedef uint64_t uint_t;
-typedef int64_t int_t;
-typedef double fp_t;
-
-#endif \ No newline at end of file
+#endif
diff --git a/include/ufo.h b/include/ufo.h
new file mode 100755
index 0000000..a92cff1
--- /dev/null
+++ b/include/ufo.h
@@ -0,0 +1,730 @@
+// https://www.sciencedirect.com/science/article/abs/pii/S1568494620303574
+
+// ufo.h - Universal Function Originator
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Memory: Single-allocation design — struct and all arrays stored in one
+// contiguous buffer for easy persistence. Use ufo_copy for deep clone;
+// ufo_free to release. Serialization via ufo_serialize / ufo_deserialize.
+
+// f_j(X) = f_j(a_0j (⊙_1j) a_1j*x_1^b_1j (⊙_2j) a_2j*x_2^b_2j (⊙_3j) ... (⊙_nj)
+// a_nj*x_2^b_nj) g_j(X) = w_j * [f_j(X)]^(c_j) y = w_0 + g_1(X) ⊚_1 g_1(X) ⊚_2
+// ... ⊚_v g_v(X)
+
+#ifndef UFO_H
+#define UFO_H
+
+#include "type.h"
+
+struct ufo_t;
+
+typedef struct ufo_t {
+ // Input count
+ u32 n;
+ // The number of internal blocks
+ u32 v;
+ // Internal weights [v][n+1]
+ f64 *a;
+ // Internal exponents [v][n]
+ f64 *b;
+ // External weights [v+1]
+ f64 *w;
+ // External exponents [v]
+ f64 *c;
+ // Unary functions [v]
+ f64 (**f)(f64);
+ // External binary functions [v]
+ f64 (**fe)(f64, f64);
+ // Internal binary functions [v][n]
+ f64 (**fi)(f64, f64);
+} ufo_t;
+
+#define ufo_ajk(ufo, j, k) (*(ufo->a + (j) * (ufo->n + 1) + (k)))
+#define ufo_bjk(ufo, j, k) (*(ufo->b + (j) * (ufo->n) + (k)))
+#define ufo_fijk(ufo, j, k) (*(ufo->fi + (j) * (ufo->n) + (k)))
+
+ufo_t *ufo_init(u32 n, u32 v);
+void ufo_free(ufo_t *ufo);
+f64 ufo_evaluate(ufo_t *ufo, f64 *x);
+void ufo_print(ufo_t *ufo);
+ufo_t *ufo_copy(const ufo_t *src);
+void ufo_randomize(ufo_t *ufo, f64 min_w, f64 max_w);
+void ufo_mutate(ufo_t *ufo, f64 rate, f64 min_w, f64 max_w);
+void ufo_crossover(ufo_t *parent1, ufo_t *parent2, u32 split);
+u32 ufo_complexity(const ufo_t *ufo);
+void ufo_set_unary(ufo_t *ufo, u32 j, f64 (*fn)(f64));
+void ufo_set_external_binary(ufo_t *ufo, u32 j, f64 (*fn)(f64, f64));
+void ufo_set_internal_binary(ufo_t *ufo, u32 j, u32 k, f64 (*fn)(f64, f64));
+void ufo_evaluate_jacobian(ufo_t *ufo, f64 *x, f64 *jacobian);
+void ufo_evaluate_batch(ufo_t *ufo, f64 *x, u32 count, f64 *out);
+void ufo_reset(ufo_t *ufo);
+void ufo_simplify(ufo_t *ufo);
+f64 ufo_similar(const ufo_t *a, const ufo_t *b);
+void ufo_serialize(const ufo_t *ufo, u8 *buffer, u64 *size);
+int ufo_deserialize(ufo_t **out, const u8 *buffer, u64 size);
+u32 ufo_serialize_size(u32 n, u32 v);
+void ufo_gradient_weights(ufo_t *ufo, f64 *x, f64 *weights_grad);
+void ufo_to_formula_string(const ufo_t *ufo, char *buf, u32 buf_size);
+
+#endif // UFO_H
+
+#ifdef UFO_IMPLEMENTATION
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tgmath.h>
+
+static f64 ufo_identity(f64);
+static f64 ufo_increment(f64);
+static f64 ufo_decrement(f64);
+static f64 ufo_reciprocal(f64);
+static f64 ufo_root(f64);
+static f64 ufo_absolute_value(f64);
+static f64 ufo_floor(f64);
+static f64 ufo_ceiling(f64);
+static f64 ufo_round(f64);
+static f64 ufo_exponent(f64);
+static f64 ufo_log_natural(f64);
+static f64 ufo_log_decimal(f64);
+static f64 ufo_log_binary(f64);
+static f64 ufo_sine(f64);
+static f64 ufo_cosine(f64);
+static f64 ufo_tangent(f64);
+
+static f64 ufo_addition(f64, f64);
+static f64 ufo_subtraction(f64, f64);
+static f64 ufo_division(f64, f64);
+static f64 ufo_multiplication(f64, f64);
+static f64 ufo_power(f64, f64);
+
+ufo_t *ufo_init(u32 n, u32 v) {
+ if (n == 0 || v == 0)
+ return NULL;
+ if (n > UINT32_MAX / 5)
+ return NULL;
+ {
+ u32 factor_a = 3 * n + 5;
+ if (factor_a < 3 * n)
+ return NULL;
+ if (v > UINT32_MAX / factor_a)
+ return NULL;
+ }
+ u32 N = 3 * v * n + 5 * v;
+ {
+ u32 factor_d = 2 * n + 3;
+ if (factor_d < 2 * n)
+ return NULL;
+ if (v > UINT32_MAX / factor_d)
+ return NULL;
+ }
+ u32 D = 2 * v * n + 3 * v;
+
+ size_t bytes_a = sizeof(f64) * (size_t)N;
+ size_t bytes_d = sizeof(f64 (*)(f64)) * (size_t)D;
+ if (N > 0 && bytes_a / N != sizeof(f64))
+ return NULL;
+ if (D > 0 && bytes_d / D != sizeof(f64 (*)(f64)))
+ return NULL;
+ if (bytes_a > SIZE_MAX - bytes_d)
+ return NULL;
+ size_t bytes = bytes_a + bytes_d;
+
+ void *buffer = malloc(bytes);
+ if (!buffer)
+ return NULL;
+
+ ufo_t *ufo = (ufo_t *)buffer;
+ ufo->n = n;
+ ufo->v = v;
+
+ // Assign everything
+ ufo->a = (f64 *)(ufo + 1);
+ ufo->b = ufo->a + v * (n + 1);
+ ufo->w = ufo->b + v * n;
+ ufo->c = ufo->w + v + 1;
+ ufo->f = (f64(**)(f64))(ufo->c + v);
+ ufo->fe = (f64(**)(f64, f64))(ufo->f + v);
+ ufo->fi = (f64(**)(f64, f64))(ufo->fe + v * n);
+
+ // Initialize all weights to one
+ f64 *tmp_fp = ufo->a;
+ while ((void *)tmp_fp != (void *)ufo->f)
+ *tmp_fp++ = 1.0;
+
+ // Initialize all unary functions to identity
+ f64 (**tmp_unary)(f64) = ufo->f;
+ while ((void *)tmp_unary < (void *)ufo->fe)
+ *tmp_unary++ = ufo_identity;
+
+ // Initialize all binary functions to addition
+ f64 (**tmp_binary)(f64, f64) = ufo->fe;
+ while ((u8 *)tmp_binary != (u8 *)buffer + bytes)
+ *tmp_binary++ = ufo_addition;
+
+ // Set bias to zero
+ ufo->w[v] = 0;
+
+ for (u32 j = 0; j < v; j++)
+ ufo_ajk(ufo, j, n) = 0;
+
+ return ufo;
+}
+
+void ufo_free(ufo_t *ufo) {
+ free(ufo);
+}
+
+static f64 ufo_ujk(ufo_t *ufo, f64 *x, u32 j, u32 k) {
+ (void)k;
+ if (!ufo || !x)
+ return 0;
+ f64 acc = ufo_ajk(ufo, j, ufo->n);
+ for (u32 i = ufo->n; i > 0; i--)
+ acc = ufo_fijk(ufo, j, i - 1)(acc, x[i - 1]);
+ return acc;
+}
+
+static f64 ufo_gj(ufo_t *ufo, f64 *x, u32 j) {
+ if (!ufo || !x)
+ return 0;
+ f64 base = ufo->f[j](ufo_ujk(ufo, x, j, 0));
+ if (base < 0 && fmod(ufo->c[j], 1.0) != 0.0)
+ return 0;
+ if (base == 0.0 && ufo->c[j] <= 0.0)
+ return 0.0;
+ return ufo->w[j] * pow(base, ufo->c[j]);
+}
+
+f64 ufo_evaluate(ufo_t *ufo, f64 *x) {
+ if (!ufo || !x)
+ return 0;
+ f64 y = 0;
+
+ // Calculate g_j(X) for every j (excluding constant term)
+ u32 j;
+ for (j = 0; j < ufo->v; j++)
+ y = ufo->fe[j](y, ufo_gj(ufo, x, j));
+
+ // Constant term
+ y = ufo->fe[0](y, ufo->w[ufo->v]);
+
+ return y;
+}
+
+void ufo_print(ufo_t *ufo) {
+ if (!ufo)
+ return;
+ printf("UFO: %p\n"
+ "\n"
+ "N: %u\n"
+ "V: %u\n"
+ "A: %p\n"
+ "B: %p\n"
+ "W: %p\n"
+ "C: %p\n"
+ "F: %p\n"
+ "FE: %p\n"
+ "FI: %p\n",
+ (void *)ufo, (unsigned)ufo->n, (unsigned)ufo->v,
+ (void *)ufo->a, (void *)ufo->b, (void *)ufo->w, (void *)ufo->c,
+ (void *)ufo->f, (void *)ufo->fe, (void *)ufo->fi);
+}
+
+static f64 ufo_identity(f64 x) { return x; }
+static f64 ufo_increment(f64 x) { return x + 1; }
+static f64 ufo_decrement(f64 x) { return x - 1; }
+static f64 ufo_reciprocal(f64 x) { return (x == 0) ? 0 : 1 / x; }
+static f64 ufo_root(f64 x) { return (x < 0) ? 0 : sqrt(x); }
+static f64 ufo_absolute_value(f64 x) { return fabs(x); }
+static f64 ufo_floor(f64 x) { return floor(x); }
+static f64 ufo_ceiling(f64 x) { return ceil(x); }
+static f64 ufo_round(f64 x) { return round(x); }
+static f64 ufo_exponent(f64 x) { return exp(x); }
+static f64 ufo_log_natural(f64 x) { return (x <= 0) ? 0 : log(x); }
+static f64 ufo_log_decimal(f64 x) { return (x <= 0) ? 0 : log10(x); }
+static f64 ufo_log_binary(f64 x) { return (x <= 0) ? 0 : log2(x); }
+static f64 ufo_sine(f64 x) { return sin(x); }
+static f64 ufo_cosine(f64 x) { return cos(x); }
+static f64 ufo_tangent(f64 x) { return tan(x); }
+
+__attribute__((unused)) static f64 (*UNARY[])(f64) = {
+ ufo_identity, ufo_increment, ufo_decrement, ufo_reciprocal,
+ ufo_root, ufo_absolute_value, ufo_floor, ufo_ceiling,
+ ufo_round, ufo_exponent, ufo_log_natural, ufo_log_decimal,
+ ufo_log_binary, ufo_sine, ufo_cosine,
+ ufo_tangent
+};
+
+f64 ufo_addition(f64 x0, f64 x1) { return (x0 + x1); }
+f64 ufo_subtraction(f64 x0, f64 x1) { return (x0 - x1); }
+f64 ufo_division(f64 x0, f64 x1) { return (x1 == 0) ? 0 : (x0 / x1); }
+f64 ufo_multiplication(f64 x0, f64 x1) { return (x0 * x1); }
+f64 ufo_power(f64 x0, f64 x1) { return (x0 >= 0 || fmod(x1, 1.0) == 0.0) ? pow(x0, x1) : 0; }
+
+__attribute__((unused)) static f64 (*BINARY[])(f64, f64) = {ufo_addition, ufo_subtraction, ufo_division,
+ ufo_multiplication, ufo_power};
+
+ufo_t *ufo_copy(const ufo_t *src) {
+ if (!src)
+ return NULL;
+ ufo_t *dst = ufo_init(src->n, src->v);
+ if (!dst)
+ return NULL;
+ memcpy(dst->a, src->a, sizeof(f64) * (src->v * (src->n + 1)));
+ memcpy(dst->b, src->b, sizeof(f64) * (src->v * src->n));
+ memcpy(dst->w, src->w, sizeof(f64) * (src->v + 1));
+ memcpy(dst->c, src->c, sizeof(f64) * src->v);
+ memcpy(dst->f, src->f, sizeof(f64 (*)(f64)) * src->v);
+ memcpy(dst->fe, src->fe, sizeof(f64 (*)(f64, f64)) * (src->v + src->v * src->n));
+ return dst;
+}
+
+void ufo_randomize(ufo_t *ufo, f64 min_w, f64 max_w) {
+ if (!ufo)
+ return;
+ f64 range = max_w - min_w;
+ for (u32 j = 0; j < ufo->v; j++) {
+ for (u32 k = 0; k < ufo->n + 1; k++)
+ ufo_ajk(ufo, j, k) = min_w + (f64)rand() / RAND_MAX * range;
+ for (u32 k = 0; k < ufo->n; k++)
+ ufo_bjk(ufo, j, k) = min_w + (f64)rand() / RAND_MAX * range;
+ ufo->w[j] = min_w + (f64)rand() / RAND_MAX * range;
+ ufo->c[j] = min_w + (f64)rand() / RAND_MAX * range;
+ ufo->f[j] = UNARY[(rand() % 16)];
+ u32 bi_idx = (rand() % 5);
+ ufo->fe[j] = BINARY[bi_idx];
+ for (u32 k = 0; k < ufo->n; k++)
+ ufo_fijk(ufo, j, k) = BINARY[(rand() % 5)];
+ }
+ ufo->w[ufo->v] = min_w + (f64)rand() / RAND_MAX * range;
+}
+
+void ufo_mutate(ufo_t *ufo, f64 rate, f64 min_w, f64 max_w) {
+ if (!ufo || rate <= 0.0)
+ return;
+ f64 range = max_w - min_w;
+ for (u32 j = 0; j < ufo->v; j++) {
+ for (u32 k = 0; k < ufo->n + 1; k++)
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo_ajk(ufo, j, k) = min_w + (f64)rand() / RAND_MAX * range;
+ for (u32 k = 0; k < ufo->n; k++)
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo_bjk(ufo, j, k) = min_w + (f64)rand() / RAND_MAX * range;
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo->w[j] = min_w + (f64)rand() / RAND_MAX * range;
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo->c[j] = min_w + (f64)rand() / RAND_MAX * range;
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo->f[j] = UNARY[(rand() % 16)];
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo->fe[j] = BINARY[(rand() % 5)];
+ for (u32 k = 0; k < ufo->n; k++)
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo_fijk(ufo, j, k) = BINARY[(rand() % 5)];
+ }
+ if ((f64)rand() / RAND_MAX < rate)
+ ufo->w[ufo->v] = min_w + (f64)rand() / RAND_MAX * range;
+}
+
+void ufo_crossover(ufo_t *parent1, ufo_t *parent2, u32 split) {
+ if (!parent1 || !parent2 || parent1->n != parent2->n || parent1->v != parent2->v)
+ return;
+ if (split > 0 && split < parent1->v) {
+ u32 n = parent1->n;
+ for (u32 j = split; j < parent1->v; j++) {
+ u32 offset = j * (n + 1);
+ f64 tmp_a;
+ for (u32 k = 0; k < n + 1; k++) {
+ tmp_a = parent1->a[offset + k];
+ parent1->a[offset + k] = parent2->a[offset + k];
+ parent2->a[offset + k] = tmp_a;
+ }
+ {
+ u32 bob = j * n;
+ f64 tmp_b;
+ for (u32 k = 0; k < n; k++) {
+ tmp_b = parent1->b[bob + k];
+ parent1->b[bob + k] = parent2->b[bob + k];
+ parent2->b[bob + k] = tmp_b;
+ }
+ }
+ {
+ f64 tmp = parent1->w[j];
+ parent1->w[j] = parent2->w[j];
+ parent2->w[j] = tmp;
+ tmp = parent1->c[j];
+ parent1->c[j] = parent2->c[j];
+ parent2->c[j] = tmp;
+ f64 (*tmp_fn)(f64) = parent1->f[j];
+ parent1->f[j] = parent2->f[j];
+ parent2->f[j] = tmp_fn;
+ f64 (*tmp_fn2)(f64, f64) = parent1->fe[j];
+ parent1->fe[j] = parent2->fe[j];
+ parent2->fe[j] = tmp_fn2;
+ for (u32 k = 0; k < n; k++) {
+ u32 fi_off = j * n + k;
+ f64 (*tmp_fi)(f64, f64) = parent1->fi[fi_off];
+ parent1->fi[fi_off] = parent2->fi[fi_off];
+ parent2->fi[fi_off] = tmp_fi;
+ }
+ }
+ }
+ }
+}
+
+u32 ufo_complexity(const ufo_t *ufo) {
+ if (!ufo)
+ return 0;
+ u32 score = 0;
+ for (u32 j = 0; j < ufo->v; j++) {
+ score++;
+ f64 c_val = ufo->c[j];
+ if (c_val != 1.0 && c_val != 0.0)
+ score++;
+ f64 test = ufo->f[j](2.0);
+ if (test != 2.0)
+ score++;
+ for (u32 k = 0; k < ufo->n; k++) {
+ f64 b_val = ufo_bjk(ufo, j, k);
+ if (b_val != 1.0 && b_val != 0.0)
+ score++;
+ }
+ }
+ return score;
+}
+
+void ufo_set_unary(ufo_t *ufo, u32 j, f64 (*fn)(f64)) {
+ if (!ufo || j >= ufo->v)
+ return;
+ ufo->f[j] = fn;
+}
+
+void ufo_set_external_binary(ufo_t *ufo, u32 j, f64 (*fn)(f64, f64)) {
+ if (!ufo || j >= ufo->v)
+ return;
+ ufo->fe[j] = fn;
+}
+
+void ufo_set_internal_binary(ufo_t *ufo, u32 j, u32 k, f64 (*fn)(f64, f64)) {
+ if (!ufo || j >= ufo->v || k >= ufo->n)
+ return;
+ ufo_fijk(ufo, j, k) = fn;
+}
+
+void ufo_evaluate_jacobian(ufo_t *ufo, f64 *x, f64 *jacobian) {
+ if (!ufo || !x || !jacobian)
+ return;
+ f64 y0 = ufo_evaluate(ufo, x);
+ for (u32 i = 0; i < ufo->n; i++)
+ jacobian[i] = 0;
+ f64 eps = 1e-6;
+ f64 *xp = malloc(sizeof(f64) * ufo->n);
+ if (!xp)
+ return;
+ for (u32 i = 0; i < ufo->n; i++) {
+ memcpy(xp, x, sizeof(f64) * ufo->n);
+ xp[i] += eps;
+ f64 yp = ufo_evaluate(ufo, xp);
+ jacobian[i] = (yp - y0) / eps;
+ }
+ free(xp);
+}
+
+void ufo_evaluate_batch(ufo_t *ufo, f64 *x, u32 count, f64 *out) {
+ if (!ufo || !x || !out || count == 0)
+ return;
+ for (u32 i = 0; i < count; i++)
+ out[i] = ufo_evaluate(ufo, x + (i64)i * (i64)ufo->n);
+}
+
+void ufo_reset(ufo_t *ufo) {
+ if (!ufo)
+ return;
+ f64 *tmp_fp = ufo->a;
+ while ((void *)tmp_fp != (void *)ufo->f)
+ *tmp_fp++ = 1.0;
+ f64 (**tmp_unary)(f64) = ufo->f;
+ while ((void *)tmp_unary < (void *)ufo->fe)
+ *tmp_unary++ = ufo_identity;
+ f64 (**tmp_binary)(f64, f64) = ufo->fe;
+ u32 total_bin = ufo->v + ufo->v * ufo->n;
+ for (u32 i = 0; i < total_bin; i++)
+ *tmp_binary++ = ufo_addition;
+ ufo->w[ufo->v] = 0;
+ for (u32 j = 0; j < ufo->v; j++)
+ ufo_ajk(ufo, j, ufo->n) = 0;
+}
+
+static int ufo_fn_index_unary(f64 (*fn)(f64)) {
+ for (int i = 0; i < 16; i++)
+ if (UNARY[i] == fn)
+ return i;
+ return -1;
+}
+
+static int ufo_fn_index_binary(f64 (*fn)(f64, f64)) {
+ for (int i = 0; i < 5; i++)
+ if (BINARY[i] == fn)
+ return i;
+ return -1;
+}
+
+void ufo_simplify(ufo_t *ufo) {
+ if (!ufo || ufo->v < 2)
+ return;
+ u32 write = 0;
+ for (u32 read = 0; read < ufo->v; read++) {
+ if (ufo->w[read] == 0.0 && ufo->c[read] == 0.0)
+ continue;
+ if (read != write) {
+ u32 n = ufo->n;
+ memcpy(ufo->a + write * (n + 1), ufo->a + read * (n + 1), sizeof(f64) * (n + 1));
+ memcpy(ufo->b + write * n, ufo->b + read * n, sizeof(f64) * n);
+ ufo->w[write] = ufo->w[read];
+ ufo->c[write] = ufo->c[read];
+ ufo->f[write] = ufo->f[read];
+ ufo->fe[write] = ufo->fe[read];
+ {
+ size_t sz = sizeof(void *) * (size_t)n;
+ memcpy((void *)(ufo->fi + write * n), (void *)(ufo->fi + read * n), sz);
+ }
+ }
+ write++;
+ }
+ for (u32 j = write; j < ufo->v; j++) {
+ ufo->w[j] = 0;
+ ufo->c[j] = 0;
+ ufo->f[j] = ufo_identity;
+ ufo->fe[j] = ufo_addition;
+ for (u32 k = 0; k < ufo->n + 1; k++)
+ ufo_ajk(ufo, j, k) = 0;
+ for (u32 k = 0; k < ufo->n; k++) {
+ ufo_bjk(ufo, j, k) = 0;
+ ufo_fijk(ufo, j, k) = ufo_addition;
+ }
+ }
+}
+
+f64 ufo_similar(const ufo_t *a, const ufo_t *b) {
+ if (!a || !b || a->n != b->n || a->v != b->v)
+ return 0;
+ u32 n = a->n;
+ u32 v = a->v;
+ f64 total = 0;
+ u32 count = 0;
+ for (u32 j = 0; j < v; j++) {
+ for (u32 k = 0; k < n + 1; k++) {
+ total += (a->a[j * (n + 1) + k] == b->a[j * (n + 1) + k]) ? 1 : 0;
+ count++;
+ }
+ for (u32 k = 0; k < n; k++) {
+ total += (a->b[j * n + k] == b->b[j * n + k]) ? 1 : 0;
+ count++;
+ }
+ total += (a->w[j] == b->w[j]) ? 1 : 0;
+ count++;
+ total += (a->c[j] == b->c[j]) ? 1 : 0;
+ count++;
+ total += (a->f[j] == b->f[j]) ? 1 : 0;
+ count++;
+ total += (a->fe[j] == b->fe[j]) ? 1 : 0;
+ count++;
+ for (u32 k = 0; k < n; k++) {
+ total += (a->fi[j * n + k] == b->fi[j * n + k]) ? 1 : 0;
+ count++;
+ }
+ }
+ total += (a->w[v] == b->w[v]) ? 1 : 0;
+ count++;
+ return count ? total / count : 1;
+}
+
+u32 ufo_serialize_size(u32 n, u32 v) {
+ return sizeof(u32) * 2
+ + sizeof(f64) * v * (n + 1)
+ + sizeof(f64) * v * n
+ + sizeof(f64) * (v + 1)
+ + sizeof(f64) * v
+ + sizeof(u8) * v
+ + sizeof(u8) * v
+ + sizeof(u8) * v * n;
+}
+
+void ufo_serialize(const ufo_t *ufo, u8 *buffer, u64 *size) {
+ if (!ufo || !buffer || !size)
+ return;
+ u32 n = ufo->n;
+ u32 v = ufo->v;
+ *size = ufo_serialize_size(n, v);
+ u8 *p = buffer;
+ memcpy(p, &n, sizeof(u32));
+ p += sizeof(u32);
+ memcpy(p, &v, sizeof(u32));
+ p += sizeof(u32);
+ memcpy(p, ufo->a, sizeof(f64) * v * (n + 1));
+ p += sizeof(f64) * v * (n + 1);
+ memcpy(p, ufo->b, sizeof(f64) * v * n);
+ p += sizeof(f64) * v * n;
+ memcpy(p, ufo->w, sizeof(f64) * (v + 1));
+ p += sizeof(f64) * (v + 1);
+ memcpy(p, ufo->c, sizeof(f64) * v);
+ p += sizeof(f64) * v;
+ for (u32 j = 0; j < v; j++) {
+ p[0] = (u8)ufo_fn_index_unary(ufo->f[j]);
+ p++;
+ }
+ for (u32 j = 0; j < v; j++) {
+ p[0] = (u8)ufo_fn_index_binary(ufo->fe[j]);
+ p++;
+ }
+ for (u32 j = 0; j < v; j++)
+ for (u32 k = 0; k < n; k++) {
+ p[0] = (u8)ufo_fn_index_binary(ufo_fijk(ufo, j, k));
+ p++;
+ }
+}
+
+int ufo_deserialize(ufo_t **out, const u8 *buffer, u64 size) {
+ if (!out || !buffer)
+ return 0;
+ u64 min = sizeof(u32) * 2 + 1;
+ if (size < min)
+ return 0;
+ u32 n, v;
+ memcpy(&n, buffer, sizeof(u32));
+ memcpy(&v, buffer + sizeof(u32), sizeof(u32));
+ u64 needed = ufo_serialize_size(n, v);
+ if (size < needed)
+ return 0;
+ ufo_t *ufo = ufo_init(n, v);
+ if (!ufo)
+ return 0;
+ *out = ufo;
+ const u8 *p = buffer + sizeof(u32) * 2;
+ memcpy(ufo->a, p, sizeof(f64) * v * (n + 1));
+ p += sizeof(f64) * v * (n + 1);
+ memcpy(ufo->b, p, sizeof(f64) * v * n);
+ p += sizeof(f64) * v * n;
+ memcpy(ufo->w, p, sizeof(f64) * (v + 1));
+ p += sizeof(f64) * (v + 1);
+ memcpy(ufo->c, p, sizeof(f64) * v);
+ p += sizeof(f64) * v;
+ for (u32 j = 0; j < v; j++) {
+ int idx = (int)p[0];
+ p++;
+ ufo->f[j] = (idx >= 0 && idx < 16) ? UNARY[(size_t)idx] : ufo_identity;
+ }
+ for (u32 j = 0; j < v; j++) {
+ int idx = (int)p[0];
+ p++;
+ ufo->fe[j] = (idx >= 0 && idx < 5) ? BINARY[(size_t)idx] : ufo_addition;
+ }
+ for (u32 j = 0; j < v; j++)
+ for (u32 k = 0; k < n; k++) {
+ int idx = (int)p[0];
+ p++;
+ ufo_fijk(ufo, j, k) = (idx >= 0 && idx < 5) ? BINARY[(size_t)idx] : ufo_addition;
+ }
+ return 1;
+}
+
+void ufo_gradient_weights(ufo_t *ufo, f64 *x, f64 *weights_grad) {
+ if (!ufo || !x || !weights_grad)
+ return;
+ f64 y0 = ufo_evaluate(ufo, x);
+ f64 eps = 1e-6;
+ u32 n = ufo->n;
+ u32 v = ufo->v;
+ for (u32 j = 0; j < v; j++)
+ for (u32 k = 0; k < n + 1; k++) {
+ f64 orig = ufo_ajk(ufo, j, k);
+ ufo_ajk(ufo, j, k) = orig + eps;
+ weights_grad[j * (n + 1) + k] = (ufo_evaluate(ufo, x) - y0) / eps;
+ ufo_ajk(ufo, j, k) = orig;
+ }
+ for (u32 j = 0; j < v; j++) {
+ f64 orig = ufo->w[j];
+ ufo->w[j] = orig + eps;
+ weights_grad[v * (n + 1) + j] = (ufo_evaluate(ufo, x) - y0) / eps;
+ ufo->w[j] = orig;
+ }
+ {
+ u32 off = v * (n + 1) + v;
+ f64 orig = ufo->w[v];
+ ufo->w[v] = orig + eps;
+ weights_grad[off] = (ufo_evaluate(ufo, x) - y0) / eps;
+ ufo->w[v] = orig;
+ }
+ for (u32 j = 0; j < v; j++) {
+ u32 off = v * (n + 1) + v + 1 + j;
+ f64 orig = ufo->c[j];
+ ufo->c[j] = orig + eps;
+ weights_grad[off] = (ufo_evaluate(ufo, x) - y0) / eps;
+ ufo->c[j] = orig;
+ }
+}
+
+static void ufo_unary_name(char *buf, u32 sz, f64 (*fn)(f64)) {
+ int idx = ufo_fn_index_unary(fn);
+ if (idx < 0) {
+ snprintf(buf, sz, "<unknown>");
+ return;
+ }
+ static const char *names[] = {"id", "inc", "dec", "recip", "root", "abs",
+ "floor", "ceil", "round", "exp", "log", "log10",
+ "log2", "sin", "cos", "tan"};
+ snprintf(buf, sz, "%s", names[idx]);
+}
+
+static void ufo_binary_name(char *buf, u32 sz, f64 (*fn)(f64, f64)) {
+ int idx = ufo_fn_index_binary(fn);
+ if (idx < 0) {
+ snprintf(buf, sz, "?");
+ return;
+ }
+ static const char *names[] = {"+", "-", "/", "*", "^"};
+ snprintf(buf, sz, "%s", names[idx]);
+}
+
+void ufo_to_formula_string(const ufo_t *ufo, char *buf, u32 buf_size) {
+ if (!ufo || !buf || buf_size < 2) {
+ if (buf && buf_size >= 1) buf[0] = '\0';
+ return;
+ }
+ u32 n = ufo->n;
+ u32 v = ufo->v;
+ u32 pos = 0;
+ buf[pos++] = '(';
+ for (u32 j = 0; j < v; j++) {
+ if (j > 0) {
+ char bn[8];
+ ufo_binary_name(bn, sizeof(bn), ufo->fe[j]);
+ pos += (u32)snprintf(buf + pos, buf_size - pos, " %s ", bn);
+ if (pos >= buf_size - 1) break;
+ }
+ char un[16];
+ ufo_unary_name(un, sizeof(un), ufo->f[j]);
+ pos += (u32)snprintf(buf + pos, buf_size - pos, "%s(", un);
+ if (pos >= buf_size - 1) break;
+ pos += (u32)snprintf(buf + pos, buf_size - pos, "%g", ufo_ajk(ufo, j, n));
+ if (pos >= buf_size - 1) break;
+ for (u32 k = 0; k < n; k++) {
+ char bn[8];
+ ufo_binary_name(bn, sizeof(bn), ufo_fijk(ufo, j, k));
+ pos += (u32)snprintf(buf + pos, buf_size - pos, " %s ", bn);
+ if (pos >= buf_size - 1) break;
+ pos += (u32)snprintf(buf + pos, buf_size - pos, "(%g*x_%u)^%g",
+ ufo_ajk(ufo, j, k), (unsigned)k, ufo_bjk(ufo, j, k));
+ if (pos >= buf_size - 1) break;
+ }
+ pos += (u32)snprintf(buf + pos, buf_size - pos, "))^%g", ufo->c[j]);
+ if (pos >= buf_size - 1) break;
+ }
+ if (pos < buf_size - 1)
+ pos += (u32)snprintf(buf + pos, buf_size - pos, ") + %g", ufo->w[v]);
+ buf[pos < buf_size ? pos : buf_size - 1] = '\0';
+}
+
+#endif // UFO_IMPLEMENTATION
diff --git a/include/uri.h b/include/uri.h
index a00400d..80df641 100755..100644
--- a/include/uri.h
+++ b/include/uri.h
@@ -14,12 +14,15 @@
//
// Any string interacting with a uri_t is copied upon both entry and
// exit, thus, all strings except those must be (de)allocated by the user.
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Limitations: uri_parse only accepts absolute URIs with a scheme. Relative
+// URIs (e.g., "/path/to/resource") will cause uri_parse to return NULL.
#ifndef URI_H
#define URI_H
-
typedef enum
{
BUILD,
@@ -33,296 +36,420 @@ typedef enum
URI_END
} uri_element_t;
+typedef struct uri_t
+{
+ char *data[URI_END];
+} uri_t;
-
-uri_t uri_init( void );
-void uri_set( uri_t *, uri_element_t, char const * );
-char * uri_get( uri_t * );
-uri_t * uri_parse( char const * );
-void uri_print( uri_t const * );
-void uri_build( uri_t * );
-
+uri_t *uri_init(void);
+void uri_free(uri_t *);
+void uri_set(uri_t *, uri_element_t, char const *);
+char *uri_get(uri_t *);
+uri_t *uri_parse(char const *);
+char *uri_remove(uri_t *, uri_element_t);
+void uri_print(uri_t const *);
+void uri_print_elements(uri_t const *);
+void uri_build(uri_t *);
#endif // URI_H
#ifdef URI_IMPLEMENTATION
-
-typedef struct uri_t
-{
- char *data[URI_END];
-} uri_t;
-
-
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include <stdint.h>
+static void uri_copy_string(char **ptr, char const *source);
-#define copy_string( ptr, source ) { \
- if( source ) \
- { \
- size_t n = strlen( source ); \
- ptr = malloc( n + 1 ); \
- ptr[n] = '\0'; \
- strcpy( ptr, source ); \
- } \
- else \
- { \
- ptr = NULL; \
- } \
+uri_t *uri_init(void)
+{
+ uri_t *u = calloc(1, sizeof(uri_t));
+ if (!u)
+ return NULL;
+ return u;
}
-
-void uri_set( uri_t *u, uri_element_t e, char const *data )
+void uri_free(uri_t *u)
{
- char *c;
- copy_string( c, data );
-
- if( u->data[e] )
- free( u->data[e] );
-
- u->data[e] = c;
+ if (u == NULL)
+ return;
+ for(int i = 0; i < URI_END; i++)
+ {
+ if(u->data[i])
+ free(u->data[i]);
+ }
+ free(u);
}
+static void uri_copy_string(char **ptr, char const *source)
+{
+ if (!ptr)
+ return;
+ if (*ptr)
+ free(*ptr);
+ if(source) {
+ size_t n = strlen(source);
+ if (n == SIZE_MAX)
+ return;
+ *ptr = malloc(n + 1);
+ if (!*ptr)
+ return;
+ memcpy(*ptr, source, n);
+ (*ptr)[n] = '\0';
+ }
+ else {
+ *ptr = NULL;
+ }
+}
-char * uri_get( uri_t *u )
+void uri_set(uri_t *u, uri_element_t e, char const *data)
{
- uri_build( u );
- char *c;
- copy_string( c, u->data[BUILD] );
+ if (u == NULL)
+ return;
+ if ((int)e < 0 || (int)e >= URI_END)
+ return;
+ uri_copy_string(&u->data[e], data);
+}
+char *uri_get(uri_t *u)
+{
+ if (u == NULL)
+ return NULL;
+ uri_build(u);
+ char *c = NULL;
+ uri_copy_string(&c, u->data[BUILD]);
return c;
}
-
-char * uri_remove( uri_t *u, uri_element_t e )
+char *uri_remove(uri_t *u, uri_element_t e)
{
+ if (u == NULL)
+ return NULL;
+ if ((int)e < 0 || (int)e >= URI_END)
+ return NULL;
char *data = u->data[e];
- uri_set( u, e, NULL );
-
+ u->data[e] = NULL;
return data;
}
-// TODO: Account for / when path is missing in query and fragment
-void uri_build( uri_t *u )
+void uri_build(uri_t *u)
{
- int n = 0;
+ if (u == NULL)
+ return;
+ size_t n = 0;
- // TODO: Convert to a for loop
- if( u->data[BUILD] )
- free( u->data[BUILD] );
+ if(u->data[BUILD])
+ free(u->data[BUILD]);
+ u->data[BUILD] = NULL;
// Length of built string
- if( u->data[SCHEME] )
- n += strlen( u->data[SCHEME] ) + 1; // ":"
-
- if( u->data[USERINFO] )
- n += strlen( u->data[USERINFO] ) + 1; // "@"
-
- if( u->data[HOST] )
- n += strlen( u->data[HOST] ) + 2; // "//"
-
- if( u->data[PORT] )
- n += strlen( u->data[PORT] ) + 1; // ":"
-
- if( u->data[PATH] )
- n += strlen( u->data[PATH] );
-
- if( u->data[QUERY] )
- n += strlen( u->data[QUERY] ) + 1; // ?
-
- if( u->data[FRAGMENT] )
- n += strlen( u->data[FRAGMENT] ) + 1; // #
+ {
+ size_t s;
+ if(u->data[SCHEME])
+ {
+ s = strlen(u->data[SCHEME]) + 1;
+ if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; }
+ n += s;
+ }
+ if(u->data[USERINFO])
+ {
+ s = strlen(u->data[USERINFO]) + 1;
+ if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; }
+ n += s;
+ }
+ if(u->data[HOST])
+ {
+ s = strlen(u->data[HOST]) + 2;
+ if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; }
+ n += s;
+ }
+ if(u->data[PORT])
+ {
+ s = strlen(u->data[PORT]) + 1;
+ if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; }
+ n += s;
+ }
+ if(u->data[PATH])
+ {
+ s = strlen(u->data[PATH]);
+ if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; }
+ n += s;
+ }
+ if(u->data[QUERY])
+ {
+ s = strlen(u->data[QUERY]) + 1;
+ if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; }
+ n += s;
+ }
+ if(u->data[FRAGMENT])
+ {
+ s = strlen(u->data[FRAGMENT]) + 1;
+ if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; }
+ n += s;
+ }
+ }
- char *build = ( char * ) malloc( n + 1 );
- build[n] = '\0';
+ if (n == SIZE_MAX)
+ return;
+ char *build = malloc(n + 1);
+ if (!build)
+ return;
+ build[0] = '\0';
+ size_t pos = 0;
// Copy strings to BUILD
- strcpy( build, u->data[SCHEME] );
- strcat( build, ":" );
+ if(u->data[SCHEME])
+ {
+ size_t len = strlen(u->data[SCHEME]);
+ memcpy(build + pos, u->data[SCHEME], len);
+ pos += len;
+ build[pos++] = ':';
+ }
- if( u->data[HOST] )
+ if(u->data[HOST])
{
- strcat( build, "//" );
+ memcpy(build + pos, "//", 2);
+ pos += 2;
- if( u->data[USERINFO] )
+ if(u->data[USERINFO])
{
- strcat( build, u->data[USERINFO] );
- strcat( build, "@" );
+ size_t len = strlen(u->data[USERINFO]);
+ memcpy(build + pos, u->data[USERINFO], len);
+ pos += len;
+ build[pos++] = '@';
}
- strcat( build, u->data[HOST] );
+ size_t len = strlen(u->data[HOST]);
+ memcpy(build + pos, u->data[HOST], len);
+ pos += len;
- if( u->data[PORT] )
+ if(u->data[PORT])
{
- strcat( build, ":" );
- strcat( build, u->data[PORT] );
+ build[pos++] = ':';
+ len = strlen(u->data[PORT]);
+ memcpy(build + pos, u->data[PORT], len);
+ pos += len;
}
}
- if( u->data[PATH] )
+ if(u->data[PATH])
{
- strcat( build, u->data[PATH] );
+ size_t len = strlen(u->data[PATH]);
+ memcpy(build + pos, u->data[PATH], len);
+ pos += len;
}
- if( u->data[QUERY] )
+ if(u->data[QUERY])
{
- strcat( build, "?" );
- strcat( build, u->data[QUERY] );
+ build[pos++] = '?';
+ size_t len = strlen(u->data[QUERY]);
+ memcpy(build + pos, u->data[QUERY], len);
+ pos += len;
}
- if( u->data[FRAGMENT] )
+ if(u->data[FRAGMENT])
{
- strcat( build, "#" );
- strcat( build, u->data[FRAGMENT] );
+ build[pos++] = '#';
+ size_t len = strlen(u->data[FRAGMENT]);
+ memcpy(build + pos, u->data[FRAGMENT], len);
+ pos += len;
}
+ build[pos] = '\0';
u->data[BUILD] = build;
}
-
-char * copy_substring( char const *from, char const *to )
+static int uri_set_range(uri_t *u, uri_element_t e, char const *from, char const *to)
{
- char *s = ( char * ) malloc( to - from + 1 );
- s[to - from] = 0;
- strncpy( s, from, to - from );
-
- return s;
-}
+ if (u == NULL || from == NULL || to == NULL)
+ return 0;
+ if ((int)e < 0 || (int)e >= URI_END)
+ return 0;
+ if (to <= from)
+ return 0;
+ size_t len = (size_t)(to - from);
+ if (len == SIZE_MAX)
+ return 0;
+ char *s = malloc(len + 1);
+ if (!s)
+ return 0;
+ s[len] = 0;
+ memcpy(s, from, len);
+ if(u->data[e])
+ free(u->data[e]);
+ u->data[e] = s;
+ return 1;
+}
-uri_t * uri_parse( char const * uri_s )
+uri_t *uri_parse(char const *uri_s)
{
- uri_t *uri = ( uri_t * ) calloc( 1, sizeof( uri_t ) );
+ if (uri_s == NULL)
+ return NULL;
+ uri_t *uri = calloc(1, sizeof(uri_t));
+ if (!uri)
+ return NULL;
char const *current = uri_s;
char const *working;
- char *token;
// SCHEME
- working = strchr( current, ( int ) ':' );
+ working = strchr(current, ':');
- if( !working )
+ if(!working)
+ {
+ uri_free(uri);
return NULL;
+ }
- token = copy_substring( current, working );
- uri_set( uri, SCHEME, token );
- current = ++working;
+ if (!uri_set_range(uri, SCHEME, current, working)) {
+ uri_free(uri);
+ return NULL;
+ }
+ current = ++working;
- // AUTHORITY
- if( ( *( working ) == '/' ) && ( *( working + 1 ) == '/' ) )
- {
- current = working += 2;
+ // AUTHORITY
+ if(working[0] == '/' && working[1] == '/')
+ {
+ current = working += 2;
- while( *working && *working != '@' && *working != ':' &&*working != '/' )
- working++;
+ char const *at = strchr(working, '@');
+ if (at) {
+ if (!uri_set_range(uri, USERINFO, current, at)) {
+ uri_free(uri);
+ return NULL;
+ }
+ current = at + 1;
- // USERINFO
- if( *working == '@' )
- {
- token = copy_substring( current, working );
- uri_set( uri, USERINFO, token );
+ char const *host_end = strpbrk(current, ":/");
+ if (!host_end)
+ host_end = current + strlen(current);
- current = ++working;
+ if (!uri_set_range(uri, HOST, current, host_end)) {
+ uri_free(uri);
+ return NULL;
+ }
+ working = host_end;
+ } else {
+ while(*working && *working != ':' && *working != '/')
+ working++;
- while( *working && *working != ':' && *working != '/' )
- working++;
- }
+ if (!uri_set_range(uri, HOST, current, working)) {
+ uri_free(uri);
+ return NULL;
+ }
+ }
- // HOST
- token = copy_substring( current, working );
- uri_set( uri, HOST, token );
+ if(!*working)
+ {
+ uri_build(uri);
+ return uri;
+ }
- if( !( *working ) )
- return uri;
+ // PORT
+ if(*working == ':')
+ {
+ current = ++working;
- // PORT
- if( *working == ':' )
- {
- current = ++working;
+ while(*working && *working != '/')
+ working++;
- while( *working && *working != '/' )
- working++;
+ if (!uri_set_range(uri, PORT, current, working)) {
+ uri_free(uri);
+ return NULL;
+ }
+ }
- token = copy_substring( current, working );
- uri_set( uri, PORT, token );
- }
+ if(!*working)
+ {
+ uri_build(uri);
+ return uri;
+ }
+ current = working;
- if( !( *working ) )
- return uri;
+ while(*working && *working != '?' && *working != '#')
+ working++;
+ }
+ else
+ {
+ // No authority section - scan for query/fragment delimiters
+ while(*working && *working != '?' && *working != '#')
+ working++;
+ }
- current = working;
+ // PATH
+ if (!uri_set_range(uri, PATH, current, working)) {
+ uri_free(uri);
+ return NULL;
+ }
- while( *working && *working != '?' && *working != '#' )
- working++;
- }
- else
- {
- while( *working && *working != '/' )
- working++;
- }
+ if(!*working)
+ {
+ uri_build(uri);
+ return uri;
+ }
- // PATH
- token = copy_substring( current, working );
- uri_set( uri, PATH, token );
+ current = working;
- if( !( *working ) )
- return uri;
+ // QUERY
+ if(*working == '?')
+ {
+ char const *query_start = ++working;
- current = working;
+ while(*working && *working != '#')
+ working++;
- // QUERY
- if( *working == '?' )
- {
- while( *working && *working != '#' )
- working++;
+ if (!uri_set_range(uri, QUERY, query_start, working)) {
+ uri_free(uri);
+ return NULL;
+ }
- token = copy_substring( current, working );
- uri_set( uri, QUERY, token );
+ current = working;
+ }
- current = working;
- }
+ // FRAGMENT
+ if(*working == '#')
+ {
+ char const *frag_start = ++working;
- // FRAGMENT
- if( *working == '#' )
- {
- while( *working )
- working++;
+ while(*working)
+ working++;
- token = copy_substring( current, working );
- uri_set( uri, FRAGMENT, token );
- }
+ if (!uri_set_range(uri, FRAGMENT, frag_start, working)) {
+ uri_free(uri);
+ return NULL;
+ }
+ }
- uri_build( uri );
+ uri_build(uri);
- return uri;
+ return uri;
}
-
-void uri_print( uri_t const *u )
+void uri_print(uri_t const *u)
{
- if( u->data[BUILD] )
- fputs( u->data[BUILD], stdout );
+ if (u == NULL)
+ return;
+ if(u->data[BUILD])
+ fputs(u->data[BUILD], stdout);
}
-
-void uri_print_elements( uri_t const *u )
+void uri_print_elements(uri_t const *u)
{
- for( int i = 0; i < URI_END; i++ )
+ if (u == NULL)
+ return;
+ for(int i = 0; i < URI_END; i++)
{
+ printf("%d", i);
- printf( "%d", i );
-
- if( u->data[i] )
+ if(u->data[i])
{
- printf( " - %s", u->data[i] );
+ printf(" - %s", u->data[i]);
}
putchar('\n');
-
}
}
-
#endif // URI_IMPLEMENTATION
diff --git a/working/buffer.h b/working/buffer.h
deleted file mode 100644
index 629821f..0000000
--- a/working/buffer.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef BUFFER_H
-#define BUFFER_H
-
-#include "./type.h"
-
-void *buffer_malloc(uint_t num, uint_t size);
-void buffer_free(void *);
-void buffer_insert(void *);
-void buffer_remove(void *);
-
-#endif
-
-#ifdef BUFFER_IMPLEMENTATION
-
-#define _buffer_from_meta(x) ((void *)((u8 *)x) + sizeof(buffer_meta_t))
-#define _buffer_to_meta(x) ((void *)((u8 *)x) + sizeof(buffer_meta_t))
-
-struct {
- uint_t i; // Array size
- uint_t n; // Array capacity
- uint_t s; // Element size
-} buffer_meta_t;
-
-void *buffer_malloc(uint_t n, uint_t s) {
- void *x = malloc((n * s) + sizeof(uint_t) * 3);
- buffer_meta_t *meta = (buffer_meta_t *)x;
-
- meta->i = 0;
- meta->n = n;
- meta->s = s;
-
- void *buffer = _buffer_from_meta(x);
-
- return buffer;
-}
-
-void buffer_free(void *buffer) { free(_buffer_to_meta(buffer)); }
-
-uint_t buffer_size(void *buffer) {
- buffer_meta_t *meta = _buffer_to_meta(buffer);
- return meta->i;
-}
-
-uint_t buffer_capacity(void *buffer) {
- buffer_meta_t *meta = _buffer_to_meta(buffer);
- return meta->n;
-}
-
-void *buffer_resize(void *buffer) {
- buffer_meta_t *meta = _buffer_to_meta(buffer);
-
- if (meta->n > 32) {
- fp_t ratio = (fp_t)meta->i / (fp_t)meta->n;
-
- if (ratio > 0.8) {
- meta->n *= 2;
- meta = realloc(meta, sizeof(buffer_meta_t) + meta->n * meta->s)
- } else if (ratio < 0.2) {
- meta->n /= 2;
- meta = realloc(meta, sizeof(buffer_meta_t) + meta->n * meta->s)
- }
- }
-
- return meta;
-}
-
-#endif \ No newline at end of file
diff --git a/working/ufo.c b/working/ufo.c
deleted file mode 100755
index b404f80..0000000
--- a/working/ufo.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#define UFO_IMPLEMENTATION
-#include "ufo.h"
-
-
-#include <stdio.h>
-
-
-int main() {
- ufo_t *ufo = ufo_init( 1, 100 );
- printf( "%f\n", ufo_evaluate( ufo, (fp_t[]){ 1.0 } ) );
- free( ufo );
-
- return 0;
-}
-
diff --git a/working/ufo.h b/working/ufo.h
deleted file mode 100755
index 2ae33ae..0000000
--- a/working/ufo.h
+++ /dev/null
@@ -1,217 +0,0 @@
-// https://www.sciencedirect.com/science/article/abs/pii/S1568494620303574
-
-// ufo.h - Universal Function Originator
-
-// f_j(X) = f_j(a_0j (⊙_1j) a_1j*x_1^b_1j (⊙_2j) a_2j*x_2^b_2j (⊙_3j) ... (⊙_nj)
-// a_nj*x_2^b_nj) g_j(X) = w_j * [f_j(X)]^(c_j) y = w_0 + g_1(X) ⊚_1 g_1(X) ⊚_2
-// ... ⊚_v g_v(X)
-
-#ifndef UFO_H
-
-#include <stdint.h>
-#include <stdlib.h>
-
-typedef double fp_t;
-
-struct ufo_t;
-
-#endif // UFO_H
-
-#ifdef UFO_IMPLEMENTATION
-
-#include <assert.h>
-#include <stdio.h>
-#include <tgmath.h>
-
-typedef struct ufo_t {
- // Input count
- uint32_t n;
- // The number of internal blocks
- uint32_t v;
- // Internal weights [v][n+1]
- fp_t *a;
- // Internal exponents [v][n]
- fp_t *b;
- // External weights [v+1]
- fp_t *w;
- // External exponents [v]
- fp_t *c;
- // Unary functions [v]
- fp_t (**f)(fp_t);
- // External binary functions [v]
- fp_t (**fe)(fp_t, fp_t);
- // Internal binary functions [v][n]
- fp_t (**fi)(fp_t, fp_t);
-} ufo_t;
-
-#define ufo_ajk(ufo, j, k) (*(ufo->a + j * (ufo->n + 1) + k))
-#define ufo_bjk(ufo, j, k) (*(ufo->b + j * (ufo->n) + k))
-#define ufo_fijk(ufo, j, k) (*(ufo->fi + j * (ufo->n) + k))
-
-ufo_t *ufo_init(uint32_t n, uint32_t v) {
- assert(n > 0);
- assert(v > 0);
-
- fp_t N = 3 * v * n + 5 * v;
- fp_t D = 2 * v * n + 3 * v;
- uint32_t bytes =
- sizeof(ufo_t) + sizeof(fp_t) * N + sizeof(void (*)(void)) * D;
-
- ufo_t *ufo = malloc(bytes);
- ufo->n = n;
- ufo->v = v;
-
- ufo->a = (void *)((uint8_t *)ufo + sizeof(ufo_t));
- ufo->b = ufo->a + v * (n + 1);
- ufo->w = ufo->b + v * n;
- ufo->c = ufo->w + v + 1;
- ufo->f = (fp_t(**)(fp_t))(ufo->c + v);
- ufo->fe = (fp_t(**)(fp_t, fp_t))(ufo->f + v);
- ufo->fi = (fp_t(**)(fp_t, fp_t))(ufo->fe + v * n);
-
- // Initialize all weights to one
- fp_t *tmp_fp = ufo->a;
- while ((void *)tmp_fp != (void *)ufo->f)
- *tmp_fp++ = 1.0;
-
- // Initialize all unary functions to identity
- fp_t (**tmp_unary)(fp_t) = ufo->f;
- while ((void *)tmp_unary < (void *)ufo->fe)
- *tmp_unary++ = ufo_identity;
-
- // Initialize all binary functions to addition
- fp_t (**tmp_binary)(fp_t, fp_t) = ufo->fe;
- while ((void *)tmp_binary != (void *)ufo + bytes)
- *tmp_binary++ = ufo_addition;
-
- // Set bias to zero
- ufo->w[v] = 0;
-
- for (uint32_t j = 0; j < v; j++)
- ufo_ajk(ufo, j, ufo->n) = 0;
-
- return ufo;
-}
-
-fp_t ufo_ujk(ufo_t *ufo, fp_t *x, uint32_t j, uint32_t k) {
- if (k >= ufo->n)
- return ufo_ajk(ufo, j, k);
-
- return ufo_fijk(ufo, j, k)(ufo_ujk(ufo, x, j, k + 1), x[k]);
-}
-
-fp_t ufo_gj(ufo_t *ufo, fp_t *x, uint32_t j) {
- return ufo->w[j] * pow(ufo->f[j](ufo_ujk(ufo, x, j, 0)), ufo->c[j]);
-}
-
-fp_t ufo_evaluate(ufo_t *ufo, fp_t *x) {
- fp_t y = 0;
-
- // Calculate g_j(X) for every j (excluding constant term)
- uint32_t j;
- for (j = 0; j < ufo->v; j++)
- y = ufo->fe[j](y, ufo_gj(ufo, x, j));
-
- // Constant term
- y = ufo->fe[j](y, ufo->w[j]);
-
- return y;
-}
-
-void ufo_print(ufo_t *ufo) {
- printf("UFO: %p\n"
- "\n"
- "N: %u\n"
- "V: %u\n"
- "A: %p\n"
- "B: %p\n"
- "W: %p\n"
- "C: %p\n"
- "F: %p\n"
- "FE: %p\n"
- "FI: %p\n",
- (void *)ufo, ufo->n, ufo->v, ufo->a, ufo->b, ufo->w, ufo->c,
- (void *)ufo->f, (void *)ufo->fe, (void *)ufo->fi);
-}
-
-fp_t ufo_identity(fp_t x) { return x; }
-fp_t ufo_increment(fp_t x) { return x + 1; }
-fp_t ufo_increment(fp_t x) { return x - 1; }
-fp_t ufo_reciporical(fp_t x) { return 1 / x; }
-fp_t ufo_root(fp_t x) { return sqrt(x); }
-fp_t ufo_absolute_value(fp_t x) { return fabs(x); }
-fp_t ufo_floor(fp_t x) { return floor(x); }
-fp_t ufo_ceiling(fp_t x) { return ceiling(x); }
-fp_t ufo_round(fp_t x) { return round(x); }
-fp_t ufo_exponent(fp_t x) { return exp(x); }
-fp_t ufo_log_natural(fp_t x) { return log(x); }
-fp_t ufo_log_decimal(fp_t x) { return log10(x); }
-fp_t ufo_log_binary(fp_t x) { return log2(x); }
-fp_t ufo_sine(fp_t x) { return sin(x); }
-fp_t ufo_cosine(fp_t x) { return cos(x); }
-fp_t ufo_tangent(fp_t x) { return tan(x); }
-
-fp_t (*UNARY[])(fp_t) = {
- ufo_identity, ufo_increment, ufo_decrement, ufo_reciporical,
- ufo_root, ufo_absolute_value, ufo_floor, ufo_ceiling,
- ufo_round, ufo_exponent, ufo_log_natural, ufo_log_decimal,
- ufo_log_binary, ufo_sine, ufo_cosine,
- ufo_tangent,
- // ufo_cosecant,
- // ufo_secant,
- // ufo_cotangent,
- // ufo_sine_hyperbolic,
- // ufo_cosine_hyperbolic,
- // ufo_tangent_hyperbolic,
- // ufo_cosecant_hyperbolic,
- // ufo_secant_hyperbolic,
- // ufo_cotangent_hyperbolic,
- // ufo_sine_inverse,
- // ufo_cosine_inverse,
- // ufo_tangent_inverse,
- // ufo_cosecant_inverse,
- // ufo_sectant_inverse,
- // ufo_cotangent_inverse,
- // ufo_sine_hyperbolic_inverse,
- // ufo_cosine_hyperbolic_inverse,
- // ufo_tangent_hyperbolic_inverse,
- // ufo_cosecant_hyperbolic_inverse,
- // ufo_secant_hyperbolic_inverse,
- // ufo_cotangent_hyperbolic_inverse,
- // ufo_sinc,
- // ufo_versine,
- // ufo_vercosine,
- // ufo_coversine,
- // ufo_covercosine,
- // ufo_haversine,
- // ufo_havercosine,
- // ufo_hacoversine,
- // ufo_hacovercosine,
- // ufo_versine_inverse,
- // ufo_vercosine_inverse,
- // ufo_coversine_inverse,
- // ufo_covercosine_inverse,
- // ufo_haversine_inverse,
- // ufo_havercosine_inverse,
- // ufo_hacoversine_inverse,
- // ufo_hacovercosine_inverse,
- // ufo_exsecant,
- // ufo_excosecant,
- // ufo_negation,
- // ufo_heaviside,
- // ufo_logistic,
- // ufo_saw,
- // ufo_square,
- // ufo_triangle
-};
-
-fp_t ufo_addition(fp_t x0, fp_t x1) { return (x0 + x1); }
-fp_t ufo_subtraction(fp_t x0, fp_t x1) { return (x0 + x1); }
-fp_t ufo_division(fp_t x0, fp_t x1) { return (x0 / x1); }
-fp_t ufo_multiplication(fp_t x0, fp_t x1) { return (x0 * x1); }
-fp_t ufo_power(fp_t x0, fp_t x1) { return pow(x0, x1); }
-
-fp_t (*BINARY[])(fp_t, fp_t) = {ufo_addition, ufo_subtraction, ufo_division,
- ufo_multiplication ufo_power};
-
-#endif // UFO_IMPLEMENTATION \ No newline at end of file