#ifndef CIFAR10_LOADER_H
#define CIFAR10_LOADER_H
// Minimal loader for the CIFAR-10 "binary version" (cifar-10-binary.tar.gz):
// each record is 1 label byte + 3072 pixel bytes (1024 R, 1024 G, 1024 B; each
// plane row-major 32x32). Include after utensil.h (cifar10_argmax takes a
// ut_tensor*). Normalises with the standard CIFAR-10 per-channel mean/std.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define CIFAR10_PIX (3 * 32 * 32)
#define CIFAR10_REC (1 + CIFAR10_PIX)
typedef struct {
int n;
float* imgs; // [n,3,32,32] NCHW, normalised
uint8_t* labels; // [n]
} cifar10_t;
static const float CIFAR10_MEAN[3] = {0.4914f, 0.4822f, 0.4465f};
static const float CIFAR10_STD[3] = {0.2470f, 0.2435f, 0.2616f};
// paths: nfiles binary batch files (data_batch_1..5.bin, or test_batch.bin),
// each holding exactly 10000 records
static cifar10_t cifar10_load(const char** paths, int nfiles) {
cifar10_t d = {.n = nfiles * 10000};
d.imgs = malloc((size_t)d.n * CIFAR10_PIX * sizeof(float));
d.labels = malloc((size_t)d.n);
uint8_t rec[CIFAR10_REC];
int idx = 0;
for (int f = 0; f < nfiles; f++) {
FILE* fp = fopen(paths[f], "rb");
for (int i = 0; i < 10000; i++) {
fread(rec, 1, CIFAR10_REC, fp);
d.labels[idx] = rec[0];
for (int p = 0; p < CIFAR10_PIX; p++) {
int ch = p / (32 * 32);
d.imgs[idx * CIFAR10_PIX + p] = (rec[1 + p] / 255.f - CIFAR10_MEAN[ch]) / CIFAR10_STD[ch];
}
idx++;
}
fclose(fp);
}
return d;
}
static void cifar10_free(cifar10_t* d) {
free(d->imgs);
free(d->labels);
}
static void cifar10_shuffle(int* a, int n) {
for (int i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
static int cifar10_argmax(ut_tensor* t, int row) {
int C = t->shape.shape[1], best = 0;
float* r = t->data + row * C;
for (int j = 1; j < C; j++)
if (r[j] > r[best]) best = j;
return best;
}
#endif