/*  array.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <strstream.h>
#include <stream.h>
#include <string.h>
#include <ctype.h>
#include <complex.h>

 
#include "outtok.h"
#include "baseio.h"
#include "usercom.h"
#include "hrdinp.h"
#include "yacintfc.h"
#include "interp.h"

#include "myfloat.h"

#include "intfc.h"
#include "user.h"
#include "cgidbg.h"
#include "lexhead.h"
#include "array.h"
#include "entenm.h"
#include "shrdary.h"
#include "travparm.h"
#include "typemap.h"



const char * TheArrayDataName = "Array_Data_XZX_" ;
int ArrayDataIndex = 1;
int ArrayDataIndexBase = 0;

static int is_int_type(DecType typ)
{
	switch(typ) {
case DecInvalid:
default:
		DbgError("::is_int_type","bad_type");
		return 0 ;
case DecFloat:
case DecName:
case DecString:
case DecComplex:
case DecDspPP:
case DecParam:
case DecProcedure:
case DecEnt:
		return 0 ;
case DecMachWord:
case DecCxMachWord:
case DecAccMachWord:
case DecCxAccMachWord:
		switch(TheArithType) {
	case ArithType::ArithTypeUndefined:
	default:
			DbgError("::is_int_type","bad_global_type");
	case ArithType::ArithFloat:
	case ArithType::ArithDouble:
			return 0 ;
	case ArithType::ArithInt16:
	case ArithType::ArithInt32:
			return 1 ;
		}
case DecInt:
		return 1 ;
	}
	return 0 ;
}


static const char * to_integer(const char * dble)
{
	const buf_size = 128 ;
	static char buf[buf_size] ;
	double val ;
	istrstream in((char *)dble);
	in >> val ;
	ostrstream out(buf,buf_size);
	memset(buf,'\0',buf_size);
	out << (int) val ;
	return buf ;
}

int ArrayData::CppList(OutTokens& Out, CppListCmds Cmd)
{
	// LogOut << "ArrayData::CppList\n" ;
	if (IsScalar() || ! Size) {
		const char * value = ConvertToString(Type,Data,0);
		if (Cmd == CppListTargetCtor)
			if (is_int_type(Type)) value = to_integer(value);
		Out.NextFillOut(value);
		return 1;
	}
	// char Buf[BufSize];
	const NumBufSize = 64 ;
	char NumBuf[NumBufSize];
	int Diff = 2 ;
	for (int32 i = 0 ; i < Size ; i++) {
		if (i) Out.NextConcat(",");
		if (!(i%2)) {
			Out.NewLine() ;
			Diff = 2 ;
		}
		const char * DoubOut = NumBuf;
		DoubOut = ConvertToString(Type,Data,i,Cmd);
		if (Cmd == CppListTargetCtor)
			if (is_int_type(Type)) DoubOut = to_integer(DoubOut);
		// DoubOut = ConvertToString(Type,Data,i,CppListCtor);
		int Negative = DoubOut[0] == '-';
		if (Negative) Diff--;
		for (int j = 0 ; j < Diff;j++) Out.NextConcat(" ");
		Diff = 24 - strlen(DoubOut);
		if (Negative) Diff++;
		Out.NextFillOut(DoubOut);
	}
	// LogOut << "ArrayData::CppList exit\n" ;
	return 1 ;
}

void ArrayData::List(OutTokens& Out)
{
	// LogOut << "ArrayData::List\n" ;
	if (IsScalar() || ! Size) {
		Out.NextFillOut(ConvertToString(Type,Data,0));
		return ;
	}
	char Buf[BufSize];
	for (int32 i = 0 ; i < Size ; i++) {
/*
 *		if (i) Out.NextConcat(",");
 *		if (!(i%5)) {
 *			Out.NewLine() ;
 *			if (Size>5) {
 *				Out.NextConcat(strcpy(Buf,dec(i,5))) ;
 *				Out.NextConcat(":");
 *			}
 *		}
 */
		Out.NextConcat("      ");
#ifndef __NT_VC__
		Out.NextConcat(strcpy(Buf,dec(i,5))) ;
#else
		Out.NextConcat(strcpy(Buf,dec(i))) ;
#endif
        Out.NextConcat(":  ");
		Out.NextFillOut(ConvertToString(Type,Data,i));
		Out.NewLine();
	}
	// LogOut << "ArrayData::List exit\n" ;
}

static void CopyArrayElement(DecType Type,void * Dest,void * Source,
	int IndexSource=0,int IndexDest = -1)
{
/*
 *	LogOut << "CopyArrayElement(" << Type << ",,,,)\n" ;
 *	if (Type== DecMachWord) LogOut << "source is " << *(MachWord*) Source
 *		<< "\n " ;
 */
	if (IndexDest < 0 ) IndexDest = IndexSource ;

#define Assign(type)							\
	*(((type *) Dest) + IndexDest) = *(((type *) Source) +		\
		IndexSource) ;						\
	break 								\

	switch(Type) {
case DecFloat:
		Assign(double) ;
case DecInt:
		Assign(int32);
case DecComplex:
		Assign(complex);
case DecMachWord:
		Assign(MachWord);
case DecCxMachWord:
		Assign(CxMachWord);
case DecAccMachWord:
		Assign(AccMachWord);
case DecCxAccMachWord:
		Assign(CxAccMachWord);
case DecName:
case DecString:
case DecProcedure:
case DecDspPP:
case DecParam:
case DecEnt:
case DecInvalid:
default:
		DbgError("CopyArrayElement","bad type");
	}
#undef Assign
/*
 *	if (Type== DecMachWord) LogOut << "Dest[" << IndexDest <<
 *		"] = " << ((MachWord*) Dest)[IndexDest] << "\n" ;
 */
}

ArrayData * ArrayData::clone(ArrayData& clone_from)
{
	Size = clone_from.Size ;
	Type = clone_from.Type ;
	Data = DeclareObjectArray(Type,Size);
	if (!Data) DbgError("ArrayData::ctor","cannot declare space");
	for (int i = 0 ; i < Size;i++) CopyArrayElement(Type,Data,clone_from.Data,
		i,i);
	return this ;
}

ArrayData * ArrayData::SetArrayData(DecType typ, void * data)
{
	Size = -1;
	Type = typ;
	Data = data ;
	return this ;
}


ArrayData * ArrayData::SetArrayData(int32 size, DecType typ, void * data)
{
	Size = size ;
	Type = typ;
	if (Size == -1 || !Size) if (data) {
		Size = -1 ;
		Data = data ;
		return this ;
	}
	if (Size < 1) DbgError("ArrayData::ArrayData","bad size");
	Data = DeclareObjectArray(Type,Size);
	if (!Data) DbgError("ArrayData::ArrayData","can't do allocate");
	if (data) for (int32 i = 0; i < Size ; i++)
		CopyArrayElement(Type,Data,data,i,i);
	Index = 0;
	return this ;
}

ArrayData * ArrayData::SetArrayData(SingleList& TheList,DecType typ)
{
	Size = TheList.Size();
	Type = typ;
	if (Size < 1) DbgError("ArrayData::ArrayData","bad Size");
	Data = DeclareObjectArray(Type,Size);
	void * ThisValue ;
	int32 i = 0;
	while (ThisValue = TheList.Get()) {
		// CopyArrayElement(Type,Data,ThisValue,i++,0);
		CopyArrayElement(Type,Data,ThisValue,0,i++);
		delete ThisValue ;
	}
	Index = 0;
	return this ;
}

const char * UserNameOfType(DecType Type)
{
	return UserNameOfType(Type,CppListCtor);
}

const char * UserNameOfType(DecType Type, CppListCmds Cmd)
{
	int MachWordFlag = 0 ;
	int ComplexFlag = 0 ;
	const char * Return = 0  ;
	switch(Type) {
case DecFloat:
		Return = "double" ;
		break ;
case DecInt:
		Return = "int" ;
		break ;
case DecName:
case DecString:
		Return = "char * (String)" ;
		break ;
case DecComplex:
		ComplexFlag = 1 ;
		Return = "complex" ;
		break ;
case DecMachWord:
		MachWordFlag = 1 ;
		Return = "MachWord" ;
		break ;
case DecCxMachWord:
		ComplexFlag = MachWordFlag = 1 ;
		Return = "CxMachWord" ;
		break ;
case DecAccMachWord:
		MachWordFlag = 1 ;
		Return = "AccMachWord" ;
		break ;
case DecCxAccMachWord:
		ComplexFlag = MachWordFlag = 1 ;
		Return = "CxAccMachWord" ;
		break ;
case DecProcedure:
		Return = "procedure name" ;
		break ;
case DecDspPP:
		// should not occur when prompting the user for input
		Return = "class object" ;
		break ;
case DecParam:
		// should not occur when prompting the user for input
		Return = "parameter list" ;
		break ;
case DecEnt:
		Return = "class object" ;
		break ;
case DecInvalid:
default:
		DbgError("UserNameOfType","bad type");
	}
	if (Cmd == CppListTargetCtor) {
		const BufSize = 64 ;
		static char Buf[BufSize];
		const char * CastSuffix = "Cast" ;
		// LogOut << "UserNameOfType - Return = " << Return << "\n" ;
		if (ComplexFlag) State.Error("complex arrays not supported");
		if (MachWordFlag)  if (strlen(Return) + strlen(CastSuffix)
			+ 1 > BufSize) DbgError("UserNameOfType",
				"buffer too small");
		strcpy(Buf,Return);
		if (MachWordFlag) strcat(Buf,CastSuffix);
		return Buf ;
		
	}
	return Return ;
}


void * StringToValue(DecType Type, const char * String)
{
	// LogOut<<"StringToValue(" << Type << ") Input: `" << String << "'\n" ;
	const BufSize = 1024 ;
	if (!IsNumeric(Type)) return 0;
	void * ReturnValue = DeclareObjectArray(Type,1) ;

	char Temp[BufSize] ;
	memset(Temp,'\0',BufSize);
	strcpy(Temp,String);
	istrstream Read (Temp,BufSize-1);
	switch (Type) {
case DecFloat:
		Read >> *(double *) ReturnValue;
/*
 *		LogOut << "StringToValue, float value = " << *(double *) ReturnValue
 *			<< "\n" ;
 */
		break ;
case DecInt:
		Read >> *(int32 *) ReturnValue;
		break ;
case DecComplex:
#ifndef __NT_VC__
		Read >> *(complex *) ReturnValue;
#else
		LogOut << "Complex input not supported on NT.\n" ;
		HelpOut << "Complex input not supported on NT.\n" ;
		return 0 ;
#endif
		break ;
case DecMachWord:
		Read >> *(MachWord *) ReturnValue;
/*
 *		LogOut << "StringToValue, MachWord value = " <<
 *			*(MachWord *) ReturnValue << "\n" ;
 */
		break ;
case DecCxMachWord:
		Read >> *(CxMachWord *) ReturnValue;
		break ;
case DecAccMachWord:
		Read >> *(AccMachWord *) ReturnValue;
		break ;
case DecCxAccMachWord:
		Read >> *(CxAccMachWord *) ReturnValue;
		break ;
case DecName:
case DecString:
		// Read >> *(char **) ReturnValue;
		// requires dynamic allocation of each element
		// break ;
case DecProcedure:
case DecDspPP:
case DecParam:
case DecEnt:
case DecInvalid:
default:
		DbgError("StringToValue","bad type");
	}
	if (Read.good()) return ReturnValue ; 
	delete ReturnValue ;
	return 0;
}

ArrayData * ArrayParam::ReadUserArrayName(char *RawBuf)
{
	char Buf[BufSize] ;
	memset(Buf,'\0',BufSize);
	istrstream Temp(RawBuf,BufSize-1);
	Temp >> Buf;
	OutputType channel = OutputPrompt ;
    if (!State.IsInteractive()) channel = OutputCppHelp ;
	if ( Temp.bad() ) {
		*Output + channel << "Invalid C++ string input: `" <<
            RawBuf << "'.\n" ;
		return 0 ;
    } 
	// UserEntity * entity = AllEntityLists->GetObject(Buf);
	ValueType * value_type = AllEntityLists->find_obj(Buf);
	if (!value_type) {
		*Output + channel << "There is no array of variable `" << Buf <<
			"'.\n";
		return 0 ;
	}
	int32 size = value_type->GetArraySize();
	if (!size) {
		*Output + channel << "Object `" << Buf << "' is not an array.\n";
		return 0 ;
	}
	if (value_type->Type != Type) {
		*Output + channel << "Array `" << Buf << "' is type `" <<
			get_dec_type_name(value_type->Type) << "', an array of type `" <<
			get_dec_type_name(Type) << "' is required.\n" ;
		return 0 ;
	}
	if (size < SizeLowerLimit || size > SizeUpperLimit) {
		*Output + channel << "Array `" << Buf << "' is size " <<
			size << ".\nAn array of at least " << SizeLowerLimit <<
			" and no more than " <<
			SizeUpperLimit << " elements is required.\n" ;
		return 0 ;
	}
	ArrayData * to_clone = value_type->array_data();
	if (!to_clone) DbgError("ArrayParam::ReadUserArrayName","bad array");
	return (new ArrayData)->clone(*to_clone);
}

enum SuppliedOptions {SuppliedIgnore,SuppliedArrayUse,SuppliedScalar} ;

static double mach_type_limit(DecType typ)
{
   switch(typ) {
case DecCxMachWord:
case DecMachWord:
        return MachWord::max_positive ;
case DecCxAccMachWord:
case DecAccMachWord:
        return AccMachWord::max_positive ;
default:
        DbgError("mach_type_limit","bad type");
    }
 return 0 ;
}


static int check_overflow_report(DecType type, double test_val)
{
	if (!IsMachWordType(type)) return  0 ;
	// LogOut << "test_val = " << test_val << "\n" ;
	if (!check_overflow(type,test_val)) return 0 ;
	*Output + OutputPrompt << "Value " << test_val  << " overflows type " <<
		get_dec_type_name(type) << ",magnitude must not exceed\n" << 
		mach_type_limit(type) / NormToOneMachWord <<
		" (the unnormalized largest magnitude is "
		<< mach_type_limit(type) << ").\n" ;
	return 1 ;
} 
	

static void * ReadUserInputValue(DecType Type, const char *RawBuf,int ix=1)
{
	OutputType channel = OutputPrompt ;
	if (!State.IsInteractive()) channel = OutputCppHelp ;
	istrstream str((char *)RawBuf);
	double test ;
	str >> test ;
	void * ThisValue = 0 ;
	if (!str.fail()) 
		if (!check_overflow_report(Type, test)) ThisValue =
				StringToValue(Type,RawBuf) ;
	if(!ThisValue) if (ix) *Output + channel
		<< "Invalid or out of range value `" << RawBuf << "'.\n" ;
	return ThisValue ;
}

static void PromptForArrayData(DecType Type, const char * Name, int32 Lower,
	int32 Upper)
{
	OutputType channel = OutputPrompt ;
    if (!State.IsInteractive()) channel = OutputCppHelp ;

	*Output + channel << "Specify between " << Lower << " and " <<
		Upper << " `" <<  UserNameOfType(Type) <<
		"' values for array `" << Name  << "' or\n" <<
		"the name of an array of this type and size.\n" ;
	if (State.IsVerbose()) {
		*Output + channel << "\n" ;
		if (!is_floating_point_simulator()) if (IsMachWordType(Type))  {
			*Output << "Entries will be normalized relative to `MachWord'.\n";
			*Output << "For example .5 will be half of the maximum positive\n";
			*Output << "`MachWord' value or " << .5 * NormToOneMachWord << ".\n";
		}
		if (IsComplex(Type)) *Output + channel << 
		"The format for complex values is `real' or `(real,imag),'\n"
		<< "where `real' and `imag' are floating point numbers.\n" ;
		*Output + channel <<
		"Enter a blank line to stop entering values." ;
		*Output + channel <<
	"\nType RETURN to select the default array or begin entering values.\n";
	}
}


static void rest_out_of_bounds(SuppliedOptions SuppliedArrayFlag=SuppliedIgnore)
{
	if (!State.IsInteractive()) switch (SuppliedArrayFlag) {
case SuppliedArrayUse:
	 	*Output << "You can change it in the original array\n" <<
			"or you can abort this statement by typing RETURN.\n" ;
		break ;
case SuppliedIgnore:
		*Output << "It will be ignored.\n" ;
		break ;
case SuppliedScalar:
		*Output << "Please specify a valid value or RETURN.\n" ;
		break ;
	}
}


static void PromptOutOfBounds(DecType Type,void * Lower, void * Upper, void * v,
	SuppliedOptions SuppliedArrayFlag=SuppliedIgnore)
{
	OutputType channel = OutputPrompt ;
    if (!State.IsInteractive()) channel = OutputCppHelp ;
	*Output + channel << "Value must be >= " <<
		ConvertToString(Type,Lower,0) ;
	*Output << " and <= " << ConvertToString(Type,Upper,0) << ".\n" ;
	if (v) {
		*Output << "Specified value (" << ConvertToString(Type,v,0) <<
			") is out of range.\n" ;
		rest_out_of_bounds();
	}
}

ArrayData * MakeArrayData(ValueType& Val)
{
	if (Val.Class != DeclBasic) return 0;
	SimpleUserObject& Obj = *(Val.Declared.Basic);
	int Size = Obj.GetArraySize();
	ArrayData * Temp = new ArrayData ;
	Temp->SetArrayData(Size,Obj.GetValue()->Type,
		Obj.GetValue()->Value.ValBuiltInClass);
	return Temp ;
}


int ValueLeq (DecType Type, void * a, void * b)
{
/*
 *	LogOut << "ValueLeq: Type = " << Type << ", a = " << *(MachWord *)a
 *		<< ", b = " << *(MachWord *)b << "\n" ; 
 *
 *	LogOut << "Double values - a = " << ConvertToDouble(Type,a) <<
 *		", b = " << ConvertToDouble(Type,b) << "\n" ;
 */

	if(!IsNumeric(Type)) DbgError("ValueLeq", "nonnumeric data");
	if(IsComplex(Type)) DbgError("ValueLeq", "complex compare not in yet");
/*
 *	LogOut << "comparing " << ConvertToDouble(Type,a) << " <= " <<
 *		ConvertToDouble(Type,b) << "\n" ;
 */
	return ConvertToDouble(Type,a) <= ConvertToDouble(Type,b) ;
}

int32 ArrayParam::GetLengthParameterValue()
{
	if (!CurrentValue) DbgError("ArrayParam::GetLengthParameter",
		"NULL current");
/*
 *	LogOut << "ArrayParam::GetLengthParameterValue = " <<
 *		CurrentValue->Size << "\n";
 */
	return CurrentValue->Size;
}


struct GetParameterTemp {
	const char * Name;
	OneParameter& Parent;
	ValueType * Val;
	int UsingSpecifiedValues ;
    ArrayData * SuppliedArray ;
    int32 SuppliedSize ;
	GetParameterTemp(const char * Name, OneParameter& Parent, ValueType * Val) ;
};

GetParameterTemp::GetParameterTemp(const char * name, OneParameter& parent,
	ValueType * val):
		Name(name),
		Parent(parent),
		Val(val),
		UsingSpecifiedValues(0),
		SuppliedArray(0),
		SuppliedSize(0)
{
}


void ArrayParam::UseSuppliedvalues(GetParameterTemp& TempParam)
{
	TempParam.UsingSpecifiedValues = 1 ;
	if (IsScalar()) {
		void * ThisValue = TempParam.Val->Value.ValBuiltInClass;
		TempParam.SuppliedArray =
			(new ArrayData)->SetArrayData(-1,Default->Type,
				ThisValue);
		for (;;) {
			int out_of_bounds =
				!ValueLeq(Type,TheLowerBound(TempParam.Parent), ThisValue) ||
		    	!ValueLeq(Type,ThisValue,TheUpperBound(TempParam.Parent)) ;
			if (!out_of_bounds) break ; else for (;;) {
				if (out_of_bounds) PromptOutOfBounds(Type, TheLowerBound(TempParam.Parent),
					TheUpperBound(TempParam.Parent),ThisValue,
						SuppliedScalar);
				const char * RawBuf = CondGetRawBufLine(0,
					"invalid parameter");
				if (!RawBuf) {
					return ;
				}
				if (RawBuf[0] == '\03') {
					State.Error("user aborted input");
					return ;
				}
				ThisValue = ReadUserInputValue(Type,RawBuf);
				if(ThisValue) break ;
			}
		}
		CurrentValue = TempParam.SuppliedArray ;
		return ;
	}
	SimpleUserObject * Temp = TempParam.Val->Declared.Basic ;
	if (!Temp) DbgError("ArrayParameter::GetParameter",
		"null simp");
	TempParam.SuppliedArray = Temp->GetArray();
	if (!TempParam.SuppliedArray) DbgError(
		"ArrayParameter::GetParameter", "null array");
/*
 *	OutTokens Out(&LogOut);
 *	TempParam.SuppliedArray->List(Out);
 *	Out.NewLine();
 */
	TempParam.SuppliedSize = TempParam.SuppliedArray->GetArraySize();
	if (TempParam.SuppliedSize > SizeUpperLimit){
		if (State.IsVerbose()) *Output + OutputCppHelp <<
			"This array is of length " << TempParam.SuppliedSize
			<< ".\nParameter `" << TempParam.Name <<
			"' must not be larger than " << SizeUpperLimit
			<< " elements long.\n" ;
		State.Error( "array is too large");
		// LogOut << "Too big\n" ;
		return ;
	}
	if (TempParam.SuppliedSize < SizeLowerLimit) {
		if (State.IsVerbose()) *Output + OutputCppHelp <<
			"This array is of length " << TempParam.SuppliedSize
			<< ".\nParameter `" << TempParam.Name <<
			"' must not be less than " << SizeLowerLimit
			<< " elements long.\n" ;
		State.Error( "array is too small");
		// LogOut << "Too small\n" ;
		return ;
	}
}

void ArrayParam::GetScalarParameter(GetParameterTemp& TempParam)
{
	if (TempParam.Val) UseSuppliedvalues(TempParam);
	CurrentValue = 0 ;
	for (;;) {
		char * RawBuf ;
		int GetInputFromUser = !TempParam.UsingSpecifiedValues ;
		void * DeleteValue = 0;
		void * ThisValue = 0;
		for(;;) {
			if (GetInputFromUser) {
				if (!State.IsInteractive()) break ;
				*Output + OutputPrompt << "Specify a `" << UserNameOfType(Type)
					<< "' value for `" << TempParam.Name  << "':\n" ;
				RawBuf = GetRawBufLine(InputPrompt) ;
				if (!*RawBuf) {
					CurrentValue = Default ;
					return ;
				} else {
					if (RawBuf[0] == '\03') {
						State.Error("user aborted input");
						return ;
					}
					DeleteValue = ThisValue = ReadUserInputValue(Type,RawBuf);
					if (!ThisValue) continue ;
				}
			} else {
				ArrayData * ToUse = TempParam.SuppliedArray ?
					TempParam.SuppliedArray : Default ;
				ThisValue = GetNthValue(Type,ToUse->Data, 0);
			}
            int out_of_bounds =
				!ValueLeq(Type,TheLowerBound(TempParam.Parent),ThisValue) ||
			    !ValueLeq(Type,ThisValue,TheUpperBound(TempParam.Parent)) ;
			if (!out_of_bounds) break ;
			{
				if (out_of_bounds) PromptOutOfBounds(Type,
					TheLowerBound(TempParam.Parent),
					TheUpperBound(TempParam.Parent),ThisValue,
					TempParam.SuppliedArray? SuppliedArrayUse:
					SuppliedScalar);
				GetInputFromUser = 1 ;
			}
		} 
		if (TempParam.SuppliedArray) CurrentValue = TempParam.SuppliedArray ;
		else {
			DeleteValue = 0 ;
			CurrentValue = new ArrayData ;
			CurrentValue->SetArrayData(Type,ThisValue);
		}
		delete DeleteValue ;
		return ;
	}
}

void ArrayParam::GetParameter(const char * X_Name, OneParameter& X_Parent,
		ValueType * X_Val,int )
{
	// If Val is NULL prompt the user to enter a value
	// otherwise check if Val is a legal value and prompt the
	// user only if it is not legal
	GetParameterTemp TempParam(X_Name,X_Parent,X_Val) ;
	if (IsScalar()) {
		GetScalarParameter(TempParam);
		return ;
	}
	if (!TempParam.Val) PromptForArrayData(Type,TempParam.Name,SizeLowerLimit,
		SizeUpperLimit) ;
	else UseSuppliedvalues(TempParam);

	int ArrayIndex = 0;

	SingleList DataList ;
	int32 ParamCount = 0;
	CurrentValue = 0;
	for (;;) {
		char * RawBuf ;
		int GetInputFromUser = !TempParam.UsingSpecifiedValues ;
		void * DeleteValue = 0;
		for(;;) {
			void * ThisValue = 0;
			if (GetInputFromUser) {
				if (!State.IsInteractive()) {
					State.Error("invalid array parameter") ;
					return ;
					break ;
				}
				*Output + OutputPrompt << TempParam.Name <<
					"[" << ParamCount << "] = ?\n" ;
				RawBuf = GetRawBufLine(InputPrompt) ;
				if (!*RawBuf) {
					if (!ParamCount) {
							CurrentValue = Default ;
							// LogOut << "Default\n" ;
							return ;
					}
					break ;
				} else {
					if (RawBuf[0] == '\03') {
						State.Error("user aborted input");
						return ;
					}
					DeleteValue = ThisValue = ReadUserInputValue(Type,
						RawBuf,ParamCount);
					if (TempParam.SuppliedArray) {
						CopyArrayElement(Type,TempParam.SuppliedArray->Data,
							ThisValue,0,ParamCount);
						GetInputFromUser = 0 ;
					} else if(!ThisValue && !ParamCount)  {
						TempParam.SuppliedArray  = ReadUserArrayName(RawBuf);
						if (TempParam.SuppliedArray) {
							TempParam.SuppliedSize =
								TempParam.SuppliedArray->Size ;
							GetInputFromUser = 0 ;
							continue ;
						}
					}
				
					
					// continue ;
				}
			} else {
/*
 *				LogOut << "Getting value " << ParamCount <<
 *					"\n" ;
 *				LogOut << "Type = " << Type << "\n" ;
 */
				ArrayData * ToUse = TempParam.SuppliedArray ?
					TempParam.SuppliedArray : Default ;
				ThisValue = GetNthValue(Type,ToUse->Data,
					ParamCount);
			}
            int out_of_bounds = 1 ;
            if (ThisValue)  out_of_bounds =
				!ValueLeq(Type,TheLowerBound(TempParam.Parent),ThisValue) ||
			    	!ValueLeq(Type,ThisValue,TheUpperBound(TempParam.Parent));
			if (!out_of_bounds) {
				ParamCount++;
				if (TempParam.SuppliedArray) {
					if (ParamCount > SizeUpperLimit) {
						DbgError("ArrayParam::GetParam",
						"bad upper size check");
					}
					if (ParamCount >= TempParam.SuppliedSize) break;
				} else {
					DataList.Append(ThisValue);
					DeleteValue = 0 ;
					GetInputFromUser =
						!TempParam.UsingSpecifiedValues ;
				} 

				if (ParamCount >= SizeUpperLimit) break ;
			} else {
				if (out_of_bounds) PromptOutOfBounds(Type,
					TheLowerBound(TempParam.Parent),
					TheUpperBound(TempParam.Parent),ThisValue,
					TempParam.SuppliedArray? SuppliedArrayUse:
					SuppliedIgnore);
				GetInputFromUser = 1 ;
			}
		} 
		delete DeleteValue ;
		if (ParamCount >= SizeLowerLimit) {
			if (TempParam.SuppliedArray) {
				CurrentValue = TempParam.SuppliedArray ;
			} else {
				CurrentValue = new ArrayData ;
				CurrentValue->SetArrayData(DataList,Type);
			}
			return ;
		}
		if (TempParam.SuppliedArray) {
			TheLog << "ParamCount = " << ParamCount <<
				", SizeLowerLimit = " << SizeLowerLimit << "\n";
			TheLog << "Param name is `" << TempParam.Name << "'\n" ;
			DbgError("ArrayParam::GetParam",
				"bad lower size check");
		}
		OutputType channel = OutputPrompt ;
        if (!State.IsInteractive()) channel = OutputCppHelp ;
		*Output + channel << "You must specify at least " <<
			(SizeLowerLimit - DataList.Size()) << 
			" values for this array.\n" ;
		if (State.IsInteractive()) *Output <<
			"Type `y' if you want to add more values or\n" <<
			" RETURN to use the default array or\n"  <<
			"or `n' to abort current operation:\n" ;
		else {
			State.Error("array is too small");
			CurrentValue = 0 ;
			// LogOut << "Too small 2\n" ;
			return ;
		}
		char c ;
		for (;;) {
			c = 0;
			GetRawBufLine(InputPrompt);
			if (!RawBuf[0]) {
				CurrentValue = Default ;
				// LogOut << "Default\n" ;
				return ;
			}
			if (RawBuf[0] == '\03') {
				State.Error("user aborted input");
				return ;
			}
			c = tolower(RawBuf[0]) ;
			if (c == 'n') {
				CurrentValue = 0;
				State.Error(
				"invalid parameter value for `",TempParam.Name,"'");
				// LogOut << "Invalid\n" ;
				return ;
			}
			if (c == 'y') break ;
			*Output + OutputPrompt <<
				"Specify `y' or `n' or RETURN:\n" ;
		}
	}
}


void * ArrayParam::GetParameterValue()
{
	// LogOut << "ArrayParm::GetParameterValue\n" ;
	if (!CurrentValue) DbgError("ArrayParam::GetParameterValue",
		"null CurrentValue");
	return CurrentValue->Data ;
}




void * ArrayParam::TheLowerBound(struct OneParameter& p)
{
	return CheckLower ? CheckLower->GetBound(CheckActionCheck,p,
			CheckLower->List) : Lower;
}

void * ArrayParam::TheUpperBound(struct OneParameter& p)
{
	return CheckUpper ? CheckUpper->GetBound(CheckActionCheck,p,
			CheckUpper->List) : Upper;
}

void ArrayParam::List(OneParameter& Parent, OutTokens& Out)
{
// describe the default value and the limits
	char Buf[BufSize];
	if (Default->IsScalar() || !Default->Size) {
		Out.NextFillOut("The default value is");
		Default->List(Out);
		Out.NextConcat(".");
		Out.NewLine();
		Out.NextFillOut("This parameter must be");
		if (CheckLower) 
			CheckLower->GetBound(CheckActionDescribe,Parent,
				CheckLower->List, &Out);
		else {
			Out.NextFillOut(">=");
			Out.NextFillOut(ConvertToString(Type,Lower));
		}
		Out.NextFillOut("and");
		if (CheckUpper) 
			CheckUpper->GetBound(CheckActionDescribe,Parent,
				CheckUpper->List, &Out);
		else {
			Out.NextFillOut("<=");
			Out.NextFillOut(ConvertToString(Type,Upper));
		}
		Out.NextConcat(".");
		return ;
	}
	Out.NextFillOut("The default array is:");
	Out.NewLine();
	Default->List(Out);
	Out.NextConcat(".");
	Out.NewLine();
	Out.NextFillOut("Each value must be");
	if (CheckLower) 
		CheckLower->GetBound(CheckActionDescribe,Parent,
			CheckLower->List, &Out);
	else {
		Out.NextFillOut(">=");
		Out.NextFillOut(ConvertToString(Type,Lower));
	}
	Out.NextFillOut("and");
	if (CheckUpper) 
		CheckUpper->GetBound(CheckActionDescribe,Parent,
			CheckUpper->List, &Out);
	else {
		Out.NextFillOut("<=");
		Out.NextFillOut(ConvertToString(Type,Upper));
	}
	Out.NextConcat(".");
	Out.NextFillOut("The array must have at least");
	Out.NextFillOut(strcpy(Buf,dec(SizeLowerLimit)));
	Out.NextFillOut("and no more than");
	Out.NextFillOut(strcpy(Buf,dec(SizeUpperLimit)));
	Out.NextFillOut("elements.");
}

void ArrayParam::ListValue(OneParameter&, OutTokens& Out )
{
// describe the current value
	if (IsScalar() || !Default->Size) Out.NextFillOut("The value is:") ;
	else Out.NextFillOut("The values are:\n") ;
	CurrentValue->List(Out);
	Out.NextConcat(".");
}

double ArrayData::access(int index)
{
	if (index <0 || index >= Size) {
		State.Error("array index out of range");
		return 0 ;
	}
	return ConvertToDouble(Type,Data,index);
}
int ArrayData::SameValue(ArrayData * Check) const
{
	if (Size != Check->Size) return 0 ;
	if (Type != Check->Type) return 0 ;
	for (int i = 0 ; i < Size ; i++) if (ConvertToDouble(Type,Data,i)
		!= ConvertToDouble(Type,Check->Data,i)) return 0 ;
	return 1;
}

int ArrayParam::CppListDecEmit(OutTokens& Out, CppListCmds Cmd)
{
	CppListHead(Out,Cmd);
	Out.NextFillOut("= {");
	CurrentValue->CppList(Out,Cmd);
	Out.NextFillOut("};");
	Out.NewLine();
	return 1;
	// LogOut << "ArrayData::CppListDecEmit exit\n" ;
}


int ArrayParam::CppListDec(OutTokens& Out, CppListCmds Cmd)
{
	// LogOut << "ArrayData::CppListDec\n" ;
	if (IsScalar()) return 1;
	if (!CurrentValue) return 1;
	if (!CurrentValue->Size) return 1;
	if (!Default || !Default->Size) DbgError("ArrayParam::CppListDec",
		"bad Default Array");
	// int Test = Default->GetIndex();
	// if (Test >= ArrayDataIndexBase) return 1;

	SharedArray * Ary = 0 ;
	TraverseParameters * Param = 0 ;
	TraverseObject * Obj = 0 ;
	SpecialOptions * Opt = Out.GetSpecialOptions();
	if (Opt) Obj = Opt->GetTraverseObject();
	if (Obj) Param = Obj->GetParameters();
	if (Param) Ary = Param->GetSharedArray();
	if (Ary) {
		ArrayData * Same = Ary->FindSame(CurrentValue);
		if (Same) {
			Default->SetIndex(GetIndex());
			return 1 ;
		}
		Default->SetIndex(ArrayDataIndex++);
		Ary->Append(CurrentValue);
		OutTokens& CodeOut = Ary->GetCodeFile();
		OutTokens& HeadOut = Ary->GetHeaderFile();
		HeadOut.NextFillOut("extern");
		CppListHead(HeadOut,Cmd);
		HeadOut.NextFillOut(";");
		HeadOut.NewLine();
		return CppListDecEmit(CodeOut,Cmd);
	}
	Default->SetIndex(ArrayDataIndex++);
	Out.NextFillOut("static");
	return CppListDecEmit(Out, Cmd);
}

void ArrayParam::CppListHead(OutTokens& Out, CppListCmds Cmd)
{
	Out.NextFillOut(UserNameOfType(Type,Cmd));
	// if (Cmd!=CppListTargetCtor) Out.NextFillOut("*");
	Out.NextFillOut(TheArrayDataName);
	char Buf[32];
	Out.NextConcat(strcpy(Buf,dec(Default->GetIndex())));
	Out.NextFillOut("[]");
	// if (Cmd!=CppListTargetCtor) Out.NextFillOut("[]");
	//if (Cmd==CppListTargetCtor) Out.NextFillOut("[]") ;
}

int ArrayParam::CppList(OutTokens& Out, CppListCmds Cmd)
{
	// LogOut << "ArrayParam::CppListDec\n" ;
	if (!CurrentValue) Out.NextFillOut("0");
	if (IsScalar() || !CurrentValue->Size) CurrentValue->CppList(Out,Cmd);
	else {
		char Buf[64];
		if (Cmd == CppListTargetCtor) if (IsMachWordType(Type)) {
			Out.NextFillOut("(");
			Out.NextFillOut(UserNameOfType(Type)) ;
			Out.NextFillOut(" *)") ;
		}
		Out.NextFillOut(TheArrayDataName);
		Out.NextConcat(strcpy(Buf,dec(Default->GetIndex()))) ;
		if (Cmd == CppListTargetCtor) {
			Out.NextConcat(",");
			Out.NextFillOut(dec(CurrentValue->Size));
		}
	}
	// LogOut << "ArrayParam::CppListDec exit\n" ;
	return 1 ;
}


