#ifndef __SEARCH_WINDOW_H__
#define __SEARCH_WINDOW_H__

// This file (C) 2004 Steven Boswell.  All rights reserved.
// Released to the public under the GNU General Public License.
// See the file COPYING for more information.

#include "config.h"
#include <assert.h>
#include "mjpeg_types.h"
#include "TemplateLib.hh"
#include "Limits.hh"
#include "ReferenceFrame.hh"



// Define this to sort pixel-groups only when asked.
//#define OPTIONALLY_SORT_PIXEL_GROUPS



// Define this to calculate and return the sum-of-absolute-differences
// associated with each found match.
//#define CALCULATE_SAD



// The generic search-window class.  It's parameterized by the size of
// elements in the pixels, the dimension of the pixels, the numeric
// type to use in tolerance calculations, the numeric type to use for
// pixel indices, a numeric type big enough to hold the product of the
// largest expected frame width/height, the width/height of pixel groups
// to operate on, a numeric type big enough to hold
// pixel-dimension * pixel-group-width * pixel-group-height bits and
// serve as an array index, and the types of pixels, reference pixels,
// and reference frames to operate on.
// When constructed, it's configured with the width/height of frames,
// the search radius (in the X and Y directions), and the error
// tolerance.
//
// The search-window works on groups of pixels.  It allows the client
// to iterate through a frame and look for pixel-groups within the
// search radius that match a given pixel-group (within the error
// tolerance).
//
// This class was originally an integral part of MotionSearcher, but
// was split out to allow the search-window to be used in other
// contexts, e.g. for the radius search in yuvmedianfilter.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK,
	class PIXEL = Pixel<PIXEL_NUM,DIM,PIXEL_TOL>,
	class REFERENCEPIXEL
		= ReferencePixel<PIXEL_TOL,PIXEL_NUM,DIM,PIXEL>,
	class REFERENCEFRAME
		= ReferenceFrame<REFERENCEPIXEL,PIXELINDEX,FRAMESIZE> >
class SearchWindow
{
public:
	typedef PIXEL Pixel_t;
		// Our pixel type.

	typedef REFERENCEPIXEL ReferencePixel_t;
		// Our reference pixel type.

	typedef REFERENCEFRAME ReferenceFrame_t;
		// Our reference frame type.

	typedef PIXEL_NUM PixelValue_t;
		// The numeric type to use in pixel values, in each dimension
		// of our pixels.

	typedef PIXEL_TOL Tolerance_t;
		// The numeric type to use in tolerance calculations.

	SearchWindow();
		// Default constructor.

	virtual ~SearchWindow();
		// Destructor.

	void Init (Status_t &a_reStatus, PIXELINDEX a_tnWidth,
			PIXELINDEX a_tnHeight, PIXELINDEX a_tnSearchRadiusX,
			PIXELINDEX a_tnSearchRadiusY, PixelValue_t a_nTolerance);
		// Initializer.  Provide the dimensions of the frames, the
		// search radius, and the error tolerance.
	
	void PurgePixelSorter (void);
		// Purge the pixel sorter.
		// Should be called every once in a while (e.g. every 100
		// frames).  Otherwise, it uses up way too much memory and
		// starts hitting virtual memory & otherwise performs bad.
	
	// A pixel group.
	class PixelGroup
	{
	public:
		Pixel_t m_atPixels[PGH][PGW];
			// The pixels we represent.

		PIXELINDEX m_tnX, m_tnY;
			// The index of the upper-left pixel in this pixel-group.

		PixelGroup();
			// Constructor.

		~PixelGroup();
			// Destructor.

		bool IsWithinTolerance (const PixelGroup &a_rOther,
				Tolerance_t a_tnTolerance
#ifdef CALCULATE_SAD
				, Tolerance_t &a_rtnSAD
#endif // CALCULATE_SAD
									   ) const;
			// Returns true if the two pixels groups are equal, within
			// the given tolerance, and backpatches the sum-of-absolute-
			// differences in a_rtnSAD.  a_tnTolerance must have been
			// generated by Pixel_t::MakeTolerance().
	};

	void StartFrame (ReferenceFrame_t *a_pReferenceFrame);
		// Begin searching through a frame.
		// Provide the frame to search through.

	void PrepareForSearch (Status_t &a_reStatus, bool a_bSortPixels);
		// Prepare for searching, i.e. find all search-window cells
		// within the search radius of the current pixel-group that
		// aren't initialized, and do so.
		// If a_bSortPixels is true, then additionally find all
		// search-window cells within the search radius of the current
		// pixel-group that aren't in the pixel-sorter already, and put
		// them there.
		// This must be called before StartSearch()/FoundNextMatch() if
		// any of the Move*() methods have been called.
	
#ifdef OPTIONALLY_SORT_PIXEL_GROUPS

	const SearchWindowCell &GetCell (PIXELINDEX a_tnY,
			PIXELINDEX a_tnX) const;
		// Return the cell at this index.

#endif // OPTIONALLY_SORT_PIXEL_GROUPS

	template <class REGION>
	void Prune (const REGION &a_rAppliedRegion,
			PIXELINDEX a_tnOffsetX, PIXELINDEX a_tnOffsetY);
		// Prune the search-window, i.e. find all pixel-groups that now
		// contain used reference-pixels, and remove them from the
		// window and from the pixel-sorter.

	void MoveRight (void);
		// Move the window one pixel to the right, removing all pixel
		// groups on the left edge of the window.

	void MoveLeft (void);
		// Move the window one pixel to the left, removing all pixel
		// groups on the right edge of the window.

	void MoveDown (void);
		// Move the window down one line, removing all pixel groups on
		// the top edge of the window.

	void FinishFrame (void);
		// Remove all search-window pixel groups from the pixel sorter,
		// leaving it empty & ready for another frame.

	// A class to keep track of our progress during a search inside the
	// pixel sorter.
	class PixelSorterIterator;

	void StartSearch (PixelSorterIterator &a_rIterator,
			const PixelGroup &a_rSearch);
		// Begin a search through the pixel sorter for the given pixel
		// group.

	const PixelGroup *FoundNextMatch
			(PixelSorterIterator &a_rIterator
#ifdef CALCULATE_SAD
			, Tolerance_t &a_rtnSAD
#endif // CALCULATE_SAD
								   ) const;
		// If there is another pixel group that matches the one being
		// searched for, returns the matched pixel group, and
		// backpatches the sum-of-absolute-differences.
		// If the search is over, returns NULL.

#ifndef NDEBUG
	static uint32_t GetPixelSorterNodeCount (void);
		// Return the number of allocated pixel-sorters.
#endif // NDEBUG

private:
	PIXELINDEX m_tnWidth;
	PIXELINDEX m_tnHeight;
		// The dimensions of each reference frame.

	PIXELINDEX m_tnSearchRadiusX, m_tnSearchRadiusY;
		// The search radius, i.e. how far from the current pixel
		// group we look when searching for possible moved instances of
		// the group.

	Tolerance_t m_tnTolerance, m_tnTwiceTolerance;
		// The error tolerance, i.e. the largest difference we're
		// willing to tolerate between pixels before considering them
		// to be different.  Also, twice the tolerance.

	ReferenceFrame_t *m_pReferenceFrame;
		// The reference frame, against which the new frame is
		// compared.
	
	PIXELINDEX m_tnX, m_tnY;
		// The index of the current pixel group.  Actually the index
		// of the top-left pixel in the current pixel group.  This
		// gets moved in a zigzag pattern, back and forth across the
		// frame and then down, until the end of the frame is reached.
	
	// A pixel group within the search radius of the current
	// pixel group.  Lists of these are attached to pixel-sorter
	// nodes (defined below).
	class PixelSorterBranchNode;
	class SearchWindowCell : public PixelGroup,
		public DoublyLinkedList<SearchWindowCell>
	{
	public:
		PixelSorterBranchNode *m_pSorter;
			// The branch of the pixel-sorter where this cell was
			// placed, the last time it was in the pixel-sorter.

		enum { m_knNotDone = 0, m_knDoneEnough = 1, m_knDone = 2 };
		int8_t m_eDoneSorting;
			// m_knDone if m_pSorter is the lowest node in the tree
			// where this cell could be placed, m_knNotDone if it's
			// possible for this cell to descend further into the tree,
			// m_knDoneEnough if it's possible but not happening.

		SearchWindowCell();
			// Default constructor.

		~SearchWindowCell();
			// Destructor.
	};

	SearchWindowCell **m_ppSearchWindow;
	SearchWindowCell *m_pSearchWindowStorage;
		// The search window.  It contains all the cells needed to
		// analyze the image.
		// m_ppSearchWindow consists of pointers to various areas within
		// m_pSearchWindowStorage; this is done solely to keep the code
		// for accessing cells simpler & easier to understand.

	PIXELINDEX m_tnSearchWindowPixelLeft, m_tnSearchWindowPixelRight,
			m_tnSearchWindowPixelTop, m_tnSearchWindowPixelBottom;
		// The extent of the search window that contains valid values
		// in the pixel-groups.  Generally (m_tnX - m_tnSearchRadiusX)
		// to (m_tnX + m_tnSearchRadiusX), & (m_tnY - m_tnSearchRadiusY)
		// to (m_tnY + m_tnSearchRadiusY), but it gets clipped by the
		// frame boundaries, and, if a run of current pixel-groups
		// contain resolved pixels, will lag until the current
		// pixel-group contains all unresolved pixels.

	PIXELINDEX m_tnSearchWindowSortLeft, m_tnSearchWindowSortRight,
			m_tnSearchWindowSortTop, m_tnSearchWindowSortBottom;
		// The extent of the search window that's been put into the
		// pixel sorter.  Generally (m_tnX - m_tnSearchRadiusX) to
		// (m_tnX + m_tnSearchRadiusX), and (m_tnY - m_tnSearchRadiusY)
		// to (m_tnY + m_tnSearchRadiusY), but it gets clipped by the
		// frame boundaries, and, if a run of current pixel-groups
		// contain resolved pixels, will lag until the current
		// pixel-group contains all unresolved pixels.

	// A branch node in the pixel-sorter tree.  One node partitions
	// search-window cells (i.e. pixel groups within the search radius)
	// into several subtrees, and contains a list of cells that can't be
	// pushed further down the tree.  Used to locate matches for the
	// current pixel group in quasi-logarithmic time.
	class PixelSorterBranchNode
	{
	public:
		SearchWindowCell m_oSplitValue;
			// A cell that contains the split values for each dimension
			// of each pixel of the groups being partitioned.  The
			// contained forward/backward pointers are used to form a
			// list of all real pixel groups (i.e. pixel groups within
			// the search radius) connected to this node.

		enum { m_knBranches = 1 << (PGH * PGW * DIM) };
			// The number of branches from each node.  There's two for
			// each combination of pixel & dimension.

		PixelSorterBranchNode *m_apBranches[m_knBranches];
			// This node's child branches.
			//
			// The bit-mask for the branch that represents a particular
			// pixel-group/pixel-dimension (x,y,dim) is calculated by
			// the formula "1 << (y * (PGW * DIM) + x * DIM + dim)".
			//
			// If the searched-for pixel is greater than its counterpart
			// in the pixel-sorter node, its corresponding bit-mask is
			// added to a running total.  When done, that total is the
			// index of the branch to descend down.  If the searched-for
			// pixel is equal to its counterpart (within the tolerance),
			// the search stops at this level.  It only takes one pixel
			// dimension like this to stop the descent.
			//
			// This way, a search for matches only has to follow one
			// series of branches all the way to the bottom of the tree
			// (i.e. there's no need for a queue of branches to try),
			// and there's no need for an incoming pixel group to end up
			// in more than one branch, no matter what the tolerance.

		PixelSorterBranchNode();
			// Default constructor.

		~PixelSorterBranchNode();
			// Destructor.

		static SORTERBITMASK GetBitMask (PIXELINDEX a_tnY,
				PIXELINDEX a_tnX, int a_nDimension);
			// Calculate the bitmask to represent the given pixel
			// dimension at the given coordinates within the pixel
			// group.

		bool ShouldCellStayHere (const SearchWindowCell &a_rCell,
				Tolerance_t a_tnTolerance,
				SORTERBITMASK &a_rtnChildIndex,
				SearchWindowCell &a_rMin,
				SearchWindowCell &a_rMax) const;
			// If the given cell should stay at this level of the tree
			// (i.e. because one of its pixel values is within the
			// tolerance value of its corresponding split point), then
			// return true.  Otherwise, calculate the index of the
			// child branch that should take this cell, and modify
			// a_rMin and a_rMax (which start as the min/max pixel
			// values for the current branch node) to be the min/max
			// for the child branch node.  (If this function returns
			// true, a_rMin and a_rMax are undefined.)

		bool DoesPixelGroupStopHere (const PixelGroup *a_pPixelGroup,
				Tolerance_t a_tnTwiceTolerance,
				SORTERBITMASK &a_rtnChildIndex,
				bool &a_rbMatchAtThisLevel) const;
			// If the given pixel group straddles this level of the tree
			// (i.e. because one of its pixel values equals its
			// corresponding split point), then return true.
			// Otherwise, calculate the index of the child branch that
			// should take this cell, and return false.
			// In either case, determine whether any search-window cells
			// that had to stop at this level of the tree could possibly
			// intersect the given pixel-group.

		void SplitValues (const PixelGroup &a_rMin,
				const PixelGroup &a_rMax);
			// Set our split values to the midpoint between the given
			// minimum & maximum.

#ifndef NDEBUG

		// Count the number of pixel-sorter objects in existence.
		private:
			static uint32_t sm_ulInstances;
		public:
			static uint32_t GetInstances (void)
				{ return sm_ulInstances; }
	
#endif // NDEBUG
	};

	PixelSorterBranchNode m_oPixelSorter;
		// The pixel sorter, i.e. the root of a tree of pixel-sorter
		// branch nodes that partitions all unresolved pixel-groups
		// within the search radius.
	SearchWindowCell m_oPixelSorterMin, m_oPixelSorterMax;
		// The minimum/maximum values for pixels.  Used to help
		// PixelSorter_Add() generate pixel values for new branch nodes.
	SearchWindowCell m_oRangeMin, m_oRangeMax;
		// Cells that contain the minimum/maximum values for pixels
		// at the current level of the pixel-sorter tree.  Used to
		// generate values for new branch nodes.  (These were local
		// variables in PixelSorter_Add(), but the construction &
		// destruction cost actually showed up in the profile!  So
		// they were moved here.)

	PixelSorterBranchNode *PixelSorter_Add (Status_t &a_reStatus,
			SearchWindowCell *a_pCell,
			PixelSorterBranchNode *a_pLastSorter,
			int8_t &a_reDoneSorting);
		// Add this search-window cell to the pixel-sorter, creating
		// new branch nodes as needed.  If this cell has been in the
		// pixel-sorter before, a_pLastSorter is where it was; this
		// avoids duplicated work.
		// Returns the pixel-sorter node where this cell is now
		// attached, and a_reDoneSorting is backpatched with whether the
		// cell could have descended further down the tree, but wasn't
		// for tree-balancing reasons.

	void PixelSorter_Remove (SearchWindowCell *a_pCell);
		// Remove this search-window cell from the pixel-sorter.
		// Does not remove branch nodes; it's assumed that created
		// branch nodes will most likely become useful again at some
		// later time.

public:
	// A class to keep track of our progress during a search inside the
	// pixel sorter.  (This has to be public, in order to allow clients
	// to create one, but the definition is hidden down here because the
	// details of it aren't public.)
	class PixelSorterIterator
	{
	public:
		const PixelGroup *m_pSearch;
			// The pixel group being searched for.

		const PixelSorterBranchNode *m_pBranch;
			// The branch being examined.
			// Initialized to &m_oPixelSorter.
			// If NULL, means the search is over.

		const SearchWindowCell *m_pCell;
			// The last cell examined.
			// If equal to m_pBranch->m_oSplitValue, means that the
			// branch hasn't been examined yet.
	};
};



// Default constructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::SearchWindow()
{
	// No frames yet.
	m_tnWidth = m_tnHeight = PIXELINDEX (0);

	// No information on the sort of search to do yet.
	m_tnSearchRadiusX = m_tnSearchRadiusY = PIXELINDEX (0);
	m_tnTolerance = m_tnTwiceTolerance = PIXEL_TOL (0);

	// No active search yet.
	m_tnX = m_tnY = PIXELINDEX (0);
	m_pReferenceFrame = NULL;

	// No search window yet.
	m_ppSearchWindow = NULL;
	m_pSearchWindowStorage = NULL;
	m_tnSearchWindowPixelLeft = m_tnSearchWindowPixelRight
		= m_tnSearchWindowPixelTop = m_tnSearchWindowPixelBottom
		= m_tnSearchWindowSortLeft = m_tnSearchWindowSortRight
		= m_tnSearchWindowSortTop = m_tnSearchWindowSortBottom
		= PIXELINDEX (0);
}



// Destructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::~SearchWindow()
{
	// Destroy the search window.
	delete[] m_ppSearchWindow;
	delete[] m_pSearchWindowStorage;
}



// Initializer.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>::Init
	(Status_t &a_reStatus, PIXELINDEX a_tnWidth, PIXELINDEX a_tnHeight,
	PIXELINDEX a_tnSearchRadiusX, PIXELINDEX a_tnSearchRadiusY,
	PixelValue_t a_tnTolerance)
{
	int i;
		// Used to loop through things.
	FRAMESIZE tnPixels;
		// The number of pixels in each frame.

	// Make sure they didn't start us off with an error.
	assert (a_reStatus == g_kNoError);

	// Make sure the sorter bit-mask type is big enough to hold
	// pixel-sorter branch-node child indices.  (Note that this is
	// technically a compile-time assertion.)
	assert (sizeof (SORTERBITMASK) * g_knBitsPerByte
		> PGH * PGW * DIM);

	// Make sure the width & height are reasonable.
	assert (a_tnWidth > PIXELINDEX (0));
	assert (a_tnHeight > PIXELINDEX (0));

	// Make sure the search radius is reasonable.
	assert (a_tnSearchRadiusX > PIXELINDEX (0)
		&& a_tnSearchRadiusY > PIXELINDEX (0)
		&& a_tnSearchRadiusX <= a_tnWidth
		&& a_tnSearchRadiusY <= a_tnHeight);

	// Make sure the tolerance is reasonable.  (A zero tolerance can
	// be used for MPEG-encoding-style motion detection.)
	assert (a_tnTolerance >= 0);

	// Calculate the number of pixels in each frame.
	tnPixels = FRAMESIZE (a_tnWidth) * FRAMESIZE (a_tnHeight);

	// Allocate enough cells for our search window.
	m_pSearchWindowStorage = new SearchWindowCell
		[(a_tnHeight - PGH + 1) * (a_tnWidth - PGW + 1)];
	if (m_pSearchWindowStorage == NULL)
	{
		a_reStatus = g_kOutOfMemory;
		return;
	}

	// Allocate enough space for our line-based index into the
	// search window storage.
	m_ppSearchWindow = new SearchWindowCell * [a_tnHeight - PGH + 1];
	if (m_ppSearchWindow == NULL)
	{
		a_reStatus = g_kOutOfMemory;
		return;
	}

	// Generate pointers to the beginning of each line in the search
	// window.
	SearchWindowCell *pNextLine = m_pSearchWindowStorage;
	for (i = 0; i < (a_tnHeight - PGH + 1); ++i)
	{
		m_ppSearchWindow[i] = pNextLine;
		pNextLine += (a_tnWidth - PGW + 1);
	}

	// Initialize the root of the pixel-sorter tree to split the
	// available range in half.  Also generate search-window cells
	// that represent the minimum/maximum values for pixels.
	{
		PIXELINDEX x, y;
			// Used to loop through pixels.
		PixelValue_t atnMin[DIM], atnHalf[DIM], atnMax[DIM];
			// What we initialize each pixel to.

		// Generate a pixel value that represents half the available
		// range in each dimension, plus the minimums & maximums.
		for (i = 0; i < DIM; i++)
		{
			atnMin[i] = Limits<PixelValue_t>::Min;
			atnHalf[i] = (Limits<PixelValue_t>::Min
				+ Limits<PixelValue_t>::Max) / 2;
			atnMax[i] = Limits<PixelValue_t>::Max;
		}

		// Loop through all pixels in the root of the pixel-sorter tree,
		// initialize them to half the maximum value.  Set up our
		// search-window minimum/maximum too.
		for (y = 0; y < PGH; ++y)
		{
			for (x = 0; x < PGW; ++x)
			{
				m_oPixelSorterMin.m_atPixels[y][x] = Pixel_t (atnMin);
				m_oPixelSorter.m_oSplitValue.m_atPixels[y][x]
					= Pixel_t (atnHalf);
				m_oPixelSorterMax.m_atPixels[y][x] = Pixel_t (atnMax);
			}
		}
	}

	// Finally, store our parameters.
	m_tnWidth = a_tnWidth;
	m_tnHeight = a_tnHeight;
	m_tnSearchRadiusX = a_tnSearchRadiusX;
	m_tnSearchRadiusY = a_tnSearchRadiusY;
	m_tnTolerance = Pixel_t::MakeTolerance (a_tnTolerance);
	m_tnTwiceTolerance = Pixel_t::MakeTolerance (2 * a_tnTolerance);
}



// Purge the pixel sorter.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::PurgePixelSorter (void)
{
	// Clear out the pixel-sorter.
	for (SORTERBITMASK i = 0; i < m_oPixelSorter.m_knBranches; ++i)
	{
		if (m_oPixelSorter.m_apBranches[i] != NULL)
		{
			delete m_oPixelSorter.m_apBranches[i];
			m_oPixelSorter.m_apBranches[i] = NULL;
		}
	}
}



// Default constructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelGroup::PixelGroup()
{
	// No location yet.
	m_tnX = m_tnY = PIXELINDEX (0);
}



// Destructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelGroup::~PixelGroup()
{
	// Nothing to do.
}



// Returns true if the two pixels groups are equal, within
// the given tolerance.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
bool
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelGroup::IsWithinTolerance (const PixelGroup &a_rOther,
	Tolerance_t a_tnTolerance
#ifdef CALCULATE_SAD
							 , Tolerance_t &a_rtnSAD
#endif // CALCULATE_SAD
													) const
{
	PIXELINDEX tnX, tnY;
		// Used to loop through pixels.

	// Compare the two pixel groups, pixel by pixel.
#ifdef CALCULATE_SAD
	a_rtnSAD = 0;
#endif // CALCULATE_SAD
	for (tnY = 0; tnY < PGH; ++tnY)
	{
		for (tnX = 0; tnX < PGW; ++tnX)
		{
#ifdef CALCULATE_SAD
			Tolerance_t tnSAD;
				// The sum-of-absolute-differences between these two
				// pixels.
#endif // CALCULATE_SAD

			// Get the two pixels of interest.
			const Pixel_t &rThisPixel = m_atPixels[tnY][tnX];
			const Pixel_t &rOtherPixel = a_rOther.m_atPixels[tnY][tnX];

			// If this pixel value is not within the tolerance of
			// the corresponding pixel in the other group, exit now.
			if (!rThisPixel.IsWithinTolerance (rOtherPixel,
				a_tnTolerance
#ifdef CALCULATE_SAD
							 , tnSAD
#endif // CALCULATE_SAD
									))
			{
				return false;
			}

#ifdef CALCULATE_SAD
			// Sum up the sum-of-absolute-differences.
			a_rtnSAD += tnSAD;
#endif // CALCULATE_SAD
		}
	}

	// The pixel groups are equal, within the given tolerance.
	return true;
}



// Default constructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::SearchWindowCell::SearchWindowCell()
{
	// We haven't been put into the pixel-sorter yet.
	m_pSorter = NULL;
	m_eDoneSorting = m_knNotDone;
}



// Destructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::SearchWindowCell::~SearchWindowCell()
{
	// Nothing to do.
}



// Begin searching through a frame.
// Provide the frame to search through.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::StartFrame (ReferenceFrame_t *a_pReferenceFrame)
{
	// Make sure they gave us a frame.
	assert (a_pReferenceFrame != NULL);
	
	// Remember the frame we're operating on this pass.
	m_pReferenceFrame = a_pReferenceFrame;
	
	// We'll be checking the upper-left corner first.
	m_tnX = m_tnY = PIXELINDEX (0);
}



// Prepare for searching, i.e. find all search-window cells within the
// search radius of the current pixel-group that aren't in the
// pixel-sorter already, and put them there.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::PrepareForSearch (Status_t &a_reStatus,
	bool a_bSortPixels)
{
	PIXELINDEX tnSearchWindowPixelLeft, tnSearchWindowPixelRight,
			tnSearchWindowPixelTop, tnSearchWindowPixelBottom;
		// What the search window should look like when we're done.
	PIXELINDEX tnX, tnY;
		// Used to loop through pixel groups.
	PIXELINDEX tnSearchLeft, tnSearchRight, tnSearchTop, tnSearchBottom;
		// The part of the search window that we actually loop through.
	SearchWindowCell *pCell;
		// The search-window cell corresponding to the pixel group being
		// manipulated.

	// Make sure they didn't start us off with an error.
	assert (a_reStatus == g_kNoError);

#ifndef OPTIONALLY_SORT_PIXEL_GROUPS

	// (If we're not doing the expanding-regions variant, make sure
	// they always want to add search-window cells to the pixel-sorter.)
	assert (a_bSortPixels);

#endif // OPTIONALLY_SORT_PIXEL_GROUPS

	// Make sure we have a new frame & reference frame to work with.
	assert (m_pReferenceFrame != NULL);

	// The search window starts in a radius around the index of the
	// current pixel group.
	tnSearchWindowPixelLeft = m_tnX - m_tnSearchRadiusX;
	tnSearchWindowPixelRight = m_tnX + m_tnSearchRadiusX
		+ PIXELINDEX (1);
	tnSearchWindowPixelTop = m_tnY - m_tnSearchRadiusY;
	tnSearchWindowPixelBottom = m_tnY + m_tnSearchRadiusY
		+ PIXELINDEX (1);

	// Then it gets clipped by the frame boundaries.
	tnSearchWindowPixelLeft = Max (tnSearchWindowPixelLeft,
		PIXELINDEX (0));
	tnSearchWindowPixelRight = Min (tnSearchWindowPixelRight,
		PIXELINDEX (m_tnWidth - PGW + 1));
	tnSearchWindowPixelTop = Max (tnSearchWindowPixelTop,
		PIXELINDEX (0));
	tnSearchWindowPixelBottom = Min (tnSearchWindowPixelBottom,
		PIXELINDEX (m_tnHeight - PGH + 1));

	// If we only have to loop through part of the search window, do
	// so, to save time.
	tnSearchLeft = tnSearchWindowPixelLeft;
	tnSearchRight = tnSearchWindowPixelRight;
	tnSearchTop = tnSearchWindowPixelTop;
	tnSearchBottom = tnSearchWindowPixelBottom;
	if (tnSearchWindowPixelLeft == m_tnSearchWindowPixelLeft
		&& tnSearchWindowPixelRight == m_tnSearchWindowPixelRight)
	{
		// We only have to do the bottom line.
		assert (tnSearchWindowPixelTop == m_tnSearchWindowPixelTop);
		tnSearchTop = m_tnSearchWindowPixelBottom;
	}
	else if (tnSearchWindowPixelTop == m_tnSearchWindowPixelTop
		&& tnSearchWindowPixelBottom == m_tnSearchWindowPixelBottom)
	{
		if (tnSearchWindowPixelLeft == m_tnSearchWindowPixelLeft)
		{
			// We only have to do the right side.
			tnSearchLeft = m_tnSearchWindowPixelRight;
		}
		else if (tnSearchWindowPixelRight == m_tnSearchWindowPixelRight)
		{
			// We only have to do the left side.
			tnSearchRight = m_tnSearchWindowPixelLeft;
		}
	}

	// Loop through the active portion of the search window, find all
	// pixel-groups that contain only unresolved pixels, and put those
	// cells into the pixel-sorter.  Skip the already-active part of the
	// search-window, if any.
	for (tnY = tnSearchTop; tnY < tnSearchBottom; ++tnY)
	{
		PIXELINDEX tnPixelX, tnPixelY;
			// Used to loop through pixels in the pixel-group.

		for (tnX = tnSearchLeft; tnX < tnSearchRight; ++tnX)
		{
			// Skip any part of the search-window that's already active.
			if (tnY >= m_tnSearchWindowPixelTop
				&& tnY < m_tnSearchWindowPixelBottom
				&& tnX >= m_tnSearchWindowPixelLeft
				&& tnX < m_tnSearchWindowPixelRight)
			{
				// (Skip the rest of the line.)
				assert (tnX == m_tnSearchWindowPixelLeft);
				tnX = m_tnSearchWindowPixelRight - PIXELINDEX (1);
				goto nextXPixel;
			}

			// Get the cell that we'll be setting up.
			pCell = &(m_ppSearchWindow[tnY][tnX]);

			// If this cell was invalidated previously, skip it.
			if (pCell->m_eDoneSorting && pCell->m_pSorter == NULL)
			{
				assert (pCell->m_pForward == NULL);
				assert (pCell->m_pBackward == NULL);
				goto nextXPixel;
			}

			// If this cell was set up previously, skip it.
			if (pCell->m_pForward != NULL)
			{
				assert (pCell->m_pBackward != NULL);
				goto sortCell;
			}

			// Now set up the pixels.
			for (tnPixelY = 0; tnPixelY < PGH; ++tnPixelY)
			{
				for (tnPixelX = 0; tnPixelX < PGW; ++tnPixelX)
				{
					ReferencePixel_t *pPixel;
						// The current reference pixel.

					// Get the current reference pixel.
					pPixel = m_pReferenceFrame->GetPixel
						(tnX + tnPixelX, tnY + tnPixelY);

					// Set up the corresponding pixel in the current
					// search-window cell.
					pCell->m_atPixels[tnPixelY][tnPixelX]
						= pPixel->GetValue();
				}
			}

			// Set up the cell's location.
			pCell->m_tnX = tnX;
			pCell->m_tnY = tnY;

			// Remember that this cell's pixels are set up (by putting
			// the cell into a circular list with itself).
			pCell->m_pForward = pCell->m_pBackward = pCell;
	
sortCell:;
#ifndef OPTIONALLY_SORT_PIXEL_GROUPS
			// Put this cell into the pixel-sorter, if it's not
			// there already.
			if (pCell->m_pForward == pCell)
			{
				// (Sanity check: the backward pointer should be
				// circular too.)
				assert (pCell->m_pBackward == pCell);

				// Take it out of the list it's in with itself.
				pCell->Remove();

				// Put it into the pixel-sorter.
				pCell->m_pSorter = PixelSorter_Add (a_reStatus,
					pCell, pCell->m_pSorter, pCell->m_eDoneSorting);
				if (a_reStatus != g_kNoError)
					return;
			}
#endif // OPTIONALLY_SORT_PIXEL_GROUPS

nextXPixel:;
		}
	}

	// The search-window now looks like this.
	m_tnSearchWindowPixelLeft = tnSearchWindowPixelLeft;
	m_tnSearchWindowPixelRight = tnSearchWindowPixelRight;
	m_tnSearchWindowPixelTop = tnSearchWindowPixelTop;
	m_tnSearchWindowPixelBottom = tnSearchWindowPixelBottom;

#ifndef OPTIONALLY_SORT_PIXEL_GROUPS

	// The search-window now looks like this.
	m_tnSearchWindowSortLeft = tnSearchWindowPixelLeft;
	m_tnSearchWindowSortRight = tnSearchWindowPixelRight;
	m_tnSearchWindowSortTop = tnSearchWindowPixelTop;
	m_tnSearchWindowSortBottom = tnSearchWindowPixelBottom;

#else // OPTIONALLY_SORT_PIXEL_GROUPS

	// Put pixel-groups into the pixel-sorter, if so asked.
	if (a_bSortPixels)
	{
		PIXELINDEX tnSearchWindowSortLeft, tnSearchWindowSortRight,
				tnSearchWindowSortTop, tnSearchWindowSortBottom;
			// What the search window should look like when we're done.

		// The search window starts in a radius around the index of the
		// current pixel group.
		tnSearchWindowSortLeft = m_tnX - m_tnSearchRadiusX;
		tnSearchWindowSortRight = m_tnX + m_tnSearchRadiusX
			+ PIXELINDEX (1);
		tnSearchWindowSortTop = m_tnY - m_tnSearchRadiusY;
		tnSearchWindowSortBottom = m_tnY + m_tnSearchRadiusY
			+ PIXELINDEX (1);
	
		// Then it gets clipped by the frame boundaries.
		tnSearchWindowSortLeft = Max (tnSearchWindowSortLeft,
			PIXELINDEX (0));
		tnSearchWindowSortRight = Min (tnSearchWindowSortRight,
			PIXELINDEX (m_tnWidth - PGW + 1));
		tnSearchWindowSortTop = Max (tnSearchWindowSortTop,
			PIXELINDEX (0));
		tnSearchWindowSortBottom = Min (tnSearchWindowSortBottom,
			PIXELINDEX (m_tnHeight - PGH + 1));
	
		// If we only have to loop through part of the search window, do
		// so, to save time.
		tnSearchLeft = tnSearchWindowSortLeft;
		tnSearchRight = tnSearchWindowSortRight;
		tnSearchTop = tnSearchWindowSortTop;
		tnSearchBottom = tnSearchWindowSortBottom;
		if (tnSearchWindowSortLeft == m_tnSearchWindowSortLeft
			&& tnSearchWindowSortRight == m_tnSearchWindowSortRight)
		{
			// We only have to do the bottom line.
			assert (tnSearchWindowSortTop == m_tnSearchWindowSortTop);
			tnSearchTop = m_tnSearchWindowSortBottom;
		}
		else if (tnSearchWindowSortTop == m_tnSearchWindowSortTop
			&& tnSearchWindowSortBottom == m_tnSearchWindowSortBottom)
		{
			if (tnSearchWindowSortLeft == m_tnSearchWindowSortLeft)
			{
				// We only have to do the right side.
				tnSearchLeft = m_tnSearchWindowSortRight;
			}
			else if (tnSearchWindowSortRight
				== m_tnSearchWindowSortRight)
			{
				// We only have to do the left side.
				tnSearchRight = m_tnSearchWindowSortLeft;
			}
		}

		// Loop through the active portion of the search window, find
		// all pixel-groups that contain only unresolved pixels, and
		// put those cells into the pixel-sorter.  Skip the
		// already-active part of the search-window, if any.
		for (tnY = tnSearchTop; tnY < tnSearchBottom; ++tnY)
		{
			for (tnX = tnSearchLeft; tnX < tnSearchRight; ++tnX)
			{
				// Skip any part of the search-window that's already
				// active.
				if (tnY >= m_tnSearchWindowSortTop
					&& tnY < m_tnSearchWindowSortBottom
					&& tnX >= m_tnSearchWindowSortLeft
					&& tnX < m_tnSearchWindowSortRight)
				{
					// (Skip the rest of the line.)
					assert (tnX == m_tnSearchWindowSortLeft);
					tnX = m_tnSearchWindowSortRight - PIXELINDEX (1);
					continue;
				}
	
				// Get the cell that we'll be setting up.
				pCell = &(m_ppSearchWindow[tnY][tnX]);
	
				// Put this cell into the pixel-sorter, if it's not
				// there already.
				if (pCell->m_pForward == pCell)
				{
					// (Sanity check: the backward pointer should be
					// circular too.)
					assert (pCell->m_pBackward == pCell);

					// Take it out of the list it's in with itself.
					pCell->Remove();

					// Put it into the pixel-sorter.
					pCell->m_pSorter = PixelSorter_Add (a_reStatus,
						pCell, pCell->m_pSorter, pCell->m_eDoneSorting);
					if (a_reStatus != g_kNoError)
						return;
				}
			}
		}

		// The search-window now looks like this.
		m_tnSearchWindowSortLeft = tnSearchWindowSortLeft;
		m_tnSearchWindowSortRight = tnSearchWindowSortRight;
		m_tnSearchWindowSortTop = tnSearchWindowSortTop;
		m_tnSearchWindowSortBottom = tnSearchWindowSortBottom;
	}
#endif // OPTIONALLY_SORT_PIXEL_GROUPS
}



#ifdef OPTIONALLY_SORT_PIXEL_GROUPS

// Return the cell at this index.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
template <class REGION>
const SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::SearchWindowCell &
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>:: GetCell (PIXELINDEX a_tnY, PIXELINDEX a_tnX) const
{
	// Make sure the indices are within range.
	assert (a_tnX >= 0 && a_tnX < m_tnWidth - PGW + 1
		&& a_tnY >= 0 && a_tnY < m_tnHeight - PGH + 1);

	// Easy enough.
	return m_ppSearchWindow[a_tnY][a_tnX];
}

#endif // OPTIONALLY_SORT_PIXEL_GROUPS



// Prune the search-window, i.e. find all pixel-groups that now
// contain used reference-pixels, and remove them from the window
// and from the pixel-sorter.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
template <class REGION>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::Prune (const REGION &a_rAppliedRegion,
	PIXELINDEX a_tnOffsetX, PIXELINDEX a_tnOffsetY)
{
	typename REGION::ConstIterator itExtent;
		// Used to loop through applied-region extents.
	PIXELINDEX tnX, tnY;
		// Used to loop through search-window cells.

	// Make sure we have a new frame & reference frame to work with.
	assert (m_pReferenceFrame != NULL);

	// Loop through all the extents in the newly-applied region, find
	// all corresponding search-window cells, and invalidate them.
	for (itExtent = a_rAppliedRegion.Begin();
		 itExtent != a_rAppliedRegion.End();
		 ++itExtent)
	{
		PIXELINDEX tnTop, tnBottom, tnLeft, tnRight;
			// The range of search-window cells invalidated by the
			// current applied-region extent.
	
		// Get the current extent.
		const typename REGION::Extent &rExtent = *itExtent;
	
		// Determine the range of search-window cells invalidated by
		// this extent.
		tnTop = rExtent.m_tnY - PGH + PIXELINDEX (1) + a_tnOffsetY;
		tnBottom = rExtent.m_tnY + PIXELINDEX (1) + a_tnOffsetY;
		tnLeft = rExtent.m_tnXStart - PGW + PIXELINDEX (1)
			+ a_tnOffsetX;
		tnRight = rExtent.m_tnXEnd + a_tnOffsetX;
		if (tnTop < 0)
			tnTop = 0;
		if (tnBottom > m_tnHeight - PGH + PIXELINDEX (1))
			tnBottom = m_tnHeight - PGH + PIXELINDEX (1);
		if (tnLeft < 0)
			tnLeft = 0;
		if (tnRight > m_tnWidth - PGW + PIXELINDEX (1))
			tnRight = m_tnWidth - PGW + PIXELINDEX (1);
	
		// Loop through the search-window cells intersected by the
		// current extent, and invalidate them.
		for (tnY = tnTop; tnY < tnBottom; ++tnY)
		{
			for (tnX = tnLeft; tnX < tnRight; ++tnX)
			{
				SearchWindowCell *pCell;
					// The search-window cell corresponding to the pixel
					// group being manipulated.

				// Get the cell.
				pCell = &(m_ppSearchWindow[tnY][tnX]);
	
				// If the cell is in use, remove it from service.
				if (pCell->m_pForward != NULL)
				{
					// (Sanity check.)
					assert (pCell->m_pBackward != NULL);
	
					// Remove this cell from the pixel-sorter, if it's
					// in there.
					if (pCell->m_pForward != pCell)
						PixelSorter_Remove (pCell);
	
					// Invalidate this cell.
					assert (pCell->m_pForward == pCell);
					pCell->Remove();
				}

				// Mark it as invalid, so that it won't be used again
				// for the rest of the frame.
				pCell->m_eDoneSorting = SearchWindowCell::m_knDone;
				pCell->m_pSorter = NULL;
			}
		}
	}
}



// Move the window one pixel to the right, removing all pixel
// groups on the left side of the window.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::MoveRight (void)
{
	PIXELINDEX tnY;
		// Used to loop through pixel groups.
	SearchWindowCell *pCell;
		// The search-window cell corresponding to the pixel group being
		// removed from the search-window.

	// If the leftmost edge of the search-window isn't at the edge of
	// the active area, we can stop now.
	if (m_tnX - m_tnSearchRadiusX != m_tnSearchWindowPixelLeft)
	{
		++m_tnX;
		return;
	}

	// If the search-window is zero width, we can stop now.
	if (m_tnSearchWindowPixelLeft == m_tnSearchWindowPixelRight)
	{
		++m_tnX;
		return;
	}

	// Make sure that the extent of pixels in the pixel-sorter is
	// consistent with the extent of initialized pixel-groups.
	assert (m_tnSearchWindowSortLeft == m_tnSearchWindowSortRight
		|| m_tnSearchWindowPixelLeft <= m_tnSearchWindowSortLeft);

	// Loop through the left edge of the window, remove all active
	// search-window cells.
	for (tnY = m_tnSearchWindowPixelTop;
		 tnY < m_tnSearchWindowPixelBottom;
		 ++tnY)
	{
		// Get the cell that we may be removing.
		pCell = &(m_ppSearchWindow[tnY][m_tnSearchWindowPixelLeft]);

		// If the cell is in the pixel sorter, remove it.
		if (pCell->m_pForward != NULL
			&& pCell->m_pForward != pCell)
		{
			PixelSorter_Remove (pCell);
		}
	}

	// The search-window now loses its left edge.
	if (m_tnSearchWindowSortLeft != m_tnSearchWindowSortRight
	&& m_tnSearchWindowSortLeft == m_tnSearchWindowPixelLeft)
		++m_tnSearchWindowSortLeft;
	++m_tnSearchWindowPixelLeft;

	// And we're one pixel-group to the right.
	++m_tnX;
}



// Move the window one pixel to the left, removing all pixel
// groups on the right side of the window.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::MoveLeft (void)
{
	PIXELINDEX tnY;
		// Used to loop through pixel groups.
	SearchWindowCell *pCell;
		// The search-window cell corresponding to the pixel group being
		// removed from the search-window.

	// If the rightmost edge of the search-window isn't at the edge of
	// the active area, we can stop now.
	if (m_tnX + m_tnSearchRadiusX
			!= m_tnSearchWindowPixelRight - PIXELINDEX (1))
	{
		--m_tnX;
		return;
	}
	
	// If the search-window is zero width, we can stop now.
	if (m_tnSearchWindowPixelLeft == m_tnSearchWindowPixelRight)
	{
		--m_tnX;
		return;
	}

	// Make sure that the extent of pixels in the pixel-sorter is
	// consistent with the extent of initialized pixel-groups.
	assert (m_tnSearchWindowSortRight == m_tnSearchWindowSortLeft
		|| m_tnSearchWindowPixelRight >= m_tnSearchWindowSortRight);

	// The search-window loses its right edge.
	if (m_tnSearchWindowSortRight != m_tnSearchWindowSortLeft
	&& m_tnSearchWindowSortRight == m_tnSearchWindowPixelRight)
		--m_tnSearchWindowSortRight;
	--m_tnSearchWindowPixelRight;

	// Loop through the right edge of the window, remove all active
	// search-window cells.
	for (tnY = m_tnSearchWindowPixelTop;
		 tnY < m_tnSearchWindowPixelBottom;
		 ++tnY)
	{
		// Get the cell that we may be removing.
		pCell = &(m_ppSearchWindow[tnY][m_tnX + m_tnSearchRadiusX]);

		// If the cell is in the pixel sorter, remove it.
		if (pCell->m_pForward != NULL
			&& pCell->m_pForward != pCell)
		{
			PixelSorter_Remove (pCell);
		}
	}

	// And we're one pixel-group to the left.
	--m_tnX;
}



// Move the window down one line, removing all pixel groups on
// the top of the window.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::MoveDown (void)
{
	PIXELINDEX tnX;
		// Used to loop through pixel groups.
	SearchWindowCell *pCell;
		// The search-window cell corresponding to the pixel group being
		// removed from the search-window.

	// If the topmost edge of the search-window isn't at the edge of the
	// active area, we can stop now.
	if (m_tnY - m_tnSearchRadiusY != m_tnSearchWindowPixelTop)
	{
		++m_tnY;
		return;
	}

	// If the search-window is zero height, we can stop now.
	if (m_tnSearchWindowPixelTop == m_tnSearchWindowPixelBottom)
	{
		++m_tnY;
		return;
	}

	// Make sure that the extent of pixels in the pixel-sorter is
	// consistent with the extent of initialized pixel-groups.
	assert (m_tnSearchWindowSortTop == m_tnSearchWindowSortBottom
		|| m_tnSearchWindowPixelTop == m_tnSearchWindowSortTop);

	// Loop through the top edge of the window, remove all active
	// search-window cells.
	for (tnX = m_tnSearchWindowPixelLeft;
		 tnX < m_tnSearchWindowPixelRight;
		 ++tnX)
	{
		// Get the cell that we may be removing.
		pCell = &(m_ppSearchWindow[m_tnSearchWindowPixelTop][tnX]);

		// If the cell is in the pixel sorter, remove it.
		if (pCell->m_pForward != NULL
			&& pCell->m_pForward != pCell)
		{
			PixelSorter_Remove (pCell);
		}
	}

	// The search-window loses its top edge.
	if (m_tnSearchWindowSortTop != m_tnSearchWindowSortBottom)
		++m_tnSearchWindowSortTop;
	++m_tnSearchWindowPixelTop;

	// And we're one pixel-group lower.
	++m_tnY;
}



// Remove all search-window pixel groups from the pixel sorter,
// leaving it empty & ready for another frame.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::FinishFrame (void)
{
	PIXELINDEX tnX, tnY;
		// Used to loop through pixel groups.

	// Loop through the search-window, invalidate all cells.
	for (tnY = 0; tnY < m_tnHeight - PGH + 1; ++tnY)
	{
		for (tnX = 0; tnX < m_tnWidth - PGW + 1; ++tnX)
		{
			SearchWindowCell *pCell;
				// The search-window cell corresponding to the pixel
				// group being removed from the search-window.

			// Get the cell that we may be removing.
			pCell = &(m_ppSearchWindow[tnY][tnX]);

			// If the cell's pixels are valid, or if it's in the pixel
			// sorter, invalidate it.
			if (pCell->m_pForward != NULL)
			{
				// (Sanity check.)
				assert (pCell->m_pBackward != NULL);
	
				// If this cell is in the pixel-sorter, remove it.
				if (pCell->m_pForward != pCell)
					PixelSorter_Remove (pCell);

				// Invalidate this cell.
				assert (pCell->m_pForward == pCell);
				pCell->Remove();
			}

			// Re-sort it in the next frame.
			pCell->m_pSorter = NULL;
			pCell->m_eDoneSorting = SearchWindowCell::m_knNotDone;
		}
	}

	// The search-window is now empty.
	m_tnSearchWindowPixelLeft = m_tnSearchWindowPixelRight
		= m_tnSearchWindowPixelTop = m_tnSearchWindowPixelBottom
		= m_tnSearchWindowSortLeft = m_tnSearchWindowSortRight
		= m_tnSearchWindowSortTop = m_tnSearchWindowSortBottom
		= PIXELINDEX (0);
	
	// We're done with this frame.  Expect our caller to give us a new
	// one.
	m_pReferenceFrame = NULL;
}



// Default constructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelSorterBranchNode::PixelSorterBranchNode()
{
#ifndef NDEBUG
	// One more instance.
	++sm_ulInstances;
#endif // NDEBUG

	// The attached search-window-cells form a circular list.  So
	// start the circle.
	m_oSplitValue.m_pForward = m_oSplitValue.m_pBackward
		= &m_oSplitValue;

	// No branches yet.
	for (int i = 0; i < m_knBranches; ++i)
		m_apBranches[i] = NULL;
}



// Destructor.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelSorterBranchNode::~PixelSorterBranchNode()
{
#ifndef NDEBUG
	// One less instance.
	--sm_ulInstances;
#endif // NDEBUG

	// Make sure we're the only one in our circular list.
	assert (m_oSplitValue.m_pForward == &m_oSplitValue);

	// Finish off the circular list.
	m_oSplitValue.Remove();

	// Recursively destroy the tree.
	for (int i = 0; i < m_knBranches; ++i)
		delete m_apBranches[i];
}



// Calculate the bitmask to represent the given pixel
// dimension at the given coordinates within the pixel
// group.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
SORTERBITMASK
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelSorterBranchNode::GetBitMask (PIXELINDEX a_tnY,
	PIXELINDEX a_tnX, int a_nDimension)
{
	// Make sure the pixel index/dimension are within limits.
	assert (a_tnY >= 0 && a_tnY < PGH);
	assert (a_tnX >= 0 && a_tnX < PGW);
	assert (a_nDimension >= 0 && a_nDimension < DIM);

	// Easy enough.
	return SORTERBITMASK (1)
		<< ((a_tnY * (PGW * DIM) + a_tnX * DIM + a_nDimension));
}



// Determine whether this cell should stay at this level of the tree,
// or calculate the details of the child branch where it should go.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
bool
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelSorterBranchNode::ShouldCellStayHere
	(const SearchWindowCell &a_rCell, Tolerance_t a_tnTolerance,
	SORTERBITMASK &a_rtnChildIndex, SearchWindowCell &a_rMin,
	SearchWindowCell &a_rMax) const
{
	PIXELINDEX tnX, tnY;
	int i;
		// Used to loop through pixels & pixel dimensions.

	// Compare the cell's pixel values to our split values, and
	// determine which child branch it should be moved to, or if it
	// should stay at this level.
	a_rtnChildIndex = 0;
	for (tnY = 0; tnY < PGH; ++tnY)
	{
		for (tnX = 0; tnX < PGW; ++tnX)
		{
			// Get the two pixels of interest.
			const Pixel_t &rCellPixel = a_rCell.m_atPixels[tnY][tnX];
			const Pixel_t &rSplitPixel
				= m_oSplitValue.m_atPixels[tnY][tnX];

			// If this pixel value is within the tolerance of
			// the split-point's corresponding pixel, then this
			// cell has to stay at this level of the tree.
			if (DIM == 1
			&& rCellPixel.IsWithinTolerance (rSplitPixel,
				a_tnTolerance))
			{
				return true;
			}

			// Determine the effect each dimension has on the
			// bitmask that we use to determine the index of the
			// child branch that this cell should descend into.
			// Also calculate the min/max for this branch.
			for (i = 0; i < DIM; i++)
			{
				// If this is a multi-dimensional pixel, we have to
				// make sure that none of its axes are within the
				// tolerance either.
				if (DIM > 1)
				{
					// Make copies of the two pixels.
					Pixel_t oCellPixel = rCellPixel;
					Pixel_t oSplitPixel = rSplitPixel;

					// Collapse all dimensions but the current one.
					for (int j = 0; j < DIM; ++j)
						if (j != i)
							oCellPixel[j] = oSplitPixel[j];
					
					// If this axis, all by itself, is within the
					// tolerance, stop here.
					if (oCellPixel.IsWithinTolerance (oSplitPixel,
						a_tnTolerance))
					{
						return true;
					}
				}

				if (rCellPixel[i] > rSplitPixel[i])
				{
					a_rtnChildIndex |= GetBitMask (tnY, tnX, i);
					a_rMin.m_atPixels[tnY][tnX][i]
						= m_oSplitValue.m_atPixels[tnY][tnX][i];
				}
				else
				{
					a_rMax.m_atPixels[tnY][tnX][i]
						= m_oSplitValue.m_atPixels[tnY][tnX][i];
				}
			}
		}
	}

	// The cell can move into a child branch.
	return false;
}



// If the given pixel group straddles this level of the tree
// (i.e. because one of its pixel values equals its
// corresponding split point), then return true.
// Otherwise, calculate the index of the child branch that
// should take this cell, and return false.
// In either case, determine whether any search-window cells
// that had to stop at this level of the tree could possibly
// intersect the given pixel-group.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
bool
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelSorterBranchNode::DoesPixelGroupStopHere
	(const PixelGroup *a_pPixelGroup, Tolerance_t a_tnTwiceTolerance,
	SORTERBITMASK &a_rtnChildIndex, bool &a_rbMatchAtThisLevel) const
{
	PIXELINDEX tnX, tnY;
	int i;
		// Used to loop through pixels & pixel dimensions.
	bool bPixelGroupStopsHere;
		// True if one of the pixel-group's pixels exactly matches
		// its corresponding split-point.
	
	// Make sure they gave us a pixel-group.
	assert (a_pPixelGroup != NULL);

	// Compare the group's pixel values to our split values, and
	// determine which child branch it should descend, or if it
	// should stop at this level.
	a_rtnChildIndex = 0;
	bPixelGroupStopsHere = false;
	a_rbMatchAtThisLevel = false;
	for (tnY = 0; tnY < PGH; ++tnY)
	{
		for (tnX = 0; tnX < PGW; ++tnX)
		{
			// Get the two pixels of interest.
			const Pixel_t &rGroupPixel
				= a_pPixelGroup->m_atPixels[tnY][tnX];
			const Pixel_t &rSplitPixel
				= m_oSplitValue.m_atPixels[tnY][tnX];

			// Determine the effect each dimension has on the
			// bitmask that we use to determine the index of the
			// child branch that this cell should descend into.
			// (If the pixel value is right on a branch, then all
			// pixels that would match it have been found at this
			// level.)
			for (i = 0; i < DIM; i++)
			{
				if (rGroupPixel[i] == rSplitPixel[i])
					bPixelGroupStopsHere = true;
				if (rGroupPixel[i] > rSplitPixel[i])
					a_rtnChildIndex |= GetBitMask (tnY, tnX, i);

				// If the current pixel is within twice the tolerance
				// of the split-point, then some of the search-window
				// cells that had to stop at this point in the tree may
				// match the current pixel-group.  (And once we figure
				// that out, we don't have to check any other pixels for
				// this property.)
				if (!a_rbMatchAtThisLevel)
				{
					// Make copies of the two pixels.
					Pixel_t oGroupPixel = rGroupPixel;
					Pixel_t oSplitPixel = rSplitPixel;

					// Collapse all dimensions but the current one.
					for (int j = 0; j < DIM; ++j)
						if (j != i)
							oGroupPixel[j] = oSplitPixel[j];
					
					// If this axis, all by itself, is within twice the
					// tolerance, then we can no longer rule out matches
					// at this level.
					if (oGroupPixel.IsWithinTolerance (oSplitPixel,
						a_tnTwiceTolerance))
					{
						a_rbMatchAtThisLevel = true;
					}
				}
			}
		}
	}

	// Return whether the pixel group can descend into a child branch.
	return bPixelGroupStopsHere;
}



// Set our split values to the midpoint between the given
// minimum & maximum.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelSorterBranchNode::SplitValues (const PixelGroup &a_rMin,
	const PixelGroup &a_rMax)
{
	PIXELINDEX tnX, tnY;
	int nDim;
		// Used to iterate through pixel-groups.

	// Loop through all the pixels & their dimensions, calculate each
	// split value.
	for (tnY = 0; tnY < PGH; ++tnY)
		for (tnX = 0; tnX < PGW; ++tnX)
			for (nDim = 0; nDim < DIM; ++nDim)
				m_oSplitValue.m_atPixels[tnY][tnX][nDim] = PIXEL_NUM
					((PIXEL_TOL (a_rMin.m_atPixels[tnY][tnX][nDim])
					+ PIXEL_TOL (a_rMax.m_atPixels[tnY][tnX][nDim]))
						/ 2);
}



#ifndef NDEBUG

template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
uint32_t
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::PixelSorterBranchNode::sm_ulInstances;

template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
uint32_t
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
	::GetPixelSorterNodeCount (void)
{
	return PixelSorterBranchNode::GetInstances();
}

#endif // NDEBUG



// Add this search-window cell to the pixel-sorter, creating
// new branch nodes as needed.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
typename SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::PixelSorterBranchNode *
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::PixelSorter_Add (Status_t &a_reStatus,
	SearchWindowCell *a_pCell, PixelSorterBranchNode *a_pLastSorter,
	int8_t &a_reDoneSorting)
{
	PixelSorterBranchNode *pCurrentBranch;
		// Where we are in the pixel-sorter tree.

	// Make sure they didn't start us off with an error.
	assert (a_reStatus == g_kNoError);

	// Make sure they gave us a cell to add.
	assert (a_pCell != NULL);

	// Make sure the cell is within the search radius.
	assert (AbsoluteValue (a_pCell->m_tnX - m_tnX)
		<= m_tnSearchRadiusX);
	assert (AbsoluteValue (a_pCell->m_tnY - m_tnY)
		<= m_tnSearchRadiusY);

	// If we know where the cell should go, just put it there and exit.
	if (a_pLastSorter != NULL && a_reDoneSorting)
	{
		a_pLastSorter->m_oSplitValue.InsertAfter
			(&(a_pLastSorter->m_oSplitValue), a_pCell);
		return a_pLastSorter;
	}

	// Initialize our traversal's minimum/maximum to the full range.
	// This will get modified as we move down the tree.
	m_oRangeMin = m_oPixelSorterMin;
	m_oRangeMax = m_oPixelSorterMax;

	// Traverse the tree, figure out where the new search-window cell
	// should be put.  Create new branch nodes as needed.
	pCurrentBranch = ((a_pLastSorter == NULL)
		? &m_oPixelSorter : a_pLastSorter);
	for (;;)
	{
		SORTERBITMASK tnChildIndex;
			// The index of the child branch node that should take the
			// newly-added cell.

		// If this branch node has no cells, just put the new cell here
		// and exit.  (This is almost like balancing the search tree.)
		if (pCurrentBranch->m_oSplitValue.m_pForward
			== &(pCurrentBranch->m_oSplitValue))
		{
			pCurrentBranch->m_oSplitValue.InsertAfter
				(&(pCurrentBranch->m_oSplitValue), a_pCell);
			a_reDoneSorting = SearchWindowCell::m_knDoneEnough;
			return pCurrentBranch;
		}

		// If the cell must stay at this level of the tree, then put
		// the cell at this level & exit.
		if (pCurrentBranch->ShouldCellStayHere (*a_pCell, m_tnTolerance,
			tnChildIndex, m_oRangeMin, m_oRangeMax))
		{
			// Hook this cell up to the branch's cell list.
			pCurrentBranch->m_oSplitValue.InsertAfter
				(&(pCurrentBranch->m_oSplitValue), a_pCell);

			// We're done.
			a_reDoneSorting = SearchWindowCell::m_knDone;
			return pCurrentBranch;
		}

		// The cell should descend into a child branch.  If that child
		// exists, then descend.
		if (pCurrentBranch->m_apBranches[tnChildIndex] != NULL)
		{
			pCurrentBranch = pCurrentBranch->m_apBranches[tnChildIndex];
			continue;
		}

		// Try to create the child branch that the cell should go into.
		pCurrentBranch->m_apBranches[tnChildIndex]
			= new PixelSorterBranchNode;
		if (pCurrentBranch->m_apBranches[tnChildIndex] == NULL)
		{
			// We ran out of memory.  Just put the cell here.
			pCurrentBranch->m_oSplitValue.InsertAfter
				(&(pCurrentBranch->m_oSplitValue), a_pCell);
			a_reDoneSorting = SearchWindowCell::m_knDoneEnough;
			return pCurrentBranch;
		}
		pCurrentBranch = pCurrentBranch->m_apBranches[tnChildIndex];

		// Set its split value to the midpoint between the min/max for
		// this child.
		pCurrentBranch->SplitValues (m_oRangeMin, m_oRangeMax);

		// Put the cell here.
		pCurrentBranch->m_oSplitValue.InsertAfter
			(&(pCurrentBranch->m_oSplitValue), a_pCell);

		// If new branch nodes are created below us, we could go deeper
		// into the tree, but in the interest of tree-balancing, we stop
		// here.  (We don't want to potentially create a new branch node
		// when this cell gets re-inserted; we'll just be happy where we
		// are.)
		a_reDoneSorting = SearchWindowCell::m_knDoneEnough;
		return pCurrentBranch;
	}
}



// Remove this search-window cell from the pixel-sorter.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::PixelSorter_Remove (SearchWindowCell *a_pCell)
{
	// Make sure they gave us a cell to remove.
	assert (a_pCell != NULL);

	// Make sure the cell is in the pixel-sorter.
	assert (a_pCell->m_pForward != NULL
		&& a_pCell->m_pForward != a_pCell);

	// Remove the cell from the pixel-sorter.
	a_pCell->Remove();

	// Put it back into a list with itself.  (This marks the cell as
	// having been initialized, saving us from having to do that work
	// again.)
	a_pCell->m_pForward = a_pCell->m_pBackward = a_pCell;
}



// Begin a search through the pixel sorter for the given pixel group.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
void
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::StartSearch
	(PixelSorterIterator &a_rIterator, const PixelGroup &a_rSearch)
{
	// Remember what cell we're searching for.
	a_rIterator.m_pSearch = &a_rSearch;

	// Start searching at the top of the tree.
	a_rIterator.m_pBranch = &m_oPixelSorter;

	// Remember to search this branch's cells.
	a_rIterator.m_pCell = &(a_rIterator.m_pBranch->m_oSplitValue);
}



// If there is another pixel group that matches the one being
// searched for, returns true and backpatches information on the
// matched pixel group.  If the search is over, returns false.
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
	class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
	class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
	class REFERENCEFRAME>
const typename SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,
	FRAMESIZE,PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::PixelGroup *
SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
	PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
	REFERENCEFRAME>::FoundNextMatch
	(PixelSorterIterator &a_rIterator
#ifdef CALCULATE_SAD
									 , Tolerance_t &a_rtnSAD
#endif // CALCULATE_SAD
															) const
{
	SORTERBITMASK tnChildIndex;
		// The index of the branch node child that the search
		// descends down next.

	// Make sure they didn't try to search past the end.
	assert (a_rIterator.m_pBranch != NULL);

	// Loop until we find a match or reach the bottom of the tree.
	for (;;)
	{
		bool bPixelGroupStopsHere;
			// true if we don't have to descend further down the tree.
		bool bMatchesAtThisLevel;
			// true if it's possible for the current pixel-group to
			// match pixel-groups that had to stop at this level in the
			// tree.

		// Move past the last match we found.  (This also works if the
		// branch was just entered, i.e. we're pointing at the branch
		// node's split values.)
		a_rIterator.m_pCell = a_rIterator.m_pCell->m_pForward;

		// See if we should descend the tree, and whether this
		// pixel-group could possibly match any search-window cells that
		// had to stay at this level of the tree.
		bPixelGroupStopsHere = a_rIterator.m_pBranch
			->DoesPixelGroupStopHere (a_rIterator.m_pSearch,
			m_tnTwiceTolerance, tnChildIndex, bMatchesAtThisLevel);

		// Loop through the cells attached to this branch, look for
		// matches, return if we find one.
		while (a_rIterator.m_pCell
			!= &(a_rIterator.m_pBranch->m_oSplitValue))
		{
#ifndef NDEBUG
			// If there are not supposed to be any matches at this
			// level of the search-tree, and the current search-window
			// cell is stuck at this level, then make sure there's no
			// match.
			if (!bMatchesAtThisLevel
				&& a_rIterator.m_pCell->m_eDoneSorting
					== SearchWindowCell::m_knDone)
			{
#ifdef CALCULATE_SAD
				assert (!a_rIterator.m_pCell->IsWithinTolerance
					(*(a_rIterator.m_pSearch), m_tnTolerance,
					a_rtnSAD));
#else // CALCULATE_SAD
				assert (!a_rIterator.m_pCell->IsWithinTolerance
					(*(a_rIterator.m_pSearch), m_tnTolerance));
#endif // CALCULATE_SAD
			}
#endif // NDEBUG

			// If the two pixel groups match, return the match.
			if ((bMatchesAtThisLevel
				|| a_rIterator.m_pCell->m_eDoneSorting
					!= SearchWindowCell::m_knDone)
			&& a_rIterator.m_pCell->IsWithinTolerance
					(*(a_rIterator.m_pSearch), m_tnTolerance
#ifdef CALCULATE_SAD
															, a_rtnSAD
#endif // CALCULATE_SAD
																	  ))
				return a_rIterator.m_pCell;

			// Move to the next cell to match against.
			a_rIterator.m_pCell = a_rIterator.m_pCell->m_pForward;
		}

		// We're done examining the cells in this node.  See if we
		// should descend the tree.
		if (bPixelGroupStopsHere)
		{
			// The search stops here.
			a_rIterator.m_pBranch = NULL;
			return NULL;
		}

		// Descend the tree.
		a_rIterator.m_pBranch
			= a_rIterator.m_pBranch->m_apBranches[tnChildIndex];
		if (a_rIterator.m_pBranch == NULL)
			return NULL;
		a_rIterator.m_pCell = &(a_rIterator.m_pBranch->m_oSplitValue);
	}
}



#endif // __SEARCH_WINDOW_H__
