package nxm.sys.lib;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.time.Instant;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
//import java.util.Hashtable;
import java.util.Map;
import java.util.regex.Pattern;

import javax.xml.datatype.XMLGregorianCalendar;

import nxm.sys.inc.AsciiMap;
import nxm.sys.inc.Constants;
import nxm.sys.inc.DataTypes;
import nxm.sys.inc.InternalUseOnly;
import nxm.sys.inc.MidasReference;
import nxm.sys.inc.ProvisionalUseOnly;
import nxm.sys.inc.Tablizable;
import nxm.sys.inc.UnicodeMap;
import nxm.sys.libm.ArgStack;

/** A class to perform data conversions.

    @author Jeff Schoen
*/
public class Convert implements DataTypes, AsciiMap, Constants {
  /** List of rounding modes. Unlike most lists this starts at -1, hence why it is private. */
  private static final String roundingList = "ROUND,UP,DOWN,CEILING,FLOOR,HALF_UP,HALF_DOWN,HALF_EVEN,UNNECESSARY";

  // The following variables have been adjusted to use set values rather than the BigDecimal corresponding value as it has ben deprecated.
  // This change is implemented in 3.9.0
  /** Rounding mode that matches {@link BigDecimal#ROUND_DOWN} (this is the same as casting to an integer). Updated in 3.9.0 */
  public static final int ROUND_DOWN        = 1;
  /** Rounding mode that matches {@link BigDecimal#ROUND_CEILING} (this is the same as using {@link Math#ceil}). Updated in 3.9.0*/
  public static final int ROUND_CEILING     = 2;
  /** Rounding mode that matches {@link BigDecimal#ROUND_FLOOR} (this is the same as using {@link Math#floor}). Updated in 3.9.0*/
  public static final int ROUND_FLOOR       = 3;
  /** Rounding mode that matches {@link BigDecimal#ROUND_HALF_UP} (see {@link #lround}). Updated in 3.9.0 */
  public static final int ROUND_HALF_UP     = 4;
  /** Rounding mode that matches {@link BigDecimal#ROUND_HALF_EVEN} (this is the same as using {@link Math#rint}). Updated in 3.9.0 */
  public static final int ROUND_HALF_EVEN   = 6;
  /** Rounding mode that matches {@link BigDecimal#ROUND_UNNECESSARY} (this indicates that it is an integer to integer conversion). Updated in 3.9.0 */
  public static final int ROUND_UNNECESSARY = 7;
  /** Rounding mode that matches the behavior of {@link Math#round}. */
  public static final int ROUND_ROUND       = -1; // No match in BigDecimal

  // 0-10 have optimized versions within BigDecimal, this just gets at them...
  // Added in 3.9.0 for NXM-3157
  private static final BigDecimal BigDecimal_NEG1 = BigDecimal.ONE.negate();
  private static final BigDecimal BigDecimal_TWO = BigDecimal.valueOf(2L);
  private static final BigDecimal BigDecimal_THREE = BigDecimal.valueOf(3L);
  private static final BigDecimal BigDecimal_FOUR = BigDecimal.valueOf(4L);
  private static final BigDecimal BigDecimal_FIVE = BigDecimal.valueOf(5L);
  private static final BigDecimal BigDecimal_SIX = BigDecimal.valueOf(6L);
  private static final BigDecimal BigDecimal_SEVEN = BigDecimal.valueOf(7L);
  private static final BigDecimal BigDecimal_EIGHT = BigDecimal.valueOf(8L);
  private static final BigDecimal BigDecimal_NINE = BigDecimal.valueOf(9L);

  // String values are used in comparisons to see if a value will fit in an Integer or Long
  private static final String     String_Integer_MAX_VALUE = String.valueOf(Integer.MAX_VALUE);
  private static final String     String_Integer_MIN_VALUE = String.valueOf(Integer.MIN_VALUE);
  private static final int        DIGITS_INTEGER_MAX_VALUE = String_Integer_MAX_VALUE.length();
  private static final String     String_Long_MAX_VALUE    = String.valueOf(Long.MAX_VALUE);
  private static final String     String_Long_MIN_VALUE    = String.valueOf(Long.MIN_VALUE);
  private static final int        DIGITS_LONG_MAX_VALUE    = String_Long_MAX_VALUE.length();

  private static Pattern WHOLE_NUMBER_PATTERN = Pattern.compile("^[-+]?\\d+$");
  private static Pattern FLOAT_NUMBER_PATTERN = Pattern.compile("^([+-]?([0-9]*)(\\.([0-9]*+))?)([e][+-]?\\d+)?$");

  //jph -- TBD -- should we make these consistent with Args and DataTypes true and false states
  private static final String s2StateTrueStates = DataTypes.trueStates+"1|ABSC|";
  private static final String s2StateFalseStates = DataTypes.falseStates+"0|INDEX|";

  private static DecimalFormat nf = new DecimalFormat("0.0000000");

  // Sedit process command fields moved here in 3.9.2 to support transition towards JDK modules
  /** How many deprecation warnings have been issued this session for the 3.1.2 SEDIT changes */
  private static int seditWarnCount = 0; // remove when SEDIT changes go as default

  /** How many deprecation warnings will be issues per session for the 3.1.2 SEDIT changes */
  private static int numSeditWarns = 3; // remove when SEDIT changes go as default
  // end Sedit process command fields

  private static byte[] bitrev = genbitrev();
  private static short[] ulaw = genulaw();

  private static final char [] alphabet = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',   //  0 to  7
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',   //  8 to 15
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',   // 16 to 23
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',   // 24 to 31
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',   // 32 to 39
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',   // 40 to 47
    'w', 'x', 'y', 'z', '0', '1', '2', '3',   // 48 to 55
    '4', '5', '6', '7', '8', '9', '+', '/' }; // 56 to 63

  /** No match in BigDecimal */
  public static final int NO_VALID_VALUE_FOUND  = -1;

  private static boolean favorJavaBehavior = false;

  private static boolean alwaysDefaultToDouble = false;

  private static boolean minSupportUnsupportedDataTypes = false;

  /** In 3.5.1 this constructor was deprecated: this constructor is intended to be private, all methods are static.
   *  @since 3.9.0 made private
   */
  private Convert() {}

  /** Converts a string to a double value without results lookup
      @param string the string to be converted.
      @return the double value.
      @see #s2d(String, Object)
   */
  public static double s2d (String string) {
    return s2d(string,null);
  }

  /** Converts a string to a double value with results lookup
      @param string String to convert
      @param ref Reference
      @return double value
   */
  public static double s2d (String string, Object ref) {
    if (string==null || string.length()==0) return 0.0;
    double[] dval = new double[3];
    if ( s2d( string.getBytes(), 0, string.length(), dval, ref) < 0)
      throw new MidasException("Illegal numeric expression: "+string);
    return dval[0];
  }

  /** Converts a string to a BigDecimal value with results lookup.
      @param string String to convert
      @param ref Reference
      @return string converted to a BigDecimal
      @since NeXtMidas 3.5.1 for Bug 2833.
  */
  private static BigDecimal s2BigDecimal (String string, Object ref) {
    if (string==null || string.length()==0) return new BigDecimal(0.0);
    BigDecimalValue bdVal = new BigDecimalValue();
    if ( s2BigDecimal( string.getBytes(), 0, string.length(), bdVal, ref) < 0)
      throw new MidasException("Illegal numeric expression: "+string);
    return bdVal.getValue();
  }

  /** Converts a string to a Number value with results lookup. This is the same as
      <code>s2n(string,null)</code>.
      @param string String to convert
      @return numeric value
      @since NeXtMidas 2.3.0
   */
  public static Number s2n (String string) {
    return s2n(string,null);
  }

  /** Converts a string to a Number value with results lookup. This is identical to
      {@link #s2d(String,Object)} except that it returns null if the input is null or
      the empty string ({@link #s2d(String,Object)} returns 0.0) and it returns null
      if the string is inconvertible ({@link #s2d(String,Object)} throws an exception).
      @param string String to convert
      @param ref Reference
      @return numeric value
      @since NeXtMidas 2.3.0
   */
  public static Number s2n (String string, Object ref) {
    return s2n(string, ref, false, true);
  }

  /** Converts a string to a int value without results lookup
      @param string String to convert
      @return double value
      @see #s2l(String, Object)
   */
  public static int s2l (String string) {
    double objAsRintDouble = Math.rint(s2d(string));
    if (!favorJavaBehavior) { // 4.0.0 fix inefficient code
      // Midas behavior
      return (int)objAsRintDouble;
    }
    else {
      // need Java behavior of wrapping values
      return (int)((long)objAsRintDouble);
    }

  }

  /** Converts a string to a int value with results lookup
      @param string String to convert
      @param ref Reference
      @return int (L:) value
   */
  public static int s2l (String string, Object ref) {
    return (int)s2d(string,ref);
  }

  /** Converts a string to a long value
      @param string String to convert
      @return long value
      @see #s2xl(String, Object)
   */
  public static long s2xl (String string) {
    // return (long)s2d(string,null); // prior to 3.5.1
    return s2xl(string,null); // since 3.5.1 for BUG 2833
  }

  /** Converts a string to a long value with results lookup
      @param string String to convert
      @param ref Reference
      @return long value
   */
  public static long s2xl (String string, Object ref) {
    // return (long)s2d(string,ref); // prior to 3.5.1
    // since 3.5.1 to not lose precision for large 64-bit integers (BUG 2833)
    if (string==null || string.length()==0) return 0;
    Long xlResult = s2xl(string.getBytes(), 0, string.length(), ref); // 1st Try to convert to X using BigDecimal based method
    if (xlResult == null) {
      xlResult = (long)s2d(string,ref); // 2nd handle out of range & NaN & Infinity cases
    }
    return xlResult;
  }

  /** Raw entry point for String to Double conversion
      @param buffer Byte array to convert
      @param index1 First index into byte array
      @param index2 Second index into byte array
      @param dval   Output array of doubles
      @param ref    Reference
      @return 1 for success, -1 for failure
   */
  public static int s2d (byte[] buffer, int index1, int index2, double[] dval, Object ref) {
    // An operator that is negated (e.g. -OP_ADD) indicates that the second value should
    // be negated before the operator is applied.
    final int OP_NONE = 0; // No operator
    final int OP_ADD  = 1; // Add (+)
    final int OP_SUB  = 2; // Subtract (-)
    final int OP_MUL  = 3; // Multiply (*)
    final int OP_DIV  = 4; // Divide (/)
    final int OP_POW  = 5; // Raise to Power (**)

    int op = OP_ADD;  // default operator
    double d=0;   // The first (or only) argument in the expression
    double dd;    // The second argument in the expression

    for (int i=index1; i<index2; ) {
      int i2 = 0;
      byte c = buffer[i];

      if (c <= SPACE);
      else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
        i2 = s2d_r(buffer,i,index2,dval,ref);
      }
      else if ( (c >= '0' && c <= '9') || (c == '.' && i<index2-1) ) {
        i2 = s2d_n(buffer,i,index2,dval,ref);
      }
      else if (c == '(') {
        i2 = s2d_p(buffer,i,index2,dval,ref);
      }
      else if (c == '+') {
        if (op==OP_NONE) op=OP_ADD;
      }
      else if (c == '-') {
        if (op==OP_NONE) op=OP_SUB;
        else op = -op;
      }
      else if (c == '*') {
        if (op==OP_NONE) op=OP_MUL;
        else if (op==OP_MUL) op=OP_POW;
        else return -1;
      }
      else if (c == '/') {
        if (op==OP_NONE) op=OP_DIV;
        else i2 = s2d_s (buffer,i,index2,dval,ref);
      }
      else {
        op = -OP_ADD;
        break;
      }

      if (i2>0) {
        dd = dval[0];
        if (op<0) { op = -op; dd = -dd; }
        if (op==OP_NONE) return -1;
        else if (op==OP_ADD) d = d+dd;
        else if (op==OP_SUB) d = d-dd;
        else if (op==OP_MUL) d = d*dd;
        else if (op==OP_DIV) d = d/dd;
        else if (op==OP_POW) d = Math.pow(d,dd);
        else return -1;
        i += i2;
        op = OP_NONE;
      }
      else if (i2==0) i = i+1;
      else return -1;
    }

    if (op==OP_NONE) dval[0]=d;
    else if (op==OP_ADD) dval[0]=d+1;
    else if (op==OP_SUB) dval[0]=d-1;
    else return -1;
    return 1;
  }

  /** Performs  String to BigDecimal conversion.
      @param buffer Byte array to convert
      @param index1 First index into byte array
      @param index2 Second index into byte array
      @param bigDecimalVal Object holding current BigDecimal result
      @param ref    Reference
      @return 1 for success, -1 for failure
      @since NeXtMidas 3.5.1 for Bug 2833.
  */
  private static int s2BigDecimal (byte[] buffer, int index1, int index2, BigDecimalValue bigDecimalVal, Object ref) {
    // An operator that is negated (e.g. -OP_ADD) indicates that the second value should
    // be negated before the operator is applied.
    final int OP_NONE = 0; // No operator
    final int OP_ADD  = 1; // Add (+)
    final int OP_SUB  = 2; // Subtract (-)
    final int OP_MUL  = 3; // Multiply (*)
    final int OP_DIV  = 4; // Divide (/)
    final int OP_POW  = 5; // Raise to Power (**)

    int op = OP_ADD;  // default operator
    BigDecimal bd = new BigDecimal(0);   // The first (or only) argument in the expression
    BigDecimal bd2nd;                    // The second argument in the expression

    for (int i=index1; i<index2; ) {
      int i2 = 0;
      byte c = buffer[i];

      if (c <= SPACE);
      else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
        i2 = s2BigDecimal_result (buffer,i,index2,bigDecimalVal,ref);
      }
      else if ( (c >= '0' && c <= '9') || (c == '.' && i<index2-1) ) {
        i2 = s2BigDecimal_number (buffer,i,index2,bigDecimalVal,ref);
      }
      else if (c == '(') {
        i2 = s2BigDecimal_paren (buffer,i,index2,bigDecimalVal,ref);
      }
      else if (c == '+') {
        if (op==OP_NONE) op=OP_ADD;
      }
      else if (c == '-') {
        if (op==OP_NONE) op=OP_SUB;
        else op = -op;
      }
      else if (c == '*') {
        if (op==OP_NONE) op=OP_MUL;
        else if (op==OP_MUL) op=OP_POW;
        else {
          bigDecimalVal.setToInvalid();
          return -1;
        }
      }
      else if (c == '/') {
        if (op==OP_NONE) op=OP_DIV;
        else i2 = s2BigDecimal_switch (buffer,i,index2,bigDecimalVal,ref);
      }
      else {
        op = -OP_ADD;
        break;
      }

      if (i2>0) {
        bd2nd = bigDecimalVal.getValue();
        if (op<0) { op = -op; bd2nd = bd2nd.negate(MathContext.DECIMAL128); }
        if (op==OP_NONE) return -1;
        else if (op==OP_ADD) {bd = bd.add(bd2nd, MathContext.DECIMAL128); }
        else if (op==OP_SUB) {bd = bd.subtract(bd2nd, MathContext.DECIMAL128); }
        else if (op==OP_MUL) {bd = bd.multiply(bd2nd, MathContext.DECIMAL128); }
        else if (op==OP_DIV) {bd = bd.divide(bd2nd, MathContext.DECIMAL128); }
        else if (op==OP_POW) {
          // Try to use BigDecimal's pow(int) method
          try {
            // If the exponent ins not an int, BigDecimal will throw an ArtihmeticException
            int exponent = bd2nd.intValueExact();
            bd = bd.pow(exponent, MathContext.DECIMAL128);
          }
          // if exponent does not fit cleanly in int, change values to doubles and calculate using Math.pow
          catch (ArithmeticException ae){
            double exponent = bd2nd.doubleValue();
            double base = bd.doubleValue();
            double result = Math.pow(base, exponent);
            bd = new BigDecimal(result);
          }
        }
        else {
          bigDecimalVal.setToInvalid();
          return -1;
        }
        i += i2;
        op = OP_NONE;
      }
      else if (i2==0) i = i+1;
      else {
        bigDecimalVal.setToInvalid();
        return -1;
      }
    }

    if (op==OP_NONE) {bigDecimalVal.setValue(bd); }
    else if (op==OP_ADD) {bigDecimalVal.setValue(bd.add(BigDecimal.ONE));  }
    else if (op==OP_SUB) {bigDecimalVal.setValue(bd.subtract(BigDecimal.ONE)); }
    else {
      bigDecimalVal.setToInvalid();
      return -1;
    }
    return 1;
  } // ************ end new for 3.5.1 Bug 2833

  /** Mnemonics to be used in convert methods:
       HMS = Hours-minutes-seconds
       DMS = Degrees-minutes-seconds
       HEX = Hexadecimal
       BIN = Binary
   */
  private static final int HMS=-1, DMS=-2, HEX=-3, BIN=-4;

  /** <b>Internal Use Only:</b> to convert String represented as a byte array to array of doubles
   * @return number of chars used in number, -1 on error
   **/
  private static int s2d_n (byte[] buffer, int index, int index2, double[] dval, Object ref) {
    int i, used=1, ie=0, ix, np=0;
    double d=0;
    byte c;

    for (i=index; i<index2-1; ) {
      c = buffer[++i];
      if (c>='0' && c<='9' && ie!=HEX && ie!=BIN);
      else if (ie==0) {
        if (c=='.' && np==0) np++;
        else if (c=='E'||c=='e') {
          ie = i;
          c = buffer[i+1];
          if (c=='+'||c=='-') i++;
        }
        else if (c==':') ie = HMS;
        else if (c==FTIC) ie = DMS;
        else if ((c=='X'||c=='x') && i==index+1 && buffer[index]=='0') ie = HEX;
        else if ((c=='B'||c=='b') && i==index+1 && buffer[index]=='0') ie = BIN;
        else break;
      }
      else if (ie == HMS) {
        if (c!='.' && c!=':') break;
      }
      else if (ie == DMS) {
        if (c!='.' && c!=FTIC) break;
      }
      else if (ie == HEX) {
             if (c>='0'&&c<='9') ix=c-ZERO;
        else if (c>='A'&&c<='F') ix=c-UPPERA+10;
        else if (c>='a'&&c<='f') ix=c-LOWERA+10;
        else break;
        d = d*16 + ix;
      }
      else if (ie == BIN) {
        if (c>='0'&&c<='1') ix=c-ZERO;
        else break;
        d = d*2 + ix;
      }
      else break;
      used = i-index+1;
    }

    if (ie >= 0) {
      String temp = new String(buffer,index,used);
      try { d = Double.parseDouble(temp); }
      catch (Exception e) { used = -1; }
    }
    else if (ie == HMS) {
      try { d = Time.tod2sec(buffer,index,index+used); }
      catch (Exception e) { used = -1; }
    }
    else if (ie == DMS) {
      try { d = dms2deg(buffer,index,index+used); }
      catch (Exception e) { used = -1; }
    }
    else if (ie == HEX) {
      double d2G = 2*(double)B1G, d4G=d2G+d2G;
      if (d >= d2G && d < d4G) d -= d4G;
    }
    if (used>0 && i<index2) {
      c = buffer[i];
      if (c=='K'||c=='k') { d *= B1K; used++; }
      if (c=='M'||c=='m') { d *= B1M; used++; }
      if (c=='G'||c=='g') { d *= B1G; used++; }
      if (c=='T'||c=='t') { d *= B1T; used++; }
    }
    dval[0] = d;
    return used;
  }

  /** To convert String represented as a byte array to a BigDecimalValue.
     @param buffer Byte array to convert
     @param index1 First index into byte array
     @param index2 Second index into byte array
     @param bigDecimalVal Object holding current BigDecimal result
     @param ref    Reference
     @return 1 for success, -1 for failure
     @since NeXtMidas 3.5.1 for BUG 2833
   */
  @InternalUseOnly("since created")
  private static int s2BigDecimal_number (byte[] buffer, int index, int index2, BigDecimalValue bdVal, Object ref) {
    int i, used=1, ie=0, ix, np=0;
    BigDecimal bd = new BigDecimal(0.0);
    byte c;

    for (i=index; i<index2-1; ) {
      c = buffer[++i];
      if (c>='0' && c<='9' && ie!=HEX && ie!=BIN);
      else if (ie==0) {
        if (c=='.' && np==0) np++;
        else if (c=='E'||c=='e') {
          ie = i;
          c = buffer[i+1];
          if (c=='+'||c=='-') i++;
        }
        else if (c==':') ie = HMS;
        else if (c==FTIC) ie = DMS;
        else if ((c=='X'||c=='x') && i==index+1 && buffer[index]=='0') ie = HEX;
        else if ((c=='B'||c=='b') && i==index+1 && buffer[index]=='0') ie = BIN;
        else break;
      }
      else if (ie == HMS) {
        if (c!='.' && c!=':') break;
      }
      else if (ie == DMS) {
        if (c!='.' && c!=FTIC) break;
      }
      else if (ie == HEX) {
             if (c>='0'&&c<='9') ix=c-ZERO;
        else if (c>='A'&&c<='F') ix=c-UPPERA+10;
        else if (c>='a'&&c<='f') ix=c-LOWERA+10;
        else break;
        bd = bd.multiply(new BigDecimal(16), MathContext.DECIMAL128);
        bd = bd.add(new BigDecimal(ix), MathContext.DECIMAL128);
      }
      else if (ie == BIN) {
        if (c>='0'&&c<='1') ix=c-ZERO;
        else break;
        bd = bd.add(bd, MathContext.DECIMAL128);
        bd = bd.add(new BigDecimal(ix), MathContext.DECIMAL128);
      }
      else break;
      used = i-index+1;
    }

    if (ie >= 0) {
      String temp = new String(buffer,index,used);
      try { bd = new BigDecimal(temp);
      }
      catch (Exception e) { used = -1; }
    }
    else if (ie == HMS) {
      try { bd = new BigDecimal(Time.tod2sec(buffer,index,index+used)); } //convert double return val to BigDecimal
      catch (Exception e) { used = -1; }
    }
    else if (ie == DMS) {
      try { bd = new BigDecimal(dms2deg(buffer,index,index+used)); } //convert double return val to BigDecimal
      catch (Exception e) { used = -1; }
    }
    else if (ie == HEX) {
      double d2G = 2*(double)B1G, d4G=d2G+d2G;
      BigDecimal bd_2G = new BigDecimal(d2G);
      BigDecimal bd_4G = new BigDecimal(d4G);
      if (bd.compareTo(bd_2G) >= 0 && bd.compareTo(bd_4G) == -1) bd = bd.subtract(bd_4G, MathContext.DECIMAL128);
    }
    if (used>0 && i<index2) {
      c = buffer[i];
      if (c=='K'||c=='k') { bd = bd.multiply(new BigDecimal(B1K), MathContext.DECIMAL128); used++; }
      if (c=='M'||c=='m') { bd = bd.multiply(new BigDecimal(B1M), MathContext.DECIMAL128); used++; }
      if (c=='G'||c=='g') { bd = bd.multiply(new BigDecimal(B1G), MathContext.DECIMAL128); used++; }
      if (c=='T'||c=='t') { bd = bd.multiply(new BigDecimal(B1T), MathContext.DECIMAL128); used++; }
    }
    bdVal.setValue(bd);
    return used;
  }

  /** Convert an object to a boolean state value. This uses {@link StringUtil#isTrue(String)} and
      {@link StringUtil#isFalse(String)} to determine if a given string is true or false. If neither
      method recognizes the string (or the string is null) an exception is thrown.
      @param val Input value.
      @return The boolean value.
      @since NeXtMidas 2.5.0
      @see #o2z(Object, Object)
   */
  public static boolean o2z (Object val) {
    return o2z(val,null);
  }

  /** Convert an object to a boolean state value. This uses the following algorithm (<code>val</code>
      is first converted to a string, <code>str</code> using {@link #o2s(Object,Object)}):
      <pre>
        if (str == null)             return false;
        if (str.length() == 0)       return false;
        if (StringUtil.isTrue (str)) return true;
        if (StringUtil.isFalse(str)) return false;
      </pre>
      Any other values cause an exception.
      @param val Input value.
      @param ref The reference object.
      @return The boolean value.
      @since NeXtMidas 2.5.0
   */
  public static boolean o2z (Object val, Object ref) {
    if (val instanceof Boolean) { return ((Boolean)val).booleanValue(); }
    String str = o2s(val,ref);

    if (str == null)             return false;
    if (str.length() == 0)       return false;
    if (StringUtil.isTrue (str)) return true;
    if (StringUtil.isFalse(str)) return false;
    if (str.startsWith("TEST(")) return inLineTest(ref,str); // since 3.1.3: inLine TEST

    throw new MidasException("Unable to convert "+str+" to type Z. No applicable conversion found.");
  }

  /** Convert a string to a boolean state value. This uses {@link StringUtil#isTrue(String)} and
      {@link StringUtil#isFalse(String)} to determine if a given string is true or false. If neither
      method recognizes the string (or the string is null) an exception is thrown.
      @param val Input string.
      @param ref The reference object.
      @return The boolean value.
      @since NeXtMidas 2.5.0
   */
  public static boolean s2z (String val, Object ref) {
    return o2z(val,ref);
  }

  /** Convert a string to a boolean state value. This uses {@link StringUtil#isTrue(String)} and
      {@link StringUtil#isFalse(String)} to determine if a given string is true or false. If neither
      method recognizes the string (or the string is null) an exception is thrown.
      @param val Input string.
      @return The boolean value.
      @since NeXtMidas 2.5.0
   */
  public static boolean s2z (String val) {
    return s2z(val,null);
  }

  /** Convert a string to a boolean state with default
      @param svalue Input string
      @param defvalue Default state if no match
      @return Y|YES|T|TRUE|ON|1|ABSC = TRUE
              N|NO|F|FALSE|OFF|0|INDEX = FALSE
              or defvalue if there is no match
   */
  public static boolean s2state (String svalue, boolean defvalue) {
    StringBuilder sb = new StringBuilder("|"); sb.append(svalue); sb.append("|");
    String text = sb.toString();//"|"+svalue.toString()+"|";    // string entry
    if (s2StateTrueStates.indexOf(text) >= 0) return true;
    if (s2StateFalseStates.indexOf(text) >= 0) return false;
    return defvalue;
  }

  /* Internal conversion of Degrees-minutes-seconds to degrees */
  private static double dms2deg (byte[] buffer,int i1,int i2) {
    int deg=0,min=0,sec=0,field=1,place=1,val=0;
    double frac=0,value; boolean neg=false;
    if (buffer[i1]==MINUS) { neg=true; i1++; }
    for (int i=i2-1; i>=i1; i--) {
      byte b = buffer[i];
      if (b>=ZERO && b<=NINE) {
        val += (b-ZERO)*place; place*=10;
        if (i==i1 || buffer[i-1]==FTIC) {
          switch (field) {
            case 1: sec = val; break;
            case 2: min = val; break;
            case 3: deg = val; break;
          }
          field++; place=1; val=0; i--;
        }
      }
      else if (b==PERIOD && field==1) { frac = ((double)val)/((double)place); place=1; val=0; }
      else return 0.0;
    }
    if (field==2) value = sec + frac;                 // ddd.ddd format
    else if (field==3) value = min + (sec+frac)/60.0; // ddd'mm.mmm format
    else value = deg + min/60.0 + (sec+frac)/3600.0;  // ddd'mm'ss.sss format
    if (neg) value = -value;
    return value;
  }

  /** Convert Degrees-minutes-seconds String to degrees
      @param str String to convert
      @return degrees as a double
   */
  public static double dms2deg (String str) {
    byte[] buffer = str.getBytes();
    return dms2deg(buffer,0,buffer.length);
  }

  /** Convert degrees to Degrees-minutes-seconds string. This is the same as
      <code>deg2dms(deg,AsciiMap.FTIC,AsciiMap.FTIC,"00")</code>.
      @param deg Degrees as a double.
      @return Degrees-minutes-seconds string.
      @see #deg2dms(double,String,String,String)
   */
  public static String deg2dms (double deg) {
    return deg2dms(deg, AsciiMap.FTIC, AsciiMap.FTIC, "00");
  }

  /** Convert degrees to Degrees-minutes-seconds string.  This is the same as
      <code>deg2dms(deg,AsciiMap.FTIC,AsciiMap.FTIC,secFormat)</code>.
      @param deg Degrees as a double.
      @param secFormat The format String of the seconds {@link java.text.DecimalFormat}
      @return Degrees-minutes-seconds string.
      @see #deg2dms(double,String,String,String)
   */
  public static String deg2dms (double deg, String secFormat) {
    return deg2dms(deg, AsciiMap.FTIC, AsciiMap.FTIC, secFormat);
  }

  /** Convert degrees to Degrees-minutes-seconds string.  This is the same as
      <code>deg2dms(deg,degSign,minSign,"00")</code>.
      @param deg Degrees as a double.
      @param degSign The degrees sign to use.
      @param minSign The minutes sign to use.
      @return Degrees-minutes-seconds string.
      @see #deg2dms(double,String,String,String)
   */
  public static String deg2dms (double deg, char degSign, char minSign) {
    return deg2dms(deg, degSign, minSign, "00");
  }

  /** Convert degrees to Degrees-minutes-seconds string.  Uses char for delimiters instead
      of String.
      @param deg     Degrees as a double.
      @param degSign The degrees sign to use.
      @param minSign The minutes sign to use.
      @param secFormat The format String of the seconds {@link java.text.DecimalFormat}
      @return Degrees-minutes-seconds string.
      @see #deg2dms(double,String,String,String)
   */
  public static String deg2dms (double deg, char degSign, char minSign, String secFormat) {
    return deg2dms(deg, Character.toString(degSign), Character.toString(minSign), secFormat);
  }

  /** Convert degrees to Degrees-minutes-seconds string.
      <br>
      Examples:
      <pre>
        deg2dms(100.1234) = 100'07'25
        deg2dms(-100.1234) = -100'07'25
        deg2dms(100.1234,':',':') = 100:07:25
        deg2dms(100.1234,":",":","00.00") = 100:07:24.74
        deg2dms(100.1234,"'","''","00.00") = 100'07''24.74
      </pre>
      @param deg     Degrees as a double.
      @param degSign The degrees sign to use.
      @param minSign The minutes sign to use.
      @param secFormat The format String of the seconds {@link java.text.DecimalFormat}
      @return Degrees-minutes-seconds string.
      @since NeXtMidas 2.1
   */
  public static String deg2dms (double deg, String degSign, String minSign, String secFormat) {
    DecimalFormat degMinDF = new DecimalFormat("00");
    DecimalFormat secDF    = new DecimalFormat(secFormat);
    StringBuilder  sb      = new StringBuilder();
    int mult = (deg<0)? -1:1;
    double dms = mult*deg*3600;
    double d = (int)dms/3600; dms -= (d*3600);
    double m = (int)dms/60; dms -= (m*60);
    double s = dms;

    String degStr = degMinDF.format(mult*d);
    String minStr = degMinDF.format(m);
    String secStr = secDF.format(s);

    if (secStr.startsWith("60")) { // Fix for Bug 400 -- format can cause rounding to ddd'mm'60
      if (m == 59) { d = d+1; m = 0;   s = 0; }
      else         {          m = m+1; s = 0; }

      degStr = degMinDF.format(mult*d);
      minStr = degMinDF.format(m);
      secStr = secDF.format(s);
    }

    sb.append(degStr).append(degSign);
    sb.append(minStr).append(minSign);
    sb.append(secStr);
    return sb.toString();
  }

  /** Convert degrees to Degrees-minutes-seconds string (may use Unicode characters). This method
      is useful when printing to the screen using Java methods that can properly display Unicode
      characters. Use {@link #deg2dms(double)} when only ASCII can be used.<br>
      <br>
      Examples:
      <pre>
        deg2dms(10.0) = "10&deg;00'00"
      </pre>

      @param deg Degrees as a double.
      @return Degrees-minutes-seconds string.
      @since NeXtMidas 1.9.5
   */
  public static String deg2dmsUnicode (double deg) {
    return deg2dms(deg, UnicodeMap.DEGREE_SIGN, UnicodeMap.FTIC);
  }

  /** Convert degrees to Degrees-minutes-seconds string (may use Unicode characters). This method
      is useful when printing to the screen using Java methods that can properly display Unicode
      characters. Use {@link #deg2dms(double)} when only ASCII can be used.<br>
      <br>
      Examples:
      <pre>
        deg2dms( 10.0, "NS") = "100&deg;00'00N"
        deg2dms(-10.0, "EW") = "100&deg;00'00W"
      </pre>

      @param deg  Degrees as a double.
      @param axis The direction (must be "NS" or "EW").
      @return Degrees-minutes-seconds string.
      @since NeXtMidas 2.1.0
   */
  public static String deg2dmsUnicode (double deg, String axis) {
    if((axis == null) || !(axis.equals("NS") || axis.equals("EW"))) {
      throw new MidasException("Convert.deg2dmsUnicode(double,String): second parameter must be "
                             + "either 'NS' or 'EW', given '" + axis + "'.");
    }

    if(deg > 0) {
      return deg2dms( deg, UnicodeMap.DEGREE_SIGN, UnicodeMap.FTIC) + axis.charAt(0);
    } else {
      return deg2dms(-deg, UnicodeMap.DEGREE_SIGN, UnicodeMap.FTIC) + axis.charAt(1);
    }
  }

  /* Internal conversion of String to double for special IEEE 754 values (NaN and Inf) */
  private static int s2d_i (double[] dval, String name, int lr) {
    if (StringUtil.isNaN(name)) {
      dval[0] = Double.NaN;
    }
    else if (StringUtil.isInf(name)) {
      dval[0] = Double.POSITIVE_INFINITY; // Case of -Inf is handled as -(Inf) by s2d(..)
    }
    else {
      lr = -1; // Not NaN or Inf
    }
    return lr;
  }

  /** Process an in-line file access read/write.
      @param ref   The Midas reference object to use.
      @param name  The name of the data file.
      @param write Indicates if the file is intended to be written to.
      @return The data file to use for read/write. If <code>write == true</code>
              then the file will be opened in <code>APPEND | INOUT</code> mode;
              if <code>write == false</code> then the file will be opened in
              <code>INPUT</code>.
      @see DataFile#APPEND
      @see DataFile#INOUT
      @see DataFile#INPUT
      @see DataFile#open(int)
      @since NeXtMidas 3.7.0 (Was formerly a private method in {@link Results})
   */
  @InternalUseOnly
  static DataFile inLineFile (MidasReference ref, String name, boolean write) {
    // JC4: Note that this code is extra-complicated since it allows one to get a subclass of
    // DataFile. I copied it as-is from Results.java as I was afraid of breaking someone but
    // have never seen a use of that code. For example, I replaced everything before the
    // df.init(..) call with a simple "DataFile df = new DataFile();" and the confidence tests
    // all passed.
    FileName fn = BaseFile.getFileNameFor(ref,name);
    BaseFile bf = BaseFile.getInstanceFor(ref,fn);
    // do this to allow inline file trim on .det sections and stuff
    DataFile df = (bf instanceof DataFile)? (DataFile)bf : new DataFile();
    df.init(ref, name, "", "", 0);
    df.open( write? DataFile.APPEND|DataFile.INOUT : DataFile.INPUT);
    return df;
  }

  /** Performs an in-line SEDIT.
   *  @param ref  The midas context or reference. Note that if an instance of {@link Args} is passed
   *              in the switches will be adopted by the SEDIT function.
   *  @param test The test in the syntax of <code>"SEDIT(...)"</code>.
   *  @return The results of the test.
   *  @since NeXtMidas 3.5.4
   */
  @ProvisionalUseOnly("Added in NeXtMidas 3.5.4")
  public static Object inLineSedit (MidasReference ref, String test) {
    String testStr = test.substring(6,test.length()-1); // Strip off "SEDIT(" and ")"
    Midas  midas   = ref2Midas(ref);
    Parser p       = new Parser(testStr, true);
    Args   args    = new Args(midas,p);
    if (ref instanceof Args) ((Args)ref).copySwitchesTo(args);
    return processSeditCommandLine(args.getCS(1),midas,args,2,false);
  }


  /** Process the command line arguments to the SEDIT command.
   *  This is its own method to be called by inline SEDIT
   *  @param inputString  Input string
   *  @param M            Midas context
   *  @param MA           Args
   *  @param nextArgIndex Number of arguments
   *  @param dbg          True if debug mode
   *  @return the resulting value
   *  @since 3.9.2 Moved here from intr.Sedit to support transition to JDK modulues
   */
  @InternalUseOnly
  public static Object processSeditCommandLine (String inputString, Midas M, Args MA,
                                        int nextArgIndex, boolean dbg) {

    // Initialize output
    Object obj = null;
    String currString = inputString;
    int inputStringArgNum = 1;

    // Get parameters
    int argCount = nextArgIndex;
    boolean clean  = MA.getState("/CLEAN",true);
    boolean strip  = MA.getState("/STRIP",false);
    boolean legacy = MA.getState("/LEGACY",MA.getState("/SEDITLEGACY",true));  //// TODO: Change default to FALSE in 3.5.x?

    // get base
    int base = MA.getL("/BASE", (MA.getState("/ONEBASE") || MA.getState("/OB"))? 1 : 0 );

    while (argCount <= MA.getNArgs()) {

      String func    = MA.getU(argCount++);

      if (dbg) System.out.println("SEDIT: argc="+argCount+" str='"+currString+
               "' func="+func+" P1='"+MA.getU(argCount)+"' P2='"+MA.getU(argCount+1)+"'");

      // Place functions in alphabetical order
      if (func.startsWith("APPEND")) {
        String p1 = MA.getCS(argCount++);
        obj = currString + p1;
      }
      else if (func.startsWith("BETW") && legacy) {
        int imin = MA.getL(argCount++);
        int imax = MA.getL(argCount++);
        // deprecation warning added for Bug 1374 as we move to make SEDIT behave like X-Midas.
        if ( seditWarnCount++<numSeditWarns && ( (base==1 && imin<=0) || imin<0 || (base==1 && imax<=0) || imax<0) ) {
          M.deprecate("SEDIT: As of NeXtMidas 3.1.2, the current default behaviour of SEDIT BETWEEN when using "+
                       " negative numbers (and zero when one-based) will change in a future release. "+
                       "See the SEDIt explain file for details of new behaviour.");
        }
        ///////  THE CODE BELOW FOR THE SPECIAL CASE AND NEGATIVE NUMBERS DOES NOT WORK AND IS DEPRECATED AS OF 3.1.2  //////
        // Special case -- 0 indicates end of string for one-base
        if (imin >= 0 && imin < currString.length()) imin = imin - base;
        else if (base == 1 && imin == 0) imin = currString.length();
        if (imax >= 0 && imax <= currString.length()) imax = imax - base;
        else if (base == 1 && imax == 0) imax = currString.length();
        obj = StringUtil.substring(currString, imin, imax);

      }
      else if (func.startsWith("BETW")) {
        // support for negative value indexing added in 3.1.2 and will one day be default (Bug 1374)
        int i1 = MA.getL(argCount++); i1-=base;
        int i2 = MA.getL(argCount++); i2-=base;
        obj = StringUtil.between(currString, i1, i2);
      }
      else if (func.startsWith("BSEA")) {
        String p1 = MA.getU(argCount++);
        int retVal = currString.lastIndexOf(p1);
        if (retVal >= 0) retVal = retVal + base;
        obj = new Data( retVal );
      }
      else if (func.startsWith("CARETS")) {
        obj = MA.evaluateCarets(currString);
      }
      else if (func.equals    ("CLEAN")) {
        obj = StringUtil.cleanString(currString);
      }
      else if (func.startsWith("ELEM")) {
        int lval = MA.getL(argCount++);
        String p2 = MA.getU(argCount++);
        obj = StringUtil.getWordByElement(currString,lval,p2);
      }
      else if (func.startsWith("ENDS")) {
        String p1 = MA.getCS(argCount++);
        obj = currString.endsWith(p1);
      }
      else if (func.equals    ("EXT")) {
        obj = FileUtil.ext(currString);
      }
      else if (func.startsWith("GSUB")) {
        String p1 = MA.getU(argCount++);
        String p2 = MA.getU(argCount++);
        obj = StringUtil.subs(currString,p1,p2,true);
      }
      else if (func.equals    ("HEAD") || func.equals ("PATH")) {
        obj = FileUtil.head(currString);
      }
      else if (func.equals    ("JOIN")) {
        Object tempObj = MA.getO(inputStringArgNum); // Go back and pick as object
        String p1 = MA.getU(argCount++);
        obj = StringUtil.join((String [])tempObj,p1);
      }
      else if (func.equals    ("KEY")) {
        String p1 = MA.getU(argCount++);
        String p2 = MA.getU(argCount++);
        obj = StringUtil.getKeyValue (currString,p1,p2);
      }
      else if (func.startsWith("LEN") && legacy) {
        int index = MA.getL(argCount++,-1);
        if (index >= 0) index = index-base;
        obj = new Data(StringUtil.length(currString, index) );
      }
      else if (func.startsWith("LEN")) {
        int index = MA.getL(argCount++);
        obj = new Data(StringUtil.lengthElement(currString, index) );
      }
      else if (func.startsWith("LOCA")) {
        obj = currString.toLowerCase();
      }
      else if (func.startsWith("MASK")) {
        String p1 = MA.getU(argCount++);
        int retval = Parser.mask(currString,p1,0);
        obj = new Data( retval );
      }
      else if (func.startsWith("NEL")) {
        String p1 = MA.getU(argCount++);
        if (p1.equals("")) p1 = ",";
        obj = new Data (StringUtil.nelements(currString,p1));
      }
      else if (func.equals    ("NFORM")) {
        double dval = MA.getD(inputStringArgNum); // Go back and pick as double
        String p1 = MA.getU(argCount++);
        obj = StringUtil.nform(dval,p1);
      }
      else if (func.startsWith("NSEA")) {
        String p1 = MA.getU(argCount++);
        int p2 = MA.getL(argCount++);
        obj = new Data(StringUtil.getOccurrenceOf(currString, p1, p2)+base);
      }
      else if (func.startsWith("PAD")) {
        // Get the arg as a string and int to handle n or +n syntax
        String newLenStr = MA.getCS(argCount);
        int newLen = MA.getL(argCount++);
        if (newLenStr !=null && newLenStr.length() > 1 && newLenStr.charAt(0) == '+') {
          newLen = currString.length() + Integer.parseInt(newLenStr.substring(1));
        }
        String padStr = MA.getCS(argCount++);
        obj = StringUtil.pad(currString, newLen, func, padStr);
      }
      else if (func.equals    ("PARSE")) {
        int lval = MA.getL(argCount++);
        String p2 = MA.getU(argCount++);
        // since delim is optional, decide if it is a new function instead
        // all delimiters are 1 char, all functions are > 1 char
        if (p2.length()>1) { p2=""; argCount--; }
        obj = StringUtil.parse(currString,lval,p2,clean);
      }
      else if (func.startsWith("PARSEAL")) {
        int lval = MA.getL(argCount++);
        String p2 = MA.getU(argCount++);
        obj = StringUtil.parseAll(currString,lval,p2,clean);
      }
      else if (func.startsWith("PARSEAR")) {
        obj = Args.parseCommand(M,currString);
      }
      else if (func.startsWith("PARSEDI")) {
        String p1 = MA.getU(argCount++);
        int retval = StringUtil.getParsedElementIndex(currString,p1,clean);
        if (retval >= 0) retval += base;
        obj = new Data( retval );
      }
      else if (func.startsWith("PREPEND")) {
        String p1 = MA.getCS(argCount++);
        obj = p1 + currString;
      }
      else if (func.equals    ("RANGE") && legacy) {
        int i1 = MA.getL(argCount++);
        int i2 = MA.getL(argCount);
        String p2 = MA.getU(argCount++);
        if (p2.equals("")) { i2 = currString.length(); }
        if ( base == 1 && (i1 == 0 || i2 == 0)) {
          if (i1 == 0) i1 = currString.length();
          if (i2 == 0) i2 = currString.length();
         // M.deprecate("Use of 0 to indicate the end of a string is deprecated, "
         //            +" is deprecated, use string.length() or leave off end index");
        }
        if (i1 >= 0 && i1 <= currString.length()) i1 = i1 - base;
        if (i2 >= 0 && i2 <  currString.length()) i2 = i2 - base;
        if (seditWarnCount++<numSeditWarns && (base==0 && (i1<0 || i2<0))) {
          M.deprecate("SEDIT: As of NeXtMidas 3.1.2, behaviour of negative numbers in RANGE function "+
                      "will change in a future release. See SEDIT explain file for details.");
        }
        obj = StringUtil.range(currString,i1,i2);
      }
      else if (func.equals    ("RANGE")) {
        // support for negative value indexing added in 3.1.2 and will one day be default (Bug 1374)
        int i1 = MA.getL(argCount++); i1-=base;
        int i2 = MA.getL(argCount++); i2-=base;
        obj = StringUtil.getStringInRange(currString, i1, i2);
      }
      else if (func.equals    ("ROOT")) {
        obj = FileUtil.root(currString);
      }
      else if (func.startsWith("SEAR")) {
        String p1 = MA.getU(argCount++);
        int retVal = currString.indexOf(p1);
        if (retVal >= 0) retVal += base;
        obj = new Data( retVal );
      }
      else if (func.startsWith("SEL") && legacy) {
        String p1 = MA.getU(argCount++);
        int retVal = -1;
        if (seditWarnCount++<numSeditWarns && base!=1) M.deprecate("SEDIT: As of NeXtMidas 3.1.2, the current behaviour of SEDIT SELECT is deprecated. "+
                      "SEDIT SELECT will become 1-index based in a future release");
        retVal = StringUtil.select(currString,p1);
        if (retVal >= 0) retVal = retVal+base;
        obj = new Data( retVal );
      }
      else if (func.startsWith("SEL")) { // change to allow 1-based index in 3.1.2 for Bug 1374
        String p1 = MA.getU(argCount++);
        int retVal = StringUtil.selectElement(currString,p1);
        obj = new Data( retVal );
      }
      else if (func.startsWith("SPLIT")) {
        String p1 = MA.getU(argCount++);
        obj = StringUtil.split (currString,p1);
      }
      else if (func.startsWith("STARTS")) {
        String p1 = MA.getCS(argCount++);
        obj = currString.startsWith(p1);
      }
      else if (func.equals    ("STRIM")) {
        obj = currString.trim();
      }
      else if (func.equals    ("STRIP")) {
        obj = StringUtil.stripQuotes(currString);
      }
      else if (func.startsWith("SUB")) {
        String p1 = MA.getU(argCount++);
        String p2 = MA.getU(argCount++);
        obj = StringUtil.subs(currString,p1,p2,false);
      }
      else if (func.equals    ("TAIL")) {
        obj = FileUtil.tail(currString);
      }
      else if (func.equals    ("TRIM")) {
        String p1 = MA.getU(argCount++);
        String p2 = MA.getU(argCount++);
        obj = StringUtil.trim (currString,p1,p2);
      }
      else if (func.startsWith("UPCA")) {
        obj = currString.toUpperCase();
      }
      else if (func.equals    ("WORD")) {
        String p1 = MA.getU(argCount++);
        obj = StringUtil.getWordByString (currString, p1);
      }
      else {
        M.warning("Unsupported function: "+func);
      }

      if (strip) obj = StringUtil.stripQuotes(obj.toString());

      // Make sure the caller gets an empty string in the case of
      // a null pointer, or the word 'null'
      obj = StringUtil.convertNullsToEmpty (obj);

      currString = obj.toString();
    }

    return obj;
  }

  /** Performs an in-line TEST.
      @param ref  The midas context or other reference.
      @param test The test in the syntax of <code>"TEST(...)"</code>.
      @return The results of the test.
      @since NeXtMidas 2.5.0
      @deprecated since NeXtMidas 3.5.0 use {@link #inLineTest(MidasReference, String)}
   */
  @Deprecated
  public static boolean inLineTest(Object ref, String test) {
    if (ref instanceof MidasReference) {
      return Args.logicalTest((MidasReference) ref, test);
    } else {
      return Args.logicalTest(Convert.ref2Midas(ref), test);
    }
  }

  /** Performs an in-line TEST.
      @param ref  The midas context or reference.
      @param test The test in the syntax of <code>"TEST(...)"</code>.
      @return The results of the test.
      @since NeXtMidas 3.5.0
   */
  public static boolean inLineTest(MidasReference ref, String test) {
    return Args.logicalTest(ref, test);
  }

  /** Performs an in-line CALC.
      @param ref  The midas context or other reference.
      @param clac The equation in the syntax of <code>"CALC(...)"</code>.
      @return The results of the calculation.
      @since NeXtMidas 2.5.0
      @deprecated since NeXtMidas 3.5.0 use {@link #inLineCalc(MidasReference, String)}
   */
  @Deprecated
  public static double[] inLineCalc(Object ref, String clac) {
    if (ref instanceof MidasReference) {
      return inLineCalc((MidasReference) ref, clac);
    } else {
      return inLineCalc(ref2Midas(ref), clac);
    }
  }

  /** Performs an in-line CALC.
      @param ref  The midas context or reference.
      @param clac The equation in the syntax of <code>"CALC(...)"</code>.
      @return The results of the calculation.
      @since NeXtMidas 3.5.0
   */
  public static double[] inLineCalc(MidasReference ref, String clac) {
    Midas M = Convert.ref2Midas(ref);
    Parser p = new Parser(clac.substring(5,clac.length()-1)+",=InLineCalc", true);
    int narg = p.elements();
    ArgStack calc = new ArgStack (narg,narg,(char)DOUBLE,1,0);
    for (int i=1; i<=narg; i++) calc.parseNext( p.get(i),M );
    calc.parseDone();
    calc.process();
    return calc.result();
  }

  /** Internal conversion of String to double for a results name */
  private static int s2d_r (byte[] buffer, int index, int size, double[] dval, Object ref) {
    Results rt = ref2Results(ref);
    int lr = Results.validNameLength(buffer,index,size);
    if (lr<=0) return 0;
    String name = new String(buffer,index,lr);
    if (rt==null) return s2d_i(dval, name, lr);
    if (name.startsWith("TEST(")) {              // inLine TEST
      dval[0] = (inLineTest(ref,name))? 1 : 0;
    }
    else if (name.startsWith("CALC(")) {        // inLine CALC
      dval[0] = inLineCalc(ref,name)[0];
    }
    else {                                      // assume RESULT
      Object obj =  rt.get(name);
      if (obj==null) return s2d_i(dval, name, lr);          // Result not found, try NaN & Inf
      try { dval[0] = Convert.o2d(obj); }
      catch (Exception e) { return s2d_i(dval, name, lr); } // Can't convert to double, try NaN & Inf
    }
    return lr;
  }

  /** Internal conversion of String to double within parenthesis */
  private static int s2d_p (byte[] buffer, int index, int size,
                            double dval[], Object ref) {
    int i,ip;
    for (ip=1, i=index+1; ip>0 && i<size; i++) {
      if (buffer[i]==RPAREN) ip--;
      if (buffer[i]==LPAREN) ip++;
    }
    // ensure we do not evaluate strings in inner parens as numbers (Bug 2314)
    int x = s2d (buffer, index+1, i-1, dval, ref);
    return (x==-1)? x : i-index;
  }

  /** Internal conversion of String to double for a switch value.

      @param buffer The string (in a byte buffer).
      @param index  The index into the buffer to start reading from.
      @param size   The size of the string.
      @param dval   The numeric buffer to put the value in (at index 0).
      @param ref    The reference object to get switches from (can be an instance of Args or
                    Command), if this is null then the method will act as a NOOP and return 0.
      @return The length of the switch or 0 if it is not a switch.
   */
  private static int s2d_s (byte[] buffer, int index, int size, double dval[], Object ref) {
    Args args = ref2Args(ref);
    if (args==null) return 0;
    int switchLength = Results.validNameLength(buffer,index+1,size) + 1;
    if (switchLength<=0) return 0;
    String name = new String(buffer,index,switchLength);
    // assume this is a filename if it starts with slash and has another embedded slash
    if (index==0 && (buffer.length > switchLength+1) && (buffer[switchLength] == '/')) return 0;
    try {
      dval[0] = args.getD(name,-1.0);
    } catch(Exception e) {
      throw new MidasException("Error converting '"+name+"="+args.getU(name)+"' to DOUBLE.", e);
    }
    return switchLength;
  }

  /**
    Internal conversion of String to BigDecimal for a results name.

    @param buffer The string (in a byte buffer).
    @param index  The index into the buffer to start reading from.
    @param size   The size of the string.
    @param dval   The numeric buffer to put the value in (at index 0).
    @param ref    The reference object to get switches from (can be an instance of Args or
                  Command), if this is null then the method will act as a NOOP and return 0.
    @return The length of the result or 0 if it is not a switch.
    @since NeXtMidas 3.5.1 for Bug 2833.
 */
  private static int s2BigDecimal_result (byte[] buffer, int index, int size, BigDecimalValue bdVal, Object ref) {
    Results rt = ref2Results(ref);
    int lr = Results.validNameLength (buffer,index,size);
    if (lr<=0) return 0;
    String name = new String(buffer,index,lr);
    if (rt==null) return NO_VALID_VALUE_FOUND;
    if (name.startsWith("TEST(")) {              // inLine TEST
      bdVal.setValue(new BigDecimal((inLineTest(ref,name))? 1 : 0));  // gets boolean value
    }
    else if (name.startsWith("CALC(")) {        // inLine CALC
      bdVal.setValue(new BigDecimal(inLineCalc(ref,name)[0]));        // *** gets double*** TODO:
    }
    else {                                      // assume RESULT
      Object obj =  rt.get(name);
      if (obj==null) return NO_VALID_VALUE_FOUND;          // Result not found, try NaN & Inf
      try { bdVal.setValue(Convert.o2BigDecimal(obj)); }
      catch (Exception e) { return NO_VALID_VALUE_FOUND; } // Can't convert to double, & BigDeicimal not support NaN & Inf
    }
    return lr;
  }

  /** Internal conversion of String to BigDecimal within parenthesis.
      @param buffer The string (in a byte buffer).
      @param index  The index into the buffer to start reading from.
      @param size   The size of the string.
      @param dval   The numeric buffer to put the value in (at index 0).
      @param ref    The reference object to get switches from (can be an instance of Args or Command),
                    if this is null then the method will act as a NOOP and return 0.
      @since NeXtMidas 3.5.1 for Bug 2833.
   */
  private static int s2BigDecimal_paren (byte[] buffer, int index, int size, BigDecimalValue bdVal, Object ref) {
    int i,ip;
    for (ip=1, i=index+1; ip>0 && i<size; i++) {
      if (buffer[i]==RPAREN) ip--;
      if (buffer[i]==LPAREN) ip++;
    }
    // ensure we do not evaluate strings in inner parens as numbers (Bug 2314)
    int x = s2BigDecimal (buffer, index+1, i-1, bdVal, ref);
    return (x==-1)? x : i-index;
  }

  /** Internal conversion of String to BigDecimal for a switch value.
      @param buffer The string (in a byte buffer).
      @param index  The index into the buffer to start reading from.
      @param size   The size of the string.
      @param bdVal  The numeric buffer to put the value in (at index 0).
      @param ref    The reference object to get switches from (can be an instance of Args or
                    Command), if this is null then the method will act as a NOOP and return 0.
      @return The length of the switch or 0 if it is not a switch.
      @since NeXtMidas 3.5.1 for Bug 2833.
   */
  private static int s2BigDecimal_switch (byte[] buffer, int index, int size, BigDecimalValue bdVal, Object ref) {
    Args args = ref2Args(ref);
    if (args==null) return 0;
    int switchLength = Results.validNameLength(buffer,index+1,size) + 1;
    if (switchLength<=0) return 0;
    String name = new String(buffer,index,switchLength);
    // assume this is a filename if it starts with slash and has another embedded slash
    if (index==0 && (buffer.length > switchLength+1) && (buffer[switchLength] == '/')) return 0;
    try {
      bdVal.setValue(new BigDecimal(args.getD(name,-1.0)));
    } catch(Exception e) {
      throw new MidasException("Error converting '"+name+"="+args.getU(name)+"' to DOUBLE.", e);
    }
    return switchLength;
  }

  /** Convert double to a string with VIS or Visual format
    @param dval double to convert
    @return String version of number
   */
  public static String d2s (double dval) {
    return d2s (dval, 0, "VIS");
  }

  /** Convert int (L:) to a string.  This is here for completeness, of course,
      in Java this is easily done with the + operator.
      @param lval number to convert
      @return String version of number
   */
  public static String l2s (int lval) {
    return ""+lval;
  }

  /** Convert int (L:) to a HEXIDECIMAL string
     @param lval number to convert
     @return Hexadecimal string version of number
   */
  public static String l2x (int lval) {
    byte[] buf = new byte[4];
    packL(buf,0,lval);
    return bb2hex(buf,0,4);
  }

  /** Format a double value into various string formats
      @param dval double to convert
      @param exp Number of digits in exponent
      @param format Midas format designator (VIS,HMS,...) or Java format string
      @return String version of double
   */
  public static String d2s (double dval, int exp, String format) {
    if (format.equals("VIS")) return Double.toString(dval);
    if (exp==-1) exp = d2s_exp (dval,dval);
    if (exp!=0) dval *= Math.pow(10.0,-exp);
    int fb = format.indexOf('#');
    int fe = format.lastIndexOf('#');
    int fp = format.indexOf('.');
    int fsig = fe-fp;
    if (fp<0) { fp=fe+1; fsig=0; }
    double round = Math.pow(10.0,-fsig)*.5;
    if (dval>0) dval+=round;
    if (dval<0) dval-=round;
    String ts = nf.format(dval);
    int tp = ts.indexOf('.');
    char[] fs = format.toCharArray();
    for (int fi=0; fi<fs.length; fi++) {
      if (fs[fi]=='#') { int ti=fi-fp+tp;
        if (ti>=0) fs[fi]=ts.charAt(ti); else fs[fi]=' '; }
      else if (fs[fi]=='?') fs[fi] = d2s_expchar(exp);
    }
    return new String(fs);
  }

  /** Get the exponential character corresponding to the number
      of digits in the exponent.
        <pre>
         Exp  Char
         ---  ----
          0    ' '
          3     k
          6     M
          9     G
         12     T
         -2     c
         -3     m
         -6     u
         -9     p
       </pre>
     If it is none of these a ? is returned.
     @param exp Number of digits in exponent
     @return The exponential character or ?
   */
  public static char d2s_expchar (int exp) {
    char c;
    switch (exp) {
      case  0: c=' '; break;
      case  3: c='k'; break;
      case  6: c='M'; break;
      case  9: c='G'; break;
      case 12: c='T'; break;
      case -2: c='c'; break;
      case -3: c='m'; break;
      case -6: c='u'; break;
      case -9: c='p'; break;
      default: c='?';
    }
    return c;
  }

  /** Get the best engineering exponent for a range of numbers
     @param dmin Range min
     @param dmax Range max
     @return Number of exponent digits (engineering)
   */
  public static int d2s_exp (double dmin, double dmax) {
    double absmax; int kengr;
    absmax = Math.max( Math.abs(dmin),Math.abs(dmax) );
    if (absmax == 0.0) return 0;
    kengr = (int)( 0.1447648 * Math.log(absmax) +.5);
    if (absmax<1.0) kengr--;
    return kengr*3;
  }

  /** Determine if two representations have same Endian format
      @param repa First representation
      @param repb Second representation
      @return true if same Endian format, false otherwise
   */
  public static boolean sameEndian (byte repa, byte repb) {
    if (repa==repb) return true;
    boolean biga = (repa==IEEE);
    boolean bigb = (repb==IEEE);
    return (biga==bigb);
  }

  /** return whether specified native format is BigEndian
      @param rep the format to check.
      @return true if Big-Endian, false otherwise.
   */
  public static boolean isBigEndian (byte rep) {
    return (rep==IEEE);
  }

  /** Convert reference types to Args, Midas, Results or Command object
      for resolving named values from results, switches, etc.
      @param ref Reference object
      @return Results object
   */
  public static Results ref2Results (Object ref) {
    Results rt=null;
         if (ref instanceof Args) rt=((Args)ref).M.results;
    else if (ref instanceof Midas) rt=((Midas)ref).results;
    else if (ref instanceof Results) rt=(Results)ref;
    else if (ref instanceof Command) rt=((Command)ref).M.results;
    return rt;
  }

  /** Convert reference type to a Midas object (if it is a {@link MidasReference}).
      @param ref Reference object
      @return Midas reference from ref or null if ref is not a MidasReference
   */
  public static Midas ref2Midas (Object ref) {
    if (ref instanceof MidasReference) { // since 3.5.1 allow all MidasReference implementations
      return ((MidasReference)ref).getMidas();
    }
    return null;
  }

  /** Convert reference object to Midas object (any class that implements {@link MidasReference}),
      with fall-back to global Shell's shared Midas context.
      @param ref Reference object
      @return always returns Midas context based on ref (when not null),
              otherwise, returns {@link Shell#getSharedMidasContext()}
      @since NeXtMidas 3.5.0
   */
  public static Midas ref2Midas (MidasReference ref) {
    if (ref == null) {    // if null, return Shell's shared midas context
      return Shell.getSharedMidasContext();
    }
    Midas midas = ref.getMidas();
    if (midas == null) {  // if still null, return Shell's shared midas context
      midas = Shell.getSharedMidasContext();
    }
    return midas;
  }

  /** Type cast a ref object to MidasReference if and only if it is a {@link MidasReference}.
   *  <pre>This is needed to return a Command object (which implements MidasReference) as is because:
   *  1. For command to work properly in certain pipe restarts scenarios.
   *  2. For proper picking of command line args in BaseFile and DataFile, etc.
   *  </pre>
   *  @param ref Reference object
   *  @return if ref is a MidasReference then return it with type cast, otherwise returns null
   *  @since NeXtMidas 3.7.0
   */
  @InternalUseOnly("Since added - should only be used by BaseFile and DataFile")
  public static MidasReference ref2MidasReference (Object ref) {
    if (ref instanceof MidasReference) {
      return (MidasReference) ref;
    }
    return null;
  }

  /** Convert object reference to String.
      @param ref Reference object
      @return A description of the reference object. For a command this is either the name of the
              command (intrinsic) or the ID of the command (primitive).
      @since NeXtMidas 2.5.0
   */
  public static String ref2String (Object ref) {
    if (ref instanceof Command) {
      Command cmd = (Command)ref;
      String str = cmd.getID();                   // for Primitives
      if (str.length() == 0) str = cmd.getName(); // Common for intrinsics (like STATUS)
      return str;
    }
    else if (ref != null) return ref.toString();
    return null;
  }

  /** @return convert object reference to Args if possible, otherwise null
      @since NeXtMidas 3.5.1
   */
  static Args ref2Args (Object ref) {
    if (ref instanceof Args) return (Args)ref;
    else if (ref instanceof Command) return ((Command)ref).args;
    return null;
  }

  /** Convert data representation of a byte array. This differs from
      {@link #rep(byte[],int,byte,int,byte,byte)} in that it takes in a (possibly non-scalar)
      format and converts all elements of that format.
      @param buf  byte array to convert
      @param off  Offset into byte array
      @param form The format of the elements
      @param n    Number of elements to convert
      @param repa Input representation
      @param repb Output representation
      @return Number of elements converted
      @throws IllegalArgumentException If the format is "NH" or "KW".
      @since NeXtMidas 2.5.1
   */
  public static int rep (byte[] buf, int off, String form, int n, byte repa, byte repb) {
    if (form.equals("NH")) throw new IllegalArgumentException("Can not convert rep for NH data.");
    if (form.equals("KW")) throw new IllegalArgumentException("Can not convert rep for KW data.");

    byte type = (byte)form.charAt(1);
    int  num  = n * Data.getSPA(form.charAt(0));

    return rep(buf, off, type, num, repa, repb);
  }

  /** Convert data representation of a byte array
      @param buf  byte array to convert
      @param off  Offset into byte array
      @param type Type of data in byte array
      @param n    Number of scalar elements to convert
      @param repa Input representation (e.g. Convert.IEEE, Convert.EEEI, Convert.VAX, Convert.CRAY)
      @param repb Output representation (e.g. {@link Convert#IEEE}, {@link Convert#EEEI}, {@link Convert#VAX})
      @return Number of elements converted
   */
  public static int rep (byte[] buf, int off, byte type, int n, byte repa, byte repb) {
    if (repa!=repb)
    switch (type) {
      case BYTE:   break;
      case ASCII:  break;
      case ASCII256: break; // 3.9.4 NXM-3570
      case STRING: break;
      case JINT:   if ( (repa==IEEE) != (repb==IEEE)) swapJ (buf,off,n,repa,repb); break;
      case INT:    if ( (repa==IEEE) != (repb==IEEE)) swap2 (buf,off,n); break;
      case LONG:   if ( (repa==IEEE) != (repb==IEEE)) swap4 (buf,off,n); break;
      case XLONG:  if ( (repa==IEEE) != (repb==IEEE)) swap8 (buf,off,n); break;
      case PACKED: if ( (repa==IEEE) != (repb==IEEE)) swap1 (buf,off,n); break;
      case FLOAT: switch (repa) {
        case VAX: switch (repb) {
          case EEEI: f_vax2eeei (buf,off,n); break;
          case IEEE: f_vax2ieee (buf,off,n); break;
        } break;
        case IEEE: switch (repb) {
          case VAX: f_ieee2vax (buf,off,n); break;
          case EEEI: swap4 (buf,off,n); break;
        } break;
        case EEEI: switch (repb) {
          case VAX: f_eeei2vax (buf,off,n); break;
          case IEEE: swap4 (buf,off,n); break;
        } break;
      } break;
      case DOUBLE: switch (repa) {
        case VAX: switch (repb) {
          case EEEI: d_vax2eeei (buf,off,n); break;
          case IEEE: d_vax2ieee (buf,off,n); break;
        } break;
        case IEEE: switch (repb) {
          case VAX: d_ieee2vax (buf,off,n); break;
          case EEEI: swap8 (buf,off,n); break;
        } break;
        case EEEI: switch (repb) {
          case VAX: d_eeei2vax (buf,off,n); break;
          case IEEE: swap8 (buf,off,n); break;
        } break;
      } break;
    }
    return n;
  }

  /** Convert data representation of a native pointer to buffer.
      This method requires native code.
      @param lbuf Native pointer to buffer
      @param off  Offset into <code>lbuf</code>
      @param type Type of data in buffer
      @param n    Number of scalar elements to convert
      @param repa Input representation
      @param repb Output representation
      @return Number of elements converted
      @since NeXtMidas 2.7.0
   */
  static int rep (long lbuf, int off, byte type, int n, byte repa, byte repb) {
    if (repa!=repb) Native.rep(lbuf, off, type, n, repa, repb);
    return n;
  }

  /** convert data representation for a Subrecord of Interest (SOI)
      for multiple records. This method requires native code.
      @param lbuf Native pointer to buffer containing records
      @param off  Offset into <code>lbuf</code> of 1st SOI
      @param type Type of data in SOI
      @param n    Number of scalars per SOI
      @param repa Input representation
      @param repb Output representation
      @param nn   Number of records to convert
      @param inc  Increment between records (in bytes)
      @return Number of records converted
      @since NeXtMidas 2.7.0
   */
  static int rep (long lbuf, int off, byte type, int n,
                         byte repa, byte repb, int nn, int inc) {
    if (repa!=repb) Native.repsoi(lbuf, off, type, n, repa, repb, nn, inc);
    return n;
  }

  /** convert data representation for a Subrecord of Interest (SOI)
      for multiple records
      @param buf  byte array containing records
      @param off  Offset into byte array of 1st SOI
      @param type Type of data in SOI
      @param n    Number of scalars per SOI
      @param repa Input representation
      @param repb Output representation
      @param nn   Number of records to convert
      @param inc  Increment between records (in bytes)
      @return Number of records converted
   */
  public static int rep (byte[] buf, int off, byte type, int n,
                         byte repa, byte repb, int nn, int inc) {
    if (repa!=repb)
    switch (type) {
      case BYTE:   break;
      case ASCII:  break;
      case ASCII256:  break; // NXM-3570
      case STRING: break;
      case INT:    if ( (repa==IEEE) != (repb==IEEE)) for (;--nn>=0;off+=inc) swap2 (buf,off,n); break;
      case LONG:   if ( (repa==IEEE) != (repb==IEEE)) for (;--nn>=0;off+=inc) swap4 (buf,off,n); break;
      case XLONG:  if ( (repa==IEEE) != (repb==IEEE)) for (;--nn>=0;off+=inc) swap8 (buf,off,n); break;
      case PACKED: if ( (repa==IEEE) != (repb==IEEE)) for (;--nn>=0;off+=inc) swap1 (buf,off,n); break;
      case FLOAT: switch (repa) {
        case VAX: switch (repb) {
          case EEEI: for (;--nn>=0;off+=inc) f_vax2eeei (buf,off,n); break;
          case IEEE: for (;--nn>=0;off+=inc) f_vax2ieee (buf,off,n); break;
        } break;
        case IEEE: switch (repb) {
          case VAX:  for (;--nn>=0;off+=inc) f_ieee2vax (buf,off,n); break;
          case EEEI: for (;--nn>=0;off+=inc) swap4 (buf,off,n); break;
        } break;
        case EEEI: switch (repb) {
          case VAX:  for (;--nn>=0;off+=inc) f_eeei2vax (buf,off,n); break;
          case IEEE: for (;--nn>=0;off+=inc) swap4 (buf,off,n); break;
        } break;
      } break;
      case DOUBLE: switch (repa) {
        case VAX: switch (repb) {
          case EEEI: for (;--nn>=0;off+=inc) d_vax2eeei (buf,off,n); break;
          case IEEE: for (;--nn>=0;off+=inc) d_vax2ieee (buf,off,n); break;
        } break;
        case IEEE: switch (repb) {
          case VAX:  for (;--nn>=0;off+=inc) d_ieee2vax (buf,off,n); break;
          case EEEI: for (;--nn>=0;off+=inc) swap8 (buf,off,n); break;
        } break;
        case EEEI: switch (repb) {
          case VAX:  for (;--nn>=0;off+=inc) d_eeei2vax (buf,off,n); break;
          case IEEE: for (;--nn>=0;off+=inc) swap8 (buf,off,n); break;
        } break;
      } break;
    }
    return n;
  }


  /** Reverse the bits within the specified byte array.
      @param buf  the array of bytes to be reversed.
      @param n    the size of the array to be reversed.
   */
  public static void bitrev (byte[] buf, int n) {
    for (int i=0; i<n; i++) buf[i] = bitrev[ buf[i]&0xFF ];
  }

  /** Reverse the bits within the specified integer array.
      @param buf  the array of integers to be reversed.
      @param n    the size of the array to be reversed.
   */
  public static void bitrev (int[] buf, int n) {
    for (int i=0; i<n; i++) buf[i] = bitrev(buf[i]);
  }

  /** Bit reverse
   *  @param a the integer to reverse.
   *  @return integer with its bits reversed
   */
  public static int bitrev (int a) {
    int b = (((int)bitrev[(a>>0)&0xFF]&0xFF)<<24)
          | (((int)bitrev[(a>>8)&0xFF]&0xFF)<<16)
          | (((int)bitrev[(a>>16)&0xFF]&0xFF)<<8)
          | (((int)bitrev[(a>>24)&0xFF]&0xFF)<<0);
    return b;
  }

  /** Generate bit reversed lookup table (LUT) buffer for Big/Little Endian Packet
     conversions to reverse the bit order within a byte. */
  private static byte[] genbitrev() {
    byte[] buf = new byte[256];
    for (int j,i=0; i<256; i++) { j = ((i&0x1)<<7) | ((i&0x2)<<5) | ((i&0x4)<<3) | ((i&0x8)<<1) |
                                      ((i&0x10)>>1) | ((i&0x20)>>3) | ((i&0x40)>>5) | ((i&0x80)>>7);
      buf[i] = (byte)(j&0xFF);
    }
    return buf;
  }

  /** LUT buffer for U-Law conversions */
  private static short[] genulaw() {
    short[] table = new short[256];
    for (int i=0; i<256; i++) {
      int j = i-128;
      int x = j^127;
      int S = 1-(2*((x&128)/128));
      int L = (x&112)/16;
      int V = x&15;
      table[i] = (short)Math.round(((S*(Math.pow(2,L)*(2*V+33)-33)*32767.0)/8031.0));
    }
    return table;
  }

  /** Swap bits in each byte in a buffer (for Endian conversions)
      @param buf byte array to swap
      @param off Offset into buf
      @param n Number of elements to swap
   */
  public static void swap1 (byte[] buf, int off, int n) {
    /* (n>>3 )is same as dividing n by 8 (for number of bits per byte) */
    for (int i=0; i<(n>>3); i++,off++) buf[off] = bitrev[ buf[off]&0xFF ];
  }

  /** Swap two bytes in a buffer (for Endian conversions)
      @param buf byte array to swap
      @param off Offset into buf
      @param n Number of elements to swap
   */
  public static void swap2 (byte[] buf, int off, int n) {
    byte b;
    for (int i=0; i<n; i++) {
      b=buf[off]; buf[off++]=buf[off]; buf[off++]=b;
    }
  }

  /** Swap four bytes in a buffer (for Endian conversions)
      @param buf byte array to swap
      @param off Offset into buf
      @param n Number of elements to swap
   */
  public static void swap4 (byte[] buf, int off, int n) {
    byte b;
    for (int i=0; i<n; i++,off+=4) {
      b=buf[off+0]; buf[off+0]=buf[off+3]; buf[off+3]=b;
      b=buf[off+1]; buf[off+1]=buf[off+2]; buf[off+2]=b;
    }
  }

  /** Swap eight bytes in a buffer (for Endian conversions)
      @param buf byte array to swap
      @param off Offset into buf
      @param n Number of elements to swap
   */
  public static void swap8 (byte[] buf, int off, int n) {
    byte b;
    for (int i=0; i<n; i++,off+=8) {
      b=buf[off+0]; buf[off+0]=buf[off+7]; buf[off+7]=b;
      b=buf[off+1]; buf[off+1]=buf[off+6]; buf[off+6]=b;
      b=buf[off+2]; buf[off+2]=buf[off+5]; buf[off+5]=b;
      b=buf[off+3]; buf[off+3]=buf[off+4]; buf[off+4]=b;
    }
  }

  /** Swap twelve bytes in a buffer (for Endian conversions)
      @param buf  byte array to swap
      @param off  Offset into buf
      @param n    Number of elements to swap
      @param repa Input representation
      @param repb Output representation
   */
  public static void swapJ (byte[] buf, int off, int n, byte repa, byte repb) {
    int i, m=n/2*3;
    byte b;
    if (repa==IEEE && repb!=IEEE) for (i=off; i<off+m; i+=3) {
      b = buf[i+1];
      buf[i+1] = (byte)( (buf[i+2]<<4) | ((buf[i]>>4)&0xF) );
      buf[i+2] = (byte)( (b<<4) | ((buf[i+2]>>4)&0xF) );
      buf[i]   = (byte)( (buf[i]<<4) | ((b>>4)&0xF) );
    }
    if (repa!=IEEE && repb==IEEE) for (i=off; i<off+m; i+=3) {
      b = buf[i+1];
      buf[i+1] = (byte)( (buf[i]<<4) | ((buf[i+2]>>4)&0xF) );
      buf[i]   = (byte)( (b<<4) | ((buf[i]>>4)&0xF) );
      buf[i+2] = (byte)( (buf[i+2]<<4) | ((b>>4)&0xF) );
    }
  }

  /** Convert float buffer (in a byte array) from VAX to IEEE
      @param buf byte array of float data to convert in-place
      @param off Offset into byte array
      @param n Number of elements to convert
   */
  public static void f_vax2ieee (byte[] buf, int off, int n) {
    byte b0,b1,b2,b3;
    for (int i=0; i<n; i++,off+=4) {
      b0 = buf[off+0]; b1 = buf[off+1]; b2 = buf[off+2]; b3 = buf[off+3];
      if (b0!=0 || b1!=0 || b2!=0 || b3!=0) b1--;
      buf[off+3] = b2; buf[off+2] = b3; buf[off+1] = b0; buf[off+0] = b1;
    }
  }

  /** Convert float buffer (in a byte array) from VAX to EEEI
      @param buf byte array of float data to convert in-place
      @param off Offset into byte array
      @param n Number of elements to convert
   */
  public static void f_vax2eeei (byte[] buf, int off, int n) {
    byte b0,b1,b2,b3;
    for (int i=0; i<n; i++,off+=4) {
      b0 = buf[off+0]; b1 = buf[off+1]; b2 = buf[off+2]; b3 = buf[off+3];
      if (b0!=0 || b1!=0 || b2!=0 || b3!=0) b1--;
      buf[off+0] = b2; buf[off+1] = b3; buf[off+2] = b0; buf[off+3] = b1;
    }
  }

  /** Convert float buffer (in a byte array) from IEEE to VAX
      @param buf byte array of float data to convert in-place
      @param off Offset into byte array
      @param n Number of elements to convert
   */
  public static void f_ieee2vax (byte[] buf, int off, int n) {
    byte b0,b1,b2,b3;
    for (int i=0; i<n; i++,off+=4) {
      b2 = buf[off+3]; b3 = buf[off+2]; b0 = buf[off+1]; b1 = buf[off];
      if (b0!=0 || b1!=0 || b2!=0 || b3!=0) b1++;
      buf[off] = b0; buf[off+1] = b1; buf[off+2] = b2; buf[off+3] = b3;
    }
  }

  /** Convert float buffer (in a byte array) from EEEI to VAX
  @param buf byte array of float data to convert in-place
  @param off Offset into byte array
  @param n Number of elements to convert
   */
  public static void f_eeei2vax (byte[] buf, int off, int n) {
    byte b0,b1,b2,b3;
    for (int i=0; i<n; i++,off+=4) {
      b2 = buf[off]; b3 = buf[off+1]; b0 = buf[off+2]; b1 = buf[off+3];
      if (b0!=0 || b1!=0 || b2!=0 || b3!=0) b1++;
      buf[off] = b0; buf[off+1] = b1; buf[off+2] = b2; buf[off+3] = b3;
    }
  }

  /** Convert double buffer (in a byte array) from VAX to IEEE
      @param buf byte array of double data to convert in-place
      @param off Offset into byte array
      @param n Number of elements to convert
   */
  public static void d_vax2ieee (byte[] buf, int off, int n) {
    int i0,i1; int bias=(1023-129)<<(52-32);
    for (int i=0; i<n; i++,off+=8) {
      i0  = ((int)buf[off+6]&0xFF)<<0;
      i0 |= ((int)buf[off+7]&0xFF)<<8;
      i0 |= ((int)buf[off+4]&0xFF)<<16;
      i0 |= ((int)buf[off+5]&0xFF)<<24;
      i1  = ((int)buf[off+2]&0xFF)<<0;
      i1 |= ((int)buf[off+3]&0xFF)<<8;
      i1 |= ((int)buf[off+0]&0xFF)<<16;
      i1 |= ((int)buf[off+1]&0x7F)<<24;
      if (i0!=0 || i1!=0) {
        i0 = (i0>>3)&0x1FFFFFFF | (i1<<29);
        i1 = (i1>>3) + bias;
        if (buf[off+1]<0) i1 |= 0x80000000;
      }
      buf[off+7] = (byte)(i0&0xFF);
      buf[off+6] = (byte)((i0>>8)&0xFF);
      buf[off+5] = (byte)((i0>>16)&0xFF);
      buf[off+4] = (byte)((i0>>24)&0xFF);
      buf[off+3] = (byte)(i1&0xFF);
      buf[off+2] = (byte)((i1>>8)&0xFF);
      buf[off+1] = (byte)((i1>>16)&0xFF);
      buf[off+0] = (byte)((i1>>24)&0xFF);
    }
  }

  /** Convert double buffer (in a byte array) from VAX to EEEI
      @param buf byte array of double data to convert in-place
      @param off Offset into byte array
      @param n Number of elements to convert
   */
  public static void d_vax2eeei (byte[] buf, int off, int n) {
    int i0,i1; int bias=(1023-129)<<(52-32);
    for (int i=0; i<n; i++,off+=8) {
      i0  = ((int)buf[off+6]&0xFF)<<0;
      i0 |= ((int)buf[off+7]&0xFF)<<8;
      i0 |= ((int)buf[off+4]&0xFF)<<16;
      i0 |= ((int)buf[off+5]&0xFF)<<24;
      i1  = ((int)buf[off+2]&0xFF)<<0;
      i1 |= ((int)buf[off+3]&0xFF)<<8;
      i1 |= ((int)buf[off+0]&0xFF)<<16;
      i1 |= ((int)buf[off+1]&0x7F)<<24;
      if (i0!=0 || i1!=0) {
        i0 = (i0>>3)&0x1FFFFFFF | (i1<<29);
        i1 = (i1>>3) + bias;
        if (buf[off+1]<0) i1 |= 0x80000000;
      }
      buf[off+0] = (byte)(i0&0xFF);
      buf[off+1] = (byte)((i0>>8)&0xFF);
      buf[off+2] = (byte)((i0>>16)&0xFF);
      buf[off+3] = (byte)((i0>>24)&0xFF);
      buf[off+4] = (byte)(i1&0xFF);
      buf[off+5] = (byte)((i1>>8)&0xFF);
      buf[off+6] = (byte)((i1>>16)&0xFF);
      buf[off+7] = (byte)((i1>>24)&0xFF);
    }
  }

  /** Convert double buffer (in a byte array) from IEEE to VAX
      @param buf byte array of double data to convert in-place
      @param off Offset into byte array
      @param n Number of elements to convert
   */
  public static void d_ieee2vax (byte[] buf, int off, int n) {
    int i0,i1; int bias = 0x37E00000;
    for (int i=0; i<n; i++,off+=8) {
      i0  = ((int)buf[off+7]&0xFF)<<0;
      i0 |= ((int)buf[off+6]&0xFF)<<8;
      i0 |= ((int)buf[off+5]&0xFF)<<16;
      i0 |= ((int)buf[off+4]&0xFF)<<24;
      i1  = ((int)buf[off+3]&0xFF)<<0;
      i1 |= ((int)buf[off+2]&0xFF)<<8;
      i1 |= ((int)buf[off+1]&0xFF)<<16;
      i1 |= ((int)buf[off]&0x7F)<<24;
      if (i0!=0 || i1!=0) {
        /* The >> operator shifts with sign placed in from the left. The
         correct shift in this case is >>> in which 0's are shifted in from
         the left. */
        i1 = ((i1 - bias)<<3)|(i0>>>29);
        if (buf[off]<0) i1 |= 0x80000000;
        i0 <<= 3;
      }
      buf[off]   = (byte)((i1>>16)&0xFF);
      buf[off+1] = (byte)((i1>>24)&0xFF);
      buf[off+2] = (byte)(i1&0xFF);
      buf[off+3] = (byte)((i1>>8)&0xFF);
      buf[off+4] = (byte)((i0>>16)&0xFF);
      buf[off+5] = (byte)((i0>>24)&0xFF);
      buf[off+6] = (byte)(i0&0xFF);
      buf[off+7] = (byte)((i0>>8)&0xFF);
    }
  }

  /** Convert double buffer (in a byte array) from EEEI to VAX
      @param buf byte array of double data to convert in-place
      @param off Offset into byte array
      @param n Number of elements to convert
   */
  public static void d_eeei2vax (byte[] buf, int off, int n) {
    int i0,i1; int bias = 0x37E00000;
    for (int i=0; i<n; i++,off+=8) {
      i0  = ((int)buf[off]&0xFF)<<0;
      i0 |= ((int)buf[off+1]&0xFF)<<8;
      i0 |= ((int)buf[off+2]&0xFF)<<16;
      i0 |= ((int)buf[off+3]&0xFF)<<24;
      i1  = ((int)buf[off+4]&0xFF)<<0;
      i1 |= ((int)buf[off+5]&0xFF)<<8;
      i1 |= ((int)buf[off+6]&0xFF)<<16;
      i1 |= ((int)buf[off+7]&0x7F)<<24;
      if (i0!=0 || i1!=0) {
        /* The >> operator shifts with sign placed in from the left. The
          correct shift in this case is >>> in which 0's are shifted in from
          the left. */
        i1 = ((i1 - bias)<<3)|(i0>>>29);
        if (buf[off+7]<0) i1 |= 0x80000000;
        i0 <<= 3;
      }
      buf[off] = (byte)((i1>>16)&0xFF);
      buf[off+1] = (byte)((i1>>24)&0xFF);
      buf[off+2] = (byte)(i1&0xFF);
      buf[off+3] = (byte)((i1>>8)&0xFF);
      buf[off+4] = (byte)((i0>>16)&0xFF);
      buf[off+5] = (byte)((i0>>24)&0xFF);
      buf[off+6] = (byte)(i0&0xFF);
      buf[off+7] = (byte)((i0>>8)&0xFF);
    }
  }

  /** Convert Object to the Object type specified by conversion. This is the same as using:
      <code>Convert.o2o(string,'_',ref)</code>.
      @param obj     Object to convert
      @param ref     Reference object (often a Midas context or a Command)
      @return Converted object
      @since NeXtMidas 2.7.0
   */
  public static Object o2o (Object obj, Object ref) {
    return o2o(obj, "_", ref);
  }

  /** Convert Object to the Object type specified by conversion
      @param obj     Object to convert
      @param newType Character representing conversion (e.g. 'D','F',...)
      @param ref     Reference object (often a Midas context or a Command)
      @return Converted object
   */
  public static Object o2o (Object obj, char newType, Object ref) {
    return o2o(obj, Character.toString(newType), ref);
  }


  /** Convert string to the Object type specified by conversion
      @param string String to convert
      @param format The format digraph (e.g. "SF","VD",...) or type letter (e.g. "D","F",...)
      @param ref    Reference object (often a Midas context or a Command)
      @return Converted object
   */
  public static Object s2o (String string, String format, Object ref) {
    // THIS IS AN EXACT COPY OF o2o(Object,String,Object) EXCEPT FOR CALL to s2o(..) IN THE RETURN STATEMENT
    if (string==null) return null;

    String newFormat = getNewFormat(format);

    int    newSize  = -1;
    int    newSPA   = -1;
    char   newMode  = newFormat.charAt(0);
    char   newType  = newFormat.charAt(1);

    if (newMode != ' ') newSPA = Data.getSPA(newMode);

    if (!validType(newType)) {
      throw new MidasException("Invalid conversion type '"+newType+"' in format string '"+format.toUpperCase()+"'.");
    }

    return s2o(string, format.toUpperCase(), newSize, newSPA, newMode, newType, false, ref);
  }



  /** Convert Object to the Object type specified by conversion
      @param obj    Object to convert
      @param format The format digraph (e.g. "SF","VD",...) or type letter (e.g. "D","F",...)
      @param ref    Reference object (often a Midas context or a Command)
      @return Converted object
   */
  public static Object o2o (Object obj, String format, Object ref) {
    // THIS IS AN EXACT COPY OF s2o(String,String,Object) EXCEPT FOR CALL to o2o(..) IN THE RETURN STATEMENT
    if (obj==null) return null;

    String newFormat = getNewFormat(format);

    int    newSize  = -1;
    int    newSPA   = -1;
    char   newMode  = newFormat.charAt(0);
    char   newType  = newFormat.charAt(1);

    if (newMode != ' ') newSPA = Data.getSPA(newMode);

    if (!validType(newType)) {
      throw new MidasException("Invalid conversion type '"+newType+"' in format string '"+format.toUpperCase()+"'.");
    }

    return o2o(obj, format.toUpperCase(), newSize, newSPA, newMode, newType, false, ref);

  }

  /** Calls {@link #o2Data(Object, String, Object)} to convert a Java object into a NeXtMidas Data object
      @param obj     Object to convert
      @param newType Data type to convert this to
      @param ref     reference Object
      @return converted Data Object
   */
  @InternalUseOnly
  public static Data o2Data (Object obj, char newType, Object ref) {
    return o2Data(obj, Character.toString(newType), ref);
  }

  /** Converts a Java Object into a NeXtMidas Data object
      @param obj    Object to convert
      @param format Data format
      @param ref    reference Object
      @return converted Data Object
   */
  @InternalUseOnly
  public static Data o2Data (Object obj, String format, Object ref) {
    if (obj==null) return null;

    String newFormat = getNewFormat(format);

    int    newSize  = -1;
    int    newSPA   = -1;
    char   newMode  = newFormat.charAt(0);
    char   newType  = newFormat.charAt(1);

    if (newMode != ' ') newSPA = Data.getSPA(newMode);

    if (DataTypes.DATA_TYPE_LIST.indexOf(newType) == -1) {
      throw new MidasException("Invalid conversion type '"+newType+"' in format string '"+format.toUpperCase()+"'.");
    }

    Object dataObj = o2o(obj, format.toUpperCase(), newSize, newSPA, newMode, newType, true, ref);
    if (!(dataObj instanceof Data)) {
      throw new MidasException("Convert: Unable to convert '"+obj+"' to Data object with "+format+".");
    }
    return (Data)dataObj;

}

  private static String getNewFormat (String format) {
    String form     = format.toUpperCase();
    char   newMode  = ' ';
    char   newType  = '_';
    int    len      = form.length();

    // Ignore ':' if included at end (e.g. "D:" or "SD:") if the only char is ":" assume
    // it is a typo and error out at bottom (when we do the validType(..) check).
    if ((len >= 2) && (form.charAt(len-1) == ':')) len = len-1;

         if (len == 0) { newType = '_';            newMode = ' ';            }
    else if (len == 1) { newType = form.charAt(0); newMode = ' ';            }
    else if (len == 2) { newType = form.charAt(1); newMode = form.charAt(0); }
    else               { throw new IllegalArgumentException("Illegal format string '"+form+"'."); }
    return "" + newMode + newType;
  }

  /** Convert Object to the Object type specified by conversion
      @param obj     Object to convert
      @param form    Input format from user, used in error messages and when debugging.
      @param newSize The number of atom per element for the new object (-1=UseDefault)
      @param newSPA  The number of scalars per atom for the new object (-1=UseDefault)
      @param newMode The mode of the new object (note that mode='V' and mode='3' both set SPA=3).
      @param newType The type of the new object.
      @param isData  The new type is assumed to ba a valid Data type
      @param ref     Reference object (often a Midas context or a Command)
      @return Converted object
   */
  private static Object o2o (Object obj, String form, int newSize, int newSPA, char newMode, char newType, boolean isData, Object ref) {

    if (obj==null) return null;

    Data    data    = null;
    String  str     = null;
    boolean single  = (newSize == -1) || (newSize == 1);
    boolean scalar  = (newSPA  == -1) || (newSPA  == 1);
    boolean ncv     = isNumericConversion(newType);
    boolean xmv     = isXMValueConversion(newType);

    if (newType == '_') {
      newType = guessType(obj,'_','_');
    }

    if (single && scalar) {
      if (newType == 'T') {
        // convert to either table or Time object
        if ((obj instanceof String) && Time.isSTDTimeString((String)obj)) { // Since 3.5.3 Bug 2883 2876
          obj = new Time((String)obj);
         }
        else obj = o2t(obj, ref);
      }
      else if (obj instanceof String) {
        str  = (String) obj;
        String trimmed = str.trim();
        // check to see if the String is a Table in disguise (since 3.3.0 - Bug 85)
        if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
          str = null;
          obj  = o2t(obj,ref);
        }
      }
      // leaving as reference code for easier support of adding unsigned keywords for test files - 3.9.3
//      else if (unsignedTestFileCreationSupport && newType == 'U') return o2u(obj); // can be avoided as was currently just used to create test data
//      else if (unsignedTestFileCreationSupport && newType == 'V') return o2v(obj);
      else if (newType == STRING)     obj  = obj.toString();
      else if (newType == 'U')        obj  = obj.toString();
      else if (newType == ASCII)      obj  = obj.toString(); // JC4: This is inconsistent!!! A_WARNING!
      else if (isData &&
               (newType == ASCII256)) obj  = obj.toString(); // 3.9.4 NXM-3570
      else if (newType == BOOL)       obj  = Boolean.valueOf(o2z(obj,ref));
      else if (obj instanceof Data)   data = (Data)obj;
      else if (newType == DOUBLE)     data = new Data(o2d(obj));
      else if (newType == FLOAT)      data = new Data(o2f(obj));
      else if (newType == XLONG)      data = new Data(o2x(obj));
      else if (newType == LONG)       data = new Data(o2l(obj));
      else if (newType == INT)        data = new Data(o2i(obj));
      else if (newType == BYTE)       data = new Data(o2b(obj));
      else if (xmv)                   obj  = XMValue.toXMValue(obj, newMode, newType, ref);
      else Shell.warning("Convert.o2o: unable to convert "+obj+" to '"+form+"'");
    }
    else {
           if (obj instanceof String) str  = (String)obj;
      else if (obj instanceof Data)   data = (Data)obj;
      else if (ncv)                   data = new Data(o2da(obj));
      else throw new MidasException("Convert.o2o: unable to convert "+obj+" to '"+form+"'");
    }

    if (str  != null) obj = s2o(str,form,newSize,newSPA,newMode,newType,isData,ref);
    if (data != null) obj = data.convertTo(newSize,newSPA,newMode,newType);

    return obj;
  }

  /** Convert string to the Object type specified by conversion. This is the same as using:
      <code>Convert.s2o(string,'_',ref)</code>.
      @param string  String to convert
      @param ref     Reference object (often a Midas context or a Command)
      @return Converted object
      @since NeXtMidas 2.7.0
   */
  public static Object s2o (String string, Object ref) {
    return s2o(string, "_", ref);
  }

  /** Convert string to the Object type specified by conversion
      @param string  String to convert
      @param newType Character representing conversion (e.g. 'D','F',...)
      @param ref     Reference object (often a Midas context or a Command)
      @return Converted object
   */
  public static Object s2o (String string, char newType, Object ref) {
    return s2o(string, Character.toString(newType), ref);
  }

  /** Convert string to the Object type specified by conversion.
      (updated in 3.5.1 Bug 2833 to preserve precision in cases where newType=XLONG)
      @param string  String to convert
      @param form    Input format from user, used in error messages and when debugging.
      @param newSize The number of atom per element for the new object (-1=UseDefault)
      @param newSPA  The number of scalars per atom for the new object (-1=UseDefault)
      @param newMode The mode of the new object (note that mode='V' and mode='3' both set SPA=3).
      @param newType The type of the new object.
      @param isData  The new type must be a valid type for Data objects
      @param ref     Reference object (often a Midas context or a Command)
      @return Converted object
   */
  private static Object s2o (String string, String form, int newSize, int newSPA, char newMode, char newType, boolean isData, Object ref) {
    // JC4: Note that is important that an instance of Data ALWAYS be returned when converting to 'A' (ASCII).
    //      DataFile (and other classes) depend heavily on this functionality. See Bug 857 for details.
    Object obj=null;
    int i, j, k;
    int length; // nominal length of the string -- changes if the string matches a switch/result
    int size;   // size of a numeric array
    int ndef=0; // numeric default used for resolving a state switch
    double[] dval   = new double[1];
    double[] dvals  = dval;
    long[]   xlval  = new long[1];  // since 3.5.1 Bug 2833
    long[]   xlvals = xlval;        // since 3.5.1 Bug 2833
    Long     xlResult;              // since 3.5.1 Bug 2833
    Results rt;
    byte type = STRING;
    boolean ncv = isNumericConversion(newType);
    boolean xmv = isXMValueConversion(newType);

    if (string==null) return null;
    length = string.length();

    // is this a switch ?
    if (length>1 && string.charAt(0)==SLASH && ref instanceof Args) {
      int vnl = Results.validNameLength (string,null,1,length);
      if (vnl==length-1) string = ((Args)ref).getRaw(string);
      if (string==null) string = "NULL";
      length = string.length();
      if (length==0) ndef = 1; // default if switch is 1 not 0
    }

    // zero length string case
    if (length==0) {
      if (ncv) return new Data(ndef).convertTo(newSize,newSPA,newMode,newType);
      else return toStringObj("",form,newSize,newSPA,newMode,newType,isData);
    }

    // special args not present case
    if (length==4 && string.equals("NULL")) {
      if (ncv) return new Data(-1).convertTo(newSize,newSPA,newMode,newType);
      else return toStringObj("",form,newSize,newSPA,newMode,newType,isData);
    }

    // quoted string
    if (string.charAt(0)==QUOTE) {
      if (newType=='Q') return string; // return with quotes
      if (ncv) throw new MidasException("Unable to convert "+string+
        " to type "+newType+" given value is a quoted string.");
      if (string.charAt(length-1)==QUOTE) length--;
      return toStringObj(string.substring(1,length),form,newSize,newSPA,newMode,newType,isData);
    }
    if (newType=='U') return string;

    // try direct results table lookup if available
    obj=null;
    rt = ref2Results(ref);
    if (rt!=null) {
      int vnl = Results.validNameLength(string,null,0,length);
      if (vnl==length) {
        if (string.startsWith("TEST(")) { } // leave for s2d with better reference
        else obj=rt.get(string.toUpperCase());
      }
    }

    if (obj != null) {
      // This is used if the object was found in the results table.
      if (ncv && (obj instanceof Boolean)) {
        obj = ((Boolean)obj).booleanValue()? 1: 0; // true->1, false->0
      }
           if (newType=='_')    return obj;
      else if (newType==OBJECT) return obj;
      else if (newType==STRING) { if (obj instanceof String) return obj; }
      else if (newType==TABLE)  { if (obj instanceof Tablizable) return o2t(obj); }
      else if (newType=='K')    { if (obj instanceof KeyVector) return obj; }
      else if (!isData && (newType==BOOL))   return Boolean.valueOf(o2z(obj));
      else if (xmv)             return XMValue.toXMValue(obj, newMode, newType, ref);
      else if (obj instanceof Data   ) return ((Data)obj).convertTo(newSize,newSPA,newMode,newType);
      else if (obj instanceof Double ) return new Data(obj).convertTo(newSize,newSPA,newMode,newType);
      else if (obj instanceof Float  ) return new Data(obj).convertTo(newSize,newSPA,newMode,newType);
      else if (obj instanceof Long   ) return new Data(obj).convertTo(newSize,newSPA,newMode,newType);
      else if (obj instanceof Integer) return new Data(obj).convertTo(newSize,newSPA,newMode,newType);
      else if (obj instanceof Short  ) return new Data(obj).convertTo(newSize,newSPA,newMode,newType);
      else if (obj instanceof Byte   ) return new Data(obj).convertTo(newSize,newSPA,newMode,newType);
      else if (obj instanceof XMValue) return ((XMValue)obj).convertTo(newSize,newSPA,newMode,newType);
    }

    // conversion to String/Ascii
    if ((newType==STRING) || (newType==ASCII) || (isData && (newType==ASCII256))) {
      return toStringObj(string,form,newSize,newSPA,newMode,newType,isData);
    }

    // conversion to table or Time Object
    if (newType == 'T') {
       if (Time.isSTDTimeString(string)) { // Since 3.5.3 Bug 2883 2876
         return new Time(string);
       }
       else return o2t(string,ref);
    }

    // conversion to table object for now
    if (newType == OBJECT) return o2t(string,ref).getCore();

    // conversion to keyvector
    if (newType == XMVALUE_KVLIST) return KeyVector.fromString(string);

    // conversion to boolean
    if (newType == BOOL) return Boolean.valueOf(o2z(string,ref));

    // conversion to XMValue
    if (xmv) return XMValue.toXMValue(string, newMode, newType, ref);

    // now get into a byte buffer
    byte[] buf = string.getBytes();

    // is it a serialized table
    if (buf[0]==LCURLY && buf[length-1]==RCURLY) {
      obj = new Table (buf);
      if (obj != null) return obj;
    }

 ///// ORDER OF THE ELSE IF BLOCK BELOW CHANGED TO ALLOW FOR CHECKING FOR   ///////
 ///// NUMERICAL EXPRESSIONS INSIDE OF PARENS (in 2.9.1 for BUG 2314)       ///////

    // try a numeric value
    // If result is typed to be an XLONG, try to calculate result using BigDecimal to preserve
    // precision. If the result will not fit in an XLONG, then s2d will be used and the type
    // set to DOUBLE. (new since 3.5.1 Bug 2833)
    if ((newType == XLONG) && ((xlResult = s2xl(buf,0,length,ref)) != null)){
      xlvals[0] = xlResult;
      type = XLONG;
      size = 1;
    } // (end new since 3.5.1 Bug 2833)
    // else, calculate using doubles and cast appropriately at end
    else if (s2d(buf,0,length,dval,ref) > 0) {
      type = DOUBLE;
      size = 1;
      if (dval[0]==Math.floor(dval[0]) && dval[0]>=Integer.MIN_VALUE && dval[0]<=Integer.MAX_VALUE) {
        type = LONG;
        int ip = string.indexOf('.');  // make sure there is no decimal point applied
        if (ip>=0 && (ip==0 || Character.isDigit(string.charAt(ip-1))) &&
                     (ip==length-1 || Character.isDigit(string.charAt(ip+1))) ) type = DOUBLE;
      }
    }
    // try a numeric array
    else if (buf[0]==LPAREN && buf[length-1]==RPAREN) {
      // If result is typed to be an XLONG, try to calculate result using BigDecimal to preserve
      // precision (new since 3.5.1 Bug 2833)
      boolean isXLongArray = false;
      size = 0; //default size to 0
      if (newType == XLONG){
        for (i=1,k=1; i<length-1; i++) if (buf[i]==COMMA) k++; // count number of items for array
        xlvals = new long[k];
        for (i=j=1,k=0; i<length; i++) {
          if (buf[i]==COMMA || i==length-1) {
            if ((xlResult = s2xl(buf,j,i,ref)) != null) {
              xlvals[k++] = xlResult;
              j = i+1;
              isXLongArray = true;
            }
            else {
              isXLongArray = false;
              break; // not going to work as type XLONG array
            }
          }
        }
        type = XLONG;
        size = k;

      } //****** end new for 3.5.1 Bug 2833
      // Try checking if it fits in a double array, if we were not already successful on generating an XLONG array
      if (!isXLongArray) {
          for (i=1,k=1; i<length-1; i++) if (buf[i]==COMMA) k++; // count number of items for array
          dvals = new double[k];
          for (i=j=1,k=0; i<length; i++) {
            if (buf[i]==COMMA || i==length-1) {
              if (s2d(buf,j,i,dval,ref) < 0) return string; // failed to convert to double, revert
              dvals[k++] = dval[0];
              j = i+1;
            }
          }
          type = DOUBLE;
          size = k;

      }
    } // end try a numeric array
    else size = 0;

    // final handling of numeric values
    if (type==DOUBLE || type==LONG) {
      Data data = new Data();
      if (ncv) type = (byte)newType;
      data.setFormatType(type);
      data.setSize(size);
      data.fromArray(dvals);
      if (size>0 && size<=4) {
        // TODO: change preceding test to (newMode==' ' && size>0 && size<=4)
        // This is used for backwards-compatibility with NXM 2.5.0 and earlier
        // which automatically converted size=1..4 to mode=S/C/V/Q
        data.resetMode();
      }
      data = data.convertTo(newSize,newSPA,newMode,newType);
      return data;
    }
    // since 3.5.1 Bug 2833
    else if (type==XLONG){
      Data data = new Data();
      if (ncv) type = (byte)newType;
      data.setFormatType(type);
      data.setSize(size);
      data.fromArray(xlvals);
      if (size>0 && size<=4) {
        // TODO: change preceding test to (newMode==' ' && size>0 && size<=4)
        // This is used for backwards-compatibility with NXM 2.5.0 and earlier
        // which automatically converted size=1..4 to mode=S/C/V/Q
        data.resetMode();
      }
      data = data.convertTo(newSize,newSPA,newMode,newType);
      return data;
    } // end if XLONG & since 3.5.1 Bug 2833
    else if (ncv) {
      throw new MidasException("Unable to convert \""+string+ "\" to type "+newType+". No applicable conversion found. " + form);
    }

    // otherwise, revert to a String
    return string;
  }

  /**
     Convert a String to an XLONG result using BigDecimal (if possible).
     {@code If the value is > Long.MAX_VALUE or < Long.MIN_VALUE or NaN or INFINITY or not a number at all,
     null will be returned)}

     Note: This forms a BigDecimal result from which the long (XLONG) value is extracted.

     @param buffer Byte array to convert
     @param index1 First index into byte array
     @param index2 Second index into byte array
     @param ref    Reference
     @return - the Long (XLONG) result, or null if the conversion was not successful
     @since NeXtMidas 3.5.1 for Bug 2833
   */
  private static Long s2xl(byte[] buffer, int index1, int index2, Object ref) {
    BigDecimalValue bdVal  = new BigDecimalValue();  // since 3.5.1 Bug 2833
    Long xlResult = null;

    try {
      if (s2BigDecimal(buffer,index1,index2,bdVal,ref) >= 0) {
        BigDecimal rounded = ((bdVal.getValue()).setScale(0, RoundingMode.FLOOR));
        xlResult= rounded.longValueExact();
      }
    }
    catch (Exception e) {
      xlResult=null;
    }
    return xlResult;
  }

  /** Converts a string to the appropriate object type (handles the special cases for type 'A'). */
  private static Object toStringObj (String string, String form, int newSize, int newSPA, char newMode, char newType, boolean isData) {
    Object obj;
    if ((newType == ASCII) || (isData && (newType==ASCII256))) {
      obj = (newSPA == -1) ? new Data(string,newType==ASCII256) : new Data(string, newSPA);
    }
    else {
           if (newSPA == -1)             obj = string;
      else if (string.length() > newSPA) obj = string.substring(0, newSPA);
      else if (string.length() < newSPA) obj = StringUtil.padRight(string, newSPA, ' ');
      else                               obj = string;
    }
    return obj;
  }

  /** Guesses the most appropriate Midas type for a given object. This method is intended for use
      within the NeXtMidas SYS option tree, however others are welcome to use it with the following
      caveat: "this is only a guess and it may change in future releases without notice." The
      following is the current list of conversions:
      <pre>
        null      -&gt; def        Double    -&gt; 'D'        Float     -&gt; 'F'
        String    -&gt; str        Long      -&gt; 'X'        Integer   -&gt; 'L'
        Table     -&gt; 'T'        Short     -&gt; 'I'        Byte      -&gt; 'B'
        Time      -&gt; 'D'        Boolean   -&gt; 'Z'        Number    -&gt; 'D'
        Character -&gt; 'S' <i>(may change in the future)</i>
        Data      -&gt; {@link Data#getFormatType()}
        XMValue   -&gt; {@link XMValue#getFormatType()}
      </pre>
      @param obj The given object.
      @param str The type to return if a String is found.
      @param def The type to return if null or unable to guess an appropriate type.
      @return A guess of which Midas type most closely matches the given object.
      @since NeXtMidas 2.7.1
   */
  public static char guessType (Object obj, char str, char def) {
    // OBJECTS (for the most part, this is roughly ordered in most-common-first order)
    if (obj     ==     null      ) return def;
    if (obj instanceof String    ) return str;
    if (obj instanceof Data      ) return (char)((Data)obj).getFormatType();
    if (obj instanceof XMValue   ) return ((XMValue)obj).getFormatType();
    if (obj instanceof Table     ) return TABLE;
    if (obj instanceof Time      ) return DOUBLE;
    if (obj instanceof Double    ) return DOUBLE;
    if (obj instanceof Float     ) return FLOAT;
    if (obj instanceof Long      ) return XLONG;
    if (obj instanceof Integer   ) return LONG;
    if (obj instanceof Short     ) return INT;
    if (obj instanceof Byte      ) return BYTE;
    if (obj instanceof Boolean   ) return BOOL;
    if (obj instanceof Character ) return STRING; // may change in the future

    // INTERFACES
    if (obj instanceof Number    ) return DOUBLE; // This must come after Double, Float, Long, etc.
    if (obj instanceof Tablizable) return TABLE; // This should come near the end
    return def;
  }

  /** Determine is a conversion character is a non-numeric non-ASCII XMValue conversion.
      @param conversion Conversion character
   */
  private static boolean isXMValueConversion (char conversion) {
    switch (conversion) {
      case XMValue.XMVALUE: return true;
      case XMValue.LIST:    return true;
      case XMValue.KVLIST:  return true;
      case XMValue.TABLE:   return true;
      default:              return false;
    }
  }

  /** Determine if a conversion character is a numeric conversion.
      @param conversion Conversion character
      @return true if conversion is a numeric conversion, false otherwise.
   */
  public static boolean isNumericConversion (char conversion) {
    return NUMERIC_DATA_TYPE_LIST.indexOf(conversion) >= 0;
  }

  /** Convert a double to a Data object
      @param value Value to convert
      @return Value as a Data object
   */
  public static Object d2o (double value) { return new Data(value); }

  /** Convert a float to a Data object
      @param value Value to convert
      @return Value as a Data object
   */
  public static Object f2o (float value) { return new Data(value); }

  /** Convert a long (X:) to a Data object
      @param value Value to convert
      @return Value as a Data object
   */
  public static Object x2o (long value) { return new Data(value); }

  /** Convert an int (L:) to a Data object
      @param value Value to convert
      @return Value as a Data object
   */
  public static Object l2o (int value) { return new Data(value); }

  /** Convert an short (I:) to a Data object
      @param value Value to convert
      @return Value as a Data object
   */
  public static Object i2o (short value) { return new Data(value); }

  /** Convert a byte to a Data object
      @param value Value to convert
      @return Value as a Data object
   */
  public static Object b2o (byte value) { return new Data(value); }

  /** Convert an Object to String with reference for Results table lookup.
      If reference is null, the Java Object toString method is called.
      @param obj Object to convert
      @param ref Reference
      @return String representation of object
   */
  public static String o2s (Object obj, Object ref) {
    if (ref!=null && obj instanceof String) {
      Results rt = ref2Results(ref);
      Object robj = rt.get((String)obj);
      if (robj instanceof String) return (String)robj;
    }
    if (obj==null) return null;
    else return obj.toString();
  }

  /** Convert an Object to double with reference
      @param obj Object to convert
      @param ref Reference
      @return double
   */
  public static double o2d (Object obj, Object ref) {
    if (obj instanceof String) return s2d((String)obj,ref);
    return o2d(obj);
  }

  /** Convert an Object to float with reference
      @param obj Object to convert
      @param ref Reference
      @return float
   */
  public static float o2f (Object obj, Object ref) {
    return (float)o2d(obj,ref);
  }

  /** Convert an Object to long with reference.
      (updated 3.5.1 Bug 2833 to use BigDecimal)
      @param obj Object to convert
      @param ref Reference
      @return long
   */
  public static long o2x (Object obj, Object ref) {
    if (obj instanceof Data)        { return ((Data)obj).toX(); }         // most common scenario
    else if (obj instanceof Number) { return ((Number)obj).longValue(); } // since 3.5.0 (Bug 2833)
    else {                                                                // since 3.5.1 (Bug 2833)
      long xlResult;
      // Try get get the XLONG value using BigDecimal to maintain precision
      try {
        BigDecimal rounded = (o2BigDecimal(obj).setScale(0, RoundingMode.HALF_EVEN));
        xlResult= rounded.longValueExact();
      }
      // As a back-up use double to handle out of range, INFINITY, and NaN cases
      catch (Exception e){
        xlResult = (long)Math.rint(o2d(obj,ref));
      }
      return xlResult;
    }
  }

  /** Convert an Object to int (L:) with reference
      @param obj Object to convert
      @param ref Reference
      @return int
   */
  public static int o2l (Object obj, Object ref) {
    if (!favorJavaBehavior) {
      // Midas behavior
      double objAsRintDouble = Math.rint(o2d(obj, ref));
      return (int)objAsRintDouble;
    }
    else {
      // need Java behavior of wrapping values
      return o2lJava(obj, null);
    }
  }

  /** Convert an Object to short (I:) with reference
     @param obj Object to convert
     @param ref Reference
     @return short
   */
  public static short o2i (Object obj, Object ref) {
    return (short)o2l(obj,ref);
  }

  /** Convert an Object to byte with reference
      @param obj Object to convert
      @param ref Reference
      @return short
   */
  public static byte o2b (Object obj, Object ref) {
    return (byte)o2l(obj,ref);
  }

  /** Convert an Object to String with reference and default
      @param obj Object to convert
      @param ref Reference
      @param def Default string
      @return Object as a String of def if null
   */
  public static String o2s (Object obj, Object ref, String def) {
    String s=o2s(obj,ref); if (s==null) s=def; return s;
  }

  /** Convert an Object to double with reference and default
      @param obj Object to convert
      @param ref Reference
      @param def Default value
      @return Object as a double of def if null
   */
  public static double o2d (Object obj, Object ref, double def) {
    if (obj==null) return def; else return o2d(obj,ref);
  }

  /** Convert an Object to int (L:) with reference and default
      @param obj Object to convert
      @param ref Reference
      @param def Default value
      @return Object as a double of def if null
   */
  public static int o2l (Object obj, Object ref, int def) {
    if (obj==null) return def; else return o2l(obj,ref);
  }

  /** Convert an Object to String. Calls Java toString method.
     @param obj Object to convert
     @return Object as a String or null if Object is null
   */
  public static String o2s (Object obj) {
    if (obj==null) return null;
    else return obj.toString();
  }

  /** Convert an Object to double. Throws IllegalArgumentException if not a valid type.
      Valid types are: Data, String, Double, Float, Long, Integer, Short, Byte, Boolean and Time.
      Since 3.3.2: supports Boolean (true=1 and false=0).
      @param obj Object to convert
      @return Object as a double
   */
  public static double o2d (Object obj) {
    // Note the Number interface includes Double, Float, Long, Integer, Short, Byte
         if (obj     ==     null   ) return 0.0;
    else if (obj instanceof Data   ) return ((Data)obj).toD();
    else if (obj instanceof String ) return s2d((String)obj);
    else if (obj instanceof Number ) return ((Number)obj).doubleValue();
    else if (obj instanceof Time   ) return ((Time)obj).getSec();
    else if (obj instanceof XMValue) return ((XMValue)obj).toD();
    else if (obj instanceof Boolean) return ((Boolean)obj).booleanValue()? 1 : 0; // since 3.3.2 (Bug 2737)
    else throw new java.lang.IllegalArgumentException("Unconvertable type ["+obj+"] to double");
  }

  /** Convert data object to List of input type.
   *  @param val type of List
   *  @return List of specified type
   *  @since NeXtMidas 3.7.1 for NXM-2945
   */
 public static List<?> o2List (Object val) {
     if (val == null)               return null;
     if (val instanceof List)       return (List<?>)val;
     if (val instanceof Data)       return ((Data)val).toVector();
     if (val instanceof Object[])   return Arrays.asList((Object[])val);

     throw new MidasException("Convert.o2List() could not convert "+ val.getClass() + " value = " +val.toString());
 }

    /** Convert an Object to BigDecimal. Throws IllegalArgumentException if not a valid type.
        Valid types are: Data, String, Double, Float, Long, Integer, Short, Byte, Boolean and Time.
        @param obj Object to convert
        @return a BigDecimal
        @since NeXtMidas 3.5.1 for Bug 2833.
  */
  public static BigDecimal o2BigDecimal (Object obj) {
    return (BigDecimal)(o2n(obj, null, BigDecimal.ZERO, true, false));
  }

  /** Convert an Object to a number. This is functionally identical to {@link #o2d(Object)} except
      that it returns null when the input object is null ({@link #o2d(Object)} returns 0.0) and it
      returns null if the object is not convertible ({@link #o2d(Object)} throws an exception). Also
      it uses {@link #s2n(String)} rather than {@link #s2d(String)} ({@link #s2n(String)} returns
      null on the empty string where {@link #s2d(String)} returns 0.0).
      @param obj Object to convert
      @return The object as a Number
   */
  public static Number o2n (Object obj) {
    return o2n(obj, null, null, false, true);
  }

  /** Convert an Object to a Number.
   *
   *  @param obj Object to convert
   *  @param ref The reference to use (Ideally a {@link Command} for proper cmdline argument picking,
                 but could be any class that implements {@link MidasReference}.
   *  @param def Default value if conversion fails
   *  @param asBigDecimal calculations and return value should be done as BigDecimal
   *  @param noAbort Do not abort even if the object does not represent a number
   *  @return numeric value in Number sub-class (or null if it is not a number and noAbort)
   *  @since NeXtMidas 3.9.0 for NXM-3157
   */
  public static Number o2n (Object obj, Object ref, Number def, boolean asBigDecimal, boolean noAbort) {
    if (obj == null) return def;

    try {
      // ==== Obj-toObj Conversions ===============================
      if (obj instanceof Instant             ) obj = Time.toTime(obj);
      if (obj instanceof Calendar            ) obj = Time.toTime(obj);
      if (obj instanceof XMLGregorianCalendar) obj = Time.toTime(obj);
      if (obj instanceof Date                ) obj = Time.toTime(obj);
      // Now catch all of the above
      if (obj instanceof Time                ) obj = ((Time)obj).toNumber();

      if (obj instanceof Data                ) obj = ((Data)obj).toNumber();
      if (obj instanceof XMValue             ) obj = ((XMValue)obj).toNumber();
      if (obj instanceof Boolean             ) obj = ((Boolean)obj) ? 1 : 0;

      // === Num-toNum Conversions ==================================
      if (asBigDecimal) {
        // Double and Float must go through the obj.toString() version, but the
        // integer types can use valueOf(..) to potentially obtain an optimized version
        if (obj instanceof Byte      ) return BigDecimal.valueOf((Byte  )obj);
        if (obj instanceof Short     ) return BigDecimal.valueOf((Short  )obj);
        if (obj instanceof Integer   ) return BigDecimal.valueOf((Integer  )obj);
        if (obj instanceof BigDecimal) return (Number)obj;
        if (obj instanceof BigInteger) return new BigDecimal((BigInteger)obj);
        if (obj instanceof Number    ) return new BigDecimal(((Number)obj).toString());
      }
      else {
        if (obj instanceof Number    ) {
          if (alwaysDefaultToDouble) {
            // always returning Double, even when precision is lost
            return ((Number)obj).doubleValue();
          }
          return (Number)obj;
        }
      }

      // == Str-to-Num Conversions ===================================
      boolean objOriginallyString = obj instanceof String;
      String str = obj.toString().trim(); // I think we always want to trim any String

      switch (str) {
        // These are extremely common...
        case "-1": return (asBigDecimal) ? BigDecimal_NEG1  : -1;
        case  "0": return (asBigDecimal) ? BigDecimal.ZERO  :  0;
        case  "1": return (asBigDecimal) ? BigDecimal.ONE   :  1;
        case  "2": return (asBigDecimal) ? BigDecimal_TWO   :  2;
        case  "3": return (asBigDecimal) ? BigDecimal_THREE :  3;
        case  "4": return (asBigDecimal) ? BigDecimal_FOUR  :  4;
        case  "5": return (asBigDecimal) ? BigDecimal_FIVE  :  5;
        case  "6": return (asBigDecimal) ? BigDecimal_SIX   :  6;
        case  "7": return (asBigDecimal) ? BigDecimal_SEVEN :  7;
        case  "8": return (asBigDecimal) ? BigDecimal_EIGHT :  8;
        case  "9": return (asBigDecimal) ? BigDecimal_NINE  :  9;
        case "10": return (asBigDecimal) ? BigDecimal.TEN   : 10;
      }

      if (str.isEmpty()) {
        str = "<empty>";  // <-- Drops through to error handling below
      }
      else {
        Number val = s2n(str,ref,asBigDecimal,objOriginallyString);
        if (val != null) return val;
      }

      if (noAbort) return def;
      throw new MidasException("Illegal numberic expression:" + str);
    }
    catch (RuntimeException e) {
      if (noAbort) return def;
      throw e;
    }
  }

  /** Converts a String to a Number<br>
   *  Cases:<br>
   *  1) asBigDecimal is true: calculated and returned as a BigDecimal if possible
   *  2) String is a pure Number: precision is preserved by using the correct Number type
   *  3) String is a result or equation: calculates and returns value as Double
   *  @param string String value to be converted to a Number
   *
   *  @param ref The reference to use (Ideally a {@link Command} for proper cmdline argument picking,
                 but could be any class that implements {@link MidasReference}.
   *  @param asBigDecimal calculations and return value should be done as BigDecimal
   *  @return Number sub-class containing value or null if String is not a simple number
   */
  public static Number s2n (String string, Object ref, boolean asBigDecimal) {
    return s2n(string, ref, asBigDecimal, true);
  }

  /** Converts a String to a Number<br>
   *  Cases:<br>
   *  1) asBigDecimal is true: calculated and returned as a BigDecimal if possible
   *  2) String is a pure Number: precision is preserved by using the correct Number type
   *  3) String is a result or equation: calculates and returns value as Double
   *  @param string String value to be converted to a Number
   *
   *  @param ref The reference to use (Ideally a {@link Command} for proper cmdline argument picking,
                 but could be any class that implements {@link MidasReference}.
   *  @param asBigDecimal calculations and return value should be done as BigDecimal
   *  @param handleResultsName check if this string a a result name holding a Number
   *  @return Number sub-class containing value or null if String is not a simple number
   */
  public static Number s2n (String string, Object ref, boolean asBigDecimal, boolean handleResultsName) {
    if (!asBigDecimal) {
      if (!alwaysDefaultToDouble) {
        // Do a quick check for a pure number string
        // Use whatever Number type it takes to keep precision
        Number quickNumber = getQuickNumber(string);
        if (quickNumber != null) return quickNumber;
        if (handleResultsName && (ref !=null)) {
          // See if the String is a Results name
          Results rt = ref2Results(ref);
          if (rt != null ) {
            if (!(string.startsWith("TEST(") || string.startsWith("CALC("))) {
              Object resObj =  rt.get(string);
              if (resObj!=null) {
                if (resObj instanceof Number) {
                  return ((Number)resObj);
                }
                if (resObj instanceof Data) {
                  return ((Data)resObj).toNumber();
                }
              }
            }
          }
        }
      }

      // This is not a pure number and we are doing our fall-back of using s2d
      // Results names and equations fall into this category)
      // Note: Loss of precision may occur
      double[] val = new double[3];
      if (s2d(string.getBytes(), 0, string.length(), val, ref) >= 0) {
        return val[0];
      }
      else {
        return null;
      }
    }
    else {
      // largest integer than can be stored in a double without loss of precision is 9.0071993e+15
      // 9,007,199,300,000,000 or by trial 9,007,199,254,740,991
      // We either want or need BigDecimal
      // Should only get to code below if asBigDecimal is true or we know it is not a results name or equation
      BigDecimalValue val = new BigDecimalValue();
      try {
        if (s2BigDecimal(string.getBytes(), 0, string.length(), val, ref) >= 0 ) {
          return val.getValue();
        }
      }
      catch (Exception e){
        // Handle case where equation results in NaN or Infinity
        val.setValue(new BigDecimal(s2d(string, ref)));
        return val.getValue();
      }
    }
    return null; // should never reach here, but Java wants it
  }

  /** If this String is just a direct String representation of a number,
   *  convert it.
   *  @param strValue String that might be a number
   *  @return the Number (Integer, Long, BigInteger, BigDecimal, Double) or null
   */
  private static Number getQuickNumber (String strValue) {
    Number quickNumber = null;
    String strValueLC = strValue.trim().toLowerCase();
    boolean isNegative = strValueLC.charAt(0) == '-';
    boolean hasPlus = strValueLC.charAt(0) == '+';
    boolean isSigned = isNegative || hasPlus;
    if (WHOLE_NUMBER_PATTERN.matcher(strValueLC).matches()) {
      // this is a int, long, or BigInteger
      int numDigits = isSigned ? strValueLC.length() - 1 : strValueLC.length();
      if ((numDigits < Convert.DIGITS_INTEGER_MAX_VALUE)) {
        // safe to go in integer - max Integer is 2,147,483,647
        quickNumber = Integer.valueOf(strValueLC);
      }
      else if ((numDigits == Convert.DIGITS_INTEGER_MAX_VALUE) &&
                isTenDigitInteger(isNegative, hasPlus ? strValueLC.substring(1) : strValueLC)) {
        quickNumber = Integer.valueOf(strValueLC);
      }
      else if (numDigits < Convert.DIGITS_LONG_MAX_VALUE) {
        // safe to go in Long - max Long is 9,223,372,036,854,775,80.
        quickNumber = Long.valueOf(strValueLC);
      }
      else if ((numDigits == Convert.DIGITS_LONG_MAX_VALUE) &&
                isNineteenDigitLong(isNegative, hasPlus ? strValueLC.substring(1) : strValueLC)) {
        // still safe to go in Long - maximum value that can be held in long is 9.223372e+18 (9,223,372,000,000,000,000)
        quickNumber = Long.valueOf(strValueLC);
      }
      else {
        quickNumber = new BigInteger(strValueLC);
      }
    }
    else if (FLOAT_NUMBER_PATTERN.matcher(strValueLC).matches()) {
      // this is a floating point number
      // need number of digits before decimal
      int locOfDecimal = strValueLC.indexOf('.');
      int numDigitsBeforeDecimal = isSigned ? locOfDecimal - 1 : locOfDecimal;
      int locOfExponent = strValueLC.indexOf('e');
      // If the value is too big, we definitely want to go to BigDecimal (max: 1.7976931348623158e+308)
      int numDigitsWhole = (locOfExponent == -1) ? numDigitsBeforeDecimal :
                                            numDigitsBeforeDecimal + Integer.valueOf(strValue.substring(locOfExponent + 1));
      // If we want to keep precision, we may need to go to BigDecimal (max digits precision is 15)
      int numDigitsPrecision = strValue.length();
      if (isSigned) { // remove one for the negative sign
        numDigitsPrecision--;
      }
      if (locOfDecimal > -1) { // remove one for the decimal
        numDigitsPrecision--;
      }
      if (locOfExponent > -1) { // remove the characters that are part of the exponent
        numDigitsPrecision -= (strValue.length()-locOfExponent);
      }

      if ((numDigitsPrecision > 15) || (numDigitsWhole > 309)) { // This has to be BigDecimal
          quickNumber = new BigDecimal(strValueLC);
        }
      else if (numDigitsWhole == 309) { // This may need to be a double still- This part still runs a little slow
        quickNumber = Double.valueOf(strValueLC);
        if ((Double)quickNumber == Double.POSITIVE_INFINITY || (Double)quickNumber == Double.NEGATIVE_INFINITY) {
          quickNumber = new BigDecimal(strValueLC);
        }
      }
      else {
        quickNumber = Double.valueOf(strValueLC);
      }
    }
    else if (strValueLC.equals("nan")) {
      quickNumber = Double.NaN;
    }
    else if (strValueLC.equals("infinity")) {
      quickNumber = Double.POSITIVE_INFINITY;
    }
    else if (strValueLC.equals("-infinity")) {
      quickNumber = Double.NEGATIVE_INFINITY;
    }
    // For reference: the largest integer than can be stored in a double without loss of precision is 9.0071993e+15
    // 9,007,199,300,000,000 or by trial 9,007,199,254,740,991
    return quickNumber;
  }

  /** Checks if a whole number value with 10 digits is small enough to fit
   *  in an Integer
   *
   *  @param isNegative is this a negative number
   *  @param value      the value to check
   *  @return           true if this value will fit in an Integer
   */
  private static boolean isTenDigitInteger (boolean isNegative, String value) {
    if (isNegative) {
      return value.compareTo(String_Integer_MIN_VALUE) >= 0;
    }
    else {
      return value.compareTo(String_Integer_MAX_VALUE) <= 0;
    }
  }

  /** Checks if a whole number value with 19 digits is small enough to fit
   *  in an Long
   *
   *  @param isNegative is this a negative number
   *  @param value      the value to check
   *  @return           true if this value will fit in an Long
   */
  private static boolean isNineteenDigitLong (boolean isNegative, String value) {
    if (isNegative) {
      return value.compareTo(String_Long_MIN_VALUE) >= 0;
    }
    else {
      return value.compareTo(String_Long_MAX_VALUE) <= 0;
    }
  }

  /** Convert an Object to a float.
      @param obj Object to convert
      @return Object as a float
      @see #o2d(Object)
   */
  public static float o2f (Object obj) { return (float)o2d(obj); }

  /** Convert an Object to a long.
      @param obj Object to convert
      @return Object as a long
      @see #o2d(Object)
   */
  public static long o2x (Object obj) {
    return o2x(obj,null); //since NeXtMidas 3.5.1 (Bug 2833)
  }

  /**
   * Convert an object to a Data Object that holds an unsigned short
   * @param obj Object to convert
   * @return  Data object that holds an unsigned short
   * @since 3.9.3 for NXM-3021
   */
  @InternalUseOnly
  public static Data o2u (Object obj) {
    String sval;
    if(obj instanceof Data) sval = String.valueOf(Convert.unpackUI(((Data)obj).buf, 0, Shell.rep));
    else sval = obj.toString();
    Data data = new Data((short) Integer.parseInt(sval));
    data.type = 'U';
    return data;
  }

  /**
   * Convert an object to a Data Object that holds an unsigned integer
   * @param obj Object to convert
   * @return  Data object that holds an unsigned integer
   * @since 3.9.3 for NXM-3021
   */
  @InternalUseOnly
  public static Data o2v (Object obj) {
    String sval;
    if (obj instanceof Data) sval = String.valueOf(Convert.unpackV(((Data)obj).buf, 0, Shell.rep));
    else if (obj instanceof Integer) sval = Integer.toUnsignedString((int) obj);
    else sval = obj.toString();
    Data data = new Data(Integer.parseUnsignedInt(sval));
    data.type = 'V';
    return data;
  }

  /** Convert an Object to an int (L:).
      @param obj Object to convert
      @return Object as an int
      @see #o2d(Object)
   */
  public static int o2l (Object obj) {
    if (!favorJavaBehavior) {
      // Midas behavior
      double objAsRintDouble = Math.rint(o2d(obj));
      return (int)objAsRintDouble;
    }
    else {
      // need Java behavior: wrapping values if original was a long and truncating
      // if is was a double
      return o2lJava(obj, null);
    }
  }

  /** Convert an Object to a Midas long (Java int) using Java behavior when possible to detect
   *  the type of the incoming object
   *  (int)double - truncate
   *  (int)long   - wrap
   *
   *   @param obj object to convert
   *   @param ref reference
   *   @return    int value
   *   @since 3.9.1 for NXM-3157
   */
  public static int o2lJava (Object obj, Object ref) {
    // Note the Number interface includes Double, Float, Long, Integer, Short, Byte
    if (obj     ==     null   ) return 0;
    else if (obj instanceof Data   ) {
      return ((Data)obj).toNumber().intValue();
    }
    else if (obj instanceof String ) {
      String strObj = (String)obj;
      Number directNumber = getQuickNumber(strObj);
      if (directNumber !=null) {
        return directNumber.intValue();
      }
      if (ref !=null) {
        // See if the String is a Results name
        Results rt = ref2Results(ref);
        if (rt != null ) {
          if (!(strObj.startsWith("TEST(") || strObj.startsWith("CALC("))) {
            Object resObj =  rt.get(strObj);
            if (resObj!=null) {
              if (resObj instanceof Number) {
                return ((Number)resObj).intValue();
              }
              if (resObj instanceof Data) {
                return ((Data)resObj).toNumber().intValue();
              }
            }
          }
        }
      }
      // We have run out of easy ways to find the actual Data type.  This
      // could be an equation. In any case, we are now just going to match
      // Midas behavior and assume this value is a double
      return (int)(long)s2d((String)obj);
    }
    else if (obj instanceof Number ) return ((Number)obj).intValue();
    else if (obj instanceof Time   ) return (int)(((Time)obj).getWSec());
    else if (obj instanceof XMValue) return ((XMValue)obj).toL();
    else if (obj instanceof Boolean) return ((Boolean)obj).booleanValue()? 1 : 0;
    else throw new java.lang.IllegalArgumentException("Unconvertable type ["+obj+"] to Midas long");
  }

  /** Convert an Object to short (I:).
      @param obj Object to convert
      @return Object as a float
      @see #o2l(Object)
   */
  public static short o2i (Object obj) { return (short)o2l(obj); }

  /** Convert an Object to a byte.
      @param obj Object to convert
      @return Object as a short
      @see #o2l(Object)
   */
  public static byte o2b (Object obj) { return (byte)o2l(obj); }

  /** Convert an Object to a double with an offset and default value.
      @param obj      Object: Data object or double array
      @param off      Offset into buffer or array
      @param defValue Default value to return if conversion fails.
      @return Converted double or default value if invalid object
   */
  public static double o2d (Object obj, int off, double defValue) {
    if (obj instanceof Data) {
      Data data = (Data)obj;
      if (off<0 || off>=data.size*data.spa) return defValue;
      return data.getD(off);
    }
    else if (obj instanceof double[]) {
      double[] data = (double[])obj;
      if (off<0 || off>=data.length) return defValue;
      return data[off];
    }
    return defValue;
  }

  /** Convert an Object (Data or double[]) to a double array
      @param obj Object to convert
      @return Converted double or default value if invalid object
   */
  public static double[] o2da (Object obj) {
    double[] da = null;
    if (obj instanceof Data) {
      Data data = (Data)obj;
      da = new double[data.size*data.spa];
      for (int i=0; i<data.size*data.spa; i++) da[i]=data.getD(i);
    }
    else if (obj instanceof double[]) {
      da = (double[])obj;
    }
    return da;
  }

  /** Convert an Object to boolean with reference.  If the object is a boolean its value is
      returned, if the object is a String {@link #isTrueString(String)} or {@link
      #isFalseString(String)} are used. If the object is a number then true is returned if the
      number is &gt; 0.
      @param obj Object to convert
      @param ref Reference
      @return Object as boolean
      @see #isTrueString(String)
      @see #isFalseString(String)
      @see #o2d(Object,Object)
   */
  public static boolean o2bool (Object obj, Object ref) {
    if (obj instanceof Boolean) return ((Boolean)obj).booleanValue();
    if (obj instanceof String) {
      String string = (String)obj;
      if (isTrueString(string)) return true;
      if (isFalseString(string)) return false;
    }
    return ( o2d(obj,ref)>0 );
  }

  /** Test to determine if a state String represents true
      @param state State string
      @return true if string is T|TRUE|Y|YES|ON, false otherwise
      @see nxm.sys.inc.DataTypes#trueStates
      @see #s2state(String,boolean)
   */
  public static boolean isTrueString (String state) {
    StringBuilder sb = new StringBuilder("|"); sb.append(state); sb.append("|");
    if (DataTypes.trueStates.indexOf(sb.toString()) >= 0) return true;
    else return false;
  }

  /** Test to determine if a state String represents false
      @param state State string
      @return true if string is F|FALSE|N|NO|OFF, false otherwise
      @see nxm.sys.inc.DataTypes#falseStates
      @see #s2state(String,boolean)
   */
  public static boolean isFalseString (String state) {
    StringBuilder sb = new StringBuilder("|"); sb.append(state); sb.append("|");
    if (DataTypes.falseStates.indexOf(sb.toString()) >= 0) return true;
    else return false;
  }

  /** Convert an Object to a NeXtMidas Table
      @param obj Object to convert
      @return table
   */
  public static Table o2t (Object obj) {
    if (obj instanceof Table) return (Table)obj;
    if (obj instanceof String) return new Table((String)obj);
    if (obj instanceof Tablizable) return ((Tablizable)obj).toTable();
    return null;
  }

  /** Convert an Object to a NeXtMidas Table while doing a stricter
      check in the case where a string is passed in.

      @param obj Object to convert
      @return table
      @since NeXtMidas 4.1.3
   */
  @InternalUseOnly
  public static Table o2tStrict (Object obj) {
    if (obj instanceof Table) return (Table)obj;
    if (obj instanceof String) {
      Table t = new Table(Table.HM,Table.CHECK_MALFORMED);
      t.fromBytes(((String)obj).getBytes());
      return t;
    }
    if (obj instanceof Tablizable) return ((Tablizable)obj).toTable();
    return null;
  }

  /** Convert an Object to a NeXtMidas Table with reference
      @param obj Object to convert
      @param ref The reference to use (Ideally a {@link Command} for proper cmdline argument picking,
                 but could be any class that implements {@link MidasReference}.
      @return table
   */
  @SuppressWarnings({"unchecked"})
  public static Table o2t (Object obj, Object ref) {
    if (obj instanceof Table) return (Table)obj;
//  if (obj instanceof Hashtable) return new Table((Hashtable<String, ?>)obj); // prior to 3.7.0 (constructor with Hashtable param is now deprecated)
    if (obj instanceof Map) return new Table((Map<String, ?>)obj);             // since 3.7.0 as part of Bug 2949 and Bug 2938
    if (obj instanceof Tablizable) return ((Tablizable)obj).toTable();
    if (!(obj instanceof String)) return null;
    String string = (String)obj;
    int ib = string.indexOf('{');
    if (ib>=0 && ib<=5) { // look for Table mode
      int ic = string.indexOf(':');
      int mode = ((Table.getDefaultFlagsInt()&Table.CHECK_MALFORMED)!=0)? Table.SLHM : Table.FB;
      if (ic>0 && ic==ib-1) {
        mode = Parser.find(Table.tableModesList,string.substring(0,ic),mode);
        string = string.substring(ib);
      }
      Table t = new Table(Table.FB);
      t.fromBytes(string.getBytes());
      if (mode!=Table.FB) t.setMode(mode);
      return t;
    }
    Results rt = ref2Results(ref);
    if (rt != null) obj = rt.get(string); // since 3.5.3 prevent NPE BUG 2876
    if (obj instanceof Table) return (Table)obj;
//  if (obj instanceof Hashtable) return new Table((Hashtable<?, ?>)obj); // prior to 3.7.0 (constructor with Hashtable param is now deprecated)
    if (obj instanceof Map) return new Table((Map<String, ?>) obj);       // since 3.7.0 as part of Bug 2949 and Bug 2938
    if (obj instanceof Tablizable) return ((Tablizable)obj).toTable();
    TextFile tf = new TextFile(ref2MidasReference(ref),string);
    if (tf.find(IOResource.DIR_INPUT)) {
      String fname = tf.getURL().toLowerCase();
      if (fname.endsWith(".tbl")) { return new Table(tf); }
      if (fname.endsWith(".xml")) { return XmlFile.fastXmlFileToTable(tf); }
    }
    return null;
  }

  /** Convert an Object to a byte array. Converts Table, String and Data object arrays or double,
      float, int short and byte primitive type arrays.
      @param obj Object to convert
      @return byte[] or null if not a valid type
   */
  public static byte[] o2ba (Object obj) {
    if      (obj instanceof Table)    return ((Table)obj).toBytes();
    else if (obj instanceof String)   return ((String)obj).getBytes();
    else if (obj instanceof Data)     return ((Data)obj).buf;
    else if (obj instanceof float[])  return ja2ba((float[]) obj);
    else if (obj instanceof double[]) return ja2ba((double[])obj);
    else if (obj instanceof long[])   return ja2ba((long[])   obj); // since 4.0.0
    else if (obj instanceof int[])    return ja2ba((int[])   obj);
    else if (obj instanceof short[])  return ja2ba((short[]) obj);
    else if (obj instanceof byte[])   return (byte[]) obj;
    else return null;
  }

  /** Convert a Java Array (ja) of double to a byte array (ba).
      @param ja double array to convert
      @return byte array
      @see #packD(byte[],int,double)
   */
  public static byte[] ja2ba (double[] ja) {
    int size = ja.length;
    byte[] buf = new byte[size*8];
    for (int i=0; i<size; i++) packD(buf,i*8,ja[i]);
    return buf;
  }

  /** Convert an array of float to a byte array.
      @param ja float array to convert
      @return byte array
      @see #packF(byte[],int,float)
   */
  public static byte[] ja2ba (float[] ja) {
    int size = ja.length;
    byte[] buf = new byte[size*4];
    for (int i=0; i<size; i++) packF(buf,i*4,ja[i]);
    return buf;
  }

  /** Convert an array of long (X:) to a byte array.
      @param ja long array to convert
      @return byte array
      @see #packX(byte[],int,long)
      @since NeXtMidas 4.0.0 for NXM-3728
   */
  public static byte[] ja2ba (long[] ja) {
    int size = ja.length;
    byte[] buf = new byte[size*8];
    for (int i=0; i<size; i++) packX(buf,i*8,ja[i]);
    return buf;
  }

  /** Convert an array of int (L:) to a byte array.
      @param ja int array to convert
      @return byte array
      @see #packL(byte[],int,int)
   */
  public static byte[] ja2ba (int[] ja) {
    int size = ja.length;
    byte[] buf = new byte[size*4];
    for (int i=0; i<size; i++) packL(buf,i*4,ja[i]);
    return buf;
  }

  /** Convert an array of short (I:) to a byte array.
      @param ja short array to convert
      @return byte array
      @see #packL(byte[],int,int)
   */
  public static byte[] ja2ba (short[] ja) {
    int size = ja.length;
    byte[] buf = new byte[size*2];
    for (int i=0; i<size; i++) packI(buf,i*2,ja[i]);
    return buf;
  }

  /** Unpack Packed Bit data element from byte buffer
      <br><br>
      Note: Packed bits are used for storage and transport only. Prior to processing,
      packed data is automatically normalized to a full byte.
      @param buf byte array of data
      @param off Bit Offset into array
      @return byte of Packed data
   */
  public static byte unpackP (byte[] buf, int off) {
    // This matches the macro defined in Native.c
    return (byte)((buf[(off)>>3]>>((off)&0x7))&0x1);
  }

  /** Unpack Packed Bit data element from byte buffer
      <br><br>
      Note: Packed bits are used for storage and transport only. Prior to processing,
      packed data is automatically normalized to a full byte.
      @param buf byte array of data
      @param off Bit Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return byte of Packed data
      @since NeXtMidas 3.5.0
   */
  public static byte unpackP (byte[] buf, int off, byte rep) {
    int start = off;
    if (rep==IEEE)  start = ((off/8)*8) + (7-(off%8));  // adjust for Big Endian
    return (byte)((buf[(start)>>3]>>((start)&0x7))&0x1);
  }

  /** Unpack Packed Nibble data element from byte buffer.
      <br><br>
      Note: Nibbles are used for storage and transport only. Prior to processing,
      Nibble data is automatically normalized to a full byte.
      <br>
      FYI: Two Nibbles are packed into a single byte where the first Nibble goes into
      the low [0-3] bits and the second Nibble goes into the high [4-7] bits.
      @param buf byte array of data
      @param off Offset into array
      @return byte of Packed data
      @see #packN(byte[], int, byte)
   */
  public static byte unpackN (byte[] buf, int off) {
    // This matches the macro defined in Native.c
    return (byte)((buf[(off)>>1]<<((((off)&1)==0)?28:24) )>>28);
/*
    byte bn = buf[off>>1];
    if ((off&1)==0) bn<<=4;
    return (byte)(bn>>4);
*/
  }

  /** Unpack Packed Nibble data element from byte buffer.
      <br><br>
      Note: Nibbles are used for storage and transport only. Prior to processing,
      Nibble data is automatically normalized to a full byte.
      <br>
      FYI: Two Nibbles are packed into a single byte where the first Nibble goes into
      the low [0-3] bits and the second Nibble goes into the high [4-7] bits.
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return byte of Packed data
      @see #packN(byte[], int, byte)
      @since NeXtMidas 3.5.0
  */
  public static byte unpackN (byte[] buf, int off, byte rep) {
    int start = off;
    if (rep==IEEE) start = ((off/2)*2) + (1-(off%2));
    return (byte)((buf[(start)>>1]<<((((start)&1)==0)?28:24) )>>28);
  }

  /** Unpack byte from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return byte value
   */
  public static byte unpackB (byte[] buf, int off) {
    return buf[off];
  }

  /** Unpack char from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return char value
   */
  public static char unpackC (byte[] buf, int off) {
    return (char)buf[off];
  }

  /** Unpack a 12 bit data element from byte buffer.
      <br><br>
      Note: JINT 12b are used for storage and transport only. Prior to processing,
      JINT data is automatically left shifted to a full short.
      <br>
      FYI: Two JINTs are packed into 3 bytes.
      @param buf byte array of data
      @param off Offset into array - in elements
      @param rep The byte representation to use (IEEE or EEEI)
      @return short of 12-bit data
      @since NeXtMidas 4.1.2 Jeff NXM-3894 contribution
  */
  public static short unpackJ (byte[] buf, int off, byte rep) {
    int boff = (off/2)*3;
    if ((off&1)==0) {
      return (short)((rep==IEEE)?
                 ((int)buf[boff+0]&0xFF)<<12 | ((int)buf[boff+1]&0xFF)<<4  :  // IEEE
                 ((int)buf[boff+1]&0xFF)<<12 | ((int)buf[boff+0]&0xFF)<<4  ); // EEEI

    } else {
      return (short)((Shell.rep==IEEE)?
                 ((int)buf[boff+0]&0xFF)<<8  | ((int)buf[boff+1]&0xF0) :  // IEEE
                 ((int)buf[boff+2]&0xFF)<<8  | ((int)buf[boff+1]&0xF0) ); // EEEI
    }
  }

  /** Unpack a 12 bit data element from byte buffer.
    <br><br>
    Note: JINT 12b are used for storage and transport only. Prior to processing,
    JINT data is automatically left shifted to a full short.
    <br>
    FYI: Two JINTs are packed into 3 bytes.
    @param buf byte array of data
    @param off Offset into array - in elements
    @return short of 12-bit data
    @since NeXtMidas 4.1.2 Jeff NXM-3894 contribution
  */
  public static short unpackJ (byte[] buf, int off) { return unpackJ(buf,off,Shell.rep); }

  /** Unpack short (I:) from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return short value
   */
  public static short unpackI (byte[] buf, int off) {
    // Except for the if, this should match unpackI(buf,off,rep)
    return (short)((Shell.rep==IEEE)?
                 ((int)buf[off+1]&0xFF) | (((int)buf[off+0]&0xFF)<<8):  // IEEE
                 ((int)buf[off+0]&0xFF) | (((int)buf[off+1]&0xFF)<<8)); // EEEI
  }

  /** Unpack short (I:) from a byte buffer
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return short value
   */
  public static short unpackI (byte[] buf, int off, byte rep) {
    // Except for the if, this should match unpackI(buf,off)
    return (short)((rep==IEEE)?
                 ((int)buf[off+1]&0xFF) | (((int)buf[off+0]&0xFF)<<8):  // IEEE
                 ((int)buf[off+0]&0xFF) | (((int)buf[off+1]&0xFF)<<8)); // EEEI
  }

  /** Unpack int (L:) from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return int value
   */
  public static int unpackL (byte[] buf, int off) {
    // Except for the if, this should match unpackL(buf,off,rep)
    return (Shell.rep==IEEE)?
             ((int)buf[off+3]&0xFF) | (((int)buf[off+2]&0xFF)<<8) | (((int)buf[off+1]&0xFF)<<16) | (((int)buf[off+0])<<24): // IEEE
             ((int)buf[off+0]&0xFF) | (((int)buf[off+1]&0xFF)<<8) | (((int)buf[off+2]&0xFF)<<16) | (((int)buf[off+3])<<24); // EEEI
  }

  /** Unpack int (L:) from a byte buffer
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return int value
   */
  public static int unpackL (byte[] buf, int off, byte rep) {
    // Except for the if, this should match unpackL(buf,off)
    return (rep==IEEE)?
             ((int)buf[off+3]&0xFF) | (((int)buf[off+2]&0xFF)<<8) | (((int)buf[off+1]&0xFF)<<16) | (((int)buf[off+0])<<24): // IEEE
             ((int)buf[off+0]&0xFF) | (((int)buf[off+1]&0xFF)<<8) | (((int)buf[off+2]&0xFF)<<16) | (((int)buf[off+3])<<24); // EEEI
  }

  /** Unpack unsigned short (U:) into an int from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return int value
      @since 3.9.3 used in unpacking U: type keyword for NXM-3021
   */
  @InternalUseOnly
  public static int unpackU (byte[] buf, int off) {
    return unpackUI(buf, off, Shell.rep);
  }

  /** Unpack unsigned short (U:) into an int from a byte buffer
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return int value
      @since 3.9.3 used in unpacking U: type keyword for NXM-3021
   */
  @InternalUseOnly
  public static int unpackUI (byte[] buf, int off, byte rep) {
    return ((rep==IEEE)?
           Short.toUnsignedInt((short)((short)(buf[off+1]&0xFF) | ((short)(buf[off+0]&0xFF)<<8))):  // IEEE
           Short.toUnsignedInt((short)((short)(buf[off+0]&0xFF) | ((short)(buf[off+1]&0xFF)<<8)))); // EEEI
  }

  /** Unpack unsigned int (V:) into a long from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return long value
      @since 3.9.3 used in unpacking V: type keyword for NXM-3021
  */
  @InternalUseOnly
  public static long unpackUL (byte[] buf, int off) {
    return unpackV(buf, off, Shell.rep);
  }

  /** Unpack unsigned int (V:) into a long from a byte buffer
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return long value
      @since 3.9.3 used in unpacking V: type keyword for NXM-3021
  */
  @InternalUseOnly
  public static long unpackV (byte[] buf, int off, byte rep) {
    return (rep==IEEE)?
           Integer.toUnsignedLong((buf[off+3]&0xFF) | ((buf[off+2]&0xFF)<<8) | ((buf[off+1]&0xFF)<<16) | ((buf[off+0])<<24)): // IEEE
           Integer.toUnsignedLong((buf[off+0]&0xFF) | ((buf[off+1]&0xFF)<<8) | ((buf[off+2]&0xFF)<<16) | ((buf[off+3])<<24)); // EEEI
  }

  /** Unpack long from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return long value
   */
  public static long unpackX (byte[] buf, int off) {
    // Except for the if, this should match unpackX(buf,off,rep)
    int val0,val1;
    if (Shell.rep==IEEE) {
      val0 = ((int)buf[off+7]&0xFF) | (((int)buf[off+6]&0xFF)<<8) | (((int)buf[off+5]&0xFF)<<16) | (((int)buf[off+4])<<24); // IEEE
      val1 = ((int)buf[off+3]&0xFF) | (((int)buf[off+2]&0xFF)<<8) | (((int)buf[off+1]&0xFF)<<16) | (((int)buf[off+0])<<24); // IEEE
    } else {
      val0 = ((int)buf[off+0]&0xFF) | (((int)buf[off+1]&0xFF)<<8) | (((int)buf[off+2]&0xFF)<<16) | (((int)buf[off+3])<<24); // EEEI
      val1 = ((int)buf[off+4]&0xFF) | (((int)buf[off+5]&0xFF)<<8) | (((int)buf[off+6]&0xFF)<<16) | (((int)buf[off+7])<<24); // EEEI
    }
    return ((long)val0&0xFFFFFFFFL) | (((long)val1)<<32);
  }

  /** Unpack long from a byte buffer
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return long value
   */
  public static long unpackX (byte[] buf, int off, byte rep) {
    // Except for the if, this should match unpackX(buf,off)
    int val0,val1;
    if (rep==IEEE) {
      val0 = ((int)buf[off+7]&0xFF) | (((int)buf[off+6]&0xFF)<<8) | (((int)buf[off+5]&0xFF)<<16) | (((int)buf[off+4])<<24); // IEEE
      val1 = ((int)buf[off+3]&0xFF) | (((int)buf[off+2]&0xFF)<<8) | (((int)buf[off+1]&0xFF)<<16) | (((int)buf[off+0])<<24); // IEEE
    } else {
      val0 = ((int)buf[off+0]&0xFF) | (((int)buf[off+1]&0xFF)<<8) | (((int)buf[off+2]&0xFF)<<16) | (((int)buf[off+3])<<24); // EEEI
      val1 = ((int)buf[off+4]&0xFF) | (((int)buf[off+5]&0xFF)<<8) | (((int)buf[off+6]&0xFF)<<16) | (((int)buf[off+7])<<24); // EEEI
    }
    return ((long)val0&0xFFFFFFFFL) | (((long)val1)<<32);
  }

  /** Unpack float from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return float value
   */
  public static float unpackF (byte[] buf, int off) {
    return Float.intBitsToFloat( unpackL(buf,off) );
  }

  /** Unpack float from a byte buffer
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return float value
   */
  public static float unpackF (byte[] buf, int off, byte rep) {
    return Float.intBitsToFloat( unpackL(buf,off,rep) );
  }

  /** Unpack double from a byte buffer
      @param buf byte array of data
      @param off byte offset into array
      @return double value
   */
  public static double unpackD (byte[] buf, int off) {
    return Double.longBitsToDouble( unpackX(buf,off) );
  }

  /** Unpack double from a byte buffer
      @param buf byte array of data
      @param off Offset into array
      @param rep The byte representation to use (IEEE or EEEI)
      @return double value
   */
  public static double unpackD (byte[] buf, int off, byte rep) {
    return Double.longBitsToDouble( unpackX(buf,off,rep) );
  }

  /** Unpack ASCII byte array from a byte buffer
      @param buf byte array of data
      @param off byte offset into input array
      @param len Length of array to return
      @return byte array
   */
  public static byte[] unpackA (byte[] buf, int off, int len) {
    byte[] val = new byte[len];
    System.arraycopy(buf,off,val,0,len);
    return val;
  }


  /** Unpack ASCII byte array from a byte buffer
    @param buf byte array of data
    @param off byte offset into input array
    @param len Length of array to return
    @return byte array
    @since NeXtMidas 3.9.4 for NXM-3570
  */
  public static byte[] unpackZ (byte[] buf, int off, int len) {
    byte[] val = new byte[len];
    System.arraycopy(buf,off,val,0,len);
    return val;
  }

  /** Unpack String from a byte buffer (this assumes that the byte buffer is holding a Fortran
      string where trailing spaces should be ignored).
      @param buf byte array of data
      @param off Offset into input byte array
      @param len Length of String to return
      @return String
   */
  public static String unpackS (byte[] buf, int off, int len) {
    for (;len>0 && buf[off+len-1]<=SPACE; len--); // Remove trailing spaces
    // JC4: The String constructors that take in an array of bytes are SLOW because they call
    //      StringCoding.decode(charsetName,...) which handles localization checks and can
    //      potentially read in Unicode data. If we are just using ASCII data we can do the
    //      conversion much faster. (See Bug 854 for details)
    //
    // return new String(buf,off,len);
    char[] chars = new char[len];
    for (int i = 0; i < len; i++) { chars[i] = (char)buf[off+i]; }
    return new String(chars);
  }


  /** Pack Packed Bit data into byte buffer
      <br><br>
      Note: Packed bits are used for storage and transport only. Prior to processing,
      packed data is automatically normalized to a full byte.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
   */
  public static void packP (byte[] buf, int off, byte val) {
    // This matches the macro defined in Native.c
    if (val==0) buf[(off)>>3] &= ~(0x1<<((off)&0x7));
    else        buf[(off)>>3] |=  (0x1<<((off)&0x7));
  }

  /** Pack Packed Bit data into byte buffer
      <br><br>
      Note: Packed bits are used for storage and transport only. Prior to processing,
      packed data is automatically normalized to a full byte.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @param rep The byte representation to use (IEEE or EEEI)
      @since NeXtMidas 3.5.0
   */
  public static void packP (byte[] buf, int off, byte val, byte rep) {
    int start = off;
    if (rep==IEEE) start = ((off/8)*8) + (7-(off%8));
    packP (buf, start, val);
  }

  /** Pack Packed Nibble data into byte buffer.
      <br><br>
      Note: Nibbles are used for storage and transport only. Prior to processing,
      Nibble data is automatically normalized to a full byte.
      <br>
      Two Nibbles are packed into a single byte where the first Nibble goes into
      the low [0-3] bits and the second Nibble goes into the high [4-7] bits.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @see #unpackN(byte[], int)
   */
  public static void packN (byte[] buf, int off, byte val) {
    // This matches the macro defined in Native.c
    int boff=off>>1, bn = buf[boff];
    bn =  ((off&1)!=0)? (bn&0x0F) | ((val<<4)&0xF0) : (bn&0xF0) | (val&0x0F);
    buf[boff] = (byte)bn;
  }

  /** Pack Packed Nibble data into byte buffer.
      <br><br>
      Note: Nibbles are used for storage and transport only. Prior to processing,
      Nibble data is automatically normalized to a full byte.
      <br>
      Two Nibbles are packed into a single byte where the first Nibble goes into
      the low [0-3] bits and the second Nibble goes into the high [4-7] bits.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @param rep The byte representation to use (IEEE or EEEI)
      @see #unpackN(byte[], int, byte)
      @since NeXtMidas 3.5.0
   */
  public static void packN (byte[] buf, int off, byte val, byte rep) {
    int start = off;
    if (rep==IEEE) start = ((off/2)*2)+ (1-(off%2));
    packN (buf, start, val);
  }

  /** Pack byte data into byte buffer.
     @param buf Packed byte array [OUTPUT]
     @param off Offset into output array
     @param val value to pack [INPUT]
   */
  public static void packB (byte[] buf, int off, byte val) {
    buf[off] = val;
  }

  /** Pack char data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
   */
  public static void packC (byte[] buf, int off, char val) {
    buf[off] = (byte)val;
  }

  /** Pack short (I:) data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
   */
  public static void packI (byte[] buf, int off, short val) {
    // Except for the if, this should match packI(buf,off,val,rep)
    if (Shell.rep==IEEE) {
      buf[off+1] = (byte)(val);
      buf[off+0] = (byte)(val>>8);
    } else {
      buf[off+0] = (byte)(val);
      buf[off+1] = (byte)(val>>8);
    }
  }

  /** Pack short (I:) data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @param rep The byte representation to use (IEEE or EEEI)
   */
  public static void packI (byte[] buf, int off, short val, byte rep) {
    // Except for the if, this should match packI(buf,off,val)
    if (rep==IEEE) {
      buf[off+1] = (byte)(val);
      buf[off+0] = (byte)(val>>8);
    } else {
      buf[off+0] = (byte)(val);
      buf[off+1] = (byte)(val>>8);
    }
  }

  /** Pack int (L:) data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
   */
  public static void packL (byte[] buf, int off, int val) {
    // Except for the if, this should match packL(buf,off,val,rep)
   if (Shell.rep==IEEE) {
    buf[off+3] = (byte)(val);
    buf[off+2] = (byte)(val>>8);
    buf[off+1] = (byte)(val>>16);
    buf[off+0] = (byte)(val>>24);
   } else {
    buf[off+0] = (byte)(val);
    buf[off+1] = (byte)(val>>8);
    buf[off+2] = (byte)(val>>16);
    buf[off+3] = (byte)(val>>24);
   }
  }

  /** Pack int (L:) data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @param rep The byte representation to use (IEEE or EEEI)
   */
  public static void packL (byte[] buf, int off, int val, byte rep) {
    // Except for the if, this should match packL(buf,off,val)
   if (rep==IEEE) {
    buf[off+3] = (byte)(val);
    buf[off+2] = (byte)(val>>8);
    buf[off+1] = (byte)(val>>16);
    buf[off+0] = (byte)(val>>24);
   } else {
    buf[off+0] = (byte)(val);
    buf[off+1] = (byte)(val>>8);
    buf[off+2] = (byte)(val>>16);
    buf[off+3] = (byte)(val>>24);
   }
  }

  /** Pack long data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
   */
  public static void packX (byte[] buf, int off, long val) {
    // Except for the if, this should match packX(buf,off,val,rep)
    int val1 = (int)(val);
    int val2 = (int)(val>>>32);
    if (Shell.rep==IEEE) {
      buf[off+7] = (byte)(val1);
      buf[off+6] = (byte)(val1>>8);
      buf[off+5] = (byte)(val1>>16);
      buf[off+4] = (byte)(val1>>24);
      buf[off+3] = (byte)(val2);
      buf[off+2] = (byte)(val2>>8);
      buf[off+1] = (byte)(val2>>16);
      buf[off+0] = (byte)(val2>>24);
    } else {
      buf[off+0] = (byte)(val1);
      buf[off+1] = (byte)(val1>>8);
      buf[off+2] = (byte)(val1>>16);
      buf[off+3] = (byte)(val1>>24);
      buf[off+4] = (byte)(val2);
      buf[off+5] = (byte)(val2>>8);
      buf[off+6] = (byte)(val2>>16);
      buf[off+7] = (byte)(val2>>24);
    }
  }

  /** Pack long data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @param rep The byte representation to use (IEEE or EEEI)
   */
  public static void packX (byte[] buf, int off, long val, byte rep) {
    // Except for the if, this should match packX(buf,off,val)
    int val1 = (int)(val);
    int val2 = (int)(val>>>32);
    if (rep==IEEE) {
      buf[off+7] = (byte)(val1);
      buf[off+6] = (byte)(val1>>8);
      buf[off+5] = (byte)(val1>>16);
      buf[off+4] = (byte)(val1>>24);
      buf[off+3] = (byte)(val2);
      buf[off+2] = (byte)(val2>>8);
      buf[off+1] = (byte)(val2>>16);
      buf[off+0] = (byte)(val2>>24);
    } else {
      buf[off+0] = (byte)(val1);
      buf[off+1] = (byte)(val1>>8);
      buf[off+2] = (byte)(val1>>16);
      buf[off+3] = (byte)(val1>>24);
      buf[off+4] = (byte)(val2);
      buf[off+5] = (byte)(val2>>8);
      buf[off+6] = (byte)(val2>>16);
      buf[off+7] = (byte)(val2>>24);
    }
  }

  /** Pack float data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
   */
  public static void packF (byte[] buf, int off, float val) {
    packL(buf,off, Float.floatToIntBits(val));
  }

  /** Pack float data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @param rep The byte representation to use (IEEE or EEEI)
   */
  public static void packF (byte[] buf, int off, float val, byte rep) {
    packL(buf,off, Float.floatToIntBits(val), rep);
  }

  /** Pack double data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
   */
  public static void packD (byte[] buf, int off, double val) {
    packX(buf,off, Double.doubleToLongBits(val));
  }

  /** Pack double data into byte buffer.
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param val value to pack [INPUT]
      @param rep The byte representation to use (IEEE or EEEI)
   */
  public static void packD (byte[] buf, int off, double val, byte rep) {
    packX(buf,off, Double.doubleToLongBits(val), rep);
  }

  /** Pack byte array data into byte buffer
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param len Number of elements in input byte array to copy
      @param value value to pack [INPUT]
   */
  public static void packA (byte[] buf, int off, int len, byte[] value) {
    System.arraycopy (value,0, buf,off, len);
  }

  /** Pack byte array data into byte buffer
      @param buf Packed byte array [OUTPUT]
      @param off Offset into output array
      @param len Number of elements in input byte array to copy
      @param value value to pack [INPUT]
      @since NeXtMidas 3.9.4 for NXM-3570
  */
  public static void packZ (byte[] buf, int off, int len, byte[] value) {
    System.arraycopy (value,0, buf,off, len);
  }

  /** Pack String data into byte buffer (this assumes that the byte buffer will hold a Fortran
      string). If the length of the string is less than <code>len</code> it will be padded out
      with spaces, if the length of the string is greater than <code>len</code> it will be
      truncated without warning. It is assumed that all characters in the string are ASCII.
      @param buf   Packed byte array [OUTPUT]
      @param off   Offset into output array
      @param len   Number of characters in string to copy
      @param value Value to pack (null is treated as empty string) [INPUT]
   */
  // NTN: If we remove this API, then classes compiled using previous version of NeXtMidas will get NoSuchMethodError
  public static void packS (byte[] buf, int off, int len, String value) {
    packS(buf,off,len,(CharSequence)value);
  }

  /** Pack String data into byte buffer (this assumes that the byte buffer will hold a Fortran
      string). If the length of the string is less than <code>len</code> it will be padded out
      with spaces, if the length of the string is greater than <code>len</code> it will be
      truncated without warning. It is assumed that all characters in the string are ASCII.
      @param buf   Packed byte array [OUTPUT]
      @param off   Offset into output array
      @param len   Number of characters in string to copy
      @param value Value to pack (null is treated as empty string) [INPUT]
   */
  public static void packS (byte[] buf, int off, int len, CharSequence value) {
    if(value == null) { value = ""; }
    int lenv = Math.min( value.length(), len );
    for (int i=0; i<lenv; i++) buf[off+i] = (byte)value.charAt(i);
    for (int i=lenv; i<len; i++) buf[off+i] = (byte)SPACE;
  }

  /*  Mimic of Jeff's Native.c packJs*/
  static void packJs (byte[] b16, byte[] b12,int off, int size) {
    int i;
    int b16idx = 0;
    int b12idx = 0;

    for (i=0; i<size/2; i++,b16idx+=4,b12idx+=3) {
      b12[b12idx+0] = (byte) ((b16[b16idx+1]<<4)|((b16[b16idx+0]>>4)&0x0F));
      b12[b12idx+1] = (byte) ((b16[b16idx+2]&0xF0)|((b16[b16idx+1]>>4)&0x0F));
      b12[b12idx+2] =  b16[b16idx+3];
    }

    // Allow for SJ - used by Results
    if ((size & 0x1) != 0) {
      if ((off & 0x1) == 0) {
        b12[b12idx+0] = (byte) ((b16[b16idx+1]<<4)|((b16[b16idx+0]>>4)&0x0F));
        b12[b12idx+1] = (byte) ((b16[b16idx+1]>>4)&0x0F);
      }
      else {
        b12[b12idx+0] = (byte) ((b16[b16idx+1]&0xF0)|((b16[b16idx+0]>>4)&0x0F));
        b12[b12idx+1] =  b16[b16idx+2];
      }
    }
  }

  /** Convert string to a SHA1 encoded byte array
      @param s String to encode
      @return encoded String
      @since NeXtMidas 3.6.0
   */
  @ProvisionalUseOnly("Added in NeXtMidas 3.6.0")
  public static byte[] getSHA1 (String s) {
    byte[] sha1=null;
    try {
      MessageDigest crypt = MessageDigest.getInstance("SHA1");
      crypt.reset();
      crypt.update(s.getBytes("UTF-8"));
      sha1 = crypt.digest();
    }
    catch (Exception e) { System.out.println("SHA-1 err: "+e); }
    return sha1;
  }

  /** Convert string to a base64 encoded string
      @param s String to encode
      @return encoded String
   */
  public static String encodeBase64 (String s) {
    return encodeBase64 (s.getBytes());
  }

  /** Convert byte array to a base64 encoded string
      @param octetString byte array to encode
      @return encoded String
   */
  public static String encodeBase64 (byte[] octetString) {
    int  bits24, bits6;

    char [ ]  out = new char [ ( ( octetString.length - 1 ) / 3 + 1 ) * 4 ];

    int outIndex = 0;
    int i        = 0;

    while ( ( i + 3 ) <= octetString.length ) {
      // store the octets
      bits24  = ( octetString [ i++ ] & 0xFF ) << 16;
      bits24 |= ( octetString [ i++ ] & 0xFF ) <<  8;
      bits24 |= ( octetString [ i++ ] & 0xFF ) <<  0;

      bits6 = ( bits24 & 0x00FC0000 ) >> 18;
      out [ outIndex++ ] = alphabet [ bits6 ];
      bits6 = ( bits24 & 0x0003F000 ) >> 12;
      out [ outIndex++ ] = alphabet [ bits6 ];
      bits6 = ( bits24 & 0x00000FC0 ) >> 6;
      out [ outIndex++ ] = alphabet [ bits6 ];
      bits6 = ( bits24 & 0x0000003F );
      out [ outIndex++ ] = alphabet [ bits6 ];
    }

    if ( octetString.length - i == 2 ) {
      // store the octets
      bits24  = ( octetString [ i     ] & 0xFF ) << 16;
      bits24 |= ( octetString [ i + 1 ] & 0xFF ) <<  8;

      bits6 = ( bits24 & 0x00FC0000 ) >> 18;
      out [ outIndex++ ] = alphabet [ bits6 ];
      bits6 = ( bits24 & 0x0003F000 ) >> 12;
      out [ outIndex++ ] = alphabet [ bits6 ];
      bits6 = ( bits24 & 0x00000FC0 ) >> 6;
      out [ outIndex++ ] = alphabet [ bits6 ];

      // padding
      out [ outIndex++ ] = '=';
    }
    else if ( octetString.length - i == 1 ) {
      // store the octets
      bits24 = ( octetString [ i ] & 0xFF ) << 16;

      bits6 = ( bits24 & 0x00FC0000 ) >> 18;
      out [ outIndex++ ] = alphabet [ bits6 ];
      bits6 = ( bits24 & 0x0003F000 ) >> 12;
      out [ outIndex++ ] = alphabet [ bits6 ];

      // padding
      out [ outIndex++ ] = '=';
      out [ outIndex++ ] = '=';
    }
    return new String ( out );
  }

  /** Check for valid Conversion type character.
      @param type Conversion type as a char
      @return true if a valid conversion type
   */
  public static boolean validType (char type) {
    return (DataTypes.CONVERT_TYPE_LIST.indexOf(type) >=0);
  }

  /** Convert data buffer to array
      @param data Input Data to convert
      @param array Output Object array
      @param aoff  Array offset
      @param atype Array type
   */
  public static void data2ja (Data data, Object array, int aoff, byte atype) {
    int n = data.size * data.ape * data.spa;
    bb2ja(data.buf,0,data.type, array,aoff,atype, n);
  }

  /** Convert value from Data buffer to double
   *
   *  @param data  The data object
   *  @param offset The offset into the data buffer
   *  @param format The format of the object in the buffer to be converted (F,D,X,L,I,B)
   *  @return the original object converted to double
   */
  public static double data2d (Data data, int offset, char format) {
    double result = 0;
    switch (format) {
      case FLOAT:  result = data.unpackF(offset); break;
      case DOUBLE: result = data.unpackD(offset); break;
      case XLONG:  result = data.unpackX(offset); break;
      case LONG:   result = data.unpackL(offset); break;
      case INT:    result = data.unpackI(offset); break;
      case BYTE:   result = data.unpackB(offset); break;
      default:     result = 0.0;                  break;
    }
    return result;
  }

  /** Convert typed byte buffer to array.
      @param buf1 Input byte array buffer
      @param type1 Input type
      @param buf2 Output byte array buffer
      @param type2 Output type
      @param n Number of elements to convert
   */
  public static void bb2ja (byte[] buf1, byte type1, Object buf2, byte type2, int n) {
    bb2ja (buf1,0,type1,buf2,0,type2,n);
  }

  private static final String hexstr="0123456789ABCDEF0123456789ABCDEF";

  /** Convert typed byte buffer to Hexadecimal string.
      @param buf byte array to convert
      @param offset Offset into byte array
      @param bpe Bytes/element in byte array
      @return Hexadecimal String
   */
  public static String bb2hex (byte[] buf, int offset, int bpe) {
    int i,j,k; String text = "0x";
    if (bpe<0) { // packed data
      if (offset<0) offset /= bpe;
      if (bpe == -1) text += hexstr.charAt(unpackP(buf,offset));
      if (bpe == -4) text += hexstr.charAt(unpackN(buf,offset)+16);
      // NXM-3894 Jeff's 12-bit J contribution
      if (bpe == -12) { j=unpackJ(buf,offset); for (k=12;k>=4;k-=4) text += hexstr.charAt((j>>k)&0xF); }
    }
    if (Shell.osrep == "EEEI") {
      for (i=bpe-1; i>=0; i--) {
        j = buf[offset+i];
        k = (j>>4)&0xF; text += hexstr.charAt(k);
        k = (j)&0xF; text += hexstr.charAt(k);
      }
    } else {
      for (i=0;i<=bpe-1; i++) {
        j = buf[offset+i];
        k = (j>>4)&0xF; text += hexstr.charAt(k);
        k = (j)&0xF; text += hexstr.charAt(k);
      }
    }
    return text;
  }

  /** Convert typed byte buffer to Binary string.
      @param buf byte array to convert
      @param offset Offset into byte array
      @param bpe Bytes/element in byte array
      @return Binary String
   */
  public static String bb2bin (byte[] buf, int offset, int bpe) {
    int i,j,k; String text = "0b";

    if (Shell.osrep == "EEEI") {
      for (i=bpe-1; i>=0; i--) {
        j = buf[offset+i];
        for (k=7; k>=0; k--) if ((j&(1<<k))!=0)  text += '1'; else text+='0';
      }
    } else {
      for (i=0;i<=bpe-1; i++) {
        j = buf[offset+i];
        for (k=7; k>=0; k--) if ((j&(1<<k))!=0)  text += '1'; else text+='0';
      }
    }
    return text;
  }

  /** Convert typed byte buffer to an octal string. The byte array passed in
      assumed to be unsigned and will NOT be sign-extended when extending the
      binary value to be a multiple of three bits.<br>
      <br>
      This method is the same as <code>bb2oct(buf,offset,bpe,false)</code>.
      @param buf    Byte array to convert.
      @param offset Offset into byte array.
      @param bpe    Bytes per element in byte array.
      @return The octal string prefixed with "0o".
      @see #bb2oct(byte[],int,int,boolean)
      @since NeXtMidas 1.7.5
   */
  public static String bb2oct(final byte[] buf, final int offset, final int bpe) {
    return bb2oct(buf, offset, bpe, false);
  }

  /** Convert typed byte buffer to an octal string. If specified, the byte array
      will be sign-extended as required when extending the buffer value so it
      contains a multiple of three bits.<br>
      <br>
      Implementation Note: <i>This method uses {@link #bb2bin(byte[],int,int)}
                           to convert <code>buf</code> to a binary value and
                           then converts the binary to octal. This is the least
                           complex way of doing the conversion since the octal
                           values rarely match up with the byte boundaries.</i>

      @param buf    Byte array to convert.
      @param offset Offset into byte array.
      @param bpe    Bytes per element in byte array.
      @param signed Indicates if the value is a 2's complement value that must
                    be sign-extended if leading bits are added to make the total
                    number of bits a multiple of three.

      @return The octal string prefixed with "0o".
      @see #bb2bin(byte[],int,int)
      @since NeXtMidas 1.7.5
   */
  public static String bb2oct(final byte[] buf, final int offset, final int bpe,
                              final boolean signed) {
    // The following is the offset value required to shift from being an ASCII
    // character '0' to an integer 0.
    // shift = 0x150 = ('0' << 2) + ('0' << 1) + ('0' << 0)
    final int shift = 0x150;
    String        bin = bb2bin(buf, offset, bpe); /* Note: Has leading "0b" */
    StringBuilder oct = new StringBuilder(bin.length() / 3);
    int index = 2; /* index value into the binary string. */
    oct.append("0o");

    // If the number of bits is not a multiple of 3, handle the first octal
    // value separately and sign extend as needed.
    switch((bin.length() - 2) % 3) {
      case 2:
        index = 4;
        oct.append((signed ? (bin.charAt(2) << 2) : ('0' << 2))
                 + (bin.charAt(2) << 1)
                 + (bin.charAt(3)     )
                 - shift);
        break;
      case 1:
        index = 3;
        oct.append((signed ? (bin.charAt(2) << 2) : ('0' << 2))
                 + (signed ? (bin.charAt(2) << 1) : ('0' << 1))
                 + bin.charAt(2)
                 - shift);
        break;
    }

    for(; index < bin.length(); index += 3) {
      oct.append((bin.charAt(  index  ) << 2)
               + (bin.charAt(index + 1) << 1)
               + (bin.charAt(index + 2)     )
               - shift);
    }
    return oct.toString();
  }

  /** Convert array to Data buffer
      @param array Object array
      @param aoff Offset into Object array
      @param atype Type of data in array
      @param data Data object
   */
  public static void ja2data (Object array, int aoff, byte atype, Data data) {
    int n = data.size * data.ape * data.spa;
    ja2bb(array,aoff,atype, data.buf,0,data.type, n);
  }

  /** Convert array to typed byte buffer
      @param buf1 Object array to convert
      @param type1 Type of buf1
      @param buf2 Output byte array
      @param type2 Type of buf2
      @param n Number of elements to convert
   */
  public static void ja2bb (Object buf1, byte type1, byte[] buf2, byte type2, int n) {
    ja2bb (buf1,0,type1,buf2,0,type2,n);
  }

  /** VAX - VAX/VMS Little-Endian Floating Point.
      <pre>
             bit: 31                       0
                 +---+----------+-----------+
                 | s |  e7..e0  |  f22..f0  |        &lt;-- Single Precision
                 +---+----------+-----------+
                   1    8 bits     23 bits

             Value = (-1)**s x 2**(e-128) x .1f

             bit: 63                       0
                 +---+-----------+-----------+
                 | s |  e10..e0  |  f51..f0  |       &lt;-- Double Precision (G-format)
                 +---+-----------+-----------+
                   1    11 bits     52 bits

             Value = (-1)**s x 2**(e-1024) x .1f

             Range = min: 0.56*10**-308  (s=0/1, e=1, f=0)
                     max: 0.9*10**+308   (s=0/1, e=2047, f=all ones)

             Special Cases:
                 Zero          0     e=0, f=0, s=0
                 Reserved            e=0, f=0, s=1

             Byte Order:
                 +--------------------------+
                 | b1 b0 b3 b2 b5 b4 b7 b6  |
                 +--------------------------+
             (This peculiar byte order is because VAX floating point originated
              in 16-bit PDP-11 days, so that 16-bit words are swapped.  E.g. note
              comment in VAX FORTRAN User Manual p. 10-13 that sign bit of floating
              point number is not the same bit as sign bit of integer*4 number.)


        Simple math shows that the VAX value is 4 times the IEEE value for both
        single precision and G-format double precision.  Hence:

        To convert IEEE to VAX single precision, SWAP BYTE PAIRS and MULT BY 4
                                                 =============================

        For double precision, SWAP BYTE PAIRS, MULT BY 4 and CONVERT VAX D to G
                              =================================================
      </pre>
   */
  public static void formatVAX() {}

  /** IEEE/VAX Big/Little Endian Ordering.
      <pre>
        EEEI - IEEE-754 Little Endian Floating Point (e.g. IBM-PC):

        Same as IEEE but bytes are in reverse order

             EEEI Byte Order:
                 +--------------------------+
                 | b7 b6 b5 b4 b3 b2 b1 b0  |
                 +--------------------------+

             VAX Byte Order:
                 +--------------------------+
                 | b1 b0 b3 b2 b5 b4 b7 b6  |
                 +--------------------------+

        Hence:
        To convert EEEI to VAX single precision, SWAP WORDS and MULT BY 4
                                                 ========================

        For double precision, REVERSE WORDS, MULT BY 4 and CONVERT VAX D to G
                              ===============================================
      </pre>
   */
  public static void formatIEEE() {}

  /** Copy disjoint bytes from one array to another
      @param buf1 Input byte array
      @param off1 Offset into buf1
      @param stride1 Stride for buf1
      @param buf2 Output byte array
      @param off2 Offset for buf2
      @param stride2 Stride for buf2
      @param bpe Bytes per element
      @param n Number of elements to move
   */
  public static void move (byte[] buf1, int off1, int stride1,
                           byte[] buf2, int off2, int stride2, int bpe, int n) {
    // if (Native.useConvert&&n>1) { Native.move (buf1,off1,stride1,buf2,off2,stride2,bpe,n); return; }
    for (; n>0; n--) {
      System.arraycopy (buf1,off1, buf2,off2, bpe);
      off1 += stride1; off2 += stride2;
    }
  }

  /** Cast a byte array to a double array
      @param buf The input byte array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
      @return the resulting double array.
   */
  public static double[] castD (byte[] buf, boolean copy) {
    if (Native.useCastX) return Native.castD(buf);
    int n = buf.length/8; double[] array = new double[n];
    if (copy) Convert.bb2ja(buf,0,DOUBLE, array,0,DOUBLE, n);
    return array;
  }

  /** Cast a byte array to a float array
      @param buf The input byte array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
      @return the resulting float array.
   */
  public static float[] castF (byte[] buf, boolean copy) {
    if (Native.useCast) return Native.castF(buf);
    int n = buf.length/4; float[] array = new float[n];
    if (copy) Convert.bb2ja(buf,0,FLOAT, array,0,FLOAT, n);
    return array;
  }

  /** Cast a byte array to an int (L) array
      @param buf  The input byte array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
      @return the resulting int array.
   */
  public static int[] castL (byte[] buf, boolean copy) {
    if (Native.useCast) return Native.castL(buf);
    int n = buf.length/4; int[] array = new int[n];
    if (copy) Convert.bb2ja(buf,0,LONG, array,0,LONG, n);
    return array;
  }

  /** Cast a byte array to a short (I) array
      @param buf The input byte array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
      @return the resulting short array.
   */
  public static short[] castI (byte[] buf, boolean copy) {
    if (Native.useCast) return Native.castI(buf);
    int n = buf.length/2; short[] array = new short[n];
    if (copy) Convert.bb2ja(buf,0,INT, array,0,INT, n);
    return array;
  }

  /** Revert a casted array to its standard form
      @param array Input array
      @param buf Output array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
   */
  public static void uncast (double[] array, byte[] buf, boolean copy) {
    if (Native.useCastX) Native.uncastD(buf);
    else if (copy) Convert.ja2bb(array,0,DOUBLE, buf,0,DOUBLE, array.length);
  }

  /** Revert a casted array to its standard form
      @param array Input array
      @param buf Output array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
   */
  public static void uncast (float[] array, byte[] buf, boolean copy) {
    if (Native.useCast) Native.uncastF(buf);
    else if (copy) Convert.ja2bb(array,0,FLOAT, buf,0,FLOAT, array.length);
  }

  /** Revert a casted array to its standard form
      @param array Input array
      @param buf Output array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
   */
  public static void uncast (int[] array, byte[] buf, boolean copy) {
    if (Native.useCast) Native.uncastL(buf);
    else if (copy) Convert.ja2bb(array,0,LONG, buf,0,LONG, array.length);
  }

  /** Revert a casted array to its standard form
      @param array Input array
      @param buf Output array
      @param copy true calls {@link #bb2ja(byte[], int, byte, java.lang.Object, int, byte, int)} to copy array values, false will return an empty array.
   */
  public static void uncast (short[] array, byte[] buf, boolean copy) {
    if (Native.useCast) Native.uncastI(buf);
    else if (copy) Convert.ja2bb(array,0,INT, buf,0,INT, array.length);
  }

  /** Converts rounding mode string to int.
      @param str Rounding mode name (with optional "ROUND_" prefix).
      @param def Default value if str is null.
      @return The numeric ID of the rounding mode.
      @since NeXtMidas 2.5.0
   */
  public static int getRoundingMode (String str, int def) {
    if (str == null) return def;
    if (str.startsWith("ROUND_")) str = str.substring(6);

    return Parser.find(roundingList, str, def, -2);
  }

  /** Converts rounding mode string to int.
      @param round The rounding mode.
      @return The name of the rounding mode (does not include "ROUND_" prefix).
      @since NeXtMidas 2.5.0
   */
  public static String getRoundingModeString (int round) {
    return Parser.get(roundingList, round, -2);
  }

  /** Rounding function to match {@link #ROUND_HALF_UP}. This was used in the NATIVE code prior to
      NeXtMidas 2.5.0 with the following definition (in this case it was ALWAYS casting the result
      to an integer value):
      <pre>
        #define LROUND(N) ((N)&gt;0.0)? (N)+0.5: (N)-0.5
      </pre>
      @param n Number to round.
      @return Properly rounded number as per the above algorithm, Note that this value MUST be cast
              to an integer value.
      @since NeXtMidas 2.5.0
   */
  public static double lround (double n) {
    return (n > 0.0)? (n+0.5): (n-0.5);
  }

  /** Enable this in another commit: This is a copy of the {@link #lround(double)} for float.
  static final float lround (float n) {
    return (n > 0.0f)? (n+0.5f): (n-0.5f);
  }
   */

  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // ----------------------------- ABANDON ALL HOPE, YE WHO ENTER HERE -----------------------------
  //
  //    The remaining section of this file deals with array and buffer conversions. It is highly
  //   optimized and takes into account, various rounding modes and saturating conversion options.
  //   This code is not friendly to look at or modify... it is here for because it has to be here
  //    and it has to be fast. If the Javadocs for the type(..), bb2ja(..) and ja2bb(..) methods
  //            are not sufficient to explain how they function, call J. Czechowski and ask him.
  //
  //
  // ------------------------------------------- WARNING -------------------------------------------
  //       Any changes to the code below must be mirrored in the type(..) method in Native.c.
  //
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////////////////


  /////////////////////////////////////////////////////////////////////////////////////////////////
  // Cast methods used internal to bb2ja, ja2bb, and type
  //    These match the methods in Native.c, except that they input type is automatically
  //    determined by the Java complier (which is why we use n2x here but use d2x,f2x,...
  //    in the C code).
  //
  // Narrowing conversions in Java:
  //    In Java casting from double to float, long, and int are saturating; as are float to long
  //    and int. See "The Java Virtual Machine Specification Second Edition" (JVMS) Section 3.11.4.
  //    Also note that any NaN values are automatically changed to 0.
  //
  //    Narrowing conversions between integer types in Java are NOT saturating (they just
  //    discard the high-order bytes. This can result in a possible loss of information about
  //    the magnitude of the numerical value and this may cause the sign of the resulting value to
  //    differ from the sign of the input value.
  //    See JVMS Section 3.11.4 Type Conversion Instructions.
  //
  // Narrowing conversions in C:
  //    According to K&R Section A6, the behavior of narrowing conversions in C is "implementation
  //    dependent" (i.e. undefined).
  //
  //   "The issue is that the C/C++ language standards state that the behavior of floating-point
  //    to integer conversion is undefined, when the resulting value doesn't fit the destination
  //    mode. The Java language specification however requires that NaNs are transformed into zero,
  //    and that overflows saturate, i.e. values larger than INT_MAX produce INT_MAX and values
  //    less than INT_MIN return INT_MIN."
  //                     -- Roger Sayle (http://gcc.gnu.org/ml/gcc-patches/2003-09/msg02090.html)
  //
  /////////////////////////////////////////////////////////////////////////////////////////////////
  // numbers to long|int|byte (TODO: rename back to (d|f|l|i|b)2(l|i|b)_sat ?)
  private static int   n2l_sat (long   val) { return (val>=Integer.MAX_VALUE)? Integer.MAX_VALUE : (val<=Integer.MIN_VALUE)? Integer.MIN_VALUE : (int  )val; }
  private static short n2i_sat (double val) { return (val>=Short.MAX_VALUE  )? Short.MAX_VALUE   : (val<=Short.MIN_VALUE  )? Short.MIN_VALUE   : (short)val; }
  private static short n2i_sat (float  val) { return (val>=Short.MAX_VALUE  )? Short.MAX_VALUE   : (val<=Short.MIN_VALUE  )? Short.MIN_VALUE   : (short)val; }
  private static short n2i_sat (long   val) { return (val>=Short.MAX_VALUE  )? Short.MAX_VALUE   : (val<=Short.MIN_VALUE  )? Short.MIN_VALUE   : (short)val; }
  private static short n2i_sat (int    val) { return (val>=Short.MAX_VALUE  )? Short.MAX_VALUE   : (val<=Short.MIN_VALUE  )? Short.MIN_VALUE   : (short)val; }
  private static byte  n2b_sat (double val) { return (val>=Byte.MAX_VALUE   )? Byte.MAX_VALUE    : (val<=Byte.MIN_VALUE   )? Byte.MIN_VALUE    : (byte )val; }
  private static byte  n2b_sat (float  val) { return (val>=Byte.MAX_VALUE   )? Byte.MAX_VALUE    : (val<=Byte.MIN_VALUE   )? Byte.MIN_VALUE    : (byte )val; }
  private static byte  n2b_sat (long   val) { return (val>=Byte.MAX_VALUE   )? Byte.MAX_VALUE    : (val<=Byte.MIN_VALUE   )? Byte.MIN_VALUE    : (byte )val; }
  private static byte  n2b_sat (int    val) { return (val>=Byte.MAX_VALUE   )? Byte.MAX_VALUE    : (val<=Byte.MIN_VALUE   )? Byte.MIN_VALUE    : (byte )val; }
  private static byte  n2b_sat (short  val) { return (val>=Byte.MAX_VALUE   )? Byte.MAX_VALUE    : (val<=Byte.MIN_VALUE   )? Byte.MIN_VALUE    : (byte )val; }
  // numbers (double, float, long, int, etc.) to nibble
  private static byte  d2n_sat (double val) { return (val>=DataTypes.NMAX   )? DataTypes.NMAX    : (val<=DataTypes.NMIN   )? DataTypes.NMIN    : (byte )val; }
  private static byte  f2n_sat (float  val) { return (val>=DataTypes.NMAX   )? DataTypes.NMAX    : (val<=DataTypes.NMIN   )? DataTypes.NMIN    : (byte )val; }
  private static byte  x2n_sat (long   val) { return (val>=DataTypes.NMAX   )? DataTypes.NMAX    : (val<=DataTypes.NMIN   )? DataTypes.NMIN    : (byte )val; }
  private static byte  l2n_sat (int    val) { return (val>=DataTypes.NMAX   )? DataTypes.NMAX    : (val<=DataTypes.NMIN   )? DataTypes.NMIN    : (byte )val; }
  private static byte  i2n_sat (short  val) { return (val>=DataTypes.NMAX   )? DataTypes.NMAX    : (val<=DataTypes.NMIN   )? DataTypes.NMIN    : (byte )val; }
  private static byte  b2n_sat (byte   val) { return (val>=DataTypes.NMAX   )? DataTypes.NMAX    : (val<=DataTypes.NMIN   )? DataTypes.NMIN    :        val; }

  // The following are only used in bb2ja
  private static final byte DRD = -81; // double, ROUND_DOWN
  private static final byte DRC = -82; // double, ROUND_CEILING
  private static final byte DRF = -83; // double, ROUND_FLOOR
  private static final byte DRE = -84; // double, ROUND_HALF_EVEN
  private static final byte DRU = -85; // double, ROUND_UNNECESSARY
  private static final byte DRR = -86; // double, ROUND_ROUND
  private static final byte DRL = -87; // double, ROUND_HALF_UP (LROUND)
  private static final byte FRD = -41; // float,  ROUND_DOWN
  private static final byte FRC = -42; // float,  ROUND_CEILING
  private static final byte FRF = -43; // float,  ROUND_FLOOR
  private static final byte FRE = -44; // float,  ROUND_HALF_EVEN
  private static final byte FRU = -45; // float,  ROUND_UNNECESSARY
  private static final byte FRR = -46; // float,  ROUND_ROUND
  private static final byte FRL = -47; // float,  ROUND_HALF_UP (LROUND)

  /** Convert typed byte buffer to Java array. This is the same as
      <code>Convert.bb2ja(buf1,off1,type1,buf2,off2,type2,size,{@link #ROUND_DOWN},true)</code>.
      @param buf1  byte array to convert
      @param off1  Offset into buf1
      @param type1 Type of buf1
      @param buf2  Output array (one of <code>double[]</code>, <code>float[]</code>, <code>long[]</code>,
                   <code>int[]</code>, <code>short[]</code>, <code>byte[]</code>).
      @param off2  Offset into buf2
      @param type2 Type of buf2 (must match type of <code>buf2</code>, e.g. <code>'D'-&gt;double[]</code>).
      @param n     Number of elements to convert
   */
  public static void bb2ja (byte[] buf1, int off1, byte type1,
                            Object buf2, int off2, byte type2, int n) {
    // Use of ROUND_DOWN matches NeXtMidas 2.4.2 (one of the tests in %test_calculator depends on this)
    bb2ja(buf1,off1,type1,buf2,off2,type2,n,ROUND_DOWN,true);
  }

  /** Convert Java array to typed byte buffer. This is the same as
      <code>Convert.ja2bb(buf1,off1,type1,buf2,off2,type2,size,{@link #ROUND_DOWN},true)</code>.
      @param buf1  Array to convert (one of <code>double[]</code>, <code>float[]</code>, <code>long[]</code>,
                   <code>int[]</code>, <code>short[]</code>, <code>byte[]</code>).
      @param off1  Offset into buf1
      @param type1 Type of buf1 (must match type of <code>buf1</code>, e.g. <code>'D'-&gt;double[]</code>).
      @param buf2  Output byte array
      @param off2  Offset into buf2
      @param type2 Type of buf2
      @param n     Number of elements to convert
   */
  public static void ja2bb (Object buf1, int off1, byte type1,
                            byte[] buf2, int off2, byte type2, int n) {
    // Use of ROUND_DOWN matches NeXtMidas 2.4.2 (one of the tests in %test_calculator depends on this)
    ja2bb(buf1,off1,type1,buf2,off2,type2,n,ROUND_DOWN,true);
  }

  /** Convert a buffer of one type into a buffer of another type. This is the same as
      <code>Convert.type(buf1,off1,type1,buf2,off2,type2,size,{@link #ROUND_HALF_UP},true)</code>.
      @param buf1  Input buffer of type1 to be converted
      @param off1  Offset (in scalar elements of type1) into Input buffer, 0..n
      @param type1 Type of input buffer
      @param buf2  Output buffer
      @param off2  Offset (in scalar elements of type2) into Output buffer, 0..n
      @param type2 Type of output buffer
      @param n     Number of elements to be converted
      @since NeXtMidas 2.5.2 (Changed first parameter from <code>Object</code> to <code>byte[]</code>.
   */
  public static void type (byte[] buf1, int off1, byte type1,
                           byte[] buf2, int off2, byte type2, int n) {
    // Use of ROUND_HALF_UP matches NATIVE code in NeXtMidas 2.4.2, Java code in 2.4.2 used ROUND_ROUND
    type(buf1,off1,type1,buf2,off2,type2,n,ROUND_HALF_UP,true);
  }

  /** Convert typed byte buffer to Java array.
      @param buf1       byte array to convert
      @param off1       Offset into buf1
      @param type1      Type of buf1
      @param buf2       Output array (one of <code>double[]</code>, <code>float[]</code>, <code>long[]</code>,
                        <code>int[]</code>, <code>short[]</code>, <code>byte[]</code>).
      @param off2       Offset into buf2
      @param type2      Type of buf2 (must match type of <code>buf2</code>, e.g. <code>'D'-&gt;double[]</code>).
      @param n          Number of elements to convert
      @param round      The rounding mode used when converting a floating point value to an integer
                        type This should be one of {@link #ROUND_DOWN}, {@link #ROUND_CEILING},
                        {@link #ROUND_FLOOR}, {@link #ROUND_HALF_EVEN}, {@link #ROUND_UNNECESSARY},
                        or {@link #ROUND_ROUND}). {@link #ROUND_DOWN} is the fastest and matches
                        the behavior of a type cast in Java.
      @param saturating Are conversions saturating? If true, when a narrowing conversion is done
                        (e.g. 'D' to 'L') in which the initial value is outside the limits of
                        the destination type, the min/max value for that type is used. If this is
                        false and the above situation occurs, the results are undefined (and
                        will vary from platform to platform). Setting this to false can improve
                        performance by 20% to 50%.
      @throws IllegalArgumentException if round is not one of the pre-defined modes.
      @throws MidasException if type1 or type2 are non-homogeneous types (since 2.9.2)
      @throws ArithmeticException if <code>round=={@link #ROUND_UNNECESSARY}</code> and floating point
                        to integer (e.g. 'D' to 'L') is being performed.
      @since NeXtMidas 2.5.2
   */
  public static void bb2ja (byte[] buf1, int off1, byte type1,
                            Object buf2, int off2, byte type2, int n, int round, boolean saturating) {
    if (type1 == NONHOMOGENEOUS || type2 == NONHOMOGENEOUS) {
      throw new MidasException("Cannot convert non-homogeneous typed buffer to Java array for "+((char)type1)+"->"+((char)type2)+" conversion not supported.");
    }
    if (Native.useConvert && n>1) { // use fast native conversion if possible
      if (Native.type(buf1,off1,type1, buf2,off2,type2, n, round, saturating) == 0) return; // Returns 0 on success
    }
    int i=off1; n+=off1;
    int j=off2;
    int status = n;

    // NXM-3984 Mimic Jeff's Native.c CJ DataType contribution
    // just move any JINT into INT byte[] array
    if (type1 == JINT) {
      byte[] buf1orig = buf1;
      int buf1Lth = (buf1.length%2 == 0) ? buf1.length*3/2 : buf1.length*3/2 +1; // SJ adjustment
      buf1 = new byte[buf1Lth];
      int bpsInt = Math.max(1,Data.getBPS(INT));
      int iOff=0;
      int jOff=off1;
      int maxIOff=n*bpsInt;
      for (;iOff<maxIOff;iOff+=bpsInt,jOff++) {
        packI(buf1,iOff, (unpackJ(buf1orig,jOff)));
      }
      type1 = INT;
    }

    byte type = type1;
    if ((type1 == DOUBLE) && (type2 != DOUBLE) && (type2 != FLOAT)) {
      switch (round) {
        case ROUND_DOWN:        type = DRD; break;
        case ROUND_CEILING:     type = DRC; break;
        case ROUND_FLOOR:       type = DRF; break;
        case ROUND_HALF_UP:     type = DRL; break;
        case ROUND_HALF_EVEN:   type = DRE; break;
        case ROUND_ROUND:       type = DRR; break;
        case ROUND_UNNECESSARY:
          throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
        default:
          throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
      }
    } else if ((type1 == FLOAT) && (type2 != DOUBLE) && (type2 != FLOAT)) {
      switch (round) {
        case ROUND_DOWN:        type = FRD; break;
        case ROUND_CEILING:     type = FRC; break;
        case ROUND_FLOOR:       type = FRF; break;
        case ROUND_HALF_UP:     type = FRL; break;
        case ROUND_HALF_EVEN:   type = FRE; break;
        case ROUND_ROUND:       type = FRR; break;
        case ROUND_UNNECESSARY:
          throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
        default:
          throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
      }
    }
    // While it may be "cleaner" to just have a unpackD(..) and unpackF(..) that take in the rounding
    // mode and handles the rounding there... the code below is faster since the check for rounding
    // mode is done a TOTAL of ONE time for the whole method call... doing the rounding within the
    // loop would be significantly more expensive.
    if (saturating) {
      switch (type2) {
        case DOUBLE: { double[] array=(double[])buf2; switch (type1) {
          case DOUBLE: for (;i<n;i++,j++) array[j]=(Double.longBitsToDouble(unpackX(buf1,i*8))); break;
          case FLOAT:  for (;i<n;i++,j++) array[j]=(Float.intBitsToFloat(unpackL(buf1,i*4))); break;
          case XLONG:  for (;i<n;i++,j++) array[j]=(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=(unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=(unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=(buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=(unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=(unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case FLOAT: { float[] array=(float[])buf2; switch (type1) {
          case DOUBLE: for (;i<n;i++,j++) array[j]=(float)(Double.longBitsToDouble(unpackX(buf1,i*8))); break;
          case FLOAT:  for (;i<n;i++,j++) array[j]=       (Float.intBitsToFloat(unpackL(buf1,i*4))); break;
          case XLONG:  for (;i<n;i++,j++) array[j]=(float)(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=(float)(unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=(float)(unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=(float)(buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=(float)(unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=(float)(unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case XLONG: { long[] array=(long[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=(long)(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=(long)(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=(long)(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=(long)(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=(long)(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=(long)(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=(long)(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=(long)(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=      (unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=      (unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=      (unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=      (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=      (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=      (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case LONG: { int[] array=(int[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=(int  )(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=(int  )(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=(int  )(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=(int  )(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=(int  )(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=(int  )(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=(int  )(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=(int  )(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=(int  )(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=(int  )(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=(int  )(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=(int  )(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=n2i_sat(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=       (unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=       (unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=       (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=       (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=       (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case INT: { short[] array=(short[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=n2i_sat(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=n2i_sat(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=n2i_sat(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=n2i_sat(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=n2i_sat(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=n2i_sat(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=n2i_sat(unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=       (unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=       (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=       (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=       (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case BYTE: { byte[] array=(byte[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=n2b_sat(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=n2b_sat(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=n2b_sat(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=n2b_sat(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=n2b_sat(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=n2b_sat(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=n2b_sat(unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=n2b_sat(unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=       (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=       (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=       (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case NIBBLE: throw new ClassCastException("Convert: Can not convert "+buf2.getClass()+" to nibble[].");
        case PACKED: throw new ClassCastException("Convert: Can not convert "+buf2.getClass()+" to packed[].");
        default: status=-1;
      }
    } else {
      switch (type2) {
        case DOUBLE: { double[] array=(double[])buf2; switch (type1) {
          case DOUBLE: for (;i<n;i++,j++) array[j]=(Double.longBitsToDouble(unpackX(buf1,i*8))); break;
          case FLOAT:  for (;i<n;i++,j++) array[j]=(Float.intBitsToFloat(unpackL(buf1,i*4))); break;
          case XLONG:  for (;i<n;i++,j++) array[j]=(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=(unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=(unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=(buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=(unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=(unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case FLOAT: { float[] array=(float[])buf2; switch (type1) {
          case DOUBLE: for (;i<n;i++,j++) array[j]=(float)(Double.longBitsToDouble(unpackX(buf1,i*8))); break;
          case FLOAT:  for (;i<n;i++,j++) array[j]=       (Float.intBitsToFloat(unpackL(buf1,i*4))); break;
          case XLONG:  for (;i<n;i++,j++) array[j]=       (unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=       (unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=       (unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=       (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=       (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=       (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case XLONG: { long[] array=(long[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=(long)(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=(long)(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=(long)(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=(long)(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=(long)(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=(long)(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=(long)(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=(long)(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=(long)(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=      (unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=      (unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=      (unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=      (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=      (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=      (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case LONG: { int[] array=(int[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=(int)(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=(int)(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=(int)(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=(int)(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=(int)(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=(int)(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=(int)(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=(int)(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=(int)(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=(int)(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=(int)(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=(int)(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=(int)(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=     (unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=     (unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=     (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=     (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=     (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case INT: { short[] array=(short[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=(short)(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=(short)(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=(short)(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=(short)(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=(short)(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=(short)(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=(short)(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=(short)(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=(short)(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=(short)(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=(short)(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=(short)(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=(short)(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=(short)(unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=       (unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=       (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=       (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=       (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case BYTE: { byte[] array=(byte[])buf2; switch (type) {
          case DRD:    for (;i<n;i++,j++) array[j]=(byte)(                Double.longBitsToDouble(unpackX(buf1,i*8)));  break;
          case DRC:    for (;i<n;i++,j++) array[j]=(byte)(Math.ceil(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRF:    for (;i<n;i++,j++) array[j]=(byte)(Math.floor(     Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRE:    for (;i<n;i++,j++) array[j]=(byte)(Math.rint(      Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRR:    for (;i<n;i++,j++) array[j]=(byte)(Math.floor(0.5d+Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case DRL:    for (;i<n;i++,j++) array[j]=(byte)(Convert.lround( Double.longBitsToDouble(unpackX(buf1,i*8)))); break;
          case FRD:    for (;i<n;i++,j++) array[j]=(byte)(                Float.intBitsToFloat(unpackL(buf1,i*4)));     break;
          case FRC:    for (;i<n;i++,j++) array[j]=(byte)(Math.ceil(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRF:    for (;i<n;i++,j++) array[j]=(byte)(Math.floor(     Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRE:    for (;i<n;i++,j++) array[j]=(byte)(Math.rint(      Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRR:    for (;i<n;i++,j++) array[j]=(byte)(Math.floor(0.5d+Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case FRL:    for (;i<n;i++,j++) array[j]=(byte)(Convert.lround( Float.intBitsToFloat(unpackL(buf1,i*4))));    break;
          case XLONG:  for (;i<n;i++,j++) array[j]=(byte)(unpackX(buf1,i*8)); break;
          case LONG:   for (;i<n;i++,j++) array[j]=(byte)(unpackL(buf1,i*4)); break;
          case INT:    for (;i<n;i++,j++) array[j]=(byte)(unpackI(buf1,i*2)); break;
          case BYTE:   for (;i<n;i++,j++) array[j]=      (buf1[i]);           break;
          case NIBBLE: for (;i<n;i++,j++) array[j]=      (unpackN(buf1,i));   break;
          case PACKED: for (;i<n;i++,j++) array[j]=      (unpackP(buf1,i));   break;
          default: status=-1;
        } } break;
        case NIBBLE: throw new ClassCastException("Convert: Can not convert "+buf2.getClass()+" to nibble[].");
        case PACKED: throw new ClassCastException("Convert: Can not convert "+buf2.getClass()+" to packed[].");
        default: status=-1;
      }
    }
    if (status<0) Shell.warning("ERROR: bb2ja(..) Unknown conversion "+((char)type1)+"->"+((char)type2));
  }

  /** Convert Java array to typed byte buffer.
      @param buf1  Input Array to convert (one of <code>double[]</code>, <code>float[]</code>, <code>long[]</code>,
                        <code>int[]</code>, <code>short[]</code>, <code>byte[]</code>).
      @param off1       Offset into buf1
      @param type1      Type of buf1 (must match type of <code>buf1</code>, e.g. <code>'D'-&gt;double[]</code>).
      @param buf2       Output byte array
      @param off2       Offset into buf2
      @param type2      Type of buf2
      @param n          Number of elements to convert
      @param round      The rounding mode used when converting a floating point value to an integer
                        type This should be one of {@link #ROUND_DOWN}, {@link #ROUND_CEILING},
                        {@link #ROUND_FLOOR}, {@link #ROUND_HALF_EVEN}, {@link #ROUND_UNNECESSARY},
                        or {@link #ROUND_ROUND}). {@link #ROUND_DOWN} is the fastest and matches
                        the behavior of a type cast in Java.
      @param saturating Are conversions saturating? If true, when a narrowing conversion is done
                        (e.g. 'D' to 'L') in which the initial value is outside the limits of
                        the destination type, the min/max value for that type is used. If this is
                        false and the above situation occurs, the results are undefined (and
                        will vary from platform to platform). Setting this to false can improve
                        performance by 20% to 50%.
      @throws IllegalArgumentException if round is not one of the pre-defined modes.
      @throws MidasException if type1 or type2 are non-homogeneous types (since 2.9.2)
      @throws ArithmeticException if <code>round=={@link #ROUND_UNNECESSARY}</code> and floating point
                        to integer (e.g. 'D' to 'L') is being performed.
      @since NeXtMidas 2.5.2
   */
  public static void ja2bb (Object buf1, int off1, byte type1,
                            byte[] buf2, int off2, byte type2, int n, int round, boolean saturating) {
    if (type1 == NONHOMOGENEOUS || type2 == NONHOMOGENEOUS) {
      throw new MidasException("Cannot convert non-homogeneous Java array to typed buffer for "+((char)type1)+"->"+((char)type2)+" conversion not supported.");
    }
    if (Native.useConvert && n>1) { // use fast native conversion if possible
      if (Native.type(buf1,off1,type1, buf2,off2,type2, n, round, saturating) == 0) return; // Returns 0 on success
    }
    int i=off1; n+=off1;
    int j=off2;
    int status = n;

    byte type2s = type2;  /* Mimic Jeff's Native.c addition for J */ // 563 in Native.c
    byte[] buf2orig = buf2;
    int off2orig = off2;
    if (type2==JINT) {
      type2 = INT;        /* first stage */ // 611 in Native.c
      buf2 = new byte[buf2.length*4/3];
      off2 = 0;
    }

    if ((round == ROUND_UNNECESSARY) && ((type2 != FLOAT) || (type2 == DOUBLE))) {
      round = ROUND_DOWN; // Rounding not necessary.
    }
    if (saturating) {
      switch (type1) {
        case DOUBLE: { double[] array=(double[])buf1;
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(array[i])); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(array[i])); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(array[i])); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(array[i]);  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(array[j])); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(array[j])); break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.ceil(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.ceil(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.ceil(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.ceil(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.ceil(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.ceil(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.floor(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.floor(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.floor(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.floor(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT: for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG: for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.rint(array[i]))); break;
                case LONG: for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.rint(array[i]))); break;
                case INT: for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.rint(array[i]))); break;
                case BYTE: for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.rint(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.rint(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.rint(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(0.5d+array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(0.5d+array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.floor(0.5d+array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.floor(0.5d+array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.floor(0.5d+array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.floor(0.5d+array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Convert.lround(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Convert.lround(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Convert.lround(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Convert.lround(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Convert.lround(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Convert.lround(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          }
        } break;
        case FLOAT: { float[] array=(float[])buf1;
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(array[i])); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(array[i])); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(array[i])); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(array[i]);  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  f2n_sat(array[j])); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(array[j])); break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.ceil(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.ceil(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.ceil(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.ceil(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.ceil(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.ceil(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.floor(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.floor(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.floor(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.floor(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.rint(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.rint(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.rint(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.rint(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.rint(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.rint(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(0.5d+array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(0.5d+array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Math.floor(0.5d+array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Math.floor(0.5d+array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Math.floor(0.5d+array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Math.floor(0.5d+array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Convert.lround(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Convert.lround(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(Convert.lround(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(Convert.lround(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  d2n_sat(Convert.lround(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(Convert.lround(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          }
        } break;
        case XLONG: { long[] array=(long[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,       (array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,n2l_sat(array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  x2n_sat(array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(array[j])); break;
            default: status=-1;
          }
        } break;
        case LONG: { int[] array=(int[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,       (array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,       (array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,n2i_sat(array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  l2n_sat(array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(array[j])); break;
            default: status=-1;
          }
        } break;
        case INT: { short[] array=(short[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,       (array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,       (array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,       (array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=       n2b_sat(array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  i2n_sat(array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,  n2b_sat(array[j])); break;
            default: status=-1;
          }
        } break;
        case BYTE: { byte[] array=(byte[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,       (array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,       (array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,       (array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=              (array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  b2n_sat(array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,         (array[j])); break;
            default: status=-1;
          }
        } break;
        case NIBBLE: throw new ClassCastException("Convert: Can not convert "+buf1.getClass()+" to nibble[].");
        case PACKED: throw new ClassCastException("Convert: Can not convert "+buf1.getClass()+" to packed[].");
        default:  status=-1;
      }
    } else {
      switch (type1) {
        case DOUBLE: { double[] array=(double[])buf1;
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(array[i])); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(array[i])); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(array[i])); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(array[i]);  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(array[j])); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(array[j])); break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.ceil(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.ceil(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.ceil(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.ceil(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.ceil(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.ceil(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.floor(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.floor(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.floor(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.floor(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.rint(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.rint(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.rint(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.rint(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.rint(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.rint(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(0.5d+array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(0.5d+array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.floor(0.5d+array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.floor(0.5d+array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.floor(0.5d+array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.floor(0.5d+array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(float)(array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Convert.lround(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Convert.lround(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Convert.lround(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Convert.lround(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Convert.lround(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Convert.lround(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          }
        } break;
        case FLOAT: { float[] array=(float[])buf1;
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(array[i])); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(array[i])); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(array[i])); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(array[i]);  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(array[j])); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(array[j])); break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.ceil(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.ceil(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.ceil(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.ceil(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.ceil(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.ceil(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.floor(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.floor(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.floor(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.floor(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.rint(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.rint(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.rint(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.rint(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.rint(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.rint(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Math.floor(0.5d+array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Math.floor(0.5d+array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Math.floor(0.5d+array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Math.floor(0.5d+array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Math.floor(0.5d+array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Math.floor(0.5d+array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
                case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
                case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(long )(Convert.lround(array[i]))); break;
                case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(Convert.lround(array[i]))); break;
                case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(Convert.lround(array[i]))); break;
                case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(Convert.lround(array[i]));  break;
                case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(Convert.lround(array[j]))); break;
                case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(Convert.lround(array[j]))); break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          }
        } break;
        case XLONG: { long[] array=(long[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,       (array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(int  )(array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(array[j])); break;
            default: status=-1;
          }
        } break;
        case LONG: { int[] array=(int[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,       (array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,       (array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,       (array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,       (array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(short)(array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte )(array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte )(array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte )(array[j])); break;
            default: status=-1;
          }
        } break;
        case INT: { short[] array=(short[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,      (array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,      (array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,      (array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,      (array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,      (array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=       (byte)(array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (byte)(array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (byte)(array[j])); break;
            default: status=-1;
          }
        } break;
        case BYTE: { byte[] array=(byte[])buf1;
          switch (type2) {
            case DOUBLE: for (;i<n;i++,j++) packD(buf2,j*8,(array[i])); break;
            case FLOAT:  for (;i<n;i++,j++) packF(buf2,j*4,(array[i])); break;
            case XLONG:  for (;i<n;i++,j++) packX(buf2,j*8,(array[i])); break;
            case LONG:   for (;i<n;i++,j++) packL(buf2,j*4,(array[i])); break;
            case INT:    for (;i<n;i++,j++) packI(buf2,j*2,(array[i])); break;
            case BYTE:   for (;i<n;i++,j++) buf2[j]=       (array[i]);  break;
            case NIBBLE: for (;i<n;i++,j++) packN(buf2,j,  (array[j])); break;
            case PACKED: for (;i<n;i++,j++) packP(buf2,j,  (array[j])); break;
            default: status=-1;
          }
        } break;
        case NIBBLE: throw new ClassCastException("Convert: Can not convert "+buf1.getClass()+" to nibble[].");
        case PACKED: throw new ClassCastException("Convert: Can not convert "+buf1.getClass()+" to packed[].");
        default:  status=-1;
      }
    }
    if (type2s==JINT) {
      int offset = (int)Math.ceil(off2orig*3.0/2);
      packJs(buf2,buf2orig,offset,n);   /* Mimic Jeff's Native.c J DataType contribution */
    }
    if (status<0) Shell.warning("ERROR: ja2bb(..) Unknown conversion "+((char)type1)+"->"+((char)type2));
  }

  private static boolean isUnsignedType (byte type) {
    if(type=='U' || type=='V') {
      return true;
    }
    return false;
  }

  /**  Set if Java Behavior is to be favored over Midas Behavior
   *
   *   @param preferJavaToMidas true if should prefer Java
   */
  @InternalUseOnly
  public static void setFavorJavaBehavior (boolean preferJavaToMidas) {
    Convert.favorJavaBehavior = preferJavaToMidas;
  }

  /** Set if double should always be used in conversions, even if it will lose precision
   *
   *  @param alwaysDefaultToDouble always default to using double for conversions
   */
  @InternalUseOnly
  public static void setAlwaysDefaultToDouble (boolean alwaysDefaultToDouble) {
    Convert.alwaysDefaultToDouble = alwaysDefaultToDouble;
  }

  /** Set if we are in the mode of minimally supporting unsupported Data Types
   *
   *  @param minSupportUnsupportedDataTypes give some minimal support for unsupported data types
   */
  @InternalUseOnly
  public static void setMinSupportUnsupportedDataTypes (boolean minSupportUnsupportedDataTypes) {
    Convert.minSupportUnsupportedDataTypes = minSupportUnsupportedDataTypes;
  }

  /** Convert a buffer of type Double, Float, Long, Short (I), Byte or Packed into a buffer of
      another type.  Type is indicated as a number as defined  in {@link nxm.sys.inc.DataTypes}
      Conversions from floating point to fixed point are rounded.
      @param buf1       Input buffer of type1 to be converted
      @param off1       Offset (in scalar elements of type1) into Input buffer, 0..n
      @param type1      Type of input buffer
      @param buf2       Output buffer
      @param off2       Offset (in scalar elements of type2) into Output buffer, 0..n
      @param type2      Type of output buffer
      @param n          Number of elements to be converted
      @param round      The rounding mode used when converting a floating point value to an integer
                        type This should be one of {@link #ROUND_DOWN}, {@link #ROUND_CEILING},
                        {@link #ROUND_FLOOR}, {@link #ROUND_HALF_EVEN}, {@link #ROUND_UNNECESSARY},
                        or {@link #ROUND_ROUND}). {@link #ROUND_DOWN} is the fastest and matches
                        the behavior of a type cast in Java.
      @param saturating Are conversions saturating? If true, when a narrowing conversion is done
                        (e.g. 'D' to 'L') in which the initial value is outside the limits of
                        the destination type, the min/max value for that type is used. If this is
                        false and the above situation occurs, the results are undefined (and
                        will vary from platform to platform). Setting this to false can improve
                        performance by 20% to 50%.
      @throws IllegalArgumentException if round is not one of the pre-defined modes
      @throws MidasException if type1 or type2 are non-homogeneous types (since 2.9.2)
      @throws ArithmeticException if <code>round=={@link #ROUND_UNNECESSARY}</code> and floating point
                        to integer (e.g. 'D' to 'L') is being performed.
      @since NeXtMidas 2.5.2
   */
  public static void type (byte[] buf1, int off1, byte type1,
                           byte[] buf2, int off2, byte type2, int n, int round, boolean saturating) {
    if (n==0 || buf1==null || buf2==null) return;
    // Check if trying to convert to unsupported type since 3.9.3
    if (isUnsignedType(type2)) {
      Midas.log("Converting values to 'U' or 'V' is not supported'", Midas.WARNING);
      return;
    }
    if (type1 == NONHOMOGENEOUS || type2 == NONHOMOGENEOUS) {
      throw new MidasException("Cannot convert non-homogeneous type buffer for "+((char)type1)+"->"+((char)type2)+" conversion not supported.");
    }
    if (!isUnsignedType(type1) && Native.useConvert && n>1 && type2!=type1) {    // use native code
      if (Native.type(buf1,off1,type1, buf2,off2,type2, n,round,saturating) == 0) return;
    }
    byte type2s = type2;  /* Mimic Jeff's Native.c addition for J */ // 563 in Native.c
    byte[] buf2orig = buf2;
    int off2orig = off2;
    if (type2==JINT) {
      type2 = INT;        /* first stage */ // 611 in Native.c
      buf2 = (type1==INT) ? buf1 : new byte[buf2.length*4/3];
      off2 = 0;
    }

    int bps1 = Math.max(1,Data.getBPS(type1));
    int bps2 = Math.max(1,Data.getBPS(type2));
    // Use j when converting to smaller type, jj when going bigger type (need to do manipulation of the jj value)
    int i=off1*bps1;
    int j=off2*bps2;
    if (type1==type2s) {      // handle noop case // need to used saved type 2 for J DataType support
      System.arraycopy(buf1,i, buf2,j, n*bps1);
      return;
    }
    int ii=i+n*bps1, jj=j+n*bps2;
    int status = n;
    if ((round == ROUND_UNNECESSARY) && ((type2 != FLOAT) || (type2 == DOUBLE))) {
      round = ROUND_DOWN; // Rounding not necessary.
    }
    if (saturating) {
      switch (type1) {
        case DOUBLE:
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(unpackD(buf1,i))); j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(unpackD(buf1,i))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(unpackD(buf1,i))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,n2i_sat(unpackD(buf1,i))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,n2b_sat(unpackD(buf1,i))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,d2n_sat(unpackD(buf1,i))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,n2b_sat(unpackD(buf1,i))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(          unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.ceil(unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.ceil(unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,n2i_sat(Math.ceil(unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,n2b_sat(Math.ceil(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,d2n_sat(Math.ceil(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,n2b_sat(Math.ceil(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(           unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.floor(unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.floor(unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,n2i_sat(Math.floor(unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,n2b_sat(Math.floor(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,d2n_sat(Math.floor(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,n2b_sat(Math.floor(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(          unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.rint(unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.rint(unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,n2i_sat(Math.rint(unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,n2b_sat(Math.rint(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,d2n_sat(Math.rint(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,n2b_sat(Math.rint(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(                unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.floor(0.5d+unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.floor(0.5d+unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,n2i_sat(Math.floor(0.5d+unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,n2b_sat(Math.floor(0.5d+unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,d2n_sat(Math.floor(0.5d+unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,n2b_sat(Math.floor(0.5d+unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(          unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Convert.lround(unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Convert.lround(unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,n2i_sat(Convert.lround(unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,n2b_sat(Convert.lround(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,d2n_sat(Convert.lround(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,n2b_sat(Convert.lround(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          } break;
        case FLOAT:
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,      (unpackF(buf1,ii))); } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long)(unpackF(buf1,ii))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(unpackF(buf1,i))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,n2i_sat(unpackF(buf1,i))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,n2b_sat(unpackF(buf1,i))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,f2n_sat(unpackF(buf1,i))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,n2b_sat(unpackF(buf1,i))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long)(Math.ceil(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.ceil(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,n2i_sat(Math.ceil(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,n2b_sat(Math.ceil(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,d2n_sat(Math.ceil(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,n2b_sat(Math.ceil(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                 (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long)(Math.floor(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.floor(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,n2i_sat(Math.floor(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,n2b_sat(Math.floor(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,d2n_sat(Math.floor(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,n2b_sat(Math.floor(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long)(Math.rint(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.rint(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,n2i_sat(Math.rint(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,n2b_sat(Math.rint(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,d2n_sat(Math.rint(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,n2b_sat(Math.rint(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                      (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long)(Math.floor(0.5f+unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.floor(0.5f+unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,n2i_sat(Math.floor(0.5f+unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,n2b_sat(Math.floor(0.5f+unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,d2n_sat(Math.floor(0.5f+unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,n2b_sat(Math.floor(0.5f+unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long)(Convert.lround(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Convert.lround(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,n2i_sat(Convert.lround(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,n2b_sat(Convert.lround(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,d2n_sat(Convert.lround(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,n2b_sat(Convert.lround(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          } break;
        case XLONG:
          switch (type2) {
            case DOUBLE: for (;i<ii;i+=8) { packD(buf2,j,       (unpackX(buf1,i))); j+=8; } break;
            case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,       (unpackX(buf1,i))); j+=4; } break;
            case LONG:   for (;i<ii;i+=8) { packL(buf2,j,n2l_sat(unpackX(buf1,i))); j+=4; } break;
            case INT:    for (;i<ii;i+=8) { packI(buf2,j,n2i_sat(unpackX(buf1,i))); j+=2; } break;
            case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,n2b_sat(unpackX(buf1,i))); j++;  } break;
            case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,x2n_sat(unpackX(buf1,i))); j++;  } break;
            case PACKED: for (;i<ii;i+=8) { packP(buf2,j,n2b_sat(unpackX(buf1,i))); j++;  } break;
            default: status=-1;
          } break;
        case LONG:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,       (unpackL(buf1,ii)));      } break;
            case FLOAT:  for (;i<ii;i+=4) {           packF(buf2,j,        (unpackL(buf1,i))); j+=4; } break;
            case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,       (unpackL(buf1,ii)));      } break;
            case INT:    for (;i<ii;i+=4) {           packI(buf2,j, n2i_sat(unpackL(buf1,i))); j+=2; } break;
            case BYTE:   for (;i<ii;i+=4) {           packB(buf2,j, n2b_sat(unpackL(buf1,i))); j++;  } break;
            case NIBBLE: for (;i<ii;i+=4) {           packN(buf2,j, l2n_sat(unpackL(buf1,i))); j++;  } break;
            case PACKED: for (;i<ii;i+=4) {           packP(buf2,j, n2b_sat(unpackL(buf1,i))); j++;  } break;
            default: status=-1;
          } break;
        case INT:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=2; jj-=8; packD(buf2,jj,       (unpackI(buf1,ii)));      } break;
            case FLOAT:  for (;ii>i;) { ii-=2; jj-=4; packF(buf2,jj,       (unpackI(buf1,ii)));      } break;
            case XLONG:  for (;ii>i;) { ii-=2; jj-=8; packX(buf2,jj,       (unpackI(buf1,ii)));      } break;
            case LONG:   for (;ii>i;) { ii-=2; jj-=4; packL(buf2,jj,       (unpackI(buf1,ii)));      } break;
            case INT:    {if (type2s!=JINT) status = -1; break;} // NXM-3894 12-bit J is being packed from INT separately
            case BYTE:   for (;i<ii;i+=2) {           packB(buf2,j, n2b_sat(unpackI(buf1,i))); j++;  } break;
            case NIBBLE: for (;i<ii;i+=2) {           packN(buf2,j, i2n_sat(unpackI(buf1,i))); j++;  } break;
            case PACKED: for (;i<ii;i+=2) {           packP(buf2,j, n2b_sat(unpackI(buf1,i))); j++;  } break;
            default: status=-1;
          } break;
        case JINT:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=1; jj-=8; packD(buf2,jj, (unpackJ(buf1,ii))); } break;
            case FLOAT:  for (;ii>i;) { ii-=1; jj-=4; packF(buf2,jj, (unpackJ(buf1,ii))); } break;
            case XLONG:  for (;ii>i;) { ii-=1; jj-=8; packX(buf2,jj, (unpackJ(buf1,ii))); } break;
            case LONG:   for (;ii>i;) { ii-=1; jj-=4; packL(buf2,jj, (unpackJ(buf1,ii))); } break;
            case INT:    for (;i<ii;i++)  { packI(buf2,j, (unpackJ(buf1,i))); j+=2; }       break;
            case BYTE:   for (;i<ii;i+=1) { packB(buf2,j, n2b_sat(unpackJ(buf1,i))); j++; }  break;
            case NIBBLE: for (;i<ii;i+=1) { packN(buf2,j, i2n_sat(unpackJ(buf1,i))); j++; }  break;
            case PACKED: for (;i<ii;i+=1) { packP(buf2,j, n2b_sat(unpackJ(buf1,i))); j++; }  break;
            default: status=-1;
           } break;
        case BYTE:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { jj-=8; packD(buf2,jj,       (buf1[--ii]));     } break;
            case FLOAT:  for (;ii>i;) { jj-=4; packF(buf2,jj,       (buf1[--ii]));     } break;
            case XLONG:  for (;ii>i;) { jj-=8; packX(buf2,jj,       (buf1[--ii]));     } break;
            case LONG:   for (;ii>i;) { jj-=4; packL(buf2,jj,       (buf1[--ii]));     } break;
            case INT:    for (;ii>i;) { jj-=2; packI(buf2,jj,       (buf1[--ii]));     } break;
            case NIBBLE: for (;i<ii;) {        packN(buf2,j, b2n_sat(buf1[i++])); j++; } break;
            case PACKED: for (;i<ii;) {        packP(buf2,j,        (buf1[i++])); j++; } break;
            default: status=-1;
          } break;
        case NIBBLE:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=1; jj-=8; packD(buf2,jj,(unpackN(buf1,ii))); } break;
            case FLOAT:  for (;ii>i;) { ii-=1; jj-=4; packF(buf2,jj,(unpackN(buf1,ii))); } break;
            case XLONG:  for (;ii>i;) { ii-=1; jj-=8; packX(buf2,jj,(unpackN(buf1,ii))); } break;
            case LONG:   for (;ii>i;) { ii-=1; jj-=4; packL(buf2,jj,(unpackN(buf1,ii))); } break;
            case INT:    for (;ii>i;) { ii-=1; jj-=2; packI(buf2,jj,(unpackN(buf1,ii))); } break;
            case BYTE:   for (;ii>i;) { ii-=1; jj-=1; packB(buf2,jj,(unpackN(buf1,ii))); } break;
            case PACKED: for (;ii>i;) { ii-=1; jj-=1; packP(buf2,jj,(unpackN(buf1,ii))); } break;
            default: status=-1;
          } break;
        case PACKED:
          switch (type2) {
            case DOUBLE: for (;ii>i;)  { ii--; jj-=8; packD(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case FLOAT:  for (;ii>i;)  { ii--; jj-=4; packF(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case XLONG:  for (;ii>i;)  { ii--; jj-=8; packX(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case LONG:   for (;ii>i;)  { ii--; jj-=4; packL(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case INT:    for (;ii>i;)  { ii--; jj-=2; packI(buf2,jj,(byte)((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case BYTE:   for (;ii>i;)  { ii--; jj-=1; packB(buf2,jj,(byte)((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case NIBBLE: for (;ii>i;)  { ii--; jj-=1; packN(buf2,jj,(byte)((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            default: status=-1;
          } break;
        case 'V':
          if(minSupportUnsupportedDataTypes) {
            switch (type2) {
              case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,       (unpackUL(buf1,ii)));      } break;
              case FLOAT:  for (;i<ii;i+=4) {           packF(buf2,j,        (unpackUL(buf1,i))); j+=4; } break;
              case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,       (unpackUL(buf1,ii)));      } break;
              case LONG:   for (;i<ii;i+=4) {           packL(buf2,j, n2l_sat(unpackUL(buf1,i))); j+=4; } break;
              case INT:    for (;i<ii;i+=4) {           packI(buf2,j, n2i_sat(unpackUL(buf1,i))); j+=2; } break;
              case BYTE:   for (;i<ii;i+=4) {           packB(buf2,j, n2b_sat(unpackUL(buf1,i))); j++;  } break;
              case NIBBLE: for (;i<ii;i+=4) {           packN(buf2,j, x2n_sat(unpackUL(buf1,i))); j++;  } break;
              case PACKED: for (;i<ii;i+=4) {           packP(buf2,j, n2b_sat(unpackUL(buf1,i))); j++;  } break;
              default: status=-1;
            }
          }
          else {
            Midas.log("Support for conversion from type 'V' needs the IOOption +MinimalSupportForUnsupportedDataTypes set", Midas.WARNING);
          }
          break;
        case 'U':
          if(minSupportUnsupportedDataTypes) {
            switch (type2) {
              case DOUBLE: for (;ii>i;) { ii-=2; jj-=8; packD(buf2,jj,       (unpackU(buf1,ii)));      } break;
              case FLOAT:  for (;ii>i;) { ii-=2; jj-=4; packF(buf2,jj,       (unpackU(buf1,ii)));      } break;
              case XLONG:  for (;ii>i;) { ii-=2; jj-=8; packX(buf2,jj,       (unpackU(buf1,ii)));      } break;
              case LONG:   for (;ii>i;) { ii-=2; jj-=4; packL(buf2,jj,       (unpackU(buf1,ii)));      } break;
              case INT:    for (;ii>i;i+=2) {           packI(buf2,j, n2i_sat(unpackU(buf1,i))); j++;  } break; //TODO think about i and j
              case BYTE:   for (;i<ii;i+=2) {           packB(buf2,j, n2b_sat(unpackU(buf1,i))); j++;  } break;
              case NIBBLE: for (;i<ii;i+=2) {           packN(buf2,j, l2n_sat(unpackU(buf1,i))); j++;  } break;
              case PACKED: for (;i<ii;i+=2) {           packP(buf2,j, n2b_sat(unpackU(buf1,i))); j++;  } break;
              default: status=-1;
            }
          }
          else {
            Midas.log("Support for conversion from type 'U' needs the IOOption +MinimalSupportForUnsupportedDataTypes set", Midas.WARNING);
          }
          break;
        default: status=-1;
      }
    } else {
      switch (type1) {
        case DOUBLE:
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(unpackD(buf1,i))); j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(unpackD(buf1,i))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(unpackD(buf1,i))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,(short)(unpackD(buf1,i))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,(byte )(unpackD(buf1,i))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,(byte )(unpackD(buf1,i))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,(byte )(unpackD(buf1,i))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(          unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.ceil(unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.ceil(unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,(short)(Math.ceil(unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,(byte )(Math.ceil(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,(byte )(Math.ceil(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,(byte )(Math.ceil(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(           unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.floor(unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.floor(unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,(short)(Math.floor(unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,(byte )(Math.floor(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,(byte )(Math.floor(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,(byte )(Math.floor(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case FLOAT: for (;i<ii;i+=8) { packF(buf2,j,(float)(          unpackD(buf1,i)));  j+=4; } break;
                case XLONG: for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.rint(unpackD(buf1,i)))); j+=8; } break;
                case LONG: for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.rint(unpackD(buf1,i)))); j+=4; } break;
                case INT: for (;i<ii;i+=8) { packI(buf2,j,(short)(Math.rint(unpackD(buf1,i)))); j+=2; } break;
                case BYTE: for (;i<ii;i+=8) { packB(buf2,j,(byte )(Math.rint(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,(byte )(Math.rint(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,(byte )(Math.rint(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case FLOAT: for (;i<ii;i+=8) { packF(buf2,j,(float)(                unpackD(buf1,i)));  j+=4; } break;
                case XLONG: for (;i<ii;i+=8) { packX(buf2,j,(long )(Math.floor(0.5d+unpackD(buf1,i)))); j+=8; } break;
                case LONG: for (;i<ii;i+=8) { packL(buf2,j,(int  )(Math.floor(0.5d+unpackD(buf1,i)))); j+=4; } break;
                case INT: for (;i<ii;i+=8) { packI(buf2,j,(short)(Math.floor(0.5d+unpackD(buf1,i)))); j+=2; } break;
                case BYTE: for (;i<ii;i+=8) { packB(buf2,j,(byte )(Math.floor(0.5d+unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,(byte )(Math.floor(0.5d+unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,(byte )(Math.floor(0.5d+unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,(float)(               unpackD(buf1,i)));  j+=4; } break;
                case XLONG:  for (;i<ii;i+=8) { packX(buf2,j,(long )(Convert.lround(unpackD(buf1,i)))); j+=8; } break;
                case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(Convert.lround(unpackD(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=8) { packI(buf2,j,(short)(Convert.lround(unpackD(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,(byte )(Convert.lround(unpackD(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,(byte )(Convert.lround(unpackD(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=8) { packP(buf2,j,(byte )(Convert.lround(unpackD(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          } break;
        case FLOAT:
          switch (round) {
            case ROUND_DOWN:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,        (unpackF(buf1,ii))); } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long  )(unpackF(buf1,ii))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(unpackF(buf1,i))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,(short)(unpackF(buf1,i))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,(byte )(unpackF(buf1,i))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,(byte )(unpackF(buf1,i))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,(byte )(unpackF(buf1,i))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_CEILING:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                  (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long  )(Math.ceil(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.ceil(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,(short)(Math.ceil(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,(byte )(Math.ceil(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,(byte )(Math.ceil(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,(byte )(Math.ceil(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_FLOOR:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                    (unpackF(buf1,ii))); } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long  )(Math.floor(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.floor(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,(short)(Math.floor(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,(byte )(Math.floor(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,(byte )(Math.floor(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,(byte )(Math.floor(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_EVEN:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                  (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long  )(Math.rint(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.rint(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,(short)(Math.rint(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,(byte )(Math.rint(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,(byte )(Math.rint(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,(byte )(Math.rint(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_ROUND: // Math.round(n) is implemented as Math.floor(0.5+n), do the math here to skip a method call.
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                        (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long  )(Math.floor(0.5f+unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Math.floor(0.5f+unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,(short)(Math.floor(0.5f+unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,(byte )(Math.floor(0.5f+unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,(byte )(Math.floor(0.5f+unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,(byte )(Math.floor(0.5f+unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_HALF_UP:
              switch (type2) {
                case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,                       (unpackF(buf1,ii)));  } break;
                case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,(long  )(Convert.lround(unpackF(buf1,ii)))); } break;
                case LONG:   for (;i<ii;i+=4) { packL(buf2,j,(int  )(Convert.lround(unpackF(buf1,i)))); j+=4; } break;
                case INT:    for (;i<ii;i+=4) { packI(buf2,j,(short)(Convert.lround(unpackF(buf1,i)))); j+=2; } break;
                case BYTE:   for (;i<ii;i+=4) { packB(buf2,j,(byte )(Convert.lround(unpackF(buf1,i)))); j++;  } break;
                case NIBBLE: for (;i<ii;i+=4) { packN(buf2,j,(byte )(Convert.lround(unpackF(buf1,i)))); j++;  } break;
                case PACKED: for (;i<ii;i+=4) { packP(buf2,j,(byte )(Convert.lround(unpackF(buf1,i)))); j++;  } break;
                default: status=-1;
              } break;
            case ROUND_UNNECESSARY:
              throw new ArithmeticException("Can not use ROUND_UNNECESSARY for "+((char)type1)+"->"+((char)type2)+" conversion.");
            default:
              throw new IllegalArgumentException("Illegal rounding mode "+round+" for "+((char)type1)+"->"+((char)type2)+" conversion.");
          } break;
        case XLONG:
          switch (type2) {
            case DOUBLE: for (;i<ii;i+=8) { packD(buf2,j,       (unpackX(buf1,i))); j+=8; } break;
            case FLOAT:  for (;i<ii;i+=8) { packF(buf2,j,       (unpackX(buf1,i))); j+=4; } break;
            case LONG:   for (;i<ii;i+=8) { packL(buf2,j,(int  )(unpackX(buf1,i))); j+=4; } break;
            case INT:    for (;i<ii;i+=8) { packI(buf2,j,(short)(unpackX(buf1,i))); j+=2; } break;
            case BYTE:   for (;i<ii;i+=8) { packB(buf2,j,(byte )(unpackX(buf1,i))); j++;  } break;
            case NIBBLE: for (;i<ii;i+=8) { packN(buf2,j,(byte )(unpackX(buf1,i))); j++;  } break;
            case PACKED: for (;i<ii;i+=8) { packP(buf2,j,(byte )(unpackX(buf1,i))); j++;  } break;
            default: status=-1;
          } break;
        case LONG:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=4; jj-=8; packD(buf2,jj,       (unpackL(buf1,ii)));      } break;
            case FLOAT:  for (;i<ii;i+=4) {           packF(buf2,j,        (unpackL(buf1,i))); j+=4; } break;
            case XLONG:  for (;ii>i;) { ii-=4; jj-=8; packX(buf2,jj,       (unpackL(buf1,ii)));      } break;
            case INT:    for (;i<ii;i+=4) {           packI(buf2,j, (short)(unpackL(buf1,i))); j+=2; } break;
            case BYTE:   for (;i<ii;i+=4) {           packB(buf2,j, (byte )(unpackL(buf1,i))); j++;  } break;
            case NIBBLE: for (;i<ii;i+=4) {           packN(buf2,j, (byte )(unpackL(buf1,i))); j++;  } break;
            case PACKED: for (;i<ii;i+=4) {           packP(buf2,j, (byte )(unpackL(buf1,i))); j++;  } break;
            default: status=-1;
          } break;
        case INT:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=2; jj-=8; packD(buf2,jj,      (unpackI(buf1,ii)));      } break;
            case FLOAT:  for (;ii>i;) { ii-=2; jj-=4; packF(buf2,jj,      (unpackI(buf1,ii)));      } break;
            case XLONG:  for (;ii>i;) { ii-=2; jj-=8; packX(buf2,jj,      (unpackI(buf1,ii)));      } break;
            case LONG:   for (;ii>i;) { ii-=2; jj-=4; packL(buf2,jj,      (unpackI(buf1,ii)));      } break;
            case INT:    {if (type2s!=JINT) status = -1; break;} // NXM-3894 12-bit J is being packed from INT separately
            case BYTE:   for (;i<ii;i+=2) {           packB(buf2,j, (byte)(unpackI(buf1,i))); j++;  } break;
            case NIBBLE: for (;i<ii;i+=2) {           packN(buf2,j, (byte)(unpackI(buf1,i))); j++;  } break;
            case PACKED: for (;i<ii;i+=2) {           packP(buf2,j, (byte)(unpackI(buf1,i))); j++;  } break;
            default: status=-1;
          } break;
        case JINT: // NXM-3894 Mimic Jeff's CJ DataType Native.c code (needs more testing)
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=1; jj-=8; packD(buf2,jj, (unpackJ(buf1,ii))); } break;
            case FLOAT:  for (;ii>i;) { ii-=1; jj-=4; packF(buf2,jj, (unpackJ(buf1,ii))); } break;
            case XLONG:  for (;ii>i;) { ii-=1; jj-=8; packX(buf2,jj, (unpackJ(buf1,ii))); } break;
            case LONG:   for (;ii>i;) { ii-=1; jj-=4; packL(buf2,jj, (unpackJ(buf1,ii))); } break;
            case INT:    for (;i<ii;i++)  { packI(buf2,j, (unpackJ(buf1,i))); j+=2; }       break;
            case BYTE:   for (;i<ii;i+=1) { packB(buf2,j, (byte)(unpackJ(buf1,i))); j++; }  break;
            case NIBBLE: for (;i<ii;i+=1) { packN(buf2,j, (byte)(unpackJ(buf1,i))); j++; }  break;
            case PACKED: for (;i<ii;i+=1) { packP(buf2,j, (byte)(unpackJ(buf1,i))); j++; }  break;
            default: status=-1;
          } break;
        case BYTE:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { jj-=8; packD(buf2,jj,(buf1[--ii]));     } break;
            case FLOAT:  for (;ii>i;) { jj-=4; packF(buf2,jj,(buf1[--ii]));     } break;
            case XLONG:  for (;ii>i;) { jj-=8; packX(buf2,jj,(buf1[--ii]));     } break;
            case LONG:   for (;ii>i;) { jj-=4; packL(buf2,jj,(buf1[--ii]));     } break;
            case INT:    for (;ii>i;) { jj-=2; packI(buf2,jj,(buf1[--ii]));     } break;
            case NIBBLE: for (;i<ii;) {        packN(buf2,j, (buf1[i++])); j++; } break;
            case PACKED: for (;i<ii;) {        packP(buf2,j, (buf1[i++])); j++; } break;
            default: status=-1;
          } break;
        case NIBBLE:
          switch (type2) {
            case DOUBLE: for (;ii>i;) { ii-=1; jj-=8; packD(buf2,jj,(unpackN(buf1,ii))); } break;
            case FLOAT:  for (;ii>i;) { ii-=1; jj-=4; packF(buf2,jj,(unpackN(buf1,ii))); } break;
            case XLONG:  for (;ii>i;) { ii-=1; jj-=8; packX(buf2,jj,(unpackN(buf1,ii))); } break;
            case LONG:   for (;ii>i;) { ii-=1; jj-=4; packL(buf2,jj,(unpackN(buf1,ii))); } break;
            case INT:    for (;ii>i;) { ii-=1; jj-=2; packI(buf2,jj,(unpackN(buf1,ii))); } break;
            case BYTE:   for (;ii>i;) { ii-=1; jj-=1; packB(buf2,jj,(unpackN(buf1,ii))); } break;
            case PACKED: for (;ii>i;) { ii-=1; jj-=1; packP(buf2,jj,(unpackN(buf1,ii))); } break;
            default: status=-1;
          }
          break;
        case PACKED:
          switch (type2) {
            case DOUBLE: for (;ii>i;)  { ii--; jj-=8; packD(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case FLOAT:  for (;ii>i;)  { ii--; jj-=4; packF(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case XLONG:  for (;ii>i;)  { ii--; jj-=8; packX(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case LONG:   for (;ii>i;)  { ii--; jj-=4; packL(buf2,jj,      ((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case INT:    for (;ii>i;)  { ii--; jj-=2; packI(buf2,jj,(byte)((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case BYTE:   for (;ii>i;)  { ii--; jj-=1; packB(buf2,jj,(byte)((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            case NIBBLE: for (;ii>i;)  { ii--; jj-=1; packN(buf2,jj,(byte)((buf1[ii>>3]>>(ii&0x7))&0x1)); } break;
            default: status=-1;
          } break;
        case UINT:
          Midas.log("Converting values from 'U' in unsaturated mode is not supported", Midas.WARNING);
          return;
        case ULONG:
          Midas.log("Converting values from 'V' in unsaturated mode is not supported", Midas.WARNING);
          return;
        default: status=-1;
      }
    }
    if (type2s==JINT) {
      int offset = (int)Math.ceil(off2orig*3.0/2); // ??????
      packJs(buf2,buf2orig,offset,n);   /* Mimic Jeff's Native.c J DataType contribution */
      if (type2==INT) return; // bbuf2 is pointing to buf1 and we are done
    }
    if (status<0) Shell.warning("ERROR: type(..) Unknown conversion "+((char)type1)+"->"+((char)type2));
  }

//  @InternalUseOnly
//  public static void setUnsignedSupport (boolean supported) {
//    unsignedTestFileCreationSupport = supported;
//  }

  /**
     Class to allow BigDecimal results to be returned through the parameter list.

     Note: Some of the methods below are not used because of the heavy reliance on the flag return
     type (that matches previous practice), but are left in place to enable a switch to using
     the valid field.

     (BigDecimal is immutable and would otherwise have to be passed as a return type, but in some
     cases the method's return type is being used as an index/flag)
     @since NeXtMidas 3.5.1 for Bug 2833
   */
  @InternalUseOnly("since created")
  private static class BigDecimalValue { // TODO: rename this class to BigDecimalHolder?
    boolean valid; // if result is a valid BigDecimal
    BigDecimal bdVal; // Holds BigDecimal value

    public BigDecimalValue() {
      this.bdVal = null;
      this.valid = false;
    }

    public BigDecimalValue (BigDecimal bdVal) {
      this.bdVal = bdVal;
      this.valid = true;
    }

    public BigDecimal getValue() {
      return bdVal;
    }

    public void setValue (BigDecimal bdVal) {
      this.bdVal = bdVal;
      this.valid = true;
    }

    public boolean isValid() {
      return valid;
    }

    public void setValid (boolean valid) {
      this.valid = valid;
    }

    public void setToValid() {
      this.valid = true;
    }

    public void setToInvalid() {
      this.valid = false;
    }

  } // end class BigDecimalValue
}
