#!/usr/bin/nickle

real
pll_out(real clock_in, int mul, int div)
{
	return clock_in * mul / div;
}

real
pll_in(real pll_out, int mul, int div)
{
	return pll_out * div / mul;
}

real
fcco_out(real clock_in, int mul, int div)
{
	real	out = pll_out(clock_in, mul, div);
	return out * div;
}

bool
valid_clock_in(real clock_in)
{
	return 10 <= clock_in && clock_in < 25;
}

bool
valid_fcco(real clock_in, int mul, int div)
{
	real fcco = fcco_out (clock_in, mul, div);
	return 156 <= fcco && fcco <= 320;
}

void
all_clocks(real clock_in) {
	for (int mul = 1; mul <= 32; mul++) {
		for (int div = 2; div <= 16; div *= 2) {
			if (!valid_fcco(clock_in, mul, div))
				printf ("clock_in %f mul  %d div %d invalid fcco %f\n",
					clock_in, mul, div, fcco_out(clock_in, mul, div));
			else
				printf ("clock_in %f mul %d div %d pll_out %f\n",
					clock_in, mul, div, pll_out(clock_in, mul, div));
		}
	}
}

typedef struct {
	real	clock_in;
	int	mul;
	int	div;
	real	pll_out;
} clock;

clock[]
all_ins(real pll_out)
{
	clock[...] clocks = {};
	for (int mul = 1; mul <= 32; mul++) {
		for (int div = 2; div <= 16; div *= 2) {
			real clock_in = pll_in(pll_out, mul, div);
			if (valid_clock_in(clock_in) && valid_fcco(clock_in, mul, div)) {
				clocks[dim(clocks)] = (clock) {
					.clock_in = clock_in,
					.mul = mul,
					.div = div,
					.pll_out = pll_out
				};
			}
		}
	}
	return clocks;
}

#all_clocks(12.0);

autoload Sort;

void
dump_clocks(clock[] clocks)
{
	printf ("Clocks for %f\n", clocks[0].pll_out);
	Sort::qsort(&clocks, bool func(clock a, clock b) { return a.clock_in > b.clock_in; });
	for (int i = 0; i < dim (clocks); i++) {
		printf("\tclock_in %f mul %d div %d\n",
		       clocks[i].clock_in,
		       clocks[i].mul,
		       clocks[i].div);
	}
}

void
find_clocks() {
	clock[] c40 = all_ins(40.0);
	clock[] c48 = all_ins(48.0);

	dump_clocks(c40);
	dump_clocks(c48);
	for (int i40 = 0; i40 < dim(c40); i40++) {
		for (int i48 = 0; i48 < dim(c48); i48++) {
			if (c40[i40].clock_in == c48[i48].clock_in)
				printf ("clock_in %f mul_40 %d div_40 %d mul_48 %d div_48 %d\n",
					c40[i40].clock_in,
					c40[i40].mul,
					c40[i40].div,
					c48[i48].mul,
					c48[i48].div);
		}
	}
}

find_clocks();
