grid :- 				% fill screen with a 2 lambda grid
	g_colour(7),
	g_enable(7),
	lambda(L),
	Xw is 688 / (2 * L),
	Yw is 512 / (2 * L),
	\+ r_grid(0, 0, Xw, Yw).

r_grid(X, Y, Xw, Yw) :- 		% square or wider than tall
	Xw >= Yw,
        !,
	p2(Yw, Yp2),
	\+ p2_grid(X, Y, Yp2),		% power of 2 sub-square
	Xw > Yp2,
	X1 is X + Yp2,
	Xw1 is Xw - Yp2,
	\+ r_grid(X1, Y, Xw1, Yw),	% strip to left
	Yw > Yp2,
	Y1 is Y + Yp2,
	Yw1 is Yw - Yp2,
	r_grid(X, Y1, Yp2, Yw1).	% strip along top

r_grid(X, Y, Xw, Yw) :- 		% taller than wide
	p2(Xw, Xp2),
	\+ p2_grid(X, Y, Xp2),		% power of two sub-square
	Yw > Xp2,
	Y1 is Y + Xp2,
	Yw1 is Yw - Xp2,
	\+ r_grid(X, Y1, Xw, Yw1),	% strip along top
	Xw > Xp2,
	X1 is X + Xp2,
	Xw1 is Xw - Xp2,
	r_grid(X1, Y, Xw1, Xp2).	% strip to left

p2(N, P) :-				% P is max power of 2 less than N
	p2a(N, 1, P).

p2a(N, M, P) :-
	2 * M > N,
	!,
	P is M.

p2a(N, M, P) :-
	M1 is 2 * M,
	p2a(N, M1, P).

p2_grid(X, Y, 1) :-			% single grid point
	!,
	lambda(L),
	Xp is X * 2 * L,
	Yp is Y * 2 * L,
	g_plot(Xp, Yp),
	fail.

p2_grid(X, Y, W) :-			% square grid with power of 2 sides
	W1 is W / 2,
	X1 is X + W1,
	Y1 is Y + W1,
	\+ p2_grid(X, Y, W1),
	\+ p2_grid(X1, Y, W1),
	\+ p2_grid(X, Y1, W1),
	p2_grid(X1, Y1, W1).

place(S) :-				% placement by mouse
	repeat,
	readmouse(S).

place :-				% default is shiftcell
	g_clear,
	grid,
	place(shiftcell).

readmouse(_) :-
	g_mouse(_, _, 2).			% middle button - stop

readmouse(S) :-
	rangemouse(Xm, Ym, B),		% left button - instantiate here
	lambda(L),			% no buttons - show cursor
	X is (Xm + L/2) / L,
	Y is (Ym + L/2) / L,
	P =.. [S,[X,Y]],
	case(B, [cursor(X,Y), P]),
	!,
	fail.

case(0, [F|_]) :- F.
case(C, [F|R]) :-
	C1 is C - 1,
	case(C1, R).

cursor(X, Y) :-				% display cursor cross at X, Y
	g_enable(8),
	lambda(L),
	range(0, 687, X*L-L/2, X0),
	range(0, 687, X*L+L/2, X1),
	range(0, 511, Y*L-L/2, Y0),
	range(0, 511, Y*L+L/2, Y1),
	g_colour(8),
	g_line(X0, Y0, X1, Y1),
	g_line(X1, Y0, X0, Y1),
	delay(50),
	g_colour(0),
	g_line(X0, Y0, X1, Y1),
	g_line(X1, Y0, X0, Y1).

rangemouse(X, Y, B) :-
	g_mouse(Xm, Ym, B),
	range(0, 687, Xm, X),
	range(0, 511, Ym, Y).

range(Min, _, N, R) :-			% R = N constrained between Min & Max
	Ne is (N),			%     N can be an expression
	Ne < Min,
	R = Min.
range(_, Max, N, R) :-
	Ne is (N),
	Ne > Max,
	R = Max.
range(_, _, N, R) :-
	R is (N).

delay(N) :-				% busy wait N milliseconds
	T is cputime * 1000 + N,
	repeat,
	T < cputime * 1000.

shiftcell(_, _, 0).			% array of shiftcells
shiftcell([X, Y], Nx, Ny) :-
	shiftcell([X, Y], Nx),
	Ny1 is Ny - 1,
	Y1 is Y + 26,
	!,				% a cut in the middle saves stacks
	shiftcell([X, Y1], Nx, Ny1).

shiftcell(_, 0).			% vector of shiftcells
shiftcell([X, Y], N) :-
	shiftcell([X, Y]),
	N1 is N - 1,
	X1 is X + 21,
	!,
	shiftcell([X1, Y], N1).

shiftcell([X, Y]) :-			% single shiftcell
	implant([X+2, Y+9], [X+8, Y+20]),
	wire(diffusion, 3, [X+3, Y], [X+7, Y]),
	wire(diffusion, 6, [X+2, Y+3], [X+2, Y+12]),
	wire(diffusion, [X+3, Y+12], [X+7, Y+12]),
	wire(diffusion, [X+4, Y+14], [X+4, Y+19]),
	wire(diffusion, 4, [X+3, Y+19], [X+3, Y+23]),
	wire(diffusion, [X+8, Y+8], [X+11, Y+8]),
	wire(diffusion, [X+9, Y+10], [X+9, Y+11]),
	wire(diffusion, [X+9, Y+11], [X+16, Y+11]),
	wire(diffusion, 4, [X+16, Y+9], [X+16, Y+13]),
	wire(poly, [X, Y+5], [X+10, Y+5]),
	wire(poly, 6, [X+2, Y+11], [X+2, Y+18]),
	wire(poly, [X+12, Y], [X+12, Y+26]),
	wire(poly, 4, [X+16, Y+7], [X+20, Y+7]),
	wire(poly, [X+16, Y+5], [X+21, Y+5]),
	wire(metal, 4, [X, Y], [X+21, Y]),
	wire(metal, 4, [X, Y+19], [X+21, Y+19]),
	wire(contactcut, [X+4, Y+1], [X+4, Y+3]),
	wire(contactcut, [X+4, Y+9], [X+4, Y+13]),
	wire(contactcut, [X+4, Y+20], [X+4, Y+22]),
	wire(contactcut, [X+17, Y+8], [X+17, Y+12]).
