/*

Copyright (C) 2004, 2005, 2006, 2007 David Bateman
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Andy Adler

This file is part of Octave.

Octave 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.

Octave 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 Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <iostream>

#include "oct-obj.h"
#include "ov-base.h"
#include "quit.h"
#include "pr-output.h"

#include "byte-swap.h"
#include "ls-oct-ascii.h"
#include "ls-utils.h"
#if defined (HAVE_HDF5)
#include "ls-hdf5.h"
#endif

#include "boolSparse.h"
#include "ov-base-sparse.h"
#include "pager.h"

template <class T>
octave_value
octave_base_sparse<T>::do_index_op (const octave_value_list& idx, 
				    bool resize_ok)
{
  octave_value retval;

  octave_idx_type n_idx = idx.length ();

  int nd = matrix.ndims ();

  switch (n_idx)
    {
    case 0:
      retval = matrix;
      break;

    case 1:
      {
	idx_vector i = idx (0).index_vector ();

	if (! error_state)
	  retval = octave_value (matrix.index (i, resize_ok));
      }
      break;

    default:
      {
	if (n_idx == 2 && nd == 2)
	  {
	    idx_vector i = idx (0).index_vector ();

	    if (! error_state)
	      {
		idx_vector j = idx (1).index_vector ();

		if (! error_state)
		  retval = octave_value (matrix.index (i, j, resize_ok));
	      }
	  }
	else
	  {
	    Array<idx_vector> idx_vec (n_idx);

	    for (octave_idx_type i = 0; i < n_idx; i++)
	      {
		idx_vec(i) = idx(i).index_vector ();

		if (error_state)
		  break;
	      }

	    if (! error_state)
	      retval = octave_value (matrix.index (idx_vec, resize_ok));
	  }
      }
      break;
    }

  return retval;
}

template <class T>
octave_value
octave_base_sparse<T>::subsref (const std::string& type,
				const std::list<octave_value_list>& idx)
{
  octave_value retval;

  switch (type[0])
    {
    case '(':
      retval = do_index_op (idx.front ());
      break;

    case '{':
    case '.':
      {
	std::string nm = type_name ();
	error ("%s cannot be indexed with %c", nm.c_str (), type[0]);
      }
      break;

    default:
      panic_impossible ();
    }

  return retval.next_subsref (type, idx);
}

template <class T>
octave_value 
octave_base_sparse<T>::subsasgn (const std::string& type,
				 const std::list<octave_value_list>& idx,
				 const octave_value& rhs)
{
  octave_value retval;

  switch (type[0])
    {
    case '(':
      {
	if (type.length () == 1)
	  retval = numeric_assign (type, idx, rhs);
	else
	  {
	    std::string nm = type_name ();
	    error ("in indexed assignment of %s, last lhs index must be ()",
		   nm.c_str ());
	  }
      }
      break;

    case '{':
    case '.':
      {
	if (is_empty ())
	  {
	    octave_value tmp = octave_value::empty_conv (type, rhs);

	    retval = tmp.subsasgn (type, idx, rhs);
	  }
	else
	  {
	    std::string nm = type_name ();
	    error ("%s cannot be indexed with %c", nm.c_str (), type[0]);
	  }
      }
      break;

    default:
      panic_impossible ();
    }

  return retval;
}

template <class T>
void 
octave_base_sparse<T>::assign (const octave_value_list& idx, const T& rhs)
{
  octave_idx_type len = idx.length ();

  for (octave_idx_type i = 0; i < len; i++)
    matrix.set_index (idx(i).index_vector ());

  ::assign (matrix, rhs);

  // Invalidate matrix type.
  typ.invalidate_type ();
}

template <class T>
octave_value 
octave_base_sparse<T>::resize (const dim_vector& dv, bool) const
{ 
  T retval (matrix); 
  retval.resize (dv); 
  return retval; 
}

template <class T>
bool 
octave_base_sparse<T>::is_true (void) const
{
  bool retval = false;
  dim_vector dv = matrix.dims ();
  octave_idx_type nel = dv.numel ();
  octave_idx_type nz = nzmax ();

  if (nz == nel && nel > 0)
    {
      T t1 (matrix.reshape (dim_vector (nel, 1)));

      SparseBoolMatrix t2 = t1.all ();

      retval = t2(0);
    }

  return retval;
}

template <class T>
bool 
octave_base_sparse<T>::print_as_scalar (void) const
{
  dim_vector dv = dims ();

  return (dv.all_ones () || dv.any_zero ());
}

template <class T>
void 
octave_base_sparse<T>::print (std::ostream& os, bool pr_as_read_syntax) const
{
  print_raw (os, pr_as_read_syntax);
  newline (os);
}

template <class T>
void 
octave_base_sparse<T>::print_info (std::ostream& os, 
				   const std::string& prefix) const
{
  matrix.print_info (os, prefix);
}

template <class T>
void
octave_base_sparse<T>::print_raw (std::ostream& os,
				  bool pr_as_read_syntax) const
{
  octave_idx_type nr = matrix.rows ();
  octave_idx_type nc = matrix.cols ();
  octave_idx_type nz = nnz ();

  // FIXME -- this should probably all be handled by a
  // separate octave_print_internal function that can handle format
  // compact, loose, etc.

  os << "Compressed Column Sparse (rows = " << nr
     << ", cols = " << nc
     << ", nnz = " << nz << ")\n";

  // add one to the printed indices to go from
  //  zero-based to one-based arrays

  if (nz != 0)
    {
      for (octave_idx_type j = 0; j < nc; j++)
	{
	  OCTAVE_QUIT;

	  // FIXME -- is there an easy way to get the max row
	  // and column indices so we can set the width appropriately
	  // and line up the columns here?  Similarly, we should look
	  // at all the nonzero values and display them with the same
	  // formatting rules that apply to columns of a matrix.

	  for (octave_idx_type i = matrix.cidx(j); i < matrix.cidx(j+1); i++)
	    {
	      os << "\n";
	      os << "  (" << matrix.ridx(i)+1 <<
		", "  << j+1 << ") -> ";

	      octave_print_internal (os, matrix.data(i), pr_as_read_syntax);
	    }
	}
    }
}

template <class T>
bool
octave_base_sparse<T>::save_ascii (std::ostream& os)
{
  dim_vector dv = this->dims ();

  // Ensure that additional memory is deallocated
  matrix.maybe_compress ();

  os << "# nnz: "      << nzmax () << "\n";
  os << "# rows: "     << dv (0) << "\n";
  os << "# columns: "  << dv (1) << "\n";

  os << this->matrix;

  return true;
}

template <class T>
bool 
octave_base_sparse<T>::load_ascii (std::istream& is)
{
  octave_idx_type nz = 0;
  octave_idx_type nr = 0;
  octave_idx_type nc = 0;
  bool success = true;

  if (extract_keyword (is, "nnz", nz, true) &&
      extract_keyword (is, "rows", nr, true) &&
      extract_keyword (is, "columns", nc, true))
    {
      T tmp (nr, nc, nz);

      is >> tmp;

      if (!is) 
	{
	  error ("load: failed to load matrix constant");
	  success = false;
	}

      matrix = tmp;
    }
  else
    {
      error ("load: failed to extract number of rows and columns");
      success = false;
    }

  return success;
}

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/
