/* Sparse Matrix Interface */

#include "portable.h"
#include "eval.h"
#include "object.h"
#include "architecture.h"
#include "number.h"
#include "steering.h"
#include "primitive.h"
#include "io.h"

#include <time.h>
#include <stdlib.h>

#include <stdio.h>
#include "klu.h"

int Sparse (void)
{
int    n = 5 ;
int    Ap [ ] = {0, 2, 5, 9, 10, 12} ;
int    Ai [ ] = { 0,  1,  0,   2,  4,  1,  2,  3,   4,  2,  1,  4} ;
double Ax [ ] = {2., 3., 3., -1., 4., 4., -3., 1., 2., 2., 6., 1.} ;
double b [ ] = {8., 45., -3., 3., 19.} ;

    klu_symbolic *Symbolic ;
    klu_numeric *Numeric ;
    klu_common Common ;
    int i ;
    klu_defaults (&Common) ;
    Symbolic = klu_analyze (n, Ap, Ai, &Common) ;
    Numeric = klu_factor (Ap, Ai, Ax, Symbolic, &Common) ;
    klu_solve (Symbolic, Numeric, 5, 1, b, &Common) ;
    klu_free_symbolic (&Symbolic, &Common) ;
    klu_free_numeric (&Numeric, &Common) ;
    for (i = 0 ; i < n ; i++) printf ("x [%d] = %g\n", i, b [i]) ;
    return (0) ;
}

#define MAXSYMBOL 100

struct symboltab {
	klu_symbolic *address;
	int time;
	int used;
	int size;
};

#define MAXNUMERIC 100

struct numerictab {
	klu_numeric *address;
	int time;
	int used;
};

struct symboltab symbol[MAXSYMBOL];
struct numerictab numeric[MAXNUMERIC];


klu_common Common;
int syminit = -1;
int numinit = -1;
#define GC_SPTIMER 60


Private void
GC_Sparse (void)
{
	for(int i = 0; i < MAXSYMBOL; i++)
		if(symbol[i].used == 1) klu_free_symbolic(&(symbol[i].address), &Common);
	for(int i = 0; i < MAXNUMERIC; i++)
		if(numeric[i].used == 1) klu_free_numeric(&(numeric[i].address), &Common);
}

/* (sparse_analyze vec). */

Private void 
Sparse_Analyze (void)
{
	int *Ap, *Ai, Aplength, Ailength, Axlength, i, index;
	double *Ax;
	char symid[] = "SPsym";

	/* Intialize */
	if(syminit){
		syminit = 0;
		for(i = 0; i < MAXSYMBOL; i++) symbol[i].used = 0;
	}
	klu_defaults(&Common);


	/* Check all paramters are correct */
	Aplength = Get_Vector_Length(Get_Vector_Elem(Top(1), 0));	
	Ailength = Get_Vector_Length(Get_Vector_Elem(Top(1), 1));
	Axlength = Get_Vector_Length(Get_Vector_Elem(Top(1), 2));

	/* Convert scheme Vector to C array */
	Ap = (int *)malloc(Aplength * sizeof(int));
	Ai = (int *)malloc(Ailength * sizeof(int));
	Ax = (double *)malloc(Axlength * sizeof(double));

 	for(i = 0; i < Aplength; i++)
 		Ap[i] = (int)Number_To_Integer(Get_Vector_Elem(Get_Vector_Elem(Top(1), 0), i));
 	for(i = 0; i < Ailength; i++)
 		Ai[i] = (int)Number_To_Integer(Get_Vector_Elem(Get_Vector_Elem(Top(1), 1), i));
 	for(i = 0; i < Axlength; i++)
 		Ax[i] = (double)Get_Number_Real_Value(Get_Vector_Elem(Get_Vector_Elem(Top(1), 2), i));
 	 
	/* Find a place to store the the symbol */
	for(i = 0; i < MAXSYMBOL; i++)
		if(symbol[i].used == 0) break;



	/* Do the klu_analyze */
	symbol[i].size = Aplength - 1;
	symbol[i].address = klu_analyze(symbol[i].size, Ap, Ai, &Common); 
	symbol[i].used = 1;
	index = i;

	/* Clean Up */
	free(Ap); free(Ai); free(Ax);
	Make_String(sizeof(symid));
	for(i = 0; i <= sizeof(symid); i++) Get_String_Value(Value_Register)[i] = symid[i];
	Push(Value_Register);
	Integer_To_Number(index);
	Push(Value_Register);
	Make_Pair();

}


Private void 
Sparse_Factor (void)
{
	int *Ap, *Ai, Aplength, Ailength, Axlength, i, sym_index, index;
	double *Ax;
	char numid[] = "NUMid";

	/* Intialize */
	if(numinit){
		for(i = 0; i < MAXNUMERIC; i++) numeric[i].used = 0;
		numinit = 0;
	}

	/* Check all paramters are correct */
	Aplength = Get_Vector_Length(Get_Vector_Elem(Top(2), 0));	
	Ailength = Get_Vector_Length(Get_Vector_Elem(Top(2), 1));
	Axlength = Get_Vector_Length(Get_Vector_Elem(Top(2), 2));

	/* Convert scheme to C */
	Ap = (int *)malloc(Aplength * sizeof(int));
	Ai = (int *)malloc(Ailength * sizeof(int));
	Ax = (double *)malloc(Axlength * sizeof(double));

 	for(i = 0; i < Aplength; i++)
 		Ap[i] = (int)Number_To_Integer(Get_Vector_Elem(Get_Vector_Elem(Top(2), 0), i));
 	for(i = 0; i < Ailength; i++)
 		Ai[i] = (int)Number_To_Integer(Get_Vector_Elem(Get_Vector_Elem(Top(2), 1), i));
 	for(i = 0; i < Axlength; i++)
 		Ax[i] = (double)Get_Number_Real_Value(Get_Vector_Elem(Get_Vector_Elem(Top(2), 2), i));
	sym_index = Number_To_Integer(Get_Pair_Cdr(Top(1)));
 	 
	/* Find a place to store the the symbol */
	symbol[sym_index].time = 0;
	for(i = 0; i < MAXSYMBOL; i++){
		if(symbol[i].used == 1) symbol[i].time++;
		if(symbol[i].time > GC_SPTIMER){
			 klu_free_symbolic(&(symbol[i].address), &Common);
			 symbol[i].used = 0;
		}
	}

	for(i = 0; i < MAXNUMERIC; i++)
		if(numeric[i].used == 0) break;

	/* Do the klu_analyze */

	numeric[i].address = klu_factor (Ap, Ai, Ax, symbol[sym_index].address, &Common) ;
	numeric[i].used = 1;
	index = i;

	/* Clean Up */
	free(Ap); free(Ai); free(Ax);
	Make_String(sizeof(numid));
	for(i = 0; i <= sizeof(numid); i++) Get_String_Value(Value_Register)[i] = numid[i];
	Push(Value_Register);
	Integer_To_Number(index);
	Push(Value_Register);
	Make_Pair();
}

Private void
Sparse_Solve (void)
{

	int i, sym_index, num_index, blength, n = 5;
	double *b;
	Object head;

	blength = Get_Vector_Length(Top(1));
	b = (double *)malloc(blength * sizeof(double));
	for(i = 0; i < blength; i++)
 		b[i] = (double)Get_Number_Real_Value(Get_Vector_Elem(Top(1), i));

	sym_index = Number_To_Integer(Get_Pair_Cdr(Top(3)));
	num_index = Number_To_Integer(Get_Pair_Cdr(Top(2)));

	symbol[sym_index].time = 0;
	numeric[num_index].time = 0;
	for(i = 0; i < MAXSYMBOL; i++){
		if(symbol[i].used == 1) symbol[i].time++;
		if(symbol[i].time > GC_SPTIMER){
			 klu_free_symbolic(&(symbol[i].address), &Common);
			 symbol[i].used = 0;
		}
	}
	for(i = 0; i < MAXNUMERIC; i++){
		if(numeric[i].used == 1) numeric[i].time++;
		if(numeric[i].time > GC_SPTIMER){
			 klu_free_numeric(&(numeric[i].address), &Common);
			 numeric[i].used = 0;
		}
	}

	klu_solve (symbol[sym_index].address, numeric[num_index].address, symbol[sym_index].size, 1, b, &Common) ;
    	/*klu_free_numeric (&(numeric[i].address), &Common) ;*/
    	for (i = 0 ; i < n ; i++) printf ("x [%d] = %g\n", i, b [i]) ;

	Make_Vector(blength);
	head = Value_Register;
	for(i = 0; i < blength; i++){
		Make_Real_Number(b[i]);
		Get_Vector_Elem(head, i) = Value_Register;
	}
	/* Clean Up */
	Value_Register = head;
	free(b);
}

		
Public void Initialize_Sparse (void)
{

	Make_Primitive("sparse_analyze", Sparse_Analyze, 1, Vector_Type, 
	The_Undefined_Type, The_Undefined_Type);

	Make_Primitive("sparse_factor", Sparse_Factor, 2, Vector_Type, 
	Pair_Type, The_Undefined_Type);

	Make_Primitive("sparse_solve", Sparse_Solve, 3, Pair_Type, 
	Pair_Type, Vector_Type);


}

