//
// Object_Matrix
//

#include "ascript/Object_Matrix.h"
#include "ascript/Expr.h"
#include "ascript/Algorithm.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Matrix
//-----------------------------------------------------------------------------
Object_Matrix::Object_Matrix(Environment &env, size_t nRows, size_t nCols) :
	Object(env.LookupClass(VTYPE_Matrix)),
	_pObjList(new Object_List(env)),
	_iRowOff(0), _iColOff(0),
	_nRows(nRows), _nCols(nCols), _nFold(nCols), _indexForColFlag(false)
{
	ValueList &valListMat = _pObjList->GetList();
	valListMat.reserve(_nRows * _nCols);
}

Object_Matrix::Object_Matrix(Environment &env,
						size_t nRows, size_t nCols, const Value &valueElem) :
	Object(env.LookupClass(VTYPE_Matrix)),
	_pObjList(new Object_List(env, nRows * nCols, valueElem)),
	_iRowOff(0), _iColOff(0),
	_nRows(nRows), _nCols(nCols), _nFold(nCols), _indexForColFlag(false)
{
}

Object_Matrix::Object_Matrix(Environment &env, Object_List *pObjList,
				size_t iRowOff, size_t iColOff, size_t nRows, size_t nCols,
				size_t nFold, bool indexForColFlag) :
	Object(env.LookupClass(VTYPE_Matrix)),
	_pObjList(pObjList),
	_iRowOff(iRowOff), _iColOff(iColOff),
	_nRows(nRows), _nCols(nCols), _nFold(nFold), _indexForColFlag(indexForColFlag)
{
}

Object_Matrix::Object_Matrix(const Object_Matrix &obj) :
	Object(obj),
	_pObjList(dynamic_cast<Object_List *>(obj._pObjList->Clone())),
	_iRowOff(obj._iRowOff), _iColOff(obj._iColOff),
	_nRows(obj._nRows), _nCols(obj._nCols),
	_nFold(obj._nFold), _indexForColFlag(obj._indexForColFlag)
{
}

Object_Matrix::~Object_Matrix()
{
	Object::Delete(_pObjList);
}

Object *Object_Matrix::Clone() const
{
	return new Object_Matrix(*this);
}

Value Object_Matrix::EmptyIndexGet(Signal sig)
{
	if (_indexForColFlag) {
		sig.SetError(ERR_IndexError, "only one empty index should be applied");
		return Value::Null;
	}
	Environment &env = *this;
	Value result;
	Object_Matrix *pObj = new Object_Matrix(env,
				Object_List::Reference(_pObjList),
				_iRowOff, _iColOff, _nRows, _nCols, _nFold, true);
	result.InitAsObject(pObj);
	return result;
}

Value Object_Matrix::IndexGet(Signal sig, const Value &valueIdx)
{
	if (!valueIdx.IsNumber()) {
		sig.SetError(ERR_IndexError, "index must be a number for list");
		return Value::Null;
	}
	size_t idx = valueIdx.GetSizeT();
	if (_indexForColFlag) {
		if (ColSize() == 1) {
			if (idx >= RowSize()) {
				SetError_IndexOutOfRange(sig);
				return Value::Null;
			}
			return *GetPointer(idx, 0);
		} else {
			if (idx >= ColSize()) {
				SetError_IndexOutOfRange(sig);
				return Value::Null;
			}
			if (RowSize() == 1) {
				return *GetPointer(0, idx);
			} else {
				return GetSub(0, idx, RowSize(), 1);
			}
		}
	} else {
		if (RowSize() == 1) {
			if (idx >= ColSize()) {
				SetError_IndexOutOfRange(sig);
				return Value::Null;
			}
			return *GetPointer(0, idx);
		} else {
			if (idx >= RowSize()) {
				SetError_IndexOutOfRange(sig);
				return Value::Null;
			}
			if (ColSize() == 1) {
				return *GetPointer(idx, 0);
			} else {
				return GetSub(idx, 0, 1, ColSize());
			}
		}
	}
}

void Object_Matrix::IndexSet(Signal sig, const Value &valueIdx, const Value &value)
{
	if (!valueIdx.IsNumber()) {
		sig.SetError(ERR_IndexError, "index must be a number for list");
		return;
	}
	size_t idx = valueIdx.GetSizeT();
	if (RowSize() == 1) {
		if (idx >= ColSize()) {
			SetError_IndexOutOfRange(sig);
			return;
		}
		*GetPointer(0, idx) = value;
	} else {
		if (idx >= RowSize()) {
			SetError_IndexOutOfRange(sig);
			return;
		}
		if (ColSize() == 1) {
			*GetPointer(idx, 0) = value;
		} else if (value.IsMatrix()) {
			
			// todo
			
		} else {
			Iterator *pIterator = (value.IsList() || value.IsIterator())?
					value.CreateIterator(sig) : new Iterator_Constant(value);
			if (sig.IsSignalled()) return;
			SetRow(*this, sig, idx, pIterator);
			Iterator::Delete(pIterator);
		}
	}
}

String Object_Matrix::ToString(Signal sig, bool exprFlag)
{
	String rtn;
	if (_nRows == 1 && _nCols == 1) {
		ValueList::const_iterator pValueElem = GetPointer(0, 0);
		rtn += pValueElem->ToString(sig, exprFlag);
	} else if (_nCols == 1) {
		rtn += "@@{";
		ValueList::const_iterator pValueElem = GetPointer(0, 0);
		size_t offset = 0;
		for (size_t iRow = 0; iRow < _nRows; iRow++, offset += FoldSize()) {
			if (iRow > 0) rtn += ", ";
			rtn += (pValueElem + offset)->ToString(sig, exprFlag);
		}
		rtn += "}";
	} else {
		rtn += "@@{";
		for (size_t iRow = 0; iRow < _nRows; iRow++) {
			if (iRow > 0) rtn += ", ";
			rtn += "{";
			ValueList::const_iterator pValueElem = GetPointer(iRow, 0);
			for (size_t iCol = 0; iCol < _nCols; iCol++, pValueElem++) {
				if (iCol > 0) rtn += ", ";
				rtn += pValueElem->ToString(sig, exprFlag);
			}
			rtn += "}";
		}
		rtn += "}";
	}
	return rtn;
}

void Object_Matrix::ToList(ValueList &valList, bool transposeFlag, bool flattenFlag)
{
	Environment &env = *this;
	if (_nRows == 1) {
		valList.reserve(_nCols);
		ValueList::const_iterator pValueElem = GetPointer(0, 0);
		for (size_t iCol = 0; iCol < _nCols; iCol++, pValueElem++) {
			valList.push_back(*pValueElem);
		}
	} else if (_nCols == 1) {
		valList.reserve(_nRows);
		ValueList::const_iterator pValueElem = GetPointer(0, 0);
		size_t offset = 0;
		for (size_t iRow = 0; iRow < _nRows; iRow++, offset += FoldSize()) {
			valList.push_back(*(pValueElem + offset));
		}
	} else if (transposeFlag) {
		if (flattenFlag) {
			valList.reserve(_nRows * _nCols);
			for (size_t iCol = 0; iCol < _nCols; iCol++) {
				ValueList::const_iterator pValueElem = GetPointer(0, iCol);
				size_t offset = 0;
				for (size_t iRow = 0; iRow < _nRows; iRow++, offset += FoldSize()) {
					valList.push_back(*(pValueElem + offset));
				}
			}
		} else {
			valList.reserve(_nCols);
			for (size_t iCol = 0; iCol < _nCols; iCol++) {
				ValueList::const_iterator pValueElem = GetPointer(0, iCol);
				Value valueCol;
				ValueList &valListCol = valueCol.InitAsList(env);
				valList.push_back(valueCol);
				size_t offset = 0;
				for (size_t iRow = 0; iRow < _nRows; iRow++, offset += FoldSize()) {
					valListCol.push_back(*(pValueElem + offset));
				}
			}
		}
	} else {
		if (flattenFlag) {
			valList.reserve(_nRows * _nCols);
			for (size_t iRow = 0; iRow < _nRows; iRow++) {
				ValueList::const_iterator pValueElem = GetPointer(iRow, 0);
				for (size_t iCol = 0; iCol < _nCols; iCol++, pValueElem++) {
					valList.push_back(*pValueElem);
				}
			}
		} else {
			valList.reserve(_nRows);
			for (size_t iRow = 0; iRow < _nRows; iRow++) {
				ValueList::const_iterator pValueElem = GetPointer(iRow, 0);
				Value valueRow;
				ValueList &valListRow = valueRow.InitAsList(env);
				valList.push_back(valueRow);
				for (size_t iCol = 0; iCol < _nCols; iCol++, pValueElem++) {
					valListRow.push_back(*pValueElem);
				}
			}
		}
	}
}

Value Object_Matrix::GetSub(size_t iRow, size_t iCol, size_t nRows, size_t nCols)
{
	Environment &env = *this;
	Value result;
	Object_Matrix *pObj = new Object_Matrix(env,
				Object_List::Reference(_pObjList),
				iRow, iCol, nRows, nCols, _nFold, false);
	result.InitAsObject(pObj);
	return result;
}

bool Object_Matrix::Set(Environment &env, Signal sig, Iterator *pIterator)
{
	for (size_t iRow = 0; iRow < _nRows; iRow++) {
		ValueList::iterator pValueElem = GetPointer(iRow, 0);
		Value value;
		for (size_t iCol = 0; iCol < _nCols && pIterator->Next(env, sig, value);
														iCol++, pValueElem++) {
			*pValueElem = value;
		}
		if (sig.IsSignalled()) return false;
	}
	return true;
}

bool Object_Matrix::SetRow(Environment &env, Signal sig, size_t iRow, Iterator *pIterator)
{
	if (iRow >= _nRows) {
		SetError_IndexOutOfRange(sig);
		return false;
	}
	Value value;
	ValueList::iterator pValueElem = GetPointer(iRow, 0);
	for (size_t iCol = 0; iCol < _nCols && pIterator->Next(env, sig, value);
												iCol++, pValueElem++) {
		*pValueElem = value;
	}
	return true;
}

bool Object_Matrix::SetCol(Environment &env, Signal sig, size_t iCol, Iterator *pIterator)
{
	if (iCol >= _nCols) {
		SetError_IndexOutOfRange(sig);
		return false;
	}
	Value value;
	ValueList::iterator pValueElem = GetPointer(0, iCol);
	size_t offset = 0;
	for (size_t iRow = 0; iRow < _nRows && pIterator->Next(env, sig, value);
												iRow++, offset += FoldSize()) {
		*(pValueElem + offset) = value;
	}
	return true;
}

Value Object_Matrix::GetRow(Environment &env, Signal sig, size_t iRow) const
{
	if (iRow >= _nRows) {
		SetError_IndexOutOfRange(sig);
		return false;
	}
	Value result;
	ValueList &valList = result.InitAsList(env);
	valList.reserve(ColSize());
	ValueList::const_iterator pValueElem = GetPointer(iRow, 0);
	for (size_t iCol = 0; iCol < ColSize(); iCol++, pValueElem++) {
		valList.push_back(*pValueElem);
	}
	return result;
}

Value Object_Matrix::GetCol(Environment &env, Signal sig, size_t iCol) const
{
	if (iCol >= _nCols) {
		SetError_IndexOutOfRange(sig);
		return false;
	}
	Value result;
	ValueList &valList = result.InitAsList(env);
	valList.reserve(RowSize());
	ValueList::const_iterator pValueElem = GetPointer(0, iCol);
	size_t offset = 0;
	for (size_t iRow = 0; iRow < RowSize(); iRow++, offset += FoldSize()) {
		valList.push_back(*(pValueElem + offset));
	}
	return result;
}

Value Object_Matrix::Transpose(Environment &env, Signal sig)
{
	Value result;
	size_t nRows = RowSize(), nCols = ColSize();
	size_t nFold = FoldSize();
	Object_Matrix *pObjMatRtn = new Object_Matrix(env, nCols, nRows);
	result.InitAsObject(pObjMatRtn);
	ValueList &valList = pObjMatRtn->GetList();
	for (size_t iCol = 0; iCol < nCols; iCol++) {
		ValueList::const_iterator pValueElem = GetPointer(0, iCol);
		size_t offset = 0;
		for (size_t iRow = 0; iRow < nRows; iRow++, offset += nFold) {
			valList.push_back(*(pValueElem + offset));
		}
	}
	return result;
}

Value Object_Matrix::Inverse(Environment &env, Signal sig)
{
	size_t nCols = ColSize(), nRows = RowSize();
	if (nCols != nRows) {
		sig.SetError(ERR_ValueError, "inversion can only be applied to square matrix");
		return Value::Null;
	}
	ValueType valType = CheckValueType();
	if (valType == VTYPE_Number) {
		NumberList mat;
		mat.reserve(nCols * 2 * nRows);
		for (size_t iRow = 0; iRow < nRows; iRow++) {
			ValueList::const_iterator pValueElem = GetPointer(iRow, 0);
			for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem++) {
				mat.push_back(pValueElem->GetNumber());
			}
			for (size_t iCol = 0; iCol < nCols; iCol++) {
				mat.push_back((iCol == iRow)? 1. : 0.);
			}
		}
		Number det;
		if (!InverseMatrix(mat, nCols, det)) {
			sig.SetError(ERR_ValueError, "failed to calculate inverse matrix");
			return Value::Null;
		}
		Value result;
		Object_Matrix *pObjMatRtn = new Object_Matrix(env, nCols, nRows);
		result.InitAsObject(pObjMatRtn);
		ValueList &valList = pObjMatRtn->GetList();
		//NumberList::iterator pResult = mat.begin() + nCols;
		size_t offset = nCols;
		for (size_t iRow = 0; iRow < nRows; iRow++) {
			for (size_t iCol = 0; iCol < nCols; iCol++, offset++) {
				valList.push_back(mat[offset]);
			}
			offset += nCols;
		}
		return result;
	} else if (valType == VTYPE_Complex) {
		ComplexList mat;
		mat.reserve(nCols * 2 * nRows);
		for (size_t iRow = 0; iRow < nRows; iRow++) {
			ValueList::const_iterator pValueElem = GetPointer(iRow, 0);
			for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem++) {
				mat.push_back(pValueElem->GetComplex());
			}
			for (size_t iCol = 0; iCol < nCols; iCol++) {
				mat.push_back((iCol == iRow)? 1. : 0.);
			}
		}
		Complex det;
		if (!InverseMatrix(mat, nCols, det)) {
			sig.SetError(ERR_ValueError, "failed to calculate inverse matrix");
			return Value::Null;
		}
		Value result;
		Object_Matrix *pObjMatRtn = new Object_Matrix(env, nCols, nRows);
		result.InitAsObject(pObjMatRtn);
		ValueList &valList = pObjMatRtn->GetList();
		//ComplexList::iterator pResult = mat.begin() + nCols;
		size_t offset = nCols;
		for (size_t iRow = 0; iRow < nRows; iRow++) {
			for (size_t iCol = 0; iCol < nCols; iCol++, offset++) {
				valList.push_back(mat[offset]);
			}
			offset += nCols;
		}
		return result;
	}
	sig.SetError(ERR_ValueError, "failed to calculate inverse matrix");
	return Value::Null;
}

bool Object_Matrix::GetElemIndex(Environment &env, Signal sig,
		const Expr *pExprIdx, size_t nElemsSrc, size_t &iElem, size_t &nElems)
{
	Value valueIdx = pExprIdx->Exec(env, sig);
	if (sig.IsSignalled()) return false;
	if (valueIdx.IsNumber()) {
		iElem = valueIdx.GetSizeT();
	} else if (valueIdx.IsIterator()) {
		Iterator *pIterator = valueIdx.GetIterator();
		if (pIterator->IsSequence()) {
			Iterator_Sequence *pIteratorEx =
							dynamic_cast<Iterator_Sequence *>(pIterator);
			iElem = static_cast<size_t>(pIteratorEx->GetBegin());
			nElems = static_cast<size_t>(
						pIteratorEx->GetEnd() - pIteratorEx->GetBegin()) + 1;
		} else if (pIterator->IsSequenceInf()) {
			Iterator_SequenceInf *pIteratorEx =
							dynamic_cast<Iterator_SequenceInf *>(pIterator);
			iElem = static_cast<size_t>(pIteratorEx->GetBegin());
			nElems = nElemsSrc - iElem;
		} else {
			sig.SetError(ERR_ValueError, "invalid index expression");
			return false;
		}
	} else {
		sig.SetError(ERR_ValueError, "invalid index expression");
		return false;
	}
	if (iElem >= nElemsSrc || iElem + nElems > nElemsSrc) {
		SetError_IndexOutOfRange(sig);
		return false;
	}
	return true;
}

Value Object_Matrix::OperatorNeg(Environment &env, Signal sig,
											const Object_Matrix *pObjMat)
{
	Value result;
	size_t nRows = pObjMat->RowSize(), nCols = pObjMat->ColSize();
	Object_Matrix *pObjMatRtn = new Object_Matrix(env, nRows, nCols);
	result.InitAsObject(pObjMatRtn);
	ValueList &valList = pObjMatRtn->GetList();
	const Function &func = env.GetFunc_Neg();
	for (size_t iRow = 0; iRow < nRows; iRow++) {
		ValueList::const_iterator pValueElem = pObjMat->GetPointer(iRow, 0);
		for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem++) {
			ValueList valListArg(*pValueElem);
			Args args(valListArg);
			Value resultElem = func.Eval(env, sig, args);
			if (sig.IsSignalled()) return Value::Null;
			valList.push_back(resultElem);
		}
	}
	return result;
}

void SetError_MatrixSizeMismatch(Signal sig)
{
	sig.SetError(ERR_ValueError, "matrix size mismatches");
}

Value Object_Matrix::OperatorPlusMinus(Environment &env, Signal sig, const Function &func,
				const Object_Matrix *pObjMat1, const Object_Matrix *pObjMat2)
{
	Value result;
	size_t nRows = pObjMat1->RowSize(), nCols = pObjMat1->ColSize();
	if (!(nRows == pObjMat2->RowSize() && nCols == pObjMat2->ColSize())) {
		SetError_MatrixSizeMismatch(sig);
		return Value::Null;
	}
	Object_Matrix *pObjMatRtn = new Object_Matrix(env, nRows, nCols);
	result.InitAsObject(pObjMatRtn);
	ValueList &valList = pObjMatRtn->GetList();
	for (size_t iRow = 0; iRow < nRows; iRow++) {
		ValueList::const_iterator pValueElem1 = pObjMat1->GetPointer(iRow, 0);
		ValueList::const_iterator pValueElem2 = pObjMat2->GetPointer(iRow, 0);
		for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem1++, pValueElem2++) {
			ValueList valListArg(*pValueElem1, *pValueElem2);
			Args args(valListArg);
			Value resultElem = func.Eval(env, sig, args);
			if (sig.IsSignalled()) return Value::Null;
			valList.push_back(resultElem);
		}
	}
	return result;
}

Value Object_Matrix::OperatorMultiply(Environment &env, Signal sig,
				const Object_Matrix *pObjMat1, const Object_Matrix *pObjMat2)
{
	Value result;
	size_t nRows = pObjMat1->RowSize(), nCols = pObjMat2->ColSize();
	size_t nElems = pObjMat1->ColSize();
	size_t nFold = pObjMat2->FoldSize();
	if (nElems != pObjMat2->RowSize()) {
		SetError_MatrixSizeMismatch(sig);
		return Value::Null;
	}
	Object_Matrix *pObjMatRtn = new Object_Matrix(env, nRows, nCols);
	result.InitAsObject(pObjMatRtn);
	ValueList &valList = pObjMatRtn->GetList();
	const Function &funcMultiply = env.GetFunc_Multiply();
	const Function &funcPlus = env.GetFunc_Plus();
	for (size_t iRow = 0; iRow < nRows; iRow++) {
		for (size_t iCol = 0; iCol < nCols; iCol++) {
			ValueList::const_iterator pValueElem1 = pObjMat1->GetPointer(iRow, 0);
			ValueList::const_iterator pValueElem2 = pObjMat2->GetPointer(0, iCol);
			Value valueAccum(0);
			size_t offset = 0;
			for (size_t iElem = 0; iElem < nElems;
									iElem++, pValueElem1++, offset += nFold) {
				Value valueElem;
				do {
					ValueList valListArg(*pValueElem1, *(pValueElem2 + offset));
					Args args(valListArg);
					valueElem = funcMultiply.Eval(env, sig, args);
					if (sig.IsSignalled()) return Value::Null;
				} while (0);
				do {
					ValueList valListArg(valueAccum, valueElem);
					Args args(valListArg);
					valueAccum = funcPlus.Eval(env, sig, args);
					if (sig.IsSignalled()) return Value::Null;
				} while (0);
			}
			valList.push_back(valueAccum);
		}
	}
	return result;
}

Value Object_Matrix::OperatorMultiply(Environment &env, Signal sig,
							const Object_Matrix *pObjMat, const Value &value)
{
	Value result;
	size_t nRows = pObjMat->RowSize(), nCols = pObjMat->ColSize();
	if (value.IsList()) {
		const ValueList &valList = value.GetList();
		if (nCols != valList.size()) {
			SetError_MatrixSizeMismatch(sig);
			return Value::Null;
		}
		const Function &funcMultiply = env.GetFunc_Multiply();
		const Function &funcPlus = env.GetFunc_Plus();
		ValueList &valListResult = result.InitAsList(env);
		valListResult.reserve(nRows);
		for (size_t iRow = 0; iRow < nRows; iRow++) {
			ValueList::const_iterator pValueElem = pObjMat->GetPointer(iRow, 0);
			Value valueAccum(0);
			foreach_const (ValueList, pValue, valList) {
				Value valueElem;
				do {
					ValueList valListArg(*pValueElem, *pValue);
					Args args(valListArg);
					valueElem = funcMultiply.Eval(env, sig, args);
					if (sig.IsSignalled()) return Value::Null;
				} while (0);
				do {
					ValueList valListArg(valueAccum, valueElem);
					Args args(valListArg);
					valueAccum = funcPlus.Eval(env, sig, args);
					if (sig.IsSignalled()) return Value::Null;
				} while (0);
				pValueElem++;
			}
			valListResult.push_back(valueAccum);
		}
		if (valListResult.size() == 1) return valListResult.front();
	} else {
		Object_Matrix *pObjMatRtn = new Object_Matrix(env, nRows, nCols);
		result.InitAsObject(pObjMatRtn);
		ValueList &valListResult = pObjMatRtn->GetList();
		const Function &func = env.GetFunc_Multiply();
		for (size_t iRow = 0; iRow < nRows; iRow++) {
			ValueList::const_iterator pValueElem = pObjMat->GetPointer(iRow, 0);
			for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem++) {
				ValueList valListArg(*pValueElem, value);
				Args args(valListArg);
				Value resultElem = func.Eval(env, sig, args);
				if (sig.IsSignalled()) return Value::Null;
				valListResult.push_back(resultElem);
			}
		}
		if (valListResult.size() == 1) return valListResult.front();
	}
	return result;
}

Value Object_Matrix::OperatorMultiply(Environment &env, Signal sig,
							const Value &value, const Object_Matrix *pObjMat)
{
	Value result;
	size_t nRows = pObjMat->RowSize(), nCols = pObjMat->ColSize();
	size_t nFold = pObjMat->FoldSize();
	if (value.IsList()) {
		const ValueList &valList = value.GetList();
		if (nRows != valList.size()) {
			SetError_MatrixSizeMismatch(sig);
			return Value::Null;
		}
		const Function &funcMultiply = env.GetFunc_Multiply();
		const Function &funcPlus = env.GetFunc_Plus();
		ValueList &valListResult = result.InitAsList(env);
		valListResult.reserve(nCols);
		for (size_t iCol = 0; iCol < nCols; iCol++) {
			ValueList::const_iterator pValueElem = pObjMat->GetPointer(0, iCol);
			Value valueAccum(0);
			size_t offset = 0;
			foreach_const (ValueList, pValue, valList) {
				Value valueElem;
				do {
					ValueList valListArg(*(pValueElem + offset), *pValue);
					Args args(valListArg);
					valueElem = funcMultiply.Eval(env, sig, args);
					if (sig.IsSignalled()) return Value::Null;
				} while (0);
				do {
					ValueList valListArg(valueAccum, valueElem);
					Args args(valListArg);
					valueAccum = funcPlus.Eval(env, sig, args);
					if (sig.IsSignalled()) return Value::Null;
				} while (0);
				offset += nFold;
			}
			valListResult.push_back(valueAccum);
		}
		if (valListResult.size() == 1) return valListResult.front();
	} else {
		Object_Matrix *pObjMatRtn = new Object_Matrix(env, nRows, nCols);
		result.InitAsObject(pObjMatRtn);
		ValueList &valListResult = pObjMatRtn->GetList();
		const Function &func = env.GetFunc_Multiply();
		for (size_t iRow = 0; iRow < nRows; iRow++) {
			ValueList::const_iterator pValueElem = pObjMat->GetPointer(iRow, 0);
			for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem++) {
				ValueList valListArg(value, *pValueElem);
				Args args(valListArg);
				Value resultElem = func.Eval(env, sig, args);
				if (sig.IsSignalled()) return Value::Null;
				valListResult.push_back(resultElem);
			}
		}
		if (valListResult.size() == 1) return valListResult.front();
	}
	return result;
}

Value Object_Matrix::OperatorDivide(Environment &env, Signal sig,
							const Object_Matrix *pObjMat, const Value &value)
{
	Value result;
	size_t nRows = pObjMat->RowSize(), nCols = pObjMat->ColSize();
	Object_Matrix *pObjMatRtn = new Object_Matrix(env, nRows, nCols);
	result.InitAsObject(pObjMatRtn);
	ValueList &valList = pObjMatRtn->GetList();
	const Function &func = env.GetFunc_Divide();
	for (size_t iRow = 0; iRow < nRows; iRow++) {
		ValueList::const_iterator pValueElem = pObjMat->GetPointer(iRow, 0);
		for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem++) {
			ValueList valListArg(*pValueElem, value);
			Args args(valListArg);
			Value resultElem = func.Eval(env, sig, args);
			if (sig.IsSignalled()) return Value::Null;
			valList.push_back(resultElem);
		}
	}
	return result;
}

ValueType Object_Matrix::CheckValueType() const
{
	ValueType valType = VTYPE_Nil;
	size_t nCols = ColSize(), nRows = RowSize();
	for (size_t iRow = 0; iRow < nRows; iRow++) {
		ValueList::const_iterator pValueElem = GetPointer(iRow, 0);
		for (size_t iCol = 0; iCol < nCols; iCol++, pValueElem++) {
			if (pValueElem->IsNumber()) {
				if (valType == VTYPE_Nil) valType = VTYPE_Number;
			} else if (pValueElem->IsComplex()) {
				valType = VTYPE_Complex;
			} else {
				return VTYPE_Nil;
			}
		}
	}
	return valType;
}

void Object_Matrix::SetError_IndexOutOfRange(Signal sig)
{
	sig.SetError(ERR_IndexError, "index is out of range");
}

//-----------------------------------------------------------------------------
// Object_Matrix::IteratorEach
//-----------------------------------------------------------------------------
Object_Matrix::IteratorEach::~IteratorEach()
{
	Object::Delete(_pObj);
}

bool Object_Matrix::IteratorEach::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_transposeFlag) {
		if (_iCol >= _pObj->ColSize()) return false;
		value = *_pObj->GetPointer(_iRow, _iCol);
		_iRow++;
		if (_iRow >= _pObj->RowSize()) {
			_iRow = 0, _iCol++;
		}
	} else {
		if (_iRow >= _pObj->RowSize()) return false;
		value = *_pObj->GetPointer(_iRow, _iCol);
		_iCol++;
		if (_iCol >= _pObj->ColSize()) {
			_iCol = 0, _iRow++;
		}
	}
	return true;
}

String Object_Matrix::IteratorEach::ToString(Signal sig) const
{
	return String("<iterator:matrix#each>");
}

//-----------------------------------------------------------------------------
// Object_Matrix::IteratorEachRow
//-----------------------------------------------------------------------------
Object_Matrix::IteratorEachRow::~IteratorEachRow()
{
	Object::Delete(_pObj);
}

bool Object_Matrix::IteratorEachRow::DoNext(Environment &env, Signal sig, Value &value)
{
	//Environment &env = *_pObj;
	if (_iRow >= _pObj->RowSize()) return false;
	value = _pObj->GetRow(env, sig, _iRow);
	_iRow++;
	return true;
}

String Object_Matrix::IteratorEachRow::ToString(Signal sig) const
{
	return String("<iterator:matrix#eachrow>");
}

//-----------------------------------------------------------------------------
// Object_Matrix::IteratorEachCol
//-----------------------------------------------------------------------------
Object_Matrix::IteratorEachCol::~IteratorEachCol()
{
	Object::Delete(_pObj);
}

bool Object_Matrix::IteratorEachCol::DoNext(Environment &env, Signal sig, Value &value)
{
	//Environment &env = *_pObj;
	if (_iCol >= _pObj->ColSize()) return false;
	value = _pObj->GetCol(env, sig, _iCol);
	_iCol++;
	return true;
}

String Object_Matrix::IteratorEachCol::ToString(Signal sig) const
{
	return String("<iterator:matrix#eachcol>");
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Matrix
//-----------------------------------------------------------------------------
// matrix#set(value)
AScript_DeclareMethod(Matrix, set)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Any);
	SetHelp("Sets all the cells of the matrix with a specified value.");
}

AScript_ImplementMethod(Matrix, set)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	Iterator *pIterator = (args.IsList(0) || args.IsIterator(0))?
								args.GetValue(0).CreateIterator(sig) :
								new Iterator_Constant(args.GetValue(0));
	if (sig.IsSignalled()) return Value::Null;
	pSelf->Set(env, sig, pIterator);
	Iterator::Delete(pIterator);
	return Value::Null;
}

// matrix#setrow(row:number, value)
AScript_DeclareMethod(Matrix, setrow)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "row", VTYPE_Number);
	DeclareArg(env, "value", VTYPE_Any);
	SetHelp("Sets cells in a selected row of the matrix with a specified value.");
}

AScript_ImplementMethod(Matrix, setrow)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	Iterator *pIterator = (args.IsList(1) || args.IsIterator(1))?
								args.GetValue(1).CreateIterator(sig) :
								new Iterator_Constant(args.GetValue(1));
	if (sig.IsSignalled()) return Value::Null;
	pSelf->SetRow(env, sig, args.GetSizeT(0), pIterator);
	Iterator::Delete(pIterator);
	return Value::Null;
}

// matrix#setcol(col:number, value)
AScript_DeclareMethod(Matrix, setcol)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "col", VTYPE_Number);
	DeclareArg(env, "value", VTYPE_Any);
	SetHelp("Sets cells in a selected column of the matrix with a specified value.");
}

AScript_ImplementMethod(Matrix, setcol)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	Iterator *pIterator = (args.IsList(1) || args.IsIterator(1))?
								args.GetValue(1).CreateIterator(sig) :
								new Iterator_Constant(args.GetValue(1));
	if (sig.IsSignalled()) return Value::Null;
	pSelf->SetCol(env, sig, args.GetSizeT(0), pIterator);
	Iterator::Delete(pIterator);
	return Value::Null;
}

// matrix#rowsize()
AScript_DeclareMethod(Matrix, rowsize)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns the matrix row size.");
}

AScript_ImplementMethod(Matrix, rowsize)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	return Value(static_cast<unsigned int>(pSelf->RowSize()));
}

// matrix#colsize()
AScript_DeclareMethod(Matrix, colsize)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns the matrix column size.");
}

AScript_ImplementMethod(Matrix, colsize)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	return Value(static_cast<unsigned int>(pSelf->ColSize()));
}

// matrix#issquare()
AScript_DeclareMethod(Matrix, issquare)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns true if the matrix is a square one.");
}

AScript_ImplementMethod(Matrix, issquare)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	return Value(pSelf->RowSize() == pSelf->ColSize());
}

// matrix#submat(row:number, col:number, nrows:number, ncols:number):map
AScript_DeclareMethod(Matrix, submat)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "row", VTYPE_Number);
	DeclareArg(env, "col", VTYPE_Number);
	DeclareArg(env, "nrows", VTYPE_Number);
	DeclareArg(env, "ncols", VTYPE_Number);
	SetHelp(
	"Returns a sub matrix that refers to cells in a specified area of the matrix.\n"
	"Modification on the returned sub matrix will affect on the original one.");
}

AScript_ImplementMethod(Matrix, submat)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	return pSelf->GetSub(args.GetSizeT(0), args.GetSizeT(1),
						args.GetSizeT(2), args.GetSizeT(3));
}

// matrix#row(row:number):map
AScript_DeclareMethod(Matrix, row)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "row", VTYPE_Number);
	SetHelp(
	"Returns a list of values copied from a specified row of the matrix.\n"
	"Modification on the returned sub matrix will affect on the original one.");
}

AScript_ImplementMethod(Matrix, row)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	return pSelf->GetRow(env, sig, args.GetSizeT(0));
}

// matrix#col(col:number):map
AScript_DeclareMethod(Matrix, col)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "col", VTYPE_Number);
	SetHelp(
	"Returns a list of values copied from a specified column of the matrix.\n"
	"Modification on the returned sub matrix will affect on the original one.");
}

AScript_ImplementMethod(Matrix, col)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	return pSelf->GetCol(env, sig, args.GetSizeT(0));
}

// matrix#each():[transpose]
AScript_DeclareMethod(Matrix, each)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_Symbol(transpose));
	SetHelp(
	"Returns an iterator that picks up each cell by scanning the matrix.\n"
	"In default, that scan is done in a horizontal direction.\n"
	"When an attribute :transpose is specified, it's done in a vertical direction.");
}

AScript_ImplementMethod(Matrix, each)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	Object_Matrix *pObj = Object_Matrix::Reference(pSelf);
	Iterator *pIterator = new Object_Matrix::IteratorEach(pObj,
								args.IsSet(AScript_Symbol(transpose)));
	return ReturnIterator(env, sig, args, pIterator);
}

// matrix#eachrow()
AScript_DeclareMethod(Matrix, eachrow)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp(
	"Returns an iterator that generates lists of values copied from each row\n"
	"of the matrix.\n");
}

AScript_ImplementMethod(Matrix, eachrow)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	Object_Matrix *pObj = Object_Matrix::Reference(pSelf);
	return ReturnIterator(env, sig, args,
							new Object_Matrix::IteratorEachRow(pObj));
}

// matrix#eachcol()
AScript_DeclareMethod(Matrix, eachcol)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp(
	"Returns an iterator that generates lists of values copied from each column\n"
	"of the matrix.\n");
}

AScript_ImplementMethod(Matrix, eachcol)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	Object_Matrix *pObj = Object_Matrix::Reference(pSelf);
	return ReturnIterator(env, sig, args,
							new Object_Matrix::IteratorEachCol(pObj));
}

// matrix#tolist():[transpose,flat]
AScript_DeclareMethod(Matrix, tolist)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_Symbol(transpose));
	DeclareAttr(AScript_Symbol(flat));
	SetHelp(
	"Converts the matrix into a list containing sub-lists that represents its rows.\n"
	"If :transpose attribute is specified, each sub-list contains values of\n"
	"corresponding column. If :flat attribute is specified, it generates\n"
	"one-dimentional list.\n"
	"Example:\n"
	">>> @@{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}.tolist()\n"
	"[[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n"
	">>> @@{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}.tolist():transpose\n"
	"[[1, 4, 7], [2, 5, 8], [3, 6, 9]]");
}

AScript_ImplementMethod(Matrix, tolist)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	Value result;
	ValueList &valList = result.InitAsList(env);
	pSelf->ToList(valList,
			args.IsSet(AScript_Symbol(transpose)),
			args.IsSet(AScript_Symbol(flat)));
	return result;
}

// matrix#transpose()
AScript_DeclareMethod(Matrix, transpose)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns a transpose matrix.");
}

AScript_ImplementMethod(Matrix, transpose)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	if (sig.IsSignalled()) return Value::Null;
	return pSelf->Transpose(env, sig);
}

// matrix#inverse()
AScript_DeclareMethod(Matrix, inverse)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns an inverse matrix.");
}

AScript_ImplementMethod(Matrix, inverse)
{
	Object_Matrix *pSelf = Object_Matrix::GetSelfObj(args);
	if (sig.IsSignalled()) return Value::Null;
	return pSelf->Inverse(env, sig);
}

// Assignment
Class_Matrix::Class_Matrix(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Matrix, set);
	AScript_AssignMethod(Matrix, setrow);
	AScript_AssignMethod(Matrix, setcol);
	AScript_AssignMethod(Matrix, rowsize);
	AScript_AssignMethod(Matrix, colsize);
	AScript_AssignMethod(Matrix, issquare);
	AScript_AssignMethod(Matrix, submat);
	AScript_AssignMethod(Matrix, row);
	AScript_AssignMethod(Matrix, col);
	AScript_AssignMethod(Matrix, each);
	AScript_AssignMethod(Matrix, eachrow);
	AScript_AssignMethod(Matrix, eachcol);
	AScript_AssignMethod(Matrix, tolist);
	AScript_AssignMethod(Matrix, transpose);
	AScript_AssignMethod(Matrix, inverse);
}

Object *Class_Matrix::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	return NULL;
}

}
