// ---------------------------------------------------------------------------
// - Rsparse.cpp                                                             -
// - afnix:mth module - real sparse object implementation                    -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2011 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Vector.hpp"
#include "Rsparse.hpp"
#include "Runnable.hpp"
#include "Exception.hpp"
 
namespace afnix {

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a default sparse object

  Rsparse::Rsparse (void) {
    d_slen = 0LL;
    p_sidx = nilp;
    p_sval = nilp;
  }

  // create a sparse object by size

  Rsparse::Rsparse (const t_long size) : Rsi (size) {
    d_slen = 0LL;
    p_sidx = nilp;
    p_sval = nilp;
  }

  // create a sparse object by size

  Rsparse::Rsparse (const t_long size, const bool fflg) : Rsi (size) {
    if (fflg == true) {
      d_slen = size;
      p_sidx = nilp;
      p_sval = new t_real[d_size];
    } else {
      d_slen = 0LL;
      p_sidx = nilp;
      p_sval = nilp;
    }
  }

  // copy construct this sparse object

  Rsparse::Rsparse (const Rsparse& that) {
    that.rdlock ();
    try {
      d_size = that.d_size;
      d_slen = that.d_slen;
      p_sidx = (that.p_sidx == nilp) ? nilp : new t_long[d_slen];
      p_sval = new t_real[d_slen];
      for (t_long i = 0; i < d_slen; i++) {
	p_sidx[i] = that.p_sidx[i];
	p_sval[i] = that.p_sval[i];
      }
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // destroy this sparse object

  Rsparse::~Rsparse (void) {
    delete [] p_sidx;
    delete [] p_sval;
  }

  // return the class name

  String Rsparse::repr (void) const {
    return "Rsparse";
  }

  // return a clone of this object

  Object* Rsparse::clone (void) const {
    return new Rsparse (*this);
  }

  // assign a sparse object to this one
  
  Rsparse& Rsparse::operator = (const Rsparse& that) {
    // check for self-assignation
    if (this == &that) return *this;
    // lock and assign
    wrlock ();
    that.rdlock ();
    try {
      // clean the old object
      delete [] p_sidx; p_sidx = nilp;
      delete [] p_sval; p_sval = nilp;
      // copy the new object
      d_size = that.d_size;
      d_slen = that.d_slen;
      p_sidx = (that.p_sidx == nilp) ? nilp : new t_long[d_slen];
      p_sval = new t_real[d_slen];
      for (t_long i = 0; i < d_slen; i++) {
	p_sidx[i] = that.p_sidx[i];
	p_sval[i] = that.p_sval[i];
      }
      // unlock and return
      unlock ();
      that.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      that.unlock ();
      throw;
    }
  }

  // get the real sparse object length

  t_long Rsparse::length (void) const {
    rdlock ();
    try {
      t_long result = d_slen;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a real sparse object index by position

  t_long Rsparse::getidx (const t_long pos) const {
    rdlock ();
    try {
      // check the position
      if ((pos < 0LL) || (pos >= d_slen)) {
	throw Exception ("position-error", "invalid sparse object position");
      }
      t_long result = (p_sidx == nilp) ? pos : p_sidx[pos];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a real sparse object value by position

  t_real Rsparse::getval (const t_long pos) const {
    rdlock ();
    try {
      // check the position
      if ((pos < 0LL) || (pos >= d_slen)) {
	throw Exception ("position-error", "invalid sparse object position");
      }
      t_real result = p_sval[pos];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // set a sparse object by index and value

  t_long Rsparse::set (const t_long sidx, const t_real sval) {
    wrlock ();
    try {
      // check if the index is valid
      if ((sidx < 0LL) || (sidx >= d_size)) {
	throw Exception ("sparse-error", "invalid index in set");
      }
      // check for null object
      if (d_slen == 0) {
	// allocate the sparse structure
	d_slen = 1LL;
	p_sidx = new t_long[d_slen];
	p_sval = new t_real[d_slen];
	// set the unique value
	p_sidx[0] = sidx;
	p_sval[0] = sval;
	// unlock and return
	unlock ();
	return 0;
      }
      // allocate the result position
      t_long result = -1;
      // set in full mode or not
      if (p_sidx != nilp) {
	for (t_long i = 0; i < d_slen; i++) {
	  if (p_sidx[i] == sidx) {
	    result = i;
	    break;
	  }
	}
      } else {
	result = sidx;
      }
      // check for valid position for adding
      if ((result == -1) && (sval != 0.0)) {
	t_long* pidx = new t_long[d_slen+1LL];
	t_real* pval = new t_real[d_slen+1LL];
	for (t_long i = 0LL; i < d_slen; i++) {
	  pidx[i] = p_sidx[i];
	  pval[i] = p_sval[i];
	}
	pidx[d_slen] = sidx;
	pval[d_slen] = sval;
	// delete old value and update
	delete [] p_sidx;
	delete [] p_sval;
	p_sidx = pidx;
	p_sval = pval;
	t_long result = d_slen++;
	unlock ();
	return result;
      }
      // set the sparse value and return
      p_sval[result] = sval;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

  // create a new object in a generic way

  Object* Rsparse::mknew (Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    
    // check for 0 argument
    if (argc == 0) return new Rsparse;
    // check for 1 argument
    if (argc == 1) {
      t_long size = argv->getlong (0);
      return new Rsparse (size);
    }
    // check for 2 arguments
    if (argc == 2) {
      t_long size = argv->getlong (0);
      bool   fflg = argv->getbool (1);
      return new Rsparse (size, fflg);
    }
    // invalid arguments
    throw Exception ("argument-error", 
		     "invalid arguments with real sparse object");
  }
}
