extern queue	squeue;			// the three queues on which ranges reside
extern queue	bfqueue;
extern queue	ufqueue;

extern double minfull;

extern double coltol;

int anymore();

// The following is used in some calls to range::enqueue(int = 0).
#define ANDBLOCK 1

class page;

enum { DRAFT = 0, FINAL = 1 };

// The mergestream currpage->stage serves as a staging area for page makeup:
// when primed, it contains a minimal acceptable chunk of input ranges.
// The page must either take or leave everything that's on stage.
class mergestream : public queue {
	page	*currpage;		// current page that's accepting stuff
  public:
	mergestream(page *cp)	{ currpage = cp; unblock(); }
	void	unblock();
	int	prime();		// stage next legal chunk
	void	pend();			// process pending chunk on stage
};

// The multicol currpage->twocol is the two-column piece of the page to which
// two-column ranges are currently being added.
// The page sets htavail to indicate how tall it is allowed to become.
// All ranges on definite must be placed when the multicol is printed.
// Each of these definite ranges also resides on one of column[0] and [1],
// which represent the current best guess about how to divide definite
// between the two columns.
class multicol : public range {
	page	*currpage;		// current page that's accepting stuff
	stream	definite;		// definitely on page
	stream	scratch;		// for trial compositions
	stream	column[2];		// left (0) and right (1) columns
	int	leftblocked;		// OK to add to left column?
	int	htavail;		// max possible ht, set by page::tryout()
	int	prevhtavail;		// max 2-colht last time we added something
	friend	class page;
public:
	multicol(page *cp)	{ currpage = cp;
				leftblocked = 0;
				htavail = 0;
				prevhtavail = -1;
				setgoal(NOGOAL); }
					// the two-column piece behaves as part
					// of the stream of single-column input.
	int	numcol()	{ return 1; }
	int	nonempty()	{ return definite.more(); }
	void	choosecol(range *, int);// add first arg to one or other column
	void	choosecol(stream*, int);// add *all ranges on first arg*
					// to one or other column
					// NOT the same as a mapcar of the
					// preceding function over the ranges
					// on the first argument!
	void	compose(int);		// divide into two columns
	void	tryout();		// decide which column gets stage contents
	void	stretch(int);		// justify both columns to given height
	int	print(int curv, int col);
	int	height();		// an upper bound on actual height
	int	rawht()		{ return max(column[0].rawht(), column[1].rawht()); }
	void	reheight(int *cv, int *mv)
				{ *cv += height(); *mv = max(*mv, *cv); }
	void	dump();
	int	isvbox()	{ return nonempty(); }	// during trimspace()
};

// These sentinel ranges are used to separate the ranges on twocol::definite
// into the chunks in which they came from the staging area.
// Thus, they preserve the results of the computation that was done to prime
// page::stage.
class sentrange : public range {
  public:
	sentrange()		{ }
	int	numcol()	{ return 2; }
	int	issentinel()	{ return 1; }
};

class page {
	int	pagesize;		// allowed maximum height
	int	prevncol;		// was last item tried 1- or 2-column?
	int	vsince;			// how many vboxes from "current" BS
					// (to avoid putting a single line on
					// a page with a very large floatable)
	stream	definite;		// definitely on page, in input order
	stream	scratch;		// playground in which to alter page
	void	cmdproc();		// process any of several commands
	void	parmproc();		// process any of several parameters
	void	tryout();		// see whether current stage contents fit
	void	compose(int);		// float and trim current page contents
	void	makescratch(int);	// fill scratch area
	void	commit();		// accept the items on stage
	void	welsh();		// reject the items on stage
	void	adddef(range *r);	// add to one of the definite queues
					// (definite or twocol->definite)
  public:
	mergestream *stage;
	friend	class mergestream;
	multicol *twocol;
	friend class multicol;
	page(int p)	{ pagesize = p;
			prevncol = 1;
			vsince = 0;
			stage = new mergestream(this);
			twocol = new multicol(this); }
	~page()	{ definite.freeall(); scratch.freeall(); }
	void	fill();
	int	blank()	{ return !definite.more() && !twocol->definite.more();}
	void	print();
};

// functions in page.c
int main(int, char **);
