/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/


/*
 * NAME
 *	cr.c
 *
 * DESCRIPTION
 *	This file contains routines that manage the creation of the HU grid
 *	structures.
 *
 */


#include <stdio.h>
#include <math.h>
#include "rt.h"



GRID	*gridlist = NULL;

/* 
	Note: gridlist doesn't need to be an array since it is only used when
	building HUG, which is done before child process creation 
*/



/*
 * NAME
 *	prn_gridlist - print HU grid to stdout
 *
 * SYNOPSIS
 *	VOID	prn_gridlist()
 *
 * RETURNS
 *	Nothing.
 */

VOID	prn_gridlist()
	{
	GRID	*g;

	fprintf(stderr, "    Print Gridlist \n");
	g = gridlist;

	while (g != NULL)
		{
		prn_grid(g);
		g = g->next;
		}

	fprintf(stderr, "    End Gridlist \n");
	}



/*
 * NAME
 *	prn_ds_stats - print HU grid data structure statistics to stdout
 *
 * SYNOPSIS
 *	VOID	prn_ds_stats()
 *
 * RETURNS
 *	Nothing.
 */

VOID	prn_ds_stats()
	{
	INT	leafs;
	INT	voxels;

	printf("\n");
	printf("****** Hierarchical uniform grid data structure statistics ******\n\n");

	printf("    Data structure evaluated as a preprocess.\n");

	printf("    gridsize                             %3ld \n", hu_gridsize);
	printf("    hashtable buckets                    %3ld \n", hu_numbuckets);
	printf("    maximum subdivision level            %3ld \n", hu_max_subdiv_level);
	printf("    maximum primitives / cell            %3ld \n", hu_max_prims_cell);
	printf("    grids                                %3ld \n", grids);

	voxels = empty_voxels + nonempty_voxels;
	leafs  = empty_voxels + nonempty_leafs;

	printf("    empty voxels                    %8ld    %3ld %% \n",          empty_voxels,    100*empty_voxels/voxels);
	printf("    nonempty voxels                 %8ld    %3ld %% \n",          nonempty_voxels, 100*nonempty_voxels/voxels);
	printf("    empty leafs                     %8ld    %3ld %% \n",          empty_voxels,    100*empty_voxels/leafs);
	printf("    nonempty leafs                  %8ld    %3ld %% \n",          nonempty_leafs,  100*nonempty_leafs/leafs);
	printf("    primitives / leaf                 %6.1lf \n",               (double)prims_in_leafs/leafs);
	printf("\n");
	}



/*
 * NAME
 *	init_masks - initialize empty and nonempty mask arrays
 *
 * SYNOPSIS
 *	VOID	init_masks()
 *
 * RETURNS
 *	Nothing.
 */

VOID	init_masks()
	{
	INT	n, i;

	n = sizeof(UINT)*8;

	for (i = 0; i < n; i++)
		{
		empty_masks[i]	  = (MSB >> i);
		nonempty_masks[i] = ~empty_masks[i];
		}

	}


/*
 * NAME
 *	init_world_grid -
 *
 * SYNOPSIS
 *	GRID	*init_world_grid(v, pepa, num_pe)
 *	VOXEL	*v;
 *	ELEMENT **pepa; 	// Prim elements in voxel containing grid.
 *	INT	num_pe; 	// # of prim elements in voxel containing grid.
 *
 * RETURNS
 *	A pointer to the grid.
 *
 */

GRID	*init_world_grid(v, pepa, num_pe)
VOXEL	*v;
ELEMENT **pepa;
INT	num_pe;
	{
	UINT	*ec;
	UINT	*pc;
	BBOX	wbox;
	GRID	*g;
	VOXEL	**ht;

	g  = ObjectMalloc(OT_GRID, 1);
	g->id = grids++;

	ht = ObjectMalloc(OT_HASHTABLE, 1);

	g->hashtable	= ht;
	g->hashtable[0] = v;

	ec = ObjectMalloc(OT_EMPTYCELLS, 1);

	g->emptycells	 = ec;
	g->emptycells[0] = 0;	    /* nonempty */

	g->indx_inc[0] = 1;
	g->indx_inc[1] = 1;
	g->indx_inc[2] = 1;
	g->num_buckets = 1;

	wbox.dnear[0] = 0.0;
	wbox.dnear[1] = 0.0;
	wbox.dnear[2] = 0.0;
	wbox.dfar[0]  = 1.0;
	wbox.dfar[1]  = 1.0;
	wbox.dfar[2]  = 1.0;

	g->min[0] = wbox.dnear[0];
	g->min[1] = wbox.dnear[1];
	g->min[2] = wbox.dnear[2];

	g->cellsize[0] = wbox.dfar[0] - wbox.dnear[0];
	g->cellsize[1] = wbox.dfar[1] - wbox.dnear[1];
	g->cellsize[2] = wbox.dfar[2] - wbox.dnear[2];

	g->subdiv_level = - 1;
	g->num_prims	= num_pe;
	g->pepa 	= pepa;
	g->next 	= NULL;

	gridlist = g;
	return (g);
	}



/*
 * NAME
 *	init_world_voxel -
 *
 * SYNOPSIS
 *	VOXEL	*init_world_voxel(pepa, numelements)
 *	ELEMENT **pepa;
 *	INT	numelements;
 *
 * RETURNS
 *	A pointer to the voxel.
 *
 */

VOXEL	*init_world_voxel(pepa, numelements)
ELEMENT **pepa;
INT	numelements;
	{
	VOXEL	*v;

	v = ObjectMalloc(OT_VOXEL, 1);

	nonempty_voxels++;

	v->id	       = 0;
	v->cell        = (CHAR *)pepa;
	v->numelements = numelements;
	v->celltype    = GSM_VOXEL;
	v->next        = NULL;

	return (v);
	}



/*
 * NAME
 *     mark_empty - mark grid as being empty
 *
 * SYNOPSIS
 *     VOID   mark_empty(index1D, g)
 *     INT    index1D;
 *     GRID   *g;
 *
 * RETURNS
 *	Nothing.
 */

VOID	mark_empty(index1D, g)
INT	index1D;
GRID	*g;
	{
	INT	i, r;
	UINT	w;

	i = index1D/(sizeof(UINT)*8);
	r = index1D - i*sizeof(UINT)*8;

	w = g->emptycells[i];
	w = w | empty_masks[r];
	g->emptycells[i] = w;
	}



/*
 * NAME
 *     mark_nonempty - mark grid as being nonempty
 *
 * SYNOPSIS
 *     VOID   mark_nonempty(index1D, g)
 *     INT    index1D;
 *     GRID   *g;
 *
 * RETURNS
 *	Nothing.
 */

VOID	mark_nonempty(index1D, g)
INT	index1D;
GRID	*g;
	{
	INT	i, r;
	UINT	w;

	i = index1D/(sizeof(UINT)*8);
	r = index1D - i*sizeof(UINT)*8;

	w = g->emptycells[i];
	w = w & nonempty_masks[r];
	g->emptycells[i] = w;
	}



/*
 * NAME
 *	insert_in_hashtable - insert voxel from grid into hashtable
 *
 * SYNOPSIS
 *	VOID	insert_in_hashtable(v, g)
 *	VOXEL	*v;
 *	GRID	*g;
 *
 * RETURNS
 *	Nothing.
 */

VOID	insert_in_hashtable(v, g)
VOXEL	*v;
GRID	*g;
	{
	INT	i, r;
	VOXEL	*vht;

	i = v->id/hu_numbuckets;
	r = v->id - i*hu_numbuckets;

	vht = g->hashtable[r];
	v->next = vht;
	g->hashtable[r] = v;
	}


/*
 * NAME
 *	**prims_in_box2 -
 *
 * SYNOPSIS
 *	ELEMENT **prims_in_box2(pepa, n_in, b, n)
 *	ELEMENT **pepa; 		// Array of element ptrs to check.
 *	INT	n_in;			// Number of elements in array.
 *	BBOX	b;			// Bounding box to check.
 *	INT	*n;			// Number of elements in the box.
 *
 * RETURNS
 *	An array of PrimElement pointers for those primitive elements in the
 *	bounding box.
 */

ELEMENT **prims_in_box2(pepa, n_in, b, n)
ELEMENT **pepa;
INT	n_in;
BBOX	b;
INT	*n;
	{
	INT	ovlap, i, j, k;
	ELEMENT *pe;
	ELEMENT **npepa;
	BBOX	bb;
	REAL	max, side, eps;

	max  = b.dfar[0] - b.dnear[0];
	side = b.dfar[1] - b.dnear[1];

	max  = max > side ? max : side;
	side = b.dfar[2] - b.dnear[2];

	max  = max > side ? max : side;
	eps  = max * 1.0E-6;

	if (n_in > 0)
		npepa = ObjectMalloc(OT_PEPARRAY, n_in);
	else
		{
		npepa == NULL;
		*n = 0;
		return (npepa);
		}

	*n = 0;
	k  = 0;

	for (j = 0; j < n_in; j++)
		{
		pe = pepa[j];
		bb = pe->bv;
		ovlap = 1;

		for (i = 0; i < 1; i++)
			{
			if (b.dnear[0] > bb.dfar[0] + eps)
				{
				ovlap = 0;
				break;
				}

			if (b.dnear[1] > bb.dfar[1] + eps)
				{
				ovlap = 0;
				break;
				}

			if (b.dnear[2] > bb.dfar[2] + eps)
				{
				ovlap = 0;
				break;
				}

			if (b.dfar[0] < bb.dnear[0] - eps)
				{
				ovlap = 0;
				break;
				}

			if (b.dfar[1] < bb.dnear[1] - eps)
				{
				ovlap = 0;
				break;
				}

			if (b.dfar[2] < bb.dnear[2] - eps)
				{
				ovlap = 0;
				break;
				}
			}

		if (ovlap == 1)
			{
			npepa[k++] = pepa[j];
			(*n)++;
			}
		}

	return (npepa);
	}



/*
 * NAME
 *	init_bintree - initialize (create) rootnode of bintree
 *
 * SYNOPSIS
 *	BTNODE	*init_bintree(ng)
 *	GRID	*ng;
 *
 * RETURNS
 *	A pointer to the newly created bintree root.
 *
 */

BTNODE	*init_bintree(ng)
GRID	*ng;
	{
	BTNODE	*btn;
	ELEMENT **pepa;

	btn = ObjectMalloc(OT_BINTREE, 1);

	btn->btn[0] = NULL;
	btn->btn[1] = NULL;
	btn->axis   = -1;

	btn->p[0] = ng->min[0];
	btn->p[1] = ng->min[1];
	btn->p[2] = ng->min[2];

	btn->n[0] = ng->indx_inc[1];
	btn->n[1] = ng->indx_inc[1];
	btn->n[2] = ng->indx_inc[1];

	btn->i[0] = 0;
	btn->i[1] = 0;
	btn->i[2] = 0;

	btn->nprims = ng->num_prims;
	btn->totalPrimsAllocated = btn->nprims;

	if (ng->num_prims > 0)
		btn->pe = ng->pepa;
	else
		btn->pe = NULL;

	return (btn);
	}


/*
 * NAME
 *	subdiv_bintree
 *
 * SYNOPSIS
 *	VOID	subdiv_bintree(btn, g)
 *	BTNODE	*btn;			// Current bintree node.
 *	GRID	*g;			// Grid being created.
 *
 * DESCRIPTION
 *	The bintree node is subdivided at the largest dimension and the
 *	primitive element list is pruned to the two new nodes.
 *
 * RETURNS
 *	Nothing.
 *
 */

VOID	subdiv_bintree(btn, g)
BTNODE	*btn;
GRID	*g;
	{
	BTNODE	*btn1, *btn2;
	INT	 n1, n2, imax, dmax;
	BBOX	 b1, b2;

	/* Find largest dimension. */

	imax = 0;
	dmax = btn->n[0];

	if (dmax < btn->n[1])
		{
		imax = 1;
		dmax = btn->n[1];
		}

	if (dmax < btn->n[2])
		{
		imax = 2;
		dmax = btn->n[2];
		}

	/* Subdiv largest dimension. */

	btn->axis   = imax;
	btn->btn[0] = NULL;
	btn->btn[1] = NULL;

	if (btn->n[imax] > 1)
		{
		n1 = btn->n[imax] / 2;
		n2 = btn->n[imax] - n1;

		btn1 = ObjectMalloc(OT_BINTREE, 1);
		btn2 = ObjectMalloc(OT_BINTREE, 1);

		btn->btn[0] = btn1;
		btn->btn[1] = btn2;

		btn1->btn[0] = NULL;
		btn1->btn[1] = NULL;
		btn1->axis   = -1;

		btn2->btn[0] = NULL;
		btn2->btn[1] = NULL;
		btn2->axis   = -1;

		btn1->i[0] = btn->i[0];
		btn1->i[1] = btn->i[1];
		btn1->i[2] = btn->i[2];

		btn2->i[0] = btn->i[0];
		btn2->i[1] = btn->i[1];
		btn2->i[2] = btn->i[2];
		btn2->i[imax] += n1;

		btn1->n[0] = btn->n[0];
		btn1->n[1] = btn->n[1];
		btn1->n[2] = btn->n[2];
		btn1->n[imax] = n1;

		btn2->n[0] = btn->n[0];
		btn2->n[1] = btn->n[1];
		btn2->n[2] = btn->n[2];
		btn2->n[imax] = n2;

		btn1->p[0] = btn->p[0];
		btn1->p[1] = btn->p[1];
		btn1->p[2] = btn->p[2];

		btn2->p[0] = btn->p[0];
		btn2->p[1] = btn->p[1];
		btn2->p[2] = btn->p[2];
		btn2->p[imax] = btn->p[imax] + n1 * g->cellsize[imax];

		b1.dnear[0] = btn1->p[0];
		b1.dnear[1] = btn1->p[1];
		b1.dnear[2] = btn1->p[2];
		b1.dfar[0]  = btn1->p[0] + btn1->n[0] * g->cellsize[0];
		b1.dfar[1]  = btn1->p[1] + btn1->n[1] * g->cellsize[1];
		b1.dfar[2]  = btn1->p[2] + btn1->n[2] * g->cellsize[2];

		btn1->pe    = prims_in_box2(btn->pe, btn->nprims, b1, &(btn1->nprims));
		btn1->totalPrimsAllocated = btn->nprims;

		b2.dnear[0] = btn2->p[0];
		b2.dnear[1] = btn2->p[1];
		b2.dnear[2] = btn2->p[2];
		b2.dfar[0]  = btn2->p[0] + btn2->n[0] * g->cellsize[0];
		b2.dfar[1]  = btn2->p[1] + btn2->n[1] * g->cellsize[1];
		b2.dfar[2]  = btn2->p[2] + btn2->n[2] * g->cellsize[2];

		btn2->pe    = prims_in_box2(btn->pe, btn->nprims, b2, &(btn2->nprims));
		btn2->totalPrimsAllocated = btn->nprims;
		
		}

	if (btn1->n[0] == 1 && btn1->n[1] == 1 && btn1->n[2] == 1)
		{
		/* further optimize the pe so no space is wasted !! */
		}

	if (btn2->n[0] == 1 && btn2->n[1] == 1 && btn2->n[2] == 1)
		{
		/* further optimize the pe so no space is wasted !! */
		}
	}



/*
 * NAME
 *	create_bintree - subdivide tree until all leaf nodes have gridsizes of
 *			 (1, 1, 1).
 *
 * SYNOPSIS
 *	VOID	create_bintree(root , g)
 *	BTNODE	*root;			// Root of bintree.
 *	GRID	*g;			// Grid being created.
 *
 * RETURNS
 *	Nothing.
 */

VOID	create_bintree(root , g)
BTNODE	*root;
GRID	*g;
	{
	BTNODE	*btn;

	/* It is assumed that root's children are NULL on entry. */

	btn = root;
	if (btn->n[0] != 1 || btn->n[1] != 1 || btn->n[2] != 1)
		{
		subdiv_bintree(btn, g);
		create_bintree(btn->btn[0], g);
		create_bintree(btn->btn[1], g);

		if ((btn->nprims) > 0)
			{
		/*	ObjectFree(OT_PEPARRAY, btn->totalPrimsAllocated, btn->pe);
			btn->pe = NULL;     */
			}
		}
	}



/*
 * NAME
 *	bintree_lookup -
 *
 * SYNOPSIS
 *	ELEMENT **bintree_lookup(root, i, j, k, g, n)
 *	BTNODE	*root;			// Root of bintree.
 *	INT	i, j, k;		// 3D indecies of cell in grid.
 *	GRID	*g;			// Grid for this bintree.
 *	INT	*n;			// # prims.
 *
 * DESCRIPTION
 *	Receive the 3D indices of the cell in the grid and return a pointer to
 *	an array of elments and the number of elements on the list.
 *
 * RETURNS
 *	An array of elements associated with the voxel
 *
 */

ELEMENT **bintree_lookup(root, i, j, k, g, n)
BTNODE	*root;
INT	i, j, k;
GRID	*g;
INT	*n;
	{
	INT	l,x;
	INT	ijk[3];
	INT	child;
	INT	idiv;
	ELEMENT **pepa;
	BTNODE	*btn;

	ijk[0] = i;
	ijk[1] = j;
	ijk[2] = k;

	btn = root;

	if (btn == NULL)
		{
		*n  = 0;
		return (NULL);
		}

	while (btn->n[0] != 1 || btn->n[1] != 1 || btn->n[2] != 1)
		{
		if (btn->axis == - 1)
			{
			/* Not a leaf & not subdivided but should be subdivided. */

			fprintf(stderr, "bintree_lookup: gridsizes (%ld, %ld, %ld), axis %ld & nprims %ld\n",
				btn->n[0], btn->n[1], btn->n[2], btn->axis, btn->nprims);

			exit(-1);
			}

		/* Descend the tree: find which branch. */

		child = 0;
		idiv  = (btn->n[btn->axis]/2);

		if (ijk[btn->axis] + 1 > idiv)
			{
			child = 1;
			ijk[btn->axis] -= idiv;
			}

		/* Child is now the correct branch. */

		btn = btn->btn[child];
		if (btn == NULL)
			{
			*n  = 0;
			return (NULL);
			}
		}

	/* Now at a leaf. */

	*n   = btn->nprims;
	pepa = btn->pe;

/*	if (btn->nprims == btn->totalPrimsAllocated)
		pepa = btn->pe;
	else
		{
		if (btn->pe)
			pepa = GlobalRealloc(btn->pe, btn->nprims*sizeof(ELEMENT *));
		else
			pepa = NULL;


		pepa = ObjectMalloc(OT_PEPARRAY, btn->nprims);
		for (x = 0; x < btn->nprims; x++)
			pepa[x] = btn->pe[x];

		ObjectFree(OT_PEPARRAY, btn->totalPrimsAllocated, btn->pe);
		btn->pe = NULL;
		}
*/
	return (pepa);
	}



/*
 * NAME
 *	deleteBinTree - delete the entire bintree and free its memory
 *
 * SYNOPSIS
 *	VOID	deleteBinTree(binTree)
 *	BTNODE	*binTree;
 *
 * DESCRIPTION
 *	Delete the entire bintree by recursively calling this procedure.
 *
 * RETURNS
 *	Nothing.
 */

VOID	deleteBinTree(binTree)
BTNODE	*binTree;
	{
	BTNODE *left, *right;

	if (binTree != NULL)
		{
		left  = binTree->btn[0];
		right = binTree->btn[1];

		deleteBinTree(left);
		deleteBinTree(right);
/*		ObjectFree(OT_BINTREE, 1, binTree);    */
		}
	}



/*
 * NAME
 *	create_grid -
 *
 * SYNOPSIS
 *	GRID	*create_grid(v, g, num_prims)
 *	VOXEL	*v;
 *	GRID	*g;
 *	INT	num_prims;	// # of prim elem in voxel to be subdivided.
 *
 * DESCRIPTION
 *
 *		Accept a voxel with an ELEMENT array and a grid and recursively
 *		uniformly subdivide the voxel to produce a new grid.  Create a
 *		new list of prim elements pruned to each new voxel.  If the
 *		list is non NULL create a voxel for it.
 *
 *	In all cases:
 *
 *		Place a pointer to the new grid in the input voxel and mark
 *		the celltype as GSM_GRID.  Return a pointer to the new grid.
 *		Link all new grids on to the list gridlist for debug purposes.
 *
 * RETURNS
 *	A pointer to the grid.
 */

GRID	*create_grid(v, g, num_prims)
VOXEL	*v;
GRID	*g;
INT	num_prims;
	{
	INT	n;
	INT	i, j, k, r;
	INT	nprims;
	INT	index1D;
	UINT	*ec;
	UINT	*pc;
	R64	nec, unsgn, ncells;
	GRID	*ng, *nng;	/* New grid. */
	BBOX	b;
	VOXEL	*nv;
	VOXEL	**ht;
	BTNODE	*bintree;
	ELEMENT **pepa;

	ng = ObjectMalloc(OT_GRID, 1);
	ng->id = grids++;

	ht = ObjectMalloc(OT_HASHTABLE, hu_numbuckets);
	ng->hashtable = ht;

	ncells = (R64)(hu_gridsize * hu_gridsize * hu_gridsize);
	total_cells += ncells;

	ec = ObjectMalloc(OT_EMPTYCELLS, (INT)ncells);
	ng->emptycells = ec;

	ng->num_prims	= num_prims;
	ng->pepa	= (ELEMENT**)v->cell;
	ng->indx_inc[0] = 1;
	ng->indx_inc[1] = hu_gridsize;
	ng->indx_inc[2] = hu_gridsize * hu_gridsize;
	ng->num_buckets = hu_numbuckets;

	k = v->id/g->indx_inc[2];
	r = v->id - k*g->indx_inc[2];
	j = r/g->indx_inc[1];
	i = r - j*g->indx_inc[1];

	ng->min[0] = g->min[0] + i * g->cellsize[0];
	ng->min[1] = g->min[1] + j * g->cellsize[1];
	ng->min[2] = g->min[2] + k * g->cellsize[2];

	ng->cellsize[0]  = g->cellsize[0]/ng->indx_inc[1];
	ng->cellsize[1]  = g->cellsize[1]/ng->indx_inc[1];
	ng->cellsize[2]  = g->cellsize[2]/ng->indx_inc[1];
	ng->subdiv_level = g->subdiv_level + 1;
	ng->next	 = gridlist;

	gridlist = ng;

	/* Calculate hierarchical grid */

	/* First create bintree. */

	bintree = init_bintree(ng);
	create_bintree(bintree, ng);

	index1D = 0;
	n = hu_gridsize;

	/*
	 *	For each cell in new grid, create an ELEMENT array for the
	 *	cell; if non empty create a voxel for it.  If nonempty
	 *	cell has more than MAX_PRIMS_PER_CELL (hu_max_prims_cell) primitives and
	 *	MAX_SUBDIV_LEVEL (hu_max_subdiv_level) has not been reached, create a
	 *	new grid (i.e. subdivide) and insert it in the hashtable.  If nonempty
	 *	cell has less than MAX_PRIMS_PER_CELL insert it in the
	 *	hashtable.  In any case make the appropriate entry in
	 *	emptycells.
	 */

	for (k = 0; k < n; k++)
		{
		for (j = 0; j < n; j++)
			{
			for (i = 0; i < n; i++)
				{
				pepa = bintree_lookup(bintree, i, j, k, ng, &nprims);

				if (pepa != NULL)
					{
					nonempty_voxels++;
					mark_nonempty(index1D, ng);

					nv = ObjectMalloc(OT_VOXEL, 1);

					nv->id		= index1D;
					nv->celltype	= GSM_VOXEL;
					nv->cell	= (CHAR*)pepa;
					nv->numelements = nprims;

					if (nprims > hu_max_prims_cell && ng->subdiv_level < hu_max_subdiv_level)
						nng = create_grid(nv, ng, nprims);
					else
						{
						/* Leaf cell. */

						nonempty_leafs++;
						prims_in_leafs += nprims;
						}

					insert_in_hashtable(nv, ng);
					}
				else
					{
					/* Empty cell. */

					empty_voxels++;
					mark_empty(index1D, ng);
					}

				index1D++;
				}
			}
		}

	/* Store new grid ptr in input voxel. */

	v->cell        = (CHAR *)ng;
	v->numelements = -1;	 /* this field doesn't make sence if cell is a GRID */
	v->celltype    = GSM_GRID;

	deleteBinTree(bintree);

	return (ng);
	}

