/* ********* ShiftSort module---implementation ********* */


#include <stdio.h>
#include <stdlib.h>
#include "kwic.h"
#include "LineStorage.h"
#include "ShiftSort.h"

/* **** local constants **** */

/* **** local types **** */

typedef struct {
int lineNum;
int shiftNum;
} LineList,*LineListPtr;

/* **** local variables **** */

static LineListPtr lineList;
static int lineCount;

/***** state invariant *****

1. lineCount >= 0

2. if lineCount == 0 then
lineList == NULL

3. if lineCount > 0
lineList is a pointer to an array of lineCount lineLists's

4. forall i in [0..lineCount-1]
lineList[i].lineNum in [0..LSNumLines()-1]
lineList[i].shiftNum in [0..LSNumWords(lineList[i].lineNum)-1]

*/

/* **** local functions **** */

/* return word wordNum from the shifted LineStorage line specified by linePtr */
static const char* getWord(LineListPtr linePtr,int wordNum)
{
int shiftedWordNum;

shiftedWordNum = (wordNum+linePtr->shiftNum) %
LSNumWords(linePtr->lineNum);
return LSGetWord(linePtr->lineNum,shiftedWordNum);
}

/*
* if line0 < line1
* return a negative value
* else if line0 == line1
* return 0
* else
* return a positive value
*/
static int lineCompare(LineListPtr line0,LineListPtr line1)
{
int i,j,minNumWords,minLine;

if (LSNumWords(line0->lineNum) < LSNumWords(line1->lineNum))
minNumWords = LSNumWords(line0->lineNum);
else
minNumWords = LSNumWords(line1->lineNum);
for (i = 0; i < minNumWords; i++) {
j = strcmp(getWord(line0,i),getWord(line1,i));
if (j != 0)
return j;
}
/* assert: line0 and line1 are equal for the first min words */
return LSNumWords(line0->lineNum) - LSNumWords(line1->lineNum);
}

/* ****exported functions**** */

void SSInit(void)
{
lineList = NULL;
lineCount = 0;
}

void SSReset(void)
{
if (lineList != NULL)
free(lineList);
lineList = NULL;
lineCount = 0;
}

KWStatus SSShiftSort(void)
{
int i,j,k;

/* compute the size of lineList */
lineCount = 0;
for (i = 0; i < LSNumLines(); i++) {
for (j = 0; j < LSNumWords(i); j++) {
/* exclude lines that start with a noise word */
if (!WTIsMember(LSGetWord(i,j)))
lineCount++;
}
}

/* allocate space for lineList */
lineList = calloc(lineCount, sizeof(LineList));
if (lineList == NULL) {
lineCount = 0;
return KWMEMORYERROR;
}

/* fill lineList */
k = 0;
for (i = 0; i < LSNumLines(); i++) {
for (j = 0; j < LSNumWords(i); j++) {
/* exclude lines that start with a noise word */
if (!WTIsMember(LSGetWord(i,j))) {
lineList[k].lineNum = i;
lineList[k].shiftNum = j;
k++;
}
}
}

/* sort the shifted lines */
qsort(lineList,lineCount,sizeof(LineList),
(int (*)(void *,void *))lineCompare);

return KWSUCCESS;
}

const char* SSGetWord(int lineNum,int wordNum)
{
if (lineNum < 0 || lineNum >= lineCount || wordNum < 0 ||
wordNum >= LSNumWords(lineList[lineNum].lineNum))
return NULL;
else
return getWord(&lineList[lineNum],wordNum);
}

int SSNumWords(int lineNum)
{
if (lineNum < 0 || lineNum >= lineCount)
return(KWRANGEERROR);
else
return(LSNumWords(lineList[lineNum].lineNum));
}

int SSGetShiftNum(int lineNum)
{
if (lineNum < 0 || lineNum >= lineCount)
return(KWRANGEERROR);
else
return(lineList[lineNum].shiftNum);
}

int SSNumLines(void)
{
return(lineCount);
}

void SSPrintState(void)
{
int i;

for (i = 0; i < lineCount; i++)
printf("lineList[%d]: lineNum=%d shiftNum=%d\n",
i,lineList[i].lineNum,lineList[i].shiftNum);
}