/*--------------------------------------------------------------------------*/
/*  Solve nonlinear systems by the Fixed-Point-Method                       */
/*                                                                          */
/*  Workspace:      2*dim                                                   */
/*                                                                          */
/*  author: Kunibert G. Siebert                                             */
/*          Institut fuer Mathematik                                        */
/*          Universitaet Augsburg                                           */
/*          Universitaetsstr. 14                                            */
/*          D-86159 Augsburg, Germany                                       */
/*                                                                          */
/*          http://scicomp.math.uni-augsburg.de/Siebert/                    */
/*                                                                          */
/* (c) by K.G. Siebert (2000-2003)                                          */
/*--------------------------------------------------------------------------*/

#include "alberta_util.h"
#include "alberta_util_intern.h"

int nls_banach(NLS_DATA *data, int dim, REAL *x)
{
  FUNCNAME("nls_banach");
  REAL err = 0.0, err_old = -1.0;
  int  iter;

  int  info = data->info;
  void (*update)(void *, int, const REAL *, bool, REAL *) = data->update;
  void *ud = data->update_data;
  int  (*solve)(void *, int, const REAL *, REAL *) = data->solve;
  void *sd = data->solve_data;
  REAL (*norm)(void *, int, const REAL *) = data->norm;
  void *nd = data->norm_data;

  WORKSPACE  *ws = CHECK_WORKSPACE(2*dim, data->ws);

/*--- Memory initialization ------------------------------------------------*/
  REAL *b  = (REAL *)ws->work;
  REAL *d  = b+dim;

  INFO(info,2,"iter. |     residual |     red. |    n |\n");

  for (iter = 1; iter <= data->max_iter; iter++)
  {
/*--- Assemble DF(x) and F(x) ----------------------------------------------*/
    (*update)(ud, dim, x, 1, b);
/*--- Initial guess is zero ------------------------------------------------*/
    dset(dim, 0.0, d, 1);
/*--- solve linear system --------------------------------------------------*/
    int n = (*solve)(sd, dim, b, d);
/*--- x = x - d ------------------------------------------------------------*/
    daxpy(dim, -1.0, d, 1, x, 1);

    err = norm ? (*norm)(nd, dim, d) : dnrm2(dim, d, 1);

    if (err_old <= 0)
      INFO(info,2,"%5d | %12.5le | -------- | %4d |\n", iter, err, n);
    else
      INFO(info,2,"%5d | %12.5le | %8.2le | %4d |\n", 
			  iter, err, err/err_old, n);

    if ((data->residual = err) < data->tolerance)
    {
      INFO(info,4,"finished successfully\n");
      if (ws != data->ws) FREE_WORKSPACE(ws);
      return(iter);
    }
    err_old = err;
  }

  if (info < 2) {
    INFO(info,1,"iter. %d, residual: %12.5le\n", iter, err);
  }
  INFO(info,1,"tolerance %le not reached\n", data->tolerance);
  if (ws != data->ws) FREE_WORKSPACE(ws);
  data->residual = err;

  return(iter);
}
