/* nag_real_partial_svd (f02wgc) Example Program.
 *
 * NAGPRODCODE Version.
 *
 * Copyright 2016 Numerical Algorithms Group.
 *
 * Mark 26, 2016.
 */
/* Pre-processor includes */
#include <stdio.h>
#include <math.h>
#include <nag.h>
#include <nag_stdlib.h>
#include <nagf02.h>

#ifdef __cplusplus
extern "C"
{
#endif
  static void NAG_CALL av(Integer *iflag, Integer m, Integer n,
                          const double x[], double ax[], Nag_Comm *comm);
#ifdef __cplusplus
}
#endif
int main(void)
{
  /*Integer scalar and array declarations */
  Integer exit_status = 0;
  Integer i, m, n, nconv, ncv, nev;
  Integer pdu, pdv;
  Nag_Comm comm;
  NagError fail;
  /*Double scalar and array declarations */
  static double ruser[1] = { -1.0 };
  double *resid = 0, *sigma = 0, *u = 0, *v = 0;
  Nag_OrderType order;

  INIT_FAIL(fail);

  printf("nag_real_partial_svd (f02wgc) Example Program Results\n\n");

  /* For communication with user-supplied functions: */
  comm.user = ruser;

  /*     Skip heading in data file */
  scanf("%*[^\n] ");
  scanf("%" NAG_IFMT "%" NAG_IFMT "%" NAG_IFMT "%" NAG_IFMT "%*[^\n]", &m, &n,
        &nev, &ncv);

#ifdef NAG_COLUMN_MAJOR
  order = Nag_ColMajor;
  pdu = m;
  pdv = n;
#else
  order = Nag_RowMajor;
  pdu = ncv;
  pdv = ncv;
#endif

  if (!(resid = NAG_ALLOC(m, double)) ||
      !(sigma = NAG_ALLOC(ncv, double)) ||
      !(u = NAG_ALLOC(m * ncv, double)) || !(v = NAG_ALLOC(n * ncv, double)))
  {
    printf("Allocation failure\n");
    exit_status = -1;
    goto END;
  }
  /*
   * nag_real_partial_svd (f02wgc)
   * Computes leading terms in the singular value decomposition of
   * a real general matrix; also computes corresponding left and right
   * singular vectors.
   */
  nag_real_partial_svd(order, m, n, nev, ncv, av, &nconv, sigma, u, pdu,
                       v, pdv, resid, &comm, &fail);
  if (fail.code != NE_NOERROR) {
    printf("Error from nag_real_partial_svd (f02wgc).\n%s\n", fail.message);
    exit_status = 1;
    goto END;
  }
  /* Print computed residuals */
  printf("%s\n", "  Singular Value    Residual");
  for (i = 0; i < nconv; i++)
    printf("%10.5f %16.2g\n", sigma[i], resid[i]);
  printf("\n");

END:
  NAG_FREE(resid);
  NAG_FREE(sigma);
  NAG_FREE(u);
  NAG_FREE(v);

  return exit_status;
}

static void NAG_CALL av(Integer *iflag, Integer m, Integer n,
                        const double x[], double ax[], Nag_Comm *comm)
{
  Integer i, j;
  double one = 1.0, zero = 0.0;
  double h, k, s, t;

  /* Matrix vector multiply: w <- A*x or w <- Trans(A)*x. */
  if (comm->user[0] == -1.0) {
    printf("(User-supplied callback av, first invocation.)\n");
    comm->user[0] = 0.0;
  }
  h = one / (double) (m + 1);
  k = one / (double) (n + 1);
  if (*iflag == 1) {
    for (i = 0; i < m; i++)
      ax[i] = zero;
    t = zero;
    for (j = 0; j < n; j++) {
      t = t + k;
      s = zero;
      for (i = 0; i < MIN(j + 1, m); i++) {
        s = s + h;
        ax[i] = ax[i] + k * s * (t - one) * x[j];
      }
      for (i = j + 1; i < m; i++) {
        s = s + h;
        ax[i] = ax[i] + k * t * (s - one) * x[j];
      }
    }
  }
  else {
    for (i = 0; i < n; i++)
      ax[i] = zero;
    t = zero;
    for (j = 0; j < n; j++) {
      t = t + k;
      s = zero;
      for (i = 0; i < MIN(j + 1, m); i++) {
        s = s + h;
        ax[j] = ax[j] + k * s * (t - one) * x[i];
      }
      for (i = j + 1; i < m; i++) {
        s = s + h;
        ax[j] = ax[j] + k * t * (s - one) * x[i];
      }
    }
  }
  return;
}