//
// Object_List
//

#include "ascript/Object_List.h"
#include "ascript/Object_Matrix.h"
#include "ascript/Object_Function.h"
#include "ascript/Object_Binary.h"
#include "ascript/Expr.h"
#include "ascript/Formatter.h"
#include "ascript/combination.hpp"

//-----------------------------------------------------------------------------
// extra
//-----------------------------------------------------------------------------
namespace std {

// extracted from combination.hpp which is supposed to be used with boost
template <class BidirectionalIterator>
  bool next_partial_permutation(BidirectionalIterator first,
                                BidirectionalIterator middle,
                                BidirectionalIterator last)
{
  reverse (middle, last);
  return next_permutation(first, last);
}

}

namespace AScript {

//-----------------------------------------------------------------------------
// Object_List
//-----------------------------------------------------------------------------
Object *Object_List::Clone() const
{
	return new Object_List(*this);
}

Value Object_List::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 (idx >= GetList().size()) {
		sig.SetError(ERR_IndexError, "index is out of range");
		return Value::Null;
	}
	return GetList()[idx];
}

void Object_List::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 (idx >= GetList().size()) {
		sig.SetError(ERR_IndexError, "index is out of range");
		return;
	}
	GetList()[idx] = value;
}

Iterator *Object_List::CreateIterator(Signal sig)
{
	return new IteratorEach(Object_List::IncRef(this));
}

String Object_List::ToString(Signal sig, bool exprFlag)
{
	bool limitLenFlag = true;
	String str;
	const ValueList &valList = GetList();
	str += "[";
	foreach_const (ValueList, pValue, valList) {
		if (pValue != valList.begin()) str += ", ";
		if (limitLenFlag && str.size() > 4096) {
			str += "...";
			break;
		}
		str += pValue->ToString(sig);
	}
	str += "]";
	return str;
}

Object_List *Object_List::SortRank(Signal sig, const Value &valDirective,
					const ValueList *pValListKey, bool rankFlag, bool stableFlag)
{
	typedef std::map<const Value *, const Value *> ValuePtrMap;
	Environment &env = *this;
	enum { MODE_Ascend, MODE_Descend, MODE_Custom } mode = MODE_Ascend;
	const Function *pFunc = NULL;
	const ValueList &valList = GetList();
	if (valDirective.IsInvalid()) {
		// nothing to do
	} else if (valDirective.IsSymbol()) {
		const Symbol *pSymbol = valDirective.GetSymbol();
		if (pSymbol->IsIdentical(AScript_Symbol(ascend))) {
			mode = MODE_Ascend;
		} else if (pSymbol->IsIdentical(AScript_Symbol(descend))) {
			mode = MODE_Descend;
		} else {
			sig.SetError(ERR_ValueError,
				"invalid symbol '%s'", pSymbol->GetName());
			return NULL;
		}
	} else if (valDirective.IsFunction()) {
		mode = MODE_Custom;
		pFunc = valDirective.GetFunction();
		if (pFunc->GetDeclOwner().size() != 2) {
			sig.SetError(ERR_TypeError,
				"only a binary function can be specified");
			return NULL;
		}
	} else {
		sig.SetError(ERR_TypeError, "invalid argument");
		return NULL;
	}
	ValuePtrList valPtrList;
	ValuePtrMap valuePtrMap;
	if (rankFlag || pValListKey == NULL) {
		foreach_const (ValueList, pValue, valList) {
			valPtrList.push_back(&(*pValue));
		}
	} else {
		ValueList::const_iterator pValueKey = pValListKey->begin();
		ValueList::const_iterator pValue = valList.begin();
		for ( ; pValueKey != pValListKey->end() && pValue != valList.end();
														pValueKey++, pValue++) {
			valPtrList.push_back(&(*pValueKey));
			valuePtrMap[&(*pValueKey)] = &(*pValue);
		}
	}
	if (stableFlag) {
		if (mode == MODE_Ascend) {
			std::stable_sort(valPtrList.begin(), valPtrList.end(), Comparator_Ascend());
		} else if (mode == MODE_Descend) {
			std::stable_sort(valPtrList.begin(), valPtrList.end(), Comparator_Descend());
		} else { // mode == MODE_Custom
			std::stable_sort(valPtrList.begin(), valPtrList.end(),
											Comparator_Custom(env, sig, pFunc));
		}
	} else {
		if (mode == MODE_Ascend) {
			std::sort(valPtrList.begin(), valPtrList.end(), Comparator_Ascend());
		} else if (mode == MODE_Descend) {
			std::sort(valPtrList.begin(), valPtrList.end(), Comparator_Descend());
		} else { // mode == MODE_Custom
			std::sort(valPtrList.begin(), valPtrList.end(),
											Comparator_Custom(env, sig, pFunc));
		}
	}
	Value result;
	ValueList &valListResult = result.InitAsList(env);
	if (rankFlag) {
		foreach_const (ValueList, pValue, valList) {
			ValuePtrList::iterator ppValue = valPtrList.begin();
			for ( ; ppValue != valPtrList.end(); ppValue++) {
				if (Value::Compare(*pValue, **ppValue) == 0) break;
			}
			if (ppValue == valPtrList.end()) {
				sig.SetError(ERR_SystemError, "fatal error in rank() operation");
				return NULL;
			} else {
				int idx = static_cast<int>(ppValue - valPtrList.begin());
				valListResult.push_back(Value(idx));
			}
		}
	} else if (pValListKey == NULL) {
		foreach_const (ValuePtrList, ppValue, valPtrList) {
			valListResult.push_back(**ppValue);
		}
	} else {
		foreach_const (ValuePtrList, ppValueKey, valPtrList) {
			const Value *pValueKey = *ppValueKey;
			valListResult.push_back(*valuePtrMap[pValueKey]);
		}
	}
	return Object_List::IncRef(result.GetListObj());
}

void Object_List::ValueVisitor_Index::Visit(Signal sig, const Value &value)
{
	ASSUME(_env, value.IsNumber());
	size_t idx = value.GetSizeT();
	if (std::find(_indexList.begin(), _indexList.end(), idx) != _indexList.end()) {
		// nothing to do
	} else if (idx < _valList.size()) {
		_indexList.push_back(idx);
	} else {
		sig.SetError(ERR_IndexError, "index is out of range");
	}
}

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

bool Object_List::IteratorEach::DoNext(Environment &env, Signal sig, Value &value)
{
	ValueList &valList = _pObj->GetList();
	if (_pValue == _pValueEnd) return false;
	value = *_pValue;
	_pValue++;
	return true;
}

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

//-----------------------------------------------------------------------------
// Object_List::IteratorReverse
//-----------------------------------------------------------------------------
Object_List::IteratorReverse::~IteratorReverse()
{
	Object::Delete(_pObj);
}

bool Object_List::IteratorReverse::DoNext(Environment &env, Signal sig, Value &value)
{
	ValueList &valList = _pObj->GetList();
	if (_pValue == valList.rend()) return false;
	value = *_pValue;
	_pValue++;
	return true;
}

String Object_List::IteratorReverse::ToString(Signal sig) const
{
	return String("<iterator:list#reverse>");
}

//-----------------------------------------------------------------------------
// Object_List::IteratorRound
//-----------------------------------------------------------------------------
Object_List::IteratorRound::~IteratorRound()
{
	Object::Delete(_pObj);
}

bool Object_List::IteratorRound::DoNext(Environment &env, Signal sig, Value &value)
{
	ValueList &valList = _pObj->GetList();
	if (_pValue == valList.end() || _cnt == 0) return false;
	value = *_pValue;
	_pValue++;
	if (_cnt > 0) _cnt--;
	if (_pValue == valList.end()) _pValue = valList.begin();
	return true;
}

String Object_List::IteratorRound::ToString(Signal sig) const
{
	return String("<iterator:list#round>");
}

//-----------------------------------------------------------------------------
// Object_List::IteratorPingpong
//-----------------------------------------------------------------------------
Object_List::IteratorPingpong::~IteratorPingpong()
{
	Object::Delete(_pObj);
}

bool Object_List::IteratorPingpong::DoNext(Environment &env, Signal sig, Value &value)
{
	ValueList &valList = _pObj->GetList();
	if (_forwardFlag) {
		if (_pValueFwd == valList.end() || _cnt == 0) return false;
		value = *_pValueFwd;
		_pValueFwd++;
		if (_cnt > 0) _cnt--;
		if (_pValueFwd == valList.end() ||
				(!_stickyFlagR && _pValueFwd + 1 == valList.end())) {
			_forwardFlag = false;
			_pValueBwd = valList.rbegin();
		}
	} else {
		if (_pValueBwd == valList.rend() || _cnt == 0) return false;
		value = *_pValueBwd;
		_pValueBwd++;
		if (_cnt > 0) _cnt--;
		if (_pValueBwd == valList.rend() ||
				(!_stickyFlagL && _pValueBwd + 1 == valList.rend())) {
			_forwardFlag = true;
			_pValueFwd = valList.begin();
		}
	}
	return true;
}

String Object_List::IteratorPingpong::ToString(Signal sig) const
{
	return String("<iterator:list#pingpong>");
}

//-----------------------------------------------------------------------------
// Object_List::IteratorFold
//-----------------------------------------------------------------------------
Object_List::IteratorFold::~IteratorFold()
{
	Object::Delete(_pObj);
}

bool Object_List::IteratorFold::DoNext(Environment &env, Signal sig, Value &value)
{
	ValueList &valList = _pObj->GetList();
	if (_offset >= valList.size()) return false;
	Iterator *pIterator = new IteratorEach(
						Object_List::IncRef(_pObj), _offset, _cntPerFold);
	if (_listItemFlag) {
		bool excludeNilFlag = false;
		value = pIterator->ToList(env, sig, true, excludeNilFlag);
		Iterator::Delete(pIterator);
	} else {
		value.InitAsIterator(env, pIterator);
	}
	_offset += _cntStep;
	return true;
}

String Object_List::IteratorFold::ToString(Signal sig) const
{
	return String("<iterator:list#fold>");
}

//-----------------------------------------------------------------------------
// Object_List::IteratorPermutation
//-----------------------------------------------------------------------------
Object_List::IteratorPermutation::IteratorPermutation(Object_List *pObj, int cnt) :
							Iterator(false), _pObj(pObj), _cnt(cnt), _validFlag(true)
{
	ValueList &valList = _pObj->GetList();
	_indexList.reserve(valList.size());
	for (size_t index = 0; index < valList.size(); index++) {
		_indexList.push_back(index);
	}
}

Object_List::IteratorPermutation::~IteratorPermutation()
{
	Object::Delete(_pObj);
}

bool Object_List::IteratorPermutation::DoNext(Environment &env, Signal sig, Value &value)
{
	if (!_validFlag) return false;
	ValueList &valList = value.InitAsList(*_pObj);
	ValueList &valListSrc = _pObj->GetList();
	if (_cnt < 0) {
		foreach (IndexList, pIndex, _indexList) {
			valList.push_back(valListSrc[*pIndex]);
		}
		_validFlag = std::next_permutation(_indexList.begin(), _indexList.end());
	} else {
		IndexList::iterator pIndex = _indexList.begin();
		for (int i = 0; i < _cnt; pIndex++, i++) {
			valList.push_back(valListSrc[*pIndex]);
		}
		_validFlag = std::next_partial_permutation(
				_indexList.begin(), _indexList.begin() + _cnt, _indexList.end());
	}
	return true;
}

String Object_List::IteratorPermutation::ToString(Signal sig) const
{
	return String("<iterator:list#permutation>");
}

//-----------------------------------------------------------------------------
// Object_List::IteratorCombination
//-----------------------------------------------------------------------------
Object_List::IteratorCombination::IteratorCombination(Object_List *pObj, int cnt) :
							Iterator(false), _pObj(pObj), _cnt(cnt), _validFlag(true)
{
	ValueList &valList = _pObj->GetList();
	_indexList.reserve(valList.size());
	for (size_t index = 0; index < valList.size(); index++) {
		_indexList.push_back(index);
	}
}

Object_List::IteratorCombination::~IteratorCombination()
{
	Object::Delete(_pObj);
}

bool Object_List::IteratorCombination::DoNext(Environment &env, Signal sig, Value &value)
{
	if (!_validFlag) return false;
	ValueList &valList = value.InitAsList(*_pObj);
	ValueList &valListSrc = _pObj->GetList();
	IndexList::iterator pIndex = _indexList.begin();
	for (int i = 0; i < _cnt; pIndex++, i++) {
		valList.push_back(valListSrc[*pIndex]);
	}
	// the following function is implemented in combination.hpp
	_validFlag = boost::next_combination(
			_indexList.begin(), _indexList.begin() + _cnt, _indexList.end());
	return true;
}

String Object_List::IteratorCombination::ToString(Signal sig) const
{
	return String("<iterator:list#combination>");
}

//-----------------------------------------------------------------------------
// Object_List::Comparator_Custom
//-----------------------------------------------------------------------------
bool Object_List::Comparator_Custom::
				operator()(const Value *pValue1, const Value *pValue2) const
{
	if (_sig.IsSignalled()) return false;
	ValueList valListArg(*pValue1, *pValue2);
	Args argsSub(valListArg);
	Value value = _pFunc->Eval(_env, _sig, argsSub);
	return value.GetNumber() < 0;
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_List
//-----------------------------------------------------------------------------
// list#clear():reduce
AScript_DeclareMethod(List, clear)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	SetHelp("Clear the content of the list.");
}

AScript_ImplementMethod(List, clear)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	pSelf->GetList().clear();
	return args.GetSelf();
}

// list#isempty()
AScript_DeclareMethod(List, isempty)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Return true if the list is empty.");
}

AScript_ImplementMethod(List, isempty)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	return Value(pSelf->GetList().empty());
}

// list#shuffle():reduce
AScript_DeclareMethod(List, shuffle)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	SetHelp("Shuffle the order of the list content based on random numbers.");
}

AScript_ImplementMethod(List, shuffle)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	RandomGenerator randomGenerator;
	std::random_shuffle(valList.begin(), valList.end(), randomGenerator);
	return args.GetSelf();
}

// list#add(elem+):reduce
AScript_DeclareMethod(List, add)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "elem", VTYPE_Any, OCCUR_OnceOrMore);
	SetHelp("Add specified items to the list.");
}

AScript_ImplementMethod(List, add)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	foreach_const (ValueList, pValue, args.GetList(0)) {
		valList.push_back(*pValue);
	}
	return args.GetSelf();
}

// list#append(elem+):reduce
AScript_DeclareMethod(List, append)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "elem", VTYPE_Any, OCCUR_OnceOrMore);
	SetHelp(
	"Adds specified items to the list. If the item is a list or an iterator,\n"
	"each element in such an item is added to the list.");
}

AScript_ImplementMethod(List, append)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	foreach_const (ValueList, pValue, args.GetList(0)) {
		if (pValue->IsList() || pValue->IsIterator()) {
			Iterator *pIterator = pValue->CreateIterator(sig);
			if (sig.IsSignalled()) return Value::Null;
			Value value;
			while (pIterator->Next(env, sig, value)) {
				valList.push_back(value);
			}
			Iterator::Delete(pIterator);
			if (sig.IsSignalled()) return Value::Null;
		} else {
			valList.push_back(*pValue);
		}
	}
	return args.GetSelf();
}

// list#shift():[raise]
AScript_DeclareMethod(List, shift)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_Symbol(raise));
	SetHelp(
	"Shifts the elements of the list. If the content of the list is [1, 2, 3, 4],\n"
	"it becomes [2, 3, 4] after calling this method. In default, no error occurs\n"
	"even when the list is empty. To raise an error for executing this method on\n"
	"an empty list, specify :raise attribute.");
}

AScript_ImplementMethod(List, shift)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	if (valList.empty()) {
		if (args.IsSet(AScript_Symbol(raise))) {
			sig.SetError(ERR_ValueError, "no items");
		}
		return Value::Null;
	}
	Value result = valList.front();
	valList.erase(valList.begin());
	return result;
}

// list#erase(idx*:number):reduce
AScript_DeclareMethod(List, erase)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "idx", VTYPE_Number, OCCUR_ZeroOrMore);
	SetHelp("Erases elements at the specified indices.");
}

AScript_ImplementMethod(List, erase)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	Object_List::ValueVisitor_Index visitor(env, valList);
	foreach_const (ValueList, pValue, args.GetList(0)) {
		pValue->Accept(sig, visitor);
	}
	Object_List::IndexList &indexList = visitor.GetIndexList();
	if (!indexList.empty()) {
		std::sort(indexList.begin(), indexList.end());
		size_t offset = 0;
		foreach_const (Object_List::IndexList, pIdx, indexList) {
			size_t idx = *pIdx;
			valList.erase(valList.begin() + idx - offset);
			offset++;
		}
	}
	return args.GetSelf();
}

// list#get(index:number):map:flat
AScript_DeclareMethod(List, get)
{
	SetMode(RSLTMODE_Normal, FLAG_Map | FLAG_Flat);
	DeclareArg(env, "index", VTYPE_Number);
	SetHelp(
	"Returns a value stored at the specified index in the list.\n"
	"An error occurs when the index is out of range.");
}

AScript_ImplementMethod(List, get)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	return pSelf->IndexGet(sig, args.GetValue(0));
}

// list#first()
AScript_DeclareMethod(List, first)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp(
	"Returns a first value in the list. An error occurs when the list is empty.");
}

AScript_ImplementMethod(List, first)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	if (valList.empty()) {
		sig.SetError(ERR_ValueError, "list is empty");
		return Value::Null;
	}
	return valList.front();
}

// list#last()
AScript_DeclareMethod(List, last)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp(
	"Returns a last value in the list. An error occurs when the list is empty.");
}

AScript_ImplementMethod(List, last)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	if (valList.empty()) {
		sig.SetError(ERR_ValueError, "list is empty");
		return Value::Null;
	}
	return valList.back();
}

// list#permutation(n?:number) {block?}
AScript_DeclareMethod(List, permutation)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator that generates lists that contain elements picked up\n"
	"from the original list in a permutation manner.\n"
	ITERATOR_HELP
	"Block parameter format: |value:list, idx:number|");
}

AScript_ImplementMethod(List, permutation)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	int cnt = args.IsNumber(0)? args.GetInt(0) : -1;
	if (cnt > 0 && pSelf->GetList().size() < static_cast<size_t>(cnt)) {
		sig.SetError(ERR_ValueError, "specified size is out of range");
		return Value::Null;
	}
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorPermutation(pObj, cnt);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#combination(n:number) {block?}
AScript_DeclareMethod(List, combination)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator that generates lists that contain elements picked up\n"
	"from the original list in a combination manner.\n"
	ITERATOR_HELP
	"Block parameter format: |value:list, idx:number|");
}

AScript_ImplementMethod(List, combination)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	int cnt = args.GetInt(0);
	if (pSelf->GetList().size() < static_cast<size_t>(cnt)) {
		sig.SetError(ERR_ValueError, "specified size is out of range");
		return Value::Null;
	}
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorCombination(pObj, cnt);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#flat()
AScript_DeclareMethod(List, flat)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns the flattened list.");
}

AScript_ImplementMethod(List, flat)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Value result;
	ValueList &valList = result.InitAsList(env);
	pSelf->GetList().ExtractFlat(valList);
	return result;
}

// list#printf(format:string):void
AScript_DeclareMethod(List, printf)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	SetHelp("Prints items in the list by using the format.");
}

AScript_ImplementMethod(List, printf)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Stream *pConsole = env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	pConsole->Printf(sig, args.GetString(0), pSelf->GetList());
	return Value::Null;
}

// list#len()
AScript_DeclareMethod(List, len)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns the length of the list.");
}

AScript_ImplementMethod(List, len)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	size_t cnt = pSelf->GetList().size();
	return Value(static_cast<unsigned int>(cnt));
}

// list#min():[index,last_index,indices]
AScript_DeclareMethod(List, min)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_Symbol(index));
	DeclareAttr(AScript_Symbol(last_index));
	DeclareAttr(AScript_Symbol(indices));
	SetHelp(
	"Returns the minimum value in the list when no attribute is specified.\n"
	"With an attribute :index, it returns an index of the minimum value.\n"
	"With an attribute :last_index, it returns the last index of the minimum value\n"
	"when more than one elements have the same value.\n"
	"With an attribute :indices, it returns a list of indices of elements that\n"
	"has the minimum value.");
}

AScript_ImplementMethod(List, min)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pIterator->MinMax(env, sig, false, args.GetAttrs());
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#max():[index,last_index,indices]
AScript_DeclareMethod(List, max)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_Symbol(index));
	DeclareAttr(AScript_Symbol(last_index));
	DeclareAttr(AScript_Symbol(indices));
	SetHelp(
	"Returns the maximum value in the list when no attribute is specified.\n"
	"With an attribute :index, it returns an index of the maximum value.\n"
	"With an attribute :last_index, it returns the last index of the maximum value\n"
	"when more than one elements have the same value.\n"
	"With an attribute :indices, it returns a list of indices of elements that\n"
	"has the maximum value.");
}

AScript_ImplementMethod(List, max)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pIterator->MinMax(env, sig, true, args.GetAttrs());
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#sum()
AScript_DeclareMethod(List, sum)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns a sum of values in the list.");
}

AScript_ImplementMethod(List, sum)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->Sum(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#average()
AScript_DeclareMethod(List, average)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns an average of values in the list.");
}

AScript_ImplementMethod(List, average)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->Average(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#variance()
AScript_DeclareMethod(List, variance)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns a variance of values in the list.");
}

AScript_ImplementMethod(List, variance)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->Variance(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#stddev()
AScript_DeclareMethod(List, stddev)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns a standard deviation of values in the list.");
}

AScript_ImplementMethod(List, stddev)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->StandardDeviation(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#and()
AScript_DeclareMethodAlias(List, and_, "and")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp(
	"Calculates a logical AND result of all the values in the list.\n"
	"Values of boolean type's false and nil are recognized as false\n"
	"while others are true.");
}

AScript_ImplementMethod(List, and_)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pIterator->And(env, sig);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#or()
AScript_DeclareMethodAlias(List, or_, "or")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp(
	"Calculates a logical OR result of all the values in the list.\n"
	"Values of boolean type's false and nil are recognized as false\n"
	"while others are true.");
}

AScript_ImplementMethod(List, or_)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pIterator->Or(env, sig);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#iscontain(value)
AScript_DeclareMethod(List, iscontain)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Any);
	SetHelp(
	"Returns true if a specified value exists in the list.");
}

AScript_ImplementMethod(List, iscontain)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	bool result = pIterator->IsContain(env, sig, args.GetValue(0));
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return Value(result);
}

// list#filter(criteria) {block?}
AScript_DeclareMethod(List, filter)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Returns a list that consists of elements of the original list after filtering\n"
	"by a criteria. A criteria can be an iterator or a function object.\n"
	"When an iterator is supplied as a criteria, it picks up true value in the iterator\n"
	"and creates a list that contains elements at corresponding position in the original list\n"
	"When a function object is applied, each element is passed to the function\n"
	"as an argument, and it collects values with evaluated results being true.");
}

AScript_ImplementMethod(List, filter)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = pIteratorSrc->Filter(env, sig, args.GetValue(0));
	if (sig.IsSignalled()) {
		Iterator::Delete(pIteratorSrc);
		return Value::Null;
	}
	return ReturnIterator(env, sig, args, pIterator);
}

// list#while(criteria) {block?}
AScript_DeclareMethodAlias(List, while_, "while")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator that picks up each element in the list while criteria\n"
	"is evaluated as true. You can specify a function object, a list or an iterator\n"
	"as the criteria.\n"
	ITERATOR_HELP
	"Block parameter format: |value:list, idx:number|");
}

AScript_ImplementMethod(List, while_)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = pIteratorSrc->While(env, sig, args.GetValue(0));
	if (sig.IsSignalled()) {
		Iterator::Delete(pIteratorSrc);
		return Value::Null;
	}
	return ReturnIterator(env, sig, args, pIterator);
}

// list#since(criteria) {block?}
AScript_DeclareMethod(List, since)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator that picks up each element in the list since criteria\n"
	"is evaluated as true. You can specify a function object, a list or an iterator\n"
	"as the criteria.\n"
	ITERATOR_HELP
	"Block parameter format: |value:list, idx:number|");
}

AScript_ImplementMethod(List, since)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = pIteratorSrc->Since(env, sig, args.GetValue(0));
	if (sig.IsSignalled()) {
		Iterator::Delete(pIteratorSrc);
		return Value::Null;
	}
	return ReturnIterator(env, sig, args, pIterator);
}

// list#map(func:function) {block?}
AScript_DeclareMethod(List, map)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "func", VTYPE_Function);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator that generates element values after applying the specfied\n"
	"function on them. The function must take one argument.\n"
	ITERATOR_HELP
	"Block parameter format: |value, idx:number|");
}

AScript_ImplementMethod(List, map)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = new Iterator_ExplicitMap(env, pIteratorSrc,
						Object_Function::Reference(args.GetFunctionObj(0)));
	return ReturnIterator(env, sig, args, pIterator);
}

// list#reduce(accum) {block}
AScript_DeclareMethod(List, reduce)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "accum", VTYPE_Any);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Evaluates a block with a parameter format |value, accum| and leaves the result\n"
	"as the next accum value. It returns the final accum value as its result.");
}

AScript_ImplementMethod(List, reduce)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
	if (pFuncBlock == NULL) {
		Iterator::Delete(pIterator);
		return Value::Null;
	}
	Value result = pIterator->Reduce(env, sig, args.GetValue(0), pFuncBlock);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#count(criteria)
AScript_DeclareMethod(List, count)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
	SetHelp(
	"Returns a number of elements that matches the given criteria which is a single-argument\n"
	"function or a value. When a function is applied, it counts the number of true after\n"
	"evaluating element value with the function. If a value is applied, it counts the number\n"
	"of elements that are equal to the value.");
}

AScript_ImplementMethod(List, count)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt = pIterator->Count(env, sig, args.GetValue(0));
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return Value(static_cast<unsigned int>(cnt));
}

// list#sort(directive?, keys[]?):[stable]
AScript_DeclareMethod(List, sort)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "directive", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareArg(env, "keys", VTYPE_Any, OCCUR_ZeroOrOnce, true);
	DeclareAttr(AScript_Symbol(stable));
	SetHelp(
	"Returns a list of elements after sorting them.\n"
	"In default, they are sorted in an ascending order. You can specify the following\n"
	"directives for sorting.\n"
	" `ascend:  ascending order\n"
	" `descend: descending order\n"
	" function  it takes two element values x and y and returns zero for x == y,\n"
	"           plus value for x < y and minus value for x > y.\n"
	"If keys is specified, it shall be used as a key instead of element values.\n"
	"When an attribute :stable is specified, the original order shall be kept for\n"
	"elements that are determined as the same.");
}

AScript_ImplementMethod(List, sort)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = pSelf->SortRank(sig, args.GetValue(0),
						args.IsList(1)? &args.GetList(1) : NULL,
						false, args.IsSet(AScript_Symbol(stable)));
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#rank(directive?):[stable]
AScript_DeclareMethod(List, rank)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "directive", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(stable));
	SetHelp(
	"Returns a list of rank numbers for elements after sorting them.\n"
	"In default, they are sorted in an ascending order. You can specify the following\n"
	"directives for sorting.\n"
	" `ascend:  ascending order\n"
	" `descend: descending order\n"
	" function  it takes two element values x and y and returns zero for x == y,\n"
	"           plus value for x < y and minus value for x > y.\n"
	"When an attribute :stable is specified, the original order shall be kept for\n"
	"elements that are determined as the same.");
}

AScript_ImplementMethod(List, rank)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = pSelf->SortRank(sig, args.GetValue(0), NULL,
							true, args.IsSet(AScript_Symbol(stable)));
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#join(sep:string => "")
AScript_DeclareMethod(List, join)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "sep", VTYPE_String, OCCUR_Once, false, false, new Expr_String(""));
	SetHelp(
	"Returns a string that joins strings of elements with the specified separator.");
}

AScript_ImplementMethod(List, join)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	ValueList &valList = pSelf->GetList();
	return Value(env, Join(valList, args.GetString(0)).c_str());
}

// list#format(format:string):map
AScript_DeclareMethod(List, format)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "format", VTYPE_String);
	SetHelp(
	"Applies element values in the list to format string that contains C printf"
	"specifiers	and returns a formatted string.");
}

AScript_ImplementMethod(List, format)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	return Value(env, Formatter::Format(sig,
						args.GetString(0), pSelf->GetList()).c_str());
}

// list#pack(format:string)
AScript_DeclareMethod(List, pack)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	SetHelp(
	"Returns a binary object packing elements values according to a format string.\n"
	"Following markers specify the order of how a multi-byte number is stored.\n"
	" @  sets as a native order for the current platform\n"
	" =  sets as a native order for the current platform\n"
	" <  sets as a little-endian order\n"
	" >  sets as a big-endian order\n"
	" !  sets as a big-endian order\n"
	"Following markers specify a storing format. They can be preceded by a number\n"
	"that specifies the number of values.\n"
	" x  just skips one byte\n"
	" c  takes a string that contains one character and stores it as a byte value\n"
	" b  stores a number as a signed byte value\n"
	" B  stores a number as an unsigned byte value\n"
	" h  stores a number as a signed half-word (2 bytes) value\n"
	" H  stores a number as an unsigned half-word (2 bytes) value\n"
	" i  stores a number as a signed integer (4 bytes) value\n"
	" I  stores a number as an unsigned integer (4 bytes) value\n"
	" l  stores a number as a signed integer (4 bytes) value\n"
	" L  stores a number as an unsigned integer (4 bytes) value\n"
	" q  stores a number as a signed long integer (8 bytes) value\n"
	" Q  stores a number as an unsigned long integer (8 bytes) value\n"
	" f  stores a number as a float (4 bytes) value\n"
	" d  stores a number as a double (8 bytes) value\n"
	" s  stores a string after character encoding\n");
}

AScript_ImplementMethod(List, pack)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Value result;
	Object_Binary *pObjBinary = result.InitAsBinary(env);
	size_t offset = 0;
	pObjBinary->Pack(sig, offset, args.GetString(0), pSelf->GetList());
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// list#each() {block?}
AScript_DeclareMethod(List, each)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator that iterates each element in the list.\n"
	ITERATOR_HELP
	"Block parameter format: |value, idx:number|");
}

AScript_ImplementMethod(List, each)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#offset(n:number):map {block?}
AScript_DeclareMethod(List, offset)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareBlock(OCCUR_Once);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, offset)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	size_t offset = args.GetSizeT(0);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorEach(pObj, offset);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#align(n:number, value?):map {block?}
AScript_DeclareMethod(List, align)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, align)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	pIterator = new Iterator_Align(pIterator, args.GetInt(0), args.GetValue(1));
	return ReturnIterator(env, sig, args, pIterator);
}

// list#skip(n:number):map {block?}
AScript_DeclareMethod(List, skip)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, skip)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	pIterator = new Iterator_Skip(pIterator, static_cast<int>(args.GetSizeT(0)));
	return ReturnIterator(env, sig, args, pIterator);
}

// list#skipnil() {block?}
AScript_DeclareMethod(List, skipnil)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, skipnil)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	pIterator = new Iterator_SkipInvalid(pIterator);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#nilto(replace) {block?}
AScript_DeclareMethod(List, nilto)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "replace", VTYPE_Any);
}

AScript_ImplementMethod(List, nilto)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	pIterator = new Iterator_ReplaceInvalid(pIterator, args.GetValue(0));
	return ReturnIterator(env, sig, args, pIterator);
}

// list#replace(value, replace) {block?}
AScript_DeclareMethod(List, replace)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Any);
	DeclareArg(env, "replace", VTYPE_Any);
}

AScript_ImplementMethod(List, replace)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	pIterator = new Iterator_Replace(pIterator,
								args.GetValue(0), args.GetValue(1));
	return ReturnIterator(env, sig, args, pIterator);
}

// list#head(n:number):map {block?}
AScript_DeclareMethod(List, head)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, head)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	int cnt = args.GetInt(0);
	Iterator *pIterator = new Object_List::IteratorEach(pObj, 0, cnt);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#tail(n:number):map {block?}
AScript_DeclareMethod(List, tail)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, tail)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	int cnt = args.GetInt(0);
	int cntMax = static_cast<int>(pObj->GetList().size());
	size_t offset = (cntMax > cnt)? cntMax - cnt : cntMax;
	Iterator *pIterator = new Object_List::IteratorEach(pObj, offset);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#reverse() {block?}
AScript_DeclareMethod(List, reverse)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, reverse)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorReverse(pObj);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#round(n?:number) {block?}
AScript_DeclareMethod(List, round)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(List, round)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	int cnt = args.IsNumber(0)? args.GetInt(0) : -1;
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorRound(pObj, cnt);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#pingpong(n?:number):[sticky,sticky_l,sticky_r] {block?}
AScript_DeclareMethod(List, pingpong)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(sticky));
	DeclareAttr(AScript_Symbol(sticky_l));
	DeclareAttr(AScript_Symbol(sticky_r));
}

AScript_ImplementMethod(List, pingpong)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	int cnt = args.IsNumber(0)? args.GetInt(0) : -1;
	bool stickyFlagL = args.IsSet(AScript_Symbol(sticky)) ||
						args.IsSet(AScript_Symbol(sticky_l));
	bool stickyFlagR = args.IsSet(AScript_Symbol(sticky)) ||
						args.IsSet(AScript_Symbol(sticky_r));
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator =
		new Object_List::IteratorPingpong(pObj, cnt, stickyFlagL, stickyFlagR);
	return ReturnIterator(env, sig, args, pIterator);
}

// list#fold(n:number, nstep?:number):[iteritem] {block?}
AScript_DeclareMethod(List, fold)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareArg(env, "nstep", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(iteritem));
}

AScript_ImplementMethod(List, fold)
{
	Object_List *pSelf = Object_List::GetSelfObj(args);
	size_t cnt = args.GetSizeT(0);
	size_t cntStep = args.IsNumber(1)? args.GetSizeT(1) : cnt;
	bool listItemFlag = !args.IsSet(AScript_Symbol(iteritem));
	Object_List *pObj = Object_List::IncRef(pSelf);
	Iterator *pIterator = new Object_List::IteratorFold(pObj, cnt, cntStep, listItemFlag);
	return ReturnIterator(env, sig, args, pIterator);
}

// assignment
Class_List::Class_List(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(List, clear);
	AScript_AssignMethod(List, isempty);
	AScript_AssignMethod(List, shuffle);
	AScript_AssignMethod(List, add);
	AScript_AssignMethod(List, append);
	AScript_AssignMethod(List, shift);
	AScript_AssignMethod(List, erase);
	AScript_AssignMethod(List, get);
	AScript_AssignMethod(List, first);
	AScript_AssignMethod(List, last);
	AScript_AssignMethod(List, permutation);
	AScript_AssignMethod(List, combination);
	AScript_AssignMethod(List, flat);
	AScript_AssignMethod(List, printf);
	// common operations with iterator
	AScript_AssignMethod(List, len);
	AScript_AssignMethod(List, min);
	AScript_AssignMethod(List, max);
	AScript_AssignMethod(List, sum);
	AScript_AssignMethod(List, average);
	AScript_AssignMethod(List, variance);
	AScript_AssignMethod(List, stddev);
	AScript_AssignMethod(List, and_);
	AScript_AssignMethod(List, or_);
	AScript_AssignMethod(List, iscontain);
	AScript_AssignMethod(List, filter);
	AScript_AssignMethod(List, while_);
	AScript_AssignMethod(List, since);
	AScript_AssignMethod(List, map);
	AScript_AssignMethod(List, reduce);
	AScript_AssignMethod(List, count);
	AScript_AssignMethod(List, sort);
	AScript_AssignMethod(List, rank);
	AScript_AssignMethod(List, join);
	AScript_AssignMethod(List, format);
	AScript_AssignMethod(List, pack);
	AScript_AssignMethod(List, each);
	AScript_AssignMethod(List, offset);
	AScript_AssignMethod(List, align);
	AScript_AssignMethod(List, skip);
	AScript_AssignMethod(List, skipnil);
	AScript_AssignMethod(List, nilto);
	AScript_AssignMethod(List, replace);
	AScript_AssignMethod(List, head);
	AScript_AssignMethod(List, tail);
	AScript_AssignMethod(List, reverse);
	AScript_AssignMethod(List, round);
	AScript_AssignMethod(List, pingpong);
	AScript_AssignMethod(List, fold);
}

bool Class_List::CastFrom(Environment &env, Signal sig, Value &value)
{
	if (value.IsType(VTYPE_Nil)) {
		value.InitAsList(env);
		return true;
	} else if (value.IsIterator()) {
		Iterator *pIterator = value.CreateIterator(sig);
		if (sig.IsSignalled()) return false;
		Value result = pIterator->ToList(env, sig, true, false);
		Iterator::Delete(pIterator);
		if (sig.IsSignalled()) return false;
		value = result;
		return true;
	} else if (value.IsMatrix()) {
		const Object_Matrix *pObjMat = value.GetMatrixObj();
		if (pObjMat->RowSize() == 1) {
			Value result = pObjMat->GetRow(env, sig, 0);
			value = result;
		} else if (pObjMat->ColSize() == 1) {
			Value result = pObjMat->GetCol(env, sig, 0);
			value = result;
		}
		return true;
	}
	return false;
}

Object *Class_List::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	return (pClass == NULL)? new Object_List(env) : new Object_List(pClass);
}

}
