shithub: zuke

ref: 524e32b1cd45a332c58b8cd681662f815628deee
dir: /shuffle.c/

View raw version
#include "shuffle.h"

void
shuffle_init(Shuffle *s, int (*rndrange)(int max_excluding), int total, int first)
{
	assert(first < total && total > 0);
	s->m = s->n = total;
	s->i = 0;
	s->xi = s->x0 = first;

	if(total < 4){
		s->a = 1;
		s->c = 3;
		s->m = 7;
		return;
	}

	s->m += 1;
	s->m |= s->m >> 1;
	s->m |= s->m >> 2;
	s->m |= s->m >> 4;
	s->m |= s->m >> 8;
	s->m |= s->m >> 16;

	s->a = 1 + rndrange(s->m/4)*4;     /* 1 ≤ a < m   && a mod 4 = 1 */
	s->c = 3 + rndrange((s->m-2)/2)*2; /* 3 ≤ c < m-1 && c mod 2 = 1 */
}

void
shuffle_resize(Shuffle *s, int total)
{
	assert(total > 0);
	s->n = total;
}

static int
next(Shuffle *s)
{
	int res;

	res = s->xi;
	if(s->n < 2){
		/* if it's less than two items, just use the first one (if any) */
		s->x0 = s->xi = s->i = 0;
		return 0;
	}else if(s->x0 >= s->n){
		/* if items were removed up to this one -- update for a period */
		s->x0 = 0;
	}

	for(;;){
		s->xi = (s->a*s->xi + s->c) & s->m;
		if(s->xi < s->n){
			s->i++;
			if(s->xi == s->x0)
				s->i = 0;
			break;
		}
	}

	return res;
}

int
shuffle_one(Shuffle *s, int index)
{
	if(s->n < 1 || index < 0)
		return index;
	index %= s->n;
	while(s->i < s->n && s->i != index)
		next(s);

	return next(s);
}

int
shuffle_for(Shuffle *s, int index)
{
	int i, r;

	if(index < 0 || index >= s->n)
		return index;
	for(i = 0; i < s->n; i++){
		r = s->i;
		if(next(s) == index){
			next(s);
			return r;
		}
	}

	return index;
}