view src/ov.cc @ 10544:9961fc022d9d

fix assignment to non-existing variables and octave_value::assign
author Jaroslav Hajek <highegg@gmail.com>
date Fri, 23 Apr 2010 11:23:43 +0200
parents 4d1fc073fbb7
children d1194069e58c
line wrap: on
line source

/*

Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005,
              2006, 2007, 2008, 2009 John W. Eaton
Copyright (C) 2009, 2010 VZLU Prague

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 "data-conv.h"
#include "quit.h"
#include "str-vec.h"

#include "oct-obj.h"
#include "oct-stream.h"
#include "ov.h"
#include "ov-base.h"
#include "ov-bool.h"
#include "ov-bool-mat.h"
#include "ov-cell.h"
#include "ov-scalar.h"
#include "ov-float.h"
#include "ov-re-mat.h"
#include "ov-flt-re-mat.h"
#include "ov-re-diag.h"
#include "ov-flt-re-diag.h"
#include "ov-perm.h"
#include "ov-bool-sparse.h"
#include "ov-cx-sparse.h"
#include "ov-re-sparse.h"
#include "ov-int8.h"
#include "ov-int16.h"
#include "ov-int32.h"
#include "ov-int64.h"
#include "ov-uint8.h"
#include "ov-uint16.h"
#include "ov-uint32.h"
#include "ov-uint64.h"
#include "ov-complex.h"
#include "ov-flt-complex.h"
#include "ov-cx-mat.h"
#include "ov-flt-cx-mat.h"
#include "ov-cx-diag.h"
#include "ov-flt-cx-diag.h"
#include "ov-ch-mat.h"
#include "ov-str-mat.h"
#include "ov-range.h"
#include "ov-struct.h"
#include "ov-class.h"
#include "ov-cs-list.h"
#include "ov-colon.h"
#include "ov-builtin.h"
#include "ov-dld-fcn.h"
#include "ov-usr-fcn.h"
#include "ov-fcn-handle.h"
#include "ov-fcn-inline.h"
#include "ov-typeinfo.h"
#include "ov-null-mat.h"
#include "ov-lazy-idx.h"

#include "defun.h"
#include "error.h"
#include "gripes.h"
#include "pager.h"
#include "parse.h"
#include "pr-output.h"
#include "symtab.h"
#include "utils.h"
#include "variables.h"

// We are likely to have a lot of octave_value objects to allocate, so
// make the grow_size large.
DEFINE_OCTAVE_ALLOCATOR2(octave_value, 1024);

// FIXME

// Octave's value type.

std::string
octave_value::unary_op_as_string (unary_op op)
{
  std::string retval;

  switch (op)
    {
    case op_not:
      retval = "!";
      break;

    case op_uplus:
      retval = "+";
      break;

    case op_uminus:
      retval = "-";
      break;

    case op_transpose:
      retval = ".'";
      break;

    case op_hermitian:
      retval = "'";
      break;

    case op_incr:
      retval = "++";
      break;

    case op_decr:
      retval = "--";
      break;

    default:
      retval = "<unknown>";
    }

  return retval;
}

std::string
octave_value::unary_op_fcn_name (unary_op op)
{
  std::string retval;

  switch (op)
    {
    case op_not:
      retval = "not";
      break;

    case op_uplus:
      retval = "uplus";
      break;

    case op_uminus:
      retval = "uminus";
      break;

    case op_transpose:
      retval = "transpose";
      break;

    case op_hermitian:
      retval = "ctranspose";
      break;

    default:
      break;
    }

  return retval;
}

std::string
octave_value::binary_op_as_string (binary_op op)
{
  std::string retval;

  switch (op)
    {
    case op_add:
      retval = "+";
      break;

    case op_sub:
      retval = "-";
      break;

    case op_mul:
      retval = "*";
      break;

    case op_div:
      retval = "/";
      break;

    case op_pow:
      retval = "^";
      break;

    case op_ldiv:
      retval = "\\";
      break;

    case op_lshift:
      retval = "<<";
      break;

    case op_rshift:
      retval = ">>";
      break;

    case op_lt:
      retval = "<";
      break;

    case op_le:
      retval = "<=";
      break;

    case op_eq:
      retval = "==";
      break;

    case op_ge:
      retval = ">=";
      break;

    case op_gt:
      retval = ">";
      break;

    case op_ne:
      retval = "!=";
      break;

    case op_el_mul:
      retval = ".*";
      break;

    case op_el_div:
      retval = "./";
      break;

    case op_el_pow:
      retval = ".^";
      break;

    case op_el_ldiv:
      retval = ".\\";
      break;

    case op_el_and:
      retval = "&";
      break;

    case op_el_or:
      retval = "|";
      break;

    case op_struct_ref:
      retval = ".";
      break;

    default:
      retval = "<unknown>";
    }

  return retval;
}

std::string
octave_value::binary_op_fcn_name (binary_op op)
{
  std::string retval;

  switch (op)
    {
    case op_add:
      retval = "plus";
      break;

    case op_sub:
      retval = "minus";
      break;

    case op_mul:
      retval = "mtimes";
      break;

    case op_div:
      retval = "mrdivide";
      break;

    case op_pow:
      retval = "mpower";
      break;

    case op_ldiv:
      retval = "mldivide";
      break;

    case op_lt:
      retval = "lt";
      break;

    case op_le:
      retval = "le";
      break;

    case op_eq:
      retval = "eq";
      break;

    case op_ge:
      retval = "ge";
      break;

    case op_gt:
      retval = "gt";
      break;

    case op_ne:
      retval = "ne";
      break;

    case op_el_mul:
      retval = "times";
      break;

    case op_el_div:
      retval = "rdivide";
      break;

    case op_el_pow:
      retval = "power";
      break;

    case op_el_ldiv:
      retval = "ldivide";
      break;

    case op_el_and:
      retval = "and";
      break;

    case op_el_or:
      retval = "or";
      break;

    default:
      break;
    }

  return retval;
}

std::string
octave_value::binary_op_fcn_name (compound_binary_op op)
{
  std::string retval;

  switch (op)
    {
    case op_trans_mul:
      retval = "transtimes";
      break;

    case op_mul_trans:
      retval = "timestrans";
      break;

    case op_herm_mul:
      retval = "hermtimes";
      break;

    case op_mul_herm:
      retval = "timesherm";
      break;

    case op_trans_ldiv:
      retval = "transldiv";
      break;

    case op_herm_ldiv:
      retval = "hermldiv";
      break;

    case op_el_and_not:
      retval = "andnot";
      break;

    case op_el_or_not:
      retval = "ornot";
      break;

    case op_el_not_and:
      retval = "notand";
      break;

    case op_el_not_or:
      retval = "notor";
      break;

    default:
      break;
    }

  return retval;
}

std::string
octave_value::assign_op_as_string (assign_op op)
{
  std::string retval;

  switch (op)
    {
    case op_asn_eq:
      retval = "=";
      break;

    case op_add_eq:
      retval = "+=";
      break;

    case op_sub_eq:
      retval = "-=";
      break;

    case op_mul_eq:
      retval = "*=";
      break;

    case op_div_eq:
      retval = "/=";
      break;

    case op_ldiv_eq:
      retval = "\\=";
      break;

    case op_pow_eq:
      retval = "^=";
      break;

    case op_lshift_eq:
      retval = "<<=";
      break;

    case op_rshift_eq:
      retval = ">>=";
      break;

    case op_el_mul_eq:
      retval = ".*=";
      break;

    case op_el_div_eq:
      retval = "./=";
      break;

    case op_el_ldiv_eq:
      retval = ".\\=";
      break;

    case op_el_pow_eq:
      retval = ".^=";
      break;

    case op_el_and_eq:
      retval = "&=";
      break;

    case op_el_or_eq:
      retval = "|=";
      break;

    default:
      retval = "<unknown>";
    }

  return retval;
}

octave_value::assign_op
octave_value::binary_op_to_assign_op (binary_op op)
{
  assign_op retval;

  switch (op)
    {
    case op_add:
      retval = op_add_eq;
      break;
    case op_sub:
      retval = op_sub_eq;
      break;
    case op_mul:
      retval = op_mul_eq;
      break;
    case op_div:
      retval = op_div_eq;
      break;
    case op_el_mul:
      retval = op_el_mul_eq;
      break;
    case op_el_div:
      retval = op_el_div_eq;
      break;
    case op_el_and:
      retval = op_el_and_eq;
      break;
    case op_el_or:
      retval = op_el_or_eq;
      break;
    default:
      retval = unknown_assign_op;
    }

  return retval;
}

octave_value::octave_value (short int i)
  : rep (new octave_scalar (i))
{
}

octave_value::octave_value (unsigned short int i)
  : rep (new octave_scalar (i))
{
}

octave_value::octave_value (int i)
  : rep (new octave_scalar (i))
{
}

octave_value::octave_value (unsigned int i)
  : rep (new octave_scalar (i))
{
}

octave_value::octave_value (long int i)
  : rep (new octave_scalar (i))
{
}

octave_value::octave_value (unsigned long int i)
  : rep (new octave_scalar (i))
{
}

#if defined (HAVE_LONG_LONG_INT)
octave_value::octave_value (long long int i)
  : rep (new octave_scalar (i))
{
}
#endif

#if defined (HAVE_UNSIGNED_LONG_LONG_INT)
octave_value::octave_value (unsigned long long int i)
  : rep (new octave_scalar (i))
{
}
#endif

octave_value::octave_value (octave_time t)
  : rep (new octave_scalar (t.double_value ()))
{
}

octave_value::octave_value (double d)
  : rep (new octave_scalar (d))
{
}

octave_value::octave_value (float d)
  : rep (new octave_float_scalar (d))
{
}

octave_value::octave_value (const Cell& c, bool is_csl)
  : rep (is_csl
         ? dynamic_cast<octave_base_value *> (new octave_cs_list (c))
         : dynamic_cast<octave_base_value *> (new octave_cell (c)))
{
}

octave_value::octave_value (const Array<octave_value>& a, bool is_csl)
  : rep (is_csl
         ? dynamic_cast<octave_base_value *> (new octave_cs_list (Cell (a)))
         : dynamic_cast<octave_base_value *> (new octave_cell (Cell (a))))
{
}

octave_value::octave_value (const Matrix& m, const MatrixType& t)
  : rep (new octave_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatMatrix& m, const MatrixType& t)
  : rep (new octave_float_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const NDArray& a)
  : rep (new octave_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatNDArray& a)
  : rep (new octave_float_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<double>& a)
  : rep (new octave_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<float>& a)
  : rep (new octave_float_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const DiagMatrix& d)
  : rep (new octave_diag_matrix (d))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatDiagMatrix& d)
  : rep (new octave_float_diag_matrix (d))
{
  maybe_mutate ();
}

octave_value::octave_value (const RowVector& v)
  : rep (new octave_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatRowVector& v)
  : rep (new octave_float_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const ColumnVector& v)
  : rep (new octave_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatColumnVector& v)
  : rep (new octave_float_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const Complex& C)
  : rep (new octave_complex (C))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatComplex& C)
  : rep (new octave_float_complex (C))
{
  maybe_mutate ();
}

octave_value::octave_value (const ComplexMatrix& m, const MatrixType& t)
  : rep (new octave_complex_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatComplexMatrix& m, const MatrixType& t)
  : rep (new octave_float_complex_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const ComplexNDArray& a)
  : rep (new octave_complex_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatComplexNDArray& a)
  : rep (new octave_float_complex_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<Complex>& a)
  : rep (new octave_complex_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<FloatComplex>& a)
  : rep (new octave_float_complex_matrix (a))
{
  maybe_mutate ();
}

octave_value::octave_value (const ComplexDiagMatrix& d)
  : rep (new octave_complex_diag_matrix (d))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatComplexDiagMatrix& d)
  : rep (new octave_float_complex_diag_matrix (d))
{
  maybe_mutate ();
}

octave_value::octave_value (const ComplexRowVector& v)
  : rep (new octave_complex_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatComplexRowVector& v)
  : rep (new octave_float_complex_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const ComplexColumnVector& v)
  : rep (new octave_complex_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const FloatComplexColumnVector& v)
  : rep (new octave_float_complex_matrix (v))
{
  maybe_mutate ();
}

octave_value::octave_value (const PermMatrix& p)
  : rep (new octave_perm_matrix (p))
{
  maybe_mutate ();
}

octave_value::octave_value (bool b)
  : rep (new octave_bool (b))
{
}

octave_value::octave_value (const boolMatrix& bm, const MatrixType& t)
  : rep (new octave_bool_matrix (bm, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const boolNDArray& bnda)
  : rep (new octave_bool_matrix (bnda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<bool>& bnda)
  : rep (new octave_bool_matrix (bnda))
{
  maybe_mutate ();
}

octave_value::octave_value (char c, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (c)
         : new octave_char_matrix_sq_str (c))
{
  maybe_mutate ();
}

octave_value::octave_value (const char *s, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (s)
         : new octave_char_matrix_sq_str (s))
{
  maybe_mutate ();
}

octave_value::octave_value (const std::string& s, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (s)
         : new octave_char_matrix_sq_str (s))
{
  maybe_mutate ();
}

octave_value::octave_value (const string_vector& s, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (s)
         : new octave_char_matrix_sq_str (s))
{
  maybe_mutate ();
}

octave_value::octave_value (const charMatrix& chm, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (chm)
         : new octave_char_matrix_sq_str (chm))
{
  maybe_mutate ();
}

octave_value::octave_value (const charNDArray& chm, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (chm)
         : new octave_char_matrix_sq_str (chm))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<char>& chm, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (chm)
         : new octave_char_matrix_sq_str (chm))
{
  maybe_mutate ();
}

octave_value::octave_value (const charMatrix& chm, bool, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (chm)
         : new octave_char_matrix_sq_str (chm))
{
  maybe_mutate ();
}

octave_value::octave_value (const charNDArray& chm, bool, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (chm)
         : new octave_char_matrix_sq_str (chm))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<char>& chm, bool, char type)
  : rep (type == '"'
         ? new octave_char_matrix_dq_str (chm)
         : new octave_char_matrix_sq_str (chm))
{
  maybe_mutate ();
}

octave_value::octave_value (const SparseMatrix& m, const MatrixType &t)
  : rep (new octave_sparse_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const Sparse<double>& m, const MatrixType &t)
  : rep (new octave_sparse_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const SparseComplexMatrix& m, const MatrixType &t)
  : rep (new octave_sparse_complex_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const Sparse<Complex>& m, const MatrixType &t)
  : rep (new octave_sparse_complex_matrix (m, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const SparseBoolMatrix& bm, const MatrixType &t)
  : rep (new octave_sparse_bool_matrix (bm, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const Sparse<bool>& bm, const MatrixType &t)
  : rep (new octave_sparse_bool_matrix (bm, t))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_int8& i)
  : rep (new octave_int8_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_uint8& i)
  : rep (new octave_uint8_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_int16& i)
  : rep (new octave_int16_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_uint16& i)
  : rep (new octave_uint16_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_int32& i)
  : rep (new octave_int32_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_uint32& i)
  : rep (new octave_uint32_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_int64& i)
  : rep (new octave_int64_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const octave_uint64& i)
  : rep (new octave_uint64_scalar (i))
{
  maybe_mutate ();
}

octave_value::octave_value (const int8NDArray& inda)
  : rep (new octave_int8_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_int8>& inda)
  : rep (new octave_int8_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const uint8NDArray& inda)
  : rep (new octave_uint8_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_uint8>& inda)
  : rep (new octave_uint8_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const int16NDArray& inda)
  : rep (new octave_int16_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_int16>& inda)
  : rep (new octave_int16_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const uint16NDArray& inda)
  : rep (new octave_uint16_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_uint16>& inda)
  : rep (new octave_uint16_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const int32NDArray& inda)
  : rep (new octave_int32_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_int32>& inda)
  : rep (new octave_int32_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const uint32NDArray& inda)
  : rep (new octave_uint32_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_uint32>& inda)
  : rep (new octave_uint32_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const int64NDArray& inda)
  : rep (new octave_int64_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_int64>& inda)
  : rep (new octave_int64_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const uint64NDArray& inda)
  : rep (new octave_uint64_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_uint64>& inda)
  : rep (new octave_uint64_matrix (inda))
{
  maybe_mutate ();
}

octave_value::octave_value (const Array<octave_idx_type>& inda, bool zero_based,
                            bool cache_index)
  : rep (new octave_matrix (inda, zero_based, cache_index))
{
  maybe_mutate ();
}

octave_value::octave_value (const idx_vector& idx, bool lazy)
  : rep ()
{
  double scalar;
  Range range;
  NDArray array;
  boolNDArray mask;
  idx_vector::idx_class_type idx_class;

  if (lazy)
    {
      // Only make lazy indices out of ranges and index vectors.
      switch (idx.idx_class ())
        {
        case idx_vector::class_range:
        case idx_vector::class_vector:
          rep = new octave_lazy_index (idx);
          maybe_mutate ();
          return;
        default:
          break;
        }
    }

  idx.unconvert (idx_class, scalar, range, array, mask);

  switch (idx_class)
    {
    case idx_vector::class_colon:
      rep = new octave_magic_colon ();
      break;
    case idx_vector::class_range:
      rep = new octave_range (range, idx);
      break;
    case idx_vector::class_scalar:
      rep = new octave_scalar (scalar);
      break;
    case idx_vector::class_vector:
      rep = new octave_matrix (array, idx);
      break;
    case idx_vector::class_mask:
      rep = new octave_bool_matrix (mask, idx);
      break;
    default:
      assert (false);
      break;
    }

  // FIXME: needed?
  maybe_mutate ();
}

octave_value::octave_value (const Array<std::string>& cellstr)
  : rep (new octave_cell (cellstr))
{
  maybe_mutate ();
}

octave_value::octave_value (double base, double limit, double inc)
  : rep (new octave_range (base, limit, inc))
{
  maybe_mutate ();
}

octave_value::octave_value (const Range& r)
  : rep (new octave_range (r))
{
  maybe_mutate ();
}

octave_value::octave_value (const Octave_map& m)
  : rep (new octave_struct (m))
{
}

octave_value::octave_value (const Octave_map& m, const std::string& id)
  : rep (new octave_class (m, id))
{
}

octave_value::octave_value (const octave_value_list& l, bool)
  : rep (new octave_cs_list (l))
{
}

octave_value::octave_value (octave_value::magic_colon)
  : rep (new octave_magic_colon ())
{
}

octave_value::octave_value (octave_base_value *new_rep, bool borrow)
  : rep (new_rep)
{
  if (borrow)
    rep->count++;
}

octave_value::octave_value (octave_base_value *new_rep, int xcount)
  : rep (new_rep)
{
  rep->count = xcount;
}

octave_base_value *
octave_value::clone (void) const
{
  return rep->clone ();
}

void
octave_value::maybe_mutate (void)
{
  octave_base_value *tmp = rep->try_narrowing_conversion ();

  if (tmp && tmp != rep)
    {
      if (--rep->count == 0)
        delete rep;

      rep = tmp;
    }    
}

octave_value
octave_value::single_subsref (const std::string& type,
                              const octave_value_list& idx)
{
  std::list<octave_value_list> i;

  i.push_back (idx);

  return rep->subsref (type, i);
}

octave_value_list
octave_value::subsref (const std::string& type,
                       const std::list<octave_value_list>& idx, int nargout)
{
  if (nargout == 1)
    return rep->subsref (type, idx);
  else
    return rep->subsref (type, idx, nargout);
}

octave_value
octave_value::next_subsref (const std::string& type,
                            const std::list<octave_value_list>& idx,
                            size_t skip) 
{
  if (! error_state && idx.size () > skip)
    {
      std::list<octave_value_list> new_idx (idx);
      for (size_t i = 0; i < skip; i++)
        new_idx.erase (new_idx.begin ());
      return subsref (type.substr (skip), new_idx);
    }
  else
    return *this;
}

octave_value_list
octave_value::next_subsref (int nargout, const std::string& type,
                            const std::list<octave_value_list>& idx,
                            size_t skip) 
{
  if (! error_state && idx.size () > skip)
    {
      std::list<octave_value_list> new_idx (idx);
      for (size_t i = 0; i < skip; i++)
        new_idx.erase (new_idx.begin ());
      return subsref (type.substr (skip), new_idx, nargout);
    }
  else
    return *this;
}

octave_value
octave_value::next_subsref (bool auto_add, const std::string& type,
                            const std::list<octave_value_list>& idx,
                            size_t skip) 
{
  if (! error_state && idx.size () > skip)
    {
      std::list<octave_value_list> new_idx (idx);
      for (size_t i = 0; i < skip; i++)
        new_idx.erase (new_idx.begin ());
      return subsref (type.substr (skip), new_idx, auto_add);
    }
  else
    return *this;
}

octave_value_list
octave_value::do_multi_index_op (int nargout, const octave_value_list& idx)
{
  return rep->do_multi_index_op (nargout, idx);
}

#if 0
static void
gripe_assign_failed (const std::string& on, const std::string& tn1,
                     const std::string& tn2)
{
  error ("assignment failed for `%s %s %s'",
         tn1.c_str (), on.c_str (), tn2.c_str ());
}
#endif

static void
gripe_assign_failed_or_no_method (const std::string& on,
                                  const std::string& tn1,
                                  const std::string& tn2)
{
  error ("assignment failed, or no method for `%s %s %s'",
         tn1.c_str (), on.c_str (), tn2.c_str ());
}

octave_value
octave_value::subsasgn (const std::string& type,
                        const std::list<octave_value_list>& idx,
                        const octave_value& rhs)
{
  return rep->subsasgn (type, idx, rhs);
}

octave_value&
octave_value::assign (assign_op op, const std::string& type,
                      const std::list<octave_value_list>& idx,
                      const octave_value& rhs)
{
  octave_value retval;

  make_unique ();

  octave_value t_rhs = rhs;

  if (op != op_asn_eq)
    {
      if (is_defined ())
        {
          octave_value t = subsref (type, idx);

          if (! error_state)
            {
              binary_op binop = op_eq_to_binary_op (op);

              if (! error_state)
                t_rhs = do_binary_op (binop, t, rhs);
            }
        }
      else
        error ("in computed assignment A(index) OP= X, A must be defined first");
    }

  if (! error_state)
    {
      if (type[0] == '.' && ! (is_map () || is_object ()))
        {
          octave_value tmp = Octave_map ();
          *this = tmp.subsasgn (type, idx, t_rhs);
        }
      else
        *this = subsasgn (type, idx, t_rhs);

      if (error_state)
        gripe_assign_failed_or_no_method (assign_op_as_string (op_asn_eq),
                                          type_name (), rhs.type_name ());
    }

  return *this;
}

octave_value&
octave_value::assign (assign_op op, const octave_value& rhs)
{
  if (op == op_asn_eq)
    // Regularize a null matrix if stored into a variable.
    operator = (rhs.storable_value ());
  else if (is_defined ())
    {
      octave_value_typeinfo::assign_op_fcn f = 0;
      
      // Only attempt to operate in-place if this variable is unshared.
      if (rep->count == 1)
        {
          int tthis = this->type_id ();
          int trhs = rhs.type_id ();

          f = octave_value_typeinfo::lookup_assign_op (op, tthis, trhs);
        }

      if (f)
        {
          try
            {
              f (*rep, octave_value_list (), *rhs.rep);
              maybe_mutate (); // Usually unnecessary, but may be needed (complex arrays).
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }
      else
        {

          binary_op binop = op_eq_to_binary_op (op);

          if (! error_state)
            {
              octave_value t = do_binary_op (binop, *this, rhs);

              if (! error_state)
                operator = (t);
            }
        }
    }
  else
    error ("in computed assignment A OP= X, A must be defined first");

  return *this;
}

octave_idx_type
octave_value::length (void) const
{
  int retval = 0;

  dim_vector dv = dims ();

  for (int i = 0; i < dv.length (); i++)
    {
      if (dv(i) < 0)
        {
          retval = -1;
          break;
        }

      if (dv(i) == 0)
        {
          retval = 0;
          break;
        }

      if (dv(i) > retval)
        retval = dv(i);
    }

  return retval;
}

bool
octave_value::is_equal (const octave_value& test) const
{
  bool retval = false;

  // If there is no op_eq for these types, we can't compare values.

  if (rows () == test.rows () && columns () == test.columns ())
    {
      octave_value tmp = do_binary_op (octave_value::op_eq, *this, test);

      // Empty array also means a match.
      if (! error_state && tmp.is_defined ())
        retval = tmp.is_true () || tmp.is_empty ();
    }

  return retval;
}

Cell
octave_value::cell_value (void) const
{
  return rep->cell_value ();
}

// Define the idx_type_value function here instead of in ov.h to avoid
// needing definitions for the SIZEOF_X macros in ov.h.

octave_idx_type
octave_value::idx_type_value (bool req_int, bool frc_str_conv) const
{
#if SIZEOF_OCTAVE_IDX_TYPE == SIZEOF_LONG
  return long_value (req_int, frc_str_conv);
#elif SIZEOF_OCTAVE_IDX_TYPE == SIZEOF_INT
  return int_value (req_int, frc_str_conv);
#else
#error "no octave_value extractor for octave_idx_type"
#endif
}

Octave_map
octave_value::map_value (void) const
{
  return rep->map_value ();
}

octave_function *
octave_value::function_value (bool silent) const
{
  return rep->function_value (silent);
}

octave_user_function *
octave_value::user_function_value (bool silent) const
{
  return rep->user_function_value (silent);
}

octave_user_script *
octave_value::user_script_value (bool silent) const
{
  return rep->user_script_value (silent);
}

octave_user_code *
octave_value::user_code_value (bool silent) const
{
  return rep->user_code_value (silent);
}

octave_fcn_handle *
octave_value::fcn_handle_value (bool silent) const
{
  return rep->fcn_handle_value (silent);
}

octave_fcn_inline *
octave_value::fcn_inline_value (bool silent) const
{
  return rep->fcn_inline_value (silent);
}

octave_value_list
octave_value::list_value (void) const
{
  return rep->list_value ();
}

static dim_vector
make_vector_dims (const dim_vector& dv, bool force_vector_conversion,
                  const std::string& my_type, const std::string& wanted_type)
{
  dim_vector retval (dv);
  retval.chop_trailing_singletons ();
  octave_idx_type nel = dv.numel ();

  if (retval.length () > 2 || (retval(0) != 1 && retval(1) != 1))
    {
      if (!force_vector_conversion)
        gripe_implicit_conversion ("Octave:array-as-vector",
                                   my_type.c_str (), wanted_type.c_str ());
      retval = dim_vector (nel, 1);
    }

  return retval;
}

ColumnVector
octave_value::column_vector_value (bool force_string_conv,
                                   bool frc_vec_conv) const
{
  return ColumnVector (vector_value (force_string_conv, 
                                     frc_vec_conv));
}

ComplexColumnVector
octave_value::complex_column_vector_value (bool force_string_conv,
                                           bool frc_vec_conv) const
{
  return ComplexColumnVector (complex_vector_value (force_string_conv, 
                                                    frc_vec_conv));
}

RowVector
octave_value::row_vector_value (bool force_string_conv,
                                bool frc_vec_conv) const
{
  return RowVector (vector_value (force_string_conv, 
                                  frc_vec_conv));
}

ComplexRowVector
octave_value::complex_row_vector_value (bool force_string_conv,
                                        bool frc_vec_conv) const
{
  return ComplexRowVector (complex_vector_value (force_string_conv, 
                                                 frc_vec_conv));
}

Array<double>
octave_value::vector_value (bool force_string_conv,
                            bool force_vector_conversion) const
{
  Array<double> retval = array_value (force_string_conv);

  if (error_state)
    return retval;
  else
    return retval.reshape (make_vector_dims (retval.dims (),
                                             force_vector_conversion,
                                             type_name (), "real vector"));
}

template <class T>
static Array<int>
convert_to_int_array (const Array<octave_int<T> >& A)
{
  Array<int> retval (A.dims ());
  octave_idx_type n = A.numel ();

  for (octave_idx_type i = 0; i < n; i++)
    retval.xelem (i) = octave_int<int> (A.xelem (i));

  return retval;
}

Array<int>
octave_value::int_vector_value (bool force_string_conv, bool require_int,
                                bool force_vector_conversion) const
{
  Array<int> retval;

  if (is_integer_type ())
    {
      if (is_int32_type ())
        retval = convert_to_int_array (int32_array_value ());
      else if (is_int64_type ())
        retval = convert_to_int_array (int64_array_value ());
      else if (is_int16_type ())
        retval = convert_to_int_array (int16_array_value ());
      else if (is_int8_type ())
        retval = convert_to_int_array (int8_array_value ());
      else if (is_uint32_type ())
        retval = convert_to_int_array (uint32_array_value ());
      else if (is_uint64_type ())
        retval = convert_to_int_array (uint64_array_value ());
      else if (is_uint16_type ())
        retval = convert_to_int_array (uint16_array_value ());
      else if (is_uint8_type ())
        retval = convert_to_int_array (uint8_array_value ());
      else
        retval = array_value (force_string_conv);
    }
  else 
    {
      const NDArray a = array_value (force_string_conv);
      if (! error_state)
        {
          if (require_int)
            {
              retval.resize (a.dims ());
              for (octave_idx_type i = 0; i < a.numel (); i++)
                {
                  double ai = a.elem (i);
                  int v = static_cast<int> (ai);
                  if (ai == v)
                    retval.xelem (i) = v;
                  else
                    {
                      error_with_cfn ("conversion to integer value failed");
                      break;
                    }
                }
            }
          else
            retval = Array<int> (a);
        }
    }


  if (error_state)
    return retval;
  else
    return retval.reshape (make_vector_dims (retval.dims (),
                                             force_vector_conversion,
                                             type_name (), "integer vector"));
}

template <class T>
static Array<octave_idx_type>
convert_to_octave_idx_type_array (const Array<octave_int<T> >& A)
{
  Array<octave_idx_type> retval (A.dims ());
  octave_idx_type n = A.numel ();

  for (octave_idx_type i = 0; i < n; i++)
    retval.xelem (i) = octave_int<octave_idx_type> (A.xelem (i));

  return retval;
}

Array<octave_idx_type>
octave_value::octave_idx_type_vector_value (bool require_int,
                                            bool force_string_conv,
                                            bool force_vector_conversion) const
{
  Array<octave_idx_type> retval;

  if (is_integer_type ())
    {
      if (is_int32_type ())
        retval = convert_to_octave_idx_type_array (int32_array_value ());
      else if (is_int64_type ())
        retval = convert_to_octave_idx_type_array (int64_array_value ());
      else if (is_int16_type ())
        retval = convert_to_octave_idx_type_array (int16_array_value ());
      else if (is_int8_type ())
        retval = convert_to_octave_idx_type_array (int8_array_value ());
      else if (is_uint32_type ())
        retval = convert_to_octave_idx_type_array (uint32_array_value ());
      else if (is_uint64_type ())
        retval = convert_to_octave_idx_type_array (uint64_array_value ());
      else if (is_uint16_type ())
        retval = convert_to_octave_idx_type_array (uint16_array_value ());
      else if (is_uint8_type ())
        retval = convert_to_octave_idx_type_array (uint8_array_value ());
      else
        retval = array_value (force_string_conv);
    }
  else 
    {
      const NDArray a = array_value (force_string_conv);
      if (! error_state)
        {
          if (require_int)
            {
              retval.resize (a.dims ());
              for (octave_idx_type i = 0; i < a.numel (); i++)
                {
                  double ai = a.elem (i);
                  octave_idx_type v = static_cast<octave_idx_type> (ai);
                  if (ai == v)
                    retval.xelem (i) = v;
                  else
                    {
                      error_with_cfn ("conversion to integer value failed");
                      break;
                    }
                }
            }
          else
            retval = Array<octave_idx_type> (a);
        }
    }


  if (error_state)
    return retval;
  else
    return retval.reshape (make_vector_dims (retval.dims (),
                                             force_vector_conversion,
                                             type_name (), "integer vector"));
}

Array<Complex>
octave_value::complex_vector_value (bool force_string_conv,
                                    bool force_vector_conversion) const
{
  Array<Complex> retval = complex_array_value (force_string_conv);

  if (error_state)
    return retval;
  else
    return retval.reshape (make_vector_dims (retval.dims (),
                                             force_vector_conversion,
                                             type_name (), "complex vector"));
}

FloatColumnVector
octave_value::float_column_vector_value (bool force_string_conv,
                                         bool frc_vec_conv) const
{
  return FloatColumnVector (float_vector_value (force_string_conv, 
                                                frc_vec_conv));
}

FloatComplexColumnVector
octave_value::float_complex_column_vector_value (bool force_string_conv,
                                                 bool frc_vec_conv) const
{
  return FloatComplexColumnVector (float_complex_vector_value (force_string_conv, 
                                                               frc_vec_conv));
}

FloatRowVector
octave_value::float_row_vector_value (bool force_string_conv,
                                      bool frc_vec_conv) const
{
  return FloatRowVector (float_vector_value (force_string_conv, 
                                             frc_vec_conv));
}

FloatComplexRowVector
octave_value::float_complex_row_vector_value (bool force_string_conv,
                                              bool frc_vec_conv) const
{
  return FloatComplexRowVector (float_complex_vector_value (force_string_conv, 
                                                           frc_vec_conv));
}

Array<float>
octave_value::float_vector_value (bool force_string_conv,
                                  bool force_vector_conversion) const
{
  Array<float> retval = float_array_value (force_string_conv);

  if (error_state)
    return retval;
  else
    return retval.reshape (make_vector_dims (retval.dims (),
                                             force_vector_conversion,
                                             type_name (), "real vector"));
}

Array<FloatComplex>
octave_value::float_complex_vector_value (bool force_string_conv,
                                          bool force_vector_conversion) const
{
  Array<FloatComplex> retval = float_complex_array_value (force_string_conv);

  if (error_state)
    return retval;
  else
    return retval.reshape (make_vector_dims (retval.dims (),
                                             force_vector_conversion,
                                             type_name (), "complex vector"));
}

octave_value 
octave_value::storable_value (void) const
{
  octave_value retval = *this;
  if (is_null_value ())
    retval = octave_value (rep->empty_clone ());
  else
    retval.maybe_economize ();

  return retval;
}

void 
octave_value::make_storable_value (void) 
{
  if (is_null_value ())
    {
      octave_base_value *rc = rep->empty_clone ();
      if (--rep->count == 0)
        delete rep;
      rep = rc;
    }
  else
    maybe_economize ();
}

int
octave_value::write (octave_stream& os, int block_size,
                     oct_data_conv::data_type output_type, int skip,
                     oct_mach_info::float_format flt_fmt) const
{
  return rep->write (os, block_size, output_type, skip, flt_fmt);
}

static void
gripe_binary_op (const std::string& on, const std::string& tn1,
                 const std::string& tn2)
{
  error ("binary operator `%s' not implemented for `%s' by `%s' operations",
         on.c_str (), tn1.c_str (), tn2.c_str ());
}

static void
gripe_binary_op_conv (const std::string& on)
{
  error ("type conversion failed for binary operator `%s'", on.c_str ());
}

octave_value
do_binary_op (octave_value::binary_op op,
              const octave_value& v1, const octave_value& v2)
{
  octave_value retval;

  int t1 = v1.type_id ();
  int t2 = v2.type_id ();

  if (t1 == octave_class::static_type_id ()
      || t2 == octave_class::static_type_id ())
    {
      octave_value_typeinfo::binary_class_op_fcn f
        = octave_value_typeinfo::lookup_binary_class_op (op);

      if (f)
        {
          try
            {
              retval = f (v1, v2);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }           
      else
        gripe_binary_op (octave_value::binary_op_as_string (op),
                         v1.class_name (), v2.class_name ());
    }
  else
    {
      // FIXME -- we need to handle overloading operators for built-in
      // classes (double, char, int8, etc.)

      octave_value_typeinfo::binary_op_fcn f
        = octave_value_typeinfo::lookup_binary_op (op, t1, t2);

      if (f)
        {
          try
            {
              retval = f (*v1.rep, *v2.rep);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }
      else
        {
          octave_value tv1;
          octave_base_value::type_conv_info cf1 = v1.numeric_conversion_function ();

          octave_value tv2;
          octave_base_value::type_conv_info cf2 = v2.numeric_conversion_function ();

          // Try biased (one-sided) conversions first.
          if (cf2.type_id () >= 0 &&
              octave_value_typeinfo::lookup_binary_op (op, t1, cf2.type_id ()))
            cf1 = 0;
          else if (cf1.type_id () >= 0 &&
                   octave_value_typeinfo::lookup_binary_op (op, cf1.type_id (), t2))
            cf2 = 0;

          if (cf1)
            {
              octave_base_value *tmp = cf1 (*v1.rep);

              if (tmp)
                {
                  tv1 = octave_value (tmp);
                  t1 = tv1.type_id ();
                }
              else
                {
                  gripe_binary_op_conv (octave_value::binary_op_as_string (op));
                  return retval;
                }
            }
          else
            tv1 = v1;

          if (cf2)
            {
              octave_base_value *tmp = cf2 (*v2.rep);

              if (tmp)
                {
                  tv2 = octave_value (tmp);
                  t2 = tv2.type_id ();
                }
              else
                {
                  gripe_binary_op_conv (octave_value::binary_op_as_string (op));
                  return retval;
                }
            }
          else
            tv2 = v2;

          if (cf1 || cf2)
            {
              retval = do_binary_op (op, tv1, tv2);
            }
          else
            {
              //demote double -> single and try again
              cf1 = tv1.numeric_demotion_function ();

              cf2 = tv2.numeric_demotion_function ();

              // Try biased (one-sided) conversions first.
              if (cf2.type_id () >= 0
                  && octave_value_typeinfo::lookup_binary_op (op, t1, cf2.type_id ()))
                cf1 = 0;
              else if (cf1.type_id () >= 0
                       && octave_value_typeinfo::lookup_binary_op (op, cf1.type_id (), t2))
                cf2 = 0;

              if (cf1)
                {
                  octave_base_value *tmp = cf1 (*tv1.rep);

                  if (tmp)
                    {
                      tv1 = octave_value (tmp);
                      t1 = tv1.type_id ();
                    }
                  else
                    {
                      gripe_binary_op_conv (octave_value::binary_op_as_string (op));
                      return retval;
                    }
                }

              if (cf2)
                {
                  octave_base_value *tmp = cf2 (*tv2.rep);

                  if (tmp)
                    {
                      tv2 = octave_value (tmp);
                      t2 = tv2.type_id ();
                    }
                  else
                    {
                      gripe_binary_op_conv (octave_value::binary_op_as_string (op));
                      return retval;
                    }
                }

              if (cf1 || cf2)
                {
                  f = octave_value_typeinfo::lookup_binary_op (op, t1, t2);

                  if (f)
                    {
                      try
                        {
                          retval = f (*tv1.rep, *tv2.rep);
                        }
                      catch (octave_execution_exception)
                        {
                          gripe_library_execution_error ();
                        }
                    }
                  else
                    gripe_binary_op (octave_value::binary_op_as_string (op),
                                     v1.type_name (), v2.type_name ());
                }
              else
                gripe_binary_op (octave_value::binary_op_as_string (op),
                                 v1.type_name (), v2.type_name ());
            }
        }
    }

  return retval;
}

static octave_value
decompose_binary_op (octave_value::compound_binary_op op,
                     const octave_value& v1, const octave_value& v2)
{
  octave_value retval;

  switch (op)
    {
    case octave_value::op_trans_mul:
      retval = do_binary_op (octave_value::op_mul,
                             do_unary_op (octave_value::op_transpose, v1),
                             v2);
      break;
    case octave_value::op_mul_trans:
      retval = do_binary_op (octave_value::op_mul,
                             v1,
                             do_unary_op (octave_value::op_transpose, v2));
      break;
    case octave_value::op_herm_mul:
      retval = do_binary_op (octave_value::op_mul,
                             do_unary_op (octave_value::op_hermitian, v1),
                             v2);
      break;
    case octave_value::op_mul_herm:
      retval = do_binary_op (octave_value::op_mul,
                             v1,
                             do_unary_op (octave_value::op_hermitian, v2));
      break;
    case octave_value::op_trans_ldiv:
      retval = do_binary_op (octave_value::op_ldiv,
                             do_unary_op (octave_value::op_transpose, v1),
                             v2);
      break;
    case octave_value::op_herm_ldiv:
      retval = do_binary_op (octave_value::op_ldiv,
                             do_unary_op (octave_value::op_hermitian, v1),
                             v2);
      break;
    case octave_value::op_el_not_and:
      retval = do_binary_op (octave_value::op_el_and,
                             do_unary_op (octave_value::op_not, v1),
                             v2);
      break;
    case octave_value::op_el_not_or:
      retval = do_binary_op (octave_value::op_el_or,
                             do_unary_op (octave_value::op_not, v1),
                             v2);
      break;
    case octave_value::op_el_and_not:
      retval = do_binary_op (octave_value::op_el_and,
                             v1,
                             do_unary_op (octave_value::op_not, v2));
      break;
    case octave_value::op_el_or_not:
      retval = do_binary_op (octave_value::op_el_or,
                             v1,
                             do_unary_op (octave_value::op_not, v2));
      break;
    default:
      error ("invalid compound operator");
      break;
    }

  return retval;
}

octave_value
do_binary_op (octave_value::compound_binary_op op,
              const octave_value& v1, const octave_value& v2)
{
  octave_value retval;

  int t1 = v1.type_id ();
  int t2 = v2.type_id ();

  if (t1 == octave_class::static_type_id ()
      || t2 == octave_class::static_type_id ())
    {
      octave_value_typeinfo::binary_class_op_fcn f
        = octave_value_typeinfo::lookup_binary_class_op (op);

      if (f)
        {
          try
            {
              retval = f (v1, v2);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }           
      else
        retval = decompose_binary_op (op, v1, v2);
    }
  else
    {
      octave_value_typeinfo::binary_op_fcn f
        = octave_value_typeinfo::lookup_binary_op (op, t1, t2);

      if (f)
        {
          try
            {
              retval = f (*v1.rep, *v2.rep);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }
      else
        retval = decompose_binary_op (op, v1, v2);
    }

  return retval;
}

static void
gripe_cat_op (const std::string& tn1, const std::string& tn2)
{
  error ("concatenation operator not implemented for `%s' by `%s' operations",
         tn1.c_str (), tn2.c_str ());
}

static void
gripe_cat_op_conv (void)
{
  error ("type conversion failed for concatenation operator");
}

octave_value
do_cat_op (const octave_value& v1, const octave_value& v2, 
           const Array<octave_idx_type>& ra_idx)
{
  octave_value retval;

  // Can't rapid return for concatenation with an empty object here as
  // something like cat(1,[],single([]) must return the correct type.

  int t1 = v1.type_id ();
  int t2 = v2.type_id ();

  octave_value_typeinfo::cat_op_fcn f
    = octave_value_typeinfo::lookup_cat_op (t1, t2);

  if (f)
    {
      try
        {
          retval = f (*v1.rep, *v2.rep, ra_idx);
        }
      catch (octave_execution_exception)
        {
          gripe_library_execution_error ();
        }
    }
  else
    {
      octave_value tv1;
      octave_base_value::type_conv_info cf1 = v1.numeric_conversion_function ();

      octave_value tv2;
      octave_base_value::type_conv_info cf2 = v2.numeric_conversion_function ();

      // Try biased (one-sided) conversions first.
      if (cf2.type_id () >= 0
          && octave_value_typeinfo::lookup_cat_op (t1, cf2.type_id ()))
        cf1 = 0;
      else if (cf1.type_id () >= 0
               && octave_value_typeinfo::lookup_cat_op (cf1.type_id (), t2))
        cf2 = 0;

      if (cf1)
        {
          octave_base_value *tmp = cf1 (*v1.rep);

          if (tmp)
            {
              tv1 = octave_value (tmp);
              t1 = tv1.type_id ();
            }
          else
            {
              gripe_cat_op_conv ();
              return retval;
            }
        }
      else
        tv1 = v1;

      if (cf2)
        {
          octave_base_value *tmp = cf2 (*v2.rep);

          if (tmp)
            {
              tv2 = octave_value (tmp);
              t2 = tv2.type_id ();
            }
          else
            {
              gripe_cat_op_conv ();
              return retval;
            }
        }
      else
        tv2 = v2;

      if (cf1 || cf2)
        {
          retval = do_cat_op (tv1, tv2, ra_idx);
        }
      else
        gripe_cat_op (v1.type_name (), v2.type_name ());
    }

  return retval;
}

void
octave_value::print_info (std::ostream& os, const std::string& prefix) const
{
  os << prefix << "type_name: " << type_name () << "\n"
     << prefix << "count:     " << get_count () << "\n"
     << prefix << "rep info:  ";

  rep->print_info (os, prefix + " ");
}

static void
gripe_unary_op (const std::string& on, const std::string& tn)
{
  error ("unary operator `%s' not implemented for `%s' operands",
         on.c_str (), tn.c_str ());
}

static void
gripe_unary_op_conv (const std::string& on)
{
  error ("type conversion failed for unary operator `%s'", on.c_str ());
}

octave_value
do_unary_op (octave_value::unary_op op, const octave_value& v)
{
  octave_value retval;

  int t = v.type_id ();

  if (t == octave_class::static_type_id ())
    {
      octave_value_typeinfo::unary_class_op_fcn f
        = octave_value_typeinfo::lookup_unary_class_op (op);

      if (f)
        {
          try
            {
              retval = f (v);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }
      else
        gripe_unary_op (octave_value::unary_op_as_string (op),
                        v.class_name ());
    }
  else
    {
      // FIXME -- we need to handle overloading operators for built-in
      // classes (double, char, int8, etc.)

      octave_value_typeinfo::unary_op_fcn f
        = octave_value_typeinfo::lookup_unary_op (op, t);

      if (f)
        {
          try
            {
              retval = f (*v.rep);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }
      else
        {
          octave_value tv;
          octave_base_value::type_conv_fcn cf
            = v.numeric_conversion_function ();

          if (cf)
            {
              octave_base_value *tmp = cf (*v.rep);

              if (tmp)
                {
                  tv = octave_value (tmp);
                  retval = do_unary_op (op, tv);
                }
              else
                gripe_unary_op_conv (octave_value::unary_op_as_string (op));
            }
          else
            gripe_unary_op (octave_value::unary_op_as_string (op),
                            v.type_name ());
        }
    }

  return retval;
}

static void
gripe_unary_op_conversion_failed (const std::string& op,
                                  const std::string& tn)
{
  error ("operator %s: type conversion for `%s' failed",
         op.c_str (), tn.c_str ());
}

const octave_value&
octave_value::do_non_const_unary_op (unary_op op)
{
  if (op == op_incr || op == op_decr)
    {
      // Genuine.
      int t = type_id ();

      octave_value_typeinfo::non_const_unary_op_fcn f
        = octave_value_typeinfo::lookup_non_const_unary_op (op, t);

      if (f)
        {
          make_unique ();

          try
            {
              f (*rep);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }
      else
        {
          octave_base_value::type_conv_fcn cf = numeric_conversion_function ();

          if (cf)
            {
              octave_base_value *tmp = cf (*rep);

              if (tmp)
                {
                  octave_base_value *old_rep = rep;
                  rep = tmp;

                  t = type_id ();

                  f = octave_value_typeinfo::lookup_non_const_unary_op (op, t);

                  if (f)
                    {
                      try
                        {
                          f (*rep);
                        }
                      catch (octave_execution_exception)
                        {
                          gripe_library_execution_error ();
                        }

                      if (old_rep && --old_rep->count == 0)
                        delete old_rep;
                    }
                  else
                    {
                      if (old_rep)
                        {
                          if (--rep->count == 0)
                            delete rep;

                          rep = old_rep;
                        }

                      gripe_unary_op (octave_value::unary_op_as_string (op),
                                      type_name ());
                    }
                }
              else
                gripe_unary_op_conversion_failed
                  (octave_value::unary_op_as_string (op), type_name ());
            }
          else
            gripe_unary_op (octave_value::unary_op_as_string (op), type_name ());
        }
    }
  else
    {
      // Non-genuine.
      int t = type_id ();

      octave_value_typeinfo::non_const_unary_op_fcn f = 0;

      // Only attempt to operate in-place if this variable is unshared.
      if (rep->count == 1)
        f = octave_value_typeinfo::lookup_non_const_unary_op (op, t);

      if (f)
        {
          try
            {
              f (*rep);
            }
          catch (octave_execution_exception)
            {
              gripe_library_execution_error ();
            }
        }
      else
        *this = do_unary_op (op, *this);
    }

  return *this;
}

#if 0
static void
gripe_unary_op_failed_or_no_method (const std::string& on,
                                    const std::string& tn) 
{
  error ("operator %s: no method, or unable to evaluate for %s operand",
         on.c_str (), tn.c_str ());
}
#endif

void
octave_value::do_non_const_unary_op (unary_op, const octave_value_list&)
{
  abort ();
}

octave_value
octave_value::do_non_const_unary_op (unary_op op, const std::string& type,
                                     const std::list<octave_value_list>& idx)
{
  octave_value retval;

  if (idx.empty ())
    {
      do_non_const_unary_op (op);

      retval = *this;
    }
  else
    {
      // FIXME -- only do the following stuff if we can't find a
      // specific function to call to handle the op= operation for the
      // types we have.

      assign_op assop = unary_op_to_assign_op (op);

      retval = assign (assop, type, idx, 1.0);
    }

  return retval;
}

octave_value::assign_op
octave_value::unary_op_to_assign_op (unary_op op)
{
  assign_op binop = unknown_assign_op;

  switch (op)
    {
    case op_incr:
      binop = op_add_eq;
      break;

    case op_decr:
      binop = op_sub_eq;
      break;

    default:
      {
        std::string on = unary_op_as_string (op);
        error ("operator %s: no assign operator found", on.c_str ());
      }
    }

  return binop;
}

octave_value::binary_op 
octave_value::op_eq_to_binary_op (assign_op op)
{
  binary_op binop = unknown_binary_op;

  switch (op)
    {
    case op_add_eq:
      binop = op_add;
      break;

    case op_sub_eq:
      binop = op_sub;
      break;

    case op_mul_eq:
      binop = op_mul;
      break;

    case op_div_eq:
      binop = op_div;
      break;

    case op_ldiv_eq:
      binop = op_ldiv;
      break;

    case op_pow_eq:
      binop = op_pow;
      break;

    case op_lshift_eq:
      binop = op_lshift;
      break;

    case op_rshift_eq:
      binop = op_rshift;
      break;

    case op_el_mul_eq:
      binop = op_el_mul;
      break;

    case op_el_div_eq:
      binop = op_el_div;
      break;

    case op_el_ldiv_eq:
      binop = op_el_ldiv;
      break;

    case op_el_pow_eq:
      binop = op_el_pow;
      break;

    case op_el_and_eq:
      binop = op_el_and;
      break;

    case op_el_or_eq:
      binop = op_el_or;
      break;

    default:
      {
        std::string on = assign_op_as_string (op);
        error ("operator %s: no binary operator found", on.c_str ());
      }
    }

  return binop;
}

octave_value
octave_value::empty_conv (const std::string& type, const octave_value& rhs)
{
  octave_value retval;

  if (type.length () > 0)
    {
      switch (type[0])
        {
        case '(':
          {
            if (type.length () > 1 && type[1] == '.')
              retval = Octave_map ();
            else
              retval = octave_value (rhs.empty_clone ());
          }
          break;

        case '{':
          retval = Cell ();
          break;

        case '.':
          retval = Octave_map ();
          break;

        default:
          panic_impossible ();
        }
    }
  else
    retval = octave_value (rhs.empty_clone ());

  return retval;
}

void
install_types (void)
{
  octave_base_value::register_type ();
  octave_cell::register_type ();
  octave_scalar::register_type ();
  octave_complex::register_type ();
  octave_matrix::register_type ();
  octave_diag_matrix::register_type ();
  octave_complex_matrix::register_type ();
  octave_complex_diag_matrix::register_type ();
  octave_range::register_type ();
  octave_bool::register_type ();
  octave_bool_matrix::register_type ();
  octave_char_matrix_str::register_type ();
  octave_char_matrix_sq_str::register_type ();
  octave_int8_scalar::register_type ();
  octave_int16_scalar::register_type ();
  octave_int32_scalar::register_type ();
  octave_int64_scalar::register_type ();
  octave_uint8_scalar::register_type ();
  octave_uint16_scalar::register_type ();
  octave_uint32_scalar::register_type ();
  octave_uint64_scalar::register_type ();
  octave_int8_matrix::register_type ();
  octave_int16_matrix::register_type ();
  octave_int32_matrix::register_type ();
  octave_int64_matrix::register_type ();
  octave_uint8_matrix::register_type ();
  octave_uint16_matrix::register_type ();
  octave_uint32_matrix::register_type ();
  octave_uint64_matrix::register_type ();
  octave_sparse_bool_matrix::register_type ();
  octave_sparse_matrix::register_type ();
  octave_sparse_complex_matrix::register_type ();
  octave_struct::register_type ();
  octave_class::register_type ();
  octave_cs_list::register_type ();
  octave_magic_colon::register_type ();
  octave_builtin::register_type ();
  octave_user_function::register_type ();
  octave_dld_function::register_type ();
  octave_fcn_handle::register_type ();
  octave_fcn_inline::register_type ();
  octave_float_scalar::register_type ();
  octave_float_complex::register_type ();
  octave_float_matrix::register_type ();
  octave_float_diag_matrix::register_type ();
  octave_float_complex_matrix::register_type ();
  octave_float_complex_diag_matrix::register_type ();
  octave_perm_matrix::register_type ();
  octave_null_matrix::register_type ();
  octave_null_str::register_type ();
  octave_null_sq_str::register_type ();
  octave_lazy_index::register_type ();
}

#if 0
DEFUN (cast, args, ,
  "-*- texinfo -*-\n\
@deftypefn {Built-in Function} {} cast (@var{val}, @var{type})\n\
Convert @var{val} to the new data type @var{type}.\n\
@seealso{class, typeinfo}\n\
@end deftypefn")
{
  octave_value retval;

  if (args.length () == 2)
    error ("cast: not implemented");
  else
    print_usage ();

  return retval;
}
#endif

DEFUN (sizeof, args, ,
  "-*- texinfo -*-\n\
@deftypefn {Built-in Function} {} sizeof (@var{val})\n\
Return the size of @var{val} in bytes\n\
@end deftypefn")
{
  octave_value retval;

  if (args.length () == 1)
    retval = args(0).byte_size ();
  else
    print_usage ();

  return retval;
}

DEFUN (subsref, args, nargout,
  "-*- texinfo -*-\n\
@deftypefn {Built-in Function} {} subsref (@var{val}, @var{idx})\n\
Perform the subscripted element selection operation according to\n\
the subscript specified by @var{idx}.\n\
\n\
The subscript @var{idx} is expected to be a structure array with\n\
fields @samp{type} and @samp{subs}.  Valid values for @samp{type}\n\
are @samp{\"()\"}, @samp{\"@{@}\"}, and @samp{\".\"}.\n\
The @samp{subs} field may be either @samp{\":\"} or a cell array\n\
of index values.\n\
\n\
The following example shows how to extract the two first columns of\n\
a matrix\n\
\n\
@example\n\
@group\n\
val = magic(3)\n\
     @result{} val = [ 8   1   6\n\
                3   5   7\n\
                4   9   2 ]\n\
idx.type = \"()\";\n\
idx.subs = @{\":\", 1:2@};\n\
subsref(val, idx)\n\
     @result{} [ 8   1 \n\
          3   5 \n\
          4   9 ]\n\
@end group\n\
@end example\n\
\n\
@noindent\n\
Note that this is the same as writing @code{val(:,1:2)}.\n\
@seealso{subsasgn, substruct}\n\
@end deftypefn")
{
  octave_value_list retval;

  if (args.length () == 2)
    {
      std::string type;
      std::list<octave_value_list> idx;

      decode_subscripts ("subsref", args(1), type, idx);

      if (! error_state)
        {
          octave_value tmp = args(0);
          retval = tmp.subsref (type, idx, nargout);
        }
    }
  else
    print_usage ();

  return retval;
}

DEFUN (subsasgn, args, ,
  "-*- texinfo -*-\n\
@deftypefn {Built-in Function} {} subsasgn (@var{val}, @var{idx}, @var{rhs})\n\
Perform the subscripted assignment operation according to\n\
the subscript specified by @var{idx}.\n\
\n\
The subscript @var{idx} is expected to be a structure array with\n\
fields @samp{type} and @samp{subs}.  Valid values for @samp{type}\n\
are @samp{\"()\"}, @samp{\"@{@}\"}, and @samp{\".\"}.\n\
The @samp{subs} field may be either @samp{\":\"} or a cell array\n\
of index values.\n\
\n\
The following example shows how to set the two first columns of a\n\
3-by-3 matrix to zero.\n\
\n\
@example\n\
@group\n\
val = magic(3);\n\
idx.type = \"()\";\n\
idx.subs = @{\":\", 1:2@};\n\
subsasgn (val, idx, 0)\n\
     @result{} [ 0   0   6\n\
          0   0   7\n\
          0   0   2 ]\n\
@end group\n\
@end example\n\
\n\
Note that this is the same as writing @code{val(:,1:2) = 0}.\n\
@seealso{subsref, substruct}\n\
@end deftypefn")
{
  octave_value retval;

  if (args.length () == 3)
    {
      std::string type;
      std::list<octave_value_list> idx;

      decode_subscripts ("subsasgn", args(1), type, idx);

      octave_value arg0 = args(0);

      arg0.make_unique ();

      if (! error_state)
        retval = arg0.subsasgn (type, idx, args(2));
    }
  else
    print_usage ();

  return retval;
}