

--
-- Copyright (C) 2023  <fastrgv@gmail.com>
--
-- 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 may read the full text of the GNU General Public License
-- at <http://www.gnu.org/licenses/>.
--




---------------------------------------------------------------------------

with gl, gl.binding, gl.pointers;
with glu, glu.binding, glu.pointers;
with glext, glext.binding, glext.pointers;

with glfw3; use glfw3;


with matutils;
with gtex;


with shader;  use shader;
with pictobj;
with pngloader;

with ada.calendar;
with ada.characters.handling;
with ada.strings.fixed;

with ada.directories;

---------------------------------------------------------------------------

with System;
with Interfaces.C;
use  type interfaces.c.unsigned;
with Interfaces.C.Pointers;
with interfaces.c.strings;


with ada.unchecked_conversion;
with Ada.Command_Line;
with Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO;
with ada.numerics.generic_elementary_functions;

with text_io;
with ada.float_text_io;
with ada.integer_text_io;

with ada.calendar;
with unchecked_deallocation;

with ada.strings.fixed;




procedure asudx is


	use Ada.Strings.Unbounded;
	use Ada.Strings.Unbounded.Text_IO;

	use text_io;
	use pngloader;
	use matutils;

	use interfaces.c;
	use interfaces.c.strings;
	use glext;
	use glext.pointers;
	use glext.binding;
	use gl;
	use gl.binding;
	use gl.pointers;



	mainWindow :  access GLFWwindow;
	Nwid,Nhit,Mwid,Mhit,Wwid,Whit,  Fwid, Fhit : aliased interfaces.c.int;


	pmvp : chars_ptr := new_string("MVP"&ascii.nul);
	pmyts : chars_ptr := new_string("myTextureSampler"&ascii.nul);


	vertbuff, uvbuff, elembuff, rgbbuff, vertexarrayid : gluint;

	matrixid, uniftex : glint;

	blu_texid,
	box_texid, black_texid, red_texid, lgrey_texid,
	floor_texid, space_texid, gray_texid, pic_texid, picttexshadid : gluint := 0;

	opict: pictobj.pictangle;

	crlf: constant string := Character'Val(13)&Character'Val(10); 
	--CR+LF(msWin) (new_line)

	--used for delayed write to console, if and when a contradiction arises:
	consoleBuf: unbounded_string;

----------------------------------------------------
	subtype i9 is integer range 1..9;
	subtype s9 is integer range 0..9;
	-- zero indicates indeterminate digit




	wasDeletion,
	showBrute,
	--linkedPairs,
	showLP,

	showKsel, --Kram: use K-key to display final [good] status
	showK, --Kram: Key-cell digit ramifications
	shoXwing,
	showY, --Ywing
	show4n, show4h, --quads

	show3h, --hiddentriples
	show3n, --nakedtriples
	show2n, show2h, --hidden pairs
	dmode,bmode,rmode,cmode,
	minimal: boolean := false; -- toggles De-clutter

	rowPairs, colPairs, boxPairsNa, boxPairsAl,
	diagPairs, boxPairs: boolean := false; -- toggles Red/Green digits

	nrows, ncols : constant integer := 9;
	subtype entryrange is integer range 1..9;
	type flagtypes is array(entryrange) of boolean;


----------------------------------------------------
	s99mx: constant integer := 12;
	subtype s99 is integer range 0..s99mx;
	seechain, nchains : array(i9) of s99;
	none: array(i9) of boolean;

	digBuf: array(i9,s99) of unbounded_string; --for replay of LinkedPairs logic

	subtype s3 is integer range 0..3;
	colorL2: array(i9,s99,entryrange,entryrange) of s3;
	-- 1st index is digitSelect
	-- 2nd index is simply the chain number...
	-- hopefully there are fewer than 19...
	-- 1 => Red; 2 => Green [colors apply to digSelect only]
	-- tracks color of linked-pairs
	--
	-- plan: choose a digit "d", 
	-- then set all linked-pairs as
	-- 2 alternating colors Red/Green...
	-- helpful rules:
	-- a) if any house has 2 d's of same color, that color means OFF
	-- b) if any OTHER d can see both colors, it must be OFF.



	type candidatetype is array(entryrange,entryrange) of flagtypes;

	hiliteKdel, --KeyCell-deleted

	hiliteXcDel, --xCycle-deletables
	hilite1,  --lone block digits
	hilite3,  --lone digits

	hiliteHQ, --hiddenQuads
	hiliteHP, --hiddenPairs
	hiliteHT, --hiddenTriples

	--removables:
	hiliteHQrem, --hiddenQuads
	hiliteHPrem, --hiddenPairs
	hiliteHTrem, --hiddenTriples

	hiliteD, --Diagonal digit pairs
	--hiliteRC, --Row/Col digit pairs
	hiliteRow, hiliteCol,
	hiliteRowRem, hiliteColRem, hiliteBoxRem, hiliteDiagRem,

	--plan: choose a digit "d", 
	--then set all linked-pairs as
	--2 alternating colors Red/Green...
	-- a) if any house has 2 d's of same color, that color means OFF
	-- b) if any OTHER d can see both colors, it must be OFF.

	hiliteBPna, -- NONaligned box pairs
	hiliteBP, -- Aligned box pairs
	candidate, drawnYet
		: candidatetype := (others=>(others=>(others=>true)));

	remEmpty
		:  constant candidatetype
			:= (others=>(others=>(others=>false)));



	--naked pairs, Triples, Quads
	hiliteNP, hiliteNT, hiliteNQ : array(entryrange,entryrange) of boolean;


	pr,pc: integer := 0; --picked row, col (by getmouse)



	--ForwDiag, BackDiag
	fdPair,bdPair : array(i9,i9) of s9;

	-- row, col, Block
	rowPair, colPair, BoxPair : array(i9,i9,i9) of s9;


	tr0,tc0, tdig: s9; -- Ywing




	type m9type is array(i9,i9) of s9;

	--yel9, 
	blu9, m9, om9: m9type := (others=>(others=>0));

	help: boolean := false;

	package blutex is new gtex; -- initialize to blue or cyan
	package blktex is new gtex; -- initialized to black text
	package grytex is new gtex; -- initialized to grey text
	package ltgtex is new gtex; -- initialized to grey text
	package whitex is new gtex; -- initialized to white text
	package redtex is new gtex; -- initialized to red text
	package magtex is new gtex; -- initialized to magenta text
	package grntex is new gtex; -- initialized to green text
	package orntex is new gtex; -- initialized to yellow text
	package yebtex is new gtex; -- initialized to yellow text
	package cyatex is new gtex; -- initialized to cyan text
	package purtex is new gtex; -- initialized to cyan text



function i2st( i: integer ) return string is
begin
	if i>=0 and i<=9 then
		return ada.strings.fixed.trim( integer'image(i), ada.strings.left);
	else
		return "";
	end if;
end i2st;




procedure susolvex is separate;


subtype coorng is integer range 1..9;
type coords is array(1..2) of coorng;
type cellseq is array(1..19) of coords;


subtype ocoorng is integer range 0..9;
type ocoords is array(1..2) of ocoorng;
type xcycletype is array(1..9,1..9) of ocoords;
type xoncycletype is array(1..9,1..9) of boolean;
xcycle: xcycletype;
xoncycle, xoffcycle: xoncycletype;
ca, cz : ocoords; -- beginning & end of a bad Xcycle
badXc, showX : boolean := false;




digSelect: s9;

rx1,cx1, rx2,cx2, rx3,cx3,
rx4,cx4, xdig, --Xwing stuff

ry1,cy1, ry2,cy2, ry3,cy3 : s9 := 0; --coorng;



-- returns cell coords of a Ywing triple
procedure yWing(
	r1,c1, r2,c2, r3,c3, tr0,tc0, dig: out s9; 
	found: out boolean) is separate;



xcd: cellseq;
xcn: integer;
procedure Xwing(
	r1,c1, r2,c2, r3,c3, r4,c4, dig: out s9;
	cdelete: out cellseq; cdn: out integer;
	found: out boolean) is separate;





-- returns 1st bad Xcycle
procedure badXcycle(
	xcycle: in out xcycletype;
	ca, cz: out ocoords;
	odig : out i9;
	found: out boolean ) is separate;


procedure goodXcycle(
	xcycle: in out xcycletype;
	ca, cz: out ocoords;
	odig : out i9;
	found, bad: out boolean;
	deletable: out candidatetype
	) is separate;









-- find hidden pairs
procedure hiddenPairs( rcd: cellseq; 
	t1,t2, dt1,dt2,
	s1,s2, ds1,ds2: out coorng; 
	found, found2: out boolean) is separate;

-- find naked pairs
procedure nakedPairs( rcd: cellseq; 
	t1,t2, s1,s2: out coorng; 
	found, found2: out boolean) is separate;



procedure setNpairs is --Naked pairs
	rcd: cellseq;
	t1,t2, s1,s2: coorng;
	found, found2: boolean;
	k,row,col: integer;

	--flag for naked block-pairs::
	showbox : boolean := true; --false; --reduce clutter

begin

	hiliteNP := (others=>(others=>false));

	--RowPairs:
	for row in 1..9 loop
		for col in 1..9 loop
			rcd(col):=(row,col);
		end loop; --col

		nakedPairs(rcd, t1,t2, s1,s2, found, found2);
		if found then
			hiliteNP( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNP( rcd(t2)(1), rcd(t2)(2) ) := true;
		end if;
		if found2 then
			hiliteNP( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNP( rcd(s2)(1), rcd(s2)(2) ) := true;
		end if;

	end loop; --row


	--ColPairs:
	for col in 1..9 loop
		for row in 1..9 loop
			rcd(row):=(row,col);
		end loop; --col

		nakedPairs(rcd, t1,t2, s1,s2, found, found2);
		if found then
			hiliteNP( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNP( rcd(t2)(1), rcd(t2)(2) ) := true;
		end if;
		if found2 then
			hiliteNP( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNP( rcd(s2)(1), rcd(s2)(2) ) := true;
		end if;

	end loop; --row


if showbox then

	--BlockPairs:
	for blkrow in 0..2 loop
	for blkcol in 0..2 loop

		k:=0;
		for r in 0..2 loop
		for c in 0..2 loop
			k:=k+1;
			row:=blkrow*3+r+1;
			col:=blkcol*3+c+1;
			rcd(k):=(row,col);
		end loop; --c
		end loop; --r

		nakedPairs(rcd, t1,t2, s1,s2, found, found2);
		if found then
			hiliteNP( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNP( rcd(t2)(1), rcd(t2)(2) ) := true;
		end if;
		if found2 then
			hiliteNP( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNP( rcd(s2)(1), rcd(s2)(2) ) := true;
		end if;

	end loop; --blkcol
	end loop; --blkrow

end if; --showbox

---------- next two are unique to X-sudoku ------------------

	--fDiagPairs:
		for diag in 1..9 loop
			rcd(diag):=(diag,diag);
		end loop;

		nakedPairs(rcd, t1,t2,  s1,s2, found, found2);
		if found then
			hiliteNP( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNP( rcd(t2)(1), rcd(t2)(2) ) := true;
		end if;
		if found2 then
			hiliteNP( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNP( rcd(s2)(1), rcd(s2)(2) ) := true;
		end if;


	--rDiagTriples:
		for diag in 1..9 loop
			rcd(diag):=(diag,10-diag);
		end loop;

		nakedPairs(rcd, t1,t2,  s1,s2,  found, found2);
		if found then
			hiliteNP( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNP( rcd(t2)(1), rcd(t2)(2) ) := true;
		end if;
		if found2 then
			hiliteNP( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNP( rcd(s2)(1), rcd(s2)(2) ) := true;
		end if;


---------- above two are unique to X-sudoku ------------------



end setNpairs;






procedure setHPairs is --Hidden pairs
	rcd: cellseq;
	dt1,dt2, ds1,ds2,
	t1,t2, s1,s2: coorng;
	found, found2: boolean;
	k,row,col: integer;
begin

	hiliteHP := (others=>(others=>(others=>false)));

if rmode then

	--RowPairs:
	for row in 1..9 loop
		for col in 1..9 loop
			rcd(col):=(row,col);
		end loop; --col

		hiddenPairs(rcd, t1,t2, dt1,dt2, s1,s2, ds1,ds2, found, found2);
		if found then
			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;

			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;

		end if;
		if found2 then
			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;

			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;

		end if;

	end loop; --row

end if; --rmode

if cmode then

	--ColPairs:
	for col in 1..9 loop
		for row in 1..9 loop
			rcd(row):=(row,col);
		end loop; --col

		hiddenPairs(rcd, t1,t2, dt1,dt2, s1,s2, ds1,ds2, found, found2);
		if found then
			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;

			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;

		end if;
		if found2 then
			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;

			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;

		end if;

	end loop; --row

end if; --cmode


if bmode then

	--BlockPairs:
	for blkrow in 0..2 loop
	for blkcol in 0..2 loop

		k:=0;
		for r in 0..2 loop
		for c in 0..2 loop
			k:=k+1;
			row:=blkrow*3+r+1;
			col:=blkcol*3+c+1;
			rcd(k):=(row,col);
		end loop; --c
		end loop; --r

		hiddenPairs(rcd, t1,t2, dt1,dt2, s1,s2, ds1,ds2, found, found2);
		if found then
			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;

			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;

		end if;
		if found2 then
			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;

			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;

		end if;

	end loop; --blkcol
	end loop; --blkrow

end if; --bmode

---------- next two are unique to X-sudoku ------------------

if dmode then

	--fDiagTriples:
		for diag in 1..9 loop
			rcd(diag):=(diag,diag);
		end loop;

		hiddenPairs(rcd, t1,t2, dt1,dt2, s1,s2, ds1,ds2, found, found2);
		if found then
			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;

			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;

		end if;
		if found2 then
			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;

			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;

		end if;


	--rDiagTriples:
		for diag in 1..9 loop
			rcd(diag):=(diag,10-diag);
		end loop;

		hiddenPairs(rcd, t1,t2, dt1,dt2, s1,s2, ds1,ds2, found, found2);
		if found then
			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;

			hiliteHP( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHP( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;

		end if;
		if found2 then
			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;

			hiliteHP( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHP( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;

		end if;

end if; --dmode

---------- above two are unique to X-sudoku ------------------

	--ID removable cellmates of hidden pairs
	hiliteHPrem := (others=>(others=>(others=>false)));
	for r in 1..9 loop
	for c in 1..9 loop
	for d in 1..9 loop
	if hiliteHP(r,c)(d) then
		for o in 1..9 loop
		if not hiliteHP(r,c)(o) and candidate(r,c)(o) then
			hiliteHPrem(r,c)(o):=true;
		end if;
		end loop; --o
	end if;
	end loop;
	end loop; --c
	end loop; --r



end setHPairs;







-- find naked triples:
procedure nakedTriples( rcd: cellseq; 
	t1, t2, t3, s1,s2,s3: out coorng; found, found2: out boolean) is separate;



procedure setNtriples is --Naked pairs/triples
	rcd: cellseq;
	t1,t2,t3, s1,s2,s3: coorng;
	found, found2: boolean;
	k,row,col: integer;

	--flag for naked block-triples:
	showbox : boolean := true; --false; --set false to reduce clutter
begin

	hiliteNT := (others=>(others=>false));



	--RowTriples:
	for row in 1..9 loop
		for col in 1..9 loop
			rcd(col):=(row,col);
		end loop; --col

		nakedTriples(rcd, t1,t2,t3, s1,s2,s3, found, found2);
		if found then
			hiliteNT( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNT( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNT( rcd(t3)(1), rcd(t3)(2) ) := true;
		end if;
		if found2 then
			hiliteNT( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNT( rcd(s2)(1), rcd(s2)(2) ) := true;
			hiliteNT( rcd(s3)(1), rcd(s3)(2) ) := true;
		end if;


	end loop; --row

	--ColTriples:
	for col in 1..9 loop
		for row in 1..9 loop
			rcd(row):=(row,col);
		end loop; --col

		nakedTriples(rcd, t1,t2,t3, s1,s2,s3, found, found2);
		if found then
			hiliteNT( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNT( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNT( rcd(t3)(1), rcd(t3)(2) ) := true;
		end if;
		if found2 then
			hiliteNT( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNT( rcd(s2)(1), rcd(s2)(2) ) := true;
			hiliteNT( rcd(s3)(1), rcd(s3)(2) ) := true;
		end if;


	end loop; --row

if showbox then

	--BlockTriples:
	for blkrow in 0..2 loop
	for blkcol in 0..2 loop

		k:=0;
		for r in 0..2 loop
		for c in 0..2 loop
			k:=k+1;
			row:=blkrow*3+r+1;
			col:=blkcol*3+c+1;
			rcd(k):=(row,col);
		end loop; --c
		end loop; --r

		nakedTriples(rcd, t1,t2,t3, s1,s2,s3, found, found2);
		if found then
			hiliteNT( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNT( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNT( rcd(t3)(1), rcd(t3)(2) ) := true;
		end if;
		if found2 then
			hiliteNT( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNT( rcd(s2)(1), rcd(s2)(2) ) := true;
			hiliteNT( rcd(s3)(1), rcd(s3)(2) ) := true;
		end if;


	end loop; --blkcol
	end loop; --blkrow

end if; --showbox

---------- next two are unique to X-sudoku ------------------

	--fDiagTriples:
		for diag in 1..9 loop
			rcd(diag):=(diag,diag);
		end loop;
		nakedTriples(rcd, t1,t2,t3, s1,s2,s3, found, found2);
		if found then
			hiliteNT( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNT( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNT( rcd(t3)(1), rcd(t3)(2) ) := true;

		end if;
		if found2 then
			hiliteNT( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNT( rcd(s2)(1), rcd(s2)(2) ) := true;
			hiliteNT( rcd(s3)(1), rcd(s3)(2) ) := true;
		end if;

	--rDiagTriples:
		for diag in 1..9 loop
			rcd(diag):=(diag,10-diag);
		end loop;
		nakedTriples(rcd, t1,t2,t3, s1,s2,s3, found, found2);
		if found then
			hiliteNT( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNT( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNT( rcd(t3)(1), rcd(t3)(2) ) := true;

		end if;
		if found2 then
			hiliteNT( rcd(s1)(1), rcd(s1)(2) ) := true;
			hiliteNT( rcd(s2)(1), rcd(s2)(2) ) := true;
			hiliteNT( rcd(s3)(1), rcd(s3)(2) ) := true;
		end if;


---------- above two are unique to X-sudoku ------------------



end setNtriples;






-- find hidden Triples
procedure hiddenTriples( rcd: cellseq; 
	t1,t2,t3, dt1,dt2,dt3,
	s1,s2,s3, ds1,ds2,ds3: out coorng; 
	found, found2: out boolean) is separate;

-- this finds Hidden-Triples:
procedure setHTriples is
	rcd: cellseq;
	t1,t2,t3, dt1,dt2,dt3,
	s1,s2,s3, ds1,ds2,ds3: coorng;
	found, found2: boolean;
	k,row,col: integer;
begin

	hiliteHT := (others=>(others=>(others=>false)));


if rmode then

	--RowTriples:
	for row in 1..9 loop
		for col in 1..9 loop
			rcd(col):=(row,col);
		end loop; --col
		hiddenTriples(rcd, 
			t1,t2,t3, dt1,dt2,dt3,
			s1,s2,s3, ds1,ds2,ds3, found, found2);
		if found then
			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;

		end if;
		if found2 then
			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds1) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds2) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds3) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds3) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds3) := true;

		end if;
	end loop; --row

end if; --rmode


if cmode then

	--ColTriples:
	for col in 1..9 loop
		for row in 1..9 loop
			rcd(row):=(row,col);
		end loop; --col
		hiddenTriples(rcd, 
			t1,t2,t3, dt1,dt2,dt3,
			s1,s2,s3, ds1,ds2,ds3, found, found2);
		if found then
			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;

		end if;
		if found2 then
			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds1) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds2) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds3) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds3) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds3) := true;

		end if;

	end loop; --col

end if; --cmode

---------- next two are unique to X-sudoku ------------------

if dmode then

	--fDiagTriples:
		for diag in 1..9 loop
			rcd(diag):=(diag,diag);
		end loop;
		hiddenTriples(rcd, 
			t1,t2,t3, dt1,dt2,dt3,
			s1,s2,s3, ds1,ds2,ds3, found, found2);
		if found then
			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;

		end if;
		if found2 then
			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds1) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds2) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds3) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds3) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds3) := true;

		end if;

	--rDiagTriples:
		for diag in 1..9 loop
			rcd(diag):=(diag,10-diag);
		end loop;
		hiddenTriples(rcd, 
			t1,t2,t3, dt1,dt2,dt3,
			s1,s2,s3, ds1,ds2,ds3, found, found2);
		if found then
			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;

		end if;
		if found2 then
			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds1) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds2) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds3) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds3) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds3) := true;

		end if;

end if; --dmode

---------- above two are unique to X-sudoku ------------------

if bmode then

	--BlockTriples:
	for blkrow in 0..2 loop
	for blkcol in 0..2 loop

		k:=0;
		for r in 0..2 loop
		for c in 0..2 loop
			k:=k+1;
			row:=blkrow*3+r+1;
			col:=blkcol*3+c+1;
			rcd(k):=(row,col);
		end loop; --c
		end loop; --r
		hiddenTriples(rcd, 
			t1,t2,t3, dt1,dt2,dt3,
			s1,s2,s3, ds1,ds2,ds3, found, found2);
		if found then

--debug
--put("A.H3 box row col " & i2st( blkrow ) & i2st( blkcol ) );
--put(" : digits: "&i2st(dt1)&i2st(dt2)&i2st(dt3));
--put(" cells: "
--	&i2st( rcd(t1)(1) ) & i2st( rcd(t1)(2) ) &" : "
--	&i2st( rcd(t2)(1) ) & i2st( rcd(t2)(2) ) &" : "
--	&i2st( rcd(t3)(1) ) & i2st( rcd(t3)(2) ));
--new_line;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;

			hiliteHT( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHT( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHT( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;

		end if;
		if found2 then

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds1) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds1) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds1) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds2) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds2) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds2) := true;

			hiliteHT( rcd(s1)(1), rcd(s1)(2) )(ds3) := true;
			hiliteHT( rcd(s2)(1), rcd(s2)(2) )(ds3) := true;
			hiliteHT( rcd(s3)(1), rcd(s3)(2) )(ds3) := true;

		end if;

	end loop; --blkcol
	end loop; --blkrow

end if; --bmode


	--ID removable cellmates of hidden triples
	hiliteHTrem := (others=>(others=>(others=>false)));
	for r in 1..9 loop
	for c in 1..9 loop
	for d in 1..9 loop
	if hiliteHT(r,c)(d) then
		for o in 1..9 loop
		if not hiliteHT(r,c)(o) and candidate(r,c)(o) then
			hiliteHTrem(r,c)(o):=true;
		end if;
		end loop; --o
	end if;
	end loop;
	end loop; --c
	end loop; --r



end setHTriples;




------- begin Quads -----------------------------------

-- find hidden Quads:
procedure hiddenQuads( rcd: cellseq; 
	t1,t2,t3,t4, dt1,dt2,dt3,dt4: out coorng; 
	found: out boolean) is separate;

procedure nakedQuads( rcd: cellseq; 
	t1, t2, t3, t4: out coorng; found: out boolean) is separate;


-- this prepares both hidden & naked quads:
procedure setQuads is
	rcd: cellseq;
	t1,t2,t3,t4, dt1,dt2,dt3,dt4: coorng;
	found: boolean;
	k,row,col: integer;

	--flag for naked block Quads:
	showbox: boolean := false; --set false to reduce clutter

begin

	hiliteHQ := (others=>(others=>(others=>false)));
	hiliteNQ := (others=>(others=>false));


	--RowQuads:
	for row in 1..9 loop
		for col in 1..9 loop
			rcd(col):=(row,col);
		end loop; --col

		hiddenQuads(rcd, t1,t2,t3,t4, dt1,dt2,dt3,dt4, found);
		if found and rmode then
			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt1) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt2) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt3) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt4) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt4) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt4) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt4) := true;

		end if;

		nakedQuads(rcd, t1,t2,t3,t4, found);
		if found then
			hiliteNQ( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNQ( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNQ( rcd(t3)(1), rcd(t3)(2) ) := true;
			hiliteNQ( rcd(t4)(1), rcd(t4)(2) ) := true;
		end if;


	end loop; --row




	--ColQuads:
	for col in 1..9 loop
		for row in 1..9 loop
			rcd(row):=(row,col);
		end loop; --col

		hiddenQuads(rcd, t1,t2,t3,t4, dt1,dt2,dt3,dt4, found);
		if found and cmode then
			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt1) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt2) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt3) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt4) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt4) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt4) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt4) := true;

		end if;

		nakedQuads(rcd, t1,t2,t3,t4, found);
		if found then
			hiliteNQ( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNQ( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNQ( rcd(t3)(1), rcd(t3)(2) ) := true;
			hiliteNQ( rcd(t4)(1), rcd(t4)(2) ) := true;
		end if;


	end loop; --col




	--BlockQuads:
	for blkrow in 0..2 loop
	for blkcol in 0..2 loop

		k:=0;
		for r in 0..2 loop
		for c in 0..2 loop
			k:=k+1;
			row:=blkrow*3+r+1;
			col:=blkcol*3+c+1;
			rcd(k):=(row,col);
		end loop; --c
		end loop; --r

		hiddenQuads(rcd, t1,t2,t3,t4, dt1,dt2,dt3,dt4, found);
		if found and bmode then
			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt1) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt2) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt3) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt4) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt4) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt4) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt4) := true;

		end if;

if showbox then

-- Attention: this causes unnecessary clutter...
-- naked quads in boxes is seldom useful!

		nakedQuads(rcd, t1,t2,t3,t4, found);
		if found then
			hiliteNQ( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNQ( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNQ( rcd(t3)(1), rcd(t3)(2) ) := true;
			hiliteNQ( rcd(t4)(1), rcd(t4)(2) ) := true;
		end if;

end if; --showbox


	end loop; --blkcol
	end loop; --blkrow




	--diagQuads:
		for dia in 1..9 loop
			rcd(dia):=(dia,dia);
		end loop; --dia

		hiddenQuads(rcd, t1,t2,t3,t4, dt1,dt2,dt3,dt4, found);
		if found and dmode then
			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt1) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt2) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt3) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt4) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt4) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt4) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt4) := true;

		end if;

		nakedQuads(rcd, t1,t2,t3,t4, found);
		if found then
			hiliteNQ( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNQ( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNQ( rcd(t3)(1), rcd(t3)(2) ) := true;
			hiliteNQ( rcd(t4)(1), rcd(t4)(2) ) := true;
		end if;






	--revDiagQuads:
		for rev in 1..9 loop
			rcd(rev):=(rev, 10-rev);
		end loop; --dia

		hiddenQuads(rcd, t1,t2,t3,t4, dt1,dt2,dt3,dt4, found);
		if found and dmode then
			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt1) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt1) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt1) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt1) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt2) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt2) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt2) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt2) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt3) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt3) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt3) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt3) := true;

			hiliteHQ( rcd(t1)(1), rcd(t1)(2) )(dt4) := true;
			hiliteHQ( rcd(t2)(1), rcd(t2)(2) )(dt4) := true;
			hiliteHQ( rcd(t3)(1), rcd(t3)(2) )(dt4) := true;
			hiliteHQ( rcd(t4)(1), rcd(t4)(2) )(dt4) := true;

		end if;

		nakedQuads(rcd, t1,t2,t3,t4, found);
		if found then
			hiliteNQ( rcd(t1)(1), rcd(t1)(2) ) := true;
			hiliteNQ( rcd(t2)(1), rcd(t2)(2) ) := true;
			hiliteNQ( rcd(t3)(1), rcd(t3)(2) ) := true;
			hiliteNQ( rcd(t4)(1), rcd(t4)(2) ) := true;
		end if;




	--ID removable cellmates of hidden quads
	hiliteHQrem := (others=>(others=>(others=>false)));
	for r in 1..9 loop
	for c in 1..9 loop
	for d in 1..9 loop
	if hiliteHQ(r,c)(d) then
		for o in 1..9 loop
		if not hiliteHQ(r,c)(o) and candidate(r,c)(o) then
			hiliteHQrem(r,c)(o):=true;
		end if;
		end loop; --o
	end if;
	end loop;
	end loop; --c
	end loop; --r





end setQuads;









------- end Quads -----------------------------------



procedure flushBlock(rr,cc:i9) is
	mm,br0,bc0,r0,c0: s9;
begin

mm:=m9(rr,cc);
if mm>0 then

	br0:= (rr-1)/3; --0..2
	bc0:= (cc-1)/3; --0..2
	r0 := br0*3; --0..6
	c0 := bc0*3; --0..6

	for r in r0+1..r0+3 loop
	for c in c0+1..c0+3 loop
	if r/=rr or c/=cc then

--tedious, but easily followed:
if showK and candidate(r,c)(mm) then
	append(consoleBuf," -");
	append(consoleBuf,i2st(mm));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(r));
	append(consoleBuf,",");
	append(consoleBuf,i2st(c));
	append(consoleBuf,")");

	hiliteKdel(r,c)(mm):=true;

end if;

		candidate(r,c)(mm):=false;
	end if;
	end loop; --c
	end loop; --r

end if;

end flushBlock;


procedure flushRow(rr,cc:i9) is
	mm: s9;
begin

mm:=m9(rr,cc);
if mm>0 then

	for c in i9 loop
		if c/=cc then

--tedious, but easily followed:
if showK and candidate(rr,c)(mm) then
	append(consoleBuf," -");
	append(consoleBuf,i2st(mm));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(rr));
	append(consoleBuf,",");
	append(consoleBuf,i2st(c));
	append(consoleBuf,")");

	hiliteKdel(rr,c)(mm):=true;

end if;

			candidate(rr,c)(mm):=false;
		end if;
	end loop;

end if;

end flushRow;


procedure flushCol(rr,cc:i9) is
	mm: s9;
begin

mm:=m9(rr,cc);
if mm>0 then

	for r in i9 loop
		if r/=rr then

--tedious, but easily followed:
if showK and candidate(r,cc)(mm) then
	append(consoleBuf," -");
	append(consoleBuf,i2st(mm));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(r));
	append(consoleBuf,",");
	append(consoleBuf,i2st(cc));
	append(consoleBuf,")");

	hiliteKdel(r,cc)(mm):=true;

end if;

			candidate(r,cc)(mm):=false;
		end if;
	end loop;

end if;

end flushCol;


procedure flushDiags(rr,cc:i9) is
	mm: s9;
begin

mm:=m9(rr,cc);
if mm>0 then

	if rr=cc then -- on main diag
	for i in i9 loop
	if i/=rr then

--tedious, but easily followed:
if showK and candidate(i,i)(mm) then
	append(consoleBuf," -");
	append(consoleBuf,i2st(mm));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(i));
	append(consoleBuf,",");
	append(consoleBuf,i2st(i));
	append(consoleBuf,")");

	hiliteKdel(i,i)(mm):=true;

end if;

		candidate(i,i)(mm):=false;
	end if;
	end loop;
	end if;

	if rr = 10-cc then -- on back diag
	for i in i9 loop
	if i/=rr then

--tedious, but easily followed:
if showK and candidate(i,10-i)(mm) then
	append(consoleBuf," -");
	append(consoleBuf,i2st(mm));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(i));
	append(consoleBuf,",");
	append(consoleBuf,i2st(10-i));
	append(consoleBuf,")");

	hiliteKdel(i,10-i)(mm):=true;

end if;

		candidate(i,10-i)(mm):=false;
	end if;
	end loop;
	end if;

end if;

end flushDiags;

-- the intent here is to pick a cell
-- with a single digit showing,
-- then eliminate dups in that row, col, block.
procedure flushDups(r,c: i9) is
	ii,k: integer := 0;
	autopromote: constant boolean := true;
begin

	if autopromote then -- not official until m9 is set:

		if m9(r,c)=0 then
			k:=0;
			for dig in 1..9 loop
			if candidate(r,c)(dig) then k:=k+1; ii:=dig; end if;
			end loop;
			if k=1 then -- ii is only candidate
				m9(r,c):=ii;
			end if;
		end if;

	end if;


	if m9(r,c)>0 then
		flushBlock(r,c);
		flushRow(r,c);
		flushCol(r,c);
		flushDiags(r,c);
	end if;

end flushDups;


procedure flush is
begin
	for row in 1..9 loop
	for col in 1..9 loop
		flushDups(row,col);
	end loop; --col
	end loop; --row
end flush;





procedure findUniqueCol( changed: out boolean ) is
	type sumtype is array(i9) of s9;
	sum: integer;
	rr: s9;
begin
changed:=false;
for cc in i9 loop -- for each column
	for dig in i9 loop -- for each digit...

		-- see if digit occurs only once in this column:

		sum:=0;
		for r in i9 loop
		if 
			m9(r,cc)=0 --not yet processed as singleton
			and 
			candidate(r,cc)(dig) 
		then
			sum:=sum+1;
			rr:=r;
		end if;
		end loop;

		if sum=1 then -- found a unique dig in col cc
			changed:=true;
			for j in i9 loop -- so eliminate all other candidates in same cell
			if j/=dig then
				candidate(rr,cc)(j):=false;
			end if;
			end loop; --j
			m9(rr,cc):=dig; --define the singleton

-- somewhat helpful:
if showK then
	append(consoleBuf, crlf);
	append(consoleBuf," +");
	append(consoleBuf,i2st(dig));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(rr));
	append(consoleBuf,",");
	append(consoleBuf,i2st(cc));
	append(consoleBuf,"):");
end if;

			flushDups(rr,cc); --eliminate dig from 3/4 houses
		end if;
	end loop;
end loop;
end findUniqueCol;


procedure findUniqueRow( changed: out boolean ) is
	type sumtype is array(i9) of s9;
	sum: integer;
	cc: s9;
begin
changed:=false;
for rr in i9 loop
	for dig in i9 loop

		-- see if dig occurs only once in this row:

		sum:=0;
		for c in i9 loop
		if 
			m9(rr,c)=0 --not yet processed as singleton
			and 
			candidate(rr,c)(dig) 
		then
			sum:=sum+1;
			cc:=c;
		end if;
		end loop; --c
		if sum=1 then -- found a unique dig in row rr
			changed:=true;
			for j in i9 loop -- so eliminate all other candidates in same cell
			if j/=dig then
				candidate(rr,cc)(j):=false;
			end if;
			end loop; --j
			m9(rr,cc):=dig;

-- somewhat helpful:
if showK then
	append(consoleBuf, crlf);
	append(consoleBuf," +");
	append(consoleBuf,i2st(dig));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(rr));
	append(consoleBuf,",");
	append(consoleBuf,i2st(cc));
	append(consoleBuf,"):");
end if;

			flushDups(rr,cc);
		end if;
	end loop; --i
end loop;
end findUniqueRow;




procedure findUniqueBlock(changed: out boolean) is
	type sumtype is array(i9) of s9;
	sum: integer;
	rr,cc, row,col: s9;
begin
changed:=false;
for br in 0..2 loop
for bc in 0..2 loop -- for each block (1 of 9)

	for dig in i9 loop -- for each possible digit 1..9

		-- see if dig occurs only once in this box:

		sum:=0;
		for r in 0..2 loop
		for c in 0..2 loop -- for each cell in block (1 of 9)
			row:=br*3+r+1;
			col:=bc*3+c+1;
			if 
				m9(row,col)=0 -- not yet processed as singleton
				and
				candidate(row,col)(dig)
			then
				sum:=sum+1;
				rr:=row; cc:=col;
			end if;
		end loop; --c
		end loop; --r

		if sum=1 then
		-- found the only cell in the block that contains dig
		-- ...so eliminate other candidates in this cell:
			changed:=true;
			for j in i9 loop
			if j/=dig then
				candidate(rr,cc)(j):=false;
			end if;
			end loop; --j
			m9(rr,cc):=dig; -- define the singleton

-- somewhat helpful:
if showK then
	append(consoleBuf, crlf);
	append(consoleBuf," +");
	append(consoleBuf,i2st(dig));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(rr));
	append(consoleBuf,",");
	append(consoleBuf,i2st(cc));
	append(consoleBuf,"):");
end if;

			flushDups(rr,cc); -- eliminate this digit from its 3 houses
		end if;

	end loop; --d

end loop; --bc
end loop; --br

end findUniqueBlock;


procedure findUniqueMainDiag(changed: out boolean) is
	type sumtype is array(i9) of s9;
	sum: integer;
	rr,cc: s9;
begin
	changed:=false;
	for dig in i9 loop

		-- see if dig occurs only once along main diag:

		sum:=0;
		for rc in i9 loop
			if 
				m9(rc,rc)=0 --not yet processed as singleton
				and 
				candidate(rc,rc)(dig) 
			then
				sum:=sum+1;
				rr:=rc; cc:=rc;
			end if;
		end loop; --rc

		if sum=1 then -- found a unique dig on main diag @ (rr,cc)
			changed:=true;
			for j in i9 loop -- so eliminate all other candidates in same cell
			if j/=dig then
				candidate(rr,cc)(j):=false;
			end if;
			end loop; --j
			m9(rr,cc):=dig;

-- somewhat helpful:
if showK then
	append(consoleBuf, crlf);
	append(consoleBuf," +");
	append(consoleBuf,i2st(dig));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(rr));
	append(consoleBuf,",");
	append(consoleBuf,i2st(cc));
	append(consoleBuf,"):");
end if;

			flushDups(rr,cc);
		end if;

	end loop; --i

end findUniqueMainDiag;



procedure findUniqueBackDiag(changed: out boolean) is
	type sumtype is array(i9) of s9;
	sum: integer;
	rr,cc: s9;
begin
	changed:=false;
	for dig in i9 loop

		sum:=0;
		for rc in i9 loop
			if 
				m9(rc,10-rc)=0 --not yet processed as singleton
				and 
				candidate(rc,10-rc)(dig) 
			then
				sum:=sum+1;
				rr:=rc; cc:=10-rc;
			end if;
		end loop; --rc

		if sum=1 then -- found a unique dig on back diag [@(rr,cc)]
			changed:=true;
			for j in i9 loop -- so eliminate all other candidates in same cell
			if j/=dig then
				candidate(rr,cc)(j):=false;
			end if;
			end loop; --j
			m9(rr,cc):=dig;

-- somewhat helpful:
if showK then
	append(consoleBuf, crlf);
	append(consoleBuf," +");
	append(consoleBuf,i2st(dig));
	append(consoleBuf,"@(");
	append(consoleBuf,i2st(rr));
	append(consoleBuf,",");
	append(consoleBuf,i2st(cc));
	append(consoleBuf,"):");
end if;

			flushDups(rr,cc);
		end if;

	end loop; --dig

end findUniqueBackDiag;


procedure findUnique(changed: out boolean) is
	chB,chF,chR,chC,chX: boolean;
begin

	flush; --14aug23 addendum...
	--this will autopromote/flush any single candidates

	findUniqueBackDiag(chB);
	findUniqueMainDiag(chF);
	findUniqueRow(chR);
	findUniqueCol(chC);
	findUniqueBlock(chX);
	changed := chB or chF or chR or chC or chX;
end findUnique;


--forward declarations:
procedure fullsavepuzzle( usetemp: boolean := false );
function fullreadpuzzle( usetemp: boolean := false ) return boolean; 
-- now sets fnamUstr



badig: s9; --output from kram
type badcelltype is array(1..18) of ocoords;
badcells: badcelltype;
nbad: s9;

procedure kram( c0: in coords; 
	bad: out s9; badsell: out badcelltype; numbad: out s9;
	forceoutput: in boolean := false
	) is separate;


----------------------------------------------------



	package fmath is new
			Ada.Numerics.generic_elementary_functions( float );
	use fmath;


	package myint_io is new text_io.integer_io(integer);
	package myfloat_io is new text_io.float_io(float);

	type vec3 is array(1..3) of float;


	onepi : constant float     := 3.14159_26535_89793;
	halfpi : constant float    := onepi/2.0;
	fourthpi : constant float  := onepi/4.0;
	twopi : constant float     := onepi*2.0;
	deg2rad : constant float   := onepi/180.0;
	rad2deg : constant float   := 180.0/onepi;

	userexit: boolean := false;

	--fanfare : glint;

	MVP, ModelMatrix, ViewMatrix, ProjectionMatrix
			: mat44 := identity;







procedure myassert( condition : boolean;  flag: integer:=0 ) is
begin
  if condition=false then
  		put("ASSERTION Failed!  ");
		if flag /= 0 then
			put_line( "@ " & integer'image(flag) );
		end if;
		new_line;
  		raise program_error;
  end if;
end myassert;










procedure InitGlfw( wid, hit, fwd,fht : in out glint; name: string ) is

	use system;

	title : interfaces.c.strings.chars_ptr := new_string(name&ascii.nul);

	maj,min,rev : aliased glint;

	awidth:  aliased glint := wid;
	aheight: aliased glint := hit;

	azero : aliased glint := 0;

	axs, ays : aliased float;
	awwid,awhit, afwid, afhit : aliased glint;


begin

	--put_line("...using fastrgv's Ada Binding to GLFW3...");

	GlfwGetVersion(maj'access,min'access,rev'access); --naturals
	--put("GLFW ver: ");
	--put(glint'image(maj));
	--put(":"&glint'image(min));
	--put(":"&glint'image(rev));
	--New_Line;



	if GlfwInit /= gl_true then
		new_line;
		put_line("glfwInit failed");
		raise program_error;
	end if;

	-- use version here that your graphics card would support:
	GlfwWindowHint( glfw_context_version_major, 3);
	GlfwWindowHint( glfw_context_version_minor, 3);
	GlfwWindowHint( glfw_opengl_forward_compat, gl_true);
	GlfwWindowHint( glfw_opengl_profile, glfw_opengl_core_profile);

	GlfwWindowHint( glfw_samples, 4);
	GlfwWindowHint( glfw_client_api, glfw_opengl_api);



	declare
		vmode: access constant glfwVidMode 
			:= glfwGetVideoMode(glfwGetPrimaryMonitor);
	begin

		--get current video mode size [full screen]
		mwid:=vmode.width;
		mhit:=vmode.height;

	end;



	mainWindow := glfwcreatewindow(
		wid, hit,	title, null, null );


		

	if mainWindow = null then
		new_line;
		put_line("glfwCreateWindow failed");
		raise program_error;
	end if;

	glfwmakecontextcurrent( mainWindow );


--HiDpi queries:
	glfwGetWindowSize(mainWindow, awwid'access, awhit'access);
	glfwGetFramebufferSize(mainWindow, afwid'access,afhit'access);
	glfwGetWindowContentScale(mainWindow, axs'access,ays'access);

	--put_line("HighDpi Queries:");
	--put_line("WindowSize: "&glint'image(awwid)&","&glint'image(awhit));
	--put_line("FrameBuffer: "&glint'image(afwid)&","&glint'image(afhit));
	--put_line("Scale: "&float'image(axs)&","&float'image(ays));

	fwd:=afwid;
	fht:=afhit;
	wid:=awwid;
	hit:=awhit;


end InitGlfw;













procedure updateMVP is -- only call once unless change in aspect ratio

	xlook, ylook, zlook, xlk,ylk,zlk, xrt,yrt,zrt, xup,yup,zup : float;
	xme,yme,zme : float;

	fovdeg : constant float := 45.0;
	fovrad : constant float := fovdeg*deg2rad;


	-- distance from eye so FOV encompasses proper field:
	eyeradius : constant float := 1.0 / fmath.tan(fovrad/2.0);

	horiAng : constant float := 0.0;
	near : constant float := 0.1;
	far  : constant float := 100.0;

	focus : constant vec3 := (0.0, -1.0, 0.0);
	eyepos: constant vec3 := (0.0, eyeradius-1.0 , 0.0);
	look  : constant vec3 := 
		( focus(1)-eyepos(1), focus(2)-eyepos(2), focus(3)-eyepos(3) );
	vertAng : constant float := fmath.arctan( look(2), look(3) );

begin

	ModelMatrix:=identity;

	xme:=eyepos(1);
	yme:=eyepos(2);
	zme:=eyepos(3);

	-- look direction:
	xlook := fmath.cos(vertang)*fmath.sin(horiang);
	ylook := fmath.sin(vertang);
	zlook := fmath.cos(vertang)*fmath.cos(horiang);

	xlk := xme+xlook;
	ylk := yme+ylook;
	zlk := zme+zlook;

	-- Right unit-Direction
	xrt:= fmath.sin(horiang-halfpi);
	yrt:= 0.0;
	zrt:= fmath.cos(horiang-halfpi);

	-- calculate UP unit-Direction
	cross( xrt,yrt,zrt, xlook,ylook,zlook, xup,yup,zup );

	perspective(ProjectionMatrix, fovdeg, 1.0,  near, far);

	lookat(ViewMatrix, xme,yme,zme, xlk,ylk,zlk, xup,yup,zup );

	MVP:=ModelMatrix;
	matXmat(MVP,ViewMatrix);
	matXmat(MVP,ProjectionMatrix);

end updateMVP;








fnamUstr: unbounded_string;



xize : integer := 80;


procedure restart( resize: boolean := true ) is
	tstr: string := to_string( fnamUstr );

	cptr : chars_ptr := 
		  new_string("Sudoku-X-Tool:  |"&tstr&"|  <h>:Help   <esc>:Quit   <f>:Flush   <u>:Unique"); --12oct21
	margin: constant glint := 200;
begin

	glfwsetwindowtitle(mainwindow, cptr);

	Wwid:=glint(xize*ncols);
	Whit:=glint(xize*nrows);


	if resize  then

		--put_line("new wid X hit X xize:");
		--put(glint'image(wwid));
		--put(" X");
		--put(glint'image(whit));
		--	put(" X");
		--	put(glint'image( glint(xize) ));
		--new_line;

		if Wwid>Mwid-margin or Whit>Mhit-margin then

			while Wwid>Mwid-margin or Whit>Mhit-margin loop
				xize:=xize-10;
				put_line("reducing xize");
				Wwid:=glint(xize*ncols);
				Whit:=glint(xize*nrows);
			end loop;

			--put_line(" resizing xize="&integer'image(xize));

			--put_line("new wid X hit X xize:");
			--put(glint'image(wwid));
			--put(" X");
			--put(glint'image(whit));
			--new_line;

		end if;

	end if; --resize


	--28may19
	Wwid:=glint(xize*ncols);
	Whit:=glint(xize*nrows);
	glfwSetWindowSize(mainWindow,Wwid,Whit);
	glfwGetFramebufferSize(mainwindow, fwid'access, fhit'access);

	glViewport(0,0,Fwid,Fhit);


end restart;








procedure release_textures is -- prepare to close down
begin

	glext.binding.glDeleteBuffers(1, vertbuff'address);
	glext.binding.glDeleteBuffers(1, rgbbuff'address);
	glext.binding.glDeleteBuffers(1, uvbuff'address);
	glext.binding.glDeleteBuffers(1, elembuff'address);

	gldeletetextures(1, floor_texid'address);
	gldeletetextures(1, space_texid'address);
	gldeletetextures(1, box_texid'address);

	gldeletetextures(1, lgrey_texid'address);
	gldeletetextures(1, gray_texid'address);
	gldeletetextures(1, black_texid'address);
	gldeletetextures(1, red_texid'address);
	gldeletetextures(1, blu_texid'address);

	glext.binding.glDeleteProgram( picttexshadid );

	glext.binding.glDeleteVertexArrays(1, vertexarrayid'address);

end release_textures;



procedure setSkin( cleanup: boolean := false ) is
begin

	gray_texid:= loadPng(mirror,"skins/gray.png");
	--lgrey_texid:= loadPng(mirror,"skins/ltgrey2.png"); --need lighter than this
	lgrey_texid:= loadPng(mirror,"skins/ltgrey3.png");

	--floor_texid:=loadPng(mirror,"skins/hatch0.png"); --white/gray
	floor_texid:=loadPng(mirror,"skins/hatch0b2.png"); --white/gray
	space_texid:=loadPng(mirror,"skins/blank.png");
	black_texid:=loadPng(mirror,"skins/black.png");
	box_texid:=loadPng(mirror,"skins/box2.png");
	red_texid:=loadPng(mirror,"skins/red.png");
	blu_texid:=loadPng(mirror,"skins/blu.png");

end setSkin;


procedure setup_textures is  -- prepare dungeon textures
begin 

	picttexshadid := loadshaders("./data/texobj.vs", "./data/texobj.fs");
	matrixid := glgetuniformlocation(picttexshadid, pmvp);
	uniftex  := glgetuniformlocation(picttexshadid, pmyts);

	setSkin;

end setup_textures;









procedure first_prep is -- main program setup
      FileId : text_io.File_Type;
begin



------- begin GLFW prep ---------------------------------------------------

	Wwid:=500;
	Whit:=500;
	--put_line("setting 500 x 500");

	InitGlfw(Wwid, Whit, Fwid,Fhit, "AdaSokoban");



	purtex.inittext2d("data/sansp.png",integer(Wwid),integer(Whit));
	cyatex.inittext2d("data/sansc.png",integer(Wwid),integer(Whit));

	orntex.inittext2d("data/sanso.png",integer(Wwid),integer(Whit));
	yebtex.inittext2d("data/sansy2.png",integer(Wwid),integer(Whit));
	grntex.inittext2d("data/sansg.png",integer(Wwid),integer(Whit));
	redtex.inittext2d("data/sansr.png",integer(Wwid),integer(Whit));
	magtex.inittext2d("data/sansm.png",integer(Wwid),integer(Whit));

	blutex.inittext2d("data/sansb0.png",integer(Wwid),integer(Whit));

	whitex.inittext2d("data/sansw.png",integer(Wwid),integer(Whit));

	blktex.inittext2d("data/sansk.png",integer(Wwid),integer(Whit));
	grytex.inittext2d("data/sansgrey30.png",integer(Wwid),integer(Whit));
	--ltgtex.inittext2d("data/sansgrey60.png",integer(Wwid),integer(Whit));
	ltgtex.inittext2d("data/sansgrey80.png",integer(Wwid),integer(Whit));

	glViewport(0,0,Fwid,Fhit);



	glgenvertexarrays(1, vertexarrayid'address );
	glbindvertexarray(vertexarrayid);
	glactivetexture(gl_texture0);

	glgenbuffers(1, vertbuff'address);
	glgenbuffers(1, rgbbuff'address);
	glgenbuffers(1, uvbuff'address);
	glgenbuffers(1, elembuff'address);



	--setup_textures; ...later

	glClearColor(0.4,0.4,0.4,1.0);

	glenable(gl_depth_test);
	gldepthfunc( gl_lequal );
	glenable( gl_cull_face );


	glEnable(GL_MULTISAMPLE);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);


end first_prep;






--3 utilities for next proc: --------------------------------

function nBox(row,col: in i9) return i9 is
begin
	return ((row-1)/3) * 3 + ((col-1)/3) +1;
end nBox;

function r0Box( nb: in i9 ) return s9 is
begin
	if nb in 1..3 then
		return 0;
	elsif nb in 4..6 then
		return 3;
	else
		return 6;
	end if;
end r0Box;


function c0Box( nb: in i9 ) return s9 is
begin
	if nb=1 or nb=4 or nb=7 then
		return 0;
	elsif nb=2 or nb=5 or nb=8 then
		return 3;
	else
		return 6;
	end if;
end c0Box;








-- identify isolated pairs/triples on rows/cols
-- set hiliteRC array accordingly
-- set rowPair( row#, col# ) := paired-col# (0=>none)
-- set colPair( col#, row# ) := paired-row# (0=>none)
procedure id_pairsRC is
	k : integer;
	nb1,nb2: i9;
	br0,bc0, br,bc: s9;
	row,col : array(1..2) of integer;
	rowrem, colrem : boolean := false;
begin

	rowPair  := (others=>(others=>(others=>0)));
	colPair  := (others=>(others=>(others=>0)));
	hiliteRow := (others=>(others=>(others=>false)));
	hiliteCol := (others=>(others=>(others=>false)));

	hiliteRowRem := (others=>(others=>(others=>false)));
	hiliteColRem := (others=>(others=>(others=>false)));


	--rows
	for r in 1..9 loop
	for idig in 1..9 loop
		k:=0;
		for c in 1..9 loop
			if candidate(r,c)(idig) then 
				k:=k+1;
				if k<=2 then
					row(k):=r;
					col(k):=c;
				end if;
			end if;
		end loop; --diag
		if k=2 then
			for d in 1..k loop
				hiliteRow(row(d),col(d))( idig ):=true;
			end loop; --d
			rowPair(idig,r,col(1)):=col(2);
			rowPair(idig,r,col(2)):=col(1);

			--if this pair share a box, flag any like digits within box
			-- here, row(1)=row(2)=r
			nb1:=nBox(row(1),col(1));
			nb2:=nBox(row(2),col(2));
			if nb1=nb2 then -- test for any removable digits
				br0:=r0Box(nb1);
				bc0:=c0Box(nb1);
				for rb in 0..2 loop
				for cb in 0..2 loop
					br:=br0+rb+1;
					bc:=bc0+cb+1;
					if br=r and (bc=col(1) or bc=col(2)) then
						null;
					elsif candidate(br,bc)(idig) then
						hiliteRowRem(br,bc)(idig):=true;
						rowrem:=true;
					end if;
				end loop; --cb
				end loop; --rb
			end if;


		end if;
	end loop; --idig
	end loop; --r


	--cols
	for c in 1..9 loop
	for idig in 1..9 loop
		k:=0;
		for r in 1..9 loop
			if candidate(r,c)(idig) then 
				k:=k+1;
				if k<=2 then
					row(k):=r;
					col(k):=c;
				end if;
			end if;
		end loop; --diag
		if k=2 then
			for d in 1..k loop
				hiliteCol(row(d),col(d))( idig ):=true;
			end loop; --d
			colPair(idig,c,row(1)):=row(2);
			colPair(idig,c,row(2)):=row(1);

			--if this pair share a box, flag any like digits within box
			-- here, col(1)=col(2)=c
			nb1:=nBox(row(1),col(1));
			nb2:=nBox(row(2),col(2));
			if nb1=nb2 then -- test for any removable digits
				br0:=r0Box(nb1);
				bc0:=c0Box(nb1);
				for rb in 0..2 loop
				for cb in 0..2 loop
					br:=br0+rb+1;
					bc:=bc0+cb+1;
					if bc=c and (br=row(1) or br=row(2)) then
						null;
					elsif candidate(br,bc)(idig) then
						hiliteColRem(br,bc)(idig):=true;
						colrem:=true;
					end if;
				end loop; --cb
				end loop; --rb
			end if;


		end if;
	end loop; --idig
	end loop; --c


end id_pairsRC;



function onFdiag(row,col: i9) return boolean is
begin
	if col=row then
		return true;
	else
		return false;
	end if;
end onFdiag;

function onBdiag(row,col: i9) return boolean is
begin
	if col=10-row then
		return true;
	else
		return false;
	end if;
end onBdiag;


-- identify isolated pairs within blocks;
-- set hiliteBP array accordingly.
-- set BoxPair( box#, cell# ) := paired-cell# (0=>none)

--BoxPair : array(i9,i9) of s9;

procedure id_pairsBP is 
	tot: integer;

	r1,c1, r2,c2, r3,c3,
	boxnum, cell1, cell2: i9;
	rr, cc : array(0..2) of integer;
	boxrem: boolean := false;
begin

	BoxPair  := (others=>(others=>(others=>0)));
	hiliteBP := (others=>(others=>(others=>false)));
	hiliteBPna := (others=>(others=>(others=>false)));
	hiliteBoxRem := (others=>(others=>(others=>false)));



	for brow in 0..2 loop
	for bcol in 0..2 loop
	boxnum:=brow*3+bcol+1;
	--process each of 9 blocks:
	for idig in 1..9 loop
	--check idig

		tot:=0;
		for r in 0..2 loop
		for c in 0..2 loop
		--cellnum:=r*3+c+1;
			if candidate( brow*3+r+1, bcol*3+c+1 )( idig ) then
				if tot<=2 then
					rr(tot):=r; cc(tot):=c;
				end if;
				tot:=tot+1;
			end if;
		end loop;
		end loop;

		if tot=2 then
			r1:=brow*3+rr(0)+1; c1:=bcol*3+cc(0)+1;
			r2:=brow*3+rr(1)+1; c2:=bcol*3+cc(1)+1;
		end if;

		--if tot=2 then --allows non-aligned
		if tot=2 and ( rr(0)=rr(1) or cc(0)=cc(1) ) then --only aligned pairs

			cell1 := rr(0)*3+cc(0)+1;
			cell2 := rr(1)*3+cc(1)+1;
			BoxPair(idig,boxnum,cell1):=cell2;
			BoxPair(idig,boxnum,cell2):=cell1;

			hiliteBP(r1,c1)(idig):=true;
			hiliteBP(r2,c2)(idig):=true;

			--flag any removables aligned with this pair
			if r1=r2 then --check all cols this row
				for co in 1..9 loop
					if co=c1 or co=c2 then
						null;
					elsif candidate(r1,co)(idig) then
						hiliteBoxRem(r1,co)(idig):=true;
						boxrem:=true;
					end if;
				end loop;

			elsif c1=c2 then --check all rows this col
				for rw in 1..9 loop
					if rw=r1 or rw=r2 then
						null;
					elsif candidate(rw,c1)(idig) then
						hiliteBoxRem(rw,c1)(idig):=true;
						boxrem:=true;
					end if;
				end loop;
			else
				raise program_error;
			end if;



		-- Extra test for X-sudoku: aligned along Forward diagonal
		elsif tot=2 and  onFdiag(r1,c1) and onFdiag(r2,c2) then

			hiliteBP(r1, c1)(idig):=true;
			hiliteBP(r2, c2)(idig):=true;

			--now test fdiag for other "idig":
			for dd in 1..9 loop
				if dd=r1 or dd=r2 then
					null;
				elsif candidate(dd,dd)(idig) then
						hiliteBoxRem(dd,dd)(idig):=true;
						boxrem:=true;
				end if;
			end loop;


		-- Extra test for X-sudoku: aligned along Back diagonal
		elsif tot=2 and  onBdiag(r1,c1) and onBdiag(r2,c2) then

			hiliteBP(r1, c1)(idig):=true;
			hiliteBP(r2, c2)(idig):=true;

			--now test bdiag for other "idig":
			for dd in 1..9 loop
				if dd=r1 or dd=r2 then
					null;
				elsif candidate(dd,10-dd)(idig) then
						hiliteBoxRem(dd,10-dd)(idig):=true;
						boxrem:=true;
				end if;
			end loop;


		elsif tot=2 then -- only NonAligned Pairs

			hiliteBPna(brow*3+rr(0)+1, bcol*3+cc(0)+1)(idig):=true;
			hiliteBPna(brow*3+rr(1)+1, bcol*3+cc(1)+1)(idig):=true;


-- addendum to capture aligned digit triples 25jul23:

		elsif tot=3 and ( ( rr(0)=rr(1) and rr(0)=rr(2) ) or ( cc(0)=cc(1) and cc(0)=cc(2) ) ) then

			r1:=brow*3+rr(0)+1; c1:=bcol*3+cc(0)+1;
			r2:=brow*3+rr(1)+1; c2:=bcol*3+cc(1)+1;
			r3:=brow*3+rr(2)+1; c3:=bcol*3+cc(2)+1;

			hiliteBP(r1,c1)(idig):=true;
			hiliteBP(r2,c2)(idig):=true;
			hiliteBP(r3,c3)(idig):=true;

			--flag any removables aligned with this pointing-triple
			if r1=r2 then --check all cols this row
				for co in 1..9 loop
					if co=c1 or co=c2 or co=c3 then
						null;
					elsif candidate(r1,co)(idig) then
						hiliteBoxRem(r1,co)(idig):=true;
						boxrem:=true;
					end if;
				end loop;

			elsif c1=c2 then --check all rows this col
				for rw in 1..9 loop
					if rw=r1 or rw=r2 or rw=r3 then
						null;
					elsif candidate(rw,c1)(idig) then
						hiliteBoxRem(rw,c1)(idig):=true;
						boxrem:=true;
					end if;
				end loop;
			else
				raise program_error;
			end if;


		end if;

	end loop; --idig

	end loop; --bcol
	end loop; --brow



end id_pairsBP;


-- addendum 3may23
-- identify isolated pairs on both diagonals
-- set hiliteD array accordingly
procedure id_pairsD is
	i : array(1..3) of integer;
	k, row, col: integer;

	nb1,nb2: i9;

	br0,bc0, br,bc: s9;
begin

	fdPair  := (others=>(others=>0));
	bdPair  := (others=>(others=>0));

	hiliteD := (others=>(others=>(others=>false)));
	hiliteDiagRem := (others=>(others=>(others=>false)));

	--Forward Diag
	for idig in 1..9 loop
		k:=0;
		for diag in 1..9 loop
			row:=diag; col:=diag;
			if candidate(row,col)(idig) then 
				k:=k+1;
				if k<=3 then
					i(k):=diag;
				end if;
			end if;
		end loop; --diag
		--if k<=3 then -- 9may23 eliminate triples
		if k=2 then
		for d in 1..k loop
			hiliteD( i(d), i(d) )( idig ):=true;
		end loop; --d
		fdPair( idig, i(1) ) := i(2);
		fdPair( idig, i(2) ) := i(1);

			--IF pair resides in same box, mark other box idig as removable 29jun23

			nb1:=nBox(i(1),i(1));
			nb2:=nBox(i(2),i(2));
			if nb1=nb2 then -- in same box

				-- test for any removable digits
				br0:=r0Box(nb1);
				bc0:=c0Box(nb1);
				for rb in 0..2 loop
				for cb in 0..2 loop
					br:=br0+rb+1;
					bc:=bc0+cb+1;
					if 
						(br=i(1) and bc=i(1))
							or
						(br=i(2) and bc=i(2)) 
					then
						null;
					elsif candidate(br,bc)(idig) then
						hiliteDiagRem(br,bc)(idig):=true;
					end if;
				end loop; --cb
				end loop; --rb


			end if; --nb1=nb2

		end if;

	end loop; --idig


	--Reverse Diag
	for idig in 1..9 loop
		k:=0;
		for diag in 1..9 loop
			row:=diag; col:=10-diag;
			if candidate(row,col)(idig) then 
				k:=k+1;
				if k<=3 then
					i(k):=diag;
				end if;
			end if;
		end loop; --diag
		--if k<=3 then -- 9may23 eliminate triples
		if k=2 then
		for d in 1..k loop
			hiliteD( i(d), 10-i(d) )( idig ):=true;
		end loop; --j
		bdPair( idig, i(1) ) := i(2);
		bdPair( idig, i(2) ) := i(1);

			--IF pair resides in same box, mark other box idig as removable 29jun23

			nb1:=nBox(i(1),10-i(1));
			nb2:=nBox(i(2),10-i(2));
			if nb1=nb2 then --in same box

				-- First, test for any removable digits
				br0:=r0Box(nb1);
				bc0:=c0Box(nb1);
				for rb in 0..2 loop
				for cb in 0..2 loop
					br:=br0+rb+1;
					bc:=bc0+cb+1;
					if 
						(br=i(1) and bc=10-i(1))
							or
						(br=i(2) and bc=10-i(2)) 
					then
						null;
					elsif candidate(br,bc)(idig) then
						hiliteDiagRem(br,bc)(idig):=true;
					end if;
				end loop; --cb
				end loop; --rb


			end if; --nb1=nb2

		end if;

	end loop; --idig

end id_pairsD;















-- intent is to identify digits that only appear once in a 3x3 block
procedure id_1 is 
	tot: integer;
	rr, cc, row,col :integer;
begin

	hilite1 := (others=>(others=>(others=>false)));

	for brow in 0..2 loop
	for bcol in 0..2 loop
	--process each of 9 blocks:

		for idig in 1..9 loop

			tot:=0;
			for r in 0..2 loop
			for c in 0..2 loop
				row:=brow*3+r+1;
				col:=bcol*3+c+1;
				if candidate(row,col)(idig) and m9(row,col)=0 then
					rr:=r; cc:=c;
					tot:=tot+1;
				end if;
			end loop;
			end loop;
			if tot=1 then 
				hilite1(brow*3+rr+1, bcol*3+cc+1)(idig):=true;
			end if;

		end loop; --idig

	end loop; --bcol
	end loop; --brow


end id_1;




procedure id_3 is --ID digits unique in one of its houses
	tot: integer;
	rr, cc :integer;
begin

	hilite3 := hilite1; --must call id_1 first!

	for r in 1..9 loop -- check each row
		for idig in 1..9 loop
			tot:=0;
			for c in 1..9 loop
				if candidate(r,c)(idig) and m9(r,c)=0 then
					rr:=r; cc:=c; tot:=tot+1;
				end if;
			end loop; --c
			if tot=1 then -- unique to this row
				hilite3(rr,cc)(idig):=true;
			end if;
		end loop; --idig
	end loop; --r


	for c in 1..9 loop -- check each col
		for idig in 1..9 loop
			tot:=0;
			for r in 1..9 loop
				if candidate(r,c)(idig) and m9(r,c)=0 then
					rr:=r; cc:=c; tot:=tot+1;
				end if;
			end loop; --c
			if tot=1 then -- unique to this col
				hilite3(rr,cc)(idig):=true;
			end if;
		end loop; --idig
	end loop; --r



	for idig in 1..9 loop -- check Fdiag
		tot:=0;
		for d in 1..9 loop
			if candidate(d,d)(idig) and m9(d,d)=0 then
				rr:=d; cc:=d; tot:=tot+1;
			end if;
		end loop; --d
		if tot=1 then -- unique to this diag
			hilite3(rr,cc)(idig):=true;
		end if;
	end loop; --idig


	for idig in 1..9 loop -- check Bdiag
		tot:=0;
		for d in 1..9 loop
			if candidate(d,10-d)(idig) and m9(d,10-d)=0 then
				rr:=d; cc:=10-d; tot:=tot+1;
			end if;
		end loop; --d
		if tot=1 then -- unique to this diag
			hilite3(rr,cc)(idig):=true;
		end if;
	end loop; --idig


end id_3;










procedure LinkedPairs is separate;






procedure Draw is

	cc,rr, c01,r01, c01a, r01a, left: float;

	shrink : constant float := 1.0; --0.99;
	dv: constant float := shrink/float(nrows);
	dh: constant float := shrink/float(ncols);

	yy: constant float := -0.99; --dist perp to horz/vert
	dy: constant float :=  0.0001; --thickness perp to h/v

	dr: constant float := dv;
	dc: constant float := dh;

	drr: constant float := dr/8.0;
	dcc: constant float := dc/8.0;

	orr, occ,
	orrul,orrur,orrll,orrlr,
	occul,occur,occll,occlr, ddhh, ddvv,

	j1,j2,j3,j4,j5,j6: float;


	tinyfont : constant integer := 8;
	lgfont : constant integer := 22;

	szfont, tot: integer;
	d: integer; --digit to display 1..9

	badmatch, Ymatch, Xmatch, oXmatch: boolean := false;

begin

	id_1;
	id_3; --all singles

	id_pairsRC; -- isolated pairs aligned in rows or cols (WAS after D)

	id_pairsBP; -- pairs within boxes; corner/PMs augmented
	id_pairsD; -- identify isolated pairs on main diagonals; corner/PMs augmented

	--experimental:
	setQuads;
	setHpairs;
	setNpairs;
	setHtriples;
	setNtriples;




	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);



	if help  then

		szfont := 12;
		left:=0.05;

		blktex.print2d("Sudoku-X Key Menu",         0.2, 0.99, szfont);

		blktex.print2d("mouse-click   cell-select/Deselect", left, 0.95, szfont);

		blktex.print2d("1-9  toggle num   <ctrl>1-9  Assert num",   left, 0.90, szfont);

		blktex.print2d("<esc> quit",             left, 0.85, szfont);


		blktex.print2d("<a/n> toggle Aligned/Non: Box-Dig-Dbls",left,0.80,szfont);


		blktex.print2d("<h>   toggle this help screen",  left, 0.75, szfont);


		blktex.print2d("<f>   Flush (all singles/selected)", left, 0.70, szfont);

		blktex.print2d("<u>   process Unique singles", left, 0.65, szfont);


		blktex.print2d("<m>   toggle standard-Minimal", left, 0.60, szfont);


		blktex.print2d("<d>  DigDbls: 1)X 2)B 3)R 4)C 5)off", left, 0.55, szfont);


		blktex.print2d("<p>  Pairs: 1234)hidden 5)naked 6)off", left, 0.50, szfont);

		blktex.print2d("<t>  Tripls: 1234)hidden 5)naked 6)off", left, 0.45, szfont);
		blktex.print2d("<q>  Quads: 1234)hidden 5)naked 6)off", left, 0.40, szfont);

		blktex.print2d("<l>   Linked pairs: next-chain", left, 0.35, szfont);

		blktex.print2d("<x>   toggle Xwing", left, 0.31, szfont);

		blktex.print2d("<y>   toggle Ywing", left, 0.27, szfont);
		blktex.print2d("<c>   toggle x-Cycle", left, 0.23, szfont);


		blktex.print2d("<k>   toggle KeyCell (all or selected)", left, 0.19, szfont);


		blktex.print2d("<b>   Brute-Force Solve", left, 0.15, szfont);


		blktex.print2d("<s>   Save current state", left, 0.10, szfont);
		blktex.print2d("<r>   Restore state", left, 0.05, szfont);
		blktex.print2d("<h>   Toggle this key menu", left, 0.00, szfont);

	else -- not help






	-- begin draw puzzle--------------------

		glUseProgram(pictTexShadID);
		glUniformMatrix4fv(MatrixID, 1, GL_FALSE, MVP(1,1)'address);
		glUniform1i(uniftex,0);


		cc := +1.0 - 2.0*(0.5*float(ncols+1)-0.5)*dh;
		rr := +1.0 - 2.0*(0.5*float(nrows+1)-0.5)*dv;
		pictobj.setrect(opict, cc,-1.0,rr,1.0,dy,1.0,j1,j2,j3,j4,j5,j6 );
		glBindTexture(GL_TEXTURE_2D, floor_texid);
		pictobj.draw(opict, vertbuff, uvbuff, elembuff);




		szfont:=9;

-- note: (row=1,col=1) = UL corner

		for row in 1..nrows loop
			for col in 1..ncols loop

				badmatch:=false;
				for i in 1..nbad loop
					if row=badcells(i)(1) and col=badcells(i)(2) then
						badmatch:=true;
					end if;
				end loop;

				-- define center of cell (1 of 81):
				-- these coords in [-1..1, -1..1]:
				cc := -1.0 + 2.0*(float(col)-0.5)*dh;
				rr := +1.0 - 2.0*(float(row)-0.5)*dv;

				-- these coords in [0..1, 0..1]:
				c01 := 0.5*(cc+1.0);
				r01 := 0.5*(rr+1.0);
				c01 := c01 - dc/4.0; -- horizontal adjustment

				pictobj.setrect(opict,-cc,yy,rr, dc,dy,dr, j1,j2,j3,j4,j5,j6); --UL

				if pr>0 and pr<10 and pc>0 and pc<10 and row=pr and col=pc then --hilight
					glBindTexture(GL_TEXTURE_2D, red_texid);
					pictobj.draw(opict, vertbuff, uvbuff, elembuff);


				elsif badmatch and showK then --bad cell
					glBindTexture(GL_TEXTURE_2D, blu_texid);
					pictobj.draw(opict, vertbuff, uvbuff, elembuff);



				elsif (row=col) or (row=10-col) then
					glBindTexture(GL_TEXTURE_2D, lgrey_texid);
					pictobj.draw(opict, vertbuff, uvbuff, elembuff);
				else
					glBindTexture(GL_TEXTURE_2D, box_texid);
					pictobj.draw(opict, vertbuff, uvbuff, elembuff);
				end if;

			end loop; -- col

		end loop; -- row


		-- now, draw numerals in each cell that
		-- have not yet been eliminated:
		drawnYet:=(others=>(others=>(others=>false)));
		for row in 1..nrows loop
			for col in 1..ncols loop

				oXmatch := false; -- hilite Objects of Xwing deletion
				for i in 1..xcn loop
					if xcd(i)=(row,col) then oXmatch:=true; end if;
				end loop;


				Xmatch :=
					(rx1=row and cx1=col) or
					(rx2=row and cx2=col) or
					(rx3=row and cx3=col) or
					(rx4=row and cx4=col);


				Ymatch :=
					(ry1=row and cy1=col) or
					(ry2=row and cy2=col) or
					(ry3=row and cy3=col);


				-- define center of cell (1 of 81):
				-- these coords in [-1..1, -1..1]:
				cc := -1.0 + 2.0*(float(col)-0.5)*dh;
				rr := +1.0 - 2.0*(float(row)-0.5)*dv;

				-- these coords in [0..1, 0..1]:
				c01 := 0.5*(cc+1.0); -- horz adjustment numeral-center
				r01 := 0.5*(rr+1.0); -- vert adjustment numeral-center

				-- microAdjust based on LL+LR PencilMarks
				r01a:=r01+0.005;
				c01a:=c01;


				r01 := r01 - dr/20.0; -- vert micro-adjustment
				c01 := c01 - dc/8.0;  -- horz micro-adjustment



				tot:=0;
				for d in 1..9 loop
					if candidate(row,col)(d) then tot:=tot+1; end if;
				end loop;


				-- set corner pencil-mark positions 3jul23:

				ddhh:= 0.035;
				ddvv:= 0.035;

				orrul:=r01a+ddvv;
				orrur:=r01a+ddvv;
				orrll:=r01a-ddvv;
				orrlr:=r01a-ddvv;

				occul:=c01a-ddhh;
				occur:=c01a+ddhh;
				occll:=c01a-ddhh;
				occlr:=c01a+ddhh;



				for r in 0..2 loop
				for c in 0..2 loop
					--orr := drr*float(r); 5may
					orr := drr*float(2-r);
					occ := dcc*float(c);
					d:=1+r*3+c; -- digit to display

					if candidate(row,col)(d) then



						-- unique digit, centered, large
						if tot=1 then -- ? need for flush ?
							-- this is proper, but too agressive:
							--if m9(row,col)=0 then --4jul23
							--	m9(row,col):=d;
							--	flushDups(row,col);
							--end if;
							if om9(row,col)>0 then --Original Spec (darker grey)
								grytex.print2d( i2st(d), c01a, r01a,  lgfont);

							elsif m9(row,col)>0 then --lighter grey
								ltgtex.print2d( i2st(d), c01a, r01a,  lgfont);

							else -- single candidate (green)
								grntex.print2d( i2st(d), c01+occ, r01+orr,  szfont);

							end if;
							drawnYet(row,col):=(others=>true);



---------- xCycle -----------------------------------------------------------------
						-- assuming showX=true then 1st digit is removable...cyan:
						elsif badxc and showX and d=digSelect and row=ca(1) and col=ca(2) then
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1);
							drawnYet(row,col)(d):=true;

						elsif badxc and showX and d=digSelect and row=cz(1) and col=cz(2) then
							blutex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1);
							drawnYet(row,col)(d):=true;

						elsif badxc and showX and d=digSelect 
						and (xoncycle(row,col) or xoffcycle(row,col)) then
							if xoncycle(row,col) then
								blutex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1);
							else
								redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1);
							end if;
							drawnYet(row,col)(d):=true;

				------------ new good xCycle ----------------------------------------

						elsif not badxc and showX 
						and d=digSelect and hiliteXcDel(row,col)(d) 
						then
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1); --deletable
							drawnYet(row,col)(d):=true;

						elsif not badxc and showX and d=digSelect 
						and (xoncycle(row,col) or xoffcycle(row,col)) then
							if xoncycle(row,col) then
								blutex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1); --ON
							else
								redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1); --OFF
							end if;
							drawnYet(row,col)(d):=true;

---------- xCycle end -----------------------------------------------------------------


----------- KeyCell begin -----------------------------------------------------------
						elsif showK and d=badig and row=pr and col=pc then --deletable
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1);
							drawnYet(row,col)(d):=true;

						--addendum 19may to show blue trail towards blue cells
						elsif showK and blu9(row,col)=d then --would-be singleton
							blutex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1);
							drawnYet(row,col)(d):=true;

						elsif showK and hiliteKdel(row,col)(d) then --deleted digit
							orntex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1);
							drawnYet(row,col)(d):=true;
----------- KeyCell end -----------------------------------------------------------


---------------Linked Pairs ------------------------------------------------------------
						elsif showLP and digSelect=d and colorL2(d,seechain(d),row,col)>0 then
							if colorL2(d,seechain(d),row,col)=1 then --Red
								redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
								drawnYet(row,col)(d):=true;
							elsif colorL2(d,seechain(d),row,col)=2 then --Blue
								blutex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
								drawnYet(row,col)(d):=true;

							elsif colorL2(d,seechain(d),row,col)=3 then --Cyan (removable)
								cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont+1); --cyan
								drawnYet(row,col)(d):=true;

							end if;
---------------Linked Pairs ------------------------------------------------------------


--------d-key------------------------------------------------------------------------a

				-- RowPairs:

						elsif rowPairs and hiliteRow(row,col)(d) and not minimal then 
						-- show digit pairs in rows
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif rowPairs and hiliteRowRem(row,col)(d) and not minimal then 
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;


				-- ColPairs:

						elsif colPairs and hiliteCol(row,col)(d) and not minimal then 
						-- show digit pairs in cols
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif colPairs and hiliteColRem(row,col)(d) and not minimal then 
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;


				-- DiagPairs:

						--(d-key) diagPairs
						elsif diagPairs and hiliteD(row,col)(d) and not minimal then 
						-- show digit pairs on diagonals
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						--(d-key) diagPairs
						elsif diagPairs and hiliteDiagRem(row,col)(d) and not minimal then 
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;


				-- BoxPairs:

						elsif hiliteBoxRem(row,col)(d) and (boxpairs or boxPairsAl)  then --26jul23
						--highlight aligned deletables
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;


						elsif hiliteBP(row,col)(d) and (boxPairs or boxPairsAl) then 
						--reveal Aligned box pairs (pointingPairs)
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

------addendum 12jul23:
						elsif hiliteBPna(row,col)(d) and boxpairsNa then 
							purtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

--------d-key------------------------------------------------------------------------






------2s 3s 4s-----------------------------------------------------------------------

						elsif show2h and hiliteHPrem(row,col)(d) and not minimal then 
						-- show hidden pairs --deletable
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif show2h and hiliteHP(row,col)(d) and not minimal then 
						-- show pairs (hidden)
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif show2n and hiliteNP(row,col) and not minimal then 
						-- show pairs (naked)
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;



						elsif show3h and hiliteHTrem(row,col)(d) and not minimal then 
						-- show hidden triples --deletable
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif show3h and hiliteHT(row,col)(d) and not minimal then 
						-- show hidden triples
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif show3n and hiliteNT(row,col) and not minimal then 
						-- show naked triples
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;



						elsif show4h and hiliteHQrem(row,col)(d) and not minimal then 
						-- show hidden quads --deletable
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif show4h and hiliteHQ(row,col)(d) and not minimal then 
						-- show hidden quads
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif show4n and hiliteNQ(row,col) and not minimal then 
						-- show naked quads
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;
------2s 3s 4s-----------------------------------------------------------------------



-----Xwing---------------------------------------------------------------------------------
						elsif shoXwing and Xmatch and d=xdig then --show main Xwing digits
							redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;


						elsif shoXwing and oXmatch and d=xdig then --show Xwing-deletable digits
							cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;
-----Xwing---------------------------------------------------------------------------------


-------- Ywing --------------------------------------------------------------------------------
						elsif showY and Ymatch then -- show Ywing cells
								redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;

						elsif showY and row=tr0 and col=tc0 and d=tdig then --Ywing deletable target
								cyatex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
							drawnYet(row,col)(d):=true;
-------- Ywing --------------------------------------------------------------------------------


						elsif 
						not minimal
						and not hilite3(row,col)(d) 
						and not hiliteBP(row,col)(d) 
						then
							ltgtex.print2d( i2st(d), c01+occ, r01+orr,  szfont); --ltGrey
							drawnYet(row,col)(d):=true;

						end if;


						if not drawnYet(row,col)(d) then

							if 
								not showLP and not showX and not showK
								and not shoXwing and not showY
								and not boxPairs and not rowPairs and not colPairs
								and not show2h and not show2n
								and not show3h and not show3n
								and not show4h and not show4n
								and not diagPairs
							then
							--we avoid this color-coded drawing of singles/boxPairs
							--in special modes above.

								if  hilite3(row,col)(d)  then --singles
									grntex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
									drawnYet(row,col)(d):=true;

--25jul23: suppress default drawing of red pointing pairs:
--								elsif 
--									hiliteBP(row,col)(d)
--									and boxPairsAl --25jul23
--								then --pointingPairs
--									redtex.print2d( i2st(d), c01+occ, r01+orr,  szfont);
--									drawnYet(row,col)(d):=true;

								end if;

							end if; --not showLP...

							if not drawnYet(row,col)(d) and not minimal then
								ltgtex.print2d( i2st(d), c01+occ, r01+orr,  szfont); --ltGrey
								drawnYet(row,col)(d):=true;
							end if;

						end if; --not drawnYet


					else -- non-candidate...has been eliminated:
						blktex.print2d( " ", c01+occ, r01+orr,  szfont);
					end if;
				end loop;
				end loop;

			end loop; -- col

		end loop; -- row


		if showK then -- KeyCell test
			grytex.print2d( "KeyCell-Test", 0.4, 0.999, tinyfont );

		elsif show2h then -- hiddenpairs
			if dmode then
				grytex.print2d( "HIDDEN PAIRS-DIA", 0.38, 0.999, tinyfont );
			elsif bmode then
				grytex.print2d( "HIDDEN PAIRS-BOX", 0.38, 0.999, tinyfont );
			elsif rmode then
				grytex.print2d( "HIDDEN PAIRS-ROW", 0.38, 0.999, tinyfont );
			elsif cmode then
				grytex.print2d( "HIDDEN PAIRS-COL", 0.38, 0.999, tinyfont );
			end if;

		elsif show2n then -- nakedpairs
			grytex.print2d( "NAKED PAIRS-D/R/C/B", 0.35, 0.999, tinyfont );

		elsif show3h then -- hidden triples
			if dmode then
				grytex.print2d( "HIDDEN TRIPLES-DIA", 0.38, 0.999, tinyfont );
			elsif bmode then
				grytex.print2d( "HIDDEN TRIPLES-BOX", 0.38, 0.999, tinyfont );
			elsif rmode then
				grytex.print2d( "HIDDEN TRIPLES-ROW", 0.38, 0.999, tinyfont );
			elsif cmode then
				grytex.print2d( "HIDDEN TRIPLES-COL", 0.38, 0.999, tinyfont );
			end if;

		elsif show3n then -- naked triples
			grytex.print2d( "NAKED TRIPLES-D/R/C/B", 0.35, 0.999, tinyfont );

		elsif diagPairs then -- diag 2
			grytex.print2d( "DIG DBLs DIAG", 0.4, 0.999, tinyfont );

		elsif rowPairs and not minimal then -- r/c 2
			grytex.print2d( "DIG DBLs ROW", 0.4, 0.999, tinyfont );

		elsif colPairs and not minimal then -- r/c 2
			grytex.print2d( "DIG DBLs COL", 0.4, 0.999, tinyfont );


		elsif show4h then --hidden quads
			if dmode then
				grytex.print2d( "HIDDEN QUADS-DIA", 0.38, 0.999, tinyfont);
			elsif bmode then
				grytex.print2d( "HIDDEN QUADS-BOX", 0.38, 0.999, tinyfont);
			elsif rmode then
				grytex.print2d( "HIDDEN QUADS-ROW", 0.38, 0.999, tinyfont);
			elsif cmode then
				grytex.print2d( "HIDDEN QUADS-COL", 0.38, 0.999, tinyfont);
			end if;

		elsif show4n then --naked quads
			grytex.print2d( "NAKED QUADS-D/R/C", 0.35, 0.999, tinyfont);


		elsif showX then --Xcycle
			grytex.print2d( 
				"xCycle-"&i2st(ca(1))&i2st(ca(2))&"-"&i2st(cz(1))&i2st(cz(2))
				,0.4, 0.999, tinyfont);



		elsif shoXwing then --Xwing
			grytex.print2d( "X-WING", 0.4, 0.999, tinyfont);



		elsif showY then --Ywing
			grytex.print2d( "Y-WING", 0.4, 0.999, tinyfont);



		elsif showLP then
			grytex.print2d( "LINKED-PAIRS(" & i2st(digSelect)
				&")"&integer'image(1+seechain(digSelect))&" of"
				&integer'image(nchains(digSelect)), 
				0.4, 0.999, tinyfont);


		elsif boxPairs and not minimal then
			grytex.print2d( "DIG Dbls/Trpls BOX", 0.35, 0.999, tinyfont );

		--elsif boxPairs and minimal then -- r/c 2
		elsif  minimal then -- r/c 2
			grytex.print2d( "Minimal View", 0.4, 0.999, tinyfont );


		else -- auto-candidate view
			grytex.print2d( "Candidate View", 0.4, 0.999, tinyfont );


		end if;

	-- end draw puzzle----------------------

	end if; --help

end Draw;
















	nc1,nc2, ncc : integer := 0;
	numstr : string(1..5) := (others=>' ');

	totgame, nlevels : integer := 0;

	tfile : text_io.file_type;




procedure checkForUserFile is
	use myint_io;
	ifil: file_type;
	i: integer;
	ok: boolean;
begin

  	if Ada.Command_Line.Argument_Count = 0 then --restore

		ok:=fullreadpuzzle; --now sets fnamUstr

		if not ok then
			raise program_error;
		end if;


  	elsif Ada.Command_Line.Argument_Count = 1 then

		declare
       fstr : string := Ada.Command_Line.Argument(1);--File
		begin

			fnamUstr:=to_unbounded_string( fstr );

			text_io.open(ifil, in_file, fstr);

			for r in 1..9 loop
			for c in 1..9 loop
				get(ifil,i);
				m9(r,c):=i;
				if i>0 and i<=9 then
					candidate(r,c) := (others=>false);
					candidate(r,c)(i) := true;
				end if;
			end loop;
			end loop;

			text_io.close(ifil);
		end; --declare

		om9:=m9;

-- note: at this point flushes are likely needed to initialize
-- the candidates. But each flush might discover additional singles
-- that require further flushing. It is best to clearly understand
-- the initial puzzle state.

		--flush; --known values are removed from all their houses

   end if;

end checkForUserFile;





procedure fullsavepuzzle( usetemp: in boolean := false ) is
	use myint_io;
	ofil: file_type;
begin

	if usetemp then
		text_io.create(ofil, out_file, "kx_tmp.txt");
	else
		text_io.create(ofil, out_file, "fxrestore.txt");
	end if;

	for r in 1..9 loop
		for c in 1..9 loop
			for d in 1..9 loop
				if candidate(r,c)(d) then
					put(ofil, "1 ");
				else
					put(ofil, "0 ");
				end if;
			end loop;
		end loop;
	end loop;

	new_line(ofil);
	put_line(ofil, to_string(fnamUstr) );

	for r in 1..9 loop
	for c in 1..9 loop
		put(ofil, " ");
		put(ofil, i2st(om9(r,c)) );
	end loop;
	end loop;


	text_io.close(ofil);

	if not usetemp then
		put_line("Saved to fxrestore.txt");
	end if;

end fullsavepuzzle;



function fullreadpuzzle( usetemp: in boolean := false ) return boolean is
	use myint_io;
	ifil: file_type;
	b: integer; --boolean
	junkUstr: unbounded_string;
	onshok: constant boolean := showK;
begin

if ada.directories.Exists("fxrestore.txt") then

	if usetemp then
		text_io.open(ifil, in_file, "kx_tmp.txt");
	else
		text_io.open(ifil, in_file, "fxrestore.txt");
	end if;

	for r in 1..9 loop
		for c in 1..9 loop
			for d in 1..9 loop
				get(ifil,b);
				if b=1 then candidate(r,c)(d):=true;
				else candidate(r,c)(d):=false;
				end if;
			end loop;
		end loop;
	end loop;

	get_line(ifil, junkUstr); --read to EOL of data line
	get_line(ifil, fnamUstr); --next [=last] line is now the original filename

	for r in 1..9 loop
	for c in 1..9 loop
		get(ifil,b); om9(r,c):=b;
	end loop;
	end loop;


	text_io.close(ifil);

	--m9:=(others=>(others=>0));
	m9:=om9;

	showK:=false; --do not register screen output during restore

	--flush;
	--findUnique(ok); -- restores m9
	--flush;

	if onshok then showK:=true; end if;

	if not usetemp then
		put_line("Read from fxrestore.txt");
	end if;

	return true;

else --fxrestore.txt does not exist
	put_line("Restore file not found!");
	return false;
end if;


end fullreadpuzzle;






	arrowdwell : constant float := 0.2; --also for autosolve
	dwell : constant float := 0.3;

	keytime, akeytime: float := 0.0;

procedure getKeyInputs( mainWin : access GLFWwindow ) is separate;

	pickdwell: constant float := 0.5;
	mptime: float := 0.0;
procedure getMouseClick( 
	mainWin : access GLFWwindow;
	wid,hit: interfaces.c.int
	) is separate;






	nz: integer;
	done: boolean := false;

begin -- asok =========================================================



	first_prep; -- init graphics/sound, defines fnum, flev

	checkForUserFile; --input puzzle

	setup_textures;

	updateMVP;

	restart;

	--9jun21 : to avoid opening with wrong drawable size
	wwid:=0;
	whit:=0;



	-- main event loop begin: --------------------------------------------
   while not userexit loop


		--GlfwPollEvents; --good
		GlfwWaitEvents;

		getKeyInputs(mainWindow);
		getMouseClick(mainWindow,wwid,whit);

		exit when glfwWindowShouldClose(mainWindow) /= 0; --14may21 addendum


-------- here we handle resizing window ----------------------


		glfwGetWindowSize( mainWindow, Nwid'access, Nhit'access );
		if( (Nwid /= wwid) or (Nhit /= whit) ) then
			wwid:=Nwid;  whit:=Nhit;

			glfwGetFramebufferSize(mainwindow, fwid'access, fhit'access);
			glViewport(0,0,Fwid,Fhit);

		end if;


		if not done then
			nz:=0;
			for r in 1..9 loop
			for c in 1..9 loop
			if m9(r,c)=0 then nz:=nz+1; end if;
			end loop; --c
			end loop; --r
			if nz=0 then
				put_line(" Solved !");
				done:=true; --message just once
				badcells := (others=>(others=>0));
				pr:=0; pc:=0;
			end if;
		end if;


		Draw; -- main drawing here

		glflush;
		glfwSwapBuffers( mainWindow );


   end loop; --------------------- main event loop end -------------------





	release_textures;
	blktex.cleanuptext;
	grytex.cleanuptext;
	ltgtex.cleanuptext;
	whitex.cleanuptext;
	blutex.cleanuptext;
	redtex.cleanuptext;
	magtex.cleanuptext;
	grntex.cleanuptext;
	orntex.cleanuptext;
	yebtex.cleanuptext;
	cyatex.cleanuptext;
	purtex.cleanuptext;


	glfwdestroywindow(mainWindow);
	glfwTerminate;


exception
when others=>

	put_line("asudx: abnormal termination");
	release_textures;
	blktex.cleanuptext;
	grytex.cleanuptext;
	ltgtex.cleanuptext;
	whitex.cleanuptext;
	blutex.cleanuptext;
	redtex.cleanuptext;
	magtex.cleanuptext;
	grntex.cleanuptext;
	orntex.cleanuptext;
	yebtex.cleanuptext;
	cyatex.cleanuptext;
	purtex.cleanuptext;

	glfwdestroywindow(mainWindow);
	glfwTerminate;

end asudx;

