# AVSLib :: 'array' package :: 'slices' module
# Copyright (c) 2005 George Zarkadas (gzarkadas@users.sourceforge.net)

# This program 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 2 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
# PURPOSE.  See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with this program; 
# if not, write to the "Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
# MA  02111-1307  USA"

DefineModule("avslib", "array", "slices")

global __load__ = Eval("""
	LoadModule("avslib", "base", "core")
	LoadModule("avslib", "string", "core")
	LoadModule("avslib", "array", "core")
	""")

# Returns a sub-array of array consisting of elements with index 
# [start_index..end_index), ie array[end_index] is *not* included.
# 'start_index' defaults to 0, 'end_index' to ArrayLen(array), 
# ie. ArrayGetRange(array) returns the entire array.
# if 'start_index' == 'end_index' a zero-length array ("") is returned.
#
Function ArrayGetRange(string array, int "start_index", int "end_index")
{
	# accept ArrayLen(array) for start index 
	# but only for the case start==end
	sta_ind = __bound_array_index(array, start_index, extend=true)
	end_ind = __bound_array_index(array, end_index, tail=true, extend=true)
	Assert(sta_ind <= end_ind, \
		"ArrayGetRange: processed 'end_index' must be greater or equal to 'start_index'")
	arrlen = array.ArrayLen()
	lstart = sta_ind < arrlen \
		? __array_index_pos(array, sta_ind) \
		: StrLen(array) + 1
	# handle fisrt the case end==start
	# in all other cases lend points to next character after the end
	# of the substring, which is what we want
	lend = (end_ind == sta_ind) \
		? lstart \
		: ((end_ind < arrlen) \
			? __array_index_pos(array, end_ind) \
				- ((end_ind > 0) ? __ARRAY_LEN : 0) \
			: StrLen(array) + 1)
	return __mid_str(array, lstart, lend - lstart)
}

# Returns array with elements of index [start_index..end_index) set to 
# the values of 'newval_array'.
# 'newval_array' length may be different than the length of the range that 
# it is set (in that case, array changes its length).
# Thus effectively ArraySetRange has also the role of 'ArrayReplaceRange'.
# 'start_index' defaults to 0, 'end_index' to ArrayLen(array), ie. 
# ArraySetRange(array, new_array) replaces array with new_array.
# NOTE: If you want to set a single new value convert it to string by using ArrayCreate.
#
Function ArraySetRange(string array, string newval_array, \
	int "start_index", int "end_index")
{ 
	# accept ArrayLen(array) for start index but only for the case start==end
	sta_ind = __bound_array_index(array, start_index, extend=true)
	end_ind = __bound_array_index(array, end_index, tail=true, extend=true)
	Assert(sta_ind <= end_ind, \
		"ArraySetRange: processed 'end_index' must be greater or equal to 'start_index'")
	arr1 = array.ArrayGetRange(0, sta_ind)
	arr2 = array.ArrayGetRange(end_ind)
	_ret = (arr1 != "" && newval_array != "") \
		? arr1 + __ARRAY_DLM + newval_array \
		: arr1 + newval_array
	_ret = (arr2 != "" && newval_array != "") \
		? _ret + __ARRAY_DLM + arr2 \
		: _ret + arr2
	return _ret
}

# if 'index; is ommited defaults to ArrayLen(array) ie to insertion at 
# the end of the array 
# NOTE: If you want to insert a single new value convert it to string 
# by using ArrayCreate.
#
Function ArrayInsRange(string array, string newval_array, int "index")
{
	index = __bound_array_index(array, index, tail=true, extend=true)
	return ArraySetRange(array, newval_array, index, index)
}

# If 'start_index' is ommited or zero the deletion starts from the first 
# element of the array  
# If 'end_index' is ommited the deletion continues to the last element of 
# the array.
#
Function ArrayDelRange(string array, int "start_index", int "end_index")
{ 
	return ArraySetRange(array, "", start_index, end_index)
}

# 'index' is the part of the array to return, chunks is the number of parts
# to split array to, 'chunksize' is the number of elements in each chunk.
# If none or both of chunks, chunksize are provided an error occures (they
# are mutually exclusive). 
# If 'chunks' is provided all chunks but the last will have 
# Floor(ArrayLen(array) / chunks) elements; index must then be in the range
# [0..chunks - 1].
# If 'chunksize' is provided all chunks but the last will have chunksize elements;
# index must then be in the range [0..Ceil(ArrayLen(array) / chunksize) - 1]
# In all cases the last chunk gets the remaining elements (which can be less or
# more than chunksize). Examples:
# * Splitting a 17-element array in 5 chunks yields four 3-element arrays 
#   and a last 5-element array.
# * Splitting a 17-element array with chunksize 4 yields four 4-element arrays
#   and a last 1-element array.
#
Function ArraySplit(string array, int index, int "chunks", int "chunksize")
{
	onedefonly = __xor2(Defined(chunks), Defined(chunksize))
	Assert(onedefonly, \
		"ArraySplit: you must supply either 'chunks' or 'chunksize' but not both")
	Assert(Defined(chunks) ? chunks > 0 : true, \
		"ArraySplit: 'chunks' must be greater than zero")
	Assert(Defined(chunksize) ? chunksize > 0 : true, \
		"ArraySplit: 'chunksize' must be greater than zero")
	# Check if chunks is passed (after the previous assertions, 
	# if chunks/chunksize is passed it cannot be zero).
	chunks = Default(chunks, 0)
	chunksize = Default(chunksize, 0)
	# compute final chunks and chunksize and check if index is in bounds
	arrlen = ArrayLen(array)
	ch_size = (chunks > 0) ? Floor(Float(arrlen) / Float(chunks)) : chunksize
	ch_chunks = (chunks > 0) ? chunks : Ceil(Float(arrlen) / Float(ch_size))
	Assert(index >= 0 && index < ch_chunks, \
		"ArraySplit: 'index' out of range")
	# Split left part of the array (up to chunk[index])
	temp = __mid_str(array, __array_index_pos(array, index * ch_size))
	# Split right part of the array (after end of chunk[index])
	return (index < ch_chunks - 1) \
		? __left_str(temp, __array_index_pos(temp, ch_size) - __ARRAY_LEN - 1) \
		: temp
}

# Joins its arguments tail -> head (the array equivalent of addition)

	Function __ajoin_elm(string "elm")
	{
		return Defined(elm) ? (elm != "" ? elm + __ARRAY_DLM : "") : ""
	}

Function ArrayJoin(string "arr01", string "arr02", string "arr03", \
	string "arr04", string "arr05", string "arr06", string "arr07", \
	string "arr08", string "arr09", string "arr10", string "arr11", \
	string "arr12", string "arr13", string "arr14", string "arr15", \
	string "arr16", string "arr17", string "arr18", string "arr19", \
	string "arr20") 
{
	jarr = \
		__ajoin_elm(arr01) + __ajoin_elm(arr02) + \
		__ajoin_elm(arr03) + __ajoin_elm(arr04) + \
		__ajoin_elm(arr05) + __ajoin_elm(arr06) + \
		__ajoin_elm(arr07) + __ajoin_elm(arr08) + \
		__ajoin_elm(arr09) + __ajoin_elm(arr10) + \
		__ajoin_elm(arr11) + __ajoin_elm(arr12) + \
		__ajoin_elm(arr13) + __ajoin_elm(arr14) + \
		__ajoin_elm(arr15) + __ajoin_elm(arr16) + \
		__ajoin_elm(arr17) + __ajoin_elm(arr18) + \
		__ajoin_elm(arr19) + __ajoin_elm(arr20)
	slen = StrLen(jarr)
	return (slen > 0) ? __left_str(jarr, slen - __ARRAY_LEN) : ""
}

Function ArrayPlex(string arr01, string arr02, string "arr03", string "arr04", \
	string "arr05", string "arr06", string "arr07", string "arr08", string "arr09", \
	string "arr10", string "arr11", string "arr12", string "arr13", string "arr14", \
	string "arr15", string "arr16", string "arr17", string "arr18", string "arr19", \
	string "arr20")
{
	strmsg = "ArrayPlex: all arrays must have equal lengths"
	arrlen = arr01.ArrayLen()

	Assert(arr02.ArrayLen() == arrlen, strmsg)
	Assert(Defined(arr03) ? arr03.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr04) ? arr04.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr05) ? arr05.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr06) ? arr06.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr07) ? arr07.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr08) ? arr08.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr09) ? arr09.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr10) ? arr10.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr11) ? arr11.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr12) ? arr12.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr13) ? arr13.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr14) ? arr14.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr15) ? arr15.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr16) ? arr16.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr17) ? arr17.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr18) ? arr18.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr19) ? arr19.ArrayLen() == arrlen : true, strmsg)
	Assert(Defined(arr20) ? arr20.ArrayLen() == arrlen : true, strmsg)

	a01 = arr01.ArrayGetString(0) + __ARRAY_DLM
	a02 = arr02.ArrayGetString(0) + __ARRAY_DLM
	a03 = Defined(arr03) ? arr03.ArrayGetString(0) + __ARRAY_DLM : ""
	a04 = Defined(arr04) ? arr04.ArrayGetString(0) + __ARRAY_DLM : ""
	a05 = Defined(arr05) ? arr05.ArrayGetString(0) + __ARRAY_DLM : ""
	a06 = Defined(arr06) ? arr06.ArrayGetString(0) + __ARRAY_DLM : ""
	a07 = Defined(arr07) ? arr07.ArrayGetString(0) + __ARRAY_DLM : ""
	a08 = Defined(arr08) ? arr08.ArrayGetString(0) + __ARRAY_DLM : ""
	a09 = Defined(arr09) ? arr09.ArrayGetString(0) + __ARRAY_DLM : ""
	a10 = Defined(arr10) ? arr10.ArrayGetString(0) + __ARRAY_DLM : ""
	a11 = Defined(arr11) ? arr11.ArrayGetString(0) + __ARRAY_DLM : ""
	a12 = Defined(arr12) ? arr12.ArrayGetString(0) + __ARRAY_DLM : ""
	a13 = Defined(arr13) ? arr13.ArrayGetString(0) + __ARRAY_DLM : ""
	a14 = Defined(arr14) ? arr14.ArrayGetString(0) + __ARRAY_DLM : ""
	a15 = Defined(arr15) ? arr15.ArrayGetString(0) + __ARRAY_DLM : ""
	a16 = Defined(arr16) ? arr16.ArrayGetString(0) + __ARRAY_DLM : ""
	a17 = Defined(arr17) ? arr17.ArrayGetString(0) + __ARRAY_DLM : ""
	a18 = Defined(arr18) ? arr18.ArrayGetString(0) + __ARRAY_DLM : ""
	a19 = Defined(arr19) ? arr19.ArrayGetString(0) + __ARRAY_DLM : ""
	a20 = Defined(arr20) ? arr20.ArrayGetString(0) + __ARRAY_DLM : ""

	elm = a01 + a02 + a03 + a04 + a05 + a06 + a07 + a08 + a09 + a10 \
		+ a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + a19 + a20

	arr01 = arrlen > 1 ? arr01.ArrayDelete(0) : arr01
	arr02 = arrlen > 1 ? arr02.ArrayDelete(0) : arr02
	ar03 = Defined(arr03) ? (arrlen > 1 ? arr03.ArrayDelete(0) : arr03) : Undef()
	ar04 = Defined(arr04) ? (arrlen > 1 ? arr04.ArrayDelete(0) : arr04) : Undef()
	ar05 = Defined(arr05) ? (arrlen > 1 ? arr05.ArrayDelete(0) : arr05) : Undef()
	ar06 = Defined(arr06) ? (arrlen > 1 ? arr06.ArrayDelete(0) : arr06) : Undef()
	ar07 = Defined(arr07) ? (arrlen > 1 ? arr07.ArrayDelete(0) : arr07) : Undef()
	ar08 = Defined(arr08) ? (arrlen > 1 ? arr08.ArrayDelete(0) : arr08) : Undef()
	ar09 = Defined(arr09) ? (arrlen > 1 ? arr09.ArrayDelete(0) : arr09) : Undef()
	ar10 = Defined(arr10) ? (arrlen > 1 ? arr10.ArrayDelete(0) : arr10) : Undef()
	ar11 = Defined(arr11) ? (arrlen > 1 ? arr11.ArrayDelete(0) : arr11) : Undef()
	ar12 = Defined(arr12) ? (arrlen > 1 ? arr12.ArrayDelete(0) : arr12) : Undef()
	ar13 = Defined(arr13) ? (arrlen > 1 ? arr13.ArrayDelete(0) : arr13) : Undef()
	ar14 = Defined(arr14) ? (arrlen > 1 ? arr14.ArrayDelete(0) : arr14) : Undef()
	ar15 = Defined(arr15) ? (arrlen > 1 ? arr15.ArrayDelete(0) : arr15) : Undef()
	ar16 = Defined(arr16) ? (arrlen > 1 ? arr16.ArrayDelete(0) : arr16) : Undef()
	ar17 = Defined(arr17) ? (arrlen > 1 ? arr17.ArrayDelete(0) : arr17) : Undef()
	ar18 = Defined(arr18) ? (arrlen > 1 ? arr18.ArrayDelete(0) : arr18) : Undef()
	ar19 = Defined(arr19) ? (arrlen > 1 ? arr19.ArrayDelete(0) : arr19) : Undef()
	ar20 = Defined(arr20) ? (arrlen > 1 ? arr20.ArrayDelete(0) : arr20) : Undef()

	# elm is guaranteed to have len > 0 and end with __ARRAY_DLM
	return (arrlen > 1) \
		? elm + ArrayPlex(arr01, arr02, ar03, ar04, ar05, ar06, ar07, \
			ar08, ar09, ar10, ar11, ar12, ar13, ar14, ar15, ar16, ar17, \
			ar18, ar19, ar20) \
		: __left_str(elm, StrLen(elm) - __ARRAY_LEN)
}

# index must be in the range [0..num_plexed_arrays - 1]
#
Function ArrayDeplex(string array, int index, int num_plexed_arrays)
{
	arrlen = array.ArrayLen()
	Assert(index >= 0 && index < num_plexed_arrays, \
		"ArrayDeplex: 'index' out of range.")
	Assert(num_plexed_arrays > 0 && num_plexed_arrays <= arrlen, \
		"ArrayDeplex: 'num_plexed_arrays' out of range.")
	Assert(arrlen % num_plexed_arrays == 0, \
		"ArrayDeplex: value of 'num_plexed_arrays' does not agree with array length.")
	return array.ArrayGetString(index) + ((arrlen > num_plexed_arrays) \
		? __ARRAY_DLM + ArrayDeplex(array.ArrayDelRange(0, num_plexed_arrays), \
			index, num_plexed_arrays) \
		: "")
}
