# int64.java

    /*
    ** Module   : int64.java
    ** Abstract : Progress 4GL compatible 64-bit integer object
    **
    ** Copyright (c) 2012-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 OM  20121217 Created initial version.
    ** 002 CA  20130123 Added c'tor which build a new instance from a BaseDataType
    **                  value, if possible. This should be used only when
    **                  constructing an instance using the value returned by a
    **                  DYNAMIC-FUNCTION call; 4GL does some special casting in
    **                  this case, which is not implemented yet.
    ** 003 CS  20130128 Added conversion support for memptr related functions for task #1635.
    ** 004 GES 20130128 Removed getTypeName() which is already implemented in a parent class.
    ** 005 GES 20130322 Fixed the BDT c'tor and modified the assign(BDT,boolean) to be the
    **                  common location for the _POLY morphing. This is safe because all current
    **                  usage would have generated a ClassCastException if that method was ever used
    **                  with an invalid type. Now, instead of the CCE, the code will morph the value
    **                  as is possible. The morphing code still needs to be finished.
    ** 006 OM  20130328 Added missing runtime implementation. Made this class the base class for
    **                  integer class and sharing common code via inheritance.
    ** 007 OM  20130527 Fixed toString() method for particular case of Long.MIN_VALUE.
    **                  Added error message when datetime/tz was passed to int64 constructor.
    **                  Added patch for converting to timezone '+HH:MM' special format.
    ** 008 OM  20130711 Fixed setValue(String) when in silent mode.
    ** 009 CA  20131001 Eliminated custom group/dec separators when format is used. In this case, they
    **                  must default to comma (,) for group and dot (.) for decimal separator.
    ** 010 GES 20131114 Minor optimization of assignment code.
    ** 011 HC  20140213 Added precise conversion to BigDecimal.
    ** 012 ECF 20150715 Replace StringBuffer with StringBuilder.
    ** 013 CA  20151222 Added instantiateDefaultExtent.
    ** 014 EVL 20160224 Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
    ** 015 GES 20160617 Added unknown literal processing for the polymorphic assign case.
    ** 016 ECF 20160614 Implemented faster copy constructors.
    ** 017 CA  20160627 Reworked undoable support - the undoables register themselves with all blocks
    **                  up the stack, until either 1. the tx block which created it is reached or
    **                  2. the last tx block where it was saved is reached.
    ** 018 GES 20171206 Removed explicit bypass on pending error (new silent error mode).
    ** 019 OM  20180219 Fixed NPE in setValue() for null parameter (SQL access).
    ** 020 CA  20181029 Assignments incoming from CALL's dynamic invoke are not bound-check.
    ** 021 OM  20190206 Added implementation of field size needed by record-length.
    ** 022 CA  20190927 Added support for direct Java access from 4GL code.
    ** 023 GES 20200520 Added 4GL enum constructor.
    **     GES 20200603 Changed the checkBounds() signature to make it more reusable.
    **     ME  20200528 Add assign for "legacy" object - resource id.
    ** 024 IAS 20201007 Added Type enum.
    **     OM  20201120 Avoid construction of supplementary, unneeded wrapping objects.
    **     CA  20210413 An integer value can be built from a raw's bytes.
    ** 025 ME  20210504 Added isIncompatibleTypesOnConversion override for invalid POLY conversion.
    **     CA  20210816 Fixed toString() for "+HH:MM" format - check must be case-insensitive.
    **     CA  20220203 Only assignment from decimal values can be out-of-bounds.
    **     AL2 20220319 Added value proxy check in assign.
    ** 026 CA  20230215 'duplicate()' method returns the real type instead of BDT.
    ** 027 ICP 20250123 Added int64 constant, static and dynamic cache support - these are immutable instances
    **                  which must not 'escape' from FWD runtime, and only used via assignment or as read-only.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import java.io.*;
    import java.math.BigDecimal;
    import java.util.*;
    import java.util.function.*;

    import com.goldencode.p2j.oo.lang.*;
    import com.goldencode.p2j.persist.*;
    import com.goldencode.p2j.security.ContextLocal;
    import com.goldencode.p2j.util.logging.*;

    /**
     * A class that represents a Progress 4GL compatible 64-bit integer object whose data is mutable.
     * This class provides the basic wrapping of the 64-bit integer primitive data, instantiation,
     * type conversion, <code>unknown</code> value representation and the core comparison logic.
     * <p>
     * All Progress language features related to integer/decimal data types are implemented in the
     * {@link MathOps} class (except comparison logic which is in {@link CompareOps}. All Progress
     * integer/decimal features are supported including all functionality that can be accessed
     * via operators and built-in Progress functions.
     *
     * It acts as storage for all internal intermediary results of arithmetic operations.
     * The implementation uses 64-bit signed integer storage.
     * As this is the base class for <code>integer</code> it shares the common code and data.
     *
     * @author    OM
     */
    public class int64
    extends NumberType
    {
       /** Logger */
       private static final CentralLogger log = CentralLogger.get(int64.class);

       /** Number of seconds in a minute. */
       protected static final int SECS_IN_A_MIN = 60;

       /** Number of seconds in an hour. */
       protected static final int SECS_IN_AN_HOUR = 60 * SECS_IN_A_MIN;

       /** Number of seconds in a day (leap minutes aren't considered). */
       protected static final int SECS_IN_A_DAY = 24 * SECS_IN_AN_HOUR;

       /** Smallest big decimal value that can be assigned. */
       private static final BigDecimal LOWER_BOUND = new BigDecimal(Long.MIN_VALUE);

       /** Largest big decimal value that can be assigned. */
       private static final BigDecimal UPPER_BOUND = new BigDecimal(Long.MAX_VALUE);

       /** Default maximum number of cached int64Constant instances in the static cache*/
       private static final int DEFAULT_STATIC_CACHE_SIZE = 1024;

       /** Default maximum number of cached int64Constant instances in the dynamic cache */
       private static final int DEFAULT_DYNAMIC_CACHE_SIZE = 512;

       /** Singleton and immutable for the int64 UNKNOWN value. */
       public static final int64Constant UNKNOWN = new int64Constant();;

       /** Stores context-local state variables. */
       private static ContextContainer work = new ContextContainer();

       /** Static cache for the singleton and immutable int64 values. */
       private static int64Constant[] STATIC_CACHE;

       /** Dynamic cache for the singleton and immutable int64 values. */
       private static int64Constant[] DYNAMIC_CACHE;

       /**
        * Stores the value of this instance. The contents of this member are invalid if the
        * <code>unknown</code> flag is <code>true</code>.
        */
       protected long value = 0;

       /**
        * Stores the state of whether or not this instance represents the <code>unknown value</code>,
        * independent of the state of this integer's value.
        */
       protected boolean unknown = true;

       /**
        * Default constructor which creates an instance representing the <code>unknown value</code>.
        */
       public int64()
       {
       }

       /**
        * This is a special c'tor which should be used only when converting the
        * value returned by a function or method with polymorphic return type into the
        * expected type (i.e. DYNAMIC-FUNCTION()).  In such cases, the 4GL does some
        * automatic type conversion (see {@link #assign(BaseDataType)}).
        *
        * @param    value
        *           The value to be used for this instance.
        */
       public int64(BaseDataType value)
       {
          initialize(value);
       }

       /**
        * Constructs an instance after casting the parameter into the internal
        * representation of this class.  No loss of precision will occur.
        * <p>
        * It is not possible to yield an <code>unknown value</code> from this
        * constructor without calling the method {@link #setUnknown} separate.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(long value)
       {
          this.unknown = false;
          this.value = value;
       }

       /**
        * Constructs an instance after casting the parameter into the internal
        * representation of this class.  Loss of precision may occur in this
        * conversion (all digits to the right of the decimal point will be
        * truncated).
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(double value)
       {
          setValue(new decimal(value));
       }

       /**
        * Constructs an instance after copying the parameter's data into the
        * internal representation of this class.
        * <p>
        * When the parameter is <code>true</code> the resulting integer value
        * will be 1. Otherwise the value is 0.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(boolean value)
       {
          setValue(value);
       }

       /**
        * Constructs an instance after copying the parameter's data into the
        * internal representation of this class.  No loss of precision will
        * occur.
        * <p>
        * If the parameter's represents the <code>unknown value</code>, this
        * instance will also represent the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(int64 value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             if (value.val() instanceof int64)
             {
                value = (int64) value.val();
             }
             else
             {
                initialize(value.val());
                return;
             }
          }
          this.unknown = value.isUnknown();
          if (!isUnknown())
          {
             this.value = value.value;
          }
       }

       /**
        * Constructs an instance after casting the parameter into the internal representation of this
        * class. No loss of precision will occur.
        * <p>
        * If the parameter's represents the <code>unknown value</code>, this instance will also
        * represent the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(integer value)
       {
          this((int64) value);
       }

       /**
        * Constructs an instance after casting the parameter into the internal representation of this
        * class. Loss of precision may occur in this conversion (all digits to the right of the
        * decimal point will be truncated).
        * <p>
        * If the parameter's represents the <code>unknown value</code>, this instance will also
        * represent the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(decimal value)
       {
          assign(value);
       }

       /**
        * Constructs an instance wth the given enum's value.
        * <p>
        * If the parameter is {@code unknown value} the resulting instance is the {@code unknown value}.
        *
        * @param    enm
        *           The enum whose value to be used for this instance.
        */
        public int64(object<? extends LegacyEnum> enm)
       {
          if (enm.isUnknown())
          {
             setUnknown();
          }
          else
          {
             setValue(enm.ref()._getValue());
          }
       }

       /**
        * Constructs an instance after copying the parameter's data into the
        * internal representation of this class.
        * <p>
        * If the parameter's represents the <code>unknown value</code>, this
        * instance will also represent the <code>unknown value</code>.
        * <p>
        * When the parameter is <code>true</code> the resulting integer value
        * will be 1. Otherwise the value is 0.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(logical value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             if (value.val() instanceof logical)
             {
                value = (logical) value.val();
             }
             else
             {
                initialize(value.val());
             }
          }
          setValue(value);
       }

       /**
        * Constructs an instance after converting the string representation
        * of a number into an integer.  Due to rounding, a loss of precision
        * may occur in this conversion (all digits to the right of the decimal
        * point will be truncated).
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(String value)
       {
          setValue(value);
       }

       /**
        * Constructs an instance after converting the string representation
        * of a number into an integer.  Due to rounding, a loss of precision
        * may occur in this conversion (all digits to the right of the decimal
        * point will be truncated).
        * <p>
        * If the parameter is <code>unknown value</code> the resulting integer is
        * the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(character value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             if (value.val() instanceof character)
             {
                value = (character) value.val();
             }
             else
             {
                initialize(value.val());
                return;
             }
          }
          if (value.isUnknown())
          {
             setUnknown();
          }
          else
          {
             setValue(value.getValue());
          }
       }

       /**
        * Constructs an instance after copying the parameter's data into the
        * internal representation of this class.
        * <p>
        * If the parameter's represents the <code>unknown value</code>, this
        * instance will also represent the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(date value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             if (value.val() instanceof date)
             {
                value = (date) value.val();
             }
             else
             {
                initialize(value.val());
                return;
             }
          }
          if (value.isUnknown())
          {
             setUnknown();
          }
          else if (value instanceof datetime)
          {
             ErrorManager.recordOrShowError(
                   5729, "Incompatible datatypes found during runtime conversion", false);
          }
          else
          {
             setValue(value.longValue());
          }
       }

       /**
        * Constructs an instance after copying the parameter's data into the
        * internal representation of this class.
        * <p>
        * If the parameter is <code>null</code>, this instance will represent
        * the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(Number value)
       {
          if (value == null)
          {
             setUnknown();
          }
          else
          {
             setValue(value.longValue());
          }
       }

       /**
        * Constructs an instance after copying the parameter's data into the
        * internal representation of this class.
        * <p>
        * If the parameter is <code>null</code>, this instance will represent
        * the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(Boolean value)
       {
          if (value == null)
          {
             setUnknown();
          }
          else
          {
             setValue(value.booleanValue());
          }
       }

       /**
        * Constructs an instance after copying the parameter's data into the
        * internal representation of this class.
        * <p>
        * If the parameter is <code>null</code>, this instance will represent
        * the <code>unknown value</code>.
        *
        * @param   value
        *          The value to be used for this instance.
        */
       public int64(Date value)
       {
          this(new date(value));
       }

       /**
        * Initializes the cache using CacheManager. The default value of the cache
        * is used when there is no size available from the configuration.
        */
       public static void initializeCache()
       {
          STATIC_CACHE = CacheManager.createArrayCache(int64.class,
                                                "static-cache",
                                                DEFAULT_STATIC_CACHE_SIZE,
                                                int64Constant.class);
          for (int i = 0; i < STATIC_CACHE.length; i++)
          {
             STATIC_CACHE[i] = new int64Constant((long) i);
          }
          DYNAMIC_CACHE = CacheManager.createArrayCache(int64.class,
                                                       "dynamic-cache",
                                                       DEFAULT_DYNAMIC_CACHE_SIZE,
                                                       int64Constant.class);
       }

       /**
        * Returns an int64Constant instance. Uses cached instances for the first n values, where n is the
        * cache size that can be configured in directory.xml.
        *
        * @param   value
        *          The value of the int64.
        * @return  An int64Constant instance.
        */
       public static int64 of(Long value)
       {
          if (value == null)
          {
             return UNKNOWN;
          }

          if (STATIC_CACHE == null)
          {
             return new int64Constant(value);
          }
          // Check static cache
          if (value >= 0 && value < STATIC_CACHE.length)
          {
             return STATIC_CACHE[Math.toIntExact(value)];
          }

          if (DYNAMIC_CACHE == null)
          {
             return new int64Constant(value);
          }
          // Check dynamic cache
          int index = (int)Math.abs(value % DYNAMIC_CACHE.length);
          synchronized (DYNAMIC_CACHE)
          {
             int64Constant cachedInstance = DYNAMIC_CACHE[index];

             if (cachedInstance != null && cachedInstance.intValue() == value)
             {
                return cachedInstance;
             }

             // Cache miss - create and replace the entry in dynamic cache
             int64Constant newInstance = new int64Constant(value);
             DYNAMIC_CACHE[index] = newInstance;
             return newInstance;
          }
       }

       /**
        * Get the type
        * @return type
        */
       public Type getType()
       {
          return Type.INT64;
       }

       /**
        * Returns the largest instance from the list. This list may not include
        * <code>null</code> values.
        *
        * @param   list
        *          The list of decimals and/or integers to check.
        *
        * @return  The largest instance or <code>unknown value</code> if any entries in the list are
        *          unknown or <code>null</code> if an empty list is passed.
        */
       public static int64 maximum(NumberType ... list)
       {
          NumberType num = (NumberType) CompareOps.maximum(list);
          if (num instanceof int64)
          {
             // this includes the particular num.isUnknown() case
             return (int64) num.duplicate();
          }
          return num.isUnknown() ? new int64() : new int64(num.longValue());
       }

       /**
        * Returns the smallest instance from the list. This list may not include
        * <code>null</code> values.
        *
        * @param   list
        *          The list of decimals and/or integers to check.
        *
        * @return  The smallest instance or <code>unknown value</code> if any entries in the list are
        *          unknown or <code>null</code> if an empty list is passed.
        */
       public static int64 minimum(NumberType ... list)
       {
          NumberType num = (NumberType) CompareOps.minimum(list);
          if (num instanceof int64)
          {
             // this includes the particular num.isUnknown() case
             return (int64) num.duplicate();
          }
          return num.isUnknown() ? new int64() : new int64(num.intValue());
       }

       /**
        * Dynamically builds the default format string for this given number type
        * which honors the given group and decimal separators.
        *
        * @return   The default format string for this type of number.
        */
       @Override
       public String buildDefaultFormat()
       {
          StringBuilder sb = new StringBuilder("->");
          sb.append(FORMAT_GROUP_SEP).append(">>>").append(FORMAT_GROUP_SEP).append(">>9");

          return sb.toString();
       }

       /**
        * Returns the default format string for this given number type (which
        * honors the given group and decimal separators), building that format
        * and caching it if this is the first call in this context.
        *
        * @return   The default format string for this type of number.
        */
       @Override
       public String obtainDefaultFormat()
       {
          WorkArea wa = work.obtain();

          if (wa.defFmt == null)
          {
             wa.defFmt = buildDefaultFormat();
          }

          return wa.defFmt;
       }

       /**
        * Returns the export format string for this given number type which
        * honors the given group and decimal separators.
        *
        * @return   Always <code>null</code>.
        */
       @Override
       public String buildExportFormat()
       {
          return null;
       }

       /**
        * An equality test which handles the most common cases with the least
        * amount of overhead.  The superclass implementation is invoked otherwise.
        * <p>
        * This implementation is consistent with {@link #hashCode()}.
        *
        * @see com.goldencode.p2j.util.BaseDataType#equals(java.lang.Object)
        */
       public boolean equals(Object o)
       {
          if (this == o)
          {
             return true;
          }

          if (o instanceof int64)
          {
             int64 that = (int64) o;

             boolean u1 = this.unknown;
             boolean u2 = that.unknown;

             if (u1 && u2)
             {
                return true;
             }

             if (u1 || u2)
             {
                return false;
             }

             return (this.value == that.value);
          }

          return super.equals(o);
       }

       /**
        * Hash code implementation which is consistent with {@link BaseDataType#equals}.
        *
        * @return  Hash code value for this object instance.
        */
       public int hashCode()
       {
          if (unknown)
          {
             return 19;
          }

          return (int)(41 + value);
       }

       /**
        * Returns the value of this instance as a <code>long</code>.  No loss of
        * precision will occur in this conversion.
        * <p>
        * This method will fail if the instance represents the
        * <code>unknown value</code>.
        *
        * @return   The value cast into a <code>long</code>.
        *
        * @throws   NullPointerException
        *           If this instance represents the <code>unknown value</code>.
        */
       @Override
       public long longValue()
       throws NullPointerException
       {
          if (unknown)
          {
             throw new NullPointerException("Unknown value cannot be converted to long.");
          }

          return value;
       }

       /**
        * Returns the value of this instance as an <code>int</code>. Loss of
        * precision may occur in this conversion if number exceeds int limits
        * <p>
        * This method will fail if the instance represents the <code>unknown value</code>.
        *
        * @return   The value as an <code>int</code>.
        *
        * @throws   NullPointerException
        *           If this instance represents the <code>unknown value</code>.
        */
       @Override
       public int intValue()
       throws NullPointerException
       {
          if (unknown)
          {
             throw new NullPointerException("Unknown value cannot be converted to int.");
          }

          return (int) value;
       }

       /**
        * Returns the value of this instance as a <code>double</code>. Some loss
        * of precision may occur in this conversion. Use {@linkplain #toBigDecimal()}
        * if keeping the precision is the concern.
        * <p>
        * If this instance represents the <code>unknown value</code>, the
        * returned <code>double</code> will be set to <code>NaN</code>.
        *
        * @return   The value cast into a <code>double</code>.
        */
       @Override
       public double doubleValue()
       {
          if (unknown)
          {
             return (new decimal()).doubleValue();
          }

          return (double) value;
       }

       /**
        * Returns the value of this instance as a <code>BigDecimal</code>. No loss
        * of precision will occur in this conversion.
        * <p>
        * If this instance represents the <code>unknown value</code>, the
        * return value will be <code>null</code>.
        *
        * @return   The <code>BigDecimal</code> instance, see above for details.
        */
       public BigDecimal toBigDecimal()
       {
          return isUnknown() ? null : new BigDecimal(value);
       }

       /**
        * Increments the value of this instance by 1.
        * <p>
        * If this instance represents the <code>unknown value</code>, nothing is done.
        */
       @Override
       public void increment()
       {
          if (unknown)
          {
             return;
          }

          setValue(value + 1);
       }

       /**
        * Decrements the value of this instance by 1.
        * <p>
        * If this instance represents the <code>unknown value</code>, nothing is done.
        */
       @Override
       public void decrement()
       {
          if (unknown)
          {
             return;
          }

          setValue(value - 1);
       }

       /**
        * Determines if this instance represents the <code>unknown value</code>.
        *
        * @return   <code>true</code> if this instance is set to the <code>unknown value</code>.
        */
       @Override
       public boolean isUnknown()
       {
          return unknown;
       }

       /**
        * Sets the state of this instance's <code>unknown value</code> state to <code>true</code>.
        */
       @Override
       public void setUnknown()
       {
          if (!unknown)
          {
             checkUndoable(true);
          }

          unknown = true;
       }

       /**
        * Creates a new instance of the same type that represents the <code>unknown value</code>.
        *
        * @return  An instance that represents the <code>unknown value</code>.
        */
       @Override
       public BaseDataType instantiateUnknown()
       {
          return new int64();
       }

       /**
        * Creates a new instance of the same type that represents the  default initialized value.
        *
        * @return  An instance that represents the default value.
        */
       @Override
       public BaseDataType instantiateDefault()
       {
          return new int64(0);
       }

       /**
        * Does the same as standard <code>clone()</code> method but returns an instance of
        * <code>BaseDataType</code> and doesn't throw the <code>CloneNotSupportedException</code>.
        *
        * @return  A clone of this instance.
        */
       @Override
       public int64 duplicate()
       {
          return new int64(this);
       }

       /**
        * Returns the value of this instance as an <code>long</code>.  No loss
        * of precision will occur in this conversion.  This is the same as the {@link #longValue}.
        * <p>
        * If this instance represents the <code>unknown value</code>, the
        * returned <code>long</code> is <b>not valid</b>!
        *
        * @return   The value as an <code>long</code>.
        */
       public long getValue()
       {
          return isUnknown() ? 0L : longValue();
       }

       @Override
       protected boolean isIncompatibleTypesOnConversion(BaseDataType value)
       {
          // invalid conversion is being thrown even if unknown
          if (value instanceof longchar || value instanceof clob || value instanceof blob)
          {
             incompatibleTypesOnConversion();
          }

          return super.isIncompatibleTypesOnConversion(value);
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * If the value is not of type <code>NumberType</code>, the following automatic type
        * conversion will occur:
        * <p>
        * <ul>
        * <li><code>unknown</code> values (or literal) will set this object to <code>unknown</code>
        * <li><code>NumberType</code> values will be assigned using usual conversion
        * <li><code>handle</code> will use the <code>hashCode()</code> method to obtain an integer.
        *          <b>The reverse operation is not valid</b> and this should only be used for viewing
        *          handle values.
        * <li><code>date</code> will convert to the number of dates passed from 4GL day 0.
        * <li><code>logical</code>. <code>true</code> values will convert to 1 while
        *          <code>false</code> are converted to 0.
        * <li><code>character</code> and <code>longchar</code>. The conversion implies parsing the
        *          string assuming the number is written in base 10.
        * <li>any other data type will raise exception
        * </ul>
        * <p>
        * This variant is meant to handle the cases of built-in functions and methods in the 4GL
        * which have polymorphic return types (e.g. DYNAMIC-FUNCTION()). This should NOT be used for
        * non-polymorphic assignments.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(BaseDataType value)
       {
          if (value == null)
          {
             setUnknown();
             return;
          }

          // the value may be a proxy; make sure to unwrap and work with the real value
          value = value.val();
          if (value.isUnknown())
          {
             setUnknown();
             return;
          }

          if (value instanceof NumberType)
          {
             // first check all numerical cases (integer, int64, decimal)
             setValue((NumberType) value);
          }
          else if (value instanceof handle)
          {
             /* a little workaround here in order to benefit from fact that when computing
                handle.toString() it will return a string representation that is automatically
                save off the reference in a weak hash map so that later, it can be converted back
                via the widgetHandle() method
              */
             setValue(value.toString());
          }
          else if (value instanceof memptr)
          {
             // TODO this type of data is strange enough in 4GL:
             // * dynamically calling int64 with memptr will fail at runtime if no-error isn't specified
             // * statically calling with memptr parameter gives some strange results despite the
             //       fact that documentation explicitly mention that RAW / MEMPTR and LONGCHAR
             //       are not supported for conversion
          }
          else if (value instanceof logical)
          {
             // boolean case ?
             setValue(((logical) value).booleanValue());
          }
          else if (value instanceof date && !(value instanceof datetime))
          {
             // date, but not datetime/datetime-tz ?
             setValue(((date) value).longValue());
          }
          else if (value instanceof character || value instanceof longchar)
          {
             // try to convert to an integer using long parsing
             String javaVal = (value instanceof character) ?
                              ((character) value).toStringMessage() : ((longchar) value).getValue();
             setValue(javaVal);
             // despite the fact that documentation explicitly mention that RAW / MEMPTR and LONGCHAR
             // are not supported for conversion, the implementation handles LONGCHAR as normal
             // CHARACTER, giving no warnings whatsoever.
          }
          else if (value instanceof object)
          {
             assign((object<?>) value);
          }
          else if (value instanceof jobject)
          {
             assign((jobject<?>) value);
          }
          else if (value instanceof BinaryData)
          {
             byte[] bytes = ((BinaryData) value).getByteArray();
             long val = 0;
             // only the first 8 bytes are used
             for (int i = 0; i < Math.min(8, bytes.length); i++)
             {
                val += ((long) bytes[i] & 0xffL) << (8 * i);
             }
             assign(val);
          }
          else
          {
             ErrorManager.recordOrThrowError(5729,
                   "Incompatible datatypes found during runtime conversion");
          }
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the
        * state of the passed instance.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(NumberType value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             assign(value.val());
             return;
          }
          setValue(value);
       }

       /**
        * Sets the state (data) of this instance based on the given data.
        *
        * @param    value
        *           The data from which to copy state.
        */
       public void assign(long value)
       {
          assign(new int64(value));
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the
        * state of the passed data.
        *
        * @param    value
        *           The data from which to copy state. <code>NaN</code> will
        *           force this instance to unknown value.
        */
       public void assign(double value)
       {
          assign(new decimal(value));
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the
        * state of the passed instance.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(Undoable value)
       {
          if (value instanceof BaseDataType)
          {
             assign((BaseDataType)value);
          }
          else
          {
             throw new RuntimeException("Assignment of " + value.getClass() +
                   " to int64 not implemented.");
          }
       }

       /**
        * Compares this instance with the specified instance and returns -1
        * if this instance is less than the specified, 0 if the two instances
        * are equal and 1 if this instance is greater than the specified
        * instance.  This is the implementation of the <code>Comparable</code>
        * interface.
        * <p>
        * The algorithm will fail to give meaningful results in the case where
        * one tries to sort against other objects that do not represent compatible
        * values.
        *
        * @param    obj
        *           The instance to compare against.
        *
        * @return   &le;-1, 0, or &ge;1 depending on if this instance is less
        *           than, equal to or greater (respectively) than the specified
        *           instance <code>obj</code>.
        */
       @Override
       public int compareTo(Object obj)
       {
          //check for unknown first:
          if (obj instanceof BaseDataType)
          {
             BaseDataType bdt = (BaseDataType)obj;
             if (this.isUnknown() && bdt.isUnknown())
             {
                // both are unknowns
                return 0;
             }
             if (this.isUnknown() || bdt.isUnknown())
             {
                // only one is unknown
                return -1;
             }
          }

          // check for integer type arguments:
          if (obj instanceof int64)
          {
             long otherVal = ((int64) obj).value;
             if (otherVal > value)
             {
                return -1;
             }
             return value == otherVal ? 0 : 1;
          }

          // check remaining cases:
          double d;
          if (obj instanceof decimal)
          {
             d = ((decimal) obj).doubleValue();
          }
          else if (obj instanceof Long)
          {
             d = ((Long) obj).doubleValue();
          }
          else if (obj instanceof Integer)
          {
             d = ((Integer) obj).doubleValue();
          }
          else if (obj instanceof Double)
          {
             d = ((Double) obj).doubleValue();
          }
          else
          {
             // different from anything else
             return -1;
          }

          int result = -1;
          // no epsilon tests are needed since a decimal class should always have a rounded value
          // (whose precision is far less than the Java epsilon value)!
          if (value > d)
          {
             result = 1;
          }
          else if (value == d)
          {
             result = 0;
          }

          return result;
       }

       /**
        * Returns the larger of the current or passed-in instance.
        *
        * @param    i
        *           The instance to compare against.
        *
        * @return   The largest instance.
        */
       public int64 maximum(int64 i)
       {
          return (int64) super.maximum(i);
       }

       /**
        * Returns the smaller of the current or passed-in instance.
        *
        * @param    i
        *           The instance to compare against.
        *
        * @return   The smallest instance.
        */
       public int64 minimum(int64 i)
       {
          return (int64) super.minimum(i);
       }

       /**
        * Creates a string representation of the instance data using the default
        * Progress 4GL format of '-&gt;,&gt;&gt;&gt;,&gt;&gt;9'.
        *
        * @return   The formatted string.
        */
       public String toString()
       {
          return toString(null);
       }

       /**
        * Creates a string representation of the instance data in a form that is
        * compatible with the <code>MESSAGE</code> language statement.  If the
        * instance represents the <code>unknown value</code>, a '?' will be returned.
        *
        * @return   The 'message' formatted string.
        */
       public String toStringMessage()
       {
          return toStringExport();
       }

       /**
        * Creates a string representation of the instance data using the 'export' format.
        *
        * @return   The 'export' formatted string.
        */
       public String toStringExport()
       {
          if (isUnknown())
          {
             return "?";
          }

          return Long.toString(longValue());
       }

       /**
        * Creates a string representation of the instance data using the user
        * specified format string (Progress 4GL compatible) or if no such
        * string is provided, the default format of '-&gt;,&gt;&gt;&gt;,&gt;&gt;9'.  All Progress
        * formatting is supported including the 'HH:MM' or 'HH:MM:SS' time
        * format for integers.
        *
        * @param   fmt
        *          The Progress 4GL format string or <code>null</code> if the default format is
        *          to be used.
        *
        * @return   The formatted string.
        */
       public String toString(String fmt)
       {
          if (isUnknown())
          {
             return "?";
          }

          String result = null;

          if (fmt == null)
          {
             fmt = getDefaultFormat(this);
          }
          else
          {
             int len = fmt.length();

             // test for the special case of a "timezone" value
             if ("+hh:mm".equalsIgnoreCase(fmt))
             {
                StringBuilder sb = new StringBuilder("+");
                long hours = this.value / 60;
                long minutes = this.value % 60;

                sb.append(String.format("%02d", hours));
                sb.append(':');
                sb.append(String.format("%02d", minutes));

                result = sb.toString();
             }

             // test for the special case of a "time" value
             if (len > 4 && fmt.substring(0, 5).equalsIgnoreCase("hh:mm"))
             {
                // it IS a time value!

                // strip off excess days to get a "time of day"
                long tod = longValue() % SECS_IN_A_DAY;

                // this is now the number of seconds since midnight, if it is a
                // non-negative number
                long time = tod;

                // negative numbers count backwards
                if (time < 0)
                {
                   time = SECS_IN_A_DAY + time;
                }
                // at this point time is a positive number

                boolean secs = false;
                boolean ampm = (fmt.indexOf('a') > 0 || fmt.indexOf('A') > 0);
                int     last = 5;

                if (len > 7 && fmt.substring(5, 8).equalsIgnoreCase(":ss"))
                {
                   secs = true;
                   last = 8;
                }

                // calculate the number of hours
                long numHrs = time / SECS_IN_AN_HOUR;

                // strip off hours to leave only minutes and seconds
                time %= SECS_IN_AN_HOUR;

                // calculate the number of minutes
                long numMin = time / SECS_IN_A_MIN;

                // strip off minutes to leave only seconds
                long numSec = time % SECS_IN_A_MIN;

                StringBuilder sb = new StringBuilder();

                // output the hours
                if (ampm)
                {
                   long hr = numHrs > 12 ? numHrs - 12 : numHrs;

                   if (hr == 0)
                   {
                      // midnight is 12 am
                      hr = 12;
                   }

                   if (hr < 10)
                   {
                      // hours are space padded instead of zero padded
                      sb.append(String.format(" %d", hr));
                   }
                   else
                   {
                      sb.append(String.format("%02d", hr));
                   }
                }
                else
                {
                   sb.append(String.format("%02d", numHrs));
                }

                // output the separator and the minutes (zero padded)
                sb.append(String.format(":%02d", numMin));

                if (secs)
                {
                   // output the separator and the seconds (zero padded)
                   sb.append(String.format(":%02d", numSec));
                }

                // add any stuff at the end of the string
                if (len > last)
                {
                   sb.append(fmt.substring(last));
                }

                // fixup the AM/PM result like Progress would (even if it is just
                // in the middle of random "extra" text)
                if (ampm && numHrs > 11)
                {
                   int replace = sb.indexOf("a");
                   if (replace >= 0)
                   {
                      sb.setCharAt(replace, 'p');
                   }
                   else
                   {
                      sb.setCharAt(sb.indexOf("A"), 'P');
                   }
                }

                result = sb.toString();
             }
          }

          if (result == null)
          {
             if (longValue() != Long.MIN_VALUE)
             {
                result = toString(Long.toString(Math.abs(longValue())), "", longValue() < 0, fmt);
             }
             else
             {
                // special case, Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE !
                result = toString("9223372036854775808", "", true, fmt);
             }
          }

          return result;
       }

       /**
        * Obtain the length (in bytes) of this BDT will use when serialized.
        *
        * @return  the length of this BDT will use when serialized.
        */
       @Override
       public int getSize()
       {
          if (isUnknown() || value == 0)
          {
             return 1;
          }
          else if (-127L <= value && value <= 127L) // 0x7F note: 7F, the other are 7E...
          {
             return 2;
          }
          else if (-32_512L <= value && value <= 32_511L) // 0x7EFFL
          {
             return 3;
          }
          else if (-8_323_072L <= value && value <= 8_323_071L) //  7E_FFFF
          {
             return 4;
          }
          else if (-2_147_483_648L <= value && value <= 2_147_483_647L) //  7FFF_FFFF note: 7F.., the other are 7E...
          {
             return 5;
          }
          else if (-545_460_846_592L <= value && value <= 545_460_846_591L) //  7E_FFFF_FFFF
          {
             return 6;
          }
          else if (-139_637_976_727_552L <= value && value <= 139_637_976_727_551L) //  7EFF_FFFF_FFFF
          {
             return 7;
          }
          else if (-35_747_322_042_253_312L <= value && value <= 35_747_322_042_253_311L) // 7E_FFFF_FFFF_FFFF
          {
             return 8;
          }
          else
          {
             return 9;
          }
       }

       /**
        * Worker method for getBits, this returns the number of bits from the
        * given position as an <code>integer</code>.
        *
        * @param  pos
        *         The 1-based index position to set, must be &gt; 0.
        * @param  len
        *         The number of bytes to take into consideration when performing the operation.
        *
        * @return The number of bits from the given position as an <code>integer</code>.
        */
       @Override
       public int64 getBitsWorker(double pos, double len)
       {
          // handle exception cases first
          if (isUnknown())
          {
             // cannot set bits on unknown value
             return this;
          }
          if (pos < 1 || pos > 64 || len < 1 || len > 64 || len + pos > 65)
          {
             // these conditions assure that we are manipulating only the available 64 bits
             ErrorManager.recordOrThrowError(
                     9219,
                     "Both the position and num-bits arguments for PUT-BITS and GET-BITS must " +
                           "be greater than 0 and less than 65. Their sum must be less than 66");
             return new int64();
          }

          // convert everything to native long
          long valCopy = this.value;
          long l_pos   = (long) pos - 1; // pos is 0-based
          long l_len   = (long) len;
          long l_rest  = 64 - l_pos - l_len; // number of bits in the left (we are not interested)

          // this shift will push the most important bits off
          valCopy  <<= l_rest;

          // this shift is in fact a double shift operation:
          // 1st: the l_rest bits pushed to the left before are dragged back and filled with 0s,
          //          clearing the left part of the bits
          // the 2nd operation will move the remaining (l_len + l_pos) bits to the right with l_pos
          //          positions eliminating the rightmost l_pos bits and filling with 0s the l_pos
          //          bits from the left.
          valCopy >>>= (l_rest + l_pos);

          // at the end of the above statement, the valCopy will contain exactly l_len bits aligned
          // to the right, which is exactly what we need
          return new int64(valCopy);
       }

       /**
        * Worker method for setBits, this copies the bit representation of an
        * integer to a location with a given number of bits within the current integer variable.
        *
        * @param    data
        *           The bit representation of an integer to be copied.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to take into consideration when performing the operation.
        */
       @Override
       public void setBitsWorker(double data, double pos, double len)
       {
          // handle exception cases first
          if (isUnknown())
          {
             // cannot set bits on unknown value
             return;
          }

          if (pos < 1 || pos > 64 || len < 1 || len > 64 || len + pos > 65)
          {
             ErrorManager.recordOrThrowError(
                     9219,
                     "Both the position and num-bits arguments for PUT-BITS and GET-BITS must " +
                     "be greater than 0 and less than 65. Their sum must be less than 66");
             return;
          }

          // convert everything to native long
          long l_pos  = (long) pos - 1; // pos is 0-based and represents the number of untouched bits from the right
          long l_len  = (long) len;
          long l_data = (long) data;
          long l_rest = 64 - l_pos - l_len;  // number of bits in the left which must remain untouched also

          // set all bits set to 1 in mask (0xFFFFFFFFFFFFFFFF). We will build a negative mask
          long mask = (long) -1;

          // this operation move the current 1 to the right preparing them to be filled with 0s at
          // the next shift
          mask >>>= l_pos;

          // two steps at one: fill the rightmost l_pos bits with 0s and continue the shift to
          // remove the leftmost l_rest bits that will be 0-ed at the next step
          mask  <<= (l_pos + l_rest);

          // fill the leftmost l_rest with 0s.
          mask >>>= l_rest;

          // We have moved the mask back and forth so that the l_len bits are on the starting position
          // the l_pos bits from the right were discarded and replaced with 0. The same stands for
          // the l_rest bits from the left.

          // Now, the mask represents (with 1) the bits that should be dropped so they are filled
          // with 0, by AND-ing with negated the mask (which has 1 on the left and right sides that
          // must not be changed and 0 on the part that will be overwritten by new data)
          long lvalue = value & ~mask;

          // shift the l_data bits on the correct position, the right l_pos will be filled with 0
          l_data <<= l_pos;

          // we apply the mask just in case that l_data has more bits that we are interested
          l_data &= mask;

          // assemble all together:
          setValue(lvalue | l_data);
       }

       /**
        * Replacement for the default object reading method. The latest state is
        * read from the input source.
        *
        * @param    in
        *           The input source from which fields will be restored.
        *
        * @throws   IOException
        *           In case of I/O errors.
        * @throws   ClassNotFoundException
        *           If payload can't be instantiated.
        */
       public void readExternal(ObjectInput in)
       throws IOException, ClassNotFoundException
       {
          value   = in.readLong();
          unknown = in.readBoolean();
       }

       /**
        * Replacement for the default object writing method. The latest state is
        * written to the output destination.
        *
        * @param    out
        *           The output destination to which fields will be saved.
        *
        * @throws   IOException
        *           In case of I/O errors.
        */
       public void writeExternal(ObjectOutput out)
       throws IOException
       {
          out.writeLong(value);
          out.writeBoolean(unknown);
       }

       /**
        * Set the value of this instance from the given numeric value.  Due to
        * rounding, a loss of precision may occur in this conversion (all digits
        * to the right of the decimal point will be truncated).
        *
        * @param   value
        *          The value to be represented in this instance.
        */
       protected void setValue(String value)
       {
          // special case: unknown value
          if (value == null || value.equals("?"))
          {
             setUnknown();
             return;
          }

          double  doubleValue = Double.NaN;
          boolean large       = false;

          try
          {
             doubleValue = parseDouble(value, true);
          }

          catch (ErrorConditionException e)
          {
             if (e.getProgressErrorCode() == 536)
             {
                // 'Decimal number is too large' error has occurred.
                large = true;
             }
             else
             {
                // Just re-throw the condition.
                throw e;
             }
          }

          boolean isUnknown = ((Double) doubleValue).equals(Double.NaN);

          if (isUnknown && ErrorManager.isSilentError() && ErrorManager.isPendingError())
          {
             int size = ErrorManager.getSize();

             if (size > 0 && ErrorManager.getErrorNumber(size).intValue() == 536)
             {
                // 'Decimal number is too large' error has occurred (ErrorManager is in silent mode)
                large = true;
             }
          }

          if (large || doubleValue > Long.MAX_VALUE || doubleValue < Long.MIN_VALUE)
          {
             ErrorManager.recordOrThrowError(78, "Value too large for integer");
          }
          else if (isUnknown)
          {
             // Math.round() will return 0 for NaN argument
             setUnknown();
          }
          else
          {
             setValue(Math.round(doubleValue));
          }
       }

       /**
        * Set the value of this instance as an <code>long</code>. No loss of precision will occur in
        * this conversion.  This also sets the <code>unknown value</code> state for this instance to
        * <code>false</code>.
        *
        * @param   newVal
        *          The value to be represented in this instance.
        *
        * @throws  ErrorConditionException
        *          Thrown if the <code>value</code> do not fit within this data-type.
        */
       public void setValue(long newVal)
       throws ErrorConditionException
       {
          if (checkBounds(newVal, false))
          {
             if (unknown || value != newVal)
             {
                checkUndoable(true);
             }

             unknown = false;
             value   = newVal;
          }
       }

       /**
        * Set the value of this instance as an <code>boolean</code>.
        * When the parameter is <code>true</code> the resulting integer value will be 1.
        * Otherwise the value is 0.
        *
        * @param    newVal
        *           The value to be represented in this instance.
        */
       public void setValue(boolean newVal)
       {
          long theVal = (newVal ? 1 : 0);
          if (unknown || value != theVal)
          {
             checkUndoable(true);
          }

          unknown = false;
          value = theVal;
       }

       /**
        * Set the value of this instance as an <code>logical</code>.
        * If the parameter's represents the <code>unknown value</code>, this instance will also
        * represent the <code>unknown value</code>.
        * <p>
        * When the parameter is <code>true</code> the resulting integer value will be 1.
        * Otherwise the value is 0.
        *
        * @param    newVal
        *           The value to be represented in this instance.
        */
       public void setValue(logical newVal)
       {
          if (newVal.isUnknown())
          {
             setUnknown();
             return;
          }

          setValue(newVal.booleanValue());
       }

       /**
        * Set the value after casting the parameter into the internal representation of this class.
        * Loss of precision may occur in this conversion (all digits to the right of the decimal
        * point will be truncated).
        *
        * @param   newVal
        *          The value to be represented in this instance.
        *
        * @throws  ErrorConditionException
        *          Thrown if the <code>value</code> do not fit within this data-type.
        */
       public void setValue(NumberType newVal)
       throws ErrorConditionException
       {
          if (newVal.isUnknown())
          {
             setUnknown();
             return;
          }

          if (newVal instanceof decimal)
          {
             // only decimals can be out-of-bounds
             BigDecimal crtVal = ((decimal) newVal).toJavaType();

             if (LOWER_BOUND.compareTo(crtVal) > 0 || UPPER_BOUND.compareTo(crtVal) < 0)
             {
                // first check for very large numbers
                ErrorManager.recordOrThrowError(78, "Value too large for integer");
                return;
             }
          }

          long val = newVal.longValue();

          if (isAssignDirect() || checkBounds(val, false))
          {
             if (unknown || value != val)
             {
                checkUndoable(true);
             }

             unknown = false;
             value   = val;
          }
       }

       /**
        * Return the current value as a Java Long wrapper.
        *
        * @return  Current value or <code>null</code> if unknown.
        */
       public Long toJavaLongType()
       {
          return isUnknown() ? null : longValue();
       }

       /**
        * Return the current value as a Java Integer wrapper.
        *
        * @return  Current value or <code>null</code> if unknown.
        */
       public Integer toJavaIntegerType()
       {
          return isUnknown() ? null : intValue();
       }

       /**
        * Checks if <code>value</code> can be assigned to this data type. This is important for
        * <code>integer</code> objects where the values must fit into 32 bit storage bounds.
        *
        * @param   value
        *          The value
        * @param   warn
        *          {@code true} if any generated error should actually be a warning (the
        *          {@code error-status:error} set to {@code false}.
        *
        * @return  Always true.
        *
        * @throws  ErrorConditionException
        *          Thrown if the <code>value</code> do not fit within this data-type.
        */
       protected boolean checkBounds(long value, boolean warn)
       throws ErrorConditionException
       {
          return true;
       }

       /**
        * Get the default initialization for an extent variable of this type.
        *
        * @return   See above.
        */
       @Override
       protected BaseDataType instantiateDefaultExtent()
       {
          return new int64(0);
       }

       /**
        * Stores global data relating to the state of the current context.
        */
       private static class WorkArea
       {
          /**
           * Stores the context-specific default format string (which honors the user's defined group
           * and decimal separators). Initialization is delegated to the implementation class.
           */
          private String defFmt = null;
       }

       /**
        * Simple container that stores and returns a context-local instance of the global work area.
        */
       private static class ContextContainer
       extends ContextLocal
       {
          /**
           * Obtains the context-local instance of the contained global work area.
           *
           * @return  The work area associated with this context.
           */
          public WorkArea obtain()
          {
             return (WorkArea) this.get();
          }

          /**
           * Initializes the work area, the first time it is requested within a new context.
           *
           * @return  The newly instantiated work area.
           */
          protected synchronized Object initialValue()
          {
             return new WorkArea();
          }
       }

       /**
        * An immutable int64 type, used for caching singleton instances for specific values.
        */
       public static class int64Constant
       extends int64
       implements BaseDataTypeConstant
       {
          /**
           * Default constructor, creates an instance that represents the unknown value.
           */
          public int64Constant()
          {
             this.unknown = true;
          }

          /**
           * Constructs an instance with a specific value.
           *
           * @param    value
           *           The value of the int64Constant instance.
           */
          public int64Constant(Long value)
          {
             this.value = value;
             this.unknown = false;
          }

          /**
           * Operation not possible.
           *
           * @throws UnsupportedOperationException
           *         This is an immutable int64 instance.
           */
          @Override
          public void setUnknown()
          {
             throw new UnsupportedOperationException("This is an immutable int64 instance.");
          }

          /**
           * Operation not possible.
           *
           * @throws UnsupportedOperationException
           *         This is an immutable int64 instance.
           */
          @Override
          public void setValue(long value) {
             throw new UnsupportedOperationException("This is an immutable int64 instance.");
          }

          /**
           * Operation not possible.
           *
           * @throws   UnsupportedOperationException
           *           This is an immutable int64 instance.
           */
          @Override
          public void assign(NumberType value)
          {
             throw new UnsupportedOperationException("This is an immutable int64 instance.");
          }

          /**
           * Operation not possible.
           *
           * @throws   UnsupportedOperationException
           *           This is an immutable int64 instance.
           */
          @Override
          public void assign(Undoable value)
          {
             throw new UnsupportedOperationException("This is an immutable int64 instance.");
          }

          /**
           * Operation not possible.
           *
           * @throws   UnsupportedOperationException
           *           This is an immutable int64 instance.
           */
          @Override
          public void assign(Object value)
          {
             throw new UnsupportedOperationException("This is an immutable int64 instance.");
          }

          /**
           * Operation not possible.
           *
           * @throws   UnsupportedOperationException
           *           This is an immutable int64 instance.
           */
          @Override
          Supplier<AbstractParameter> getAssigner()
          {
             throw new UnsupportedOperationException("This is an immutable int64 instance.");
          }

       }
    }