/*	SDB - sort routines	*/

#include "bdscio.h"
#include "dbqdefs.h"

struct skey *get_skeys(sptr)
	struct scan *sptr;
{
	struct skey *skeys,*newskey,*lastskey;

	skeys = lastskey = NULL;
	while (TRUE) {
		if (db_ntoken() != ID)
			{ RETERR(SYNTAX) }

		if ((newskey = CALLOC(KEYSIZE)) == NULL)
			{ RETERR(INSMEM) }

		newskey->sk_next = NULL;
		if (!sfind_attr(sptr,newskey,dbv_tstring)) {
			CFREE(newskey);
			return (NULL);
		}
		if (db_token() == ASCENDING || dbv_token == DESCENDING) {
			newskey->sk_type = dbv_token;
			db_ntoken();
		}
		else
			newskey->sk_type = ASCENDING;

		if (lastskey == NULL)
			skeys = newskey;
		else
			lastskey->sk_next = newskey;
		lastskey = newskey;

		if (db_token() != ',')
			break;
		db_ntoken();
	}
	return (skeys);
}

int *db_sort(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
	char *fmt;
{
	struct scan *sptr1,*sptr2,*sptr3;
	struct skey *skeys;
	int result;
#ifdef DBPI
	if (fmt != NULL)
		db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);
#endif
	result = FALSE;		/* be pessimistic */
	if (db_token() == ID)
		db_ntoken();
	else
		strcpy(dbv_tstring,"current");
	if ((sptr1 = db_ropen(dbv_tstring)) == NULL)
		goto sortex;
	if ((sptr2 = db_ropen(dbv_tstring)) == NULL)
		goto sortx1;
#ifdef QUICKSORT
	if ((sptr3 = db_ropen(dbv_tstring)) == NULL)
		goto sortx2;
#endif
	if (db_ntoken() != BY) {
		dbv_errcode = SYNTAX;
		goto sortx3; }
	if ((skeys = get_skeys(sptr1)) == NULL)
		goto sortx3;

	result = dqsort(skeys,sptr1,sptr2,sptr3);

	free_skeys(skeys);

sortx3:
#ifdef QUICKSORT
	db_rclose(sptr3);
#endif
sortx2:
	db_rclose(sptr2);
sortx1:
	db_rclose(sptr1);
sortex:
	return (result);
}

free_skeys(skeys)
	struct skey *skeys;
{
	struct skey *thisskey;

	for (thisskey = skeys; skeys != NULL; thisskey = skeys) {
		skeys = skeys->sk_next;
		CFREE(thisskey);
	}
}

int sfind_attr(sptr,newskey,aname)
	struct scan *sptr; struct skey *newskey; char *aname;
{
	struct attribute *aptr;
	int i,astart;
	astart = 1;
	for (i = 0; i < NATTRS; i++) {
		aptr = &sptr->sc_relation->rl_header.hd_attrs[i];

		if (aptr->at_name[0] == 0)
			break;

		if (db_sncmp(aname,aptr->at_name,ANSIZE) == 0) {
			newskey->sk_start = astart;
			newskey->sk_aptr = aptr;
			return (TRUE);
		}

		astart += aptr->at_size;
	}

	RETERR(ATUNDF)
}

int dqsort(skeys,sptr1,sptr2,sptr3)
	struct skey *skeys; struct scan *sptr1,*sptr2,*sptr3;
{
	int passes, swaps;
	int i,j,m,n;
	int rec1,rec2,rec3;
	int k, l, r;	/* orig */

	passes = 0; swaps = 0;

#ifdef QUICKSORT

	rec1 = 0; rec2 = 0; rec3 = 0;
	n = sptr1->sc_relation->rl_tcnt;
	m = n;

	while(m>1) {
		passes++;
		if ((m/=3) < 1) m = 1;
		for (j=1; j<=n-m; j++) {
		  if (rec1 != j+m) {
			if (!db_rget(sptr1, rec1=j+m)) return (FALSE);
			}
		  for (i=j; i>=1; i-=m) {
			if (rec2 != i) {
				if (!db_rget(sptr2, rec2=i)) return (FALSE);
				}
			if (scompare(skeys,sptr1,sptr2) > 0)
				break;
			if (rec3 != i+m) {
				if (!db_rget(sptr3, rec3=i+m)) return (FALSE);
				}
			sassign( sptr3, sptr2);
			swaps++;
			}
		  if (rec1 != i+m) {
			if (rec3 != i+m) {
				if (!db_rget(sptr3, rec3=i+m)) return (FALSE);
				}
			sassign( sptr3, sptr1);
			swaps++;
			}
		  }
		}

#else
/*	original sort	*/

	l = 2;
	r = sptr1->sc_relation->rl_tcnt;
	k = r;

	do {
		for (j = r; j >= l; j--) {
			if (!db_rget(sptr1,j-1))
				return (FALSE);
			if (!db_rget(sptr2,j))
				return (FALSE);
			if (scompare(skeys,sptr1,sptr2) > 0) {
				sswap(sptr1,sptr2);
				k = j;
				swaps++;
			}
		}
		l = k + 1;
		for (j = l; j <= r; j++) {
			if (!db_rget(sptr1,j-1))
				return (FALSE);
			if (!db_rget(sptr2,j))
				return (FALSE);
			if (scompare(skeys,sptr1,sptr2) > 0) {
				sswap(sptr1,sptr2);
				k = j;
				swaps++;
			}
		}
		r = k - 1;
		passes++;
	} while (l <= r);

#endif

	printf("%d swaps in %d passes\n",swaps,passes);

	return (TRUE);
}

int scompare(skeys,sptr1,sptr2)
	struct skey *skeys; struct scan *sptr1,*sptr2;
{
	struct skey *cskey;
	int result;
	for (cskey = skeys; cskey != NULL; cskey = cskey->sk_next)
		if ((result = cattr(cskey,sptr1,sptr2)) != 0)
			break;
	return (result);
}

int cattr(cskey,sptr1,sptr2)
	struct skey *cskey; struct scan *sptr1,*sptr2;
{
	int astart,aend,i;

	astart = cskey->sk_start;
	aend = astart + cskey->sk_aptr->at_size;
	for (i = astart; i < aend; i++)
		if (sptr1->sc_tuple[i] != sptr2->sc_tuple[i])
			break;
	if (i == aend)
		return (0);

	if (sptr1->sc_tuple[i] < sptr2->sc_tuple[i])
		if (cskey->sk_type == ASCENDING)
			return (-1);
		else
			return (1);
	else
		if (cskey->sk_type == ASCENDING)
			return (1);
		else
			return (-1);
}

#ifdef QUICKSORT
int sassign(sptr1,sptr2)
	struct scan *sptr1,*sptr2;
{
	unsigned tnum1, tnum2;

	tnum1 = sptr1->sc_atnum;

	if (!db_rput(sptr2,tnum1))
		return (FALSE);
	return (TRUE);
}

#else

int sswap(sptr1,sptr2)
	struct scan *sptr1,*sptr2;
{
	unsigned tnum1, tnum2;

	tnum1 = sptr1->sc_atnum;
	tnum2 = sptr2->sc_atnum;

	if (!db_rput(sptr1,tnum2))
		return (FALSE);
	if (!db_rput(sptr2,tnum1))
		return (FALSE);
	return (TRUE);
}

#endif

	if (!db_rput(sptr1,tnum2))
		return (FALSE);
	if (!db_rput(sptr2,tnum1))
		return (FALSE);
	return (TRUE);
