task-gen.c

/**
 * Copyright (c) 2024, SWGY, Inc. <ron@sw.gy>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <err.h>
#include <errno.h>
#include <getopt.h> /* getopt_long */
#include <stdio.h>
#include <stdlib.h>
#include <time.h> /* ctime */
#include <unistd.h>

#include <sys/stdint.h> /* UINT16_MAX */
#include <sys/types.h> /* ctime etc */

/*
 * Print a task definition with the given proportion of congruence for stimulus
 * and responses.
 */
int print_task(float s_c, float s_r, int last_task);

static struct option longopts[] = {
	{ "num-tasks",          required_argument, NULL, 'n' },
	{ "stimulus-congruent", required_argument, NULL, 's' },
	{ "response-congruent", required_argument, NULL, 'r' },
	{ NULL,                 0,                 NULL,  0  }
};

static __dead void
usage(const char *n)
{
	fprintf(stderr, "usage: %s -n NUM_TASKS --stimulus-congruent=X "
	    "--response-congruent=Y\n", n);
	exit(1);
}

int
main(int argc, char **argv)
{
	int 	ch, numjobs, last_task;
	float 	s_c, r_c;
	time_t	now;

	const char *errstr, *time_str;

	numjobs = -1;
	s_c = r_c = 0.0f;
	while ((ch = getopt_long(argc, argv, "n:s:r:", longopts, NULL)) != -1) {
		switch (ch) {
		case 'n':
			numjobs = strtonum(optarg, 1, 1800, &errstr);
			if (errstr != NULL)
				err(1, "Problem parsing num jobs %s: %s",
				    errstr, optarg);
			break;
		case 's':
			s_c = (float) atof(optarg);
			break;
		case 'r':
			r_c = (float) atof(optarg);
			break;
		default:
			usage(*argv); /* does not return */
		}
	}

	if (pledge("stdio", NULL) == -1 || pledge(NULL, NULL) == -1)
		err(1, "pledge");

	if (numjobs < 1 || numjobs > 1800)
		usage(*argv); /* does not return */

	if (s_c < 0.0 || s_c > 1.0 || r_c < 0.0 || r_c > 1.0) {
		fprintf(stderr, "Congruence ratios must be between 0 and 1\n");
		exit(1);
	}

	time(&now);
	if ((time_str = ctime(&now)) == NULL)
		err(1, "ctime");
	printf("# Tasks generated on %s", time_str);
	printf("# %s -n %d --stimulus-congruent %f --response-congruent %f\n",
	    *argv, numjobs, s_c, r_c);
	last_task = -1;
	for (; numjobs > 0; --numjobs) {
		last_task = print_task(s_c, r_c, last_task);
	}
	exit(0);
}

static int
tt(float s_c, float s_r, uint32_t v)
{
	int retval;
	retval = 0;
	if (v & 1) /* red stimulus */
		retval = 4;
	else /* blue stimulus */
		retval = 0;
	/* Now determine the stimulus type using the upper 16 bits */
	if ((((v>>16) & UINT16_MAX) / (float) UINT16_MAX) > s_c)
		retval += 2;
	/* determine the response placement using the high bit */
	if (!(v & (1<<31)))
		retval += 8;

	/* next determine the response type using the lower 16 bits */
	if (((v & UINT16_MAX) / (float) UINT16_MAX) > s_r)
		retval += 1;
	return retval;
}

int
print_task(float s_c, float s_r, int last_task)
{
	uint32_t v;

	for (v = arc4random(); tt(s_c, s_r, v) == last_task; v = arc4random()) ;
	/* The low bit determines which stimulus to use: one or two */
	if (v & 1)
		printf("1");
	else
		printf("2");
	/* Now determine the stimulus type using the upper 16 bits */
	if ((((v>>16) & UINT16_MAX) / (float) UINT16_MAX) > s_c)
		printf("SI "); /* incongruent */
	else
		printf("SC "); /* congruent */

	/* determine the response placement using the high bit */
	if (v & (1<<31))
		printf("1");
	else
		printf("2");
	/* next determine the response type using the lower 16 bits */
	if (((v & UINT16_MAX) / (float) UINT16_MAX) > s_r)
		printf("RI\n"); /* incongruent */
	else
		printf("RC\n"); /* congruent */
	return tt(s_c, s_r, v);
}