#pragma once
#include "garray.h"

/*
Ordenaciones de varias ordenaciones de un array
la funcion de orden f_ord(e1, e2)
ha de devolver	<0 si e1<e2
				0 si e1=e2
				>0 si e1>e2
*/
typedef struct Indices_gorden
{
	int i; //indice a a elemento con esta posicion
	int io;//posicion que ocupa el elemento de la lista original en este orden
}Indices_gorden;
typedef struct Orden_gorden
{
	int (*f_ord)(void* e1, void*e2);//funcion de orden
	Cgarray<Indices_gorden> ind;//indices
}Orden_gorden;

template <typename T>
class Cgorden
{
public:
	//variables-----------------------------------
	Cgarray<Orden_gorden> ord;
	Cgarray<T> *elem;
	//c y d---------------------------------------
	//**************************************************************
	Cgorden(Cgarray<T> *el=NULL)
	{
			elem=el;
	}
	//**************************************************************
	~Cgorden()
	{
		//borramos arrays
		for (int i=0; i<ord.n; i++)
		{
			if(ord[i].ind.ptr)
			{
				free(ord[i].ind.ptr);
				ord[i].ind.n=ord[i].ind.m=0;
			}
		}
	}
	
	//**************************************************************
	inline BOOL add_ord(int (*f_ord)(void*, void*))//aņade un orden nuevo
	{
		Indices_gorden ii;
		int i;
		Orden_gorden o;
		//inicia orden------------------
		o.f_ord=f_ord;
		o.ind+=elem->n;
		for(i=0; i<elem->n; i++)
		{
			ii.i=i;
			o.ind+ii;
		}
		//-------------------------------
		//ordena
		ord_qsort(0,elem->n-1,&o);
		//reasigna----------------------------
		for(i=0; i<elem->n; i++)
			o.ind[o.ind[i].i]=i;
		//aņade el orden-----------------

		ord+o;
		o.ind.ptr=NULL;
		o.ind.m=0;
		return TRUE;
	}
	//**************************************************************
	inline T* get(int i, int o)//da el elemento iesimo del orden o
	{
		return NULL;
	}
	//**************************************************************
	inline int geti(int i, int o)//da el indice del elemento que iesimo del orden o
	{
		return ord[o].ind[i].i;
	}
	//**************************************************************
	inline int geti(int i, int o, int oi)//da el la posicion que tiene  el iesimo del orden o en el orden oi
	{
		return ord[oi].ind[ord[o].ind[i].i].io;
	}
	//******************************************************************
	inline void ord_qsort(int ini, int fin, Orden_gorden *o)
	{
		int ii,kf,ki, jf,ji,p1,p, com;
		BOOL bus=TRUE;
		//caso base-------------------
		p=(fin-ini);
		if(p<1)
			return;
		if (p<2)
		{
			if(0<o->f_ord(elem->get(o->ind[ini].i),elem->get(o->ind[fin].i)))
			{
				ii=o->ind[ini].i;
				o->ind[ini].i=o->ind[fin].i;
				o->ind[fin].i=ii;
			}
			return;
		}
		//a pivotar-------------------
		//inicializacion
		p+=ini;
		p1=p;
		kf=fin;
		ki=ini;
		jf=ji=-1;
		while(bus)
		{
			//busca por la izquierda-----
			while(p>ki)
			{
				com=o->f_ord(elem->get(o->ind[p].i),elem->get(o->ind[ki].i));
				if(com<0)
				{
					if(jf>=0)
					{
						ii=o->ind[jf].i;
						o->ind[jf].i=o->ind[ki].i;
						o->ind[ki].i=ii;
						jf=-1;
						ki++;
					}
					else
					{
						ji=ki;//encontrado tio que va al otro lado
						break;
					}
					
				}
				else if(com==0)
				{
					//elementos iguales se pone al centro
					p--;
					ii=o->ind[p].i;
					o->ind[p].i=o->ind[ki].i;
					o->ind[ki].i=ii;
				}
				else
					ki++;
			}
			//busca por la derecha-----
			while(p1<kf)
			{
				com=o->f_ord(elem->get(o->ind[p1].i),elem->get(o->ind[kf].i));
				if(com>0)
				{
					if(ji>=0)
					{
						ii=o->ind[ji].i;
						o->ind[ji].i=o->ind[kf].i;
						o->ind[kf].i=ii;
						ji=-1;
						kf--;
					}
					else
					{
						jf=kf;//encontrado tio que va al otro lado
						break;
					}
					
				}
				else if(com==0)
				{
					//elementos iguales se pone al centro
					p1++;
					ii=o->ind[p1].i;
					o->ind[p1].i=o->ind[kf].i;
					o->ind[kf].i=ii;
				}
				else
					kf--;
			}
			//condicion de fuga---
			bus=(p>ki) && (p1<kf);
		}
		//falta por ordenar de un lado
		//lo malo es que tocara mover memo
		//se puede sin mover memo
		if(p>ki)
		{
			//falta la izquierda-----------------
			while(p>ki)
			{
				com=o->f_ord(elem->get(o->ind[p].i),elem->get(o->ind[ki].i));
				if(com<0)
				{
					//cambia elemento al otro lado del pivote
					ii=o->ind[p-1].i;
					o->ind[p-1].i=o->ind[p1].i;
					o->ind[p1].i=o->ind[ki].i;
					o->ind[ki].i=ii;
					p--;
					p1--;
					continue;
				}
				else if(com==0)
				{
					//elementos iguales se pone al centro
					p--;
					ii=o->ind[p].i;
					o->ind[p].i=o->ind[ki].i;
					o->ind[ki].i=ii;
					continue;
				}
				ki++;
			}
		}
		else
		{
			while(p1<kf)
			{
				com=o->f_ord(elem->get(o->ind[p1].i),elem->get(o->ind[kf].i));
				if(com<0)
				{
					//cambia elemento al otro lado del pivote
					ii=o->ind[p1+1].i;
					o->ind[p1+1].i=o->ind[p].i;
					o->ind[p].i=o->ind[kf].i;
					o->ind[kf].i=ii;
					p++;
					p1++;
					continue;
				}
				else if(com==0)
				{
					//elementos iguales se pone al centro
					p1++;
					ii=o->ind[p1].i;
					o->ind[p1].i=o->ind[kf].i;
					o->ind[kf].i=ii;
					continue;
				}
				kf--;
			}
		}

		ord_qsort(ini, p-1, o);
		ord_qsort(p1+1, fin, o);
	}
};