/* $Id: t_bdRandomOctets.c $ */

/*
 * Copyright (C) 2001-26 David Ireland, D.I. Management Services Pty Limited
 * <https://di-mgt.com.au/contact/> <https://di-mgt.com.au/bigdigits.html>
 * SPDX-License-Identifier: MPL-2.0
 *
 * Last updated:
 * $Date: 2026-03-29 05:11:00 $
 * $Revision: 2.7.0 $
 * $Author: dai $
 */

/* Examples using the `bigdRand` functions:
bdRandomOctets()
bdRandomSeeded()
bdRandomNumber()
*/

#include <stdio.h>
#include <assert.h>
#include <math.h>    // For sqrt in statistics
#include "bigd.h"
#include "bigdRand.h"

#define TEST_LEN 24
#define TEST_N 32
#define NTESTS (TEST_N * 10)

static void pr_byteshex(const char *prefix, const unsigned char *bytes, size_t nbytes, const char *suffix)
{
    size_t i;
    if (prefix) printf("%s", prefix);
    for (i = 0; i < nbytes; i++)
    {
        printf("%02X", bytes[i]);
    }
    if (suffix) printf("%s", suffix);
}

int main(void)
{
    unsigned char bytes[TEST_LEN];
    size_t nbytes;
    int ntests, itest, i;
    BIGD a, p, N;
    int score[TEST_N] = { 0 };
    int t;
    BIGD temp;
    int themin, themax;
    double mean, var, sd;
    
    ntests = TEST_LEN;
    printf("Testing bdRandomOctets...\n");
    for (itest = 0; itest < ntests; itest++)
    {
        //printf("Test %d...\n", itest+1);
        nbytes = TEST_LEN - itest;
        bdRandomOctets(bytes, nbytes, NULL, 0);
        pr_byteshex("", bytes, nbytes, "\n");
    }

    // Now test bdRandomSeeded calling internal fn bdRandomOctets
    printf("Testing bdRandomSeeded with bdRandomOctets()...\n");
    a = bdNew();
    for (i = 0; i < 10; i++)
    {
        bdRandomSeeded(a, 128, NULL, 0, bdRandomOctets);
        bdPrintHex("r=", a, "\n");
    }

    // And generate some primes
    printf("Generating primes with bdGeneratePrime() calling bdRandomOctets()...\n");
    p = bdNew();
    for (i = 0; i < 4; i++)
    {
        bdGeneratePrime(p, 256, 128, NULL, 0, bdRandomOctets);
        bdPrintHex("p=", p, "\n");
    }

    // Generate some random numbers in the range [0,N-1] where N = 32
    // (and keep score of count for each value)
    temp = bdNew();
    N = bdNew();
    bdSetShort(N, TEST_N);
    bdPrintDecimal("Random numbers in the range [0,N-1] computed by bdRandomNumber(N), N=", N, ":\n");
    for (i = 0; i < NTESTS; i++)
    {
        bdRandomNumber(a, N);
        bdPrintDecimal("", a, " ");
        /* Convert BIGD into an int */
        t = (int)bdShortMod(temp, a, 0xFFFFFFFF);
        assert(0 <= t && t < TEST_N);
        score[t] += 1;
    }
    printf("\n");
    printf("[Statistics] Distribution over %d tests (EV=%.1f):\n", NTESTS, (double)NTESTS / TEST_N);
    mean = var = 0.0;
    themin = 99999;
    themax = 0;
    for (i = 0; i < TEST_N; i++)
    {
        int v = score[i];
        printf("%d ", v);
        if (v > themax) themax = v;
        if (v < themin) themin = v;
        mean += (double)v;
        var += v * v;
    }
    printf("\n");
    mean /= TEST_N;
    var = var / TEST_N - mean*mean;
    sd = sqrt(var);
    printf("mean=%.3f sd=%.3f min=%d (%.2f sd) max=%d (%+.2f sd)\n", 
        mean, sd, themin, (themin-mean)/sd, themax, (themax-mean)/sd);

    return 0;
}