// ---------------------------------------------------------------------------
// - Rvector.cpp                                                             -
// - afnix:mth module - real vector 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 "Real.hpp"
#include "Vector.hpp"
#include "Rvector.hpp"
#include "Algebra.hpp"
#include "Utility.hpp"
#include "Exception.hpp"
 
namespace afnix {

  // -------------------------------------------------------------------------
  // - public section                                                        -
  // -------------------------------------------------------------------------

  // generate a random vector by size

  Rvector Rvector::random (const t_long size, 
			   const t_real rmin, const t_real rmax) {
    // create a vector by size
    Rvector result (size);
    // fill the vector
    Algebra::random (result, rmin, rmax);
    // done
    return  result;
  }
    
  // add a vector with a scalar

  Rvector operator + (const Rvector& x, const t_real s) {
    x.rdlock ();
    try {
      // create a result vector
      Rvector r (x.getsize ());
      // add the scalar
      Algebra::add (r, x, s);
      // unlock and return
      x.unlock ();
      return r;
    } catch (...) {
      x.unlock ();
      throw;
    }
  }

  // add a vector with another one

  Rvector operator + (const Rvector& x, const Rvector& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      // create a result vector
      Rvector r (x.getsize ());
      // add the scalar
      Algebra::add (r, x, y);
      // unlock and return
      x.unlock ();
      y.unlock ();
      return r;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // substract a vector with a scalar

  Rvector operator - (const Rvector& x, const t_real s) {
    x.rdlock ();
    try {
      // create a result vector
      Rvector r (x.getsize ());
      // add the scalar
      Algebra::sub (r, x, s);
      // unlock and return
      x.unlock ();
      return r;
    } catch (...) {
      x.unlock ();
      throw;
    }
  }

  // substract a vector with another one

  Rvector operator - (const Rvector& x, const Rvector& y) {
    x.rdlock ();
    y.rdlock ();
    try {
      // create a result vector
      Rvector r (x.getsize ());
      // add the scalar
      Algebra::sub (r, x, y);
      // unlock and return
      x.unlock ();
      y.unlock ();
      return r;
    } catch (...) {
      x.unlock ();
      y.unlock ();
      throw;
    }
  }

  // multiply a vector with a scalar

  Rvector operator * (const Rvector& x, const t_real s) {
    x.rdlock ();
    try {
      // create a result vector
      Rvector r (x.getsize ());
      // add the scalar
      Algebra::mul (r, x, s);
      // unlock and return
      x.unlock ();
      return r;
    } catch (...) {
      x.unlock ();
      throw;
    }
  }

  // divide a vector with a scalar

  Rvector operator / (const Rvector& x, const t_real s) {
    x.rdlock ();
    try {
      // create a result vector
      Rvector r (x.getsize ());
      // add the scalar
      Algebra::div (r, x, s);
      // unlock and return
      x.unlock ();
      return r;
    } catch (...) {
      x.unlock ();
      throw;
    }
  }

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

  // create a default vector

  Rvector::Rvector (void) {
    p_vtab = nilp;
  }

  // create a vector by size

  Rvector::Rvector (const t_long size) : Rvi (size) {
    p_vtab = (d_size == 0) ? nilp : new t_real [d_size];
    clear ();
  }

  // copy construct this vector

  Rvector::Rvector (const Rvector& that) {
    that.rdlock ();
    try {
      d_size = that.d_size;
      p_vtab = (d_size == 0) ? nilp : new t_real[d_size];
      for (t_long i = 0; i< d_size; i++) p_vtab[i] = that.p_vtab[i];
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }
	
  // destroy this vector

  Rvector::~Rvector (void) {
    delete [] p_vtab;
  }

  // return the class name

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

  // return a clone of this object

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

  // assign a vector to this one

  Rvector& Rvector::operator = (const Rvector& that) {
    // check for self-assignation
    if (this == &that) return *this;
    // lock and assign
    wrlock ();
    that.rdlock ();
    try {
      // delete the old vector
      if (d_size != that.d_size) {
	delete [] p_vtab;
	d_size = that.d_size;
	p_vtab = (d_size == 0) ? nilp : new t_real[d_size];
      }
      // assign the new one
      for (t_long i = 0; i< d_size; i++) p_vtab[i] = that.p_vtab[i];
      // unlock and return
      unlock ();
      that.unlock ();
      return *this;
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // set a vector by position

  void Rvector::set (const t_long pos, const t_real val) {
    wrlock ();
    try {
      if ((pos < 0) || (pos > d_size)) {
	throw Exception ("index-error", "invalid vector position");
      }
      p_vtab[pos] = val;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a vector by position

  t_real Rvector::get (const t_long pos) const {
    rdlock ();
    try {
      if ((pos < 0) || (pos > d_size)) {
	throw Exception ("index-error", "invalid vector position");
      }
      t_real result = p_vtab[pos];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

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

  // create a new object in a generic way

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

  // operate this vector with another object

  Object* Rvector::oper (t_oper type, Object* object) {
    Real*    dobj = dynamic_cast <Real*>    (object);
    Rvector* vobj = dynamic_cast <Rvector*> (object);
    switch (type) {
    case Object::ADD:
      if (vobj != nilp) return new Rvector (*this + *vobj);
      if (dobj != nilp) return new Rvector (*this + dobj->toreal ());
      break;
    case Object::SUB:
      if (vobj != nilp) return new Rvector (*this - *vobj);
      if (dobj != nilp) return new Rvector (*this - dobj->toreal ());
      break;
    case Object::MUL:
      if (dobj != nilp) return new Rvector (*this * dobj->toreal ());
      break;
    case Object::DIV:
      if (dobj != nilp) return new Rvector (*this / dobj->toreal ());
      break;
    default:
      throw Exception ("vector-error", "invalid operator with r-vector",
		       Object::repr (object));
      break;
    }
    throw Exception ("type-error", "invalid operand with r-vector",
		     Object::repr (object));
  }
}

