[runtime tests] Added a couple more RDD tests

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING XUnit
USING System.Globalization

BEGIN NAMESPACE XSharp.VO.Tests

	CLASS TransFormTests

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformLogicalTest() AS VOID
			// with @R other chars are appended, but only a single YN or TF flag is written. Other positions become a space
			Assert.Equal("Y", Transform(TRUE, "Y"))
			Assert.Equal("N", Transform(FALSE, "Y"))
			Assert.Equal("T", Transform(TRUE, "L"))
			Assert.Equal("F", Transform(FALSE, "L"))
			Assert.Equal("T x", Transform(TRUE, "@R L x"))
			Assert.Equal("F x", Transform(FALSE, "@R L x"))
			Assert.Equal("Y x", Transform(TRUE, "@R YYx"))
			Assert.Equal("N x", Transform(FALSE, "@R YYx"))
			SetNatDll("French.DLL")
			Assert.Equal("O", Transform(TRUE, "Y"))
			Assert.Equal("N", Transform(FALSE, "Y"))
			Assert.Equal("T", Transform(TRUE, "L"))     // you would expect V but VO returns T
			Assert.Equal("F", Transform(FALSE, "L"))
			Assert.Equal("T x", Transform(TRUE, "@R L x"))  // you would expect V but VO returns T x
			Assert.Equal("F x", Transform(FALSE, "@R L x"))
			Assert.Equal("O x", Transform(TRUE, "@R YYx"))
			Assert.Equal("N x", Transform(FALSE, "@R YYx"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformDateTest() AS VOID
			// with @R other chars are appended, but only a single YN or TF flag is written. Other positions become a space
            SetCentury(TRUE)
			Assert.Equal("10/12/2010", Transform(2010.12.10,"@E"))
			Assert.Equal("20/11/2010", Transform(2010.11.20,"@E"))
			SetDateFormat("dd-mm-yyyy")
			Assert.Equal("20-11-2010", Transform(2010.11.20,"@D"))
			SetDateFormat("dd-mm-yy")
			Assert.Equal("20-11-10", Transform(2010.11.20,"@D"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformStringTest() AS VOID
			Assert.Equal("ABC", Transform("abc", "@!"))
			Assert.Equal("AbC", Transform("abc", "!a!"))
			Assert.Equal("1234 AB", Transform("1234ab", "@R! 9999 AA"))
			Assert.Equal("1234 AB", Transform("1234ab", "@R 9999 !!"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformLongTest() AS VOID
			SetDecimalSep('.')
			SetThousandSep(',')
			Assert.Equal("  1", Transform(1, "999"))
			Assert.Equal("123", Transform(123, "###"))
			Assert.Equal("**", Transform(123, "##"))
			Assert.Equal("**", Transform(123, "99"))
			Assert.Equal("(1)", Transform(-1, "@( 99"))
			Assert.Equal("(-1)", Transform(-1, "@( 999"))
			Assert.Equal("( -1)", Transform(-1, "@( 9999"))
			Assert.Equal("(-1)", Transform(-1, "@) 9999"))
			Assert.Equal(" (-1)", Transform(-1, "@) 99999"))
			Assert.Equal("   ", Transform(0, "@Z 999"))
			Assert.Equal(" -1 DB", Transform(-1, "@X 999"))
			Assert.Equal(" -1 DB", Transform(-1, "@XC 999"))
			Assert.Equal("  1 CR", Transform(1, "@XC 999"))
			Assert.Equal("  1 CR", Transform(1, "@C 999"))
			Assert.Equal("  0", Transform(0, "@C 999"))
			Assert.Equal("1,234", Transform(1234, "9,999"))
			SetDecimalSep(',')
			SetThousandSep('.')
			Assert.Equal("1.234", Transform(1234, "9,999"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformFLoatTest() AS VOID
			SetDecimalSep('.')
			SetThousandSep(',')
			Assert.Equal("1.23", Transform(1.23, "9.99"))
			Assert.Equal("1,23", Transform(1.23, "@E 9.99"))
			Assert.Equal("*.**", Transform(12.34, "9.99"))
        [Fact, Trait("Category", "TransForm")];
        METHOD TransformNumericTest AS VOID
			SetDecimalSep(',')
			SetThousandSep('.')
            Assert.Equal("      2,45", Transform (    2.45 , "@R 999,999.99" ) )
            Assert.Equal("     -2,45", Transform (   -2.45 , "@R 999,999.99" ) )
            Assert.Equal("    124,00", Transform (     124 , "@R 999,999.99" )  )
            Assert.Equal("   -124,00", Transform (    -124 , "@R 999,999.99" )  )

        [Fact, Trait("Category", "Unformat")];
        METHOD UnformatCTest AS VOID
            Assert.Equal("8161XV", Unformat("8161 XV", "@R! 9999 AA","C"))
            Assert.Equal("8161 XV", Unformat("8161 XV", "@! 9999 AA","C"))

       [Fact, Trait("Category", "Unformat")];
        METHOD UnformatNTest AS VOID
			SetDecimalSep(',')
			SetThousandSep('.')
            Assert.Equal( 1.234d, (REAL8)(FLOAT) Unformat("1,234", "9.999","N"))
            Assert.Equal( 1234567.89d, (REAL8)(FLOAT) Unformat("1.234.567,89", "9,999,999.99","N"))

// some new failing tests:
	[Fact, Trait("Category", "TransForm")];
	METHOD IntLiteralTest() AS VOID
		LOCAL dec AS DWORD
		LOCAL thou AS DWORD

		dec := SetDecimalSep(Asc(","))
		thou := SetThousandSep(Asc("."))
		Assert.Equal(" 12.345,00" , Transform(12345.0, "999,999.99"))
		// this fails:
		Assert.Equal(" 12.345,00" , Transform(12345, "999,999.99"))

		SetDecimalSep(Asc("."))
		SetThousandSep(Asc(","))
		Assert.Equal(" 12,345.00" , Transform(12345.0, "999,999.99"))
		Assert.Equal(" 12,345.00" , Transform(12345, "999,999.99"))

		SetDecimalSep(dec)
		SetThousandSep(thou)
	RETURN

// Tests that need to be moved in separate files in VO.Tests:
	[Fact, Trait("Category", "StringEq")];
	METHOD StringEquality() AS VOID
		LOCAL c1,c2 AS STRING
		LOCAL u1,u2 AS USUAL
		LOCAL exact AS LOGIC
		u1 := c1 := "TESTLONG"
		u2 := c2 := "TEST"
		exact := SetExact( FALSE )

		Assert.Equal(TRUE, c1 = c2)
		Assert.Equal(TRUE, c1 != c2)

		Assert.Equal(TRUE, u1 = u2)
		Assert.Equal(FALSE, u1 != u2)

		SetExact( exact )
	RETURN

	[Fact, Trait("Category", "Str")];
	METHOD StrTests() AS VOID
		LOCAL exact,thou,digit,decimal,fixed_,digitfixed AS USUAL
		exact := SetDecimalSep(Asc("."))
		thou := SetThousandSep(Asc(","))
		digit := SetDigit(6)
		decimal := SetDecimal(3)
		fixed_ := SetFixed(FALSE)
		digitfixed := SetDigitFixed(FALSE)

		Assert.Equal( "     1.2"			, Str(1.2) )
		Assert.Equal( "    12.3"			, Str(12.3) )
		Assert.Equal( "    12.34"			, Str(12.34) )
		Assert.Equal( "   123.456"			, Str(123.456) )
		Assert.Equal( "  1234.567"			, Str(1234.567) )
		Assert.Equal( "  1234.5678"			, Str(1234.5678) )
		Assert.Equal( "  1234.5679012345"	, Str(1234.5679012345) )
		Assert.Equal( "123456.78901234568"	, Str(123456.78901234567) )
		Assert.Equal( "999999" , Str(999999) )
		Assert.Equal( "******" , Str(1000000) )

		SetDecimalSep(exact)
		SetThousandSep(thou)
		SetDigit(digit)
		SetDecimal(decimal)
		SetFixed(fixed_)
		SetDigitFixed(digitfixed)
	RETURN

	[Fact, Trait("Category", "Str")];
	METHOD Str_With_SetFixed_TRUE() AS VOID

	   SetDecimalSep(Asc("."))
	   SetThousandSep(Asc(","))
	   SetDecimal(3)
	   SetDigit(12)

	   SetFixed(TRUE)
	   SetDigitFixed(TRUE)

	   Assert.Equal( Str(12.34)            		, "          12.340")
	   Assert.Equal( Str1(12.34)          		, "          12.340")
	   Assert.Equal( Str(123.456)         		, "         123.456")
	   Assert.Equal( Str1(123.456)       		, "         123.456")
	   Assert.Equal( Str(1230.4567)         	, "        1230.457")
	   Assert.Equal( Str1(1230.4567)        	, "        1230.457")
	   Assert.Equal( Str(100000001234.4567)		, "100000001234.457")
	   Assert.Equal( Str1(100000001234.4567)	, "100000001234.457")
	   Assert.Equal( Str(1000000001234.4567)	, "****************")
	   Assert.Equal( Str1(1000000001234.4567)	, "****************")
	   Assert.Equal( Str(12.3)              	, "          12.300")
	   Assert.Equal( Str1(12.3)            		, "          12.300")
	   Assert.Equal( Str(12.34567)        	 	, "          12.346")
	   Assert.Equal( Str1(12.34567)        		, "          12.346")
	   Assert.Equal( Str(12.34 , 10, 2)    		, "     12.34")
	   Assert.Equal( Str(12.34 , 5, 2)     		, "12.34")
	   Assert.Equal( Str(12.34 , 4, 2)     		, "****") // VO wrong: 12.3
	   Assert.Equal( Str(12.34 , 4, 1)     		, "12.3")
	   Assert.Equal( Str(12.34 , 2, 1)     		, "**")
	   Assert.Equal( Str3(12.34 , 10, 2)   		, "     12.34")
	   Assert.Equal( Str3(12.34 , 5, 2)    		, "12.34")
	   Assert.Equal( Str3(12.34 , 4, 2)     	, "****") // VO wrong: 12.3
	   Assert.Equal( Str3(12.34 , 4, 1)     	, "12.3")
	   Assert.Equal( Str3(12.34 , 2, 1)    		, "**")
	   Assert.Equal( Str(-12.34)            	, "         -12.340")
	   Assert.Equal( Str1(-12.34)           	, "         -12.340")
	   Assert.Equal( Str(-12.3456)         		, "         -12.346")
	   Assert.Equal( Str1(-12.3456)         	, "         -12.346")
	   Assert.Equal( Str(-12.34 , 10, 2)    	, "    -12.34")
	   Assert.Equal( Str(-12.34 , 5, 2)     	, "*****")	 //VO wrong: -2.34
	   Assert.Equal( Str(-12.34 , 4, 2)     	, "****")    // VO wrong: -12.
	   Assert.Equal( Str(-12.34 , 4, 1)     	, "****")    //VO wrong: -2.3
       Assert.Equal( Str(-12.34 , 4, 0)     	, " -12")    //VO wrong: -2.3
	   Assert.Equal( Str(-12.34 , 2, 1)     	, "**")
	RETURN

	[Fact, Trait("Category", "Str")];
	METHOD Str_With_SetFixed_FALSE() AS VOID

	   SetDecimalSep(Asc("."))
	   SetThousandSep(Asc(","))
	   SetDecimal(3)
	   SetDigit(12)

	   SetFixed(FALSE)
	   SetDigitFixed(FALSE)

	   Assert.Equal( Str(12.34)            		, "          12.34")
	   Assert.Equal( Str1(12.34)          		, "          12.34")
	   Assert.Equal( Str(123.456)         		, "         123.456")
	   Assert.Equal( Str1(123.456)				, "         123.456")
	   Assert.Equal( Str(1230.4567)         	, "        1230.4567")
	   Assert.Equal( Str1(1230.4567)        	, "        1230.4567")
	   Assert.Equal( Str(100000001234.4567)		, "100000001234.4567")
	   Assert.Equal( Str1(100000001234.4567)	, "100000001234.4567")
	   Assert.Equal( Str(1000000001234.4567)	, "*****************")
	   Assert.Equal( Str1(1000000001234.4567)	, "*****************")
	   Assert.Equal( Str(12.3)              	, "          12.3")
	   Assert.Equal( Str1(12.3)            		, "          12.3")
	   Assert.Equal( Str(12.34567)         		, "          12.34567")
	   Assert.Equal( Str1(12.34567)        		, "          12.34567")
	   Assert.Equal( Str(12.34 , 10, 2)    		, "     12.34")
	   Assert.Equal( Str(12.34 , 5, 2)     		, "12.34")
	   Assert.Equal( Str(12.34 , 4, 2)     		, "****") // VO returns 12.3
	   Assert.Equal( Str(12.34 , 4, 1)     		, "12.3")
	   Assert.Equal( Str(12.34 , 2, 1)     		, "**")
	   Assert.Equal( Str3(12.34 , 10, 2)   		, "     12.34")
	   Assert.Equal( Str3(12.34 , 5, 2)    		, "12.34")
	   Assert.Equal( Str3(12.34 , 4, 2)     	, "****")   // VO 12.3
	   Assert.Equal( Str3(12.34 , 4, 1)     	, "12.3")
	   Assert.Equal( Str3(12.34 , 2, 1)    		, "**")
	   Assert.Equal( Str(-12.34)            	, "         -12.34")
	   Assert.Equal( Str1(-12.34)           	, "         -12.34")
	   Assert.Equal( Str(-12.3456)         		, "         -12.3456")
	   Assert.Equal( Str1(-12.3456)         	, "         -12.3456")
	   Assert.Equal( Str(-12.34 , 10, 2)    	, "    -12.34")
	   Assert.Equal( Str(-12.34 , 5, 2)     	, "*****")	 //VO: -2.34
	   Assert.Equal( Str(-12.34 , 4, 2)     	, "****")    // VO
	   Assert.Equal( Str(-12.34 , 4, 1)     	, "****")    //VO: -2.3
	   Assert.Equal( Str(-12.34 , 2, 1)     	, "**")

	[Fact, Trait("Category", "Val")];
	METHOD ValTests() AS VOID
		SetDecimalSep(',')
		SetThousandSep('.')
		Assert.Equal(123, (INT) Val("123") )
		Assert.Equal(123.456, (FLOAT) Val("123,456") )

		Assert.Equal(0, (INT) Val("") )

		Assert.Equal(0, (INT) Val("abc") )
		Assert.Equal(123, (INT) Val("123abc") )
		Assert.Equal(123, (INT) Val("123abc456") )
		Assert.Equal(123, (INT) Val("123abc456,789") )
		Assert.Equal(0, (INT) Val("abc123456,789") )
		Assert.Equal(255, (INT) Val("0xFF") )
		Assert.Equal(0xFFFF, (INT) Val("0xFFFF") )
		Assert.Equal(4294967295, (INT64) Val("0xFFFFFFFF") )
		Assert.Equal(11, (INT) Val("11L11") )

		Assert.Equal(1.0001, (FLOAT) Val("1,000.1") )
		Assert.Equal(1.0011, (FLOAT) Val("1,001.1") )
		SetDecimalSep('.')
		SetThousandSep(',')
		Assert.Equal(1000.1, (FLOAT) Val("1,000.1") )
		Assert.Equal(1001.1, (FLOAT) Val("1,001.1") )
	RETURN

	[Fact, Trait("Category", "SplitPath")];
	METHOD SplitPathTests() AS VOID
		LOCAL cDrive := "",cDir := "",cFile := "",cExt := "" AS STRING
		SplitPath("C:\folder\file.ext" ,  REF cDrive , REF cDir , REF cFile , REF cExt)

		Assert.Equal("C:" , cDrive)
		Assert.Equal("\folder\" , cDir)

		Assert.Equal("file" , cFile)
		Assert.Equal(".ext" , cExt)
	RETURN

	[Fact, Trait("Category", "SplitPathVO")];
	METHOD SplitPathVO() AS VOID
		LOCAL cPath ,cDrive ,cDir ,cFile ,cExt AS STRING
		LOCAL pszDrive, pszDir AS PSZ
		LOCAL pszFile, pszExt AS PSZ

		cPath := "C:\testfolder\testfile.prg"

		pszDrive := MemAlloc(2+1)
		pszDir   := MemAlloc(255+1)
		pszFile  := MemAlloc(255+1)
		pszExt   := MemAlloc(7+1)

		SplitPath( String2Psz(cPath), pszDrive, pszDir, pszFile, pszExt)

		cDrive := Psz2String(pszDrive)
		cDir   := Psz2String(pszDir)
		cFile  := Psz2String(pszFile)
		cExt   := Psz2String(pszExt)

		MemFree(pszDrive)
		MemFree(pszDir)
		MemFree(pszFile)
		MemFree(pszExt)

		Assert.Equal("\testfolder\" , cDir)
		Assert.Equal("\testfolder\" , cDir)
		Assert.Equal("testfile" , cFile)
		Assert.Equal(".prg" , cExt)
	RETURN

	[Fact, Trait("Category", "Directory")];
	METHOD DirectoryTests() AS VOID
		Assert.Equal(0u , ALen(Directory("K:\drive does not exist")))
		Assert.Equal(0u , ALen(Directory("K:\drive does not exist","V")))
		Assert.Equal(0u , ALen(Directory("C:\invalid:path")))
		Assert.Equal(1u , ALen(Directory("C:\invalid:path","V")))
		Assert.Equal(0u , ALen(Directory("C:invalid:path")))
		Assert.Equal(1u , ALen(Directory("C:invalid:path","V")))
		Assert.Equal(0u , ALen(Directory("N:")))
		Assert.Equal(0u , ALen(Directory("N:","V")))

	[Fact, Trait("Category", "Empty")];
	METHOD EmptyFuncTests() AS VOID
		Assert.True(Empty(0))
		Assert.True(Empty(FALSE))
		Assert.True(Empty(""))
		Assert.True(Empty(Chr(10) + Chr(9) + Chr(13)))
		Assert.True(Empty(NULL_SYMBOL))
		Assert.True(Empty(0000.00.00))
		Assert.True(Empty({}))

		Assert.True(Empty(NULL_PTR))
		LOCAL p AS PTR
		p := NULL_PTR
		Assert.True(Empty(p))

		Assert.True(Empty(NULL_PSZ))
		LOCAL z AS PSZ
		z := NULL_PSZ
		Assert.True(Empty(z))

		Assert.False(Empty(1))
		Assert.False(Empty(TRUE))
		Assert.False(Empty("a"))
		Assert.False(Empty(Chr(1)))
		Assert.False(Empty(#abc))
		Assert.False(Empty(2000.01.01))
		Assert.False(Empty({{}}))

		p := @p
		Assert.False(Empty(p))
		z := String2Psz("test")
		Assert.False(Empty(z))

	[Fact, Trait("Category", "AClone")];
	METHOD AClone_Tests() AS VOID
		Assert.True(ALen(AClone({1}))==1)
		Assert.True(ALen(AClone({1,2}))==2)
		Assert.True(  AClone({1,2})[2] ==2  )
		Assert.True(  AClone({1,{2,3},4})[2,2] == 3  )
		Assert.True(  AClone({1,{},4})[3] == 4  )

		Assert.True(AClone(NULL_ARRAY)==NULL_ARRAY)
		LOCAL aNull := NULL_ARRAY AS ARRAY
		Assert.True(AClone(aNull)==NULL_ARRAY)
		Assert.True(ALen(AClone({}))==0)
		Assert.True(ALen(AClone({{}}))==1)

	[Fact, Trait("Category", "ACloneShallow")];
	METHOD ACloneShallow_Tests() AS VOID
		Assert.True(ALen(ACloneShallow({1}))==1)
		Assert.True(ALen(ACloneShallow({1,2}))==2)
		Assert.True(  ACloneShallow({1,2})[2] ==2  )
		Assert.True(  ACloneShallow({1,{2,3},4})[2,2] == 3  )
		Assert.True(  ACloneShallow({1,{},4})[3] == 4  )

		LOCAL aSub AS ARRAY
		aSub := {1,2}
		Assert.True(  ACloneShallow({1,aSub,3})[2] == aSub  )

		Assert.True(ACloneShallow(NULL_ARRAY)==NULL_ARRAY)
		LOCAL aNull AS ARRAY
		aNull := NULL_ARRAY
		Assert.True(ACloneShallow(aNull)==NULL_ARRAY)
		Assert.True(ALen(ACloneShallow({}))==0)
		Assert.True(ALen(ACloneShallow({{}}))==1)

	[Fact, Trait("Category", "AClone")];
	METHOD ACloneWith_NULL_ARRAY_as_Elements() AS VOID
		Assert.True(  ALen(  AClone({NULL_ARRAY,NULL_ARRAY}) ) == 2  )
		Assert.True(  AClone({NULL_ARRAY})[1] == NULL_ARRAY  )

		Assert.True(  ALen(  ACloneShallow({NULL_ARRAY,NULL_ARRAY}) ) == 2  )
		Assert.True(  ACloneShallow({NULL_ARRAY})[1] == NULL_ARRAY  )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBCreate_Tests() AS VOID
		LOCAL aFields AS ARRAY
		aFields := {{"TEST","C",10,0}}

		LOCAL cFileName_WithExt AS STRING
		LOCAL cFileName_NoExt AS STRING
		cFileName_NoExt := "C:\TEMP\testdbf"
		cFileName_WithExt := cFileName_NoExt + ".dbf"
		IF System.IO.File.Exists(cFileName_WithExt)
			System.IO.File.Delete(cFileName_WithExt)
		END IF
		Assert.True(  DBCreate(cFileName_NoExt , aFields , "DBFNTX")  )
		Assert.True(  System.IO.File.Exists(cFileName_WithExt) )

		IF System.IO.File.Exists(cFileName_WithExt)
			System.IO.File.Delete(cFileName_WithExt)
		END IF
		Assert.True(  DBCreate(cFileName_WithExt , aFields , "DBFNTX")  )
		Assert.True(  System.IO.File.Exists(cFileName_WithExt) )

		cFileName_WithExt := cFileName_NoExt + ".none"
		IF System.IO.File.Exists(cFileName_WithExt)
			System.IO.File.Delete(cFileName_WithExt)
		END IF
		Assert.True(  DBCreate(cFileName_WithExt , aFields , "DBFNTX")  )
		Assert.True(  System.IO.File.Exists(cFileName_WithExt) )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBAppend_Exclusive() AS VOID
		LOCAL aFields AS ARRAY
		LOCAL cFileName AS STRING
		aFields := {{"TEST","C",10,0}}
		cFileName := "C:\TEMP\testdbf"

		Assert.True(  DBCreate(cFileName , aFields , "DBFNTX")  )
		Assert.True(  DBUseArea(,"DBFNTX",cFileName,,FALSE) )
		Assert.True(  RecCount() == 0 )
		Assert.True(  DBAppend() )
		FieldPut(1 , "test")
		Assert.True(  AllTrim(FieldGet(1)) == "test" )
		Assert.True(  DBCloseArea() )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBAppend_Shared() AS VOID
		LOCAL aFields AS ARRAY
		LOCAL cFileName AS STRING
		aFields := {{"TEST","C",10,0}}
		cFileName := "C:\TEMP\testdbf"

		Assert.True(  DBCreate(cFileName , aFields , "DBFNTX")  )
		Assert.True(  DBUseArea(,"DBFNTX",cFileName,,TRUE) )
		Assert.True(  RecCount() == 0 )
		Assert.True(  DBAppend() )
		FieldPut(1 , "test")
		Assert.True(  AllTrim(FieldGet(1)) == "test" )
		Assert.True(  DBCloseArea() )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBAppend_more() AS VOID
	LOCAL cDbf AS STRING
		cDbf := "c:\temp\testappend.DbF"
		RDDSetDefault( "DBFNTX" )
		Assert.True(  DBCreate(cDbf , { {"TEST","C",10,0} }) )
		// Appending in exclusive mode:
		Assert.True( DBUseArea(, , cDbf , "alias1" , FALSE) )
		Assert.True( DBAppend() )
		Assert.True( RecCount() == 1 )
		FieldPut(1, "test") // ok
		Assert.True( AllTrim(FieldGet(1)) == "test" )
		Assert.True( DBCloseArea() )

		// Appending in SHARED mode:
		Assert.True( DBUseArea(, , cDbf , "alias2" , TRUE) )
		Assert.True( RecCount() == 1 )
		Assert.True( DBAppend() )// returns true but does not append record
		Assert.True( RecCount() == 2 )
		FieldPut(1, "test2") // ok
		Assert.True( AllTrim(FieldGet(1)) == "test2" )
		Assert.True( DBCloseArea() )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBUseArea_same_file_twice() AS VOID
	LOCAL cDbf AS STRING
		RDDSetDefault( "DBFNTX" )
		cDbf := "c:\temp\testtwice.DbF"
		Assert.True(  DBCreate(cDbf , { {"TEST","C",10,0} }) )

		// shared mode
		Assert.True( DBUseArea(, , cDbf , , FALSE) )
		Assert.True( DBCloseArea() )

		Assert.True( DBUseArea(, , cDbf , , FALSE) )
		Assert.True( DBCloseArea() )
	END CLASS
END NAMESPACE

CLASS TestClassParent
END CLASS
CLASS TestClassChild INHERIT TestClassParent
END CLASS

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections.Generic
USING System.Linq
USING System.Text
USING XUnit
USING System.Globalization

BEGIN NAMESPACE XSharp.VO.Tests

	CLASS TransFormTests

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformLogicalTest() AS VOID
			// with @R other chars are appended, but only a single YN or TF flag is written. Other positions become a space
			Assert.Equal("Y", Transform(TRUE, "Y"))
			Assert.Equal("N", Transform(FALSE, "Y"))
			Assert.Equal("T", Transform(TRUE, "L"))
			Assert.Equal("F", Transform(FALSE, "L"))
			Assert.Equal("T x", Transform(TRUE, "@R L x"))
			Assert.Equal("F x", Transform(FALSE, "@R L x"))
			Assert.Equal("Y x", Transform(TRUE, "@R YYx"))
			Assert.Equal("N x", Transform(FALSE, "@R YYx"))
			SetNatDll("French.DLL")
			Assert.Equal("O", Transform(TRUE, "Y"))
			Assert.Equal("N", Transform(FALSE, "Y"))
			Assert.Equal("T", Transform(TRUE, "L"))     // you would expect V but VO returns T
			Assert.Equal("F", Transform(FALSE, "L"))
			Assert.Equal("T x", Transform(TRUE, "@R L x"))  // you would expect V but VO returns T x
			Assert.Equal("F x", Transform(FALSE, "@R L x"))
			Assert.Equal("O x", Transform(TRUE, "@R YYx"))
			Assert.Equal("N x", Transform(FALSE, "@R YYx"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformDateTest() AS VOID
			// with @R other chars are appended, but only a single YN or TF flag is written. Other positions become a space
            SetCentury(TRUE)
			Assert.Equal("10/12/2010", Transform(2010.12.10,"@E"))
			Assert.Equal("20/11/2010", Transform(2010.11.20,"@E"))
			SetDateFormat("dd-mm-yyyy")
			Assert.Equal("20-11-2010", Transform(2010.11.20,"@D"))
			SetDateFormat("dd-mm-yy")
			Assert.Equal("20-11-10", Transform(2010.11.20,"@D"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformStringTest() AS VOID
			Assert.Equal("ABC", Transform("abc", "@!"))
			Assert.Equal("AbC", Transform("abc", "!a!"))
			Assert.Equal("1234 AB", Transform("1234ab", "@R! 9999 AA"))
			Assert.Equal("1234 AB", Transform("1234ab", "@R 9999 !!"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformLongTest() AS VOID
			SetDecimalSep('.')
			SetThousandSep(',')
			Assert.Equal("  1", Transform(1, "999"))
			Assert.Equal("123", Transform(123, "###"))
			Assert.Equal("**", Transform(123, "##"))
			Assert.Equal("**", Transform(123, "99"))
			Assert.Equal("(1)", Transform(-1, "@( 99"))
			Assert.Equal("(-1)", Transform(-1, "@( 999"))
			Assert.Equal("( -1)", Transform(-1, "@( 9999"))
			Assert.Equal("(-1)", Transform(-1, "@) 9999"))
			Assert.Equal(" (-1)", Transform(-1, "@) 99999"))
			Assert.Equal("   ", Transform(0, "@Z 999"))
			Assert.Equal(" -1 DB", Transform(-1, "@X 999"))
			Assert.Equal(" -1 DB", Transform(-1, "@XC 999"))
			Assert.Equal("  1 CR", Transform(1, "@XC 999"))
			Assert.Equal("  1 CR", Transform(1, "@C 999"))
			Assert.Equal("  0", Transform(0, "@C 999"))
			Assert.Equal("1,234", Transform(1234, "9,999"))
			SetDecimalSep(',')
			SetThousandSep('.')
			Assert.Equal("1.234", Transform(1234, "9,999"))

		[Fact, Trait("Category", "TransForm")];
		METHOD TransformFLoatTest() AS VOID
			SetDecimalSep('.')
			SetThousandSep(',')
			Assert.Equal("1.23", Transform(1.23, "9.99"))
			Assert.Equal("1,23", Transform(1.23, "@E 9.99"))
			Assert.Equal("*.**", Transform(12.34, "9.99"))
        [Fact, Trait("Category", "TransForm")];
        METHOD TransformNumericTest AS VOID
			SetDecimalSep(',')
			SetThousandSep('.')
            Assert.Equal("      2,45", Transform (    2.45 , "@R 999,999.99" ) )
            Assert.Equal("     -2,45", Transform (   -2.45 , "@R 999,999.99" ) )
            Assert.Equal("    124,00", Transform (     124 , "@R 999,999.99" )  )
            Assert.Equal("   -124,00", Transform (    -124 , "@R 999,999.99" )  )

        [Fact, Trait("Category", "Unformat")];
        METHOD UnformatCTest AS VOID
            Assert.Equal("8161XV", Unformat("8161 XV", "@R! 9999 AA","C"))
            Assert.Equal("8161 XV", Unformat("8161 XV", "@! 9999 AA","C"))

       [Fact, Trait("Category", "Unformat")];
        METHOD UnformatNTest AS VOID
			SetDecimalSep(',')
			SetThousandSep('.')
            Assert.Equal( 1.234d, (REAL8)(FLOAT) Unformat("1,234", "9.999","N"))
            Assert.Equal( 1234567.89d, (REAL8)(FLOAT) Unformat("1.234.567,89", "9,999,999.99","N"))

// some new failing tests:
	[Fact, Trait("Category", "TransForm")];
	METHOD IntLiteralTest() AS VOID
		LOCAL dec AS DWORD
		LOCAL thou AS DWORD

		dec := SetDecimalSep(Asc(","))
		thou := SetThousandSep(Asc("."))
		Assert.Equal(" 12.345,00" , Transform(12345.0, "999,999.99"))
		// this fails:
		Assert.Equal(" 12.345,00" , Transform(12345, "999,999.99"))

		SetDecimalSep(Asc("."))
		SetThousandSep(Asc(","))
		Assert.Equal(" 12,345.00" , Transform(12345.0, "999,999.99"))
		Assert.Equal(" 12,345.00" , Transform(12345, "999,999.99"))

		SetDecimalSep(dec)
		SetThousandSep(thou)
	RETURN

// Tests that need to be moved in separate files in VO.Tests:
	[Fact, Trait("Category", "StringEq")];
	METHOD StringEquality() AS VOID
		LOCAL c1,c2 AS STRING
		LOCAL u1,u2 AS USUAL
		LOCAL exact AS LOGIC
		u1 := c1 := "TESTLONG"
		u2 := c2 := "TEST"
		exact := SetExact( FALSE )

		Assert.Equal(TRUE, c1 = c2)
		Assert.Equal(TRUE, c1 != c2)

		Assert.Equal(TRUE, u1 = u2)
		Assert.Equal(FALSE, u1 != u2)

		SetExact( exact )
	RETURN

	[Fact, Trait("Category", "Str")];
	METHOD StrTests() AS VOID
		LOCAL exact,thou,digit,decimal,fixed_,digitfixed AS USUAL
		exact := SetDecimalSep(Asc("."))
		thou := SetThousandSep(Asc(","))
		digit := SetDigit(6)
		decimal := SetDecimal(3)
		fixed_ := SetFixed(FALSE)
		digitfixed := SetDigitFixed(FALSE)

		Assert.Equal( "     1.2"			, Str(1.2) )
		Assert.Equal( "    12.3"			, Str(12.3) )
		Assert.Equal( "    12.34"			, Str(12.34) )
		Assert.Equal( "   123.456"			, Str(123.456) )
		Assert.Equal( "  1234.567"			, Str(1234.567) )
		Assert.Equal( "  1234.5678"			, Str(1234.5678) )
		Assert.Equal( "  1234.5679012345"	, Str(1234.5679012345) )
		Assert.Equal( "123456.78901234568"	, Str(123456.78901234567) )
		Assert.Equal( "999999" , Str(999999) )
		Assert.Equal( "******" , Str(1000000) )

		SetDecimalSep(exact)
		SetThousandSep(thou)
		SetDigit(digit)
		SetDecimal(decimal)
		SetFixed(fixed_)
		SetDigitFixed(digitfixed)
	RETURN

	[Fact, Trait("Category", "Str")];
	METHOD Str_With_SetFixed_TRUE() AS VOID

	   SetDecimalSep(Asc("."))
	   SetThousandSep(Asc(","))
	   SetDecimal(3)
	   SetDigit(12)

	   SetFixed(TRUE)
	   SetDigitFixed(TRUE)

	   Assert.Equal( Str(12.34)            		, "          12.340")
	   Assert.Equal( Str1(12.34)          		, "          12.340")
	   Assert.Equal( Str(123.456)         		, "         123.456")
	   Assert.Equal( Str1(123.456)       		, "         123.456")
	   Assert.Equal( Str(1230.4567)         	, "        1230.457")
	   Assert.Equal( Str1(1230.4567)        	, "        1230.457")
	   Assert.Equal( Str(100000001234.4567)		, "100000001234.457")
	   Assert.Equal( Str1(100000001234.4567)	, "100000001234.457")
	   Assert.Equal( Str(1000000001234.4567)	, "****************")
	   Assert.Equal( Str1(1000000001234.4567)	, "****************")
	   Assert.Equal( Str(12.3)              	, "          12.300")
	   Assert.Equal( Str1(12.3)            		, "          12.300")
	   Assert.Equal( Str(12.34567)        	 	, "          12.346")
	   Assert.Equal( Str1(12.34567)        		, "          12.346")
	   Assert.Equal( Str(12.34 , 10, 2)    		, "     12.34")
	   Assert.Equal( Str(12.34 , 5, 2)     		, "12.34")
	   Assert.Equal( Str(12.34 , 4, 2)     		, "****") // VO wrong: 12.3
	   Assert.Equal( Str(12.34 , 4, 1)     		, "12.3")
	   Assert.Equal( Str(12.34 , 2, 1)     		, "**")
	   Assert.Equal( Str3(12.34 , 10, 2)   		, "     12.34")
	   Assert.Equal( Str3(12.34 , 5, 2)    		, "12.34")
	   Assert.Equal( Str3(12.34 , 4, 2)     	, "****") // VO wrong: 12.3
	   Assert.Equal( Str3(12.34 , 4, 1)     	, "12.3")
	   Assert.Equal( Str3(12.34 , 2, 1)    		, "**")
	   Assert.Equal( Str(-12.34)            	, "         -12.340")
	   Assert.Equal( Str1(-12.34)           	, "         -12.340")
	   Assert.Equal( Str(-12.3456)         		, "         -12.346")
	   Assert.Equal( Str1(-12.3456)         	, "         -12.346")
	   Assert.Equal( Str(-12.34 , 10, 2)    	, "    -12.34")
	   Assert.Equal( Str(-12.34 , 5, 2)     	, "*****")	 //VO wrong: -2.34
	   Assert.Equal( Str(-12.34 , 4, 2)     	, "****")    // VO wrong: -12.
	   Assert.Equal( Str(-12.34 , 4, 1)     	, "****")    //VO wrong: -2.3
       Assert.Equal( Str(-12.34 , 4, 0)     	, " -12")    //VO wrong: -2.3
	   Assert.Equal( Str(-12.34 , 2, 1)     	, "**")
	RETURN

	[Fact, Trait("Category", "Str")];
	METHOD Str_With_SetFixed_FALSE() AS VOID

	   SetDecimalSep(Asc("."))
	   SetThousandSep(Asc(","))
	   SetDecimal(3)
	   SetDigit(12)

	   SetFixed(FALSE)
	   SetDigitFixed(FALSE)

	   Assert.Equal( Str(12.34)            		, "          12.34")
	   Assert.Equal( Str1(12.34)          		, "          12.34")
	   Assert.Equal( Str(123.456)         		, "         123.456")
	   Assert.Equal( Str1(123.456)				, "         123.456")
	   Assert.Equal( Str(1230.4567)         	, "        1230.4567")
	   Assert.Equal( Str1(1230.4567)        	, "        1230.4567")
	   Assert.Equal( Str(100000001234.4567)		, "100000001234.4567")
	   Assert.Equal( Str1(100000001234.4567)	, "100000001234.4567")
	   Assert.Equal( Str(1000000001234.4567)	, "*****************")
	   Assert.Equal( Str1(1000000001234.4567)	, "*****************")
	   Assert.Equal( Str(12.3)              	, "          12.3")
	   Assert.Equal( Str1(12.3)            		, "          12.3")
	   Assert.Equal( Str(12.34567)         		, "          12.34567")
	   Assert.Equal( Str1(12.34567)        		, "          12.34567")
	   Assert.Equal( Str(12.34 , 10, 2)    		, "     12.34")
	   Assert.Equal( Str(12.34 , 5, 2)     		, "12.34")
	   Assert.Equal( Str(12.34 , 4, 2)     		, "****") // VO returns 12.3
	   Assert.Equal( Str(12.34 , 4, 1)     		, "12.3")
	   Assert.Equal( Str(12.34 , 2, 1)     		, "**")
	   Assert.Equal( Str3(12.34 , 10, 2)   		, "     12.34")
	   Assert.Equal( Str3(12.34 , 5, 2)    		, "12.34")
	   Assert.Equal( Str3(12.34 , 4, 2)     	, "****")   // VO 12.3
	   Assert.Equal( Str3(12.34 , 4, 1)     	, "12.3")
	   Assert.Equal( Str3(12.34 , 2, 1)    		, "**")
	   Assert.Equal( Str(-12.34)            	, "         -12.34")
	   Assert.Equal( Str1(-12.34)           	, "         -12.34")
	   Assert.Equal( Str(-12.3456)         		, "         -12.3456")
	   Assert.Equal( Str1(-12.3456)         	, "         -12.3456")
	   Assert.Equal( Str(-12.34 , 10, 2)    	, "    -12.34")
	   Assert.Equal( Str(-12.34 , 5, 2)     	, "*****")	 //VO: -2.34
	   Assert.Equal( Str(-12.34 , 4, 2)     	, "****")    // VO
	   Assert.Equal( Str(-12.34 , 4, 1)     	, "****")    //VO: -2.3
	   Assert.Equal( Str(-12.34 , 2, 1)     	, "**")

	[Fact, Trait("Category", "Val")];
	METHOD ValTests() AS VOID
		SetDecimalSep(',')
		SetThousandSep('.')
		Assert.Equal(123, (INT) Val("123") )
		Assert.Equal(123.456, (FLOAT) Val("123,456") )

		Assert.Equal(0, (INT) Val("") )

		Assert.Equal(0, (INT) Val("abc") )
		Assert.Equal(123, (INT) Val("123abc") )
		Assert.Equal(123, (INT) Val("123abc456") )
		Assert.Equal(123, (INT) Val("123abc456,789") )
		Assert.Equal(0, (INT) Val("abc123456,789") )
		Assert.Equal(255, (INT) Val("0xFF") )
		Assert.Equal(0xFFFF, (INT) Val("0xFFFF") )
		Assert.Equal(4294967295, (INT64) Val("0xFFFFFFFF") )
		Assert.Equal(11, (INT) Val("11L11") )

		Assert.Equal(1.0001, (FLOAT) Val("1,000.1") )
		Assert.Equal(1.0011, (FLOAT) Val("1,001.1") )
		SetDecimalSep('.')
		SetThousandSep(',')
		Assert.Equal(1000.1, (FLOAT) Val("1,000.1") )
		Assert.Equal(1001.1, (FLOAT) Val("1,001.1") )
	RETURN

	[Fact, Trait("Category", "SplitPath")];
	METHOD SplitPathTests() AS VOID
		LOCAL cDrive := "",cDir := "",cFile := "",cExt := "" AS STRING
		SplitPath("C:\folder\file.ext" ,  REF cDrive , REF cDir , REF cFile , REF cExt)

		Assert.Equal("C:" , cDrive)
		Assert.Equal("\folder\" , cDir)

		Assert.Equal("file" , cFile)
		Assert.Equal(".ext" , cExt)
	RETURN

	[Fact, Trait("Category", "SplitPathVO")];
	METHOD SplitPathVO() AS VOID
		LOCAL cPath ,cDrive ,cDir ,cFile ,cExt AS STRING
		LOCAL pszDrive, pszDir AS PSZ
		LOCAL pszFile, pszExt AS PSZ

		cPath := "C:\testfolder\testfile.prg"

		pszDrive := MemAlloc(2+1)
		pszDir   := MemAlloc(255+1)
		pszFile  := MemAlloc(255+1)
		pszExt   := MemAlloc(7+1)

		SplitPath( String2Psz(cPath), pszDrive, pszDir, pszFile, pszExt)

		cDrive := Psz2String(pszDrive)
		cDir   := Psz2String(pszDir)
		cFile  := Psz2String(pszFile)
		cExt   := Psz2String(pszExt)

		MemFree(pszDrive)
		MemFree(pszDir)
		MemFree(pszFile)
		MemFree(pszExt)

		Assert.Equal("\testfolder\" , cDir)
		Assert.Equal("\testfolder\" , cDir)
		Assert.Equal("testfile" , cFile)
		Assert.Equal(".prg" , cExt)
	RETURN

	[Fact, Trait("Category", "Directory")];
	METHOD DirectoryTests() AS VOID
		Assert.Equal(0u , ALen(Directory("K:\drive does not exist")))
		Assert.Equal(0u , ALen(Directory("K:\drive does not exist","V")))
		Assert.Equal(0u , ALen(Directory("C:\invalid:path")))
		Assert.Equal(1u , ALen(Directory("C:\invalid:path","V")))
		Assert.Equal(0u , ALen(Directory("C:invalid:path")))
		Assert.Equal(1u , ALen(Directory("C:invalid:path","V")))
		Assert.Equal(0u , ALen(Directory("N:")))
		Assert.Equal(0u , ALen(Directory("N:","V")))

	[Fact, Trait("Category", "Empty")];
	METHOD EmptyFuncTests() AS VOID
		Assert.True(Empty(0))
		Assert.True(Empty(FALSE))
		Assert.True(Empty(""))
		Assert.True(Empty(Chr(10) + Chr(9) + Chr(13)))
		Assert.True(Empty(NULL_SYMBOL))
		Assert.True(Empty(0000.00.00))
		Assert.True(Empty({}))

		Assert.True(Empty(NULL_PTR))
		LOCAL p AS PTR
		p := NULL_PTR
		Assert.True(Empty(p))

		Assert.True(Empty(NULL_PSZ))
		LOCAL z AS PSZ
		z := NULL_PSZ
		Assert.True(Empty(z))

		Assert.False(Empty(1))
		Assert.False(Empty(TRUE))
		Assert.False(Empty("a"))
		Assert.False(Empty(Chr(1)))
		Assert.False(Empty(#abc))
		Assert.False(Empty(2000.01.01))
		Assert.False(Empty({{}}))

		p := @p
		Assert.False(Empty(p))
		z := String2Psz("test")
		Assert.False(Empty(z))

	[Fact, Trait("Category", "AClone")];
	METHOD AClone_Tests() AS VOID
		Assert.True(ALen(AClone({1}))==1)
		Assert.True(ALen(AClone({1,2}))==2)
		Assert.True(  AClone({1,2})[2] ==2  )
		Assert.True(  AClone({1,{2,3},4})[2,2] == 3  )
		Assert.True(  AClone({1,{},4})[3] == 4  )

		Assert.True(AClone(NULL_ARRAY)==NULL_ARRAY)
		LOCAL aNull := NULL_ARRAY AS ARRAY
		Assert.True(AClone(aNull)==NULL_ARRAY)
		Assert.True(ALen(AClone({}))==0)
		Assert.True(ALen(AClone({{}}))==1)

	[Fact, Trait("Category", "ACloneShallow")];
	METHOD ACloneShallow_Tests() AS VOID
		Assert.True(ALen(ACloneShallow({1}))==1)
		Assert.True(ALen(ACloneShallow({1,2}))==2)
		Assert.True(  ACloneShallow({1,2})[2] ==2  )
		Assert.True(  ACloneShallow({1,{2,3},4})[2,2] == 3  )
		Assert.True(  ACloneShallow({1,{},4})[3] == 4  )

		LOCAL aSub AS ARRAY
		aSub := {1,2}
		Assert.True(  ACloneShallow({1,aSub,3})[2] == aSub  )

		Assert.True(ACloneShallow(NULL_ARRAY)==NULL_ARRAY)
		LOCAL aNull AS ARRAY
		aNull := NULL_ARRAY
		Assert.True(ACloneShallow(aNull)==NULL_ARRAY)
		Assert.True(ALen(ACloneShallow({}))==0)
		Assert.True(ALen(ACloneShallow({{}}))==1)

	[Fact, Trait("Category", "AClone")];
	METHOD ACloneWith_NULL_ARRAY_as_Elements() AS VOID
		Assert.True(  ALen(  AClone({NULL_ARRAY,NULL_ARRAY}) ) == 2  )
		Assert.True(  AClone({NULL_ARRAY})[1] == NULL_ARRAY  )

		Assert.True(  ALen(  ACloneShallow({NULL_ARRAY,NULL_ARRAY}) ) == 2  )
		Assert.True(  ACloneShallow({NULL_ARRAY})[1] == NULL_ARRAY  )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBCreate_Tests() AS VOID
		LOCAL aFields AS ARRAY
		aFields := {{"TEST","C",10,0}}

		LOCAL cFileName_WithExt AS STRING
		LOCAL cFileName_NoExt AS STRING
		cFileName_NoExt := "C:\TEMP\testdbf"
		cFileName_WithExt := cFileName_NoExt + ".dbf"
		IF System.IO.File.Exists(cFileName_WithExt)
			System.IO.File.Delete(cFileName_WithExt)
		END IF
		Assert.True(  DBCreate(cFileName_NoExt , aFields , "DBFNTX")  )
		Assert.True(  System.IO.File.Exists(cFileName_WithExt) )

		IF System.IO.File.Exists(cFileName_WithExt)
			System.IO.File.Delete(cFileName_WithExt)
		END IF
		Assert.True(  DBCreate(cFileName_WithExt , aFields , "DBFNTX")  )
		Assert.True(  System.IO.File.Exists(cFileName_WithExt) )

		cFileName_WithExt := cFileName_NoExt + ".none"
		IF System.IO.File.Exists(cFileName_WithExt)
			System.IO.File.Delete(cFileName_WithExt)
		END IF
		Assert.True(  DBCreate(cFileName_WithExt , aFields , "DBFNTX")  )
		Assert.True(  System.IO.File.Exists(cFileName_WithExt) )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBAppend_Exclusive() AS VOID
		LOCAL aFields AS ARRAY
		LOCAL cFileName AS STRING
		aFields := {{"TEST","C",10,0}}
		cFileName := "C:\TEMP\testdbf"

		Assert.True(  DBCreate(cFileName , aFields , "DBFNTX")  )
		Assert.True(  DBUseArea(,"DBFNTX",cFileName,,FALSE) )
		Assert.True(  RecCount() == 0 )
		Assert.True(  DBAppend() )
		FieldPut(1 , "test")
		Assert.True(  AllTrim(FieldGet(1)) == "test" )
		Assert.True(  DBCloseArea() )

	[Fact, Trait("Category", "DBFFuncs")];
	METHOD DBAppend_Shared() AS VOID
		LOCAL aFields AS ARRAY
		LOCAL cFileName AS STRING
		aFields := {{"TEST","C",10,0}}
		cFileName := "C:\TEMP\testdbf"

		Assert.True(  DBCreate(cFileName , aFields , "DBFNTX")  )
		Assert.True(  DBUseArea(,"DBFNTX",cFileName,,TRUE) )
		Assert.True(  RecCount() == 0 )
		Assert.True(  DBAppend() )
		FieldPut(1 , "test")
		Assert.True(  AllTrim(FieldGet(1)) == "test" )
		Assert.True(  DBCloseArea() )

	END CLASS
END NAMESPACE

CLASS TestClassParent
END CLASS
CLASS TestClassChild INHERIT TestClassParent
END CLASS


tweak it a bit more

tweak it a bit more

import "../src/LangPrelude.prg" (maybeM)
import "Inventory.prg"

monad maybeM

data Fin :: Nat ~> *0 where
  Fz :: Fin (S m)
  Fs :: Fin m -> Fin (S m)
 deriving Nat(f)

data DigitList :: Nat ~> *0 where
  None :: DigitList a
  Longer :: DigitList a -> Fin a -> DigitList a
 deriving LeftList(dl)

up :: Nat' (2+b)t -> DigitList (2+b)t -> DigitList (2+b)t
up _ []dl = [1f]dl
up b [bla; f]dl = case tryIncr b f of
                  Nothing -> [up b bla; 0f]dl
                  Just d -> [bla; d]dl
  where tryIncr :: Nat' (1+b)t -> Fin (1+b)t -> Maybe (Fin (1+b)t)
        tryIncr 2v 1f = Nothing
        tryIncr (2+v)v 0f = Just 1f
        tryIncr (2+v)v (1+f)f = do { i <- tryIncr (1+v)v f; return (1+i)f }

nup :: Nat' (2+b)t -> Int -> DigitList (2+b)t -> DigitList (2+b)t
nup _ 0 dl = dl
nup b n dl = nup b (n - 1) (up b dl)

-- same with reflected types
--
data Fin' :: Nat ~> Nat ~> *0 where
  Fz' :: Fin' (S m) Z
  Fs' :: Fin' m n -> Fin' (S m) (S n)
 deriving Nat(fp)

data DigitList' :: Nat ~> Inventory Nat ~> *0 where
  None' :: DigitList' a []i
  Longer' :: DigitList' a inv -> Fin' a t -> DigitList' a [inv; t]i
 deriving LeftList(dlp)

upInv :: Nat ~> Inventory Nat ~> Inventory Nat
{upInv b []i} = [1t]i
{upInv (1+b)t [rest; b]i} = [{upInv (1+b)t rest}; 0t]i
{upInv (2+b)t [rest; a]i} = [rest; (1+a)t]i

up' :: Nat' (2+b)t -> DigitList' (2+b)t i -> DigitList' (2+b)t {upInv (2+b)t i}
up' _ []dlp = [1fp]dlp
up' b [bla; f]dlp = case tryIncr b f of
                    L Eq -> [up' b bla; 0fp]dlp
                    R d -> [bla; d]dlp
  where tryIncr :: Nat' (1+b)t -> Fin' (1+b)t a -> (Equal b a + Fin' (1+b)t (1+a)t)
        tryIncr 2v 1fp = L Eq
        tryIncr (2+v)v 0fp = R 1fp
        tryIncr (2+v)v (1+f)fp = case tryIncr (1+v)v f of
                                  L Eq -> L Eq
                                  R i -> R (1+i)fp

import "../src/LangPrelude.prg" (maybeM)
import "Inventory.prg"

monad maybeM

data Fin :: Nat ~> *0 where
  Fz :: Fin (S m)
  Fs :: Fin m -> Fin (S m)
 deriving Nat(f)

data DigitList :: Nat ~> *0 where
  None :: DigitList a
  Longer :: DigitList a -> Fin a -> DigitList a
 deriving LeftList(dl)

up :: Nat' (2+b)t -> DigitList (2+b)t -> DigitList (2+b)t
up _ []dl = [1f]dl
up b [bla; f]dl = case tryIncr b f of
                  Nothing -> [up b bla; 0f]dl
                  Just d -> [bla; d]dl
  where tryIncr :: Nat' (1+b)t -> Fin (1+b)t -> Maybe (Fin (1+b)t)
        tryIncr 2v 1f = Nothing
        tryIncr (2+v)v 0f = Just 1f
        tryIncr (2+v)v (1+f)f = do { i <- tryIncr (1+v)v f; return (1+i)f }

nup :: Nat' (2+b)t -> Int -> DigitList (2+b)t -> DigitList (2+b)t
nup _ 0 dl = dl
nup b n dl = nup b (n - 1) (up b dl)

-- same with reflected types
--
data Fin' :: Nat ~> Nat ~> *0 where
  Fz' :: Fin' (S m) Z
  Fs' :: Fin' m n -> Fin' (S m) (S n)
 deriving Nat(fp)

data DigitList' :: Nat ~> Inventory Nat ~> *0 where
  None' :: DigitList' a []i
  Longer' :: DigitList' a inv -> Fin' a t -> DigitList' a [inv; t]i
 deriving LeftList(dlp)

upInv :: Nat ~> Inventory Nat ~> Inventory Nat
{upInv b []i} = [1t]i
{upInv (1+b)t [rest; b]i} = [{upInv (1+b)t rest}; 0t]i
{upInv (2+b)t [rest; a]i} = [rest; (1+a)t]i

up' :: Nat' (2+b)t -> DigitList' (2+b)t i -> DigitList' (2+b)t {upInv (2+b)t i}
up' _ []dlp = [1fp]dlp
up' b [bla; f]dlp = case tryIncr b f of
                    L Eq -> [up' b bla; 0fp]dlp
                    R d -> [bla; d]dlp
  where tryIncr :: Nat' (1+b)t -> Fin' (1+b)t a -> (Equal (1+b)t (1+a)t + Fin' (1+b)t (1+a)t)
        tryIncr 2v 1fp = L Eq
        tryIncr (2+v)v 0fp = R 1fp
        tryIncr (2+v)v (1+f)fp = case tryIncr (1+v)v f of
                                  L Eq -> L Eq
                                  R i -> R (1+i)fp


enable deBruijn notation

enable deBruijn notation

-- See: http://en.wikipedia.org/wiki/Normalisation_by_evaluation
-- and also http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.2361
-- almost there: http://www.cs.nott.ac.uk/~txa/publ/flops04.pdf
--       slides: http://www.cs.nott.ac.uk/~txa/talks/flops-slides.pdf
-- other slides: http://cs.ioc.ee/~tarmo/tsem08/abel-slides.pdf
--     by Fiore: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.130.1867
--     and Abel: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.278.4776
--
import "../src/LangPrelude.prg" (listM,Monad,compose,const)

monad listM

-- abstract syntax -------------------------------------------------------------

data Ty :: *0 ~> *0 where
   Bool :: Ty Bool
   Arr :: Ty a -> Ty b -> Ty (a -> b)

data Var :: *0 ~> *0 ~> *0 where
   ZVar :: Var (h,t) t
   SVar :: Var h t -> Var (h,s) t
 deriving Nat(d)

data Exp :: *0 ~> *0 ~> *0 where
   Var :: Var g t -> Exp g t
   Lam :: Ty a -> Exp (g,a) b -> Exp g (a -> b)
   App :: Exp g (s -> t) -> Exp g s -> Exp g t
   If :: Exp g Bool -> Exp g t -> Exp g t -> Exp g t
   ETrue :: Exp g Bool
   EFalse :: Exp g Bool

-- smart constructors ----------------------------------------------------------
lamE :: Ty s -> (Exp (g,s) s -> Exp (g,s) t) -> Exp g (s -> t)
lamE s f = Lam s (f $ Var 0d)

ifE :: Exp g Bool -> Exp g t -> Exp g t -> Exp g t
ifE t ETrue EFalse = t
ifE t e e' = if eqE e e' then e else If t e e'

-- boring equality tests -------------------------------------------------------
eqB :: BoxExp t -> BoxExp s -> Bool
eqB (Box e) (Box e') = eqE e e'

eqE :: Exp g t -> Exp h s -> Bool
eqE (Var x) (Var y) = eqV x y
eqE (Lam s e) (Lam s' e') = eqT s s' && eqE e e'
eqE (App e1 e2) (App e1' e2') = eqE e1 e1' && eqE e2 e2'
eqE (If e1 e2 e3) (If e1' e2' e3') = eqE e1 e1' && (eqE e2 e2' && eqE e3 e3')
eqE ETrue ETrue = True
eqE EFalse EFalse = True
eqE _ _ = False

eqT :: Ty t -> Ty s -> Bool
eqT (Arr s t) (Arr s' t') = eqT s s' && eqT t t'
eqT Bool Bool = True
eqT _ _ = False

eqV :: Var g t -> Var h s -> Bool
eqV (1+x)d (1+y)d = eqV x y
eqV 0d 0d = True
eqV _ _ = False

-- evaluation ------------------------------------------------------------------
var :: Var g t -> g -> t
var 0d     (_,t) = t
var (1+x)d (h,s) = var x h

exp :: Exp g t -> g -> t
exp (Var x)    g = var x g
exp (Lam _ e)  g = \a -> exp e (g,a)
exp (App e e') g = exp e g (exp e' g)
exp ETrue      g = True
exp EFalse     g = False
exp (If c t e) g = if exp c g then exp t g else exp e g

-- type inference --------------------------------------------------------------

data TyEnv :: *0 ~> *0 where
   Cons :: Ty t -> TyEnv h -> TyEnv (h,t)
   Nil :: TyEnv g

infer :: TyEnv g -> Exp g t -> Ty t
infer g (Var x)         = inferVar g x
infer g (Lam t e)       = Arr t (infer (Cons t g) e)
infer g (App e e')      = case infer g e of Arr _ t -> t
infer g ETrue           = Bool
infer g EFalse          = Bool
infer g (If _ e _)      = infer g e

inferVar :: TyEnv g -> Var g t -> Ty t
inferVar (Cons t h) (1+x)d = inferVar h x
inferVar (Cons t h) 0d     = t

-- tree monad ------------------------------------------------------------------

data Tree a = Val a | Choice (Tree a) (Tree a)
-- doesn't yet force trees to be fully balanced:
--      Val :: a -> Tree a Z
--      Choice :: Tree a n -> Tree a n -> Tree a (S n)

treeM = Monad Val bind error
  where bind (Val a) f = f a
        bind (Choice l r) f = Choice (bind l f) (bind r f)

tmap f x = do a <- x; return (f a)
  where monad treeM

flatten t = fl t []
 where
   fl (Val a)      k = a:k
   fl (Choice l r) k = fl l (fl r k)

-- quote & friends -------------------------------------------------------------

-- for values --------------------------
enumV           :: Ty t -> Tree t
questionsV      :: Ty t -> [t -> Bool]

enumV Bool      = Choice (Val True) (Val False)
enumV (Arr s t) = mkEnum (questionsV s) (enumV t)
 where
   monad treeM
   mkEnum [] t = tmap const t
   mkEnum (q:qs) es = do
                   f1 <- mkEnum qs es
                   f2 <- mkEnum qs es
                   return (\d -> if q d then f1 d else f2 d)

questionsV Bool         = return (\x -> x)
questionsV (Arr s t)    = do
                          d <- flatten (enumV s)
                          q <- questionsV t
                          return (\f -> q (f d))

-- for expressions ---------------------
enumE           :: Ty t -> Tree (Exp g t)
questionsE      :: Ty t -> [Exp g t -> Exp g Bool]

enumE Bool      = Choice (Val ETrue) (Val EFalse)
enumE (Arr s t) = tmap (lamE s) (mkEnumE (questionsE s) (enumE t))
 where
   monad treeM
   mkEnumE [] t = tmap const t
   mkEnumE (q:qs) es = do
                        f1 <- mkEnumE qs es
                        f2 <- mkEnumE qs es
                        return (\d -> ifE (q d) (f1 d) (f2 d))

questionsE Bool         = return (\x -> x)
questionsE (Arr s t)    = do
                          d <- flatten (enumE s)
                          q <- questionsE t
                          return (\f -> q (App f d))

-- should be
--      find (List (Exp g Bool) n) -> Tree (Exp g a) n -> Exp g a
find :: [Exp g Bool] -> Tree (Exp g a) -> Exp g a
find []         (Val a)         = a
find (b:bs)     (Choice l r)    = ifE b (find bs l) (find bs r)
find _          _               = error "bad arguments to find"

quote :: Ty t -> t -> Exp g t
quote Bool      t = case t of True -> ETrue; False -> EFalse
quote (Arr s t) f = lamE s (\e -> find (do q <- questionsE s; return (q e))
                                        (tmap (quote t . f) (enumV s)))

-- normalization (by evaluation) -----------------------------------------------
data BoxExp t = Box (forall g. Exp g t)

normalize :: Ty t -> BoxExp t -> BoxExp t
normalize s (Box e) = Box (quote s (exp e ()))

-- examples --------------------------------------------------------------------
b2b = Arr Bool Bool
b22b = Arr b2b b2b
zero = Var 0d
one = Var 1d
once   = Box (Lam b2b (Lam Bool (App one zero)))
twice  = Box (Lam b2b (Lam Bool (App one (App one zero))))
thrice = Box (Lam b2b (Lam Bool (App one (App one (App one zero)))))

test = [ eqB (nf b22b thrice) (nf b22b once)
       , eqB (nf b22b twice)  (nf b22b once)]
  where nf = normalize

-- See: http://en.wikipedia.org/wiki/Normalisation_by_evaluation
-- and also http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.2361
-- almost there: http://www.cs.nott.ac.uk/~txa/publ/flops04.pdf
--       slides: http://www.cs.nott.ac.uk/~txa/talks/flops-slides.pdf
-- other slides: http://cs.ioc.ee/~tarmo/tsem08/abel-slides.pdf
--     by Fiore: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.130.1867
--     and Abel: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.278.4776
--
import "../src/LangPrelude.prg" (listM,Monad,compose,const)

monad listM

-- abstract syntax -------------------------------------------------------------

data Ty :: *0 ~> *0 where
   Bool :: Ty Bool
   Arr :: Ty a -> Ty b -> Ty (a -> b)

data Var :: *0 ~> *0 ~> *0 where
   ZVar :: Var (h,t) t
   SVar :: Var h t -> Var (h,s) t

data Exp :: *0 ~> *0 ~> *0 where
   Var :: Var g t -> Exp g t
   Lam :: Ty a -> Exp (g,a) b -> Exp g (a -> b)
   App :: Exp g (s -> t) -> Exp g s -> Exp g t
   If :: Exp g Bool -> Exp g t -> Exp g t -> Exp g t
   ETrue :: Exp g Bool
   EFalse :: Exp g Bool

-- smart constructors ----------------------------------------------------------
lamE :: Ty s -> (Exp (g,s) s -> Exp (g,s) t) -> Exp g (s -> t)
lamE s f = Lam s (f (Var ZVar))

ifE :: Exp g Bool -> Exp g t -> Exp g t -> Exp g t
ifE t ETrue EFalse = t
ifE t e e' = if eqE e e' then e else If t e e'

-- boring equality tests -------------------------------------------------------
eqB :: BoxExp t -> BoxExp s -> Bool
eqB (Box e) (Box e') = eqE e e'

eqE :: Exp g t -> Exp h s -> Bool
eqE (Var x) (Var y) = eqV x y
eqE (Lam s e) (Lam s' e') = eqT s s' && eqE e e'
eqE (App e1 e2) (App e1' e2') = eqE e1 e1' && eqE e2 e2'
eqE (If e1 e2 e3) (If e1' e2' e3') = eqE e1 e1' && (eqE e2 e2' && eqE e3 e3')
eqE ETrue ETrue = True
eqE EFalse EFalse = True
eqE _ _ = False

eqT :: Ty t -> Ty s -> Bool
eqT (Arr s t) (Arr s' t') = eqT s s' && eqT t t'
eqT Bool Bool = True
eqT _ _ = False

eqV :: Var g t -> Var h s -> Bool
eqV (SVar x) (SVar y) = eqV x y
eqV ZVar ZVar = True
eqV _ _ = False

-- evaluation ------------------------------------------------------------------
var :: Var g t -> g -> t
var ZVar     (_,t) = t
var (SVar x) (h,s) = var x h

exp :: Exp g t -> g -> t
exp (Var x)    g = var x g
exp (Lam _ e)  g = \a -> exp e (g,a)
exp (App e e') g = exp e g (exp e' g)
exp ETrue      g = True
exp EFalse     g = False
exp (If c t e) g = if exp c g then exp t g else exp e g

-- type inference --------------------------------------------------------------

data TyEnv :: *0 ~> *0 where
   Cons :: Ty t -> TyEnv h -> TyEnv (h,t)
   Nil :: TyEnv g

infer :: TyEnv g -> Exp g t -> Ty t
infer g (Var x)         = inferVar g x
infer g (Lam t e)       = Arr t (infer (Cons t g) e)
infer g (App e e')      = case infer g e of Arr _ t -> t
infer g ETrue           = Bool
infer g EFalse          = Bool
infer g (If _ e _)      = infer g e

inferVar :: TyEnv g -> Var g t -> Ty t
inferVar (Cons t h) (SVar x) = inferVar h x
inferVar (Cons t h) (ZVar)   = t

-- tree monad ------------------------------------------------------------------

data Tree a = Val a | Choice (Tree a) (Tree a)
-- doesn't yet force trees to be fully balanced:
--      Val :: a -> Tree a Z
--      Choice :: Tree a n -> Tree a n -> Tree a (S n)

treeM = Monad Val bind error
  where bind (Val a) f = f a
        bind (Choice l r) f = Choice (bind l f) (bind r f)

tmap f x = do a <- x; return (f a)
  where monad treeM

flatten t = fl t []
 where
   fl (Val a)      k = a:k
   fl (Choice l r) k = fl l (fl r k)

-- quote & friends -------------------------------------------------------------

-- for values --------------------------
enumV           :: Ty t -> Tree t
questionsV      :: Ty t -> [t -> Bool]

enumV Bool      = Choice (Val True) (Val False)
enumV (Arr s t) = mkEnum (questionsV s) (enumV t)
 where
   monad treeM
   mkEnum [] t = tmap const t
   mkEnum (q:qs) es = do
                   f1 <- mkEnum qs es
                   f2 <- mkEnum qs es
                   return (\d -> if q d then f1 d else f2 d)

questionsV Bool         = return (\x -> x)
questionsV (Arr s t)    = do
                          d <- flatten (enumV s)
                          q <- questionsV t
                          return (\f -> q (f d))

-- for expressions ---------------------
enumE           :: Ty t -> Tree (Exp g t)
questionsE      :: Ty t -> [Exp g t -> Exp g Bool]

enumE Bool      = Choice (Val ETrue) (Val EFalse)
enumE (Arr s t) = tmap (lamE s) (mkEnumE (questionsE s) (enumE t))
 where
   monad treeM
   mkEnumE [] t = tmap const t
   mkEnumE (q:qs) es = do
                        f1 <- mkEnumE qs es
                        f2 <- mkEnumE qs es
                        return (\d -> ifE (q d) (f1 d) (f2 d))

questionsE Bool         = return (\x -> x)
questionsE (Arr s t)    = do
                          d <- flatten (enumE s)
                          q <- questionsE t
                          return (\f -> q (App f d))

-- should be
--      find (List (Exp g Bool) n) -> Tree (Exp g a) n -> Exp g a
find :: [Exp g Bool] -> Tree (Exp g a) -> Exp g a
find []         (Val a)         = a
find (b:bs)     (Choice l r)    = ifE b (find bs l) (find bs r)
find _          _               = error "bad arguments to find"

quote :: Ty t -> t -> Exp g t
quote Bool      t = case t of True -> ETrue; False -> EFalse
quote (Arr s t) f = lamE s (\e -> find (do q <- questionsE s; return (q e))
                                        (tmap (quote t . f) (enumV s)))

-- normalization (by evaluation) -----------------------------------------------
data BoxExp t = Box (forall g. Exp g t)

normalize :: Ty t -> BoxExp t -> BoxExp t
normalize s (Box e) = Box (quote s (exp e ()))

-- examples --------------------------------------------------------------------
b2b = Arr Bool Bool
b22b = Arr b2b b2b
zero = Var ZVar
one = Var (SVar ZVar)
once   = Box (Lam b2b (Lam Bool (App one zero)))
twice  = Box (Lam b2b (Lam Bool (App one (App one zero))))
thrice = Box (Lam b2b (Lam Bool (App one (App one (App one zero)))))

test = [ eqB (nf b22b thrice) (nf b22b once)
       , eqB (nf b22b twice)  (nf b22b once)]
  where nf = normalize