/* bibiliteca  raizes.h
   Assunto:		Estudo das raizes de uma função definida no programa.

   Neste biblioteca:
   1) equação da função
   2) rotinas para encontrar raizes
   3) rotinas para saida de dados, para disco e para tela

	Preparação para orientação a objeto   

	Palavras-chave:	troca de sinal, raiz, varredura             
	por Tarcisio Praciano Pereira - 10 lições para aprender C++
	Sobral, Abrl de 2009   - UeVA
*/
                  
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <math.h>
#include "Ambiente.h" // (0) Altere aqui

using namespace std;  // a evitar - polui o espaço de nomes 

// *********   declarações de tipos de dados  *********************

Ambiente Tela;
typedef	struct	{float a; float b; float delta;} malha;
typedef	struct	{float	raiz; string	metodo; } resultado;

// *********   fim das  declarações de tipos de dados  **************


// -------------------------------------------------------------
// ----------- os métodos do projeto (um índice) ----------------
float	f(float	x);

void	noticia();          // diz o que o programa faz
void	escreve_a_malha(malha mal);  // diz qual é a mlha
void	escreve_intervalo(float a, float b); // imprime [a,b]

malha	entrada_dados(malha	mal); // recebe a malha
int	varredura(malha mal);
resultado seleciona_metodo(malha malha_fina, int teto);
resultado raiz_tangente2(malha mal,  int teto); // apagar
int	convexidade(float a, float b);
resultado raiz_tangente(malha mal, int teto);
resultado raiz_secante (malha mal,  int teto);
resultado raiz_binaria (malha mal,  int teto);
int 	verifica_raiz_tangente(malha malha_fina);
void	raiz_exata(float a, float b);
void	relatorio_final(int teste);
//  ----------------   fim do índice  ----------------------------
//  --------------------------------------------------------------



// ****************************************************************
// definição das funções
// ***********************************


// há várias equações, para escolher uma, apague o comentário //
float	f(float	x)
{
	// return(-(x+3)*(x+3)*(x-2)*(x+2) ); // (50)
	// return( (x*x*x*x*x - x*x*x*x - x*x*x - x*x - x + 2)*sin(2*x) );  // (50)
	// return(x*x);    // (50) escolha somente um return()
	// return( x*x*x*x*x - x*x*x*x - x*x*x - x*x - x + 2 );  // (50)
	return(x*x*sin(2*x)); // (50) 
	// return( x*x*x*x*x - 4*x*x*x + x*x + 0.2*sin(19*x) - 0.10782); // função com vários gogos...
	// return( x*x*x*x*x - 4*x*x*x + x*x + 0.2*sin(19*x) ); // função com vários gogos...
	// return(1/(1 + x*x)  );
}



// -----------------------------------------------------------------
// ---------------    entrada  de  dados   e comunicacao ------------
void noticia()
{
	//  *********** entrada de dados 
	cout 	<< "Este programa recebe do usuário a malha  [a,b], delta  " << endl
	<< "	e faz busca de raizes usando os métodos " << endl
	<< "	           da tangente, secante e busca binário" << endl
	<< "indicando que método usou em cada caso. O programa apresenta um" << endl
	<< "relatório ao final indicando os intervalo onde encontrou raizes" << endl 
	<< "e valor da função no primeiro extremo de cada intervalo onde" << endl
	<< "o programa tenha encontrado alguma raíz da função." << endl; 
}


void	escreve_a_malha(malha mal)
{
	float a=mal.a,b=mal.b,passo=mal.delta;
	cout << "Sua busca de raízes no intervalo "; escreve_intervalo(a,b); 
	cout << "  com passo  " << passo 	<< endl; 
}

void	escreve_intervalo(float a, float b)
{
	cout << "[" << a << "," << b << "]";
}

// (10) 
malha	entrada_dados(malha mal)
{
   cout	<< "Intervalo [a,b] e passo da malha" << endl;
	mal.a = Tela.entrada_float("a = ", mal.a); // (11) definida em ambiente.h
	mal.b = Tela.entrada_float("b = ", mal.b); // (12) definida em ambiente.h
	cout << "Forneca-me o passo da malha \n";
	cout << "Sugestao  0.01 <  delta   <  0.5  \n";
	mal.delta = Tela.entrada_float("delta  = " , mal.delta);  // (13) 	
	//  *******   entrada de dados  ************************	
	return(mal);	// devolve a malha
}
// --------------   fim da entrada de dados -------------------------
// ------------------------------------------------------------------



// ---------------------------------------------------------------
// --------------- inicio do controle  ---------------------------
// varre e procura os eventos: (1) troca sinal f,  (2) troca sinal f'
int	varredura(malha mal){
	//ofstream dados; // figura
	int	quantidade=0, teto=20, numero=0; // caso nao encontre raizes, teste=1
	float a=mal.a,b=mal.b, delta=mal.delta; // para ficar mais próxima da matemáica
	float deltaf1, deltaf2;  // apenas o númerador do quociente de diferenças
	malha	malha_fina; // variável local,definir malha em sub-intervalo
	//dados.open("dados", ios::trunc);   // figura 
	while( a <= b) 	
	{
		if (  f(a)*f(a+delta) == 0  ) {  // (31)  raiz exata
			raiz_exata(a,b); 
			quantidade++; // conta uma raiz
		} // (1)  fim teste raiz exata
		else if ( f(a)*f(a+delta) < 0) {	// (32) troca de sinal de  f
		   //cout << " Encontrou uma troca de sinal no intervalo ";
		   //escreve_intervalo(a,a+delta); Tela.apeteco2();
			malha_fina.a 		= a; malha_fina.b = a+delta; 
			malha_fina.delta 	= delta/100;
			seleciona_metodo(malha_fina, teto);
			quantidade++; // conta uma raiz
		}	// (1)  fim teste troca de sinal de f
		deltaf1= f(a+delta)-f(a); 	// (41) somente num do quoc. diferenças
		deltaf2= f(a+2*delta)-f(a+delta); // (42) somente num do quoc. diferenças
		if ( deltaf1*deltaf2 < 0 ) { //  (43) troca de sinal de f' 
		   //cout << " Encontrou uma troca de sinal de f' no intervalo ";
		   //escreve_intervalo(a,a+delta); Tela.apeteco2();		
			malha_fina.a = a; malha_fina.b = a+delta; 
			malha_fina.delta = delta/1000;
			numero = verifica_raiz_tangente(malha_fina); // 0, 1
		}  // (2)  fim teste troca de sinal de f'
		quantidade = quantidade + numero; // vem de verifica_raiz_tangente()
		//dados << a << "  "  << f(a)  << endl;   // figura
		a = a + delta;
	} // fim do while da varredura
	//dados.close();   // figura
	return(quantidade); // se não encontrar --> 0
} // fim da varredura


// se houver troca de sinal de f - vem para cá
resultado seleciona_metodo(malha malha_fina, int teto) {
   //ofstream dados;   // figura 
	float a=malha_fina.a, b,  delta = malha_fina.b - malha_fina.a;
	int	opcao = 1; // Método da tangente é a opção do programa
	resultado	data; // para enviar dados 
	cout << "Troca de sinal da função no intervalo ";
				escreve_intervalo(a, a+delta);  cout  << endl;
	a = malha_fina.a; 
	b = malha_fina.b; 
	delta = malha_fina.delta;
	cout 	<< "Selecione o método para determinação fina " << endl
			<< "da raíz neste intervalo: " << endl
			<< "(1) Método da tangente -----> < 1 > " << endl
			<< "(1) Método da secante  -----> < 2 > " << endl			
			<< "(1) Busca binária  ---------> < 3 > " << endl << endl;
	opcao = Tela.entrada_int(
					"Sua escolha < 1 , 2 , 3 >  --->", opcao);			
   switch(opcao)   {  // executa o método selecionado
   	case 2:
   		data = raiz_secante(malha_fina, teto);				
   		break;
		case 3:
		   data  =  raiz_binaria(malha_fina, teto); // passa malha
		   break;
		default:
			data  =  raiz_tangente(malha_fina, teto); // passa malha	
	} // fim do switch 
	//
	cout << "Raiz p/ " << data.metodo << " x =  "  << data.raiz  << endl;
	cout << "Valor de f na raíz aprox = " << data.raiz 
			<< " -> " << f(data.raiz)  << endl;
   //dados.open("dados",ios::app);  // figura
	//dados << data.raiz << "    "  << f(data.raiz) << endl;  // figura
   //dados.close();   // figura	
	return(data); //  raiz, teste - teste \in {0, 1, 2, ...n}
} 
//-----------  fim do controle  -----------------------------
//----------------------------------------------------------


// Verificar, pode ser apenas  0,1  os casos .....
// então função vai devolver 1,2,3,4
//          der-    der+ 
//  ccv+ |   1       2      ccv+ função é concâva   a>0 na parábola
//       |
//  ccv- |   3       4      ccv- função é convexa   a<0 na parábola
// ----------------------------------------------------------------
// recebe um pequeno intervalo [a,b] onde verifica cvx de derivada
// testa se a função é convexa, côncava e derivada positiva, negativa
int  convexidade(float a,float b)
{
	int caso = 0; //   1,2,3,4  ver acima
	float eps=(b-a)/1000; // divide o passo a malha por 100
	// côncava  se C > 0 somente numerador do quoc. de segunda ordem
	float C = f(a+20*eps) - 2*f(a+10*eps) + f(a); // seg der aprox - numerador
	float D = f(a+eps)-f(a); // operador diferença  - eps
	// aqui em quatro casos combinando C, D positivo/negativo	
	if (( C > 0)*(D > 0)) caso = 2;  // c = b, eps <0  - pto de tangencia
	if (( C > 0)*(D < 0)) caso = 1;	// c = a, eps >0  - pto de tangencia
	if (( C < 0)*(D > 0)) caso = 1;	// c = a, eps >0  - pto de tangencia
	if (( C < 0)*(D < 0)) caso = 2;	// c = b, eps <0  - pto de tangencia
	return(caso);
}

//  Os --- "métodos"  --------------------
// reta tangente y = f(a) + df(a)(x - a) 
// zero da reta tangente  x0 =  a - f(a)/df(a)  
// substituida por raiz_tangente() e convexidade() 
// fica como contra-exemplo - código muito grande
resultado raiz_tangente2(malha mal, int teto)
{
	resultado data ={mal.a, "método da tangente"};
	//ofstream dados;   // figura 
	float a=mal.a, b=mal.b, eps=mal.delta; // para ficar maix próximo da matemática
	// côncava  se C > 0 somente numerador do quoc. de segunda ordem
	float C = f(a+10*eps) - 2*f(a+20*eps) + f(a); // seg der aprox - numerador
	float D = (f(a+eps)-f(a))/eps; // derivada aproximada - eps
	// aqui em quatro casos combinando C, D positivo/negativo
   if ( (C > 0) * (D <  0)) { //côncava,  derivada positiva
   //dados.open("dados",ios::app);  // figura
   while ( ( fabs(f(a)) > eps )*teto ) // se teto == 0 para
   {
		a = a - f(a)/D;  // raiz aproximada
		D = (f(a+eps)-f(a))/eps; // derivada aproximada - eps  	
		teto--; // reduz do teto, se for zero para
		//dados << a << "    "  << f(a) << endl;  // figura
	} // fim do while
	data.raiz =a;
	//dados.close();   // figura		
	} // fim do if() inicio do else
	else {  // côncavo, derivada positiva
	   while (  (fabs(f(b)) > eps)*teto )  {
   	D = (f(b)-f(b-eps))/eps; // derivada aproximada - eps
		b = b - f(b)/D;  // raiz aproximada
		teto--;  // deduz do teto, se for zero para
		//dados << a << "    "  << f(a) << endl;  // figura
	}	// fim do while
	//ofstream dados; dados.open("dados",ios::app);  // figura
	// dados.close();   // figura
	data.raiz =b;
	} // fim do else
   if ( (C < 0) * (D > 0)) {	
	// se for convexa - pega a outra ponto do intervalo
	//dados.open("dados",ios::app);    // figura
   while (  (fabs(f(a)) > eps)*teto )
   {
   	D = (-f(a)+f(a+eps))/eps; // derivada aproximada - eps
		a = a - f(a)/D;  // raiz aproximada
		teto--;  // deduz do teto, se for zero para
	//	dados << a << "    "  << f(a) << endl;   // figura
	} // fim do while
	data.raiz =a;	
	//dados.close();   // figura
	} // fim do if() começo do else
	else { 
	//dados.open("dados", ios::app);    // figura
   while (  (fabs(f(b)) > eps)*teto )
   {
   	D = (f(b+eps)-f(b))/eps; // derivada aproximada - eps
		b = b - f(b)/D;  // raiz aproximada
		teto--;  // deduz do teto, se for zero para
		//dados << a << "    "  << f(a) << endl;   // figura
	} // fim do while	
	data.raiz = b;
	//dados.close();					 // figura
	} // fim do else
	return(data);
} // fim da   raiz_tangente2()  -  fora de uso


// nova versao de raiz_tangente - chama convexidade() para decidir
resultado raiz_tangente(malha mal, int teto){
	resultado data ={mal.a, "método da tangente"};
	//ofstream dados;   // figura 
	float a=mal.a, b=mal.b, eps=mal.delta; // para ficar maix próximo da matemática
	float c = a; // ponto para tangente, a, b  dependendo do caso
	float delta = eps=(b-a)/100000, D; // eps pode trocar de sinal - (500) erro
	int	caso = convexidade(a,b); //  convexidade() 1,2
   if ( (caso == 1)) { // parte do extremo b em [a,b] -  (500) erro
   	c = a; } // calcula tangentes retroativamente
	else { c = b;  eps=-eps; }   // parte do extremo  a  em [a,b]
   D = (f(c+eps)-f(c))/delta; // derivada aproximada - eps 
   // dados.open("dados",ios::app);  // figura
   while (  ( fabs(f(c)) > delta/100 )*teto   ) { // se teto == 0 para
		c = c - f(c)/D;  // raiz aproximada
		D = (f(c+eps)-f(c))/delta; // derivada aproximada - eps  	
		teto--; // reduz do teto, se for zero para
		//dados << c << "    "  << f(c) << endl;  // figura
	} // fim do while
	data.raiz =c;
	//dados.close();   // figura		
	return(data);
} // fim da   raiz_tangente()



resultado raiz_secante(malha mal,  int teto)
{
   //ofstream dados;    // figura
	resultado data ={mal.a, "método da secante"};
	float a=mal.a,b=mal.b,delta=mal.delta;
	float m = (f(b)- f(a))/(b-a); // coeficiente angular
	float c = a - f(a)/m;  // raiz aproximada
	// se teto==0, para! , se |f(x)| < delta,  para!
	//dados.open("dados",ios::app);   // figura
   while( ( teto*( fabsf(f(c)) > delta ) ) ) 
	{ //  teto=iterações  delta é a precisão 
		if (f(a)*f(c) < 0) b = c;
		  else a = c; // descobre intervalo de troca de sinal
		m = (f(b)- f(a))/(b-a);
		//dados << c << "  " << f(c) << endl;  // figura
		c = a - f(a)/m;
		teto--; // desconta o número de iterações se for zero, para
	}
	//dados.close();     // figura
	data.raiz = c;
return(data);
};


resultado raiz_binaria (malha mal,  int teto)
{
   //ofstream dados; // figura
	resultado data ={mal.a, "método da busca binária"};
	float a=mal.a,b=mal.b,delta=mal.delta;
	float c = (a + b)/2.0;  // ponto médio do intervalo
	// se teto==0, para! , se |f(x)| < delta,  para!
	//dados.open("dados",ios::app);   // figura
	while( ( teto*( fabsf(f(c)) > delta ) ) ) 
	{ //  teto=iterações  delta é a precisão 
		if (f(a)*f(c) < 0) 
				b = c;
		else 
				a = c; // descobre intervalo de troca de sinal
		c = (a + b)/2.0;		
		teto--; // desconta o número de iterações se for zero, para
		}
		//dados << c << "   "  << f(c) << endl;  // figura	
	//dados.close();     // figura
	data.raiz = c;
return(data);
};


// vem para cá quando derivada se anula - devolve 0, 1
int verifica_raiz_tangente(malha malha_fina) {
	int 			quantidade = 0;
	string		metodo="Possível raiz tangente, derivada nula";
	//ofstream dados;  // figura
	float a=malha_fina.a, b = malha_fina.b, delta = malha_fina.delta;
	//dados.open("dados",ios::app);   // figura
	while( a < b) 	{
		if ( fabsf( f(a) ) < delta )		{
			quantidade=1; // aumenta a quantidade
			cout	<< metodo << " no intervalo ";
			escreve_intervalo(a,a+delta);
			a = b; // termina o laço
		}
	//dados << a << "  " << f(a) << endl;  // figura	
	a = a + delta;		 
	}
	//dados.close();  // figura
	return(quantidade);
} // fim rotina de verificação de raiz tangente - graf tangente OX
// fim dos ------ "métodos" -------------------------



// ------------------relatórios ---------------------
//---------------------------------------------------
void	raiz_exata(float a, float b) {
		if ( f(a) == 0 )
		   cout << "Uma raíz exata " <<  a << endl;
		else 
		     	cout << "Uma raíz exata " << b  << endl;
} // fim da raiz_exata


void	relatorio_final(int quantidade){
	if (!quantidade) // se quantidade for zero, ! é negação
		{
		cout 	<< "Nenhuma raíz foi encontrada no intervalo dado !"  << endl
				<< "Rode, novamente, o programa, com passo mais fino... " 
				<< endl;
		}
	if (quantidade == 1) // quantidade 1
		cout << "Achei uma raíz "  << endl;
	else  					// quantidade maior que 1  	
		cout 	<< "Achei " << quantidade 
						<< " intervalos onde há raíz "  << endl;
	//system("less dados"); Tela.apeteco2();						
} 
//----------- fim dos relatórios -----------------------
//------------------------------------------------------- 






/*
	Comentários:  

(10) Entrada de dados. 
		Os dados do programa podem ser aceitos apenas dando enter quando
	for feita pergunta sobre valores a fornecer. Rode o programa
	apenas dando enter na primeira vez.
	O método entrada_float()  permite que haja dados pre-definidos
	e preserva estes dados com uso do enter como resposta.
	

(30) Testes com o sinal e zero de f 
		(31) primeiro verifica se há um zero exato, depois se
		(32) há troca de sinal
	
(40)  Testa se a derivada  troca de sinal
		(41) calcula o numerado do primeiro quociente de diferenças
		(42) calcula o numerado do segundo  quociente de diferenças
      (43) verifica se o quociente de diferenças troca de sinal
      	
(50)	em uma função somente um return()  deve ser
	executado. em f() há vários, todos desligados, menos um que
	é o que está em vigor. Troque pela equação desejada ou
	escolha outra equação.



*/
