// utf-8

/*	Copyright  (c)	Günter Woigk   2001-2003
  					mailto:kio@little-bat.de

	This file is free software

 	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.  

 	Permission to use, copy, modify, distribute, and sell this software and
 	its documentation for any purpose is hereby granted without fee, provided
 	that the above copyright notice appear in all copies and that both that
 	copyright notice and this permission notice appear in supporting
 	documentation, and that the name of the copyright holder not be used
 	in advertising or publicity pertaining to distribution of the software
 	without specific, written prior permission.  The copyright holder makes no
 	representations about the suitability of this software for any purpose.
 	It is provided "as is" without express or implied warranty.

 	THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 	INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 	EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 	CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 	DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 	TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 	PERFORMANCE OF THIS SOFTWARE.

	08.aug.2001	kio	started work on this file
	27.oct.2001 kio finished 2nd rework
	31.oct.2001 kio started test suite; fixed union bug
	04.nov.2001	kio finished test suite, documentation
	21.nov.2001 kio list().array now array of Var* ... again.
	2002-03-21 kio	fixed bug in InsertItems() if !XXXSAFE
	2002-04-01 kio	fixed bug in AppendList()
	2002-04-05 kio	enhanced operators on lists, lots of changes
	2002-04-07 kio	fixed bug in operator> et.al. for lists
	2002-05-20 kio	trap for sizeof(double)>12
	2003-08-05 kio	ToList() new, ToString() new, changed ToNumber() for isVoid
	2003-08-21 kio	eliminated isVoid
	2003-08-23 kio	operator>>=() and operator<<=() now use ldexp() => now left operand may be non-int
*/

#define 	SAFE	3
#define 	LOG		2

#include	"config.h"
#include	<math.h>
#include	"var.h"


#ifndef NAN
	const double NAN = 0.0/0.0;
#endif


// ---- dispatcher stuff ----
inline void CheckStack() { }
inline void DISP() { }



/* ----------------------------------------------------------------
		Class Var

		maintain named variables of different types
---------------------------------------------------------------- */



/* ----	validate variables ----------------------------------------
*/
void Var::validate ( cstr file, long line ) const
{
	#define VTRAP(X) if (!(X))	{} else Abort ( file, line, Using("internal error@%d",__LINE__) )

// test global prerequiste: double fits in data[]:
	TRAP(sizeof(double)>sizeof(data));

	CheckStack();
	VTRAP(!this);
	VTRAP(type_invalid());

	if (parent)
	{
		VTRAP((int)index<0);
		VTRAP(parent->type!=isList);
		VTRAP(parent->list().array==NULL);
		VTRAP(parent->list().used<=index);
		VTRAP(parent->list().array[index]!=this);
	}

	if (type==isList)
	{
		Var**a = list().array;
		uint u = list().used;
		uint s = list().size;

		VTRAP(s!=0 && u>s);
		VTRAP((u||s)&&!a);

		for (uint i=0;i<u;i++)
		{
			Var* v = a[i];
			if (!v) continue;
			VTRAP(v->type_invalid());
			VTRAP(v->parent==NULL);
			VTRAP(v->parent!=this);
			VTRAP(v->index!=i);

			if (v->type==isList) v->validate(file,line);
		}
	}

	#undef VTRAP
}

#define XXXCHECK(v)	if(XXXSAFE)(v)->validate(__FILE__,__LINE__)
#define XXCHECK(v)	if(XXSAFE) (v)->validate(__FILE__,__LINE__)
#define XCHECK(v)	if(XSAFE)  (v)->validate(__FILE__,__LINE__)
#define CHECK(v)	if(SAFE)   (v)->validate(__FILE__,__LINE__)

void Var::Validate ( ) const
{
	CHECK(this);
}


/* ----	make list valid ---------------------------------------
		in:  data void
		out: data valid
*/
inline void Var::init_list ( )
{
	list().array = NULL;
	list().size  = 0;
	list().used  = 0; 
}


/* ----	make list invalid -------------------------------------
		löscht alle variablen im array und delete[] array
		außer: size==0 => subslicer => don't destroy original!
		in:  data valid
		out: data void
*/
void Var::kill_list ( )
{
	if (list().size!=0) 	// if not subslicer
	{
		XXXTRAP(list().used>list().size);
		XXXTRAP(list().used>0&&list().array==NULL);

		Var** a = list().array;
		Var** e = a+list().used;
		while (a<e) delete *(--e);
		delete[] a;	
	}
}


/* ----	create copy of source vektor -----------------------
*/
void Var::copy_list ( cVar& q )
{	XXXCHECK(&q);
	XXXTRAP(q.type!=isList);
//	XXTRAP(q.Contains(this));			für diesen Test muss this valid sein!!!

	uint n = q.list().used;

	if (n==0) goto i;
	else
	{
		Var** qa = q.list().array;
		Var** za =   list().array = new Var*[n];
		if (za==NULL) goto x;
		memset(za,0,n*sizeof(Var*));
		list().used = list().size = n;
		
		do
		{	Var* v = qa[--n];
			if (v) 
			{
				za[n] = v = new Var(*v); 
				if (v==NULL) goto y; 
				v->parent=this; v->index=n; 
			}
		} while (n>0);
		return;
	}

y:	kill_list();
x:	SetError(outofmemory);
i:	init_list();
}


/* ----	fix parent pointers in list items ------------------
*/
inline void Var::fix_parents ( )
{
	if (type==isList && list().size!=0 )	// liste && !subslicer
	{
		Var** a = list().array;
		Var** e = a + list().used;
		while (a<e) { Var* v = *a++; if (v) v->parent = this; }

		XXXCHECK(this);
	}
}

/* ----	move list from Var q to Var this ----------------------
		in:  data void,  q.data valid
		out: data valid, q.data void
*/
void Var::move_list ( Var& q )
{
	list().array = q.list().array;
	list().size  = q.list().size;
	list().used  = q.list().used;
	fix_parents();
}


/* ----	purge data ------------------------------------------
		purge text of String or Proc
		purge List recursively
		in:  data valid
		out: data invalid
*/
void Var::kill_data ( )
{
	switch(type)
	{
	case isProc:
	case isText:	text()._kill(); break;
	case isList:	kill_list(); break;
	case isNumber:	break;
	}
}


void Var::init_data ( vtype v )
{
	type=v;

	switch(v)
	{
	case isProc:
	case isText: 	new(&text()) String; /*text()._init();*/ break;
	case isList:	init_list(); break;
	case isNumber:	value()=0.0; break;
	}
}


/* ----	copy source data from target data -----------------------------
		in:   data void		q.data valid
		out:  data valid	q.data valid
*/
void Var::copy_data ( cVar& q )
{	XXXTRAP  ( !this );
	XXXTRAP  ( &q==this );
	XXXCHECK ( &q );

	type   = q.type;
	
	switch(type)
	{
	case isProc:
	case isText:	text()._init(q.text()); break;
	case isList:	copy_list(q);           break;
	case isNumber:	value() = q.value();    break;
	}
}


/* ----	move source data from target data -----------------------------
		in:   data void		q.data valid
		out:  data valid	q.data void
*/
void Var::move_data ( Var& q )
{	XXXTRAP  ( !this );
	XXXTRAP  ( &q==this );
	XXXCHECK ( &q );

	type    = q.type;

	switch(type)
	{
	case isProc:
	case isText:	text()._move(q.text()); break;
	case isList:	move_list(q);			break;
	case isNumber:	value() = q.value();	break;
	}
}


/* ----	swap source data with target data -----------------------------
		in:   data valid	q.data valid
		out:  data valid	q.data valid
		note for lists: data must not intersect or circular list may result
*/
void Var::swap_data ( Var& q )
{	XXXCHECK ( &q );
	XXXCHECK ( this );
	XXXTRAP  ( Contains(q) );
	XXXTRAP  ( q.Contains(this) );

	Var z;
	XXXTRAP(z.type!=isNumber);
	z.move_data(q);
	q.move_data(*this);
	move_data(z);
	z.type=isNumber;
}


/* ----	setup link for list item -------------------------------------------
*/
void Var::init_link ( Var* par, uint idx )
{	XXXTRAP ( !this );
	XXXCHECK( par );
	XXXTRAP ( par->type!=isList );
	XXXTRAP ( par->IsSubslicer() );

	if ( idx>=par->list().used )
	{
		par->ResizeList(idx+1);
	}
	
	XXXTRAP ( par->list().array[idx]!=NULL );	

	par->list().array[idx] = this;
	parent = par;
	index  = idx;
}


/* ----	remove item from list -----------------
			slot in parent's list is set to NULL
2002-03-08:	if this is the last used item in the parent's list
			then all trailing NULL items in parent's list are removed from the used count
*/
void Var::kill_link ( )
{
	Var* p = parent;
	if (p) 
	{
		uint i = index;
		Var** a = p->list().array;
		a[i] = NULL;
		if (p->list().used==i+1)
		{
			while(i--&&!a[i]) {}
			p->list().used = i+1;
		}
	}
}


/* ----	find item by name --------------------------------------------------
		find named item in list
		returns found Var or NULL
		name may contain '.' for deeper levels
		named item is searched starting at the end of the list
*/
Var* Var::FindItem ( cString& nam ) const
{
	if (type==isList)
	{
		Var** a = list().array;
		Var** e = a+list().used;

		long dot = nam.Find('.');

		if(dot>=0)
		{
			String pnam = nam.LeftString(dot);

			while (  e > a )
			{
				Var* v = *--e;
				if ( v && pnam==v->name ) return v->FindItem(nam.MidString(dot+1));
			}
		}
		else
		{
			while (  e > a )
			{
				Var* v = *--e;
				if ( v && nam==v->name ) return v;
			}
		}
	}
	return NULL;
}


/* ----	creator, destructor ---------------------------------------------------
*/
Var::Var ( vtype t )
{
	init_link(); init_data(t);
	XXXCHECK(this);
}

Var::Var ( const String& nam, vtype typ )
:	name(nam)
{
	init_data(typ);
	init_link();
	XXXCHECK(this);
}

Var::Var ( double valu )
{
	init_data(valu);
	init_link();
	XXXCHECK(this);
}

Var::Var ( const String& nam, double valu )
:	name(nam)
{
	init_data(valu);
	init_link();
	XXXCHECK(this);
}

Var::Var ( const String& txt )
{
	init_data(txt);
	init_link();
	XXXCHECK(this);
}

Var::Var ( const String& nam, const String& txt )
:	name(nam)
{
	init_data(txt);
	init_link();
	XXXCHECK(this);
}


/* ----	Assignment ----------------------------------
		normal:     purge own data  &  copy source data
					wenn this eine Liste ist, wird der Inhalt rekursiv deleted!
					(bei Überschneidung von q mit z nur der nicht wiederverwertbare Teil)

		zusätzlich: prüfung auf überschneidung von quelle und ziel:

		wenn ziel==list && quelle element darin: 
			normales vorgehen würde quelldaten vernichten
			=> erst quelldaten aus list extrahieren

			=>  a = { b,c }  =>  a = a[0]  =>  a == b
			
			••• Wichtig ••• Es ist vielleicht nicht offensichtlich,
			aber in diesem Fall wird auch die Quellvariable q selbst deleted:
			q ist item in list z und alles was in z nicht re-used werden kann, wird deleted.
			würde q nicht deleted, wäre dies ein memory leak.

		wenn quelle==list && ziel element darin:
			normales vorgehen würde teil der quelldaten vernichten
			=> erst quelldaten duplizieren
			Anm.: es wäre möglich, unnötiges duplizieren der eigenen instanz (evtl. selbst ein fetter list!) zu vermeiden:
				  eigenen pfad bis zur quellinstanz merken => index-liste
				  eigene daten in scratch moven => eigene stelle jetzt leer
				  quelle nach ziel kopieren => es fehlt noch der eigene subtree
				  kopie der eigenen position anhand der index-liste suchen
				  daten aus scratch in die kopie der eigenen position moven
			
			=> a = { b,c }  =>  a[0] = a  =>  a == { {b,c} c }
*/

Var& Var::operator= ( cVar& q ) 
{	XXXCHECK( this );
	XXXCHECK( &q );

	if (this!=&q)
	{
		if ( IsList() && Contains(q) )		// z.B.:	a = a[n]
		{
			Var* z = &(Var&)q;
			z->unlink();
			kill_data();
			move_data(*z);
			z->init_data();
			delete z;
			goto x;		// q ist gelöscht!
		}
		else if ( q.IsList() && q.Contains(this) )		// z.B.:	a[n] = a
		{
			Var z(q);
			kill_data();
			move_data(z);
			z.init_data();
			XXXCHECK( &z );
		}
		else
		{
			kill_data();
			copy_data(q);
		}
	}

	XXXCHECK(&q);
x:	XXXCHECK(this);

	return *this;
}


/* ----	copy data from source ----------------------------------
		Daten dürfen sich nicht überschneiden!
*/
void Var::CopyData ( Var& q )
{	XXXCHECK ( this );
	XXXCHECK ( &q );
	XXXTRAP  ( Contains(q)      );
	XXXTRAP  ( q.Contains(this) );

	kill_data();
	copy_data(q);
}
	

/* ----	move data from source ----------------------------------------
		Daten dürfen sich nicht überschneiden!
*/
void Var::MoveData ( Var& q )
{	XXXCHECK ( this );
	XXXCHECK ( &q );
	XXXTRAP  ( Contains(q)      );
	XXXTRAP  ( q.Contains(this) );

	kill_data();
	move_data(q);
	q.init_data();
}


/* ----	swap data with source ----------------------------------
		Daten dürfen sich nicht überschneiden!
*/
void Var::SwapData ( Var& q )
{	XXXCHECK ( this );
	XXXCHECK ( &q );
	XXXTRAP  ( Contains(&q)     );
	XXXTRAP  ( q.Contains(this) );

	swap_data(q);
}
	

/* ----	resize list ------------------------------------------
		grow list   => loose fit
		shrink list => tight fit
*/
void Var::ResizeList ( uint newused )
{
	XTRAP(type!=isList);
	XTRAP(IsSubslicer());		// subslicer?
	XTRAP(newused>>26);			// oorange security
	XXXCHECK(this);

	if (newused>list().used)
	{
// grow list  =>  loose fit
		if (newused>list().size)
		{
			list().size = newused + 10;
			Var** newarray = new Var*[list().size]; OMEM(newarray);
			memcpy ( newarray, list().array, list().used*sizeof(Var*) );		// move items flat
			delete[] list().array;
			list().array = newarray;
		}

	//	memset ( list().array+list().used, 0, (newused-list().used)*sizeof(Var*) );
	//	list().used = newused;
		do { list().array[list().used++] = NULL; } while (list().used<newused);	// händisch 'createn'
	}

	else 
	{
// shrink list  =>  tight fit
		if (newused<list().size)	// else used==size==newused
		{
			while (list().used>newused) delete list().array[--list().used];		// händisch destroyen
			list().size = newused;
			Var** newarray = NULL;

			if (newused>0)
			{
				newarray = new Var*[newused];
				memcpy ( newarray,list().array,newused*sizeof(Var*) );			// move items flat
			}
	
			delete[] list().array;
			list().array = newarray;
		}
	}

	XXXCHECK(this);
}


/* ----	operator[] -------------------------------------------------
		increases list size if required  except if this list is a subslicer!
		creates list item if required    except if this list is a subslicer!
*/
Var& Var::operator[] ( uint idx )
{
	XTRAP(type!=isList);

	if (idx>=list().used) { XXTRAP((idx+1)==0); ResizeList(idx+1); }
	Var* v = list().array[idx];
	if (v==NULL) { list().array[idx] = v = new Var(this,idx); OMEM(v); }
	return *v;
}


/* ----	Insert empty items into list -------------------------------
		if idx>used then nothing is done, because accessing 
		items beyond used automatically creates them
*/
void Var::InsertItems ( uint idx, uint n )
{	XXXCHECK(this);
	XTRAP(type!=isList);
	XTRAP(IsSubslicer());

	if (n!=0 && 							// nothing to do?
	//	type==isList &&						// basic security
	//	list().size!=0 && 					// size==0&&used>0  =>  subslicer
		idx<list().used)					// idx>=used  =>  just grow list  =>  automatically done when accessing items via operator[]
	{
		XXXTRAP(n>>31);
		uint m = list().used+n;
		ResizeList(m);						// make room
		Var** array = list().array;

		while (m>idx+n) 					// move items up
		{
			m--;
			XXXTRAP(array[m]!=NULL);
			array[m] = array[m-n];
			if (array[m]) array[m]->index = m;
			array[m-n]=NULL;
		}

	/*	while (m>idx)						// init items in gap
		{
			m--;
			array[m] = NULL;
		}
	*/
	}

	XXXCHECK(this);
}


/* ----	insert item into list ----------------------------
*/
void Var::InsertItem ( uint idx, Var* q )
{	XXXCHECK( this );
	XXXCHECK( q );
//	XXTRAP  ( q->Contains(this) );		done by link()

	InsertItems(idx,1);
	q->link(this,idx);
}


/* ----	append item to list ----------------------------
*/
void Var::AppendItem ( Var* q )
{	XXXCHECK( this );
	XXXCHECK( q );
//	XXTRAP  ( q->Contains(this) );		done by link()

	q->link(this,list().used);
}


/* ----	Remove items from list -------------------------------
*/
void Var::DeleteItems ( uint idx, uint n )
{	XXXCHECK(this);
	XTRAP   (type!=isList);
	XTRAP   (IsSubslicer());
	
	if (n!=0 && 							// nothing to do?
	//	type==isList &&						// basic security
	//	list().size!=0 && 					// size==0&&used>0  =>  subslicer
		idx<list().used)					// idx>=used  =>  there's nothing to delete
	{
		if (n>=list().used-idx)
		{
			ResizeList(idx);
		}
		else
		{
			Var** array = list().array;

			for (uint i=idx; i<idx+n; i++)
			{
				delete array[i];
			}

			uint m = list().used-n;			
			list().used = m;

			for (uint i=idx; i<m; i++)
			{
				array[i] = array[i+n];
				if (array[i]) array[i]->index = i;
			}
		}
	}

	XXXCHECK(this);
}


/* ----	remove last item from list ---------------------------
		fast, no shrinking of array
*/
void Var::DeleteLastItem ( )
{	XXXCHECK(this);
	XTRAP   (type!=isList);
	XTRAP   (IsSubslicer());

	XXTRAP ( list().used==0 );
	delete list().array[--list().used];
}


/* ----	append list q to this list --------------------------
		in:  this and q must be lists
		out: the contents of q is appended to this list
		     q becomes an empty list
*/
void Var::AppendList ( Var& q )
{	XXXCHECK(this);
	XXXCHECK(&q);
	XTRAP   (type!=isList);
	XTRAP   (q.type!=isList);
	XXTRAP  (IsSubslicer());
	XXTRAP  (q.IsSubslicer());
	if (this==&q) return;
	XXTRAP  (q.Contains(this));

	uint  n = list().used;
	uint  m = q.list().used;
	ResizeList(n+m);
	Var** a = list().array;
	Var** b = q.list().array;
	
	while (m--)
	{
		if (b[m])
		{
			b[m]->parent = this;
			b[m]->index  = n+m;
			a[n+m] = b[m];
		}
	}

	delete[] b;
	q.init_list();

	XXXCHECK(this);
	XXXCHECK(&q);
}


/* ----	vergleiche zwei Variablen ---------------------------------------
		value und text: trivial
		proc: vergleich der Adressen
		list: analog zu strings: Vergleich elementweise
		mixed types: entsprechend Typ
*/				
bool Var::operator== ( const Var& q ) const
{	XXXCHECK(this);
	XXXCHECK(&q);
	
	if (type==q.type)
	{
		switch(type)
		{
		case isNumber:
				// *** 2002-05-20 kio: ***
				// *** note: ppc and i386 and maybe others return NaN != NaN ***
				// *** note: ppc and maybe others: if comparing data[] directly, then 0 != -0 ***
				// *** NaN!=NaN is probably IEEE compliant, so i'll leave it this way ***
				return value() == q.value() /* || (isnan(value())&&isnan(value())) */ ;
		case isText: //	return text()  == q.text();			text <=> proc
		case isProc:	return proc()  == q.proc();

		case isList:
			if (list().used!=q.list().used) return no;
			Var v;	// = 0.0
			uint i = list().used;
			while (i-->0)
			{
				Var* a = list().array[i];	if (!a) a=&v;
				Var* b = q.list().array[i];	if (!b) b=&v;
				if (*a!=*b) return no;
			}
			return yes;
		}
	}

	return no;				// mixed_type=no
}

bool Var::operator> ( const Var& q ) const
{	XXXCHECK(this);
	XXXCHECK(&q);

	if (type == q.type)
	{
		switch(type)
		{
		case isNumber:	return value() > q.value();
		case isText: //	return text()  > q.text();			text <=> proc
		case isProc:	return proc()  > q.proc();

		case isList:
			Var v;	// 0.0
			uint n = Min(list().used,q.list().used);
			for (uint i=0; i<n; i++)
			{
				Var* a = list().array[i];	if (!a) a=&v;
				Var* b = q.list().array[i];	if (!b) b=&v;
				if (*a!=*b) return *a>*b;
			}
			return list().used>q.list().used;
		}
	}

	return type>q.type;		// mixed type
}

bool Var::operator< ( const Var& q ) const
{	XXXCHECK(this);
	XXXCHECK(&q);

	if (type == q.type)
	{
		switch(type)
		{
		case isNumber:	return value() < q.value();
		case isText: //	return text()  < q.text();			text <=> proc
		case isProc:	return proc()  < q.proc();

		case isList:
			Var v;	// 0.0
			uint n = Min(list().used,q.list().used);
			for (uint i=0; i<n; i++)
			{
				Var* a = list().array[i];	if (!a) a=&v;
				Var* b = q.list().array[i];	if (!b) b=&v;
				if (*a!=*b) return *a<*b;
			}
			return list().used<q.list().used;
		}
	}

	return type<q.type;		// mixed type
}


/* ----	sort list -------------------------------------------------------
*/
#ifdef SWAP
#undef SWAP
#endif

#define TYPE		VarPtr
#define BEHIND(A,B)	(*(A)>*(B))
#define SWAP(A,B)	Swap((A),(B)),Swap((A)->index,(B)->index)
#define SORTER		Var::Sort

#include "sort/threshold-sorter-2.h"

void Var::Sort()
{
	if (type==isList)
	{
		for(uint i=list().used;i--;)if(list().array[i]==NULL) list().array[i]=new Var();		// workaroud: sorting with NULLs in list is difficult...
		if (list().used) Sort(list().array,list().array+list().used);
	}
}


/* ----	backward sort list ------------------------------------------------
*/
#define TYPE		VarPtr
#define BEHIND(A,B)	(*(A)<*(B))
#define SWAP(A,B)	Swap((A),(B)),Swap((A)->index,(B)->index)
#define SORTER		Var::RSort

#include "sort/threshold-sorter-2.h"

void Var::RSort()
{
	if (type==isList)
	{
		for(uint i=list().used;i--;)if(list().array[i]==NULL) list().array[i]=new Var();		// workaroud: sorting with NULLs in list is difficult...
		RSort(list().array,list().array+list().used);
	}
}


/* ----	Greater of two variables -----------------------
		returns a reference to the larger one
		=> Max(a,b)=c possible
		any data type may be passed (as in Sort())
		returns this if both are same
*/
Var& Var::MaxVar ( Var& q )
{
	if (*this>=q) return *this; else return q;
}


/* ----	Smaller of two variables -----------------------
		returns a reference to the smaller one
		=> Max(a,b)=c possible
		any data type may be passed (as in Sort())
		returns this if both are same
*/
Var& Var::MinVar ( Var& q )
{
	if (*this<=q) return *this; else return q;
}


/* ----	largest item of list -------------------------
		returns a reference to the largest item in list
		=> Max(a)=b possible
		returns this if this is no list or list.used==0
		any data type may be passed (as in Cmp())
		returns lowest index item if multiple items are same
*/
Var& Var::MaxVar ( )
{
	if (type==isList && list().used>0)
	{
		Var** array = list().array;
		uint i=0;
		Var* m = NULL;
		while(m==NULL&&i<list().used) m = array[i++];
		if (m==NULL) return (this)[0];	// operator[] creates item
		for (;i<list().used; i++) if (*array[i]>*m) m = array[i];
		return *m;
	}
	return *this;
}


/* ----	smallest item of list -------------------------
		returns a reference to the smallest item in list
		=> Min(a)=b possible
		returns this if this is no list or list.used==0
		any data type may be passed (as in Cmp())
		returns lowest index item if multiple items are same
*/
Var& Var::MinVar ( )
{
	if (type==isList && list().used>0)
	{
		Var** array = list().array;
		Var* m = array[0];
		if (m==NULL) return (this)[0];	// operator[] creates item
		for (uint i=1; i<list().used; i++) if (*array[i]<*m) m = array[i];
		return *m;
	}
	return *this;
}



/* ----	Convert Character Set -------------------------------
*/
void Var::ConvertTo ( CharEncoding ctype )
{
	if (type==isText)
	{
		text() = text().ConvertedTo(ctype);
	}
	else if (type==isList)
	{
		DISP();

		for (uint i=0;i<list().used;i++)
		{
			Var* v = list().array[i];
			if (v) v->ConvertTo(ctype);
		}
	}
}

void Var::ConvertFrom ( CharEncoding ctype )
{
	if (type==isText)
	{
		text() = text().ConvertedFrom(ctype);
	}
	else if (type==isList)
	{
		DISP();

		for (uint i=0;i<list().used;i++)
		{
			Var* v = list().array[i];
			if (v) v->ConvertFrom(ctype);
		}
	}
}



/* ----	Log to Log Window -------------------------------
		List variable contents to log window
*/
void Var::LogVar ( )
{	XXXTRAP(!this);
	XXXCHECK(this);

	switch(type)
	{
	case isNumber:	Log("%s ", CString(NumString(value())));		break;
	case isText:	Log(Using("%s ",CString(text().ToQuoted())));	break;
	case isProc: 	Log("proc ");									break;

	case isList:
		Log("{ ");
		
		for ( uint i=0; i<list().used; i++ ) 
		{
			DISP();
			Var* v = list().array[i];
			if (v && v->Name().Len()) Log(Using("%s=",CString(v->Name())));
			if (v) v->LogVar(); else Log("0 ");
		}

		Log("} ");
		break;
	}
}


/* ----	create listing of variable contents -------------------------------
		flag indicates whether to quote and escape strings
		listing of list may be *big*!
		flags:	quote_strings:	strings are quoted and special chars are escaped with '\'
				item_names:		names of named items in lists are included like { a=25 b=0.1 }
*/
String Var::ListVar ( bool quote_strings, bool item_names )
{	XXXTRAP(!this);
	XXXCHECK(this);

	switch(type)
	{
	default:		return NumString(value());			// isNumber
	case isProc:	return "proc";
	case isText:	return text().ToEscaped(quote_strings);

	case isList:
		String s("{ ");

		for ( uint i=0; i<list().used; i++ )
		{
			DISP();
			Var* v = list().array[i];
			if (v)
			{
				if ( item_names && v && v->Name().Len() ) s += v->Name() + "=";
				s += v->ListVar(yes,item_names) + (i==list().used-1?" ":", ");
			}
			else
			{
				s += (i==list().used-1?"0 ":"0, ");
			}
		}

		return s + "}";
	}
}


/* ----	convert variable to numeric value -------------------------

		as required for automatic type casting in vipsi

		number	->	number
		text	->	NumVal(text)
		proc	->	((can't eval to number))
		List	->	((recursive; not handled here))
*/
double Var::ToNumber ( ) const
{
	if (type==isNumber) return value();
	if (type==isText)   return text().NumVal();
	SetError(notanumber);
	return NAN;
}


/* ----	convert Variable to String ----------------------------

		as required for automatic type casting in vipsi
		currently only for operator: text # text

		note:					vipsi function string(x) behaves differently

		number	-> "123.456"
		string	-> as is
		List	-> "{ 1, 2, 3, *, "text\n", ... }"
		Proc	-> disass
*/
String Var::ToString ( bool quotestring, String(*disass)(cString&) ) const
{
	switch(type)
	{
	default:		return NumString(value());			// isNumber
	case isText:	return quotestring ? text().ToQuoted() : text();
	case isProc:	return disass ? (*disass)(text()) : "proc(..){..}";
	case isList:
		if(list().used==0) return "{ }";

		Var* v = list().array[0];
		String s = "{ ";
		s += v ? v->ToString(yes,disass) : "0";
		String sep = ", ";

		for ( uint i=1; i<list().used; i++ )
		{
			DISP();
			v = list().array[i];
			s += v ? sep + v->ToString(yes,disass) : ", 0";
		}

		return s + " }";
	}
}


/* ---- convert Variable to List ------------------------------

		as required for vipsi: List ## List

		number	->	{ number }
		text	->	{ text }
		proc	->	{ proc }
		List	->	as is
*/
void Var::ConvertToList ( )
{
	if(IsNoList())
	{
		Var* z = new Var(*this);
		SetList();
		AppendItem(z);
	}
}


/* ----	split string at character ------------------------------
		""			-> { }
		"c"			-> { "" }
		"---"		-> { "---" }
		"---c---"	-> { "---", "---" }
		"c---"		-> { "", "---" }
		"---c"		-> { "---" }
*/
void Var::SplitString ( UCS4Char c )
{
	String s;

	switch(type)
	{
	case isText:
		s = text();
		break;
	case isList:
		for(uint i=0;i<list().used;i++)
		{ Var* v = list().array[i]; if (v) v->SplitString(c); };
		return;
	default:
		s = ToString(no,NULL);
		break;
	}

	SetList();

	long i=0,j;
	while ( (j=s.Find(c,i))>=0 )
	{
		AppendItem(new Var(s.SubString(i,j)));
		i = j+1;
		while(i<s.Len()&&s[i]==c) i++;
	}
	if (i<s.Len()) AppendItem(new Var(s.MidString(i)));
}


/* ---- var OP= number ----------------------------------------
*/
Var& Var::ListOp ( Var&(Var::*op)(double), double q)
{
	XXTRAP(IsNoList());
	Var** a = list().array;
	uint  n = list().used;
	while (n--) if (a[n]) (a[n]->*op)(q);
	return *this;
}

Var& Var::operator+= ( double q )
{
	if (type==isNumber) { value()+=q; return *this; }
	if (type!=isList)   { return operator=(ToNumber()+q); }
	return ListOp( &Var::operator+=, q );
}

Var& Var::operator-= ( double q )
{
	if (type==isNumber) { value()-=q; return *this; }
	if (type!=isList)   { return operator=(ToNumber()-q); }
	return ListOp( &Var::operator-=, q );
}

Var& Var::operator*= ( double q )
{
	if (type==isNumber) { value()*=q; return *this; }
	if (type!=isList)   { return operator=(ToNumber()*q); }
	return ListOp( &Var::operator*=, q );
}

Var& Var::operator/= ( double q )
{
	if (type==isNumber) { value()/=q; return *this; }
	if (type!=isList)   { return operator=(ToNumber()/q); }
	return ListOp( &Var::operator/=, q );
}

Var& Var::operator%= ( double q )
{
	if (type==isNumber) { value()=fmod(value(),q); return *this; }
	if (type!=isList)   { return operator=(fmod(ToNumber(),q)); }
	return ListOp( &Var::operator%=, q );
}

Var& Var::operator>>= ( double q )
{
	if (type==isNumber) { a: value() = ldexp(value(),-int(floor(0.5+q))); /* ulong(value())>>int(q); */ return *this; }
	if (type!=isList)   { *this=ToNumber(); goto a; }
	return ListOp( &Var::operator>>=, q );
}

Var& Var::operator<<= ( double q )
{
	if (type==isNumber) { a: value() = ldexp(value(),int(floor(0.5+q))); /* ulong(value())<<int(q); */ return *this; }
	if (type!=isList)   { *this=ToNumber(); goto a; }
	return ListOp( &Var::operator<<=, q );
}

Var& Var::operator&= ( double q )
{
	if (type==isNumber) { a: value()=ulong(value()) & ulong(q); return *this; }
	if (type!=isList)   { *this=ToNumber(); goto a; }
	return ListOp( &Var::operator&=, q );
}

Var& Var::operator|= ( double q )
{
	if (type==isNumber) { a: value()=ulong(value()) | ulong(q); return *this; }
	if (type!=isList)   { *this=ToNumber(); goto a; }
	return ListOp( &Var::operator|=, q );
}

Var& Var::operator^= ( double q )
{
	if (type==isNumber) { a: value()=ulong(value()) ^ ulong(q); return *this; }
	if (type!=isList)   { *this=ToNumber(); goto a; }
	return ListOp( &Var::operator^=, q );
}


/* ---- var OP= var ----------------------------------------
*/
Var& Var::ListOp ( Var&(Var::*op)(double), cVar& q)
{	Var **qa,**za,*qp,*zp;
	uint n;

	XXTRAP(q.IsNoList());

	qa = q.list().array;
	n  = q.list().used;

	if (IsList())
	{
		za = list().array;
		n  = Min(n,list().used);
		while(n--)
		{
			if (!(zp=za[n])) continue;
			if (!(qp=qa[n])) continue;
			if (qp->IsNumber()) { (zp->*op)(qp->value()); continue; }
			if (!qp->IsList())  { (zp->*op)(qp->ToNumber()); continue; }
			zp->ListOp(op,*qp);
		}
	}
	else
	{
		float f = ToNumber();
		SetList(); ResizeList(n);
		za = list().array;
		while(n--)
		{
			if (!(qp=qa[n])) continue;
			zp = new Var(qp->Name(),f);
			zp->init_link(this,n);
			if (qp->IsList()) zp->ListOp(op,*qp);
			else (zp->*op)(qp->ToNumber());
		}
	}

	return *this;
}


#define OPERATOR(OPG)											\
Var& Var::operator OPG ( cVar& q )								\
{																\
	if (q.type==isNumber) return operator OPG(q.value());		\
	if (q.type!=isList)   return operator OPG(q.ToNumber());	\
	return ListOp(&Var::operator OPG,q);						\
}

OPERATOR(+=)
OPERATOR(-=)
OPERATOR(*=)
OPERATOR(/=)
OPERATOR(%=)
OPERATOR(>>=)
OPERATOR(<<=)
OPERATOR(&=)
OPERATOR(|=)
OPERATOR(^=)

#undef OPERATOR



/* ---- var OP number ----------------------------------------
		var OP var
*/
#define OPERATOR(OP,OPG)					\
Var Var::OP ( double q ) const				\
{											\
	Var z(*this);							\
	return z.OPG(q);						\
}											\
Var Var::OP ( cVar& q ) const				\
{											\
	Var z(*this);							\
	return z.OPG(q);						\
}

OPERATOR(operator +, operator +=)
OPERATOR(operator -, operator -=)
OPERATOR(operator *, operator *=)
OPERATOR(operator /, operator /=)
OPERATOR(operator %, operator %=)
OPERATOR(operator >>,operator >>=)
OPERATOR(operator <<,operator <<=)
OPERATOR(operator &, operator &=)
OPERATOR(operator |, operator |=)
OPERATOR(operator ^, operator ^=)

#undef OPERATOR


// *********************************************************
//				TEXT CONCATENATION
// *********************************************************

/* ---- var #= list ----------------------------------------
*/
Var& Var::append_list ( cVar& q, String(*disass_proc)(cString&) )
{	Var **qa,**za,*qp,*zp;
	uint n;

	XXXTRAP(q.IsNoList());

	qa = q.list().array;
	n  = q.list().used;

	if (IsList())	// list #= list
	{
		za = list().array;
		n  = Min(n,list().used);
		while(n--)
		{
			if (!(zp=za[n])) continue;
			if (!(qp=qa[n])) continue;
			if (qp->IsText())   { zp->AppendString(qp->text(),disass_proc); continue; }
			if (qp->IsNoList()) { zp->AppendString(qp->ToString(no,disass_proc),disass_proc); continue; }
			zp->append_list(*qp,disass_proc);
		}
	}
	else
	{
		String f = ToString(no,disass_proc);
		SetList(); ResizeList(n);
		za = list().array;
		while(n--)
		{
			if (!(qp=qa[n])) continue;
			zp = new Var(qp->Name(),f);
			zp->init_link(this,n);
			if (qp->IsList()) zp->append_list(*qp,disass_proc);
			else zp->AppendString(qp->ToString(no,disass_proc),disass_proc);
		}
	}

	return *this;
}

Var& Var::AppendString ( cString& q, String(*disass_proc)(cString&) )
{
	if (IsText())   {                                   text()+=q; return *this; }
	if (IsNoList()) { *this = ToString(no,disass_proc); text()+=q; return *this; }

	Var** a = list().array;
	uint  n = list().used;
	while (n--) if (a[n]) a[n]->AppendString(q,disass_proc);
	return *this;
}

Var& Var::AppendString ( cVar& q, String(*disass_proc)(cString&) )
{
	if (q.IsText())   return AppendString(q.text(),disass_proc);
	if (q.IsNoList()) return AppendString(q.ToString(no,disass_proc),disass_proc);
	return append_list(q,disass_proc);
}

Var Var::ConcatString ( cString& q, String(*disass_proc)(cString&) ) const
{
	Var z(*this);
	return z.AppendString(q,disass_proc);
}

Var Var::ConcatString ( cVar& q, String(*disass_proc)(cString&) ) const
{
	Var z(*this);
	return z.AppendString(q,disass_proc);
}


/* ---- text concatenation ----------------------------------------
		text+text = text
		zahl+text = text
		list+text = list
*/
Var& Var::operator+= ( cString&  q  )
{
	if (IsText())   {                    text()+=q; return *this; }
	if (IsNoList()) { ToString(no,NULL); text()+=q; return *this; }

	Var** a = list().array;
	uint  n = list().used;
	while (n--) if (a[n]) a[n]->operator+=(q);
	return *this;
}

Var Var::operator+ ( cString&  q  ) const
{
	Var z(*this);
	return z.operator+=(q);
}




/* ----	Boolean ----------------------
*/
bool Var::operator&& ( double q ) const
{
	if (IsNumber()) return value()    && q;
	if (IsNoList()) return ToNumber() && q;
//	if (IsList())
	SetError(notanumber); return 0;
}

bool Var::operator|| ( double q ) const
{
	if (IsNumber()) return value()    || q;
	if (IsNoList()) return ToNumber() || q;
//	if (IsList())
	SetError(notanumber); return 0;
}

bool Var::operator&& ( cVar& q ) const
{
	if (q.IsNumber()) return operator&&(q.value());
	if (q.IsNoList()) return operator&&(q.ToNumber());
//	if (IsList())
	SetError(notanumber); return 0;
}

bool Var::operator|| ( cVar& q ) const
{
	if (q.IsNumber()) return operator||(q.value());
	if (q.IsNoList()) return operator||(q.ToNumber());
//	if (IsList())
	SetError(notanumber); return 0;
}












