################################################################################
# TSL-LIBRARY:	EMOS_DDT_ACCESS_Range_Lib
################################################################################
# Copyright (C) 2000  EMOS Computer Consulting GmbH
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# For further information please contact:
#
#	Dean Rajovic
#	EMOS Computer Consulting GmbH
#	Oskar-Messter-Straße 25
#	85737 Ismaning
#	Germany
#	tel.: +49 89 608 765-0
#	mailto:drajovic@emos.de
#	http://www.emos.de
################################################################################
# $Revision: 1.1.1.1 $
# $Author: drajovic $
# $Date: 2004/03/24 20:13:57 $
# $Archive: /MERCURY/Projects/EMOS_GPL/DDT/emos_ddt_access_range_lib/script $
# $NoKeywords: $
################################################################################

#**# 
#*	This library defines an "iterator" (as much as one can emulate a real 
#*	iterator in a language such as TSL) for data tables (ddt-interface).
#*	The main benefits of this iterator are:
#*	   1.) you can loop through any table in an uniform way,
#*	   2.) you can choose one of four algorithms to iterate through the range:
#*	         * sequntial (forward)
#*	         * sequential (reverse)
#*	         * random (values may get repeated)
#*	         * random (values will not be repeated)
#*	   3.) you can simultaneously have iterators for multiple data tables
#*	       (ane at a time per table!)
#*	   4.) you can use it to iterate any range of numbers (unrelated to data 
#*	       tables).
#* <p>NOTE!
#* <ul>
#* <li> For more detailed instruction see comments for DDT_ACCESS_lib.</li>
#* <li> This iterator does not (yet) support the syntax like DDT_ACCESS_lib.
#*		You can only specify a range with <code>from</code> and <code>to</code> parameters. It would
#*		be great if someone could find time to implement this.</li>
#* </ul>
#* PUBLIC CONSTANTS:
#* <ul>
#*	<li>DDT_ACCESS_SEQUENTIAL</li>
#*	<li>DDT_ACCESS_REVERSE_ACCESS</li>
#*	<li>DDT_ACCESS_RANDOM</li>
#*	<li>DDT_ACCESS_RANDOM_NOREPEAT</li>
#*	<li>DDT_ACCESS_INVALID_MODE</li>
#*	<li>DDT_ACCESS_DEFAULT_MODE</li>
#* </ul>
#*/

#====================================================
# public variables
#====================================================

public const DDT_ACCESS_SEQUENTIAL = 1;
public const DDT_ACCESS_REVERSE = 2;
public const DDT_ACCESS_RANDOM = 3;
public const DDT_ACCESS_RANDOM_NOREPEAT = 4;

public const DDT_ACCESS_INVALID_MODE = -666;
public const DDT_ACCESS_DEFAULT_MODE = DDT_ACCESS_SEQUENTIAL;

#====================================================
# private variables
#====================================================

static accessMode[];
static minPos[], maxPos[], curPos[];
static posUnused[];
static indexArray[];

#====================================================
# public functions
#====================================================

#**
#*	Initialises the range-access algorithm.
#* @param table (in)	the name od the iterator (data table)
#* @param from (in)	begining  of range
#* @param to (in)		end of range
#* @param mode (in)	one of four possible access modes
#*				[default: DDT_ACCESS_SEQUENTIAL]
#* @return
#*	E_OK:	initialisation succeeded
#*	!E_OK:	initialisation failed
#*/

public function DDT_ACCESS_init_range( in table, in from, in to, in mode )
{
	auto rc;

	if ( from*1 <= 0 )
		return E_ILLEGAL_PARAMETER;
	if ( to*1 < from*1 )
		return E_ILLEGAL_PARAMETER;

	minPos[table] = from*1;
	maxPos[table] = to*1;

	init_index_array( table );

	switch ( mode )
	{
		case DDT_ACCESS_SEQUENTIAL:
			curPos[table] = minPos[table] - 1;
			break;
		case DDT_ACCESS_REVERSE:
			curPos[table] = maxPos[table] + 1;
			break;
		case DDT_ACCESS_RANDOM:
			curPos[table] = 0;
			break;
		case DDT_ACCESS_RANDOM_NOREPEAT:
			curPos[table] = 0;
			break;
		default:
			return  DDT_ACCESS_INVALID_MODE;
	}
	accessMode[table] = mode;

	return E_OK;
}

#**
#*	Removes range-access for given table and frees the internal buffers.
#* @param table (in)	the name od the iterator (data table)
#*/

public function DDT_ACCESS_clean_range ( in table )
{
	delete_dimension( accessMode, table );
	delete_dimension( minPos, table );
	delete_dimension( maxPos, table );
	delete_dimension( curPos, table );
	delete_dimension( posUnused, table );
	delete_dimension( indexArray, table );
}

#**
#*	Indicates whether there subsequent call to 
#*	DDT_get_next_in_range() is about to succeed.
#* @param table (in)	the name od the iterator (data table)
#* @return
#*	TRUE:	there is still something left to be processed
#*	FALSE:	all tests processed
#*/

public function DDT_ACCESS_has_more_in_range ( in table )
{
	switch ( accessMode[table] )
	{
		case DDT_ACCESS_SEQUENTIAL:
		case DDT_ACCESS_REVERSE:
		case DDT_ACCESS_RANDOM:
		case DDT_ACCESS_RANDOM_NOREPEAT:
			break;
		default:
			return  0;
	}
	return posUnused[table];
}

#**
#*	Returns the index of the next test to be processed.
#* @param table (in)	the name od the iterator (data table)
#* @return
#*	>0:	test number
#*	<0:	error ocurred
#*/

public function DDT_ACCESS_get_next_in_range ( in table )
{
	switch ( accessMode[table] )
	{
		case DDT_ACCESS_SEQUENTIAL:
			curPos[table]++;
			break;
		case DDT_ACCESS_REVERSE:
			curPos[table]--;
			break;
		case DDT_ACCESS_RANDOM:
			curPos[table] = get_random_pos( table );
			break;
		case DDT_ACCESS_RANDOM_NOREPEAT:
			curPos[table] = get_norepeat_random_pos( table );
			break;
		default:
			return  DDT_ACCESS_INVALID_MODE;
	}
	return set_pos_used( table, curPos[table] );
}

static function delete_index_array( in table )
{
	delete_dimension( indexArray, table );
	delete minPos[table];
	delete maxPos[table];
	delete curPos[table];
	delete posUnused[table];
}

static function init_index_array( in table )
{
	auto i;
	delete_dimension( indexArray, table );
	for ( i=minPos[table]; i <= maxPos[table]; i++ )
		indexArray[table, i] = 0;
	posUnused[table] = maxPos[table];
}

static function set_pos_used( in table, in pos )
{
	if ( indexArray[table, pos] == 0 )
	{
		indexArray[table, pos] = 1;
		posUnused[table]--;
	}
	return pos;
}

static function get_random_pos( in table )
{
	return int( rand() * ( maxPos[table] - minPos[table] + 1) ) + minPos[table];
}

static function get_norepeat_random_pos( in table )
{
	auto x;
	while (1)
	{
		x = get_random_pos( table );
		if ( indexArray[table, x] == 0 )
			break;
	}
	return set_pos_used( table, x );
}

#**
#*	This function will delete a dimension from a multidimensional array.
#*<p> WARNING!
#*	It will delete ALL dimensions with the same name. For example
#*   assume your three-dimensional array "arr" has the following content:
#*<pre>		arr["x",1,"x"] = "x1x";
#*		arr["x",1,"y"] = "x1y";
#*		arr["y",1,"x"] = "y1x";
#*		arr["y",1,"y"] = "y1Y";</pre>
#*	then the following command
#*<pre>		delete_dimension( arr, "x" );</pre>
#*	will delete everything BUT
#*<pre>		arr["y",1,"y"] = "y1Y";</pre>
#* @param arr (inout)	array to be processed
#* @param dim (in)	dimension(s) to be removed
#*/

static function delete_dimension( inout arr[], in dim )
{
	auto i;
	for ( i in arr )
		if ( match (i, dim) )
			delete arr[i];
}

###########################################################################
# TSL-LIBRARY:	EMOS_DDT_ACCESS_Range_Lib
###########################################################################