FACTORIAL.c



/* CALCULO DEL FACTORIAL DE NUMEROS GRANDES
	- SI LA MEMORIA LO PERMITE -
	SE UTILIZA UN DIGITO POR CELDA
	( LISTAS LIGADAS )
*/

#include <stdio.h>
#include <stdlib.h>

struct Nodo
{
	int Digito;
	struct Nodo *Sig;
	struct Nodo *Ant;
};
struct Nodo *Numero_Entrada;			// NUmero al que se le calcularA el factorial
struct Nodo *Factorial;				// Resultado al calcular el factorial de Numero_Entrada


void Solicita_Dato(char*);
struct Nodo *Crea_Nodo(int);
struct Nodo *Crea_Lista(char*);
void Muestra_Lista(struct Nodo*);
int Compara(struct Nodo*, struct Nodo*);
struct Nodo *Suma_Lista_Escalar(struct Nodo *, int);
void Calcula_Factorial();
struct Nodo *Producto_Lista_Escalar(struct Nodo*, int, struct Nodo*, int);
struct Nodo *Producto_Lista_Lista(struct Nodo*, struct Nodo*);

struct Nodo *Crea_Nodo(int Numero)
{
	struct Nodo *Temporal = (struct Nodo *)malloc(sizeof(struct Nodo *));
	Temporal->Digito = Numero;
	Temporal->Ant = Temporal->Sig = NULL;
	return Temporal;
}


// (FACTOR_1, ESCALAR, SUMA_PARCIAL, posiciOn_de_corrimiento)			SUMA = RESP * ESC
struct Nodo *Producto_Lista_Escalar(struct Nodo *RESP, int ESC, struct Nodo *SUMA, int POS)
{
	int TEMP = 0;							// Entero para guardar el producto parcial de ESCALAR * ESCALAR
	int LLEVO = 0;							// Se almacena el acarreo si lo hay
	struct Nodo *Nuevo = NULL;					// CreaciOn de nuevos nodos

	while (RESP->Sig != NULL) RESP = RESP->Sig;			// Se posiciona el apuntador en el dIgito menos significativo
	while (SUMA->Sig != NULL) SUMA = SUMA->Sig;			// Se posiciona el apuntador en el dIgito menos significativo
	while ( POS ) { SUMA = SUMA->Ant; --POS; }			// Se recorre el apuntador hacia atrAs POS posiciones

	while ( RESP != NULL )						// Mientras existan dIgitos en RESP
	{
		TEMP = ESC * RESP->Digito + SUMA->Digito + LLEVO;
		if (TEMP > 9) { SUMA->Digito = TEMP % 10; LLEVO = (TEMP - SUMA->Digito) / 10; }
		else { SUMA->Digito = TEMP; LLEVO = 0; }
		if (SUMA->Ant != NULL) SUMA = SUMA->Ant;
		else
		{
			Nuevo = Crea_Nodo(0);
			Nuevo->Sig = SUMA; SUMA->Ant = Nuevo; SUMA = Nuevo;
		}
		RESP = RESP->Ant;
	}
	if ( LLEVO ) SUMA->Digito = LLEVO;
	else								// Se elimina el nodo mas significativo con contenido de cero
	{ Nuevo = SUMA; SUMA = SUMA->Sig; SUMA->Ant = NULL; free(Nuevo); }
	while (SUMA->Ant != NULL) SUMA = SUMA->Ant;			// Se regresa al dIgito mas significativo
	return SUMA;
}

struct Nodo *Producto_Lista_Lista(struct Nodo *Factorial, struct Nodo *Factor)
{
	struct Nodo *Suma_Acumulada = Crea_Nodo(0);
	int Inicio = 0;						// Cantidad de dIgitos que se corren para realizar la suma acumulada
	
	while(Factor->Sig != NULL) Factor = Factor->Sig;	// Se posiciona el apuntador en el dIgito menos significativo

	while ( 1 )						// Ciclo infinito hasta la cantidad de dIgitos de Factor
	{
		Suma_Acumulada = Producto_Lista_Escalar(Factorial, Factor->Digito, Suma_Acumulada, Inicio);
		if ( Factor->Ant != NULL ) { Factor = Factor->Ant; Inicio++; }			// Se hace un corrimiento a la izquierda
		else break;
	}
	while (Suma_Acumulada->Ant != NULL) Suma_Acumulada = Suma_Acumulada->Ant;
	return Suma_Acumulada;
}


void Calcula_Factorial()
{
	struct Nodo *Factor = Crea_Nodo(1);				// Inicializa el caso base
	Factorial = Crea_Nodo(1);					// Inicializa el caso base
	int Detectar = 0;						// Bandera cuando se iguala el Factor con el Numero_Entrada

	while (!Detectar)						// Mientras Factor sea menor que Numero_Entrada
	{
		Detectar = Compara(Factor, Numero_Entrada);		// Ya se llegO al nUmero deseado pero aUn asI se multiplica
		Factorial = Producto_Lista_Lista(Factorial, Factor);
		Factor = Suma_Lista_Escalar(Factor, 1);
	}
}

struct Nodo *Suma_Lista_Escalar(struct Nodo *Lista, int ESCALAR)
{
	struct Nodo *Nuevo = NULL;
	
	while(Lista->Sig != NULL) Lista = Lista->Sig;			// Se posiciona el apuntador en el dIgito menos significativo
	while ( 1 )							// Ciclo infinito por si hay corrimientos
	{
		Lista->Digito += ESCALAR;
		if (Lista->Digito < 10) break;				// No hay corrimiento. No tiene caso continuar
		ESCALAR = (Lista->Digito - Lista->Digito % 10) / 10;
		Lista->Digito = Lista->Digito - ESCALAR*10;
		if ( Lista->Ant != NULL ) Lista = Lista->Ant;
		else							// Significa que ya llegO al principio de la lista y se efectUa un corrimiento
		{
			Nuevo = Crea_Nodo(ESCALAR);			// Por consiguiente se agrega un nodo al principio (dIgito mas significativo)
			Nuevo->Sig = Lista;
			Lista->Ant = Nuevo;
			Lista = Nuevo;
			break;
		}
	}
	while(Lista->Ant != NULL) Lista = Lista->Ant;			// Se posiciona el apuntador en el dIgito mas significativo
	return Lista;
}

int Compara(struct Nodo *N1, struct Nodo *N2)
{
	while(N1)
	{
		if (N1->Digito != N2->Digito) return 0;					// Con un digito que sea diferente, todo es diferente.
		if (N1->Sig != NULL && N2->Sig != NULL) { N1 = N1->Sig; N2 = N2->Sig; }	// Existe un prOximo Digito
		else if (N1->Sig == NULL && N2->Sig == NULL) return 1;				// Ambas llegaron al final al mismo tiempo
			else return 0;								// Al menos una llegO al final y la otra no
	}
	return 1;
}

void Muestra_Lista(struct Nodo *Lista)	{ while(Lista) { printf("%d ", Lista->Digito); Lista = Lista->Sig; } printf("\nOK\n"); }
void Solicita_Dato(char *Numero)	{ printf("Introduce el nUmero al que se le calcularA el factorial: "); gets(Numero); }

struct Nodo *Crea_Lista(char *CAD)
{
	int Indice = 0;
	struct Nodo *Nuevo=NULL, *Aux=NULL, *Lista=NULL;

	while( CAD[Indice] != '\0')
	{
		Nuevo = Crea_Nodo(CAD[Indice] - 48);  // (Ascii - 48 = N) Convierte a decimal
		
		if (!Lista) Lista = Aux = Nuevo;
		else
		{
			Aux->Sig = Nuevo;
			Nuevo->Ant = Aux;
			Aux = Nuevo;
		}
		Indice++;
	}
	return Lista;
}

int main()
{
	char *Numero = (char*)malloc(sizeof(char*)*100);
	Numero_Entrada = Factorial = NULL;				// Se inicializan como listas vacIas

	Solicita_Dato(Numero);
	Numero_Entrada = Crea_Lista(Numero);
	Calcula_Factorial();
	Muestra_Lista(Factorial);
}