/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef vbitset_deeaeda9_1fc9_43e4_9026_c5eeb93c6ebb_h
#define vbitset_deeaeda9_1fc9_43e4_9026_c5eeb93c6ebb_h

#include <intrin.h>
#include <gslib/config.h>
#include <gslib/std.h>

__gslib_begin__

inline int bit_count(uint n)
{
    uint t = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111);
    return ((t + (t >> 3)) & 030707070707) % 63;
}

inline uint bitelim_first_nonzero(uint n) { return n & (n - 1); }
inline uint bitmask_first_nonzero(uint n) { return n ^ (n - 1); }

inline int bitscan_forward(uint n)
{
    uint p;
    return _BitScanForward((unsigned long*)&p, n) ? (int)p : -1;
}

inline int bitscan_reverse(uint n)
{
    uint p;
    return _BitScanReverse((unsigned long*)&p, n) ? (int)p : -1;
}

class vbitset
{
public:
    typedef vector<uint> container;
    static const uint knot_size = sizeof(uint) * 8;

protected:
    container           _container;
    int                 _size;

public:
    vbitset() { _size = 0; }
    int knot_count() const { return (int)_container.size(); }
    uint knot_at(int i) const
    {
        assert(i < knot_count());
        return _container.at(i);
    }
    void set_knot(int p, uint k)
    {
        if(p >= knot_count()) {
            assert(!"unexpected.");
            return;
        }
        _container.at(p) = k;
    }
    uint& ref_knot_at(int i)
    {
        assert(i < knot_count());
        return _container.at(i);
    }
    int capacity() const { return knot_size * knot_count(); }
    int size() const { return _size; }
    int length() const { return _size; }
    void set(int pos) { reset(pos, true); }
    void reset(int pos, bool b = false)
    {
        int cc = cycle_count(pos);
        int cidx = cycle_index(pos);
        ensure_container_capacity(pos, cc, cidx);
        uint& n = _container.at(cc);
        uint mask = (1 << cidx);
        b ? (n |= mask) : (n &= ~mask);
    }
    void reset(bool b)
    {
        int cc = cycle_count(_size);
        int cidx = cycle_index(_size);
        int m = b ? -1 : 0;
        memset(&_container.front(), m, cc * sizeof(uint));
        uint mask = (1 << (cidx + 1)) - 1;
        uint& n = _container.at(cc);
        b ? (n |= mask) : (n &= ~mask);
    }
    bool test(int pos)
    {
        if(pos >= _size)
            return false;
        int cc = cycle_count(pos);
        int cidx = cycle_index(pos);
        uint n = knot_at(cc);
        uint mask = (1 << cidx);
        return (n & mask) != 0;
    }
    void resize(int s)
    {
        if(s == 0) {
            _container.clear();
            _size = 0;
            return;
        }
        if(s == _size)
            return;
        else if(s < _size) {
            ensure_container_capacity(s - 1);
            return;
        }
        else {
            int cc = cycle_count(s - 1);
            _container.resize(cc + 1);
            _size = s;
        }
    }
    void rotate_left(int bias)
    {
        if(!bias)
            return;
        if(bias < 0)
            return rotate_right(-bias);
        bias %= _size;
        if(!bias)
            return;
        int cc = cycle_count(bias);
        int cm = cycle_index(bias);
        rotate_left(cc, cm);
    }
    void rotate_right(int bias)
    {
        if(!bias)
            return;
        if(bias < 0)
            return rotate_left(-bias);
        bias %= _size;
        if(!bias)
            return;
        int cc = cycle_count(bias);
        int cm = cycle_index(bias);
        rotate_right(cc, cm);
    }
    // todo: following
    void rotate_left(int cc, int cm)
    {
        assert(cc >= 0 && cm >= 0);
        if(!cm) {
            uint* ptr = new uint[cc];
            int crst = (int)_container.size() - cc;
            memcpy_s(ptr, sizeof(uint) * cc, &_container.front(), _container.capacity());
            memcpy_s(&_container.front(), sizeof(uint) * crst, &_container.at(cc), sizeof(uint) * crst);
            memcpy_s(&_container.front(), sizeof(uint) * cc, ptr, sizeof(uint) * cc);
            delete [] ptr;
        }
        else {

        }
    }
    void rotate_right(int cc, int cm)
    {
        assert(cc >= 0 && cm >= 0);
    }
    void shift_left(int bias);
    void shift_right(int bias);
    void shift_left_1(int bias);
    void shift_right_1(int bias);

public:
    static int cycle_count(int p) { return p / knot_size; }
    static int cycle_index(int p) { return p % knot_size; }

protected:
    void ensure_container_capacity(int pos)
    {
        if(pos < _size)
            return;
        int cc = cycle_count(pos);
        int cidx = cycle_index(pos);
        ensure_container_capacity(pos, cc, cidx);
    }
    void ensure_container_capacity(int pos, int cc, int cidx)
    {
        assert(cc * knot_size + cidx == pos);
        if(pos < _size)
            return;
        int kc = knot_count();
        assert(kc <= cc);
        if(kc == cc) {
            _container.push_back(0);
            _size = pos + 1;
        }
        else {
            cc ++;
            _container.resize(cc);
            int delta = cc - kc;
            memset(&_container.at(kc), 0, delta * sizeof(uint));
            _size = pos + 1;
        }
    }
};

__gslib_end__

#endif


#include <petsc.h>
#include "um.h"

PetscErrorCode UMInitialize(UM *mesh) {
    mesh->N = 0;
    mesh->K = 0;
    mesh->P = 0;
    mesh->loc = NULL;
    mesh->e = NULL;
    mesh->bf = NULL;
    mesh->ns = NULL;
    return 0;
}

PetscErrorCode UMDestroy(UM *mesh) {
    PetscErrorCode ierr;
    ierr = VecDestroy(&(mesh->loc)); CHKERRQ(ierr);
    ierr = ISDestroy(&(mesh->e)); CHKERRQ(ierr);
    ierr = ISDestroy(&(mesh->bf)); CHKERRQ(ierr);
    ierr = ISDestroy(&(mesh->ns)); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode UMViewASCII(UM *mesh, PetscViewer viewer) {
    PetscErrorCode ierr;
    PetscInt        n, k;
    const Node      *aloc;
    const PetscInt  *ae, *abf, *ans;

    ierr = PetscViewerASCIIPushSynchronized(viewer); CHKERRQ(ierr);
    if (mesh->loc && (mesh->N > 0)) {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"%d nodes at (x,y) coordinates:\n",mesh->N); CHKERRQ(ierr);
        ierr = VecGetArrayRead(mesh->loc,(const PetscReal **)&aloc); CHKERRQ(ierr);
        for (n = 0; n < mesh->N; n++) {
            ierr = PetscViewerASCIISynchronizedPrintf(viewer,"    %3d : (%g,%g)\n",
                               n,aloc[n].x,aloc[n].y); CHKERRQ(ierr);
        }
        ierr = VecRestoreArrayRead(mesh->loc,(const PetscReal **)&aloc); CHKERRQ(ierr);
    } else {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"node coordinates empty or unallocated\n"); CHKERRQ(ierr);
    }
    if (mesh->e && (mesh->K > 0)) {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"%d elements:\n",mesh->K); CHKERRQ(ierr);
        ierr = ISGetIndices(mesh->e,&ae); CHKERRQ(ierr);
        for (k = 0; k < mesh->K; k++) {
            ierr = PetscPrintf(PETSC_COMM_WORLD,"    %3d : %3d %3d %3d\n",
                               k,ae[3*k+0],ae[3*k+1],ae[3*k+2]); CHKERRQ(ierr);
        }
        ierr = ISRestoreIndices(mesh->e,&ae); CHKERRQ(ierr);
    } else {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"element index triples empty or unallocated\n"); CHKERRQ(ierr);
    }
    if (mesh->bf && (mesh->N > 0)) {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"%d boundary flags at nodes (0 = interior, 1 = boundary, 2 = Dirichlet):\n",mesh->N); CHKERRQ(ierr);
        ierr = ISGetIndices(mesh->bf,&abf); CHKERRQ(ierr);
        for (n = 0; n < mesh->N; n++) {
            ierr = PetscViewerASCIISynchronizedPrintf(viewer,"    %3d : %1d\n",
                               n,abf[n]); CHKERRQ(ierr);
        }
        ierr = ISRestoreIndices(mesh->bf,&abf); CHKERRQ(ierr);
    } else {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"boundary flags empty or unallocated\n"); CHKERRQ(ierr);
    }
    if (mesh->ns && (mesh->P > 0)) {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"%d Neumann boundary segments:\n",mesh->P); CHKERRQ(ierr);
        ierr = ISGetIndices(mesh->ns,&ans); CHKERRQ(ierr);
        for (n = 0; n < mesh->P; n++) {
            ierr = PetscViewerASCIISynchronizedPrintf(viewer,"    %3d : %3d %3d\n",
                               n,ans[2*n+0],ans[2*n+1]); CHKERRQ(ierr);
        }
        ierr = ISRestoreIndices(mesh->ns,&ans); CHKERRQ(ierr);
    } else {
        ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Neumann boundary segments empty or unallocated\n"); CHKERRQ(ierr);
    }
    ierr = PetscViewerASCIIPopSynchronized(viewer); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode UMViewSolutionBinary(UM *mesh, char *filename, Vec u) {
    PetscErrorCode ierr;
    PetscInt       Nu;
    PetscViewer viewer;
    ierr = VecGetSize(u,&Nu); CHKERRQ(ierr);
    if (Nu != mesh->N) {
        SETERRQ2(PETSC_COMM_SELF,1,
           "incompatible sizes of u (=%d) and number of nodes (=%d)\n",Nu,mesh->N);
    }
    ierr = PetscViewerBinaryOpen(PETSC_COMM_WORLD,filename,FILE_MODE_WRITE,&viewer); CHKERRQ(ierr);
    ierr = VecView(u,viewer); CHKERRQ(ierr);
    ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode UMReadNodes(UM *mesh, char *filename) {
    PetscErrorCode ierr;
    PetscInt       twoN;
    PetscViewer viewer;
    if (mesh->N > 0) {
        SETERRQ(PETSC_COMM_SELF,1,"nodes already created?\n");
    }
    ierr = VecCreate(PETSC_COMM_WORLD,&mesh->loc); CHKERRQ(ierr);
    ierr = VecSetFromOptions(mesh->loc); CHKERRQ(ierr);
    ierr = PetscViewerBinaryOpen(PETSC_COMM_WORLD,filename,FILE_MODE_READ,&viewer); CHKERRQ(ierr);
    ierr = VecLoad(mesh->loc,viewer); CHKERRQ(ierr);
    ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
    ierr = VecGetSize(mesh->loc,&twoN); CHKERRQ(ierr);
    if (twoN % 2 != 0) {
        SETERRQ1(PETSC_COMM_SELF,2,"node locations loaded from %s are not N pairs\n",filename);
    }
    mesh->N = twoN / 2;
    return 0;
}

PetscErrorCode UMCheckElements(UM *mesh) {
    PetscErrorCode ierr;
    const PetscInt  *ae;
    PetscInt        k, m;
    if ((mesh->K == 0) || (mesh->e == NULL)) {
        SETERRQ(PETSC_COMM_SELF,1,
                "number of elements unknown; call UMReadElements() first\n");
    }
    if (mesh->N == 0) {
        SETERRQ(PETSC_COMM_SELF,2,
                "node size unknown so element check impossible; call UMReadNodes() first\n");
    }
    ierr = ISGetIndices(mesh->e,&ae); CHKERRQ(ierr);
    for (k = 0; k < mesh->K; k++) {
        for (m = 0; m < 3; m++) {
            if ((ae[3*k+m] < 0) || (ae[3*k+m] >= mesh->N)) {
                SETERRQ3(PETSC_COMM_SELF,3,
                   "index e[%d]=%d invalid: not between 0 and N-1=%d\n",
                   3*k+m,ae[3*k+m],mesh->N-1);
            }
        }
        // FIXME: could add check for distinct indices
    }
    ierr = ISRestoreIndices(mesh->e,&ae); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode UMCheckBoundaryData(UM *mesh) {
    PetscErrorCode ierr;
    const PetscInt  *ans, *abf;
    PetscInt        n, m;
    if (mesh->N == 0) {
        SETERRQ(PETSC_COMM_SELF,2,
                "node size unknown so boundary flag check impossible; call UMReadNodes() first\n");
    }
    if (mesh->bf == NULL) {
        SETERRQ(PETSC_COMM_SELF,1,
                "boundary flags at nodes not allocated; call UMReadNodes() first\n");
    }
    if ((mesh->P > 0) && (mesh->ns == NULL)) {
        SETERRQ(PETSC_COMM_SELF,3,
                "inconsistent data for Neumann boundary segments\n");
    }
    ierr = ISGetIndices(mesh->bf,&abf); CHKERRQ(ierr);
    for (n = 0; n < mesh->N; n++) {
        switch (abf[n]) {
            case 0 :
            case 1 :
            case 2 :
                break;
            default :
                SETERRQ2(PETSC_COMM_SELF,5,
                   "boundary flag bf[%d]=%d invalid: not in {0,1,2}\n",
                   n,abf[n]);
        }
    }
    ierr = ISRestoreIndices(mesh->bf,&abf); CHKERRQ(ierr);
    if (mesh->P > 0) {
        ierr = ISGetIndices(mesh->ns,&ans); CHKERRQ(ierr);
        for (n = 0; n < mesh->P; n++) {
            for (m = 0; m < 2; m++) {
                if ((ans[2*n+m] < 0) || (ans[2*n+m] >= mesh->N)) {
                    SETERRQ3(PETSC_COMM_SELF,6,
                       "index ns[%d]=%d invalid: not between 0 and N-1=%d\n",
                       2*n+m,ans[3*n+m],mesh->N-1);
                }
            }
        }
        ierr = ISRestoreIndices(mesh->ns,&ans); CHKERRQ(ierr);
    }
    return 0;
}

PetscErrorCode UMReadISs(UM *mesh, char *filename) {
    PetscErrorCode ierr;
    PetscViewer  viewer;
    PetscInt     n_bf;
    if ((!mesh->loc) || (mesh->N == 0)) {
        SETERRQ(PETSC_COMM_SELF,2,
                "node coordinates not created ... do that first ... stopping\n");
    }
    if ((mesh->K > 0) || (mesh->P > 0) || (mesh->e != NULL) || (mesh->bf != NULL) || (mesh->ns != NULL)) {
        SETERRQ(PETSC_COMM_SELF,1,
                "elements, boundary flags, Neumann boundary segments already created? ... stopping\n");
    }
    ierr = PetscViewerBinaryOpen(PETSC_COMM_WORLD,filename,FILE_MODE_READ,&viewer); CHKERRQ(ierr);
    // create and load e
    ierr = ISCreate(PETSC_COMM_WORLD,&(mesh->e)); CHKERRQ(ierr);
    ierr = ISLoad(mesh->e,viewer); CHKERRQ(ierr);
    ierr = ISGetSize(mesh->e,&(mesh->K)); CHKERRQ(ierr);
    if (mesh->K % 3 != 0) {
        SETERRQ1(PETSC_COMM_SELF,3,
                 "IS e loaded from %s is wrong size for list of element triples\n",filename);
    }
    mesh->K /= 3;
    // create and load bf
    ierr = ISCreate(PETSC_COMM_WORLD,&(mesh->bf)); CHKERRQ(ierr);
    ierr = ISLoad(mesh->bf,viewer); CHKERRQ(ierr);
    ierr = ISGetSize(mesh->bf,&n_bf); CHKERRQ(ierr);
    if (n_bf != mesh->N) {
        SETERRQ1(PETSC_COMM_SELF,4,
                 "IS bf loaded from %s is wrong size for list of boundary flags\n",filename);
    }
    // FIXME  seems there is no way to tell if file is empty at this point
    // create and load ns last ... may *start with a negative value* in which case set P = 0
    const PetscInt *ans;
    ierr = ISCreate(PETSC_COMM_WORLD,&(mesh->ns)); CHKERRQ(ierr);
    ierr = ISLoad(mesh->ns,viewer); CHKERRQ(ierr);
    ierr = ISGetIndices(mesh->ns,&ans); CHKERRQ(ierr);
    if (ans[0] < 0) {
        ISDestroy(&(mesh->ns));
        mesh->ns = NULL;
        mesh->P = 0;
    } else {
        ierr = ISGetSize(mesh->ns,&(mesh->P)); CHKERRQ(ierr);
        if (mesh->P % 2 != 0) {
            SETERRQ1(PETSC_COMM_SELF,4,
                     "IS s loaded from %s is wrong size for list of Neumann boundary segment pairs\n",filename);
        }
        mesh->P /= 2;
    }
    ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);

    // check that mesh is complete now
    ierr = UMCheckElements(mesh); CHKERRQ(ierr);
    ierr = UMCheckBoundaryData(mesh); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode UMStats(UM *mesh, PetscReal *maxh, PetscReal *meanh,
                       PetscReal *maxa, PetscReal *meana) {
    PetscErrorCode ierr;
    const PetscInt *ae;
    const Node     *aloc;
    PetscInt       k;
    PetscReal      x[3], y[3], ax, ay, bx, by, cx, cy, h, a,
                   Maxh = 0.0, Maxa = 0.0, Sumh = 0.0, Suma = 0.0;
    if ((mesh->K == 0) || (mesh->e == NULL)) {
        SETERRQ(PETSC_COMM_SELF,1,
                "number of elements unknown; call UMReadElements() first\n");
    }
    if (mesh->N == 0) {
        SETERRQ(PETSC_COMM_SELF,2,
                "node size unknown so element check impossible; call UMReadNodes() first\n");
    }
    ierr = UMGetNodeCoordArrayRead(mesh,&aloc); CHKERRQ(ierr);
    ierr = ISGetIndices(mesh->e,&ae); CHKERRQ(ierr);
    for (k = 0; k < mesh->K; k++) {
        x[0] = aloc[ae[3*k]].x;
        y[0] = aloc[ae[3*k]].y;
        x[1] = aloc[ae[3*k+1]].x;
        y[1] = aloc[ae[3*k+1]].y;
        x[2] = aloc[ae[3*k+2]].x;
        y[2] = aloc[ae[3*k+2]].y;
        ax = x[1] - x[0];
        ay = y[1] - y[0];
        bx = x[2] - x[0];
        by = y[2] - y[0];
        cx = x[1] - x[2];
        cy = y[1] - y[2];
        h = PetscMax(ax*ax+ay*ay, PetscMax(bx*bx+by*by, cx*cx+cy*cy));
        h = sqrt(h);
        a = 0.5 * PetscAbs(ax*by-ay*bx);
        Maxh = PetscMax(Maxh,h);
        Sumh += h;
        Maxa = PetscMax(Maxa,a);
        Suma += a;
    }
    ierr = ISRestoreIndices(mesh->e,&ae); CHKERRQ(ierr);
    ierr = UMRestoreNodeCoordArrayRead(mesh,&aloc); CHKERRQ(ierr);
    if (maxh)  *maxh = Maxh;
    if (maxa)  *maxa = Maxa;
    if (meanh)  *meanh = Sumh / mesh->K;
    if (meana)  *meana = Suma / mesh->K;
    return 0;
}

PetscErrorCode UMGetNodeCoordArrayRead(UM *mesh, const Node **xy) {
    PetscErrorCode ierr;
    if ((!mesh->loc) || (mesh->N == 0)) {
        SETERRQ(PETSC_COMM_SELF,1,"node coordinates not created ... stopping\n");
    }
    ierr = VecGetArrayRead(mesh->loc,(const PetscReal **)xy); CHKERRQ(ierr);
    return 0;
}

PetscErrorCode UMRestoreNodeCoordArrayRead(UM *mesh, const Node **xy) {
    PetscErrorCode ierr;
    if ((!mesh->loc) || (mesh->N == 0)) {
        SETERRQ(PETSC_COMM_SELF,1,"node coordinates not created ... stopping\n");
    }
    ierr = VecRestoreArrayRead(mesh->loc,(const PetscReal **)xy); CHKERRQ(ierr);
    return 0;
}


#include <errno.h>
#include <getopt.h>
#include <gsl/gsl_spmatrix.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

static int verbose = 0;
static int help = 0;

int test (size_t m,
	  size_t n,
	  size_t r,
	  size_t c,
	  size_t nnz,
	  const size_t *ind,
	  const size_t *ptr,
	  const double *data,
          int trials,
          int verbose);

static void usage () {
  fprintf(stderr,"usage: spmv [options] <input>\n"
  "  <input>                    MatrixMarket file (multiply this matrix)\n"
  "  -r, --block_r <arg>        Row block size\n"
  "  -c, --block_c <arg>        Column block size\n"
  "  -t, --trials <arg>         Number of trials to run\n"
  "  -v, --verbose              Verbose mode\n"
  "  -q, --quiet                Quiet mode\n"
  "  -h, --help                 Display help message\n");
}

int main (int argc, char **argv) {

  size_t b_r = 1;
  size_t b_c = 1;
  int trials = 1;

  /* Beware. Option parsing below. */
  long longarg;
  double doublearg;
  while (1) {
    static char *options = "r:c:t:vqh";
    static struct option long_options[] = {
        {"block_r",  required_argument, 0, 'r'},
        {"block_c",  required_argument, 0, 'c'},
        {"trials",   required_argument, 0, 't'},
        {"verbose",   no_argument, &verbose, 1},
        {"quiet",     no_argument, &verbose, 0},
        {"help",      no_argument, &help,    1},
        {0, 0, 0, 0}
      };

    /* getopt_long stores the option index here. */
    int option_index = 0;

    int c = getopt_long (argc, argv, options,
                     long_options, &option_index);

    /* Detect the end of the options. */
    if (c == -1)
      break;

    if (c == 0 && long_options[option_index].flag == 0)
      c = long_options[option_index].val;

    switch (c) {
      case 0:
        /* If this option set a flag, do nothing else now. */
        break;

      case 'r':
        errno = 0;
        longarg = strtol(optarg, 0, 10);
        if (errno != 0 || longarg < 1) {
          printf("option -r takes an integer column block size >= 1\n");
          usage();
          return 1;
        }
        b_r = longarg;
        break;

      case 'c':
        errno = 0;
        longarg = strtol(optarg, 0, 10);
        if (errno != 0 || longarg < 1) {
          printf("option -c takes an integer column block size >= 1\n");
          usage();
          return 1;
        }
        b_c = longarg;
        break;

      case 't':
        errno = 0;
        longarg = strtol(optarg, 0, 10);
        if (errno != 0 || longarg < 1) {
          printf("option -t takes an integer number of trials >= 1\n");
          usage();
          return 1;
        }
        trials = longarg;
        break;

      case 'v':
        verbose = 1;
        break;

      case 'q':
        verbose = 0;
        break;

      case 'h':
        help = 1;
        break;

      case '?':
        usage();
        return 1;

      default:
        abort();
    }
  }

  if (help) {
    printf("Run a fill estimation algorithm!\n");
    usage();
    return 0;
  }

  if (argc - optind > 1) {
    printf("<input> cannot be more than one file\n");
    usage();
    return 1;
  }

  if (argc - optind < 1) {
    printf("<input> not specified\n");
    usage();
    return 1;
  }

  struct stat statthing;
  if (stat(argv[optind], &statthing) < 0 || !S_ISREG(statthing.st_mode)){
    printf("<input> must be filename of MatrixMarket matrix\n");
    usage();
    return 1;
  }

  FILE *f = fopen(argv[optind], "r");
  gsl_spmatrix *triples = gsl_spmatrix_fscanf(f);
  fclose(f);
  if (triples == 0) {
    printf("<input> must be filename of MatrixMarket matrix\n");
    usage();
    return 1;
  }

  int ret = test(triples->size1, triples->size2, b_r, b_c, triples->nz, triples->i, triples->p, triples->data, trials, verbose);

  gsl_spmatrix_free(triples);

  return ret;

}


/**
 *
 * @file qwrapper_zlaswp.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @precisions normal z -> c d s
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zlaswp(Quark *quark, Quark_Task_Flags *task_flags,
                       int n, PLASMA_Complex64_t *A, int lda,
                       int i1,  int i2, const int *ipiv, int inc)
{
    DAG_CORE_LASWP;
    QUARK_Insert_Task(
        quark, CORE_zlaswp_quark, task_flags,
        sizeof(int),                      &n,    VALUE,
        sizeof(PLASMA_Complex64_t)*lda*n,  A,        INOUT | LOCALITY,
        sizeof(int),                      &lda,  VALUE,
        sizeof(int),                      &i1,   VALUE,
        sizeof(int),                      &i2,   VALUE,
        sizeof(int)*n,                     ipiv,     INPUT,
        sizeof(int),                      &inc,  VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zlaswp_quark = PCORE_zlaswp_quark
#define CORE_zlaswp_quark PCORE_zlaswp_quark
#endif
void CORE_zlaswp_quark(Quark *quark)
{
    int n, lda, i1, i2, inc;
    int *ipiv;
    PLASMA_Complex64_t *A;

    quark_unpack_args_7(quark, n, A, lda, i1, i2, ipiv, inc);
    LAPACKE_zlaswp_work(LAPACK_COL_MAJOR, n, A, lda, i1, i2, ipiv, inc );
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zlaswp_f2(Quark *quark, Quark_Task_Flags *task_flags,
                          int n, PLASMA_Complex64_t *A, int lda,
                          int i1,  int i2, const int *ipiv, int inc,
                          PLASMA_Complex64_t *fake1, int szefake1, int flag1,
                          PLASMA_Complex64_t *fake2, int szefake2, int flag2)
{
    DAG_CORE_LASWP;
    QUARK_Insert_Task(
        quark, CORE_zlaswp_f2_quark, task_flags,
        sizeof(int),                        &n,     VALUE,
        sizeof(PLASMA_Complex64_t)*lda*n,    A,         INOUT | LOCALITY,
        sizeof(int),                        &lda,   VALUE,
        sizeof(int),                        &i1,    VALUE,
        sizeof(int),                        &i2,    VALUE,
        sizeof(int)*n,                       ipiv,      INPUT,
        sizeof(int),                        &inc,   VALUE,
        sizeof(PLASMA_Complex64_t)*szefake1, fake1,     flag1,
        sizeof(PLASMA_Complex64_t)*szefake2, fake2,     flag2,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zlaswp_f2_quark = PCORE_zlaswp_f2_quark
#define CORE_zlaswp_f2_quark PCORE_zlaswp_f2_quark
#endif
void CORE_zlaswp_f2_quark(Quark* quark)
{
    int n, lda, i1, i2, inc;
    int *ipiv;
    PLASMA_Complex64_t *A;
    void *fake1, *fake2;

    quark_unpack_args_9(quark, n, A, lda, i1, i2, ipiv, inc, fake1, fake2);
    LAPACKE_zlaswp_work(LAPACK_COL_MAJOR, n, A, lda, i1, i2, ipiv, inc );
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zlaswp_ontile(Quark *quark, Quark_Task_Flags *task_flags,
                              PLASMA_desc descA, PLASMA_Complex64_t *Aij,
                              int i1,  int i2, const int *ipiv, int inc, PLASMA_Complex64_t *fakepanel)
{
    DAG_CORE_LASWP;
    if (fakepanel == Aij) {
        QUARK_Insert_Task(
            quark, CORE_zlaswp_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(PLASMA_Complex64_t)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(PLASMA_Complex64_t)*1,      fakepanel,     SCRATCH,
            0);
    } else {
        QUARK_Insert_Task(
            quark, CORE_zlaswp_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(PLASMA_Complex64_t)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(PLASMA_Complex64_t)*1,      fakepanel,     INOUT,
            0);
    }
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zlaswp_ontile_quark = PCORE_zlaswp_ontile_quark
#define CORE_zlaswp_ontile_quark PCORE_zlaswp_ontile_quark
#endif
void CORE_zlaswp_ontile_quark(Quark *quark)
{
    int i1, i2, inc;
    int *ipiv;
    PLASMA_Complex64_t *A, *fake;
    PLASMA_desc descA;

    quark_unpack_args_7(quark, descA, A, i1, i2, ipiv, inc, fake);
    CORE_zlaswp_ontile(descA, i1, i2, ipiv, inc);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zlaswp_ontile_f2(Quark *quark, Quark_Task_Flags *task_flags,
                                 PLASMA_desc descA, PLASMA_Complex64_t *Aij,
                                 int i1,  int i2, const int *ipiv, int inc,
                                 PLASMA_Complex64_t *fake1, int szefake1, int flag1,
                                 PLASMA_Complex64_t *fake2, int szefake2, int flag2)
{
    DAG_CORE_LASWP;
    QUARK_Insert_Task(
        quark, CORE_zlaswp_ontile_f2_quark, task_flags,
        sizeof(PLASMA_desc),                &descA, VALUE,
        sizeof(PLASMA_Complex64_t)*1,        Aij,       INOUT | LOCALITY,
        sizeof(int),                        &i1,    VALUE,
        sizeof(int),                        &i2,    VALUE,
        sizeof(int)*(i2-i1+1)*abs(inc),      ipiv,      INPUT,
        sizeof(int),                        &inc,   VALUE,
        sizeof(PLASMA_Complex64_t)*szefake1, fake1, flag1,
        sizeof(PLASMA_Complex64_t)*szefake2, fake2, flag2,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zlaswp_ontile_f2_quark = PCORE_zlaswp_ontile_f2_quark
#define CORE_zlaswp_ontile_f2_quark PCORE_zlaswp_ontile_f2_quark
#endif
void CORE_zlaswp_ontile_f2_quark(Quark *quark)
{
    int i1, i2, inc;
    int *ipiv;
    PLASMA_Complex64_t *A;
    PLASMA_desc descA;
    void *fake1, *fake2;

    quark_unpack_args_8(quark, descA, A, i1, i2, ipiv, inc, fake1, fake2);
    CORE_zlaswp_ontile(descA, i1, i2, ipiv, inc);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zswptr_ontile(Quark *quark, Quark_Task_Flags *task_flags,
                              PLASMA_desc descA, PLASMA_Complex64_t *Aij,
                              int i1,  int i2, const int *ipiv, int inc,
                              const PLASMA_Complex64_t *Akk, int ldak)
{
    DAG_CORE_TRSM;
    QUARK_Insert_Task(
        quark, CORE_zswptr_ontile_quark, task_flags,
        sizeof(PLASMA_desc),              &descA, VALUE,
        sizeof(PLASMA_Complex64_t)*1,      Aij,       INOUT | LOCALITY,
        sizeof(int),                      &i1,    VALUE,
        sizeof(int),                      &i2,    VALUE,
        sizeof(int)*(i2-i1+1)*abs(inc),    ipiv,      INPUT,
        sizeof(int),                      &inc,   VALUE,
        sizeof(PLASMA_Complex64_t)*ldak,   Akk,       INPUT,
        sizeof(int),                      &ldak,  VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zswptr_ontile_quark = PCORE_zswptr_ontile_quark
#define CORE_zswptr_ontile_quark PCORE_zswptr_ontile_quark
#endif
void CORE_zswptr_ontile_quark(Quark *quark)
{
    int i1, i2, inc, ldak;
    int *ipiv;
    PLASMA_Complex64_t *A, *Akk;
    PLASMA_desc descA;

    quark_unpack_args_8(quark, descA, A, i1, i2, ipiv, inc, Akk, ldak);
    CORE_zswptr_ontile(descA, i1, i2, ipiv, inc, Akk, ldak);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_zlaswpc_ontile(Quark *quark, Quark_Task_Flags *task_flags,
                              PLASMA_desc descA, PLASMA_Complex64_t *Aij,
                              int i1,  int i2, const int *ipiv, int inc, PLASMA_Complex64_t *fakepanel)
{
    DAG_CORE_LASWP;
    if (fakepanel == Aij) {
        QUARK_Insert_Task(
            quark, CORE_zlaswpc_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(PLASMA_Complex64_t)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(PLASMA_Complex64_t)*1,      fakepanel,     SCRATCH,
            0);
    } else {
        QUARK_Insert_Task(
            quark, CORE_zlaswpc_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(PLASMA_Complex64_t)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(PLASMA_Complex64_t)*1,      fakepanel,     INOUT,
            0);
    }
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_zlaswpc_ontile_quark = PCORE_zlaswpc_ontile_quark
#define CORE_zlaswpc_ontile_quark PCORE_zlaswpc_ontile_quark
#endif
void CORE_zlaswpc_ontile_quark(Quark *quark)
{
    int i1, i2, inc;
    int *ipiv;
    PLASMA_Complex64_t *A, *fake;
    PLASMA_desc descA;

    quark_unpack_args_7(quark, descA, A, i1, i2, ipiv, inc, fake);
    CORE_zlaswpc_ontile(descA, i1, i2, ipiv, inc);
}


/* roots/gsl_roots.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Reid Priedhorsky, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_ROOTS_H__
#define __GSL_ROOTS_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_math.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
  {
    const char *name;
    size_t size;
    int (*set) (void *state, gsl_function * f, double * root, double x_lower, double x_upper);
    int (*iterate) (void *state, gsl_function * f, double * root, double * x_lower, double * x_upper);
  }
gsl_root_fsolver_type;

typedef struct
  {
    const gsl_root_fsolver_type * type;
    gsl_function * function ;
    double root ;
    double x_lower;
    double x_upper;
    void *state;
  }
gsl_root_fsolver;

typedef struct
  {
    const char *name;
    size_t size;
    int (*set) (void *state, gsl_function_fdf * f, double * root);
    int (*iterate) (void *state, gsl_function_fdf * f, double * root);
  }
gsl_root_fdfsolver_type;

typedef struct
  {
    const gsl_root_fdfsolver_type * type;
    gsl_function_fdf * fdf ;
    double root ;
    void *state;
  }
gsl_root_fdfsolver;

GSL_FUN gsl_root_fsolver *
gsl_root_fsolver_alloc (const gsl_root_fsolver_type * T);
GSL_FUN void gsl_root_fsolver_free (gsl_root_fsolver * s);

GSL_FUN int gsl_root_fsolver_set (gsl_root_fsolver * s,
                          gsl_function * f,
                          double x_lower, double x_upper);

GSL_FUN int gsl_root_fsolver_iterate (gsl_root_fsolver * s);

GSL_FUN const char * gsl_root_fsolver_name (const gsl_root_fsolver * s);
GSL_FUN double gsl_root_fsolver_root (const gsl_root_fsolver * s);
GSL_FUN double gsl_root_fsolver_x_lower (const gsl_root_fsolver * s);
GSL_FUN double gsl_root_fsolver_x_upper (const gsl_root_fsolver * s);

GSL_FUN gsl_root_fdfsolver *
gsl_root_fdfsolver_alloc (const gsl_root_fdfsolver_type * T);

GSL_FUN int
gsl_root_fdfsolver_set (gsl_root_fdfsolver * s,
                         gsl_function_fdf * fdf, double root);

GSL_FUN int
gsl_root_fdfsolver_iterate (gsl_root_fdfsolver * s);

GSL_FUN void
gsl_root_fdfsolver_free (gsl_root_fdfsolver * s);

GSL_FUN const char * gsl_root_fdfsolver_name (const gsl_root_fdfsolver * s);
GSL_FUN double gsl_root_fdfsolver_root (const gsl_root_fdfsolver * s);

GSL_FUN int
gsl_root_test_interval (double x_lower, double x_upper, double epsabs, double epsrel);

GSL_FUN int
gsl_root_test_residual (double f, double epsabs);

GSL_FUN int
gsl_root_test_delta (double x1, double x0, double epsabs, double epsrel);

GSL_VAR const gsl_root_fsolver_type  * gsl_root_fsolver_bisection;
GSL_VAR const gsl_root_fsolver_type  * gsl_root_fsolver_brent;
GSL_VAR const gsl_root_fsolver_type  * gsl_root_fsolver_falsepos;
GSL_VAR const gsl_root_fdfsolver_type  * gsl_root_fdfsolver_newton;
GSL_VAR const gsl_root_fdfsolver_type  * gsl_root_fdfsolver_secant;
GSL_VAR const gsl_root_fdfsolver_type  * gsl_root_fdfsolver_steffenson;

__END_DECLS

#endif /* __GSL_ROOTS_H__ */


#include <stdio.h>
#include <gsl/gsl_rng.h>

gsl_rng * r;  /* global generator */

int
main (void)
{
  const gsl_rng_type * T;

  gsl_rng_env_setup();

  T = gsl_rng_default;
  r = gsl_rng_alloc (T);

  printf ("generator type: %s\n", gsl_rng_name (r));
  printf ("seed = %lu\n", gsl_rng_default_seed);
  printf ("first value = %lu\n", gsl_rng_get (r));

  gsl_rng_free (r);
  return 0;
}


/**
 *
 * @file qwrapper_dlaset2.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Hatem Ltaief
 * @date 2010-11-15
 * @generated d Tue Jan  7 11:44:59 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_dlaset2(Quark *quark, Quark_Task_Flags *task_flags,
                       PLASMA_enum uplo, int M, int N,
                       double alpha, double *A, int LDA)
{
    DAG_CORE_LASET;
    QUARK_Insert_Task(quark, CORE_dlaset2_quark, task_flags,
        sizeof(PLASMA_enum),                &uplo,  VALUE,
        sizeof(int),                        &M,     VALUE,
        sizeof(int),                        &N,     VALUE,
        sizeof(double),         &alpha, VALUE,
        sizeof(double)*M*N,     A,      OUTPUT,
        sizeof(int),                        &LDA,   VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_dlaset2_quark = PCORE_dlaset2_quark
#define CORE_dlaset2_quark PCORE_dlaset2_quark
#endif
void CORE_dlaset2_quark(Quark *quark)
{
    PLASMA_enum uplo;
    int M;
    int N;
    double alpha;
    double *A;
    int LDA;

    quark_unpack_args_6(quark, uplo, M, N, alpha, A, LDA);
    CORE_dlaset2(uplo, M, N, alpha, A, LDA);
}


/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
**                                                                                **
** This file forms part of the Underworld geophysics modelling application.         **
**                                                                                **
** For full license and copyright information, please refer to the LICENSE.md file  **
** located at the project root, or contact the authors.                             **
**                                                                                **
**~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/
#include <petsc.h>
#include <petscvec.h>
#include <petscmat.h>
#include <petscksp.h>

#include "common-driver-utils.h"

/*

writes out

file1
  1) its, residual, time
  2) a summary of results
file2
  1) solver configuration

*/
PetscErrorCode BSSCR_solver_output( KSP ksp, PetscInt monitor_index, const char res_file[], const char solver_conf[] )
{
	PetscViewer v;
	MPI_Comm comm;
	PetscInt i,nargs;
	char **args;

	PetscObjectGetComm( (PetscObject)ksp, &comm );
	PetscViewerASCIIOpen( comm, res_file, &v );

	BSSCR_GeneratePetscHeader_for_viewer( v );
        PetscViewerASCIIPrintf( v, "\n");
	BSSCR_KSPLogSolve( v, monitor_index, ksp );
	BSSCR_BSSCR_KSPLogSolveSummary( v, monitor_index, ksp );

	Stg_PetscViewerDestroy(&v );

	//////////////////////////////////////////////////////////

	PetscObjectGetComm( (PetscObject)ksp, &comm );
	PetscViewerASCIIOpen( comm, solver_conf, &v );

	BSSCR_GeneratePetscHeader_for_viewer( v );
	PetscViewerASCIIPrintf( v, "\n\nSolver configuration: \n");

	PetscViewerASCIIPrintf( v, "\n1) Command line arguments supplied: \n");
	PetscViewerASCIIPushTab(v);
        PetscGetArgs( &nargs, &args );
        for( i=0; i<nargs; i++ ) {
                PetscViewerASCIIPrintf( v, "[%d]: %s \n", i, args[i] );
        }
	PetscViewerASCIIPopTab(v);

	PetscViewerASCIIPrintf( v, "\n2) Petsc KSPView(): \n");
	KSPView( ksp, v );

	Stg_PetscViewerDestroy(&v );

	PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "BSSCR_KSPLogConvergenceRate"
PetscErrorCode BSSCR_KSPLogConvergenceRate(PetscViewer v,PetscInt monitor_index, KSP ksp)
{
	PetscInt i;
	PetscReal *rlog;
	PetscLogDouble *log;
	PetscInt nits;
	PetscReal rho;

	BSSCR_KSPLogGetResidualTimeHistory(ksp,monitor_index, &nits,&rlog,&log);
	PetscViewerASCIIPrintf( v, "#-----------------------------------------------------------------------------------------------------------------------\n");
	PetscViewerASCIIPrintf( v, "# it         |r|              |r_0|               |r|/|r_0|          -log10(r/r0)        rho     |rn|/|r0|^{1/n}        \n" );
	PetscViewerASCIIPrintf( v, "#-----------------------------------------------------------------------------------------------------------------------\n");
	rho = 0.0;
	i = 0;
	PetscViewerASCIIPrintf( v, "%1.4d %14.12e %14.12e %14.12e %14.12e %14.12e %14.12e\n", i, rlog[i],rlog[0],rlog[i]/rlog[0],-log10(rlog[i]/rlog[0]), rho, 0.0 );
	for( i=1; i<nits; i++ ) {
		rho = (-log10(rlog[i]/rlog[0]))-(-log10(rlog[i-1]/rlog[0]));
		PetscViewerASCIIPrintf( v, "%1.4d %14.12e %14.12e %14.12e %14.12e %14.12e %14.12e \n",
				i, rlog[i],rlog[0],rlog[i]/rlog[0],-log10(rlog[i]/rlog[0]), rho, pow(rlog[i]/rlog[0], 1.0/(double)i) );
	}

	PetscFunctionReturn(0);
}


/**
 *
 * @file qwrapper_slaswp.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated s Tue Jan  7 11:44:58 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_slaswp(Quark *quark, Quark_Task_Flags *task_flags,
                       int n, float *A, int lda,
                       int i1,  int i2, const int *ipiv, int inc)
{
    DAG_CORE_LASWP;
    QUARK_Insert_Task(
        quark, CORE_slaswp_quark, task_flags,
        sizeof(int),                      &n,    VALUE,
        sizeof(float)*lda*n,  A,        INOUT | LOCALITY,
        sizeof(int),                      &lda,  VALUE,
        sizeof(int),                      &i1,   VALUE,
        sizeof(int),                      &i2,   VALUE,
        sizeof(int)*n,                     ipiv,     INPUT,
        sizeof(int),                      &inc,  VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswp_quark = PCORE_slaswp_quark
#define CORE_slaswp_quark PCORE_slaswp_quark
#endif
void CORE_slaswp_quark(Quark *quark)
{
    int n, lda, i1, i2, inc;
    int *ipiv;
    float *A;

    quark_unpack_args_7(quark, n, A, lda, i1, i2, ipiv, inc);
    LAPACKE_slaswp_work(LAPACK_COL_MAJOR, n, A, lda, i1, i2, ipiv, inc );
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_slaswp_f2(Quark *quark, Quark_Task_Flags *task_flags,
                          int n, float *A, int lda,
                          int i1,  int i2, const int *ipiv, int inc,
                          float *fake1, int szefake1, int flag1,
                          float *fake2, int szefake2, int flag2)
{
    DAG_CORE_LASWP;
    QUARK_Insert_Task(
        quark, CORE_slaswp_f2_quark, task_flags,
        sizeof(int),                        &n,     VALUE,
        sizeof(float)*lda*n,    A,         INOUT | LOCALITY,
        sizeof(int),                        &lda,   VALUE,
        sizeof(int),                        &i1,    VALUE,
        sizeof(int),                        &i2,    VALUE,
        sizeof(int)*n,                       ipiv,      INPUT,
        sizeof(int),                        &inc,   VALUE,
        sizeof(float)*szefake1, fake1,     flag1,
        sizeof(float)*szefake2, fake2,     flag2,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswp_f2_quark = PCORE_slaswp_f2_quark
#define CORE_slaswp_f2_quark PCORE_slaswp_f2_quark
#endif
void CORE_slaswp_f2_quark(Quark* quark)
{
    int n, lda, i1, i2, inc;
    int *ipiv;
    float *A;
    void *fake1, *fake2;

    quark_unpack_args_9(quark, n, A, lda, i1, i2, ipiv, inc, fake1, fake2);
    LAPACKE_slaswp_work(LAPACK_COL_MAJOR, n, A, lda, i1, i2, ipiv, inc );
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_slaswp_ontile(Quark *quark, Quark_Task_Flags *task_flags,
                              PLASMA_desc descA, float *Aij,
                              int i1,  int i2, const int *ipiv, int inc, float *fakepanel)
{
    DAG_CORE_LASWP;
    if (fakepanel == Aij) {
        QUARK_Insert_Task(
            quark, CORE_slaswp_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(float)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(float)*1,      fakepanel,     SCRATCH,
            0);
    } else {
        QUARK_Insert_Task(
            quark, CORE_slaswp_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(float)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(float)*1,      fakepanel,     INOUT,
            0);
    }
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswp_ontile_quark = PCORE_slaswp_ontile_quark
#define CORE_slaswp_ontile_quark PCORE_slaswp_ontile_quark
#endif
void CORE_slaswp_ontile_quark(Quark *quark)
{
    int i1, i2, inc;
    int *ipiv;
    float *A, *fake;
    PLASMA_desc descA;

    quark_unpack_args_7(quark, descA, A, i1, i2, ipiv, inc, fake);
    CORE_slaswp_ontile(descA, i1, i2, ipiv, inc);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_slaswp_ontile_f2(Quark *quark, Quark_Task_Flags *task_flags,
                                 PLASMA_desc descA, float *Aij,
                                 int i1,  int i2, const int *ipiv, int inc,
                                 float *fake1, int szefake1, int flag1,
                                 float *fake2, int szefake2, int flag2)
{
    DAG_CORE_LASWP;
    QUARK_Insert_Task(
        quark, CORE_slaswp_ontile_f2_quark, task_flags,
        sizeof(PLASMA_desc),                &descA, VALUE,
        sizeof(float)*1,        Aij,       INOUT | LOCALITY,
        sizeof(int),                        &i1,    VALUE,
        sizeof(int),                        &i2,    VALUE,
        sizeof(int)*(i2-i1+1)*abs(inc),      ipiv,      INPUT,
        sizeof(int),                        &inc,   VALUE,
        sizeof(float)*szefake1, fake1, flag1,
        sizeof(float)*szefake2, fake2, flag2,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswp_ontile_f2_quark = PCORE_slaswp_ontile_f2_quark
#define CORE_slaswp_ontile_f2_quark PCORE_slaswp_ontile_f2_quark
#endif
void CORE_slaswp_ontile_f2_quark(Quark *quark)
{
    int i1, i2, inc;
    int *ipiv;
    float *A;
    PLASMA_desc descA;
    void *fake1, *fake2;

    quark_unpack_args_8(quark, descA, A, i1, i2, ipiv, inc, fake1, fake2);
    CORE_slaswp_ontile(descA, i1, i2, ipiv, inc);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_sswptr_ontile(Quark *quark, Quark_Task_Flags *task_flags,
                              PLASMA_desc descA, float *Aij,
                              int i1,  int i2, const int *ipiv, int inc,
                              const float *Akk, int ldak)
{
    DAG_CORE_TRSM;
    QUARK_Insert_Task(
        quark, CORE_sswptr_ontile_quark, task_flags,
        sizeof(PLASMA_desc),              &descA, VALUE,
        sizeof(float)*1,      Aij,       INOUT | LOCALITY,
        sizeof(int),                      &i1,    VALUE,
        sizeof(int),                      &i2,    VALUE,
        sizeof(int)*(i2-i1+1)*abs(inc),    ipiv,      INPUT,
        sizeof(int),                      &inc,   VALUE,
        sizeof(float)*ldak,   Akk,       INPUT,
        sizeof(int),                      &ldak,  VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_sswptr_ontile_quark = PCORE_sswptr_ontile_quark
#define CORE_sswptr_ontile_quark PCORE_sswptr_ontile_quark
#endif
void CORE_sswptr_ontile_quark(Quark *quark)
{
    int i1, i2, inc, ldak;
    int *ipiv;
    float *A, *Akk;
    PLASMA_desc descA;

    quark_unpack_args_8(quark, descA, A, i1, i2, ipiv, inc, Akk, ldak);
    CORE_sswptr_ontile(descA, i1, i2, ipiv, inc, Akk, ldak);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_slaswpc_ontile(Quark *quark, Quark_Task_Flags *task_flags,
                              PLASMA_desc descA, float *Aij,
                              int i1,  int i2, const int *ipiv, int inc, float *fakepanel)
{
    DAG_CORE_LASWP;
    if (fakepanel == Aij) {
        QUARK_Insert_Task(
            quark, CORE_slaswpc_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(float)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(float)*1,      fakepanel,     SCRATCH,
            0);
    } else {
        QUARK_Insert_Task(
            quark, CORE_slaswpc_ontile_quark, task_flags,
            sizeof(PLASMA_desc),              &descA,     VALUE,
            sizeof(float)*1,      Aij,           INOUT | LOCALITY,
            sizeof(int),                      &i1,        VALUE,
            sizeof(int),                      &i2,        VALUE,
            sizeof(int)*(i2-i1+1)*abs(inc),   ipiv,           INPUT,
            sizeof(int),                      &inc,       VALUE,
            sizeof(float)*1,      fakepanel,     INOUT,
            0);
    }
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_slaswpc_ontile_quark = PCORE_slaswpc_ontile_quark
#define CORE_slaswpc_ontile_quark PCORE_slaswpc_ontile_quark
#endif
void CORE_slaswpc_ontile_quark(Quark *quark)
{
    int i1, i2, inc;
    int *ipiv;
    float *A, *fake;
    PLASMA_desc descA;

    quark_unpack_args_7(quark, descA, A, i1, i2, ipiv, inc, fake);
    CORE_slaswpc_ontile(descA, i1, i2, ipiv, inc);
}


/* histogram/test2d_trap.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_histogram2d.h>
#include <gsl/gsl_test.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_ieee_utils.h>

#define N 107
#define M 239

static void my_error_handler (const char *reason, const char *file,
                              int line, int err);
static int status = 0;

void
test2d_trap (void)
{
  gsl_histogram2d *h;
  double result, lower, upper;
  size_t i, j;

  gsl_set_error_handler (&my_error_handler);

  gsl_ieee_env_setup ();

  status = 0;
  h = gsl_histogram2d_calloc (0, 10);
  gsl_test (!status, "gsl_histogram_calloc traps zero-width histogram");
  gsl_test (h != 0,
            "gsl_histogram2d_calloc returns NULL for zero-width histogram");

  status = 0;
  h = gsl_histogram2d_calloc (10, 0);
  gsl_test (!status, "gsl_histogram_calloc traps zero-length histogram");
  gsl_test (h != 0,
            "gsl_histogram2d_calloc returns NULL for zero-length histogram");

  status = 0;
  h = gsl_histogram2d_calloc_uniform (0, 10, 0.0, 1.0, 0.0, 1.0);
  gsl_test (!status,
            "gsl_histogram2d_calloc_uniform traps zero-width histogram");
  gsl_test (h != 0,
    "gsl_histogram2d_calloc_uniform returns NULL for zero-width histogram");

  status = 0;
  h = gsl_histogram2d_calloc_uniform (10, 0, 0.0, 1.0, 0.0, 1.0);
  gsl_test (!status,
            "gsl_histogram2d_calloc_uniform traps zero-length histogram");
  gsl_test (h != 0,
   "gsl_histogram2d_calloc_uniform returns NULL for zero-length histogram");

  status = 0;
  h = gsl_histogram2d_calloc_uniform (10, 10, 0.0, 1.0, 1.0, 1.0);
  gsl_test (!status,
            "gsl_histogram2d_calloc_uniform traps equal endpoints");
  gsl_test (h != 0,
         "gsl_histogram2d_calloc_uniform returns NULL for equal endpoints");

  status = 0;
  h = gsl_histogram2d_calloc_uniform (10, 10, 1.0, 1.0, 0.0, 1.0);
  gsl_test (!status,
            "gsl_histogram2d_calloc_uniform traps equal endpoints");
  gsl_test (h != 0,
         "gsl_histogram2d_calloc_uniform returns NULL for equal endpoints");

  status = 0;
  h = gsl_histogram2d_calloc_uniform (10, 10, 0.0, 1.0, 2.0, 1.0);
  gsl_test (!status,
            "gsl_histogram2d_calloc_uniform traps invalid range");
  gsl_test (h != 0,
            "gsl_histogram2d_calloc_uniform returns NULL for invalid range");

  status = 0;
  h = gsl_histogram2d_calloc_uniform (10, 10, 2.0, 1.0, 0.0, 1.0);
  gsl_test (!status,
            "gsl_histogram2d_calloc_uniform traps invalid range");
  gsl_test (h != 0,
            "gsl_histogram2d_calloc_uniform returns NULL for invalid range");

  h = gsl_histogram2d_calloc_uniform (N, M, 0.0, 1.0, 0.0, 1.0);

  status = gsl_histogram2d_accumulate (h, 1.0, 0.0, 10.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_accumulate traps x at xmax");

  status = gsl_histogram2d_accumulate (h, 2.0, 0.0, 100.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_accumulate traps x above xmax");

  status = gsl_histogram2d_accumulate (h, -1.0, 0.0, 1000.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_accumulate traps x below xmin");

  status = gsl_histogram2d_accumulate (h, 0.0, 1.0, 10.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_accumulate traps y at ymax");

  status = gsl_histogram2d_accumulate (h, 0.0, 2.0, 100.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_accumulate traps y above ymax");

  status = gsl_histogram2d_accumulate (h, 0.0, -1.0, 1000.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_accumulate traps y below ymin");

  status = gsl_histogram2d_increment (h, 1.0, 0.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_increment traps x at xmax");

  status = gsl_histogram2d_increment (h, 2.0, 0.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_increment traps x above xmax");

  status = gsl_histogram2d_increment (h, -1.0, 0.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_increment traps x below xmin");

  status = gsl_histogram2d_increment (h, 0.0, 1.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_increment traps y at ymax");

  status = gsl_histogram2d_increment (h, 0.0, 2.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_increment traps y above ymax");

  status = gsl_histogram2d_increment (h, 0.0, -1.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_increment traps y below ymin");

  result = gsl_histogram2d_get (h, N, 0);
  gsl_test (result != 0, "gsl_histogram2d_get traps x index at nx");

  result = gsl_histogram2d_get (h, N + 1, 0);
  gsl_test (result != 0, "gsl_histogram2d_get traps x index above nx");

  result = gsl_histogram2d_get (h, 0, M);
  gsl_test (result != 0, "gsl_histogram2d_get traps y index at ny");

  result = gsl_histogram2d_get (h, 0, M + 1);
  gsl_test (result != 0, "gsl_histogram2d_get traps y index above ny");

  status = gsl_histogram2d_get_xrange (h, N, &lower, &upper);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_get_xrange traps index at nx");

  status = gsl_histogram2d_get_xrange (h, N + 1, &lower, &upper);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_get_xrange traps index above nx");

  status = gsl_histogram2d_get_yrange (h, M, &lower, &upper);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_get_yrange traps index at ny");

  status = gsl_histogram2d_get_yrange (h, M + 1, &lower, &upper);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_get_yrange traps index above ny");

  status = 0;
  gsl_histogram2d_find (h, -0.01, 0.0, &i, &j);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_find traps x below xmin");

  status = 0;
  gsl_histogram2d_find (h, 1.0, 0.0, &i, &j);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_find traps x at xmax");

  status = 0;
  gsl_histogram2d_find (h, 1.1, 0.0, &i, &j);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_find traps x above xmax");

  status = 0;
  gsl_histogram2d_find (h, 0.0, -0.01, &i, &j);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_find traps y below ymin");

  status = 0;
  gsl_histogram2d_find (h, 0.0, 1.0, &i, &j);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_find traps y at ymax");

  status = 0;
  gsl_histogram2d_find (h, 0.0, 1.1, &i, &j);
  gsl_test (status != GSL_EDOM, "gsl_histogram2d_find traps y above ymax");

  gsl_histogram2d_free (h);
}

static void
my_error_handler (const char *reason, const char *file, int line, int err)
{
  if (0)
    printf ("(caught [%s:%d: %s (%d)])\n", file, line, reason, err);
  status = 1;
}


/* #define _XOPEN_SOURCE 500 */
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <inttypes.h>
#include <limits.h>

#ifndef SIZE_MAX
#define SIZE_MAX (~(size_t)0)
#endif

#ifdef _OPENMP
#include <omp.h>
#endif

/* for open/close and other file io*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/*for sendfile (in-kernel file to file copy, supposedly very fast) */
#ifdef USE_SENDFILE
#include <sys/sendfile.h>
#endif

#if defined(USE_MMAP)
#include <sys/mman.h>
#elif defined(USE_SENDFILE)
#include <sys/sendfile.h>
#else
#endif

#ifdef USE_WRITEV
#ifndef USE_MMAP
#error USE_MMAP must be enabled with USE_WRITEV
#endif
#include <sys/uio.h>
#endif

#include <gsl/gsl_rng.h>

#include "macros.h"
#include "utils.h"
#include "progressbar.h"
#include "gadget_utils.h"

/* Copied straight from https://fossies.org/dox/gsl-2.2.1/shuffle_8c_source.html*/
/* Adapted to generate array indices. The returned random indices are in increasing order */
int gsl_ran_arr_index (const gsl_rng * r, size_t * dest, const size_t k, const size_t n)
{
  /* Choose k out of n items, return an array x[] of the k items.
      These items will preserve the relative order of the original
      input -- you can use shuffle() to randomize the output if you
      wish */

  if (k > n) {
	fprintf(stderr,"k=%zu is greater than n=%zu. Cannot sample more than n items\n",
			k, n);
	return EXIT_FAILURE;
  }
  if(k == n) {
	for(size_t i=0;i<n;i++) {
	  dest[i] = i;
	}
  } else {
	size_t j=0;
	for (size_t i = 0; i < n && j < k; i++) {
	  if ((n - i) * gsl_rng_uniform (r) < k - j) {
		dest[j] = i;
		j++ ;
	  }
	}
  }

  return EXIT_SUCCESS;
}

int write_random_subsample_of_field(int in_fd, int out_fd, off_t in_offset, off_t out_offset, const int dest_npart, const size_t itemsize, size_t *random_indices
#ifdef USE_MMAP
									,char *in_memblock
#endif
)
{
#ifdef USE_MMAP
  in_memblock += in_offset;//These are here so that I don't lose the original pointer from mmap
#endif

#ifndef USE_MMAP
  size_t buf[itemsize];
#endif

  int interrupted=0;

#ifndef _OPENMP
  init_my_progressbar(dest_npart,&interrupted);
#endif

#ifdef USE_WRITEV
  //vector writes. Loop has to be re-written in units of IOV_MAX
  for(int i=0;i<dest_npart;i+=IOV_MAX) {
#ifndef _OPENMP
      my_progressbar(i,&interrupted);//progress-bar will be jumpy
#endif
      const int nleft = ((dest_npart - i) > IOV_MAX) ? IOV_MAX:(dest_npart - i);
      struct iovec iov[IOV_MAX];
      for(int j=0;j<nleft;j++){
          const size_t ind = random_indices[j];
          const size_t offset_in_field = ind * itemsize;
          iov[j].iov_base = in_memblock + offset_in_field;
          iov[j].iov_len = itemsize;
      }
      random_indices += nleft;

      ssize_t bytes_written = writev(out_fd, iov, nleft);
      XRETURN(bytes_written == nleft*itemsize, EXIT_FAILURE, "Expected to write bytes = %zu but wrote %zd instead\n",nleft*itemsize, bytes_written);
  }
#else  //Not using WRITEV -> 3 options here i) MMAP + write ii) sendfile iii) pread + write
  for(int i=0;i<dest_npart;i++) {
#ifndef _OPENMP
	my_progressbar(i,&interrupted);
#endif
	const size_t ind = random_indices[i];
	size_t offset_in_field = ind * itemsize;
#if defined(USE_MMAP)
	ssize_t bytes_written = write(out_fd, in_memblock + offset_in_field, itemsize);
#elif defined(USE_SENDFILE)
	off_t input_offset = in_offset + offset_in_field;
	ssize_t bytes_written = sendfile(out_fd, in_fd, &input_offset, itemsize);
#else
	ssize_t bytes_read = pread(in_fd, &buf, itemsize, in_offset + offset_in_field);
	XRETURN(bytes_read == itemsize, EXIT_FAILURE, "Expected to read bytes = %zu but read %zd instead\n",itemsize, bytes_read);
	ssize_t bytes_written = write(out_fd, &buf, itemsize);
#endif//default case (pread + write)

	XRETURN(bytes_written == itemsize, EXIT_FAILURE, "Expected to write bytes = %zu but wrote %zd instead\n",itemsize, bytes_written);
	out_offset += itemsize;
  }
#endif //end of WRITEV

#ifndef _OPENMP
  finish_myprogressbar(&interrupted);
#endif

  return EXIT_SUCCESS;
}

int subsample_single_gadgetfile(const int dest_npart, const char *inputfile, const char *outputfile, const size_t id_bytes, const gsl_rng *rng, const double fraction, const int64_t nparttotal)
{
  if(dest_npart <= 0) {
	fprintf(stderr,"Error: Desired number of particles =%d in the subsampled file must be > 0\n",dest_npart);
	return EXIT_FAILURE;
  }
  struct timespec tstart, t0, t1;
  current_utc_time(&tstart);

  int in_fd=-1;
  int out_fd=-1;
  int status = EXIT_FAILURE;
  in_fd = open(inputfile, O_RDONLY);
  if(in_fd < 0) {
	fprintf(stderr,"Error (in function %s, line # %d) while opening input file = `%s'\n",__FUNCTION__,__LINE__,inputfile);
	perror(NULL);
	return EXIT_FAILURE;
  }

#ifdef USE_MMAP
  struct stat sb;
  if(fstat(in_fd, &sb) < 0) {
	perror(NULL);
	return EXIT_FAILURE;
  }
  char *in_memblock = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in_fd, 0);
#endif

  //Check that that the output file does not exist.
  {
	FILE *fp = fopen(outputfile,"r");
	if(fp != NULL) {
	  fclose(fp);
	  fprintf(stderr,"Warning: Output file = `%s' should not exist. "
			  "Aborting so as to avoid accidentally over-writing regular fles\n",outputfile);
	  return EXIT_FAILURE;
	}
  }

  out_fd = open(outputfile, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);//set the mode since the file is being created
  if(out_fd < 0) {
	fprintf(stderr,"Error (in function %s, line # %d) while opening output file = `%s'\n",__FUNCTION__,__LINE__, outputfile);
	perror(NULL);
	return EXIT_FAILURE;
  }

  /* Reserve disk-space for dest_npart particles, written as a fortran binary */
  const size_t pos_vel_itemsize = sizeof(float)*3;
  const off_t header_disk_size = 4 + sizeof(struct io_header) + 4;  //header
  const off_t pos_disk_size = 4 + pos_vel_itemsize*dest_npart + 4; //all 3 positions for each particle
  const off_t vel_disk_size = 4 + pos_vel_itemsize*dest_npart + 4; //all 3 velocities for each particle
  const off_t id_disk_size = 	4 + id_bytes*dest_npart + 4;//all particle IDs

  /* These are all of the fields to be written */
  const off_t outputfile_size = header_disk_size + pos_disk_size + vel_disk_size + id_disk_size;

  status = posix_fallocate(out_fd, 0, outputfile_size);
  if(status < 0) {
  	fprintf(stderr,"Error: Could not reserve disk space for %d particles (output file: `%s', expected file-size on disk = %zu bytes)\n",
  			dest_npart, outputfile, (size_t) outputfile_size);
  	perror(NULL);
  	return status;
  }

  struct io_header hdr;
  int dummy1=0,dummy2=0;
  const off_t pos_start_offset = header_disk_size + 4;
  const off_t vel_start_offset = header_disk_size + pos_disk_size + 4;
  const off_t id_start_offset = header_disk_size + pos_disk_size + vel_disk_size + 4;

  read(in_fd, &dummy1, 4);
  read(in_fd, &hdr, sizeof(struct io_header));
  read(in_fd, &dummy2, 4);
  if(dummy1 != 256 || dummy2 != 256) {
	fprintf(stderr,"Error: Padding bytes for header = %d (front) and %d (end) should be exactly 256\n",dummy1, dummy2);
	return EXIT_FAILURE;
  }
#ifdef USE_MMAP
  if(fraction == 1.0) {
	XRETURN(dest_npart == hdr.npart[1],EXIT_FAILURE,
			"for fraction = 1.0, input npart = %d must equal subsampled npart = %d\n",hdr.npart[1], dest_npart);
	/* XRETURN(sb.st_size == outputfile_size, EXIT_FAILURE, */
	/* 		"for fraction = 1.0, input (=%zu bytes) and output file sizes (=%zu bytes) must be the same",sb.st_size, outputfile_size); */
  }
#endif

  for(int type=0;type<6;type++) {
	if(type==1) {
	  continue;
	}

	/* There should only be dark-matter particles */
	if(hdr.npart[type] > 0) {
	  fprintf(stderr,"Error: Input file `%s' contains %d particles of type=%d. This code only works for dark-matter only simulations\n",
			  inputfile, hdr.npart[type], type);
	  return EXIT_FAILURE;
	}
  }

  /* The number of subsampled particles wanted can at most be the numbers present in the file*/
  if(dest_npart > hdr.npart[1]) {
	fprintf(stderr,"Error: Number of subsampled particles = %d exceeds the number of particles in the file = %d\n",
			dest_npart,  hdr.npart[1]);
	return EXIT_FAILURE;
  }

  struct io_header out_hdr = hdr;
  out_hdr.npart[1] = dest_npart;
  out_hdr.npartTotal[1] = nparttotal;
  out_hdr.npartTotalHighWord[1] = (nparttotal >> 32);
  out_hdr.mass[1] /= fraction;

  write(out_fd, &dummy1, sizeof(dummy1));
  write(out_fd, &out_hdr, sizeof(struct io_header));
  write(out_fd, &dummy2, sizeof(dummy2));

  //create an array of random indices
  size_t *random_indices = calloc(dest_npart, sizeof(*random_indices));
  status = gsl_ran_arr_index(rng, random_indices, (size_t) dest_npart, (size_t) hdr.npart[1]);

  //write positions
  current_utc_time(&t0);
  int posvel_disk_size=pos_vel_itemsize*dest_npart;
  write(out_fd, &posvel_disk_size, sizeof(posvel_disk_size));
  status = write_random_subsample_of_field(in_fd, out_fd, pos_start_offset, -1,dest_npart, pos_vel_itemsize, random_indices
#ifdef USE_MMAP
											   ,in_memblock
#endif
											   );
  if(status != EXIT_SUCCESS) {
	return status;
  }
  write(out_fd, &posvel_disk_size, sizeof(posvel_disk_size));
  current_utc_time(&t1);
  double pos_time = REALTIME_ELAPSED_NS(t0,t1)*1e-9;

  //write velocities
  write(out_fd, &posvel_disk_size, sizeof(posvel_disk_size));
  status = write_random_subsample_of_field(in_fd, out_fd, vel_start_offset, -1,dest_npart, pos_vel_itemsize, random_indices
#ifdef USE_MMAP
											   ,in_memblock
#endif
											   );
  if(status != EXIT_SUCCESS) {
	return status;
  }
  write(out_fd, &posvel_disk_size, sizeof(posvel_disk_size));
  current_utc_time(&t0);
  double vel_time = REALTIME_ELAPSED_NS(t1, t0)*1e-9;

  //write ids
  int id_size = id_bytes * dest_npart;
  write(out_fd, &id_size, sizeof(id_size));
  status = write_random_subsample_of_field(in_fd, out_fd, vel_start_offset, -1,dest_npart, id_bytes, random_indices
#ifdef USE_MMAP
                                           ,in_memblock
#endif
                                           );
  if(status != EXIT_SUCCESS) {
	return status;
  }
  write(out_fd, &id_size, sizeof(id_size));
  current_utc_time(&t1);
  double id_time = REALTIME_ELAPSED_NS(t0,t1)*1e-9;

  free(random_indices);

  //close the input file -> we are only reading, unlikely to be error
  close(in_fd);

#ifdef USE_MMAP
  munmap(in_memblock, sb.st_size);
#endif

  //check for error code here since disk quota might be hit
  status = close(out_fd);
  if(status != EXIT_SUCCESS){
	fprintf(stderr,"Error while closing output file = `%s'\n",outputfile);
	perror(NULL);
	return status;
  }
  current_utc_time(&t1);
  /* fprintf(stderr,"Done with file. Total time taken = %8.4lf seconds (pos_time = %6.3e vel_time = %6.3e id_time = %6.3e seconds)\n",REALTIME_ELAPSED_NS(tstart,t1)*1e-9, */
  /*   	  pos_time,vel_time,id_time); */

  return EXIT_SUCCESS;
}

int main(int argc,char **argv)
{
  const char argnames[][100]={"fraction","input file","output file"};
  int nargs=sizeof(argnames)/(sizeof(char)*100);
  char input_filename[MAXLEN];
  char output_filename[MAXLEN];
  struct timespec tstart,t0,t1;
  int64_t nparticles_written=0,nparticles_withmass=0;
  const gsl_rng_type * rng_type = gsl_rng_ranlxd1;
  gsl_rng * all_procs_rng = gsl_rng_alloc(rng_type);
  unsigned long seed = 42;
  int64_t TotNumPart;
  current_utc_time(&tstart);

  if (argc != 4 )  {
	fprintf(stderr,"ERROR: %s usage - <fraction>  <gadget snapshot name>  <output filename>\n",argv[0]);
	fprintf(stderr,"Each file will be subsampled to get (roughly) that fraction for each particle-type\n");
    fprintf(stderr,"\nFound: %d parameters\n ",argc-1);
	int i;
    for(i=1;i<argc;i++) {
      if(i <= nargs)
		fprintf(stderr,"\t\t %s = `%s' \n",argnames[i-1],argv[i]);
      else
		fprintf(stderr,"\t\t <> = `%s' \n",argv[i]);
    }
    if(i <= nargs) {
      fprintf(stderr,"\nMissing required parameters: \n");
      for(i=argc;i<=nargs;i++)
		fprintf(stderr,"\t\t %20s = `?'\n",argnames[i-1]);
    }
	fprintf(stderr,"\n\n");
    exit(EXIT_FAILURE);
  }

  double fraction = atof(argv[1]);
  XRETURN(fraction > 0 && fraction <= 1.0, EXIT_FAILURE, "Subsample fraction = %lf needs to be in (0,1]", fraction);
  strncpy(input_filename,argv[2],MAXLEN);
  strncpy(output_filename,argv[3],MAXLEN);
  XRETURN(strncmp(input_filename,output_filename,MAXLEN) != 0, EXIT_FAILURE, "Input filename = `%s' and output filename = `%s' are the same",input_filename, output_filename);
  const int nfiles = get_gadget_nfiles(input_filename);
  struct io_header header = get_gadget_header(input_filename);
  TotNumPart = get_Numpart(&header);
  XRETURN(header.npartTotal[0] == 0 && header.npartTotalHighWord[0]  == 0, EXIT_FAILURE, "Subsampling will not work with gas particles");

  fprintf(stderr,"Running `%s' on %d files with the following parameters \n",argv[0],nfiles);
  fprintf(stderr,"\n\t\t ---------------------------------------------\n");
  for(int i=1;i<=nargs;i++) {
	fprintf(stderr,"\t\t %-25s = %s \n",argnames[i-1],argv[i]);
  }
#ifdef _OPENMP
#pragma omp parallel
  {
      int nthreads = omp_get_num_threads();
      int tid = omp_get_thread_num();
      if(tid == 0)
          fprintf(stderr,"\t\t %-25s = %d \n","nthreads", nthreads);
  }
#endif
  fprintf(stderr,"\t\t ---------------------------------------------\n\n");

  if(SIZE_MAX != 0xFFFFFFFFFFFFFFFF) {
      fprintf(stderr,"Error: SIZE_MAX=%zu should be (2^64-1)\n", SIZE_MAX);
      return EXIT_FAILURE;
  }

  size_t id_bytes;
  int interrupted=0;
  size_t seedtable[nfiles];
  int64_t nparttotal=0;
  fprintf(stderr,"Checking all input files ...\n");
  init_my_progressbar(nfiles, &interrupted);
  for(int ifile=0;ifile<nfiles;ifile++) {
      seedtable[ifile] = SIZE_MAX * gsl_rng_uniform(all_procs_rng);
      char inputfile[MAXLEN];
      my_snprintf(inputfile,MAXLEN,"%s.%d",input_filename, ifile);
      if(ifile == 0) {
          id_bytes = get_gadget_id_bytes(inputfile);
          fprintf(stderr,"Gadget ID bytes = %zu\n",id_bytes);
          interrupted=1;
      }
      struct io_header hdr = get_gadget_header(inputfile);
      const int dest_npart = fraction * hdr.npart[1];
      XRETURN((int) (dest_npart*3*sizeof(float))  < INT_MAX, EXIT_FAILURE,
              "Padding bytes will overflow, please reduce the value of fraction (currently, fraction = %lf)\n",fraction);
      my_progressbar(ifile,&interrupted);
      nparttotal += dest_npart;
  }
  finish_myprogressbar(&interrupted);
  fprintf(stderr,"Checking all input files .....done\n\n");

  int numdone=0, errorflag=0, savestatus=0;

  init_my_progressbar(nfiles, &interrupted);
#ifdef _OPENMP
#pragma omp parallel shared(numdone, savestatus)
  {
      int nthreads = omp_get_num_threads();
      int tid = omp_get_thread_num();
#pragma omp for schedule(dynamic)
#endif
      for(int ifile=0;ifile<nfiles;ifile++) {
          if(errorflag == 0) {
              //Setup the rng
              gsl_rng * rng = gsl_rng_alloc(rng_type);
              gsl_rng_set(rng, seedtable[ifile]);

#ifdef _OPENMP
#pragma omp atomic
              numdone++;

              if(tid == 0)
#endif
                  my_progressbar(numdone,&interrupted);

              char inputfile[MAXLEN],outputfile[MAXLEN];
              my_snprintf(inputfile,MAXLEN,"%s.%d",input_filename,ifile);
              my_snprintf(outputfile, MAXLEN,"%s.%d",output_filename,ifile);
              struct io_header hdr = get_gadget_header(inputfile);
              const int dest_npart = fraction * hdr.npart[1];
              int status = subsample_single_gadgetfile(dest_npart, inputfile, outputfile, id_bytes, rng, fraction, nparttotal);
              if(status != EXIT_SUCCESS) {
                  savestatus = status;
                  errorflag = 1;
              }
              gsl_rng_free(rng);

              /* #ifdef _OPENMP           */
              /* #pragma omp critical(info) */
              /*               { */
              /* #endif */
              /*                   fprintf(stderr,"Wrote %d subsampled particles out of %d particles (%d/%d files).\n", */
              /*                           dest_npart, hdr.npart[1], ifile+1,nfiles); */

              /*                   interrupted=1; */
              /* #ifdef _OPENMP */
              /*               }//critical region */
              /* #endif */

          }//errorflag == 0 condition
      }//for loop over nfiles

#ifdef _OPENMP
  }//omp parallel region
#endif

  gsl_rng_free(all_procs_rng);
  finish_myprogressbar(&interrupted);

  if(errorflag != 0) {
      return savestatus;
  }

  current_utc_time(&t1);
  fprintf(stderr,"subsample_Gadget> Done. Wrote %"PRId64" particles to file `%s'. Time taken = %6.2lf mins\n",
		  nparttotal,output_filename,REALTIME_ELAPSED_NS(tstart, t1)*1e-9/60.0);

  return EXIT_SUCCESS;
}


/*
GENETIC - A simple genetic algorithm.

Copyright 2014, Javier Burguete Tolosa.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
	list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
	this list of conditions and the following disclaimer in the documentation
	and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY Javier Burguete Tolosa ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL Javier Burguete Tolosa OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
 * \file entity.c
 * \brief Source file to define the entity functions.
 * \author Javier Burguete Tolosa.
 * \copyright Copyright 2014 Javier Burguete Tolosa. All rights reserved.
 */
#define _GNU_SOURCE
#include <gsl/gsl_rng.h>
#include <glib.h>
#include "entity.h"

/**
 * Function to create an entity.
 */
void
entity_new (Entity * entity,    ///< Entity struct.
            unsigned int genome_nbytes,
            ///< Number of bytes of the entity genome.
            unsigned int id)    ///< Identifier number.
{
  entity->id = id;
  // Aligning in 4 bytes
  entity->nbytes = ((genome_nbytes + 3) / 4) * 4;
  entity->genome = (char *) g_slice_alloc (entity->nbytes);
}

/**
 * Function to init randomly the genome of an entity.
 */
void
entity_init (Entity * entity,   ///< Entity struct.
             gsl_rng * rng)     ///< GSL random numbers generator.
{
  unsigned int i;
  for (i = 0; i < entity->nbytes; ++i)
    entity->genome[i] = (char) gsl_rng_uniform_int (rng, 256);
}

/**
 * Function to free the memory used by an entity.
 */
void
entity_free (Entity * entity)   ///< Entity struct.
{
  g_slice_free1 (entity->nbytes, entity->genome);
}


/**
 *
 * @file qwrapper_dlacpy.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Julien Langou
 * @author Henricus Bouwmeester
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated d Tue Jan  7 11:44:56 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_dlacpy(Quark *quark, Quark_Task_Flags *task_flags,
                       PLASMA_enum uplo, int m, int n, int nb,
                       const double *A, int lda,
                       double *B, int ldb)
{
    DAG_CORE_LACPY;
    QUARK_Insert_Task(quark, CORE_dlacpy_quark, task_flags,
        sizeof(PLASMA_enum),                &uplo,  VALUE,
        sizeof(int),                        &m,     VALUE,
        sizeof(int),                        &n,     VALUE,
        sizeof(double)*nb*nb,    A,             INPUT,
        sizeof(int),                        &lda,   VALUE,
        sizeof(double)*nb*nb,    B,             OUTPUT,
        sizeof(int),                        &ldb,   VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_dlacpy_quark = PCORE_dlacpy_quark
#define CORE_dlacpy_quark PCORE_dlacpy_quark
#endif
void CORE_dlacpy_quark(Quark *quark)
{
    PLASMA_enum uplo;
    int M;
    int N;
    const double *A;
    int LDA;
    double *B;
    int LDB;

    quark_unpack_args_7(quark, uplo, M, N, A, LDA, B, LDB);
    LAPACKE_dlacpy_work(
        LAPACK_COL_MAJOR,
        lapack_const(uplo),
        M, N, A, LDA, B, LDB);
}

/***************************************************************************//**
 *
 **/
void QUARK_CORE_dlacpy_f1(Quark *quark, Quark_Task_Flags *task_flags,
                          PLASMA_enum uplo, int m, int n, int nb,
                          const double *A, int lda,
                          double *B, int ldb,
                          double *fake1, int szefake1, int flag1)
{
    DAG_CORE_LACPY;
    if ( fake1 == B ) {
        QUARK_Insert_Task(quark, CORE_dlacpy_quark, task_flags,
            sizeof(PLASMA_enum),                &uplo,  VALUE,
            sizeof(int),                        &m,     VALUE,
            sizeof(int),                        &n,     VALUE,
            sizeof(double)*nb*nb,    A,             INPUT,
            sizeof(int),                        &lda,   VALUE,
            sizeof(double)*nb*nb,    B,             OUTPUT | flag1,
            sizeof(int),                        &ldb,   VALUE,
            0);
    }
    else {
        QUARK_Insert_Task(quark, CORE_dlacpy_f1_quark, task_flags,
            sizeof(PLASMA_enum),                &uplo,  VALUE,
            sizeof(int),                        &m,     VALUE,
            sizeof(int),                        &n,     VALUE,
            sizeof(double)*nb*nb,    A,             INPUT,
            sizeof(int),                        &lda,   VALUE,
            sizeof(double)*nb*nb,    B,             OUTPUT,
            sizeof(int),                        &ldb,   VALUE,
            sizeof(double)*szefake1, fake1,         flag1,
            0);
    }
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_dlacpy_f1_quark = PCORE_dlacpy_f1_quark
#define CORE_dlacpy_f1_quark PCORE_dlacpy_f1_quark
#endif
void CORE_dlacpy_f1_quark(Quark *quark)
{
    PLASMA_enum uplo;
    int M;
    int N;
    const double *A;
    int LDA;
    double *B;
    int LDB;
    void *fake1;

    quark_unpack_args_8(quark, uplo, M, N, A, LDA, B, LDB, fake1);
    LAPACKE_dlacpy_work(
        LAPACK_COL_MAJOR,
        lapack_const(uplo),
        M, N, A, LDA, B, LDB);
}