/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1996-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                   Phong Vo <kpv@research.att.com>                    *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#include	"rshdr.h"

/*	Get a list of objects from a sorting context.
**	Derived contexts will be unionized.
**
**	Written by Kiem-Phong Vo (07/19/96)
*/

typedef struct _union_s
{	Rsobj_t*		obj;
	int			pos;
	struct _union_s*	equi;
} Union_t;

#define ADDOBJ(list,tail,obj)	(tail = tail ? (tail->right = obj) : (list = obj) )

/* Lists are kept in reverse order to ease data movement.
** Ties are broken by list positions to preserve stability.
*/
#if __STD_C
static int uninsert(Union_t** list, int n, Union_t* un)
#else
static int uninsert(list, n, un)
Union_t**	list;
int		n;
Union_t*	un;
#endif
{
	reg Rsobj_t	*obj, *o;
	reg Union_t	**l, **r, **m, *p, *h;
	reg int		cmp;

	obj = un->obj;
	r = (l = list) + n;

	if(n > 4)
	{	while(l != r)
		{	m = l + (r-l)/2;
			o = (*m)->obj;
			OBJCMP(o,obj,cmp);
			if(cmp == 0)
				l = r = m;
			else if(cmp > 0)
				l = l == m ? r : m;
			else	r = m;
		}
	}
	else
	{	for(r -= 1, cmp = 1; r >= l; --r)
		{	o = (*r)->obj;
			OBJCMP(o,obj,cmp);
			if(cmp > 0)
				{ l = r+1; break; }
			else if(cmp == 0)
				{ l = r; break; }
		}
	}

	if(cmp == 0)
	{	for(p = NIL(Union_t*), h = *l;; )
			if(un->pos < h->pos || !(p=h, h=h->equi) )
				break;
		un->equi = h;
		if(p)	p->equi = un;
		else	*l = un;
	}
	else
	{	for(r = list+n; r > l; --r)
			*r = *(r-1);
		*l = un; un->equi = NIL(Union_t*);
		n += 1;
	}

	return n;
}

#if __STD_C
static Rsobj_t* unionize(Rsobj_t** objlist, int n)
#else
static Rsobj_t* unionize(objlist, n)
Rsobj_t**	objlist;
int		n;
#endif
{
	reg int		p, cmp;
	reg Union_t	*un, *u, *pu;
	reg Rsobj_t	*obj, *o, *e, *list, *tail;
	Union_t		**ulist, *uarray;
	reg int		n_list;

	if(!(ulist = (Union_t**)vmalloc(Vmheap,n*sizeof(Union_t*))) )
		return NIL(Rsobj_t*);
	if(!(uarray = (Union_t*)vmalloc(Vmheap,n*sizeof(Union_t))) )
	{	vmfree(Vmheap,ulist);
		return NIL(Rsobj_t*);
	}

	for(p = 0, n_list = 0; p < n; ++p)
	{	/* set up header data for quick comparisons */
		for(obj = objlist[p]; obj; obj = obj->right)
			OBJHEAD(obj);

		uarray[p].obj = objlist[p];
		uarray[p].pos = p;
		n_list = uninsert(ulist,n_list,uarray+p);
	}

	list = tail = NIL(Rsobj_t*);
	while(n_list > 0)
	{	un = ulist[n_list -= 1];
		if(n_list == 0 && !un->equi)	/* last unmerged list */
		{	obj = un->obj;
			ADDOBJ(list,tail,obj);
		}
		else if(un->equi)	/* a set of equivalence classes */
		{	obj = un->obj; un->obj = obj->right;
			for(;;)
			{	u = un->equi;
				if(un->obj)
					n_list = uninsert(ulist,n_list,un);
				if(!(un = u) )
					break;

				/* union with equal list of obj */
				o = u->obj; u->obj = o->right;
				if((e = obj->equal) )
					e->left->right = o;
				else	obj->equal = e = o;

				if((o->right = o->equal) )
					e->left = o->equal->left;
				else	e->left = o;
			}
			obj->equal->left->right = NIL(Rsobj_t*);

			ADDOBJ(list,tail,obj);

			if(n_list == 0)
				tail->right = NIL(Rsobj_t*);
		}
		else /* at least 2 distinct lists are left */
		{	o = ulist[p = n_list-1]->obj;
			for(obj = un->obj;; )	/* keep peeling off least objects */
			{	un->obj = obj->right;
				ADDOBJ(list,tail,obj);

				if(!(obj = un->obj) )
					break;
				else
				{	OBJCMP(obj,o,cmp);
					if(cmp >= 0)
						break;
				}
			}
			if(obj)
			{	if(cmp > 0) /* new min element */
				{	ulist[n_list] = ulist[p];
					if(p == 0)
					{	n_list = 2;
						ulist[0] = un;
					}
					else if(uninsert(ulist,p,un) == p)
						ulist[p] = ulist[n_list];
					else	n_list += 1;
				}
				else /*if(cmp == 0) -- new equivalence class */
				{	for(pu = NIL(Union_t*), u = ulist[p];; )
						if(un->pos < u->pos ||
						   !(pu = u, u = u->equi) )
							break;
					un->equi = u;
					if(pu)	pu->equi = un;
					else	ulist[p] = un;
				}
			}
		}
	}

	vmfree(Vmheap,ulist);
	vmfree(Vmheap,uarray);

	return list;
}

#if __STD_C
Rsobj_t* rslist(Rs_t* rs)
#else
Rsobj_t* rslist(rs)
Rs_t*	rs;
#endif
{
	reg Rsobj_t	*list, *next, *p, *r, *t, *e;
	reg int		n, type;
	reg uchar*	k;

	if((type = rs->type)&RS_SORTED)
		return rs->sorted;

	if((list = (*rs->meth->listf)(rs)) && rs->n_list > 0)
	{	rs->list = (Rsobj_t**)vmresize(rs->vm,
				    rs->list, (rs->n_list+1)*sizeof(Rsobj_t*),
				    VM_RSCOPY|VM_RSMOVE);
		if(!rs->list)
			return NIL(Rsobj_t*);
		rs->list[rs->n_list] = list;
		rs->n_list += 1;
	}

	if(rs->n_list > 0)
	{	list = rs->n_list > 1 ? unionize(rs->list,rs->n_list) : rs->list[0];

		vmfree(rs->vm,rs->list);
		rs->list = NIL(Rsobj_t**);
		rs->n_list = 0;
	}

	if((type&RS_UNIQ) || !(type&RS_DATA) )
	{	for(r = list; r; r = r->right)
			if((e = r->equal) )
				e->left->right = NIL(Rsobj_t*);
	}
	else
	{	int	(*insertf)_ARG_((Rs_t*, Rsobj_t*)) = rs->meth->insertf;
		Rsobj_t*(*listf)_ARG_((Rs_t*)) = rs->meth->listf;

		for(p = NIL(Rsobj_t*), r = list; r; r = t)
		{	t = r->right;
			if(!(e = r->equal) )
			{	p = r;
				continue;
			}

			/* resort using whole data */
			r->right = e;
			e->left->right = NIL(Rsobj_t*);

			rs->type &= ~RS_KSAMELEN;
			if(type&RS_DSAMELEN)
				rs->type |= RS_KSAMELEN;

			for(; r; r = next)
			{	next = r->right;

				k = r->key;
				r->key = r->data;
				r->data = k;
				n = r->keylen;
				r->keylen = r->datalen;
				r->datalen = n;

				(*insertf)(rs,r);
			}

			r = (*listf)(rs);
			if(p)
				p->right = r;
			else	list = r;

			p = r->left;
			p->right = t;

			for(; r != t; r = r->right)
			{	k = r->key;
				r->key = r->data;
				r->data = k;
				n = r->keylen;
				r->keylen = r->datalen;
				r->datalen = n;

				if((e = r->equal) )
				{	e->left->right = NIL(Rsobj_t*);
					for(; e; e = e->right)
					{	k = e->key;
						e->key = e->data;
						e->data = k;
						n = e->keylen;
						e->keylen = e->datalen;
						e->datalen = n;
					}
				}
			}

			rs->type = type;
		}
	}

	if((type&RS_REVERSE) )
	{	for(p = NIL(Rsobj_t*), r = list; r; r = t)
		{	t = r->right;

			if((e = r->equal) )	/* invert equal list */
			{	reg Rsobj_t*	ende;
				for(list = r, ende = e->left; e != ende; e = next)
				{	next = e->right;
					e->right = list;
					list = e;
				}
				list->left = r; r->right = NIL(Rsobj_t*);
				r = ende; r->equal = list;
			}

			r->right = p;
			p = r;
		}
		list = p;
	}

	if((rs->sorted = list) )
		rs->type |= RS_SORTED;
	return list;
}
