Fix DateTime.Minvalue == NULL_DATE problem

//
// 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.Runtime.Serialization
USING System.Globalization
USING System.Runtime.InteropServices
USING System.Runtime.CompilerServices
USING XSharp
USING System.Diagnostics
BEGIN NAMESPACE XSharp
	/// Internal type that implements the VO Compatible DATE type

	/// This type has many operators and implicit converters that normally are never directly called from user code.
	/// It holds year, month, day in 32 bits. For date calculations it uses the System.DateTime calculation logic.
	/// There are implicit converters between Date and DateTime.
	///
	/// <seealso cref="T:XSharp.IDate"/>
	/// <seealso cref="T:XSharp.RDD.DbDate"/>
	[DebuggerDisplay("{ToString(),nq}", Type := "DATE" )];
	[DebuggerTypeProxy(TYPEOF(DateDebugView))];
	[StructLayout(LayoutKind.Explicit,Pack := 1)];
	PUBLIC STRUCTURE __VODate IMPLEMENTS System.IComparable, ;
		System.IFormattable, ;
		System.IConvertible, ;
		IDate, ;
		IComparable<__VODate>, ;
		IEquatable<__VODate>
		// This structure uses an explicit layout to map
		// year, month and date into 32 bits
		// the _ymd field is for convenience, so we can do a single numeric comparison
		// to determine equality or relative comparisons in stead of doing 3 comparisons
		// for date calculation we use the Value PROPERTY which returns a System.DateTime type
		// Note that the Vulcan type uses a datetime which takes 8 bytes. We only use 4 bytes
		#region fields
			[FieldOffSet(00)] PRIVATE _ymd   AS System.Int32
			[FieldOffSet(00)] PRIVATE _year  AS System.UInt16
			[FieldOffSet(02)] PRIVATE _month AS System.Byte
			[FieldOffSet(03)] PRIVATE _day   AS System.Byte
		#endregion

		#region STATIC fields
			/// <exclude />
			STATIC INITONLY PUBLIC  _NULL_DATE AS DATE
			/// <exclude />
			CONST  VO_MIN_DATE := 2415386U AS DWORD	// 1901-01-01
			/// <exclude />
			CONST  VO_MAX_DATE := 4606840U AS DWORD	// 7900-12-31
			/// <exclude />
			STATIC INITONLY _dtCalc AS Datetime

		#endregion

		#region datetime conversions
			PUBLIC PROPERTY @@Value AS System.DateTime
				GET
					IF (_ymd == 0)
						RETURN System.DateTime.MinValue
					ENDIF
					RETURN System.DateTime{_year, _month, _day}
				END GET
				SET
				    IF Value != DateTime.MinValue
					    _year  := (WORD) Value:Year
					    _month := (BYTE) Value:Month
					    _day   := (BYTE) Value:Day
				    ELSE
					    _ymd := 0
				    ENDIF
				END SET
			END PROPERTY
		#endregion

		#region constructors
			STATIC CONSTRUCTOR()
				_NULL_DATE  := DATE{}
				_dtCalc     := DateTime{1901,1,1}
				RETURN

			/// Construct a date from a DateTime value.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(dt AS System.DateTime)
                if dt != DateTime.MinValue
				    _year  := (WORD) dt:Year
				    _month := (BYTE) dt:Month
				    _day   := (BYTE) dt:Day
                else
                    _ymd := 0
                endif
				RETURN

			/// Construct a date from another IDate type.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(d AS IDate)
				_year  := (WORD) d:Year
				_month := (BYTE) d:Month
				_day   := (BYTE) d:Day
				RETURN

			/// Construct a date from a number of Ticks.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(ticks AS INT64)
				SELF(System.DateTime{ticks})
				RETURN

			/// Construct a date from a string. This assumes the string is in current Date format
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(strDate AS STRING)
				LOCAL dValue := CToD(strDate) AS DATE
				_ymd := dValue:_ymd

			/// Construct a date from a string using the specified Date Format.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(strDate AS STRING, strFormat AS STRING)
				LOCAL dValue := CToD(strDate,strFormat) AS DATE
				_ymd := dValue:_ymd

			/// Construct a date from year, month, day
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(year AS INT, month AS INT, day AS INT)
                IF year != 0 .AND. month != 0 .AND. day != 0
				    TRY
					    // this may throw an exception when the combination is not valid
  				        VAR dt := System.DateTime{year, month, day}
					    _year  := (WORD) dt:Year
					    _month := (BYTE) dt:Month
					    _day   := (BYTE) dt:Day
				    CATCH
        				_ymd := 0 // null_date
					    NOP
                     END TRY
                ELSE
				    _ymd := 0 // null_date
                ENDIF
				RETURN

			/// Construct a date from year, month, day
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(year AS DWORD, month AS DWORD, day AS DWORD)
				// Chain to Int constructor
				SELF( (INT) year, (INT) month, (INT) day)
				RETURN

		#endregion

		#region methods
			/// <inheritdoc />
			METHOD CompareTo(o AS OBJECT) AS LONG
				VAR rhs := (DATE)o
				RETURN _ymd:CompareTo(rhs:_ymd)

			/// <inheritdoc />
			METHOD CompareTo(rhs AS DATE) AS LONG
				RETURN _ymd:CompareTo(rhs:_ymd)

			/// <inheritdoc />
			OVERRIDE METHOD GetHashCode() AS LONG
				RETURN _ymd:GetHashCode()

			/// <inheritdoc />
			METHOD GetTypeCode() AS System.TypeCode
				RETURN TypeCode.DateTime
		#endregion
		#region Operators
			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR ++(lhs AS DATE) AS DATE
				RETURN lhs:Add(1)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS USUAL) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS REAL8) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS LONG) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR +(lhs AS DATE, days AS INT64) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR +(lhs AS DATE, ts AS System.TimeSpan) AS DATE
				RETURN lhs:Add(ts)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS DWORD) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS UINT64) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR --(lhs AS DATE) AS DATE
				RETURN lhs:Subtract(1)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, rhs AS DATE) AS LONG
				RETURN lhs:Subtract(rhs)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS USUAL) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS REAL8) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS LONG) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS INT64) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, ts AS System.TimeSpan) AS DATE
				RETURN lhs:Subtract(ts)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS DWORD) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS UINT64) AS DATE
				RETURN lhs:Subtract(days)

		#endregion
		#region Equality Operators

			/// <inheritdoc />
			METHOD Equals(lhs AS DATE) AS LOGIC
				RETURN _ymd == lhs:_ymd

			/// <inheritdoc />
			OVERRIDE METHOD Equals(o AS OBJECT) AS LOGIC
				IF o != NULL
					IF o:getType() == TYPEOF(DATE)
						RETURN SELF:Equals(  (DATE) o)
					ELSEIF o:getType() == TYPEOF(System.DateTime)
						RETURN SELF:Equals( DATE{ (System.DateTime) o})
					ENDIF
				ENDIF
			RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR ==(lhs AS DATE, rhs AS DATE) AS LOGIC
				RETURN (lhs:_ymd == rhs:_ymd)

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR !=(lhs AS DATE, rhs AS DATE) AS LOGIC
				RETURN (lhs:_ymd != rhs:_ymd)

		#endregion
		#region Implicit and Explicit converters

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(dvalue AS DATE) AS DWORD
				// convert to julian date like vo
				// # of days since 1-1-1901 + 2415386
				LOCAL nDays AS DWORD
				nDays := (DWORD) (dValue:@@Value - _dtCalc):Days + VO_MIN_DATE
				RETURN nDays

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(dValue AS DATE) AS INT
				LOCAL nDays AS LONG
				nDays := (dValue:@@Value - _dtCalc):Days + VO_MIN_DATE
				RETURN nDays

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(dw AS DWORD ) AS DATE
				// convert julian date to our date
				LOCAL result := _NULL_DATE AS DATE
				IF dw >= VO_MIN_DATE .AND. dw <= VO_MAX_DATE
					LOCAL dt AS DateTime
					dt := _dtCalc:AddDays ( (INT) dw - VO_MIN_DATE)
					result := DATE{dt}
				ENDIF
				RETURN result

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(i AS INT) AS DATE
				LOCAL result := _NULL_DATE AS DATE
				IF i >= VO_MIN_DATE .AND. i <= VO_MAX_DATE
					LOCAL dt AS DateTime
					dt := _dtCalc:AddDays ( i - VO_MIN_DATE)
					result := DATE{dt}
				ENDIF
				RETURN result

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR IMPLICIT(v AS System.DateTime) AS DATE
				RETURN DATE{v}

			/// <exclude />
			METHOD ToDateTime() AS System.DateTime
				RETURN SELF:@@Value

			/// <exclude />
			METHOD FromDateTime(dtvalue AS System.DateTime) AS DATE
				RETURN DATE{dtvalue}

		#endregion
		#region Comparison Operators
			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR >(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _ymd for > comparison because that would depend
				//       on the processor layout
				IF lhs:_ymd == rhs:_ymd
					RETURN FALSE
				ELSEIF lhs:_year > rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month > rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day > rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR >=(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _ymd for > comparison because that would depend
				//       on the processor layout
				IF lhs:_ymd == rhs:_ymd
					RETURN TRUE
				ELSEIF lhs:_year > rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month > rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day >= rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR IMPLICIT(v AS DATE) AS System.DateTime
				RETURN v:@@Value

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR <(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _ymd for < comparison because that would depend
				//       on the processor layout
				IF lhs:_ymd == rhs:_ymd
					RETURN FALSE
				ELSEIF lhs:_year < rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month < rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day < rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR <=(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _ymd for < comparison because that would depend
				//       on the processor layout
				IF lhs:_ymd == rhs:_ymd
					RETURN TRUE
				ELSEIF lhs:_year < rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month < rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day <= rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE
		#endregion

		#region ADD and Subtract Methods
			/// <exclude />
			METHOD Add(days AS USUAL) AS DATE
				IF days:IsLong
					RETURN SELF:Add( (LONG) days)
				ELSEIF days:IsFloat
					 RETURN SELF:Add( (REAL8) days)
				ELSE
					THROW Error.ArgumentError(__ENTITY__,NAMEOF(days), "Incompatible argument for Date:Add()")
				ENDIF
			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS REAL8) AS DATE
				VAR res := SELF:@@Value:AddDays(days)
				RETURN DATE{res}
			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS LONG) AS DATE
				VAR res := SELF:@@Value:AddDays(days)
				RETURN DATE{res}
			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS INT64) AS DATE
				VAR res := SELF:@@Value:AddDays(days)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(span AS System.TimeSpan) AS DATE
				VAR res := SELF:@@Value:Add(span)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS DWORD) AS DATE
				VAR res := SELF:@@Value:AddDays(days)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS UINT64) AS DATE
				VAR res := SELF:@@Value:AddDays(days)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(lhs AS DATE) AS LONG
				LOCAL span AS System.TimeSpan
				span := (System.TimeSpan)(SELF:@@Value - lhs:@@Value)
				RETURN span:Days

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS USUAL) AS DATE
				IF days:IsLong
					RETURN SELF:Add( -(LONG) days)
				ELSEIF days:IsFloat
					 RETURN SELF:Add( -(REAL8) days)
				ELSE
					THROW Error.ArgumentError(__ENTITY__,NAMEOF(days), "Incompatible argument for Date:Subtract()")
				ENDIF

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS REAL8) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS LONG) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS INT64) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(ts AS System.TimeSpan) AS DATE
				RETURN SELF:Add(-ts)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS DWORD) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS UINT64) AS DATE
				RETURN SELF:Add(-(INT64)days)
		#endregion

		#region IConvertable
			// forward most methods to the DateTime class so there will
			// be a proper (localized) error message
			/// <inheritdoc />
			METHOD IConvertible.ToBoolean(provider AS System.IFormatProvider) AS LOGIC
				RETURN ((IConvertible)@@Value):ToBoolean(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToByte(provider AS System.IFormatProvider) AS BYTE
				RETURN ((IConvertible)@@Value):ToByte(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToChar(provider AS System.IFormatProvider) AS CHAR
				RETURN ((IConvertible)@@Value):ToChar(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToDateTime(provider AS System.IFormatProvider) AS System.DateTime
				RETURN @@Value

			/// <inheritdoc />
			METHOD IConvertible.ToDecimal(provider AS System.IFormatProvider) AS Decimal
				RETURN ((IConvertible)@@Value):ToDecimal(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToDouble(provider AS System.IFormatProvider) AS REAL8
				RETURN ((IConvertible)@@Value):ToDouble(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToInt16(provider AS System.IFormatProvider) AS SHORT
				RETURN ((IConvertible)@@Value):ToInt16(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToInt32(provider AS System.IFormatProvider) AS LONG
				RETURN ((IConvertible)@@Value):ToInt32(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToInt64(provider AS System.IFormatProvider) AS INT64
				RETURN ((IConvertible)@@Value):ToInt64(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToSByte(provider AS System.IFormatProvider) AS SByte
				RETURN ((IConvertible)@@Value):ToSByte(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToSingle(provider AS System.IFormatProvider) AS REAL4
				RETURN ((IConvertible)@@Value):ToSingle(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToType(conversionType AS System.Type, provider AS System.IFormatProvider) AS OBJECT
				IF conversionType == TYPEOF(DATE)
					RETURN SELF
				ELSEIF conversionType == TYPEOF(System.DateTime)
					RETURN SELF:@@Value
				ENDIF
				RETURN ((IConvertible)@@Value):ToType(conversionType, provider)

			/// <inheritdoc />
			METHOD IConvertible.ToUInt16(provider AS System.IFormatProvider) AS WORD
				RETURN ((IConvertible)@@Value):ToUInt16(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToUInt32(provider AS System.IFormatProvider) AS DWORD
				RETURN ((IConvertible)@@Value):ToUInt32(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToUInt64(provider AS System.IFormatProvider) AS UINT64
				RETURN ((IConvertible)@@Value):ToUInt64(provider)
		#endregion

		#region ToString()
			// Use DateTime ToString() methods as helpers
			/// <inheritdoc />
			OVERRIDE METHOD ToString() AS STRING
				IF (_ymd == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
			RETURN DToC(SELF)
			/// <inheritdoc />
			METHOD ToString(provider AS System.IFormatProvider) AS STRING
				IF (_ymd == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
				RETURN @@Value:ToString(provider)
			/// <inheritdoc cref="M:System.DateTime.ToString(System.String)"/>
			METHOD ToString(s AS STRING) AS STRING
				IF (_ymd == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
				RETURN @@Value:ToString(s)
			/// <inheritdoc />
			METHOD ToString(s AS STRING, fp AS System.IFormatProvider) AS STRING
				IF (_ymd == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
				IF (s == NULL)
					s := XSharp.RuntimeState.DateFormat
				ENDIF
				RETURN @@Value:ToString(s, fp)
		#endregion
		#region properties

			/// <inheritdoc />
			PROPERTY IsEmpty AS LOGIC
				GET
					RETURN _ymd == 0
				END GET
			END PROPERTY
			/// <inheritdoc />
			PROPERTY Month	AS INT GET _month
			/// <inheritdoc />
			PROPERTY Year	AS INT GET _year
			/// <inheritdoc />
			PROPERTY Day	AS INT GET _day
			// Next properties for easy access in right type
			INTERNAL PROPERTY DYear		AS DWORD GET _year
			INTERNAL PROPERTY DMonth	AS DWORD GET _month
			INTERNAL PROPERTY DDay		AS DWORD GET _day

			INTERNAL CLASS DateDebugView
				PRIVATE _value AS DATE
				PUBLIC CONSTRUCTOR (d AS DATE)
					_value := d

				PUBLIC PROPERTY Year	AS INT GET _value:Year
				PUBLIC PROPERTY Month	AS INT GET _value:Month
				PUBLIC PROPERTY Day		AS INT GET _value:Day

			END CLASS

		#endregion

		#region STATIC Properties

			/// <exclude />
			STATIC METHOD NullDate AS DATE
				RETURN  _NULL_DATE

		#endregion
	END STRUCTURE
END NAMESPACE

//
// 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.Runtime.Serialization
USING System.Globalization
USING System.Runtime.InteropServices
USING System.Runtime.CompilerServices
USING XSharp
USING System.Diagnostics
BEGIN NAMESPACE XSharp
	/// Internal type that implements the VO Compatible DATE type

	/// This type has many operators and implicit converters that normally are never directly called from user code.
	/// It holds year, month, day in 32 bits. For date calculations it uses the System.DateTime calculation logic.
	/// There are implicit converters between Date and DateTime.
	///
	/// <seealso cref="T:XSharp.IDate"/>
	/// <seealso cref="T:XSharp.RDD.DbDate"/>
	[DebuggerDisplay("{ToString(),nq}", Type := "DATE" )];
	[DebuggerTypeProxy(TYPEOF(DateDebugView))];
	[StructLayout(LayoutKind.Explicit,Pack := 1)];
	PUBLIC STRUCTURE __VODate IMPLEMENTS System.IComparable, ;
		System.IFormattable, ;
		System.IConvertible, ;
		IDate, ;
		IComparable<__VODate>, ;
		IEquatable<__VODate>
		// This structure uses an explicit layout to map
		// year, month and date into 32 bits
		// the _value field is for convenience, so we can do a single numeric comparison
		// to determine equality or relative comparisons in stead of doing 3 comparisons
		// for date calculation we use the Value PROPERTY which returns a System.DateTime type
		// Note that the Vulcan type uses a datetime which takes 8 bytes. We only use 4 bytes
		#region fields
			[FieldOffSet(00)] PRIVATE _value AS System.Int32
			[FieldOffSet(00)] PRIVATE _year  AS System.UInt16
			[FieldOffSet(02)] PRIVATE _month AS System.Byte
			[FieldOffSet(03)] PRIVATE _day   AS System.Byte
		#endregion

		#region STATIC fields
			/// <exclude />
			STATIC INITONLY PUBLIC  _NULL_DATE AS DATE
			/// <exclude />
			CONST  VO_MIN_DATE := 2415386U AS DWORD	// 1901-01-01
			/// <exclude />
			CONST  VO_MAX_DATE := 4606840U AS DWORD	// 7900-12-31
			/// <exclude />
			STATIC INITONLY _dtCalc AS Datetime

		#endregion

		#region datetime conversions
			/// <exclude />
			PROPERTY @@Value AS System.DateTime  GET SELF:_dtValue SET _dtValue := VALUE

			PRIVATE PROPERTY _dtValue AS System.DateTime
				GET
					IF (_value == 0)
						RETURN System.DateTime.MinValue
					ENDIF
					RETURN System.DateTime{_year, _month, _day}
				END GET
				SET
				IF @@Value != DateTime.MinValue
					_year  := (WORD) @@Value:Year
					_month := (BYTE) @@Value:Month
					_day   := (BYTE) @@Value:Day
				ELSE
					_value := 0
				ENDIF
				END SET
			END PROPERTY
		#endregion

		#region constructors
			STATIC CONSTRUCTOR()
				_NULL_DATE  := DATE{}
				_dtCalc     := DateTime{1901,1,1}
				RETURN

			/// Construct a date from a DateTime value.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(lhs AS System.DateTime)
				_value := 0
				_year  := (WORD) lhs:Year
				_month := (BYTE) lhs:Month
				_day   := (BYTE) lhs:Day
				RETURN

			/// Construct a date from another IDate type.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(d AS iDate)
				_value := 0
				_year  := (WORD) d:Year
				_month := (BYTE) d:Month
				_day   := (BYTE) d:Day
				RETURN

			/// Construct a date from a number of Ticks.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(ticks AS INT64)
				SELF(System.DateTime{ticks})
				RETURN

			/// Construct a date from a string. This assumes the string is in current Date format
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(strDate AS STRING)
				LOCAL dValue := CToD(strDate) AS DATE
				_value := 0
				_year  := dValue:_year
				_month := dValue:_month
				_day   := dValue:_day

			/// Construct a date from a string using the specified Date Format.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(strDate AS STRING, strFormat AS STRING)
				LOCAL dValue := CToD(strDate,strFormat) AS DATE
				_value := 0
				_year  := dValue:_year
				_month := dValue:_month
				_day   := dValue:_day

			/// Construct a date from year, month, day
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(year AS INT, month AS INT, day AS INT)
				_value := 0 // null_date
				_year  := 0
				_month := 0
				_day   := 0
                IF year != 0 .AND. month != 0 .AND. day != 0
				    TRY
					    // this may throw an exception when the combination is not valid

					    VAR lhs := System.DateTime{year, month, day}
					    _value := 0
					    _year  := (WORD) lhs:Year
					    _month := (BYTE) lhs:Month
					    _day   := (BYTE) lhs:Day
				    CATCH
					    NOP
                    END TRY
                ENDIF
				RETURN

			/// Construct a date from year, month, day
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			CONSTRUCTOR(year AS DWORD, month AS DWORD, day AS DWORD)
				// Chain to Int constructor
				SELF( (INT) year, (INT) month, (INT) day)
				RETURN

		#endregion

		#region methods
			/// <inheritdoc />
			METHOD CompareTo(o AS OBJECT) AS LONG
				VAR rhs := (DATE)o
				RETURN _value:CompareTo(rhs:_value)

			/// <inheritdoc />
			METHOD CompareTo(rhs AS DATE) AS LONG
				RETURN _value:CompareTo(rhs:_value)

			/// <inheritdoc />
			OVERRIDE METHOD GetHashCode() AS LONG
				RETURN _value:GetHashCode()

			/// <inheritdoc />
			METHOD GetTypeCode() AS System.TypeCode
				RETURN TypeCode.DateTime
		#endregion
		#region Operators
			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR ++(lhs AS DATE) AS DATE
				RETURN lhs:Add(1)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS USUAL) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS REAL8) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS LONG) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR +(lhs AS DATE, days AS INT64) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR +(lhs AS DATE, ts AS System.TimeSpan) AS DATE
				RETURN lhs:Add(ts)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS DWORD) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR +(lhs AS DATE, days AS UINT64) AS DATE
				RETURN lhs:Add(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR --(lhs AS DATE) AS DATE
				RETURN lhs:Subtract(1)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, rhs AS DATE) AS LONG
				RETURN lhs:Subtract(rhs)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS USUAL) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS REAL8) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS LONG) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS INT64) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, ts AS System.TimeSpan) AS DATE
				RETURN lhs:Subtract(ts)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS DWORD) AS DATE
				RETURN lhs:Subtract(days)

			/// This operator is used in code generated by the compiler when needed.
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			STATIC OPERATOR -(lhs AS DATE, days AS UINT64) AS DATE
				RETURN lhs:Subtract(days)

		#endregion
		#region Equality Operators

			/// <inheritdoc />
			METHOD Equals(lhs AS DATE) AS LOGIC
				RETURN _value == lhs:_value

			/// <inheritdoc />
			OVERRIDE METHOD Equals(o AS OBJECT) AS LOGIC
				IF o != NULL
					IF o:getType() == TYPEOF(DATE)
						RETURN SELF:Equals(  (DATE) o)
					ELSEIF o:getType() == TYPEOF(System.DateTime)
						RETURN SELF:Equals( DATE{ (System.DateTime) o})
					ENDIF
				ENDIF
			RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR ==(lhs AS DATE, rhs AS DATE) AS LOGIC
				RETURN (lhs:_value == rhs:_value)

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR !=(lhs AS DATE, rhs AS DATE) AS LOGIC
				RETURN (lhs:_value != rhs:_value)

		#endregion
		#region Implicit and Explicit converters

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(dvalue AS DATE) AS DWORD
				// convert to julian date like vo
				// # of days since 1-1-1901 + 2415386
				LOCAL nDays AS DWORD
				nDays := (DWORD) (dValue:_dtValue - _dtCalc):Days + VO_MIN_DATE
				RETURN nDays

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(dValue AS DATE) AS INT
				LOCAL nDays AS LONG
				nDays := (dValue:_dtValue - _dtCalc):Days + VO_MIN_DATE
				RETURN nDays

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(dw AS DWORD ) AS DATE
				// convert julian date to our date
				LOCAL result := _NULL_DATE AS DATE
				IF dw >= VO_MIN_DATE .AND. dw <= VO_MAX_DATE
					LOCAL dt AS DateTime
					dt := _dtCalc:AddDays ( (INT) dw - VO_MIN_DATE)
					result := DATE{dt}
				ENDIF
				RETURN result

			/// This explicit converter is used in code generated by the compiler when you use a cast in your code.
			STATIC OPERATOR EXPLICIT(i AS INT) AS DATE
				LOCAL result := _NULL_DATE AS DATE
				IF i >= VO_MIN_DATE .AND. i <= VO_MAX_DATE
					LOCAL dt AS DateTime
					dt := _dtCalc:AddDays ( i - VO_MIN_DATE)
					result := DATE{dt}
				ENDIF
				RETURN result

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR IMPLICIT(v AS System.DateTime) AS DATE
				RETURN DATE{v}

			/// <exclude />
			METHOD ToDateTime() AS System.DateTime
				RETURN SELF:_dtValue

			/// <exclude />
			METHOD FromDateTime(dtvalue AS System.DateTime) AS DATE
				RETURN DATE{dtvalue}

		#endregion
		#region Comparison Operators
			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR >(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _value for > comparison because that would depend
				//       on the processor layout
				IF lhs:_value == rhs:_value
					RETURN FALSE
				ELSEIF lhs:_year > rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month > rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day > rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR >=(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _value for > comparison because that would depend
				//       on the processor layout
				IF lhs:_value == rhs:_value
					RETURN TRUE
				ELSEIF lhs:_year > rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month > rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day >= rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR IMPLICIT(v AS DATE) AS System.DateTime
				RETURN v:_dtValue

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR <(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _value for < comparison because that would depend
				//       on the processor layout
				IF lhs:_value == rhs:_value
					RETURN FALSE
				ELSEIF lhs:_year < rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month < rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day < rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE

			/// This operator is used in code generated by the compiler when needed.
			STATIC OPERATOR <=(lhs AS DATE, rhs AS DATE) AS LOGIC
				// note: cannot use _value for < comparison because that would depend
				//       on the processor layout
				IF lhs:_value == rhs:_value
					RETURN TRUE
				ELSEIF lhs:_year < rhs:_year
					RETURN TRUE
				ELSEIF lhs:_year == rhs:_year
					IF lhs:_month < rhs:_month
						RETURN TRUE
					ELSEIF lhs:_month == rhs:_month .AND. lhs:_day <= rhs:_day
						RETURN TRUE
					ENDIF
				ENDIF
				RETURN FALSE
		#endregion

		#region ADD and Subtract Methods
			/// <exclude />
			METHOD Add(days AS USUAL) AS DATE
				IF days:IsLong
					RETURN SELF:Add( (LONG) days)
				ELSEIF days:IsFloat
					 RETURN SELF:Add( (REAL8) days)
				ELSE
					THROW Error.ArgumentError(__ENTITY__,NAMEOF(days), "Incompatible argument for Date:Add()")
				ENDIF
			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS REAL8) AS DATE
				VAR res := SELF:_dtValue:AddDays(days)
				RETURN DATE{res}
			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS LONG) AS DATE
				VAR res := SELF:_dtValue:AddDays(days)
				RETURN DATE{res}
			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS INT64) AS DATE
				VAR res := SELF:_dtValue:AddDays(days)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(span AS System.TimeSpan) AS DATE
				VAR res := SELF:_dtValue:Add(span)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS DWORD) AS DATE
				VAR res := SELF:_dtValue:AddDays(days)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Add(days AS UINT64) AS DATE
				VAR res := SELF:_dtValue:AddDays(days)
				RETURN DATE{res}

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(lhs AS DATE) AS LONG
				LOCAL span AS System.TimeSpan
				span := (System.TimeSpan)(SELF:_dtValue - lhs:_dtValue)
				RETURN span:Days

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS USUAL) AS DATE
				IF days:IsLong
					RETURN SELF:Add( -(LONG) days)
				ELSEIF days:IsFloat
					 RETURN SELF:Add( -(REAL8) days)
				ELSE
					THROW Error.ArgumentError(__ENTITY__,NAMEOF(days), "Incompatible argument for Date:Subtract()")
				ENDIF

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS REAL8) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS LONG) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS INT64) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(ts AS System.TimeSpan) AS DATE
				RETURN SELF:Add(-ts)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS DWORD) AS DATE
				RETURN SELF:Add(-days)

			/// <exclude />
            [MethodImpl(MethodImplOptions.AggressiveInlining)];
			METHOD Subtract(days AS UINT64) AS DATE
				RETURN SELF:Add(-(INT64)days)
		#endregion

		#region IConvertable
			// forward most methods to the DateTime class so there will
			// be a proper (localized) error message
			/// <inheritdoc />
			METHOD IConvertible.ToBoolean(provider AS System.IFormatProvider) AS LOGIC
				RETURN ((IConvertible)@@Value):ToBoolean(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToByte(provider AS System.IFormatProvider) AS BYTE
				RETURN ((IConvertible)@@Value):ToByte(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToChar(provider AS System.IFormatProvider) AS CHAR
				RETURN ((IConvertible)@@Value):ToChar(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToDateTime(provider AS System.IFormatProvider) AS System.DateTime
				RETURN @@Value

			/// <inheritdoc />
			METHOD IConvertible.ToDecimal(provider AS System.IFormatProvider) AS Decimal
				RETURN ((IConvertible)@@Value):ToDecimal(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToDouble(provider AS System.IFormatProvider) AS REAL8
				RETURN ((IConvertible)@@Value):ToDouble(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToInt16(provider AS System.IFormatProvider) AS SHORT
				RETURN ((IConvertible)@@Value):ToInt16(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToInt32(provider AS System.IFormatProvider) AS LONG
				RETURN ((IConvertible)@@Value):ToInt32(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToInt64(provider AS System.IFormatProvider) AS INT64
				RETURN ((IConvertible)@@Value):ToInt64(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToSByte(provider AS System.IFormatProvider) AS SByte
				RETURN ((IConvertible)@@Value):ToSByte(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToSingle(provider AS System.IFormatProvider) AS REAL4
				RETURN ((IConvertible)@@Value):ToSingle(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToType(conversionType AS System.Type, provider AS System.IFormatProvider) AS OBJECT
				IF conversionType == TYPEOF(DATE)
					RETURN SELF
				ELSEIF conversionType == TYPEOF(System.DateTime)
					RETURN SELF:_dtValue
				ENDIF
				RETURN ((IConvertible)@@Value):ToType(conversionType, provider)

			/// <inheritdoc />
			METHOD IConvertible.ToUInt16(provider AS System.IFormatProvider) AS WORD
				RETURN ((IConvertible)@@Value):ToUInt16(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToUInt32(provider AS System.IFormatProvider) AS DWORD
				RETURN ((IConvertible)@@Value):ToUInt32(provider)

			/// <inheritdoc />
			METHOD IConvertible.ToUInt64(provider AS System.IFormatProvider) AS UINT64
				RETURN ((IConvertible)@@Value):ToUInt64(provider)
		#endregion

		#region ToString()
			// Use DateTime ToString() methods as helpers
			/// <inheritdoc />
			OVERRIDE METHOD ToString() AS STRING
				IF (_value == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
			RETURN DToC(SELF)
			/// <inheritdoc />
			METHOD ToString(provider AS System.IFormatProvider) AS STRING
				IF (_value == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
				RETURN @@Value:ToString(provider)
			/// <inheritdoc cref="M:System.DateTime.ToString(System.String)"/>
			METHOD ToString(s AS STRING) AS STRING
				IF (_value == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
				RETURN @@Value:ToString(s)
			/// <inheritdoc />
			METHOD ToString(s AS STRING, fp AS System.IFormatProvider) AS STRING
				IF (_value == 0)
					RETURN RuntimeState.NullDateString
				ENDIF
				IF (s == NULL)
					s := XSharp.RuntimeState.DateFormat
				ENDIF
				RETURN @@Value:ToString(s, fp)
		#endregion
		#region properties

			/// <inheritdoc />
			PROPERTY IsEmpty AS LOGIC
				GET
					RETURN _value == 0
				END GET
			END PROPERTY
			/// <inheritdoc />
			PROPERTY Month	AS INT GET _month
			/// <inheritdoc />
			PROPERTY Year	AS INT GET _year
			/// <inheritdoc />
			PROPERTY Day	AS INT GET _day
			// Next properties for easy access in right type
			INTERNAL PROPERTY DYear		AS DWORD GET _year
			INTERNAL PROPERTY DMonth	AS DWORD GET _month
			INTERNAL PROPERTY DDay		AS DWORD GET _day

			INTERNAL CLASS DateDebugView
				PRIVATE _value AS DATE
				PUBLIC CONSTRUCTOR (d AS DATE)
					_value := d

				PUBLIC PROPERTY Year	AS INT GET _value:Year
				PUBLIC PROPERTY Month	AS INT GET _value:Month
				PUBLIC PROPERTY Day		AS INT GET _value:Day

			END CLASS

		#endregion

		#region STATIC Properties

			/// <exclude />
			STATIC METHOD NullDate AS DATE
				RETURN  _NULL_DATE

		#endregion
	END STRUCTURE
END NAMESPACE


make tryIncr local, so we can simplify it without loosening the semantics also add an n-times 'up' function

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)

tryIncr' :: Nat' (2+b)t -> Fin' (2+b)t a -> (Equal (2+b)t (1+a)t + Fin' (2+b)t (1+a)t)
tryIncr' 2v 1fp = L Eq
tryIncr' (2+v)v 0fp = R 1fp
tryIncr' (3+v)v 1fp = R 2fp
tryIncr' (3+v)v (1+f)fp = case tryIncr' (2+v)v f of
                          L Eq -> L Eq
                          R i -> R (1+i)fp

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

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

tryIncr :: Nat' (2+b)t -> Fin (2+b)t -> Maybe (Fin (2+b)t)
tryIncr 2v 1f = Nothing
tryIncr (2+v)v 0f = Just 1f
tryIncr (3+v)v 1f = Just 2f
tryIncr (3+v)v (1+f)f = do { i <- tryIncr (2+v)v f; return (1+i)f }

-- 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)

tryIncr' :: Nat' (2+b)t -> Fin' (2+b)t a -> (Equal (2+b)t (1+a)t + Fin' (2+b)t (1+a)t)
tryIncr' 2v 1fp = L Eq
tryIncr' (2+v)v 0fp = R 1fp
tryIncr' (3+v)v 1fp = R 2fp
tryIncr' (3+v)v (1+f)fp = case tryIncr' (2+v)v f of
                          L Eq -> L Eq
                          R i -> R (1+i)fp

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


we have something that works

we have something that works

-- Modeling Opetopes
-- here is Eric's editor: http://sma.epfl.ch/~finster/opetope/opetope.html
--
-- Note: Kock et al. use a similar scheme, where
--          - dots    <-->   lines
--          - spheres <-->   dots
--          - lines disappear
--          -                spheres appear

-- Note: In Eric's new work (at IAS) there are no
--       dots any more, instead he uses 'external cards', so
--          - (external) cards   <-->  lines
--          - space between stacked cards <--> external cards
--          - lines disappear
--          -                (regular) cards appear

import "../tests/Nat.prg"

-- GLOBAL TODOs
----------------

-- Naming: Seems essential for identifying things Hor(Name), Ver(Tree)
--         Name can be Label, then Niches (= sequences of opetope names).
--         the latter arise from the composition rule.
--         Note: don't store names in Stacks, that kills alpha
--               conversion. Instead names should be pushed into nodes
--               'singleton style' from outside: (node :: Tree (Hor `g))

-- Morphisms: given an opetope and a pointer to one of its cards
--            extract the opetope of that face (do we need a label
--            space to reliably find it?). This must be a type
--            function, with an algorithm accompanying it to extract
--            the face proper.

-- Composition: grafting of trees to external cards, but only if the
--              morphisms (faces) match. lower-dim stuff is unaffected.

-- Swapping: given a proof of equality between two niches, provide an
--           operation to swap them, replacing old faces by new ones,
--           FGL-style (functional graph library), just note that
--           certain morphisms are gone, others appeared.

-- References: a disciplined way of saying "I am an identical node to that guy",
--             by corollary these always obtain the same names.
--             a topologist would say 'surgery' (http://en.wikipedia.org/wiki/Surgery_theory)
--             We get a new Tree flavour, Ref, at least one up, followed by
--             naturals to index a node in a subtree.
--             At this point we are doing algebraic topology, and dealing with
--             closed manifolds, so that we can do homology etc. on them.
--             In logic this corresponds to relations, satisfiability.

-- Quoting: So far we have only dealt with data, i.e. quoted (inert) syntax.
--          we need a way to convert this syntax to code (e.g. at some stage),
--          to obtain semantics. We can have the usual splicing game.
--          Lisp does this with sexprs and macros.
--          The code needs then be executed (run, decreasing stage), when
--          it becomes active, and we can observe the manifestation of its
--          semantics. The reduction system could go along the lines of the
--          (typed) LambdaGraph.

-- NbE: I fully believe we can use this technique to reduce opetopic terms
--      to normal form. ('many syntax', 'one semantics' adjunction:
--                       http://www.logicmatters.net/resources/pdfs/Galois.pdf)

-- TODO: these three must be LeftLists
--
kind Trees = NoTree | Pre (Tree d) Trees deriving syntax (ts) List(NoTree, Pre)

data ZoomCompley :: Tree e ~> Tree f ~> * where
  Nily :: ZoomCompley a a
  Consy :: Stack a b -> ZoomCompley b c -> ZoomCompley a c
 deriving List(cply)

data ZoomComplex :: Trees ~> * where
  Nilx :: ZoomComplex [()tr]ts
  Endx :: Stack a b -> ZoomComplex [a, b]ts
  Consx :: Stack a b -> ZoomComplex [b; c]ts -> ZoomComplex [a, b; c]ts
 deriving syntax (cplx) List(Nilx, Consx) Item(Endx)

data Dir :: *2 where
  Hor :: Dir
  Ver :: Dir

-- define the Tree kind, a rose tree
-- note: we may need a two-level kind, since we need to track disjointness
--       of subtrees
-- todo: later we may want to have a label kind parameter too

data Tree :: Dir ~> *1 where
  Unit :: Tree Ver
  Ni :: Tree Hor
  Fork :: Tree d ~> Tree Hor ~> Tree Hor
 deriving syntax (tr) List(Ni, Fork) Unit(Unit)

-- make it singleton

data Tree' :: Tree d ~> * where
  In :: Tree' ()tr
  Fork :: treelike head -> Tree' tail -> Tree' [head; tail]tr
  Done :: Tree' []tr
 deriving syntax (ar) List(Done, Fork) Unit(In) -- "arbre"
-- http://www.papoulipoesique.com/wp-content/uploads/2013/06/arbre.jpg

-- define a proposition for subtrees
-- to be checked: TakeHead must ensure that all the node is consumed

prop Subtree :: Tree d ~> Tree e ~> * where
  UnitSub :: Subtree ()tr tr
  BothNil :: Subtree []tr []tr
  TakeHead :: Subtree head head' -> Subtree tail tail' -> Subtree [head; tail]tr [head'; tail']tr

-- TODO: separate the notions of Niche / Frame / Cell
kind Volume = FramedHollow | FilledCell
kind Diagram = Closed Volume | OpenNiche
-- should be a parameter to Stack

-- now we can stack cards (these are zooms in Kock et al. parlance)

data Stack :: Tree d ~> Tree e ~> * where
  Corolla :: Corolla tr => Tree' tr -> Stack tr ()tr

  -- TODO: needed?
  --Expand :: (Subtree consumed tr, {nodeValence consumed} `Equal` {nodeValence tr}) => Stack consumed prod -> Stack tr prod

  -- add another target face to a web
  Target :: ({nodes tr} `Equal` {nodeValence out}) => Stack tr out -> Stack tr out

  -- the following three grab a node (and possibly its offsprings) and incorporate it into a single card
  NodeDone :: Stack []tr [()tr]tr
  Pick :: {- EntireNode => -} Stack head prodhead -> Stack tail prodtail -> Stack [head; tail]tr [prodhead; prodtail]tr
  Exclude :: {- EntireNode => -} Stack tail prod -> Stack [()tr; tail]tr prod
  -- TODO: maybe just specify a subtree (Tree') and that's it

  On :: (Subtree tr' tr, Pointers 1t at out) => Stack tr' out' -> Stack tr out -> Tree' at -> Stack tr {graft out' at out}

  -- building niches (pasting diagrams)
  -- TODO: these must produce a composite labelled Hor
  NicheDone :: Stack ()tr []tr
  Niche :: Stack tr out -> Stack tr [out]tr
  Also :: Pointers 1t at tr
       => Tree' at -> Stack tr' out' -> Stack tr out -> Stack {extgraft tr' at tr} [out'; out]tr

  -- put a frame around a niche
  Frame :: (Corolla out, {nodes tr}  `Equal` {nodeValence out}) => Stack tr out -> Stack tr out

 deriving syntax(z) Record(NicheDone, Also) Item(Target)

stop = ({}z)z

-- this is bogus, just make it compile again...
up :: Reference tr => Stack ()tr tr -> Stack ()tr [tr]tr
up inner = ({()ar=inner}z)z

-- it remains to define corollas

-- TODO: one could say {nodes tr} == 1
prop Corolla :: Tree d ~> * where
  None' :: Corolla []tr
  One' :: Corolla tail -> Corolla [()tr; tail]tr

--  o   --->  |
--  |         |

lolliCell :: Stack []tr ()tr
lolliCell = Corolla Done

--  |         |
--  o   --->  |
--  |         |

dolliCell :: Stack [()tr]tr ()tr
dolliCell = Corolla $ Fork In Done

--              |
--  [o]   --->  o
--   |          |

lolliFrame :: Stack []tr [()tr]tr
lolliFrame = Target NodeDone

--   |          |
--  [o]   --->  o
--   |          |

dolliFrame :: Stack [()tr]tr [()tr]tr
dolliFrame = Target (Exclude NodeDone)

--   |          |
--   |          o
-- [[o]]  --->  |
--   |          o
--   |          |

stacked :: Stack [()tr]tr [[()tr]tr]tr
stacked = (dolliFrame `On` dolliFrame) (In `Fork` Done)

--   |
--  [|]   --->  o
--   |          |

crossed :: Stack ()tr []tr
crossed = stop

-- we can now join things
--
dolliForever = [dolliFrame; dolliForever]cply
nopetope = [crossed, lolliFrame; dolliForever]cply

-- back to unit as fast as possible...
--
stopetope = [crossed, lolliFrame, dolliCell]cply
neverstopetope = [crossed, lolliFrame, dolliCell; neverstopetope]cply
stopetope' = [crossed, lolliFrame; (dolliCell)cplx]cplx
stopetope'' = [crossed, lolliFrame, dolliCell]cplx

--   |          o
-- [[|]]  --->  |
--   |          o
--   |          |

drossed :: Stack ()tr [[]tr]tr
drossed = up stop

-- ################################
-- ############  Niches ###########
-- ################################

--  |          +
--  |          |

-- this is a niche, but assume to be a frame
niche0 :: Stack ()tr []tr
niche0 = {}z

--  |           |
-- [|]          +
--  |           |

-- this is a niche, but assume to be a frame
niche1 :: Stack ()tr [[]tr]tr
niche1 = {()ar=stop; {}z}z

--  |         o   o
-- [|]  --->   \ /
-- [|]          +
--  |           |

-- this is a niche, but assume to be a frame
niche2 :: Stack ()tr [[]tr, []tr]tr
niche2 = {()ar=stop; niche1}z

--                |
--  |         o   o
-- [o]  --->   \ /
-- [|]          +
--  |           |

niche10 :: Stack [()tr]tr [[()tr]tr, []tr]tr
niche10 = {()ar=Exclude NodeDone; Niche stop}z

-- ################################
-- ############  Frames ###########
-- ################################

--   |              |
--  /|\         o   o
-- |[o]|  --->   \ /
-- |[|]|          o
--  \|/           |
--   |

cyclops :: Stack [()tr]tr [[()tr]tr, []tr]tr
cyclops = (niche10)z

-- Stacking, Valence, Affine subtree, Substitute at an affine position

-- counting Units in a tree

valence :: Tree d ~> Nat
{valence []tr} = 0t
{valence [head; tail]tr} = {plus {valence head} {valence tail}}
{valence ()tr} = 1t

-- counting siblings of a tree

nodeValence :: Tree d ~> Nat
{nodeValence []tr} = 0t
{nodeValence [head; tail]tr} = (1+{nodeValence tail})t

-- counting nodes in a tree

nodes :: Tree d ~> Nat
{nodes ()tr} = t
{nodes []tr} = 1t
{nodes [head; tail]tr} = {plus {nodes head} {nodes tail}}

-- nodeValenceAt: given a (multi)pointer, determine the node valences at those positions
--               WHERE     IN
nodeValenceAt :: Tree d ~> Tree e ~> Nat
{nodeValenceAt ()tr tree} = {nodeValence tree}
{nodeValenceAt []tr []tr} = 0t
{nodeValenceAt [head'; tail']tr [head; tail]tr} = {plus {nodeValenceAt head' head} {nodeValenceAt tail' tail}}

-- a pointer is a valence-1 subtree of a tree
-- it is used to mark a unit in a tree where substitution will occur
--
prop Pointers :: Nat ~> Tree d ~> Tree e ~> * where
  Finger :: Pointers 1t ()tr ()tr
  Finished :: Pointers 0t []tr []tr
  ThisWay :: Pointers 1t head' head -> Pointers n tail' tail -> Pointers (1+n)t [head'; tail']tr [head; tail]tr
  ElseWhere :: Pointers n tail' tail -> Pointers n [[]tr; tail']tr [head; tail]tr

-- http://en.wikipedia.org/wiki/Grafting
--
-- graft WHAT      WHERE     IN
graft :: Tree d ~> Tree e ~> Tree f ~> Tree f
{graft what ()tr ()tr} = what
{graft what []tr tr} = tr
{graft what [head'; tail']tr [head; tail]tr} = [{graft what head' head}; {graft what tail' tail}]tr

-- extgraft: extend and graft
--
extgraft :: Tree what ~> Tree wher ~> Tree tree ~> Tree tree'
{extgraft what ()tr ()tr} = what
{extgraft what []tr ()tr} = ()tr
{extgraft what [head; tail]tr ()tr} = {extgrafthor what [head; tail]tr}
{extgraft what []tr []tr} = []tr
{extgraft what []tr [head; tail]tr} = [head; tail]tr
{extgraft what [head'; tail']tr [head; tail]tr} = [{extgraft what head' head}; {extgraft what tail' tail}]tr

-- helper:
extgrafthor :: Tree what ~> Tree Hor ~> Tree Hor
{extgrafthor what []tr} = []tr
{extgrafthor what [head; tail]tr} = [{extgraft what head ()tr}; {extgrafthor what tail}]tr

{- NOTE: we have an Omega bug here:

prompt> :norm {graft []tr ()tr ()tr}
Normalizes to:
  []tr

prompt> :kind []tr
[]tr :: Tree Hor  = []tr

prompt> :kind {graft []tr ()tr ()tr}
{graft []tr ()tr ()tr} :: Tree Ver  = {graft []tr ()tr ()tr}

... BUT: Hor /= Ver :-(
-}

-- substitution: replace a pointed node (of valence n) in a tree with an
-- other tree of (valence n) -- TODO

-- HERE IS A NICE RESEARCH QUESTION:
-- is it possible to "proof search" a Zoom?
-- i.e.
{-

class ZoomLike (int :: Tree) (out :: Tree) where
  zoom :: (Nodes int ~ Valence out) => Zoom int out

-}

prop Reference :: Tree Hor ~> * where
  Stop :: Reference []tr
  Up :: Reference tr -> Reference [tr]tr
 deriving Nat(d) -- de Bruijn index

prop Teleport :: Tree Hor ~> * where
  Gate :: Teleport [()tr]tr
  Tele :: Teleport tr -> Teleport [tr]tr
 deriving Nat(sk) -- skips

{-

0
|   |
1   L  (lambda node)
|   |
2   1  (how many skips)
 \ /
  o
  |
  B  (binds here)
  |

Encodes \x.xx

-}

r2 = 2d
t1 = 1sk

lamX_XX = addBinder $ [r2, t1]ar

prop BindsUp :: Nat ~> Tree Hor ~> * where
  LastSkip :: BindsUp 1t [()tr]tr
  MoreSkip :: Teleport tr -> BindsUp n tr -> BindsUp (1+n)t [tr]tr
  -- prove Application node
  HeadBindsUp :: Nat' n -> BindsUp (1+n)t head -> BindsUp n [head; tail]tr
  TailBindsUp :: Nat' n -> BindsUp n [head; tail]tr -> BindsUp n [nonempty, head; tail]tr

-- TODO: make sure that something teleports here
addBinder :: {-BindsUp 1t tr =>-} Tree' tr -> Tree' [tr]tr
addBinder term = [term]ar

testB :: BindsUp 0t [[[[]tr]tr]tr,[()tr]tr]tr -> Tree' [[[[]tr]tr]tr,[()tr]tr]tr
testB ev = [r2, 0sk]ar
testB' = testB (TailBindsUp 0v (HeadBindsUp 0v (LastSkip)))

--TODO: prop Lambda :: Tree Hor ~> * where

{-
The slogan is: binder nodes do not count as result nodes:
 - just consider the next node upstream
 - incoming (external) cards, when identified to a binder do not count as inputs to application
 - incoming (external) cards, when identified to an (iterated) application become pattern matching (sigma).

In the end all identifications have a semantics (hopefully!), when the counting rules are ensured.

-}

-- Modeling Opetopes
-- here is Eric's editor: http://sma.epfl.ch/~finster/opetope/opetope.html
--
-- Note: Kock et al. use a similar scheme, where
--          - dots    <-->   lines
--          - spheres <-->   dots
--          - lines disappear
--          -                spheres appear

-- Note: In Eric's new work (at IAS) there are no
--       dots any more, instead he uses 'external cards', so
--          - (external) cards   <-->  lines
--          - space between stacked cards <--> external cards
--          - lines disappear
--          -                (regular) cards appear

import "../tests/Nat.prg"

-- GLOBAL TODOs
----------------

-- Naming: Seems essential for identifying things Hor(Name), Ver(Tree)
--         Name can be Label, then Niches (= sequences of opetope names).
--         the latter arise from the composition rule.
--         Note: don't store names in Stacks, that kills alpha
--               conversion. Instead names should be pushed into nodes
--               'singleton style' from outside: (node :: Tree (Hor `g))

-- Morphisms: given an opetope and a pointer to one of its cards
--            extract the opetope of that face (do we need a label
--            space to reliably find it?). This must be a type
--            function, with an algorithm accompanying it to extract
--            the face proper.

-- Composition: grafting of trees to external cards, but only if the
--              morphisms (faces) match. lower-dim stuff is unaffected.

-- Swapping: given a proof of equality between two niches, provide an
--           operation to swap them, replacing old faces by new ones,
--           FGL-style (functional graph library), just note that
--           certain morphisms are gone, others appeared.

-- References: a disciplined way of saying "I am an identical node to that guy",
--             by corollary these always obtain the same names.
--             a topologist would say 'surgery' (http://en.wikipedia.org/wiki/Surgery_theory)
--             We get a new Tree flavour, Ref, at least one up, followed by
--             naturals to index a node in a subtree.
--             At this point we are doing algebraic topology, and dealing with
--             closed manifolds, so that we can do homology etc. on them.
--             In logic this corresponds to relations, satisfiability.

-- Quoting: So far we have only dealt with data, i.e. quoted (inert) syntax.
--          we need a way to convert this syntax to code (e.g. at some stage),
--          to obtain semantics. We can have the usual splicing game.
--          Lisp does this with sexprs and macros.
--          The code needs then be executed (run, decreasing stage), when
--          it becomes active, and we can observe the manifestation of its
--          semantics. The reduction system could go along the lines of the
--          (typed) LambdaGraph.

-- NbE: I fully believe we can use this technique to reduce opetopic terms
--      to normal form. ('many syntax', 'one semantics' adjunction:
--                       http://www.logicmatters.net/resources/pdfs/Galois.pdf)

-- TODO: these three must be LeftLists
--
kind Trees = NoTree | Pre (Tree d) Trees deriving syntax (ts) List(NoTree, Pre)

data ZoomCompley :: Tree e ~> Tree f ~> * where
  Nily :: ZoomCompley a a
  Consy :: Stack a b -> ZoomCompley b c -> ZoomCompley a c
 deriving List(cply)

data ZoomComplex :: Trees ~> * where
  Nilx :: ZoomComplex [()tr]ts
  Endx :: Stack a b -> ZoomComplex [a, b]ts
  Consx :: Stack a b -> ZoomComplex [b; c]ts -> ZoomComplex [a, b; c]ts
 deriving syntax (cplx) List(Nilx, Consx) Item(Endx)

data Dir :: *2 where
  Hor :: Dir
  Ver :: Dir

-- define the Tree kind, a rose tree
-- note: we may need a two-level kind, since we need to track disjointness
--       of subtrees
-- todo: later we may want to have a label kind parameter too

data Tree :: Dir ~> *1 where
  Unit :: Tree Ver
  Ni :: Tree Hor
  Fork :: Tree d ~> Tree Hor ~> Tree Hor
 deriving syntax (tr) List(Ni, Fork) Unit(Unit)

-- make it singleton

data Tree' :: Tree d ~> * where
  In :: Tree' ()tr
  Fork :: treelike head -> Tree' tail -> Tree' [head; tail]tr
  Done :: Tree' []tr
 deriving syntax (ar) List(Done, Fork) Unit(In) -- "arbre"
-- http://www.papoulipoesique.com/wp-content/uploads/2013/06/arbre.jpg

-- define a proposition for subtrees
-- to be checked: TakeHead must ensure that all the node is consumed

prop Subtree :: Tree d ~> Tree e ~> * where
  UnitSub :: Subtree ()tr tr
  BothNil :: Subtree []tr []tr
  TakeHead :: Subtree head head' -> Subtree tail tail' -> Subtree [head; tail]tr [head'; tail']tr

-- TODO: separate the notions of Niche / Frame / Cell
kind Volume = FramedHollow | FilledCell
kind Diagram = Closed Volume | OpenNiche
-- should be a parameter to Stack

-- now we can stack cards (these are zooms in Kock et al. parlance)

data Stack :: Tree d ~> Tree e ~> * where
  Corolla :: Corolla tr => Tree' tr -> Stack tr ()tr

  -- TODO: needed?
  --Expand :: (Subtree consumed tr, {nodeValence consumed} `Equal` {nodeValence tr}) => Stack consumed prod -> Stack tr prod

  -- add another target face to a web
  Target :: ({nodes tr} `Equal` {nodeValence out}) => Stack tr out -> Stack tr out

  -- the following three grab a node (and possibly its offsprings) and incorporate it into a single card
  NodeDone :: Stack []tr [()tr]tr
  Pick :: {- EntireNode => -} Stack head prodhead -> Stack tail prodtail -> Stack [head; tail]tr [prodhead; prodtail]tr
  Exclude :: {- EntireNode => -} Stack tail prod -> Stack [()tr; tail]tr prod
  -- TODO: maybe just specify a subtree (Tree') and that's it

  On :: (Subtree tr' tr, Pointers 1t at out) => Stack tr' out' -> Stack tr out -> Tree' at -> Stack tr {graft out' at out}

  -- building niches (pasting diagrams)
  -- TODO: these must produce a composite labelled Hor
  NicheDone :: Stack ()tr []tr
  Niche :: Stack tr out -> Stack tr [out]tr
  Also :: Pointers 1t at tr
       => Tree' at -> Stack tr' out' -> Stack tr out -> Stack {extgraft tr' at tr} [out'; out]tr

  -- put a frame around a niche
  Frame :: (Corolla out, {nodes tr}  `Equal` {nodeValence out}) => Stack tr out -> Stack tr out

 deriving syntax(z) Record(NicheDone, Also) Item(Target)

stop = ({}z)z

-- this is bogus, just make it compile again...
up :: Reference tr => Stack ()tr tr -> Stack ()tr [tr]tr
up inner = ({()ar=inner}z)z

-- it remains to define corollas

-- TODO: one could say {nodes tr} == 1
prop Corolla :: Tree d ~> * where
  None' :: Corolla []tr
  One' :: Corolla tail -> Corolla [()tr; tail]tr

--  o   --->  |
--  |         |

lolliCell :: Stack []tr ()tr
lolliCell = Corolla Done

--  |         |
--  o   --->  |
--  |         |

dolliCell :: Stack [()tr]tr ()tr
dolliCell = Corolla $ Fork In Done

--              |
--  [o]   --->  o
--   |          |

lolliFrame :: Stack []tr [()tr]tr
lolliFrame = Target NodeDone

--   |          |
--  [o]   --->  o
--   |          |

dolliFrame :: Stack [()tr]tr [()tr]tr
dolliFrame = Target (Exclude NodeDone)

--   |          |
--   |          o
-- [[o]]  --->  |
--   |          o
--   |          |

stacked :: Stack [()tr]tr [[()tr]tr]tr
stacked = (dolliFrame `On` dolliFrame) (In `Fork` Done)

--   |
--  [|]   --->  o
--   |          |

crossed :: Stack ()tr []tr
crossed = stop

-- we can now join things
--
dolliForever = [dolliFrame; dolliForever]cply
nopetope = [crossed, lolliFrame; dolliForever]cply

-- back to unit as fast as possible...
--
stopetope = [crossed, lolliFrame, dolliCell]cply
neverstopetope = [crossed, lolliFrame, dolliCell; neverstopetope]cply
stopetope' = [crossed, lolliFrame; (dolliCell)cplx]cplx
stopetope'' = [crossed, lolliFrame, dolliCell]cplx

--   |          o
-- [[|]]  --->  |
--   |          o
--   |          |

drossed :: Stack ()tr [[]tr]tr
drossed = up stop

-- ################################
-- ############  Niches ###########
-- ################################

--  |          +
--  |          |

-- this is a niche, but assume to be a frame
niche0 :: Stack ()tr []tr
niche0 = {}z

--  |           |
-- [|]          +
--  |           |

-- this is a niche, but assume to be a frame
niche1 :: Stack ()tr [[]tr]tr
niche1 = {()ar=stop; {}z}z

--  |         o   o
-- [|]  --->   \ /
-- [|]          +
--  |           |

-- this is a niche, but assume to be a frame
niche2 :: Stack ()tr [[]tr, []tr]tr
niche2 = {()ar=stop; niche1}z

--                |
--  |         o   o
-- [o]  --->   \ /
-- [|]          +
--  |           |

niche10 :: Stack [()tr]tr [[()tr]tr, []tr]tr
niche10 = {()ar=Exclude NodeDone; Niche stop}z

-- ################################
-- ############  Frames ###########
-- ################################

--   |              |
--  /|\         o   o
-- |[o]|  --->   \ /
-- |[|]|          o
--  \|/           |
--   |

cyclops :: Stack [()tr]tr [[()tr]tr, []tr]tr
cyclops = (niche10)z

-- Stacking, Valence, Affine subtree, Substitute at an affine position

-- counting Units in a tree

valence :: Tree d ~> Nat
{valence []tr} = 0t
{valence [head; tail]tr} = {plus {valence head} {valence tail}}
{valence ()tr} = 1t

-- counting siblings of a tree

nodeValence :: Tree d ~> Nat
{nodeValence []tr} = 0t
{nodeValence [head; tail]tr} = (1+{nodeValence tail})t

-- counting nodes in a tree

nodes :: Tree d ~> Nat
{nodes ()tr} = t
{nodes []tr} = 1t
{nodes [head; tail]tr} = {plus {nodes head} {nodes tail}}

-- nodeValenceAt: given a (multi)pointer, determine the node valences at those positions
--               WHERE     IN
nodeValenceAt :: Tree d ~> Tree e ~> Nat
{nodeValenceAt ()tr tree} = {nodeValence tree}
{nodeValenceAt []tr []tr} = 0t
{nodeValenceAt [head'; tail']tr [head; tail]tr} = {plus {nodeValenceAt head' head} {nodeValenceAt tail' tail}}

-- a pointer is a valence-1 subtree of a tree
-- it is used to mark a unit in a tree where substitution will occur
--
prop Pointers :: Nat ~> Tree d ~> Tree e ~> * where
  Finger :: Pointers 1t ()tr ()tr
  Finished :: Pointers 0t []tr []tr
  ThisWay :: Pointers 1t head' head -> Pointers n tail' tail -> Pointers (1+n)t [head'; tail']tr [head; tail]tr
  ElseWhere :: Pointers n tail' tail -> Pointers n [[]tr; tail']tr [head; tail]tr

-- http://en.wikipedia.org/wiki/Grafting
--
-- graft WHAT      WHERE     IN
graft :: Tree d ~> Tree e ~> Tree f ~> Tree f
{graft what ()tr ()tr} = what
{graft what []tr tr} = tr
{graft what [head'; tail']tr [head; tail]tr} = [{graft what head' head}; {graft what tail' tail}]tr

-- extgraft: extend and graft
--
extgraft :: Tree what ~> Tree wher ~> Tree tree ~> Tree tree'
{extgraft what ()tr ()tr} = what
{extgraft what []tr ()tr} = ()tr
{extgraft what [head; tail]tr ()tr} = {extgrafthor what [head; tail]tr}
{extgraft what []tr []tr} = []tr
{extgraft what []tr [head; tail]tr} = [head; tail]tr
{extgraft what [head'; tail']tr [head; tail]tr} = [{extgraft what head' head}; {extgraft what tail' tail}]tr

-- helper:
extgrafthor :: Tree what ~> Tree Hor ~> Tree Hor
{extgrafthor what []tr} = []tr
{extgrafthor what [head; tail]tr} = [{extgraft what head ()tr}; {extgrafthor what tail}]tr

{- NOTE: we have an Omega bug here:

prompt> :norm {graft []tr ()tr ()tr}
Normalizes to:
  []tr

prompt> :kind []tr
[]tr :: Tree Hor  = []tr

prompt> :kind {graft []tr ()tr ()tr}
{graft []tr ()tr ()tr} :: Tree Ver  = {graft []tr ()tr ()tr}

... BUT: Hor /= Ver :-(
-}

-- substitution: replace a pointed node (of valence n) in a tree with an
-- other tree of (valence n) -- TODO

-- HERE IS A NICE RESEARCH QUESTION:
-- is it possible to "proof search" a Zoom?
-- i.e.
{-

class ZoomLike (int :: Tree) (out :: Tree) where
  zoom :: (Nodes int ~ Valence out) => Zoom int out

-}

prop Reference :: Tree Hor ~> * where
  Stop :: Reference []tr
  Up :: Reference tr -> Reference [tr]tr
 deriving Nat(d) -- de Bruijn index

prop Teleport :: Tree Hor ~> * where
  Gate :: Teleport [()tr]tr
  Tele :: Teleport tr -> Teleport [tr]tr
 deriving Nat(sk) -- skips

{-

0
|   |
1   L  (lambda node)
|   |
2   1  (how many skips)
 \ /
  o
  |
  B  (binds here)
  |

Encodes \x.xx

-}

r2 = 2d
t1 = 1sk

lamX_XX = addBinder $ [r2, t1]ar

prop BindsUp :: Nat ~> Tree Hor ~> * where
  LastSkip :: BindsUp 1t [()tr]tr
  MoreSkip :: Nat' n -> Teleport tr -> BindsUp n tr -> BindsUp (1+n)t [tr]tr
  -- prove Application node
  HeadBindsUp :: Nat' n -> BindsUp (1+n)t [head, nonempty; tail]tr -> BindsUp n [head, nonempty; tail]tr
  TailBindsUp :: Nat' n -> BindsUp n [nonempty; tail]tr -> BindsUp n [head, nonempty; tail]tr

-- TODO: make sure that something teleports here
addBinder :: {-BindsUp 1t tr =>-} Tree' tr -> Tree' [tr]tr
addBinder term = [term]ar

testB :: BindsUp 0t [[[[]tr]tr]tr]tr -> Tree' [[[[]tr]tr]tr,[()tr]tr]tr
testB ev = [r2, 0sk]ar

--TODO: prop Lambda :: Tree Hor ~> * where

{-
The slogan is: binder nodes do not count as result nodes:
 - just consider the next node upstream
 - incoming (external) cards, when identified to a binder do not count as inputs to application
 - incoming (external) cards, when identified to an (iterated) application become pattern matching (sigma).

In the end all identifications have a semantics (hopefully!), when the counting rules are ensured.

-}


QtCore: module updated to use ISOPTARRAY

/*

  Qt5xHb - bibliotecas de ligao entre Harbour/xHarbour e Qt Framework 5

  Copyright (C) 2012-2017 Marcos Antonio Gambeta <marcosgambeta@uol.com.br>

*/

#include "hbclass.ch"
#include "qt5xhb_clsid.ch"

CLASS QStandardPaths

   DATA pointer
   DATA class_id INIT Class_Id_QStandardPaths
   DATA class_flags INIT 0
   DATA self_destruction INIT .F.

   METHOD writableLocation
   METHOD standardLocations
   METHOD locate
   METHOD locateAll
   METHOD displayName
   METHOD findExecutable
   METHOD setTestModeEnabled
   METHOD isTestModeEnabled

   METHOD newFrom
   METHOD newFromObject
   METHOD newFromPointer
   METHOD selfDestruction
   METHOD setSelfDestruction

   DESTRUCTOR destroyObject

END CLASS

PROCEDURE destroyObject () CLASS QStandardPaths
   IF ::self_destruction
      ::delete()
   ENDIF
RETURN

#pragma BEGINDUMP

#include <Qt>

#ifndef __XHARBOUR__
#include <QStandardPaths>
#endif

#include "qt5xhb_common.h"
#include "qt5xhb_clsid.h"
#include "qt5xhb_utils.h"

#ifdef __XHARBOUR__
#include <QStandardPaths>
#endif

/*
static QString writableLocation(StandardLocation type)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_WRITABLELOCATION )
{
  if( ISNUM(1) )
  {
    int par1 = hb_parni(1);
    hb_retc( (const char *) QStandardPaths::writableLocation (  (QStandardPaths::StandardLocation) par1 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QStringList standardLocations(StandardLocation type)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_STANDARDLOCATIONS )
{
  if( ISNUM(1) )
  {
    int par1 = hb_parni(1);
    QStringList strl = QStandardPaths::standardLocations (  (QStandardPaths::StandardLocation) par1 );
    _qt5xhb_convert_qstringlist_to_array ( strl );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QString locate(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_LOCATE )
{
  if( ISNUM(1) && ISCHAR(2) && ISOPTNUM(3) )
  {
    int par1 = hb_parni(1);
    QString par2 = QLatin1String( hb_parc(2) );
    int par3 = ISNIL(3)? (int) QStandardPaths::LocateFile : hb_parni(3);
    hb_retc( (const char *) QStandardPaths::locate (  (QStandardPaths::StandardLocation) par1, par2,  (QStandardPaths::LocateOptions) par3 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_LOCATEALL )
{
  if( ISNUM(1) && ISCHAR(2) && ISOPTNUM(3) )
  {
    int par1 = hb_parni(1);
    QString par2 = QLatin1String( hb_parc(2) );
    int par3 = ISNIL(3)? (int) QStandardPaths::LocateFile : hb_parni(3);
    QStringList strl = QStandardPaths::locateAll (  (QStandardPaths::StandardLocation) par1, par2,  (QStandardPaths::LocateOptions) par3 );
    _qt5xhb_convert_qstringlist_to_array ( strl );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QString displayName(StandardLocation type)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_DISPLAYNAME )
{
  if( ISNUM(1) )
  {
    int par1 = hb_parni(1);
    hb_retc( (const char *) QStandardPaths::displayName (  (QStandardPaths::StandardLocation) par1 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QString findExecutable(const QString &executableName, const QStringList &paths = QStringList())
*/
HB_FUNC_STATIC( QSTANDARDPATHS_FINDEXECUTABLE )
{
  if( ISCHAR(1) && ISOPTARRAY(2) )
  {
    QString par1 = QLatin1String( hb_parc(1) );
    QStringList par2 = _qt5xhb_convert_array_parameter_to_qstringlist(2);
    //PHB_ITEM aStrings2 = hb_param(2, HB_IT_ARRAY);
    //int i2;
    //int nLen2 = hb_arrayLen(aStrings2);
    //for (i2=0;i2<nLen2;i2++)
    //{
    //  QString temp = QLatin1String( hb_arrayGetCPtr(aStrings2, i2+1) );
    //  par2 << temp;
    //}
    hb_retc( (const char *) QStandardPaths::findExecutable ( par1, par2 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static void setTestModeEnabled(bool testMode)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_SETTESTMODEENABLED )
{
  if( ISLOG(1) )
  {
    QStandardPaths::setTestModeEnabled ( (bool) hb_parl(1) );
    hb_itemReturn( hb_stackSelfItem() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static bool isTestModeEnabled()
*/
HB_FUNC_STATIC( QSTANDARDPATHS_ISTESTMODEENABLED )
{
  hb_retl( QStandardPaths::isTestModeEnabled (  ) );
}

HB_FUNC_STATIC( QSTANDARDPATHS_NEWFROM )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISOBJECT(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_objSendMsg( hb_param(1, HB_IT_OBJECT ), "POINTER", 0 ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else if( hb_pcount() == 1 && ISPOINTER(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_param(1, HB_IT_POINTER ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }

  hb_itemReturn( self );
}

HB_FUNC_STATIC( QSTANDARDPATHS_NEWFROMOBJECT )
{
  HB_FUNC_EXEC( QSTANDARDPATHS_NEWFROM );
}

HB_FUNC_STATIC( QSTANDARDPATHS_NEWFROMPOINTER )
{
  HB_FUNC_EXEC( QSTANDARDPATHS_NEWFROM );
}

HB_FUNC_STATIC( QSTANDARDPATHS_SELFDESTRUCTION )
{
  hb_retl( (bool) hb_itemGetL( hb_objSendMsg( hb_stackSelfItem(), "SELF_DESTRUCTION", 0 ) ) );
}

HB_FUNC_STATIC( QSTANDARDPATHS_SETSELFDESTRUCTION )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISLOG(1) )
  {
    PHB_ITEM des = hb_itemPutL( NULL, hb_parl(1) );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }

  hb_itemReturn( self );
}

#pragma ENDDUMP

/*

  Qt5xHb - bibliotecas de ligao entre Harbour/xHarbour e Qt Framework 5

  Copyright (C) 2012-2017 Marcos Antonio Gambeta <marcosgambeta@uol.com.br>

*/

#include "hbclass.ch"
#include "qt5xhb_clsid.ch"

CLASS QStandardPaths

   DATA pointer
   DATA class_id INIT Class_Id_QStandardPaths
   DATA class_flags INIT 0
   DATA self_destruction INIT .F.

   METHOD writableLocation
   METHOD standardLocations
   METHOD locate
   METHOD locateAll
   METHOD displayName
   METHOD findExecutable
   METHOD setTestModeEnabled
   METHOD isTestModeEnabled

   METHOD newFrom
   METHOD newFromObject
   METHOD newFromPointer
   METHOD selfDestruction
   METHOD setSelfDestruction

   DESTRUCTOR destroyObject

END CLASS

PROCEDURE destroyObject () CLASS QStandardPaths
   IF ::self_destruction
      ::delete()
   ENDIF
RETURN

#pragma BEGINDUMP

#include <Qt>

#ifndef __XHARBOUR__
#include <QStandardPaths>
#endif

#include "qt5xhb_common.h"
#include "qt5xhb_clsid.h"
#include "qt5xhb_utils.h"

#ifdef __XHARBOUR__
#include <QStandardPaths>
#endif

/*
static QString writableLocation(StandardLocation type)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_WRITABLELOCATION )
{
  if( ISNUM(1) )
  {
    int par1 = hb_parni(1);
    hb_retc( (const char *) QStandardPaths::writableLocation (  (QStandardPaths::StandardLocation) par1 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QStringList standardLocations(StandardLocation type)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_STANDARDLOCATIONS )
{
  if( ISNUM(1) )
  {
    int par1 = hb_parni(1);
    QStringList strl = QStandardPaths::standardLocations (  (QStandardPaths::StandardLocation) par1 );
    _qt5xhb_convert_qstringlist_to_array ( strl );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QString locate(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_LOCATE )
{
  if( ISNUM(1) && ISCHAR(2) && ISOPTNUM(3) )
  {
    int par1 = hb_parni(1);
    QString par2 = QLatin1String( hb_parc(2) );
    int par3 = ISNIL(3)? (int) QStandardPaths::LocateFile : hb_parni(3);
    hb_retc( (const char *) QStandardPaths::locate (  (QStandardPaths::StandardLocation) par1, par2,  (QStandardPaths::LocateOptions) par3 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_LOCATEALL )
{
  if( ISNUM(1) && ISCHAR(2) && ISOPTNUM(3) )
  {
    int par1 = hb_parni(1);
    QString par2 = QLatin1String( hb_parc(2) );
    int par3 = ISNIL(3)? (int) QStandardPaths::LocateFile : hb_parni(3);
    QStringList strl = QStandardPaths::locateAll (  (QStandardPaths::StandardLocation) par1, par2,  (QStandardPaths::LocateOptions) par3 );
    _qt5xhb_convert_qstringlist_to_array ( strl );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QString displayName(StandardLocation type)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_DISPLAYNAME )
{
  if( ISNUM(1) )
  {
    int par1 = hb_parni(1);
    hb_retc( (const char *) QStandardPaths::displayName (  (QStandardPaths::StandardLocation) par1 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static QString findExecutable(const QString &executableName, const QStringList &paths = QStringList())
*/
HB_FUNC_STATIC( QSTANDARDPATHS_FINDEXECUTABLE )
{
  if( ISCHAR(1) && (ISARRAY(2)||ISNIL(2)) )
  {
    QString par1 = QLatin1String( hb_parc(1) );
    QStringList par2 = _qt5xhb_convert_array_parameter_to_qstringlist(2);
    //PHB_ITEM aStrings2 = hb_param(2, HB_IT_ARRAY);
    //int i2;
    //int nLen2 = hb_arrayLen(aStrings2);
    //for (i2=0;i2<nLen2;i2++)
    //{
    //  QString temp = QLatin1String( hb_arrayGetCPtr(aStrings2, i2+1) );
    //  par2 << temp;
    //}
    hb_retc( (const char *) QStandardPaths::findExecutable ( par1, par2 ).toLatin1().data() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static void setTestModeEnabled(bool testMode)
*/
HB_FUNC_STATIC( QSTANDARDPATHS_SETTESTMODEENABLED )
{
  if( ISLOG(1) )
  {
    QStandardPaths::setTestModeEnabled ( (bool) hb_parl(1) );
    hb_itemReturn( hb_stackSelfItem() );
  }
  else
  {
    hb_errRT_BASE( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
  }
}

/*
static bool isTestModeEnabled()
*/
HB_FUNC_STATIC( QSTANDARDPATHS_ISTESTMODEENABLED )
{
  hb_retl( QStandardPaths::isTestModeEnabled (  ) );
}

HB_FUNC_STATIC( QSTANDARDPATHS_NEWFROM )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISOBJECT(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_objSendMsg( hb_param(1, HB_IT_OBJECT ), "POINTER", 0 ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }
  else if( hb_pcount() == 1 && ISPOINTER(1) )
  {
    PHB_ITEM ptr = hb_itemPutPtr( NULL, (void *) hb_itemGetPtr( hb_param(1, HB_IT_POINTER ) ) );
    hb_objSendMsg( self, "_pointer", 1, ptr );
    hb_itemRelease( ptr );
    PHB_ITEM des = hb_itemPutL( NULL, false );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }

  hb_itemReturn( self );
}

HB_FUNC_STATIC( QSTANDARDPATHS_NEWFROMOBJECT )
{
  HB_FUNC_EXEC( QSTANDARDPATHS_NEWFROM );
}

HB_FUNC_STATIC( QSTANDARDPATHS_NEWFROMPOINTER )
{
  HB_FUNC_EXEC( QSTANDARDPATHS_NEWFROM );
}

HB_FUNC_STATIC( QSTANDARDPATHS_SELFDESTRUCTION )
{
  hb_retl( (bool) hb_itemGetL( hb_objSendMsg( hb_stackSelfItem(), "SELF_DESTRUCTION", 0 ) ) );
}

HB_FUNC_STATIC( QSTANDARDPATHS_SETSELFDESTRUCTION )
{
  PHB_ITEM self = hb_stackSelfItem();

  if( hb_pcount() == 1 && ISLOG(1) )
  {
    PHB_ITEM des = hb_itemPutL( NULL, hb_parl(1) );
    hb_objSendMsg( self, "_self_destruction", 1, des );
    hb_itemRelease( des );
  }

  hb_itemReturn( self );
}

#pragma ENDDUMP