sparsemap/test/qc.c
2024-07-22 09:33:44 -04:00

831 lines
18 KiB
C

/********************************************************************
* Copyright (c) 2014, Andrea Zito
* All rights reserved.
*
* License: BSD3
********************************************************************/
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "qc.h"
// TODO: fix these...
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-function-declaration"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wint-conversion"
#pragma GCC diagnostic ignored "-Wpedantic"
typedef void (*QCC_genRaw)(void *ptr);
typedef void (*QCC_genRawR)(void *ptr, void *from, void *to);
struct QCC_Stamp {
char *label;
int n;
struct QCC_Stamp *next;
};
typedef struct QCC_Result {
QCC_TestStatus status;
QCC_Stamp *stamps;
QCC_GenValue **arguments;
int argumentsN;
} QCC_Result;
enum QCC_deref_type { NONE, LONG, INT, FLOAT, DOUBLE, BYTE, CHAR };
void
QCC_init(int seed)
{
if (seed) {
srandom(seed);
} else {
srandom(time(NULL));
}
}
/***********************************************************************
* Generators helper functions
***********************************************************************/
static char *
QCC_showSimpleValue(void *value, enum QCC_deref_type dt, int maxsize, const char *format)
{
char *vc = malloc(sizeof(char) * (maxsize + 1));
switch (dt) {
case LONG:
snprintf(vc, maxsize + 1, format, *(long *)value);
break;
case INT:
snprintf(vc, maxsize + 1, format, *(int *)value);
break;
case FLOAT:
snprintf(vc, maxsize + 1, format, *(float *)value);
break;
case DOUBLE:
snprintf(vc, maxsize + 1, format, *(double *)value);
break;
case CHAR:
snprintf(vc, maxsize + 1, format, *(char *)value);
break;
case BYTE:
snprintf(vc, maxsize + 1, format, *(uint8_t *)value);
break;
default:
snprintf(vc, maxsize + 1, format, *(int *)value);
break;
}
vc[maxsize] = 0;
return vc;
}
static void
QCC_freeSimpleValue(void *value)
{
free(value);
}
QCC_GenValue *
QCC_initGenValue(void *value, int n, QCC_showValue show, QCC_freeValue free)
{
QCC_GenValue *gv = malloc(sizeof(QCC_GenValue));
*gv = (QCC_GenValue) { .value = value, .n = n, .show = show, .free = free };
return gv;
}
/***********************************************************************
* Generators implementations
***********************************************************************/
static char *
QCC_showLong(void *value, int len)
{
return QCC_showSimpleValue(value, LONG, 21, "%ld");
}
void
QCC_genLongAtR(long *l, long *from, long *to)
{
long _from = *from;
long _to = *to;
unsigned long n = _to - _from;
if (n > RAND_MAX) {
_from = _from + n / 2 - RAND_MAX / 2;
_to = _from + n / 2 + RAND_MAX / 2;
n = _to - _from;
}
*l = (random() % n) + _from;
}
void
QCC_genLongAt(long *l)
{
long from = QCC_LONG_FROM;
long to = QCC_LONG_TO;
QCC_genLongAtR(l, &from, &to);
}
QCC_GenValue *
QCC_genLongR(long from, long to)
{
long *v = malloc(sizeof(long));
QCC_genLongAtR(v, &from, &to);
return QCC_initGenValue(v, 1, QCC_showLong, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genLong()
{
return QCC_genLongR(QCC_LONG_FROM, QCC_LONG_TO);
}
static char *
QCC_showInt(void *value, int len)
{
return QCC_showSimpleValue(value, INT, 11, "%d");
}
void
QCC_genIntAtR(int *i, int *from, int *to)
{
int n = *to - *from;
*i = (random() % n) + *from;
}
void
QCC_genIntAt(int *i)
{
int from = QCC_INT_FROM;
int to = QCC_INT_TO;
QCC_genIntAtR(i, &from, &to);
}
QCC_GenValue *
QCC_genIntR(int from, int to)
{
int *v = malloc(sizeof(int));
QCC_genIntAtR(v, &from, &to);
return QCC_initGenValue(v, 1, QCC_showInt, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genInt()
{
return QCC_genIntR(QCC_INT_FROM, QCC_INT_TO);
}
static char *
QCC_showDouble(void *value, int len)
{
return QCC_showSimpleValue(value, DOUBLE, 50, "%.12e");
}
void
QCC_genDoubleAtR(double *d, double *from, double *to)
{
double r = (double)random() / (double)RAND_MAX;
*d = *from + (*to - *from) * r;
}
void
QCC_genDoubleAt(double *d)
{
double from = QCC_DOUBLE_FROM;
double to = QCC_DOUBLE_TO;
QCC_genDoubleAtR(d, &from, &to);
}
QCC_GenValue *
QCC_genDoubleR(double from, double to)
{
double *v = malloc(sizeof(double));
QCC_genDoubleAtR(v, &from, &to);
return QCC_initGenValue(v, 1, QCC_showDouble, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genDouble()
{
return QCC_genDoubleR(QCC_DOUBLE_FROM, QCC_DOUBLE_TO);
}
static char *
QCC_showFloat(void *value, int len)
{
return QCC_showSimpleValue(value, FLOAT, 50, "%.12e");
}
void
QCC_genFloatAtR(float *f, float *from, float *to)
{
float r = (float)random() / (float)RAND_MAX;
*f = *from + (*to - *from) * r;
}
void
QCC_genFloatAt(float *f)
{
float from = QCC_FLOAT_FROM;
float to = QCC_FLOAT_TO;
QCC_genFloatAtR(f, &from, &to);
}
QCC_GenValue *
QCC_genFloatR(float from, float to)
{
float *v = malloc(sizeof(float));
QCC_genFloatAtR(v, &from, &to);
return QCC_initGenValue(v, 1, QCC_showFloat, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genFloat()
{
return QCC_genFloatR(QCC_FLOAT_FROM, QCC_FLOAT_TO);
}
static char *
QCC_showBoolean(void *value, int len)
{
QCC_Boolean *b = (QCC_Boolean *)value;
if (*b) {
return strdup("TRUE");
} else {
return strdup("FALSE");
}
}
void
QCC_genBooleanAt(QCC_Boolean *b)
{
double r = (double)random() / (double)RAND_MAX;
*b = r > 0.5 ? QCC_TRUE : QCC_FALSE;
}
QCC_GenValue *
QCC_genBoolean()
{
QCC_Boolean *v = malloc(sizeof(QCC_Boolean));
QCC_genBooleanAt(v);
return QCC_initGenValue(v, 1, QCC_showBoolean, QCC_freeSimpleValue);
}
static char *
QCC_showChar(void *value, int len)
{
return QCC_showSimpleValue(value, CHAR, 3, "'%c'");
}
void
QCC_genCharAt(char *c)
{
*c = (char)(random() % 93) + 33;
}
QCC_GenValue *
QCC_genChar()
{
char *v = malloc(sizeof(char));
QCC_genCharAt(v);
return QCC_initGenValue(v, 1, QCC_showChar, QCC_freeSimpleValue);
}
/***********************************************************************
* Array generators implementations
***********************************************************************/
QCC_GenValue *
QCC_genArrayOf(int len, QCC_genRaw elemGen, size_t elemSize, QCC_showValue show, QCC_freeValue free)
{
int n = random() % len;
uint8_t *arr = malloc(n * elemSize);
int p, i;
for (i = 0, p = 0; i < n; i++, p += elemSize)
elemGen(arr + p);
return QCC_initGenValue(arr, n, show, free);
}
QCC_GenValue *
QCC_genArrayOfR(int len, QCC_genRawR elemGen, void *from, void *to, size_t elemSize, QCC_showValue show, QCC_freeValue free)
{
int n = random() % len;
uint8_t *arr = malloc(n * elemSize);
int p, i;
for (i = 0, p = 0; i < n; i++, p += elemSize)
elemGen(arr + p, from, to);
return QCC_initGenValue(arr, n, show, free);
}
static char *
QCC_showString(void *value, int len)
{
return QCC_showSimpleValue(value, NONE, len, "%s");
}
QCC_GenValue *
QCC_genStringL(int len)
{
QCC_GenValue *s = QCC_genArrayOf(len, (QCC_genRaw)QCC_genCharAt, sizeof(char), QCC_showString, QCC_freeSimpleValue);
((char *)s->value)[s->n - 1] = '\0';
return s;
}
QCC_GenValue *
QCC_genString()
{
return QCC_genStringL(50);
}
static char *
QCC_showSimpleArray(void *value, size_t elemSize, QCC_showValue showValue, int len)
{
char **valStr = malloc(sizeof(char *) * len);
int valStrLen = 0;
int i;
for (i = 0; i < len; i++) {
valStr[i] = showValue(((uint8_t *)value) + (i * elemSize), 1);
valStrLen += strlen(valStr[i]);
}
int totStrLen = valStrLen + 2 + 2 * len + 1;
char *str = malloc(sizeof(char) * totStrLen);
sprintf(str, "[");
int currLen = 1;
for (i = 0; i < len; i++) {
if (i == 0) {
sprintf(str + currLen, "%s", valStr[i]);
} else {
sprintf(str + currLen, ", %s", valStr[i]);
currLen += 2;
}
currLen += strlen(valStr[i]);
free(valStr[i]);
}
sprintf(str + currLen, "]");
free(valStr);
return str;
}
static char *
QCC_showByte(void *value, int len)
{
return QCC_showSimpleValue(value, BYTE, 3, "'%x'");
}
static char *
QCC_showArrayByte(void *value, int n)
{
return QCC_showSimpleArray(value, sizeof(long), QCC_showByte, n);
}
void
QCC_genByteAtR(uint8_t *b, uint8_t *from, uint8_t *to)
{
uint8_t r = (uint8_t)random() / (uint8_t)RAND_MAX;
*b = *from + (*to - *from) * r;
}
void
QCC_genByteAt(uint8_t *b)
{
*b = ((uint8_t)random() / (uint8_t)RAND_MAX) % UINT8_MAX;
}
QCC_GenValue *
QCC_genArrayByteLR(int len, long from, long to)
{
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genByteAtR, &from, &to, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayByteL(int len)
{
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genByteAt, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayByte()
{
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genByteAt, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
}
static char *
QCC_showArrayLong(void *value, int n)
{
return QCC_showSimpleArray(value, sizeof(long), QCC_showLong, n);
}
QCC_GenValue *
QCC_genArrayLongLR(int len, long from, long to)
{
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genLongAtR, &from, &to, sizeof(long), QCC_showArrayLong, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayLongL(int len)
{
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genLongAt, sizeof(long), QCC_showArrayLong, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayLong()
{
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genLongAt, sizeof(long), QCC_showArrayLong, QCC_freeSimpleValue);
}
static char *
QCC_showArrayInt(void *value, int n)
{
return QCC_showSimpleArray(value, sizeof(int), QCC_showInt, n);
}
QCC_GenValue *
QCC_genArrayIntLR(int len, int from, int to)
{
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genIntAtR, &from, &to, sizeof(int), QCC_showArrayInt, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayIntL(int len)
{
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genIntAt, sizeof(int), QCC_showArrayInt, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayInt()
{
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genIntAt, sizeof(int), QCC_showArrayInt, QCC_freeSimpleValue);
}
static char *
QCC_showArrayDouble(void *value, int n)
{
return QCC_showSimpleArray(value, sizeof(double), QCC_showDouble, n);
}
QCC_GenValue *
QCC_genArrayDoubleLR(int len, double from, double to)
{
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genDoubleAtR, &from, &to, sizeof(double), QCC_showArrayDouble, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayDoubleL(int len)
{
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genDoubleAt, sizeof(double), QCC_showArrayDouble, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayDouble()
{
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genDoubleAt, sizeof(double), QCC_showArrayDouble, QCC_freeSimpleValue);
}
static char *
QCC_showArrayFloat(void *value, int n)
{
return QCC_showSimpleArray(value, sizeof(float), QCC_showFloat, n);
}
QCC_GenValue *
QCC_genArrayFloatLR(int len, float from, float to)
{
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genFloatAtR, &from, &to, sizeof(float), QCC_showArrayFloat, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayFloatL(int len)
{
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genFloatAt, sizeof(float), QCC_showArrayFloat, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayFloat()
{
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genFloatAt, sizeof(float), QCC_showArrayFloat, QCC_freeSimpleValue);
}
static char *
QCC_showArrayBoolean(void *value, int n)
{
return QCC_showSimpleArray(value, sizeof(QCC_Boolean), QCC_showBoolean, n);
}
QCC_GenValue *
QCC_genArrayBooleanL(int len)
{
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genBooleanAt, sizeof(QCC_Boolean), QCC_showArrayBoolean, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayBoolean()
{
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genBooleanAt, sizeof(QCC_Boolean), QCC_showArrayBoolean, QCC_freeSimpleValue);
}
static char *
QCC_showArrayChar(void *value, int n)
{
return QCC_showSimpleArray(value, sizeof(char), QCC_showChar, n);
}
QCC_GenValue *
QCC_genArrayCharL(int len)
{
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genCharAt, sizeof(char), QCC_showArrayChar, QCC_freeSimpleValue);
}
QCC_GenValue *
QCC_genArrayChar()
{
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genCharAt, sizeof(char), QCC_showArrayChar, QCC_freeSimpleValue);
}
/***********************************************************************
* Convenience functions
***********************************************************************/
QCC_TestStatus
QCC_not(QCC_TestStatus propStatus)
{
switch (propStatus) {
case QCC_OK:
return QCC_FAIL;
case QCC_FAIL:
return QCC_OK;
case QCC_NOTHING:
return QCC_NOTHING;
default:
return QCC_FAIL;
}
}
QCC_TestStatus
QCC_and(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status)
{
switch (prop1Status) {
case QCC_OK:
return prop2Status;
case QCC_FAIL:
return QCC_FAIL;
case QCC_NOTHING:
return QCC_NOTHING;
default:
return QCC_FAIL;
}
}
QCC_TestStatus
QCC_or(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status)
{
switch (prop1Status) {
case QCC_OK:
return QCC_OK;
case QCC_FAIL:
case QCC_NOTHING:
return prop2Status;
default:
return QCC_FAIL;
}
}
QCC_TestStatus
QCC_xor(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status)
{
switch (prop1Status) {
case QCC_OK:
return QCC_not(prop2Status);
case QCC_FAIL:
return prop2Status;
case QCC_NOTHING:
return QCC_NOTHING;
default:
return QCC_FAIL;
}
}
/***********************************************************************
* Categorization function
***********************************************************************/
void
QCC_freeStamp(QCC_Stamp *stamps)
{
QCC_Stamp *tstamps;
QCC_Stamp *curr;
for (tstamps = stamps; tstamps != NULL;) {
curr = tstamps;
tstamps = tstamps->next;
free(curr->label);
free(curr);
}
}
static void
QCC_insertOrdStamp(QCC_Stamp **stamps, QCC_Stamp *s)
{
QCC_Stamp *new = malloc(sizeof(QCC_Stamp));
*new = (QCC_Stamp) { .label = strdup(s->label), .n = s->n, .next = NULL };
QCC_Stamp *tstamp = *stamps;
QCC_Stamp **pre = stamps;
while (tstamp) {
if (tstamp->n < new->n) {
break;
} else {
pre = &tstamp->next;
tstamp = tstamp->next;
}
}
*pre = new;
new->next = tstamp;
}
static QCC_Stamp *
QCC_sortStamp(QCC_Stamp *stamps)
{
QCC_Stamp *sortedStamps = NULL;
QCC_Stamp *tstamp;
for (tstamp = stamps; tstamp != NULL; tstamp = tstamp->next) {
QCC_insertOrdStamp(&sortedStamps, tstamp);
}
return sortedStamps;
}
static void
QCC_labelN(QCC_Stamp **stamps, char *label, int n)
{
QCC_Stamp *ptr = *stamps;
QCC_Stamp *pre = NULL;
QCC_Stamp *new;
while (ptr) {
if (strcmp(ptr->label, label) == 0) {
(ptr->n)++;
return;
}
pre = ptr;
ptr = ptr->next;
}
new = malloc(sizeof(QCC_Stamp));
*new = (QCC_Stamp) { .label = strdup(label), .n = 1, .next = NULL };
if (pre) {
pre->next = new;
} else {
*stamps = new;
}
}
void
QCC_label(QCC_Stamp **stamps, char *label)
{
return QCC_labelN(stamps, label, 1);
}
static void
QCC_mergeLabels(QCC_Stamp **dst, QCC_Stamp *src)
{
for (; src != NULL; src = src->next)
QCC_labelN(dst, src->label, src->n);
}
/***********************************************************************
* Testing functions
***********************************************************************/
void
QCC_freeGenValues(QCC_GenValue **arguments, int argumentsN)
{
int i;
for (i = 0; i < argumentsN; i++) {
arguments[i]->free(arguments[i]->value);
free(arguments[i]);
}
if (arguments)
free(arguments);
}
void
QCC_freeResult(QCC_Result *res)
{
QCC_freeStamp(res->stamps);
QCC_freeGenValues(res->arguments, res->argumentsN);
}
QCC_Result
QCC_vforAll(QCC_property prop, int genNum, va_list genP)
{ // QCC_gen *genLst,
QCC_GenValue **vals = NULL;
if (genNum) {
int i;
vals = malloc(sizeof(QCC_GenValue *) * genNum);
for (i = 0; i < genNum; i++) {
QCC_gen gen = va_arg(genP, QCC_gen);
vals[i] = gen();
}
}
QCC_Stamp *stamps = NULL;
QCC_TestStatus status = prop(vals, genNum, &stamps);
return (QCC_Result) { .status = status, .stamps = stamps, .arguments = vals, .argumentsN = genNum };
}
QCC_Result
QCC_forAll(QCC_property prop, int genNum, ...)
{ // QCC_gen *genLst,
va_list genP;
QCC_Result res;
va_start(genP, genNum);
res = QCC_vforAll(prop, genNum, genP);
va_end(genP);
return res;
}
static void
QCC_printStamps(QCC_Stamp *stamps, int n)
{
QCC_Stamp *sortedStamps = QCC_sortStamp(stamps);
QCC_Stamp *tstamps;
for (tstamps = sortedStamps; tstamps != NULL; tstamps = tstamps->next)
printf("%.2f%%\t%s\n", (tstamps->n / (float)n) * 100, tstamps->label);
if (sortedStamps)
QCC_freeStamp(sortedStamps);
}
static void
QCC_printArguments(QCC_GenValue **arguments, int argumentsN)
{
int i;
for (i = 0; i < argumentsN; i++) {
char *s = arguments[i]->show(arguments[i]->value, arguments[i]->n);
printf("%s\n", s);
free(s);
}
}
int
QCC_testForAll(int num, int maxFail, QCC_property prop, int genNum, ...)
{
va_list genP;
int succ = 0;
int fail = 0;
QCC_Result res = { .status = QCC_OK };
QCC_Stamp *stamps = NULL;
while (succ < num && fail < maxFail) {
va_start(genP, genNum);
res = QCC_vforAll(prop, genNum, genP);
va_end(genP);
if (res.status == QCC_FAIL) {
break;
} else {
if (res.status == QCC_OK) {
succ++;
QCC_mergeLabels(&stamps, res.stamps);
} else
fail++;
QCC_freeResult(&res);
}
}
if (succ == num) {
//printf("%d test passed (%d)!\n", succ, fail);
QCC_printStamps(stamps, succ);
if (stamps)
QCC_freeStamp(stamps);
return 0;
} else if (res.status == QCC_FAIL) {
printf("\nFalsifiable after %d test\n", succ + 1);
QCC_printArguments(res.arguments, res.argumentsN);
QCC_freeResult(&res);
if (stamps)
QCC_freeStamp(stamps);
return 1;
} else if (fail >= maxFail) {
printf("\nGave up after %d tests!\n", succ);
QCC_printStamps(stamps, succ);
if (stamps)
QCC_freeStamp(stamps);
return -1;
}
return 0;
}
#pragma GCC diagnostic pop