/// OPTIMİZASYON
//  Steepest Ascent-descent - En dik yamaç metodu
//  Tüm türevler sayısal olarak alınmıştır.
import java.util.*;
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
import javax.swing.*;

abstract class f_x
{
  //single function single independent variable
  // example f=x*x
  abstract double func(double x);
 
  public  double df(double x)
  {
  // türev alt programı	
  // df fonksiyonunun türevini hesaplar
  double h0=0.0256;
  int i,m;
  int n=7;
  //derivative of a simple function
  double T[][];
  T=new double[n][n];
  double h[];
  h=new double[n];
  //vector<double> h(n,0);
  for(i=0;i<n;i++)
  {
  h[i]=0;
  for(int j=0;j<n;j++)
    T[i][j]=0;
  }
  h[0]=h0;
  double r=0.5;
  for( i=1;i<n;i++)
  { h[i]=h0*Math.pow(r,i);}
  for(i=0;i<n;i++)
  { T[i][0]=( func(x + h[i]) - func( x - h[i]))/(2.0*h[i]);}
  for(m=1;m<n;m++)
  {  for(i=0;i<n-m;i++)
     { T[i][m]=(h[i]*h[i]*T[i+1][m-1] - h[i+m]*h[i+m]*T[i][m-1])/
             (h[i]*h[i]- h[i+m]*h[i+m]);}
  }
  double xx=T[0][n-1];
  return xx;
  }
}

//============= Tanımı gereken fonksiyon ================
class fb extends f_x
{
   double func (double x)
   { return -(2.0*Math.sin(x)-x*x/10.0);}
}

class fc extends f_x
{
   double func (double x)
   { return -(2.0*x-1.75*x*x+1.1*x*x*x-0.25*x*x*x*x);}
}


//===========================================================

class dfb extends f_x
{  
   f_x f1;
   
   public dfb(f_x ff)
   {f1=ff;}
  	
   double func (double x)
   { return f1.df(x);} 
}

public class OPO4A
{

public static double newton(f_x f1,double x)
{
// df ile verilen türev fonksiyonunun köklerini bulur
dfb dfxx=new dfb(f1);
int nmax=500;
double tolerance=1.0e-15;
double fx,dfx;
for(int i=0;i<nmax;i++)
{
fx=dfxx.func(x);
dfx=dfxx.df(x);
x-=fx/dfx;
if(Math.abs(fx)<tolerance)
{
return x;
}
}
return x;
}


public static double newton_2nd_derivative(f_x f1,double x)
{
// df ile verilen türev fonksiyonunun köklerini bulur
// bunun anlamı asıl fonksiyonun optimumunu bulur.
dfb dfxx=new dfb(f1);
int nmax=500;
double dx=1e-3;
double x1m;
double tolerance=1.0e-15;
double fx,fxm,dfx,dfxm,d2fx;
for(int i=0;i<nmax;i++)
{
fx=dfxx.func(x);
fxm=dfxx.func(x-dx);
dfx=dfxx.df(x);
dfxm=dfxx.df((x-dx));
d2fx=-6.0/dx/dx*(fx-fxm)+2.0/dx*(2.0*dfx+dfxm);
x-=(fx/dfx+.5*fx*fx/(dfx*dfx*dfx)*d2fx);
if(Math.abs(fx)<tolerance)
{ return x; }
}
return x;
}	

     public static void main (String args[])  
     {//bu versiyon değişik fonksiyonları aynı programın içinde optimize edebilir. 
	 fb fxx=new fb();    
     double x0;
     x0=Double.parseDouble(JOptionPane.showInputDialog(" optimum nokta tahmini giriniz  x0 : "));
     double r;
     r= newton(fxx,x0);
     JOptionPane.showMessageDialog(null," optimizasyon değeri  : "+r+"\nFonksiyon değeri : "+fxx.func(r),
     "Newton optimizasyon : ",JOptionPane.PLAIN_MESSAGE);
     r= newton_2nd_derivative(fxx,x0);
     JOptionPane.showMessageDialog(null," optimizasyon değeri  : "+r+"\nFonksiyon değeri : "+fxx.func(r),
     "Newton 2nci türev optimizasyon : ",JOptionPane.PLAIN_MESSAGE);
     fc fxx1=new fc();
     x0=Double.parseDouble(JOptionPane.showInputDialog(" optimum nokta tahmini giriniz  x0 : "));
     r= newton(fxx1,x0);
     JOptionPane.showMessageDialog(null," optimizasyon değeri  : "+r+"\nFonksiyon değeri : "+fxx1.func(r),
     "Newton optimizasyon : ",JOptionPane.PLAIN_MESSAGE);
     System.exit(0);
     
     } 
}