//------------------------------------------------------------------------------
// <copyright file="SqlDecimal.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
//  </copyright>
// <owner current="true" primary="true">junfang</owner>
// <owner current="true" primary="false">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------

//**************************************************************************
// @File: SqlNumeric.cs
//
// Create by:    JunFang
// @Owner:      JunFang
//
// Purpose: Implementation of SqlMoney which is equivalent to
//            data type "numeric" and "decimal" in SQL Server
//
// Notes:
//
// History:
//
//    @Version: Yukon
//    118776 JXF  09/17/02 Double to SqlDecimal/SqlMoney conversion
//    102740 BDS  03/28/02 UDT Serialization
//    86121 AZA  08/21/01 Stop accessing Precision and Scale on null SqlDecimal
//                        objects.
//
//   09/17/99  JunFang    Created and implemented as first drop.
//
// @EndHeader@
//**************************************************************************

using System;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace System.Data.SqlTypes {

    /// <devdoc>
    ///    <para>
    ///       Represents a fixed precision and scale numeric value between -10<superscript term='38'/>
    ///       -1 and 10<superscript term='38'/> -1 to be stored in or retrieved from a database.
    ///    </para>
    /// </devdoc>
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    [XmlSchemaProvider("GetXsdType")]
    public struct SqlDecimal : INullable, IComparable, IXmlSerializable {
        // data in CSsNumeric in SQL Server
        // BYTE    m_cbLen;                // # of DWORDs + 1 (1 is for sign)
        // BYTE    m_bPrec;                // precision
        // BYTE    m_bScale;                // scale
        // BYTE    m_bSign;                // NUM_POSITIVE or NUM_NEGATIVE
        // ULONG    m_rgulData [x_culNumeMax];

        internal byte    m_bStatus;      // bit 0: fNotNull, bit 1: fNegative
        internal byte    m_bLen;         // number of uints used, = (CSsNumeric.m_cbLen - 1) / 4.
        internal byte    m_bPrec;
        internal byte    m_bScale;
        internal UInt32  m_data1;
        internal UInt32  m_data2;
        internal UInt32  m_data3;
        internal UInt32  m_data4;

        private static readonly byte NUMERIC_MAX_PRECISION = 38;            // Maximum precision of numeric
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public  static readonly byte MaxPrecision = NUMERIC_MAX_PRECISION;  // max SS precision
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public  static readonly byte MaxScale = NUMERIC_MAX_PRECISION;      // max SS scale

        private static readonly byte    x_bNullMask     = 1;    // bit mask for null bit in m_bStatus
        private static readonly byte    x_bIsNull       = 0;    // is null
        private static readonly byte    x_bNotNull      = 1;    // is not null
        private static readonly byte    x_bReverseNullMask  = unchecked((byte)~x_bNullMask);

        private static readonly byte    x_bSignMask     = 2;    // bit mask for sign bit in m_bStatus
        private static readonly byte    x_bPositive     = 0;    // is positive
        private static readonly byte    x_bNegative     = 2;    // is negative
        private static readonly byte    x_bReverseSignMask  = unchecked((byte)~x_bSignMask);

        private static readonly uint    x_uiZero            = (uint) 0;

        private static readonly int     x_cNumeMax          = 4;
        private static readonly long    x_lInt32Base        = ((long)1) << 32;      // 2**32
        private static readonly ulong   x_ulInt32Base       = ((ulong)1) << 32;     // 2**32
        private static readonly ulong   x_ulInt32BaseForMod = x_ulInt32Base - 1;    // 2**32 - 1 (0xFFF...FF)

        internal static readonly ulong   x_llMax            = Int64.MaxValue;   // Max of Int64

        private static readonly uint    x_ulBase10          = 10;

        private static readonly double  DUINT_BASE  = (double)x_lInt32Base;     // 2**32
        private static readonly double  DUINT_BASE2 = DUINT_BASE * DUINT_BASE;  // 2**64
        private static readonly double  DUINT_BASE3 = DUINT_BASE2 * DUINT_BASE; // 2**96
        private static readonly double  DMAX_NUME   = 1.0e+38;                  // Max value of numeric
        private static readonly uint    DBL_DIG     = 17;                       // Max decimal digits of double

        private static readonly byte    x_cNumeDivScaleMin = 6;     // Minimum result scale of numeric division

        // Array of multipliers for lAdjust and Ceiling/Floor.
        private static readonly uint[] x_rgulShiftBase = new uint[9] {
            10,
            10 * 10,
            10 * 10 * 10,
            10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10 * 10 * 10 * 10 * 10
        };

#region DecimalHelperTableGenerator
/*
        // the code below will generate the DecimalHelpers tables
        static private string[] HelperNames = {
            "DecimalHelpersLo", "DecimalHelpersMid", "DecimalHelpersHi", "DecimalHelpersHiHi",
        };


        static private void DumpDecimalHelperParts(int index)
        {
            SqlDecimal sqlDecimalValue = 10;    // start precision=2

            Console.WriteLine("static private readonly UInt32[] {0} = {{", HelperNames[index]);
            for (int precision = 2; precision <= SqlDecimal.MaxPrecision; precision++){
                Console.WriteLine("    0x{0,8:x8}, // precision:{1}, value:{2}", sqlDecimalValue.Data[index], precision, sqlDecimalValue.ToString());
                if (precision < SqlDecimal.MaxPrecision){
                    sqlDecimalValue *= 10;
                }
            }
            sqlDecimalValue = SqlDecimal.MaxValue;
            int[] data = sqlDecimalValue.Data;
            UInt32[] udata = { (UInt32)data[0], (UInt32)data[1], (UInt32)data[2], (UInt32)data[3]};
            bool carry = true;
            for (int i = 0; i < 4; i++){
                if (carry){
                    carry = (++udata[i] == 0);
                }
            }
            Console.WriteLine("    0x{0,8:x8}, // precision:{1}+1, value:{2}+1", udata[index], SqlDecimal.MaxPrecision, SqlDecimal.MaxValue.ToString());

            Console.WriteLine("};");
            Console.WriteLine();
        }


        static public void CreateDecimalHelperTable()
        {
            for (int i = 0; i < 4; i++)
            {
                DumpDecimalHelperParts(i);
            }
        }

*/
#endregion

#region DecimalHelperTable
        static private readonly UInt32[] DecimalHelpersLo = {
            0x0000000a, // precision:2, value:10
            0x00000064, // precision:3, value:100
            0x000003e8, // precision:4, value:1000
            0x00002710, // precision:5, value:10000
            0x000186a0, // precision:6, value:100000
            0x000f4240, // precision:7, value:1000000
            0x00989680, // precision:8, value:10000000
            0x05f5e100, // precision:9, value:100000000
            0x3b9aca00, // precision:10, value:1000000000
            0x540be400, // precision:11, value:10000000000
            0x4876e800, // precision:12, value:100000000000
            0xd4a51000, // precision:13, value:1000000000000
            0x4e72a000, // precision:14, value:10000000000000
            0x107a4000, // precision:15, value:100000000000000
            0xa4c68000, // precision:16, value:1000000000000000
            0x6fc10000, // precision:17, value:10000000000000000
            0x5d8a0000, // precision:18, value:100000000000000000
            0xa7640000, // precision:19, value:1000000000000000000
            0x89e80000, // precision:20, value:10000000000000000000
            0x63100000, // precision:21, value:100000000000000000000
            0xdea00000, // precision:22, value:1000000000000000000000
            0xb2400000, // precision:23, value:10000000000000000000000
            0xf6800000, // precision:24, value:100000000000000000000000
            0xa1000000, // precision:25, value:1000000000000000000000000
            0x4a000000, // precision:26, value:10000000000000000000000000
            0xe4000000, // precision:27, value:100000000000000000000000000
            0xe8000000, // precision:28, value:1000000000000000000000000000
            0x10000000, // precision:29, value:10000000000000000000000000000
            0xa0000000, // precision:30, value:100000000000000000000000000000
            0x40000000, // precision:31, value:1000000000000000000000000000000
            0x80000000, // precision:32, value:10000000000000000000000000000000
            0x00000000, // precision:33, value:100000000000000000000000000000000
            0x00000000, // precision:34, value:1000000000000000000000000000000000
            0x00000000, // precision:35, value:10000000000000000000000000000000000
            0x00000000, // precision:36, value:100000000000000000000000000000000000
            0x00000000, // precision:37, value:1000000000000000000000000000000000000
            0x00000000, // precision:38, value:10000000000000000000000000000000000000
            0x00000000, // precision:38+1, value:99999999999999999999999999999999999999+1
        };

        static private readonly UInt32[] DecimalHelpersMid = {
            0x00000000, // precision:2, value:10
            0x00000000, // precision:3, value:100
            0x00000000, // precision:4, value:1000
            0x00000000, // precision:5, value:10000
            0x00000000, // precision:6, value:100000
            0x00000000, // precision:7, value:1000000
            0x00000000, // precision:8, value:10000000
            0x00000000, // precision:9, value:100000000
            0x00000000, // precision:10, value:1000000000
            0x00000002, // precision:11, value:10000000000
            0x00000017, // precision:12, value:100000000000
            0x000000e8, // precision:13, value:1000000000000
            0x00000918, // precision:14, value:10000000000000
            0x00005af3, // precision:15, value:100000000000000
            0x00038d7e, // precision:16, value:1000000000000000
            0x002386f2, // precision:17, value:10000000000000000
            0x01634578, // precision:18, value:100000000000000000
            0x0de0b6b3, // precision:19, value:1000000000000000000
            0x8ac72304, // precision:20, value:10000000000000000000
            0x6bc75e2d, // precision:21, value:100000000000000000000
            0x35c9adc5, // precision:22, value:1000000000000000000000
            0x19e0c9ba, // precision:23, value:10000000000000000000000
            0x02c7e14a, // precision:24, value:100000000000000000000000
            0x1bcecced, // precision:25, value:1000000000000000000000000
            0x16140148, // precision:26, value:10000000000000000000000000
            0xdcc80cd2, // precision:27, value:100000000000000000000000000
            0x9fd0803c, // precision:28, value:1000000000000000000000000000
            0x3e250261, // precision:29, value:10000000000000000000000000000
            0x6d7217ca, // precision:30, value:100000000000000000000000000000
            0x4674edea, // precision:31, value:1000000000000000000000000000000
            0xc0914b26, // precision:32, value:10000000000000000000000000000000
            0x85acef81, // precision:33, value:100000000000000000000000000000000
            0x38c15b0a, // precision:34, value:1000000000000000000000000000000000
            0x378d8e64, // precision:35, value:10000000000000000000000000000000000
            0x2b878fe8, // precision:36, value:100000000000000000000000000000000000
            0xb34b9f10, // precision:37, value:1000000000000000000000000000000000000
            0x00f436a0, // precision:38, value:10000000000000000000000000000000000000
            0x098a2240, // precision:38+1, value:99999999999999999999999999999999999999+1
        };

        static private readonly UInt32[] DecimalHelpersHi = {
            0x00000000, // precision:2, value:10
            0x00000000, // precision:3, value:100
            0x00000000, // precision:4, value:1000
            0x00000000, // precision:5, value:10000
            0x00000000, // precision:6, value:100000
            0x00000000, // precision:7, value:1000000
            0x00000000, // precision:8, value:10000000
            0x00000000, // precision:9, value:100000000
            0x00000000, // precision:10, value:1000000000
            0x00000000, // precision:11, value:10000000000
            0x00000000, // precision:12, value:100000000000
            0x00000000, // precision:13, value:1000000000000
            0x00000000, // precision:14, value:10000000000000
            0x00000000, // precision:15, value:100000000000000
            0x00000000, // precision:16, value:1000000000000000
            0x00000000, // precision:17, value:10000000000000000
            0x00000000, // precision:18, value:100000000000000000
            0x00000000, // precision:19, value:1000000000000000000
            0x00000000, // precision:20, value:10000000000000000000
            0x00000005, // precision:21, value:100000000000000000000
            0x00000036, // precision:22, value:1000000000000000000000
            0x0000021e, // precision:23, value:10000000000000000000000
            0x0000152d, // precision:24, value:100000000000000000000000
            0x0000d3c2, // precision:25, value:1000000000000000000000000
            0x00084595, // precision:26, value:10000000000000000000000000
            0x0052b7d2, // precision:27, value:100000000000000000000000000
            0x033b2e3c, // precision:28, value:1000000000000000000000000000
            0x204fce5e, // precision:29, value:10000000000000000000000000000
            0x431e0fae, // precision:30, value:100000000000000000000000000000
            0x9f2c9cd0, // precision:31, value:1000000000000000000000000000000
            0x37be2022, // precision:32, value:10000000000000000000000000000000
            0x2d6d415b, // precision:33, value:100000000000000000000000000000000
            0xc6448d93, // precision:34, value:1000000000000000000000000000000000
            0xbead87c0, // precision:35, value:10000000000000000000000000000000000
            0x72c74d82, // precision:36, value:100000000000000000000000000000000000
            0x7bc90715, // precision:37, value:1000000000000000000000000000000000000
            0xd5da46d9, // precision:38, value:10000000000000000000000000000000000000
            0x5a86c47a, // precision:38+1, value:99999999999999999999999999999999999999+1
        };

        static private readonly UInt32[] DecimalHelpersHiHi = {
            0x00000000, // precision:2, value:10
            0x00000000, // precision:3, value:100
            0x00000000, // precision:4, value:1000
            0x00000000, // precision:5, value:10000
            0x00000000, // precision:6, value:100000
            0x00000000, // precision:7, value:1000000
            0x00000000, // precision:8, value:10000000
            0x00000000, // precision:9, value:100000000
            0x00000000, // precision:10, value:1000000000
            0x00000000, // precision:11, value:10000000000
            0x00000000, // precision:12, value:100000000000
            0x00000000, // precision:13, value:1000000000000
            0x00000000, // precision:14, value:10000000000000
            0x00000000, // precision:15, value:100000000000000
            0x00000000, // precision:16, value:1000000000000000
            0x00000000, // precision:17, value:10000000000000000
            0x00000000, // precision:18, value:100000000000000000
            0x00000000, // precision:19, value:1000000000000000000
            0x00000000, // precision:20, value:10000000000000000000
            0x00000000, // precision:21, value:100000000000000000000
            0x00000000, // precision:22, value:1000000000000000000000
            0x00000000, // precision:23, value:10000000000000000000000
            0x00000000, // precision:24, value:100000000000000000000000
            0x00000000, // precision:25, value:1000000000000000000000000
            0x00000000, // precision:26, value:10000000000000000000000000
            0x00000000, // precision:27, value:100000000000000000000000000
            0x00000000, // precision:28, value:1000000000000000000000000000
            0x00000000, // precision:29, value:10000000000000000000000000000
            0x00000001, // precision:30, value:100000000000000000000000000000
            0x0000000c, // precision:31, value:1000000000000000000000000000000
            0x0000007e, // precision:32, value:10000000000000000000000000000000
            0x000004ee, // precision:33, value:100000000000000000000000000000000
            0x0000314d, // precision:34, value:1000000000000000000000000000000000
            0x0001ed09, // precision:35, value:10000000000000000000000000000000000
            0x00134261, // precision:36, value:100000000000000000000000000000000000
            0x00c097ce, // precision:37, value:1000000000000000000000000000000000000
            0x0785ee10, // precision:38, value:10000000000000000000000000000000000000
            0x4b3b4ca8, // precision:38+1, value:99999999999999999999999999999999999999+1
        };
#endregion

        // note that the algorithm covers a range from -5 to +4 from the initial index
        // at the end of the algorithm the tableindex will point to the greatest value that is
        // less than the current value
        // except (!) if the current value is less than 10 (precision=1). There is no value ins
        // the table that is less than 10. In this case the algorithm terminates earlier.
        //
        // The startindex values have been chosen so that the highest possible index (startindex+5)
        // does not point to a value that has bits in a higher word set
        //
        private const int HelperTableStartIndexLo = 5;
        private const int HelperTableStartIndexMid = 15;
        private const int HelperTableStartIndexHi = 24;
        private const int HelperTableStartIndexHiHi = 33;

        private byte CalculatePrecision() {
            int tableIndex;
            byte precision;
            UInt32[] decimalHelpers;
            UInt32 decimalPart;

            if (m_data4 != 0) {
                tableIndex = HelperTableStartIndexHiHi;
                decimalHelpers = DecimalHelpersHiHi;
                decimalPart = m_data4;
            }
            else if (m_data3 != 0) {
                tableIndex = HelperTableStartIndexHi;
                decimalHelpers = DecimalHelpersHi;
                decimalPart = m_data3;
            }
            else if (m_data2 != 0) {
                tableIndex = HelperTableStartIndexMid;
                decimalHelpers = DecimalHelpersMid;
                decimalPart = m_data2;
            }
            else {
                tableIndex = HelperTableStartIndexLo;
                decimalHelpers = DecimalHelpersLo;
                decimalPart = m_data1;
            }


            // this code will move the index no more than -2 -2 -1 (-5) or +2 +1 +1 (+4)
            // from the initial position
            //
            if (decimalPart < decimalHelpers[tableIndex]) {
                tableIndex -= 2;
                if (decimalPart < decimalHelpers[tableIndex]) {
                    tableIndex -= 2;
                    if (decimalPart < decimalHelpers[tableIndex]) {
                        tableIndex -= 1;
                    }
                    else {
                        tableIndex += 1;
                    }
                }
                else {
                    tableIndex += 1;
                }
            }
            else {
                tableIndex += 2;
                if (decimalPart < decimalHelpers[tableIndex]) {
                    tableIndex -= 1;
                }
                else {
                    tableIndex += 1;
                }
            }
            if (decimalPart >= decimalHelpers[tableIndex]) {
                tableIndex += 1;
                if (tableIndex == 37 && decimalPart >= decimalHelpers[tableIndex]) {
                    // This can happen only if the actual value is greater than 1E+38,
                    // in which case, tableIndex starts at 33 and ends at 37.
                    // Note that in this case, the actual value will be out of SqlDeicmal's range,
                    // and tableIndex is out of the array boudary. We'll throw later in ctors.
                    tableIndex += 1;
                }
            }

            precision = (byte)(tableIndex + 1);
            if (precision > 1) {
                // not done yet
                // tableIndex may still be off by one since we didn't look at the lower words
                //
                if (VerifyPrecision((byte)(precision-1))) {
                    precision -= 1;
                }
            }
            Debug.Assert ((precision == MaxPrecision+1) || VerifyPrecision(precision), "Calcualted precision too low?");
            Debug.Assert ((precision == 1) || !VerifyPrecision((byte)(precision-1)), "Calculated precision too high?");

            // adjust the precision
            // This might not be correct but is to our best knowledge
            precision = Math.Max(precision, m_bScale);
#if DEBUG
            byte bPrec = m_bPrec;   // store current value
            m_bPrec = MaxPrecision; // BActualPrec does not work if m_bPrec uninitialized!
            byte bActualPrecision = BActualPrec();
            m_bPrec=bPrec;  // restore current value

            Debug.Assert (precision==bActualPrecision,String.Format((IFormatProvider)null, "CalculatePrecision={0}, BActualPrec={1}. Results must be equal!", precision, bActualPrecision));
#endif
            return precision;
        }

        // VerifyPrecision
        //
        // returns true if the current value is less or equal than the max value of the
        // supplied precision.
        //
        private bool VerifyPrecision (byte precision) {
            Debug.Assert(precision>0,"Precision cannot be less than 1");
            Debug.Assert(precision<=MaxPrecision, "Precision > MaxPrecision");

            int  tableIndex = checked((int)precision -1);
            if (m_data4 < DecimalHelpersHiHi[tableIndex]) {
                return true;
            }
            else if (m_data4 == DecimalHelpersHiHi[tableIndex]) {
                if (m_data3 < DecimalHelpersHi[tableIndex]) {
                    return true;
                }
                else if (m_data3 == DecimalHelpersHi[tableIndex]) {
                    if (m_data2 < DecimalHelpersMid[tableIndex]) {
                        return true;
                    }
                    else if (m_data2 == DecimalHelpersMid[tableIndex]) {
                        if (m_data1 < DecimalHelpersLo[tableIndex]) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }



        // constructor
        // construct a Null
        private SqlDecimal(bool fNull) {
            m_bLen =
            m_bPrec =
            m_bScale = (byte)0;
            m_bStatus = 0;
            m_data1 =
            m_data2 =
            m_data3 =
            m_data4 = x_uiZero;
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public SqlDecimal(Decimal value) {
            // set the null bit
            m_bStatus = x_bNotNull;

            // note: using usafe code we could get the data directly out of the structure like that:
            // UInt32 * pInt = (UInt32*) &value;
            // UInt32 sgnscl = *pInt++;

            // m_data3 = *pInt++; // high part
            // m_data1 = *pInt++; // lo part
            // m_data2 = *pInt++; // mid part

            int[] bits = Decimal.GetBits(value);

            UInt32 sgnscl = (UInt32)bits[3];
            m_data1 = (UInt32)bits[0];
            m_data2 = (UInt32)bits[1];
            m_data3 = (UInt32)bits[2];
            m_data4 = x_uiZero;

            // set the sign bit
            m_bStatus |= ((sgnscl & 0x80000000) == 0x80000000)? x_bNegative:(byte)0;

            if (m_data3 != 0)
                m_bLen = 3;
            else if (m_data2 != 0)
                m_bLen = 2;
            else
                m_bLen = 1;

            // Get the scale info from Decimal
            m_bScale = (byte)((int)(sgnscl & 0xff0000) >>16);

            // initialize precision
            m_bPrec = 0; // The this object cannot be used before all of its fields are assigned to


            m_bPrec = CalculatePrecision();
// CalculatePrecision adjusts!
//            if (m_bPrec < m_bScale)
//                m_bPrec = m_bScale;
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public SqlDecimal(int value) {
            // set the null bit
            m_bStatus = x_bNotNull;

            uint uiValue = (uint) value;

            // set the sign bit
            if (value < 0) {
                m_bStatus |= x_bNegative;
                // The negative of -2147483648 doesn't fit into int32, directly cast to int should work.
                if (value != Int32.MinValue)
                    uiValue = (uint)(- value);
            }

            // set the data
            m_data1 = uiValue;
            m_data2 = m_data3 = m_data4 = x_uiZero;

            m_bLen = 1;
            m_bPrec = BGetPrecUI4(m_data1);
            m_bScale = 0;
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public SqlDecimal(long value) {
            // set the null bit
            m_bStatus = x_bNotNull;

            ulong dwl = (ulong)value;

            // set the sign bit
            if (value < 0) {
                m_bStatus |= x_bNegative;
                // The negative of Int64.MinValue doesn't fit into int64, directly cast to ulong should work.
                if (value != Int64.MinValue)
                    dwl = (ulong)(- value);
            }

            // Copy DWL into bottom 2 UI4s of numeric
            m_data1 = (uint)dwl;
            m_data2 = (uint)(dwl >> 32);
            m_data3 = m_data4 = 0;

            // Trim any leading zero from the length
            m_bLen = (byte)((m_data2 == 0) ? 1 : 2);

            m_bPrec = BGetPrecUI8(dwl);
            m_bScale = 0;

            AssertValid();
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public SqlDecimal(byte bPrecision, byte bScale, bool fPositive, int[] bits) {
            CheckValidPrecScale(bPrecision, bScale);
            if (bits == null)
                throw new ArgumentNullException("bits");
            else if (bits.Length != 4)
                throw new ArgumentException(SQLResource.InvalidArraySizeMessage, "bits");

            m_bPrec = bPrecision;
            m_bScale = bScale;
            m_data1 = (uint)bits[0];
            m_data2 = (uint)bits[1];
            m_data3 = (uint)bits[2];
            m_data4 = (uint)bits[3];
            m_bLen = 1;
            for (int i = 3; i >= 0; i --) {
                if (bits[i] != 0) {
                    m_bLen = (byte)(i + 1);
                    break;
                }
            }

            // set the null bit
            m_bStatus = x_bNotNull;

            // set the sign bit
            if (!fPositive) {
                m_bStatus |= x_bNegative;
            }

            // If result is -0, adjust sign to positive.
            if (FZero())
                SetPositive();

            if (bPrecision < CalculatePrecision())
                throw new OverflowException(SQLResource.ArithOverflowMessage);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public SqlDecimal(byte bPrecision, byte bScale, bool fPositive, int data1, int data2, int data3, int data4) {
            CheckValidPrecScale(bPrecision, bScale);
            m_bPrec = bPrecision;
            m_bScale = bScale;

            m_data1 = (uint)data1;
            m_data2 = (uint)data2;
            m_data3 = (uint)data3;
            m_data4 = (uint)data4;

            m_bLen = 1;
            if (data4 == 0)
                if (data3 == 0)
                    if (data2 == 0)
                        m_bLen = 1;
                    else
                        m_bLen = 2;
                else
                    m_bLen = 3;
            else
                m_bLen = 4;

            // set the null bit
            m_bStatus = x_bNotNull;

            // set the sign bit
            if (!fPositive) {
                m_bStatus |= x_bNegative;
            }

            // If result is -0, adjust sign to positive.
            if (FZero())
                SetPositive();

            if (bPrecision < CalculatePrecision())
                throw new OverflowException(SQLResource.ArithOverflowMessage);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public SqlDecimal(double dVal) : this(false) {
            // set the null bit
            m_bStatus = x_bNotNull;

            // Split double to sign, integer, and fractional parts
            if (dVal < 0) {
                dVal = -dVal;
                m_bStatus |= x_bNegative;
            }

            // If it will not fit into numeric(NUMERIC_MAX_PRECISION,0), overflow.
            if (dVal >= DMAX_NUME)
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            double dInt = Math.Floor(dVal);
            double dFrac = dVal - dInt;

            m_bPrec  = NUMERIC_MAX_PRECISION;
            m_bLen = 1;
            if (dInt > 0.0) {
                dVal = Math.Floor (dInt / DUINT_BASE);
                m_data1 = (uint)(dInt - dVal * DUINT_BASE);
                dInt = dVal;

                if (dInt > 0.0) {
                    dVal = Math.Floor (dInt / DUINT_BASE);
                    m_data2 = (uint)(dInt - dVal * DUINT_BASE);
                    dInt = dVal;
                    m_bLen ++;

                    if (dInt > 0.0) {
                        dVal = Math.Floor (dInt / DUINT_BASE);
                        m_data3 = (uint)(dInt - dVal * DUINT_BASE);
                        dInt = dVal;
                        m_bLen ++;

                        if (dInt > 0.0) {
                            dVal = Math.Floor (dInt / DUINT_BASE);
                            m_data4 = (uint) (dInt - dVal * DUINT_BASE);
                            dInt = dVal;
                            m_bLen ++;
                        }
                    }
                }
            }

            uint ulLen, ulLenDelta;
            uint ulTemp;

            // Get size of the integer part
            ulLen = FZero() ? 0 : (uint)CalculatePrecision();
            SQLDebug.Check(ulLen <= NUMERIC_MAX_PRECISION, "ulLen <= NUMERIC_MAX_PRECISION", "");

            // If we got more than 17 decimal digits, zero lower ones.
            if (ulLen > DBL_DIG) {
                // Divide number by 10 while there are more then 17 digits
                uint ulWrk = ulLen - DBL_DIG;
                do {
                    ulTemp = DivByULong (10);
                    ulWrk --;
                }
                while (ulWrk > 0);
                ulWrk = ulLen - DBL_DIG;

                // Round, if necessary. # of digits can change. Cannot be overflow.
                if (ulTemp >= 5) {
                    AddULong (1);
                    ulLen = CalculatePrecision() + ulWrk;
                }

                // Multiply back
                do {
                    MultByULong (10);
                    ulWrk --;
                }
                while (ulWrk > 0);
            }

            m_bScale = (byte)(ulLen < DBL_DIG ? DBL_DIG - ulLen : 0);
            m_bPrec = (byte)(ulLen + m_bScale);

            // Add meaningful fractional part - max 9 digits per iteration
            if (m_bScale > 0) {
                ulLen = m_bScale;
                do {
                    ulLenDelta = (ulLen >= 9) ? 9 : ulLen;

                    dFrac *= (double) x_rgulShiftBase[(int)ulLenDelta-1];
                    ulLen -= ulLenDelta;
                    MultByULong (x_rgulShiftBase[(int)ulLenDelta-1]);
                    AddULong ((uint) dFrac);
                    dFrac -= Math.Floor (dFrac);
                }
                while (ulLen > 0);
            }

            // Round, if necessary
            if (dFrac >= 0.5) {
                AddULong (1);
            }

            if (FZero())
                SetPositive();

            AssertValid();
        }

        private SqlDecimal(uint[] rglData, byte bLen, byte bPrec, byte bScale, bool fPositive) {
            CheckValidPrecScale(bPrec, bScale);
            SQLDebug.Check(rglData.Length >= 4);

            m_bLen = bLen;
            m_bPrec = bPrec;
            m_bScale = bScale;
            m_data1 = rglData[0];
            m_data2 = rglData[1];
            m_data3 = rglData[2];
            m_data4 = rglData[3];

            // set the null bit
            m_bStatus = x_bNotNull;

            // set the sign bit
            if (!fPositive) {
                m_bStatus |= x_bNegative;
            }

            // If result is -0, adjust sign to positive.
            if (FZero())
                SetPositive();
        }


        // INullable
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public bool IsNull {
            get { return(m_bStatus & x_bNullMask) == x_bIsNull;}
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Decimal Value {
            get { return ToDecimal();}
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public bool IsPositive {
            get {
                if (IsNull)
                    throw new SqlNullValueException();
                return(m_bStatus & x_bSignMask) == x_bPositive;
            }
        }

        private void SetPositive() {
            SQLDebug.Check(!IsNull);
            m_bStatus = (byte)(m_bStatus & x_bReverseSignMask);
        }

        private void SetSignBit(bool fPositive) {
            SQLDebug.Check(!IsNull);
            m_bStatus = (byte)(fPositive ? (m_bStatus & x_bReverseSignMask) : (m_bStatus | x_bNegative));
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public byte Precision {
            get {
                if (IsNull)
                    throw new SqlNullValueException();
                return m_bPrec;
            }
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public byte Scale {
            get {
                if (IsNull)
                    throw new SqlNullValueException();
                return m_bScale;
            }
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public int[] Data {
            get {
                if (IsNull)
                    throw new SqlNullValueException();
                return new int[4] { (int)m_data1, (int)m_data2, (int)m_data3, (int)m_data4};
            }
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public byte[] BinData {
            get {
                if (IsNull)
                    throw new SqlNullValueException();

                int data1 = (int)m_data1;
                int data2 = (int)m_data2;
                int data3 = (int)m_data3;
                int data4 = (int)m_data4;
                byte[] rgBinData = new byte[16];
                rgBinData[0] = (byte)(data1 & 0xff);
                data1 >>= 8;
                rgBinData[1] = (byte)(data1 & 0xff);
                data1 >>= 8;
                rgBinData[2] = (byte)(data1 & 0xff);
                data1 >>= 8;
                rgBinData[3] = (byte)(data1 & 0xff);
                rgBinData[4] = (byte)(data2 & 0xff);
                data2 >>= 8;
                rgBinData[5] = (byte)(data2 & 0xff);
                data2 >>= 8;
                rgBinData[6] = (byte)(data2 & 0xff);
                data2 >>= 8;
                rgBinData[7] = (byte)(data2 & 0xff);
                rgBinData[8] = (byte)(data3 & 0xff);
                data3 >>= 8;
                rgBinData[9] = (byte)(data3 & 0xff);
                data3 >>= 8;
                rgBinData[10] = (byte)(data3 & 0xff);
                data3 >>= 8;
                rgBinData[11] = (byte)(data3 & 0xff);
                rgBinData[12] = (byte)(data4 & 0xff);
                data4 >>= 8;
                rgBinData[13] = (byte)(data4 & 0xff);
                data4 >>= 8;
                rgBinData[14] = (byte)(data4 & 0xff);
                data4 >>= 8;
                rgBinData[15] = (byte)(data4 & 0xff);

                return rgBinData;
            }
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public override String ToString() {
            if (IsNull)
                return SQLResource.NullString;
            AssertValid();

            // Make local copy of data to avoid modifying input.
            uint[]  rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4};
            int     culLen = m_bLen;
            char[]  pszTmp = new char[NUMERIC_MAX_PRECISION + 1];   //Local Character buffer to hold
                                                                    //the decimal digits, from the
                                                                    //lowest significant to highest significant

            int     iDigits = 0;//Number of significant digits
            uint    ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit

            // Build the final numeric string by inserting the sign, reversing
            // the order and inserting the decimal number at the correct position

            //Retrieve each digit from the lowest significant digit
            while (culLen > 1 || rgulNumeric[0] != 0) {
                MpDiv1 (rgulNumeric, ref culLen, x_ulBase10, out ulRem);
                //modulo x_ulBase10 is the lowest significant digit
                pszTmp[iDigits++] = ChFromDigit(ulRem);
            }

            // if scale of the number has not been
            // reached pad remaining number with zeros.
            while (iDigits <= m_bScale) {
                pszTmp[iDigits++] = ChFromDigit(0);
            }


            int uiResultLen = 0;
            int iCurChar = 0;
            char[] szResult;

            // Increment the result length if scale > 0 (need to add '.')            
            if (m_bScale > 0) {
                uiResultLen = 1;
            }

            if (IsPositive) {
                szResult = new char[uiResultLen + iDigits];
            }
            else {
                // Increment the result length if negative (need to add '-')            
                szResult = new char[uiResultLen + iDigits + 1];
                szResult[iCurChar ++] = '-';
            }          
            
            while (iDigits > 0) {
                if (iDigits-- == m_bScale)
                    szResult[iCurChar ++] = '.';

                szResult[iCurChar ++] = pszTmp[iDigits];
            }

            AssertValid();

            return new String(szResult);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal Parse(String s) {
            if (s == null)
                throw new ArgumentNullException("s");

            if (s == SQLResource.NullString)
                return SqlDecimal.Null;

            SqlDecimal snResult = SqlDecimal.Null;

            char[] rgwchStr = s.ToCharArray();
            int cwchStr = rgwchStr.Length;

            int        iData;            //index to string
            char    usChar;            //current value in string
            int        lDecPnt = -1;    //position of decimal point in string
            int     iCurChar = 0;

            //Must initialize precision and scale to valid values
            snResult.m_bPrec  = 1;
            snResult.m_bScale = 0;

            //Must initialize *this to zero
            snResult.SetToZero ();

            // Trim trailing blanks.
            while (cwchStr != 0 && rgwchStr[cwchStr-1] == ' ')
                cwchStr--;

            // If string contains only spaces, stop
            if (cwchStr == 0)
                throw new FormatException(SQLResource.FormatMessage);

            // Trim leading blanks.
            while (rgwchStr[iCurChar] == ' ') {
                iCurChar++;
                cwchStr--;
            }

            // Get sign for numeric value.
            if (rgwchStr[iCurChar] == '-') {
                snResult.SetSignBit(false);
                iCurChar++;
                cwchStr--;
            }
            else {
                snResult.SetSignBit(true);
                if (rgwchStr[iCurChar] == '+') {
                    iCurChar++;
                    cwchStr--;
                }
            }

            // Hack: Check for "0.". If so, replace by ".0".
            while ((cwchStr > 2) && (rgwchStr[iCurChar] == '0')) {
                iCurChar++;
                cwchStr--;
            }
            if (2 == cwchStr && '0' == rgwchStr[iCurChar] && '.' == rgwchStr[iCurChar + 1]) {
                rgwchStr[iCurChar] = '.';
                rgwchStr[iCurChar + 1] = '0';
            }

            // Invalid string?
            if (cwchStr == 0 || cwchStr > NUMERIC_MAX_PRECISION + 1)
                throw new FormatException(SQLResource.FormatMessage);

            // Trim leading zeros.  (There shouldn't be any except for floats
            // less than 1.  e.g.  0.01)
            while ((cwchStr > 1) && (rgwchStr[iCurChar] == '0')) {
                iCurChar++;
                cwchStr--;
            }

            // Convert string to numeric value by looping through input string.
            for(iData=0; iData < cwchStr; iData++)
            {
                usChar = rgwchStr[iCurChar];
                iCurChar ++;

                if (usChar >= '0' && usChar <= '9')
                    usChar -= '0';
                else if (usChar == '.' && lDecPnt < 0) {
                    lDecPnt = iData;
                    continue;
                }
                else
                    throw new FormatException(SQLResource.FormatMessage);

                snResult.MultByULong(x_ulBase10);
                snResult.AddULong(usChar);
            }

            // Save precision and scale.
            if (lDecPnt < 0) {
                snResult.m_bPrec  = (byte)iData;
                snResult.m_bScale = 0;
            }
            else {
                snResult.m_bPrec =  (byte)(iData - 1);
                snResult.m_bScale = (byte) (snResult.m_bPrec - lDecPnt);
            }

            //Check for overflow condition
            if (snResult.m_bPrec > NUMERIC_MAX_PRECISION)
                throw new FormatException(SQLResource.FormatMessage);

            // Check for invalid precision for numeric value.
            // e.g., when string is ".", precision will be 0
            if (snResult.m_bPrec == 0)
                throw new FormatException(SQLResource.FormatMessage);

            // If result is -0, adjust sign to positive.
            if (snResult.FZero())
                snResult.SetPositive();

            snResult.AssertValid();

            return snResult;
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public double ToDouble() {
            if (IsNull)
                throw new SqlNullValueException();

            double dRet = 0.0;

            dRet = (double)m_data4;
            dRet = dRet * x_lInt32Base + m_data3;
            dRet = dRet * x_lInt32Base + m_data2;
            dRet = dRet * x_lInt32Base + m_data1;

            dRet /= System.Math.Pow(10.0, m_bScale);

            return IsPositive ? dRet : - dRet;
        }

        private Decimal ToDecimal() {
            if (IsNull)
                throw new SqlNullValueException();

            if ((int)m_data4 != 0 || m_bScale > 28)
                throw new OverflowException(SQLResource.ConversionOverflowMessage);

            return new Decimal((int)m_data1, (int)m_data2, (int)m_data3, !IsPositive, m_bScale);
        }

        // Implicit conversion from Decimal to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static implicit operator SqlDecimal(Decimal x) {
            return new SqlDecimal(x);
        }

        // Explicit conversion from Double to SqlDecimal
        public static explicit operator SqlDecimal(double x) {
            return new SqlDecimal(x);
        }

        // Implicit conversion from long to SqlDecimal
        public static implicit operator SqlDecimal(long x) {
            return new SqlDecimal(new Decimal(x));
        }

        // Explicit conversion from SqlDecimal to Decimal. Throw exception if x is Null.
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static explicit operator Decimal(SqlDecimal x) {
            return x.Value;
        }


        // Unary operators
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal operator -(SqlDecimal x) {
            if (x.IsNull)
                return Null;
            else {
                SqlDecimal s = x;
                if (s.FZero())
                    s.SetPositive();
                else
                    s.SetSignBit(!s.IsPositive);
                return s;
            }
        }


        // Binary operators

        // Arithmetic operators
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal operator +(SqlDecimal x, SqlDecimal y) {
            if (x.IsNull || y.IsNull)
                return Null;

            ulong   dwlAccum;           //accumulated sum
            bool    fMySignPos;         //sign of x was positive at start
            bool    fOpSignPos;         // sign of y positive at start
            bool    fResSignPos = true; //sign of result should be positive
            int     MyScale;    //scale of x
            int     OpScale;    //scale of y
            int     ResScale;   //scale of result
            int     ResPrec;    //precision of result
            int     ResInteger; //number of digits for the integer part of result
            int     culOp1;     //# of UI4s in x
            int     culOp2;     //# of UI4s in y
            int     iulData;    //which UI4 we are operating on in x, y
            byte    bLen;       // length for the result


            x.AssertValid();
            y.AssertValid();

            fMySignPos = x.IsPositive;
            fOpSignPos = y.IsPositive;

            //result scale = max(s1,s2)
            //result precison = max(s1,s2) + max(p1-s1,p2-s2)
            MyScale = x.m_bScale;
            OpScale = y.m_bScale;

            // Calculate the integer part of the result.
            ResInteger = Math.Max((int)x.m_bPrec - MyScale, (int)y.m_bPrec - OpScale);
            SQLDebug.Check (ResInteger <= MaxPrecision);

            // Calculate the scale of the result.
            ResScale = Math.Max(MyScale, OpScale);
            SQLDebug.Check (ResScale <= MaxScale);

            // Calculate the precision of the result.
            // Add 1 for final carry.
            ResPrec = ResInteger + ResScale + 1;
            ResPrec = Math.Min(MaxPrecision, ResPrec);

            // If precision adjusted, scale is reduced to keep the integer part untruncated.
            // But discard the extra carry, only keep the interger part as ResInteger, not ResInteger + 1.
            SQLDebug.Check(ResPrec - ResInteger >= 0);
            if (ResPrec - ResInteger < ResScale)
                ResScale = ResPrec - ResInteger;

            // Adjust both operands to be the same scale as ResScale.
            if (MyScale != ResScale)
                x.AdjustScale(ResScale - MyScale, true);

            if (OpScale != ResScale)
                y.AdjustScale(ResScale - OpScale, true);

            // When sign of first operand is negative
            // negate all operands including result.
            if (!fMySignPos) {
                fMySignPos = !fMySignPos;
                fOpSignPos = !fOpSignPos;
                fResSignPos = !fResSignPos;
            }

            // Initialize operand lengths and pointer.
            culOp1 = x.m_bLen;
            culOp2 = y.m_bLen;

            uint[] rglData1 = new uint[4] {x.m_data1, x.m_data2, x.m_data3, x.m_data4};
            uint[] rglData2 = new uint[4] {y.m_data1, y.m_data2, y.m_data3, y.m_data4};

            if (fOpSignPos) {
                dwlAccum = 0;

                // 

                // Loop through UI4s adding operands and putting result in *this
                // of the operands and put result in *this
                for (iulData = 0; iulData < culOp1 || iulData < culOp2; iulData++) {
                    // None of these DWORDLONG additions can overflow, as dwlAccum comes in < x_lInt32Base
                    if (iulData < culOp1)
                        dwlAccum += rglData1[iulData];
                    if (iulData < culOp2)
                        dwlAccum += rglData2[iulData];

                    rglData1[iulData] = (uint)dwlAccum; // equiv to mod x_lInt32Base
                    dwlAccum >>= 32; // equiv to div x_lInt32Base
                }

                //If carry
                if (dwlAccum != 0) {
                    SQLDebug.Check(dwlAccum < x_ulInt32Base);

                    //Either overflowed
                    if (iulData == x_cNumeMax)
                        throw new OverflowException(SQLResource.ArithOverflowMessage);

                    // Or extended length
                    rglData1[iulData] = (uint)dwlAccum;
                    iulData ++;
                }

                // Set result length
                bLen = (byte) iulData;
            }
            else {
                int iulLastNonZero = 0; // The last nonzero UI

                // When second operand is negative, switch operands
                // if operand2 is greater than operand1
                if (x.LAbsCmp (y) < 0) {
                    fResSignPos = !fResSignPos;
                    uint[] rguiTemp = rglData2;
                    rglData2 = rglData1;
                    rglData1 = rguiTemp;
                    culOp1 = culOp2;
                    culOp2 = x.m_bLen;
                }

                dwlAccum = x_ulInt32Base;
                for (iulData = 0; iulData < culOp1 || iulData < culOp2; iulData++) {
                    if (iulData < culOp1)
                        dwlAccum += rglData1[iulData];
                    if (iulData < culOp2)
                        dwlAccum -= rglData2[iulData];

                    rglData1[iulData] = (uint)dwlAccum; // equiv to mod BaseUI4
                    if (rglData1[iulData] != 0)
                        iulLastNonZero = iulData;
                    dwlAccum >>= 32; // equiv to /= BaseUI4
                    dwlAccum += x_ulInt32BaseForMod; // equiv to BaseUI4 - 1
                }
                // Set length based on highest non-zero ULONG
                bLen = (byte) (iulLastNonZero + 1);
            }

            SqlDecimal ret = new SqlDecimal(rglData1, bLen, (byte)ResPrec, (byte)ResScale, fResSignPos);

			if (ret.FGt10_38() || ret.CalculatePrecision () > NUMERIC_MAX_PRECISION)
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            if (ret.FZero())
                ret.SetPositive();

            ret.AssertValid();

            return ret;
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal operator -(SqlDecimal x, SqlDecimal y) {
            return x + (-y);
        }

        //    MultNm()
        //
        //    Multiply two numerics.
        //
        //  Parameters:
        //        x    - IN Multiplier
        //        y    - IN Multiplicand
        //
        //    Result scale and precision(same as in SQL Server Manual and Hydra):
        //        scale = s1 + s2
        //        precison = s1 + s2 + (p1 - s1) + (p2 - s2) + 1
        //
        //    Overflow Rules:
        //        If scale is greater than NUMERIC_MAX_PRECISION it is set to
        //    NUMERIC_MAX_PRECISION.  If precision is greater than NUMERIC_MAX_PRECISION
        //    it is set to NUMERIC_MAX_PRECISION, then scale is reduced to keep the
        //    integer part untruncated but keeping a minimum value of x_cNumeDivScaleMin.
        //    For example, if using the above formula, the resulting precision is 46 and
        //    scale is 10, the precision will be reduced to 38. To keep the integral part
        //    untruncated the scale needs be reduced to 2, but since x_cNumeDivScaleMin
        //    is set to 6 currently, resulting scale will be 6.
        //        O_OVERFLOW is returned only if the actual precision is greater than
        //     NUMERIC_MAX_PRECISION or the actual length is greater than x_cbNumeBuf.
        //
        //    Algorithm:
        //        Starting from the lowest significant UI4, for each UI4 of the multiplier
        //    iterate through the UI4s of the multiplicand starting from
        //    the least significant UI4s, multiply the multiplier UI4 with
        //    multiplicand UI4, update the result buffer with the product modulo
        //    x_dwlBaseUI4 at the same index as the multiplicand, and carry the quotient to
        //    add to the next multiplicand UI4.  Until the end of the multiplier data
        //    array is reached.
        //
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal operator *(SqlDecimal x, SqlDecimal y) {
            x.AssertValid();
            y.AssertValid();

            if (x.IsNull || y.IsNull)
                return Null;

            //Implementation:
            //        I) Figure result scale,prec
            //        II) Perform mult.
            //        III) Adjust product to result scale,prec

            // Local variables for actual multiplication
            int     iulPlier;           //index of UI4 in the Multiplier
            uint    ulPlier;            //current mutiplier UI4
            ulong   dwlAccum;           //accumulated sum
            ulong   dwlNextAccum;       //overflow of accumulated sum
            int     culCand = y.m_bLen; //length of multiplicand in UI4s

            //Local variables to track scale,precision
            int ActualScale;                    // Scale after mult done
            int ResScale;                       // Final scale we will force result to
            int ResPrec;                        // Final precision we will force result to
            int ResInteger;                     // # of digits in integer part of result (prec-scale)
            int lScaleAdjust;   //How much result scale will be adjusted
            bool fResPositive;  // Result sign

            SqlDecimal  ret;


            //I) Figure result prec,scale
            ActualScale = x.m_bScale + y.m_bScale;
            ResScale = ActualScale;
            ResInteger = (x.m_bPrec - x.m_bScale) + (y.m_bPrec - y.m_bScale) + 1;

            //result precison = s1 + s2 + (p1 - s1) + (p2 - s2) + 1
            ResPrec = ResScale + ResInteger;

            // Downward adjust res prec,scale if either larger than NUMERIC_MAX_PRECISION
            if (ResPrec >  NUMERIC_MAX_PRECISION)
                ResPrec = NUMERIC_MAX_PRECISION;
            if (ResScale > NUMERIC_MAX_PRECISION)
                ResScale = NUMERIC_MAX_PRECISION;

            //
            // It is possible when two large numbers are being multiplied the scale
            // can be reduced to 0 to keep data untruncated; the fix here is to
            // preserve a minimum scale of 6.
            //
            // If overflow, reduce the scale to avoid truncation of data
            ResScale = Math.Min((ResPrec - ResInteger),ResScale);
            // But keep a minimum scale of NUMERIC_MIN_DVSCALE
            ResScale = Math.Max(ResScale, Math.Min(ActualScale,x_cNumeDivScaleMin));

            lScaleAdjust = ResScale - ActualScale;

            fResPositive = (x.IsPositive == y.IsPositive);//positive if both signs same.

            // II) Perform multiplication

            uint[] rglData1 = new uint[4] {x.m_data1, x.m_data2, x.m_data3, x.m_data4};
            uint[] rglData2 = new uint[4] {y.m_data1, y.m_data2, y.m_data3, y.m_data4};

            //Local buffer to hold the result of multiplication.
            //Longer than CReNumeBuf because full precision of multiplication is carried out
            const int   x_culNumeMultRes = 9;       // Maximum # UI4s in result buffer in multiplication
            uint[]      rgulRes = new uint[x_culNumeMultRes]; //new [] are already initialized to zero
            int         culRes;             // # of UI4s in result
            int         idRes = 0;

            //Iterate over the bytes of multiplier
            for (iulPlier = 0; iulPlier < x.m_bLen; iulPlier++) {
                ulPlier = rglData1[iulPlier];
                dwlAccum = 0;

                //Multiply each UI4 of multiCand by ulPliear and accumulate into result buffer

                // Start on correct place in result
                idRes = iulPlier;

                for (int iulCand = 0; iulCand < culCand; iulCand++) {
                    // dwlAccum = dwlAccum + rgulRes[idRes] + ulPlier*rglData2[iulCand]
                    //        use dwlNextAccum to detect overflow of DWORDLONG
                    dwlNextAccum = dwlAccum + rgulRes[idRes];
                    ulong ulTemp = (ulong)rglData2[iulCand];
                    dwlAccum =  (ulong)ulPlier * ulTemp;
                    dwlAccum += dwlNextAccum;
                    if (dwlAccum < dwlNextAccum) // indicates dwl addition overflowed
                        dwlNextAccum = x_ulInt32Base; // = maxUI64/x_dwlBaseUI4
                    else
                        dwlNextAccum = 0;

                    // Update result and accum
                    rgulRes[idRes ++] = (uint)(dwlAccum);// & x_ulInt32BaseForMod); // equiv to mod x_lInt32Base
                    dwlAccum = (dwlAccum >> 32) + dwlNextAccum; // equiv to div BaseUI4 + dwlNAccum

                    // dwlNextAccum can't overflow next iteration
                    SQLDebug.Check(dwlAccum < x_ulInt32Base*2);
                }

                SQLDebug.Check(dwlAccum < x_ulInt32Base); // can never final accum > 1 more UI4
                if (dwlAccum != 0)
                    rgulRes[idRes ++] = (uint)dwlAccum;
            }
            // Skip leading 0s (may exist if we are multiplying by 0)
            for (;(rgulRes[idRes] == 0) && (idRes > 0); idRes--)
                ;
            // Calculate actual result length
            culRes = idRes + 1;

            // III) Adjust precision,scale to result prec,scale
            if (lScaleAdjust != 0) {
                // If need to decrease scale
                if (lScaleAdjust < 0) {
                    SQLDebug.Check(NUMERIC_MAX_PRECISION == ResPrec);

                    // have to adjust - might yet end up fitting.
                    // Cannot call AdjustScale - number cannot fit in a numeric, so
                    // have to duplicate code here

                    uint    ulRem;          //Remainder when downshifting
                    uint    ulShiftBase;    //What to multiply by to effect scale adjust

                    do {
                        if (lScaleAdjust <= -9) {
                            ulShiftBase = x_rgulShiftBase[8];
                            lScaleAdjust += 9;
                        }
                        else {
                            ulShiftBase = x_rgulShiftBase[-lScaleAdjust - 1];
                            lScaleAdjust = 0;
                        }
                        MpDiv1 (rgulRes, ref culRes, ulShiftBase, out ulRem);
                    }
                    while (lScaleAdjust != 0);

                    // Still do not fit?
                    if (culRes > x_cNumeMax)
                        throw new OverflowException(SQLResource.ArithOverflowMessage);

                    for (idRes = culRes; idRes < x_cNumeMax; idRes ++)
                        rgulRes[idRes] = 0;
                    ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec, (byte)ResScale, fResPositive);

                    // Is it greater than 10**38?
                    if (ret.FGt10_38())
                        throw new OverflowException(SQLResource.ArithOverflowMessage);

                    ret.AssertValid();

                    // If remainder is 5 or above, increment/decrement by 1.
                    if (ulRem >= ulShiftBase/2)
                        ret.AddULong(1);
                    // After adjusting, if the result is 0 and remainder is less than 5,
                    // set the sign to be positive
                    if (ret.FZero ())
                        ret.SetPositive();

                    return ret;
                }

                // Otherwise call AdjustScale
                if (culRes > x_cNumeMax)    // Do not fit now, so will not fit after asjustement
                    throw new OverflowException(SQLResource.ArithOverflowMessage);
                // NOTE: Have not check for value in the range (10**38..2**128),
                // as we'll call AdjustScale with positive argument, and it'll
                // return "normal" overflow

                for (idRes = culRes; idRes < x_cNumeMax; idRes ++)
                    rgulRes[idRes] = 0;
                ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec, (byte)ActualScale, fResPositive);

                if (ret.FZero ())
                    ret.SetPositive();

                ret.AssertValid();

                ret.AdjustScale(lScaleAdjust, true);

                return ret;
            }
            else {
                if (culRes > x_cNumeMax)
                    throw new OverflowException(SQLResource.ArithOverflowMessage);

                for (idRes = culRes; idRes < x_cNumeMax; idRes ++)
                    rgulRes[idRes] = 0;
                ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec, (byte)ResScale, fResPositive);

                // Is it greater than 10**38?
                if (ret.FGt10_38 ())
                    throw new OverflowException(SQLResource.ArithOverflowMessage);

                if (ret.FZero ())
                    ret.SetPositive();

                ret.AssertValid();

                return ret;
            }
        }

        //-----------------------------------------------------------
        //DivNm():
        //  Divide numeric by numeric.
        //    The Quotient will be returned in *this
        //
        //Result scale&precision:
        //    NOTE: same as in Hydra but different from SQL Server Manual,
        //            where scale = max(s1+p2-s2+1,x_cNumeDivScaleMin)):
        //        scale = max(s1 + p2 + 1, x_cNumeDivScaleMin);
        //        precision = max(s1 + p2 + 1, x_cNumeDivScaleMin) + p1 + p2 + 1;
        //
        //Overflow Rules:
        //        If scale is greater than NUMERIC_MAX_PRECISION it is set to
        //    NUMERIC_MAX_PRECISION.  If precision is greater than NUMERIC_MAX_PRECISION
        //    it's set to NUMERIC_MAX_PRECISION, then scale is reduced to keep the
        //    integer part untruncated but keeping a minimum value of x_cNumeDivScaleMin.
        //    For example, if using the above formula, the resulting precision is 46 and
        //    scale is 10, the precision will be reduced to 38, to keep the integral part
        //    untruncated the scale needs be recuded to 2, but since x_cNumeDivScaleMin
        //    is set to 6 currently, resulting scale will be 6.
        //        OverflowException is throwed only if the actual precision is greater than
        //    NUMERIC_MAX_PRECISION or actual length is greater than x_cbNumeBuf
        //
        //Algorithm
        //  Call general purpose arbitrary precision division routine with scale = 0.
        //    Scale,prec adjusted later.
        //
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal operator /(SqlDecimal x, SqlDecimal y) {
            if (x.IsNull || y.IsNull)
                return Null;

            x.AssertValid();
            y.AssertValid();

            // Variables for figuring prec,scale
            int bScaleD;            // Input Scale of dividend (output scale of remainder)
            int bPrecD;             // Input Prec of dividend (output prec of remainder)
            int ResScale;           // Final scale we will force quotient to
            int ResPrec;            // Final precision we will force quotient to
            int ResInteger;         // # of digits in integer part of result (prec-scale)
            int MinScale;           // Temp to help compute ResScale
            int lScaleAdjust;       // How much result scale will be adjusted
            bool fResSignPos;       // sign of result

            // Steps:
            //    1) Figure result prec,scale; adjust scale of dividend
            //    2) Compute result remainder/quotient in 0 scale numbers
            //    3) Set result prec,scale and adjust as necessary

            // 0) Check for Div by 0
            if (y.FZero())
                throw new DivideByZeroException(SQLResource.DivideByZeroMessage);

            // 1) Figure out result prec,scale,sign..
            fResSignPos = (x.IsPositive == y.IsPositive);//sign of result

            //scale = max(s1 + p2 + 1, x_cNumeDivScaleMin);
            //precision = max(s1 + p2 + 1, x_cNumeDivScaleMin) + p1 + p2 + 1;
            //For backward compatibility, use exactly the same scheme as in Hydra
            bScaleD = x.m_bScale;
            bPrecD = x.m_bPrec;
            ResScale = Math.Max(x.m_bScale + y.m_bPrec + 1, x_cNumeDivScaleMin);
            ResInteger = x.m_bPrec - x.m_bScale + y.m_bScale;
            ResPrec = ResScale + x.m_bPrec + y.m_bPrec + 1;
            MinScale = Math.Min(ResScale, x_cNumeDivScaleMin);

            ResInteger = Math.Min(ResInteger, NUMERIC_MAX_PRECISION);
            ResPrec = ResInteger + ResScale;

            if (ResPrec >  NUMERIC_MAX_PRECISION)
                ResPrec = NUMERIC_MAX_PRECISION;

            // If overflow, reduce the scale to avoid truncation of data
            ResScale = Math.Min((ResPrec - ResInteger), ResScale);
            ResScale = Math.Max(ResScale, MinScale);

            //Adjust the scale of the dividend
            lScaleAdjust = ResScale - (int)x.m_bScale + (int)y.m_bScale;
            x.AdjustScale(lScaleAdjust, true);

            // Step2: Actual Computation

            uint[] rgulData1 = new uint[4] {x.m_data1, x.m_data2, x.m_data3, x.m_data4};
            uint[] rgulData2 = new uint[4] {y.m_data1, y.m_data2, y.m_data3, y.m_data4};

            // Buffers for arbitrary precision divide
            uint[] rgulR = new uint[x_cNumeMax + 1];
            uint[] rgulQ = new uint[x_cNumeMax];
            // # of ULONGs in result
            int culQ, culR;

            // Divide mantissas. V is not zero - already checked.
            // Cannot overflow, as Q <= U, R <= V. (and both are positive)
            MpDiv(rgulData1, x.m_bLen, rgulData2, y.m_bLen, rgulQ, out culQ, rgulR, out culR);

            // Construct the result from Q
            ZeroToMaxLen (rgulQ, culQ);
            SqlDecimal ret = new SqlDecimal(rgulQ, (byte)culQ, (byte)ResPrec, (byte)ResScale, fResSignPos);

            if (ret.FZero())
                ret.SetPositive();

            ret.AssertValid();

            return ret;
        }



        // Implicit conversions

        // Implicit conversion from SqlBoolean to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static explicit operator SqlDecimal(SqlBoolean x) {
            return x.IsNull ? Null : new SqlDecimal((int)x.ByteValue);
        }

        // Implicit conversion from SqlByte to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static implicit operator SqlDecimal(SqlByte x) {
            return x.IsNull ? Null : new SqlDecimal((int)(x.Value));
        }

        // Implicit conversion from SqlInt16 to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static implicit operator SqlDecimal(SqlInt16 x) {
            return x.IsNull ? Null : new SqlDecimal((int)(x.Value));
        }

        // Implicit conversion from SqlInt32 to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static implicit operator SqlDecimal(SqlInt32 x) {
            return x.IsNull ? Null : new SqlDecimal(x.Value);
        }

        // Implicit conversion from SqlInt64 to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static implicit operator SqlDecimal(SqlInt64 x) {
            return x.IsNull ? Null : new SqlDecimal(x.Value);
        }

        // Implicit conversion from SqlMoney to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static implicit operator SqlDecimal(SqlMoney x) {
            return x.IsNull ? Null : new SqlDecimal(x.ToDecimal());
        }


        // Explicit conversions

         // Explicit conversion from SqlSingle to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static explicit operator SqlDecimal(SqlSingle x) {
            return x.IsNull ? SqlDecimal.Null : new SqlDecimal((double)(x.Value));
        }

       // Explicit conversion from SqlDouble to SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static explicit operator SqlDecimal(SqlDouble x) {
            return x.IsNull ? SqlDecimal.Null : new SqlDecimal(x.Value);
        }

        // Explicit conversion from SqlString to SqlDecimal
        // Throws FormatException or OverflowException if necessary.
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static explicit operator SqlDecimal(SqlString x) {
            return x.IsNull ? Null : SqlDecimal.Parse(x.Value);
        }

        // private methods

        //----------------------------------------------------------------------
        // Is this RE numeric valid?
        [System.Diagnostics.Conditional("DEBUG")]
        private void AssertValid() {
            if (IsNull)
                return;

            // Scale,Prec in range
            SQLDebug.Check(m_bScale <= NUMERIC_MAX_PRECISION, "m_bScale <= NUMERIC_MAX_PRECISION", "In AssertValid");
            SQLDebug.Check(m_bScale <= m_bPrec, "m_bScale <= m_bPrec", "In AssertValid");
            SQLDebug.Check(m_bScale >= 0, "m_bScale >= 0", "In AssertValid");
            SQLDebug.Check(m_bPrec > 0, "m_bPrec > 0", "In AssertValid");

            SQLDebug.Check(CLenFromPrec(m_bPrec) >= m_bLen, "CLenFromPrec(m_bPrec) >= m_bLen", "In AssertValid");
            SQLDebug.Check(m_bLen <= x_cNumeMax, "m_bLen <= x_cNumeMax", "In AssertValid");

            uint[] rglData = new uint[4] {m_data1, m_data2, m_data3, m_data4};

            // highest UI4 is non-0 unless value "zero"
            if (rglData[m_bLen-1] == 0) {
                SQLDebug.Check(m_bLen == 1, "m_bLen == 1", "In AssertValid");
            }

            // All UI4s from length to end are 0
            for (int iulData = m_bLen; iulData < x_cNumeMax; iulData++)
                SQLDebug.Check(rglData[iulData] == 0, "rglData[iulData] == 0", "In AssertValid");
        }
/*
        // print the data members
        [System.Diagnostics.Conditional("DEBUG")]
        private void Print() {
            if (IsNull) {
                Debug.WriteLine("Numeric: Null");
                return;
            }
            Debug.WriteLine("Numeric: data - " + m_data4.ToString() + ", " + m_data3.ToString() + ", " +
                              m_data2.ToString() + ", " + m_data1.ToString());
            Debug.WriteLine("\tlen = " + m_bLen.ToString() + ", Prec = " + m_bPrec.ToString() +
                              ", Scale = " + m_bScale.ToString() + ", Sign = " + IsPositive.ToString());
        }

        [System.Diagnostics.Conditional("DEBUG")]
        private void Print(String s) {
            Debug.WriteLine("*** " + s + " ***");
            if (IsNull) {
                Debug.WriteLine("Numeric: Null");
                return;
            }
            Debug.WriteLine("Numeric: data - " + m_data4.ToString() + ", " + m_data3.ToString() + ", " +
                              m_data2.ToString() + ", " + m_data1.ToString());
            Debug.WriteLine("\tlen = " + m_bLen.ToString() + ", Prec = " + m_bPrec.ToString() +
                              ", Scale = " + m_bScale.ToString() + ", Sign = " + IsPositive.ToString());
        }
*/
        // Set all extra uints to zero
        private static void ZeroToMaxLen(uint[] rgulData, int cUI4sCur) {
            SQLDebug.Check(rgulData.Length == x_cNumeMax, "rgulData.Length == x_cNumeMax", "Invalid array length");

            switch (cUI4sCur) {
                case 1:
                    rgulData[1] = rgulData[2] = rgulData[3] = 0;
                    break;

                case 2:
                    rgulData[2] = rgulData[3] = 0;
                    break;

                case 3:
                    rgulData[3] = 0;
                    break;
            }
        }

        // Set all extra uints to zero
        /*
        private void ZeroToMaxLen(int cUI4sCur) {
            switch (cUI4sCur) {
                case 1:
                    m_data2 = m_data3 = m_data4 = x_uiZero;
                    break;

                case 2:
                    m_data3 = m_data4 = x_uiZero;
                    break;

                case 3:
                    m_data4 = x_uiZero;
                    break;
            }
        }
        */

        //Determine the number of uints needed for a numeric given a precision
        //Precision        Length
        //    0            invalid
        //    1-9            1
        //    10-19        2
        //    20-28        3
        //    29-38        4
        // The array in Shiloh. Listed here for comparison.
        //private static readonly byte[] rgCLenFromPrec = new byte[] {5,5,5,5,5,5,5,5,5,9,9,9,9,9,
        //    9,9,9,9,9,13,13,13,13,13,13,13,13,13,17,17,17,17,17,17,17,17,17,17};
        private static readonly byte[] rgCLenFromPrec = new byte[] {1,1,1,1,1,1,1,1,1,2,2,2,2,2,
            2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4};

        private static byte CLenFromPrec(byte bPrec) {
            SQLDebug.Check(bPrec <= MaxPrecision && bPrec > 0, "bPrec <= MaxPrecision && bPrec > 0",
                           "Invalid numeric precision");
            return rgCLenFromPrec[bPrec-1];
        }

        // check whether is zero
        private bool FZero() {
            return(m_data1 == 0) && (m_bLen <= 1);
        }

        // Find the case where we overflowed 10**38, but not 2**128
        private bool FGt10_38() {
            return    m_data4 >= 0x4b3b4ca8L && m_bLen == 4 &&
            ((m_data4 > 0x4b3b4ca8L) || (m_data3 > 0x5a86c47aL) ||
             (m_data3 == 0x5a86c47aL) && (m_data2 >= 0x098a2240L));
        }

        private bool FGt10_38(uint[] rglData) {
            SQLDebug.Check(rglData.Length == 4, "rglData.Length == 4", "Wrong array length: " + rglData.Length.ToString(CultureInfo.InvariantCulture));

            return    rglData[3] >= 0x4b3b4ca8L &&
            ((rglData[3] > 0x4b3b4ca8L) || (rglData[2] > 0x5a86c47aL) ||
             (rglData[2] == 0x5a86c47aL) && (rglData[1] >= 0x098a2240L));
        }


        // Powers of ten (used by BGetPrecI4,BGetPrecI8)
        private static readonly uint x_ulT1 =       10;
        private static readonly uint x_ulT2 =       100;
        private static readonly uint x_ulT3 =       1000;
        private static readonly uint x_ulT4 =       10000;
        private static readonly uint x_ulT5 =       100000;
        private static readonly uint x_ulT6 =       1000000;
        private static readonly uint x_ulT7 =       10000000;
        private static readonly uint x_ulT8 =       100000000;
        private static readonly uint x_ulT9 =       1000000000;
        private static readonly ulong x_dwlT10 =    10000000000;
        private static readonly ulong x_dwlT11 =    100000000000;
        private static readonly ulong x_dwlT12 =    1000000000000;
        private static readonly ulong x_dwlT13 =    10000000000000;
        private static readonly ulong x_dwlT14 =    100000000000000;
        private static readonly ulong x_dwlT15 =    1000000000000000;
        private static readonly ulong x_dwlT16 =    10000000000000000;
        private static readonly ulong x_dwlT17 =    100000000000000000;
        private static readonly ulong x_dwlT18 =    1000000000000000000;
        private static readonly ulong x_dwlT19 =    10000000000000000000;

        //------------------------------------------------------------------
        //BGetPrecI4
        //    Return the precision(number of significant digits) of a integer
        private static byte BGetPrecUI4(uint value) {
            int ret;

            // Now do the (almost) binary search
            if (value < x_ulT4) {
                if (value < x_ulT2)
                    ret = value >= x_ulT1 ? 2 : 1;
                else
                    ret = value >= x_ulT3 ? 4 : 3;
            }
            else if (value < x_ulT8) {
                if (value < x_ulT6)
                    ret = value >= x_ulT5 ? 6 : 5;
                else
                    ret = value >= x_ulT7 ? 8 : 7;
            }
            else
                ret = value >= x_ulT9 ? 10 : 9;

            return(byte)ret;
        }

        //------------------------------------------------------------------
        //BGetPrecI8
        //    Return the precision (number of significant digits) of an Int8
        private static byte  BGetPrecUI8(uint  ulU0, uint ulU1) {
            ulong dwlVal = ((ulong) ulU0) + (((ulong) ulU1) << 32);
            return BGetPrecUI8 (dwlVal);
        }

        private static byte  BGetPrecUI8(ulong dwlVal) {
            int ret;

            // Now do the (almost) binary search
            if (dwlVal < x_ulT8) {
                uint ulVal = (uint) dwlVal;

                if (ulVal < x_ulT4) {
                    if (ulVal < x_ulT2)
                        ret = (ulVal >= x_ulT1) ? 2 : 1;
                    else
                        ret = (ulVal >= x_ulT3) ? 4 : 3;
                }
                else {
                    if (ulVal < x_ulT6)
                        ret = (ulVal >= x_ulT5) ? 6 : 5;
                    else
                        ret = (ulVal >= x_ulT7) ? 8 : 7;
                }
            }
            else if (dwlVal < x_dwlT16) {
                if (dwlVal < x_dwlT12) {
                    if (dwlVal < x_dwlT10)
                        ret = (dwlVal >= x_ulT9) ? 10 : 9;
                    else
                        ret = (dwlVal >= x_dwlT11) ? 12 : 11;
                }
                else {
                    if (dwlVal < x_dwlT14)
                        ret = (dwlVal >= x_dwlT13) ? 14 : 13;
                    else
                        ret = (dwlVal >= x_dwlT15) ? 16 : 15;
                }
            }
            else {
                if (dwlVal < x_dwlT18)
                    ret = (dwlVal >= x_dwlT17) ? 18 : 17;
                else
                    ret = (dwlVal >= x_dwlT19) ? 20 : 19;
            }

            return(byte)ret;
        }

#if DEBUG
        // This is the old precision routine. It is only used in debug code to verify that both code paths
        // return the same value

        //
        //    BActualPrec()
        //
        //    Determine the actual number of significant digits (precision) of a numeric
        //
        //    Parameters:
        //
        //    Complexity:
        //        For small numerics, use simpler routines = O(n)
        //        Else, max 3 divisions of mp by ULONG, then again simpler routine.
        //
        //    Returns:
        //        a byte containing the actual precision
        //
        private byte BActualPrec () {
            if (m_bPrec == 0 || m_bLen < 1)
                return 0;

            int     ciulU = m_bLen;
            int     Prec;
            uint    ulRem;

            if (ciulU == 1) {
                Prec = BGetPrecUI4 (m_data1);
            }
            else if (ciulU == 2) {
                Prec = BGetPrecUI8 (m_data1, m_data2);
            }
            else {
                uint[]  rgulU = new uint[4] { m_data1, m_data2, m_data3, m_data4};
                Prec = 0;
                do {
                    MpDiv1 (rgulU, ref ciulU, 1000000000, out ulRem);
                    Prec += 9;
                }
                while (ciulU > 2);
                SQLDebug.Check (Prec == 9 || Prec == 18 || Prec == 27);
                Prec += BGetPrecUI8 (rgulU[0], rgulU[1]);
            }

            // If number of significant digits less than scale, return scale
            return(Prec < m_bScale ? m_bScale : (byte)Prec);
        }
#endif

        //    AddULong()
        //
        //    Add ulAdd to this numeric.  The result will be returned in *this.
        //
        //    Parameters:
        //        this    - IN Operand1 & OUT Result
        //        ulAdd    - IN operand2.
        //
        private void AddULong(uint ulAdd) {
            ulong   dwlAccum = (ulong) ulAdd;
            int     iData;                  // which UI4 in this we are on
            int     iDataMax = (int)m_bLen; // # of UI4s in this

            uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4};

            // Add, starting at the LS UI4 until out of UI4s or no carry
            iData = 0;
            do {
                dwlAccum += (ulong) rguiData[iData];
                rguiData[iData] = (uint)dwlAccum;       // equivalent to mod x_dwlBaseUI4
                dwlAccum >>= 32;                        // equivalent to dwlAccum /= x_dwlBaseUI4;
                if (0 == dwlAccum) {
                    StoreFromWorkingArray(rguiData);
                    return;
                }
                iData ++;
            }
            while (iData < iDataMax);

            // There is carry at the end

            SQLDebug.Check(dwlAccum < x_ulInt32Base, "dwlAccum < x_lInt32Base", "");

            // Either overflowed
            if (iData == x_cNumeMax)
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            // Or need to extend length by 1 UI4
            rguiData[iData] = (uint)dwlAccum;
            m_bLen ++;

            if (FGt10_38 (rguiData))
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            StoreFromWorkingArray(rguiData);
        }

        // multiply by a long integer
        private void MultByULong(uint uiMultiplier) {
            int     iDataMax = m_bLen; // How many UI4s currently in *this

            ulong   dwlAccum = 0;       // accumulated sum
            ulong   dwlNextAccum = 0;   // accumulation past dwlAccum
            int     iData;              // which UI4 in *This we are on.

            uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4};

            for (iData = 0; iData < iDataMax; iData++) {
                SQLDebug.Check(dwlAccum < x_ulInt32Base);

                ulong ulTemp = (ulong)rguiData[iData];
                dwlNextAccum = ulTemp * (ulong)uiMultiplier;
                dwlAccum += dwlNextAccum;
                if (dwlAccum < dwlNextAccum)        // Overflow of int64 add
                    dwlNextAccum = x_ulInt32Base;   // how much to add to dwlAccum after div x_dwlBaseUI4
                else
                    dwlNextAccum = 0;
                rguiData[iData] = (uint)dwlAccum;           // equivalent to mod x_dwlBaseUI4
                dwlAccum = (dwlAccum >> 32) + dwlNextAccum; // equivalent to div x_dwlBaseUI4
            }

            // If any carry,
            if (dwlAccum != 0) {
                // Either overflowed
                SQLDebug.Check(dwlAccum < x_ulInt32Base, "dwlAccum < x_dwlBaseUI4", "Integer overflow");
                if (iDataMax == x_cNumeMax)
                    throw new OverflowException(SQLResource.ArithOverflowMessage);

                // Or extend length by one uint
                rguiData[iDataMax] = (uint)dwlAccum;
                m_bLen ++;
            }

            if (FGt10_38 (rguiData))
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            StoreFromWorkingArray(rguiData);
        }


        //    DivByULong()
        //
        //    Divide numeric value by a ULONG.  The result will be returned
        //    in the dividend *this.
        //
        //    Parameters:
        //        this        - IN Dividend & OUT Result
        //        ulDivisor    - IN Divisor
        //    Returns:        - OUT Remainder
        //
        private uint DivByULong(uint iDivisor) {
            ulong   dwlDivisor = (ulong) iDivisor;
            ulong   dwlAccum = 0;           //Accumulated sum
            uint    ulQuotientCur = 0;      // Value of the current UI4 of the quotient
            bool    fAllZero = true;    // All of the quotient (so far) has been 0
            int     iData;              //Which UI4 currently on

            // Check for zero divisor.
            if (dwlDivisor == 0)
                throw new DivideByZeroException(SQLResource.DivideByZeroMessage);

            // Copy into array, so that we can iterate through the data
            uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4};

            // Start from the MS UI4 of quotient, divide by divisor, placing result
            //        in quotient and carrying the remainder.
            //DEVNOTE DWORDLONG sufficient accumulator since:
            //        Accum < Divisor <= 2^32 - 1    at start each loop
            //                                    initially,and mod end previous loop
            //        Accum*2^32 < 2^64 - 2^32
            //                                    multiply both side by 2^32 (x_dwlBaseUI4)
            //        Accum*2^32 + m_rgulData < 2^64
            //                                    rglData < 2^32
            for (iData = m_bLen; iData > 0; iData--) {
                SQLDebug.Check(dwlAccum < dwlDivisor);
                dwlAccum = (dwlAccum << 32) + (ulong)(rguiData[iData-1]); // dwlA*x_dwlBaseUI4 + rglData
                SQLDebug.Check((dwlAccum / dwlDivisor) < x_ulInt32Base);
                //Update dividend to the quotient.
                ulQuotientCur = (uint)(dwlAccum / dwlDivisor);
                rguiData[iData-1] = ulQuotientCur;
                //Remainder to be carried to the next lower significant byte.
                dwlAccum = dwlAccum % dwlDivisor;

                // While current part of quotient still 0, reduce length
                if (fAllZero && (ulQuotientCur == 0)) {
                    m_bLen --;
                } else {
                    fAllZero = false;
                }
            }

            StoreFromWorkingArray(rguiData);

            // If result is 0, preserve sign but set length to 5
            if (fAllZero)
                m_bLen = 1;

            AssertValid();

            // return the remainder
            SQLDebug.Check(dwlAccum < x_ulInt32Base);
            return(uint)dwlAccum;
        }

        //    AdjustScale()
        //
        //    Adjust number of digits to the right of the decimal point.
        //    A positive adjustment increases the scale of the numeric value
        //    while a negative adjustment decreases the scale.  When decreasing
        //    the scale for the numeric value, the remainder is checked and
        //    rounded accordingly.
        //
        internal void AdjustScale(int digits, bool fRound) {
            SQLDebug.Check(!IsNull, "!IsNull", "In AdjustScale");

            uint    ulRem;                  //Remainder when downshifting
            uint    ulShiftBase;            //What to multiply by to effect scale adjust
            bool    fNeedRound = false;     //Do we really need to round?
            byte    bNewScale, bNewPrec;
            int     lAdjust = digits;

            //If downshifting causes truncation of data
            if (lAdjust + m_bScale < 0)
                throw new SqlTruncateException();

            //If uphifting causes scale overflow
            if (lAdjust + m_bScale > NUMERIC_MAX_PRECISION)
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            bNewScale = (byte) (lAdjust + m_bScale);
            bNewPrec = (byte) (Math.Min(NUMERIC_MAX_PRECISION,Math.Max(1,lAdjust + m_bPrec)));

            if (lAdjust > 0) {
                m_bScale = bNewScale;
                m_bPrec  = bNewPrec;

                while (lAdjust > 0) {
                    //if lAdjust>=9, downshift by 10^9 each time, otherwise by the full amount
                    if (lAdjust >= 9) {
                        ulShiftBase = x_rgulShiftBase[8];
                        lAdjust -= 9;
                    }
                    else {
                        ulShiftBase = x_rgulShiftBase[lAdjust-1];
                        lAdjust = 0;
                    }
                    MultByULong(ulShiftBase);
                }
            }
            else if (lAdjust < 0) {
                do {
                    if (lAdjust <= -9) {
                        ulShiftBase = x_rgulShiftBase[8];
                        lAdjust += 9;
                    }
                    else {
                        ulShiftBase = x_rgulShiftBase[-lAdjust - 1];
                        lAdjust = 0;
                    }
                    ulRem = DivByULong (ulShiftBase);
                }
                while (lAdjust < 0);

                // Do we really need to round?
                fNeedRound = (ulRem >= ulShiftBase/2);

                m_bScale = bNewScale;
                m_bPrec =  bNewPrec;
            }

            AssertValid();

            // After adjusting, if the result is 0 and remainder is less than 5,
            // set the sign to be positive and return.
            if (fNeedRound && fRound) {
                // If remainder is 5 or above, increment/decrement by 1.
                AddULong(1);
            }
            else if (FZero())
                SetPositive();
        }

        // Adjust scale of a SqlDecimal
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal AdjustScale(SqlDecimal n, int digits, bool fRound) {
            if (n.IsNull)
                return SqlDecimal.Null;

            SqlDecimal ret = n;
            ret.AdjustScale(digits, fRound);
            return ret;
        }

        // Convert to a specific precision and scale
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal ConvertToPrecScale(SqlDecimal n, int precision, int scale) {
            CheckValidPrecScale(precision, scale);
            n.AssertValid();

            if (n.IsNull)
                return SqlDecimal.Null;

            SqlDecimal ret = n;

            int lPrecAdjust = precision - (int)ret.m_bPrec;//Adjustment to precision
            int lScaleAdjust = scale - (int)ret.m_bScale;//Adjustment to scale

            //Adjust scale
            ret.AdjustScale(lScaleAdjust, true);

// devnote: is that still true?
            //Shouldn't truncate the integer digits; BActualPrec() is an expensive
            //function call, so test the data length first to eliminate majority
            //of cases

            //The number of bytes storage required by the new precision
            byte cbWithNewPrec = CLenFromPrec((byte)precision);

            if (cbWithNewPrec < ret.m_bLen) {
                //if current actual length greater than length corresponding to bNewPrec
                //there must be truncating
                throw new SqlTruncateException();
            }
            else if (cbWithNewPrec == ret.m_bLen) {
                //if the two lengths equal, need to check the actual precision
                if (precision < ret.CalculatePrecision())
                    throw new SqlTruncateException();
            }

            //Adjust precision
            ret.m_bPrec = (byte)precision;

            ret.AssertValid();

            return ret;
        }

        //    LAbsCmp()
        //
        //    Compare the absolute value of two numerics without checking scale
        //
        //  Parameters:
        //        this    - IN Operand1
        //        snumOp    - IN Operand2
        //
        //    Returns:
        //        positive    - |this| > |snumOp|
        //        0            - |this| = |snumOp|
        //        negative    - |this| < |snumOp|
        //
        private int LAbsCmp(SqlDecimal snumOp) {

            int         iData;  //which UI4 we are operating on
            int         culOp;  //#of UI4s on operand
            int         culThis; //# of UI4s in this

            // If one longer, it is larger
            culOp = snumOp.m_bLen;
            culThis = m_bLen;
            if (culOp != culThis)
                return(culThis > culOp) ? 1 : -1;

            uint[] rglData1 = new uint[4] {m_data1, m_data2, m_data3, m_data4};
            uint[] rglData2 = new uint[4] {snumOp.m_data1, snumOp.m_data2, snumOp.m_data3, snumOp.m_data4};

            // Loop through numeric value checking each byte for differences.
            iData = culOp-1;
            do {
                // 

                if (rglData1[iData] != rglData2[iData])
                    return((rglData1[iData] > rglData2[iData]) ? 1 : -1);
                iData --;
            }
            while (iData >= 0);

            // All UI4s the same, return 0.
            return 0;
        }

        // Move multi-precision number
        private static void MpMove
        (
        uint[]  rgulS,      // In    | Source number
        int     ciulS,      // In    | # of digits in S
        uint[]  rgulD,      // Out    | Destination number
        out int ciulD       // Out    | # of digits in D
        ) {
            ciulD = ciulS;

            SQLDebug.Check(rgulS.Length >= ciulS, "rgulS.Length >= ciulS", "Invalid array length");
            SQLDebug.Check(rgulD.Length >= ciulS, "rgulD.Length >= ciulS", "Invalid array length");

            for (int i = 0; i < ciulS; i ++)
                rgulD[i] = rgulS[i];
        }

        // Set multi-precision number to one super-digit
        private static void MpSet
        (
        uint[]  rgulD,      // Out    | Number
        out int ciulD,      // Out    | # of digits in D
        uint    iulN        // In    | ULONG to set
        ) {
            ciulD = 1;
            rgulD[0] = iulN;
        }

        // Normalize multi-precision number - remove leading zeroes
        private static void MpNormalize
        (
        uint[]  rgulU,      // In   | Number
        ref int ciulU       // InOut| # of digits
        ) {
            while (ciulU > 1 && rgulU[ciulU-1] == 0)
                ciulU --;
        }

        // Multi-precision one super-digit multiply in place.
        // D = D * X
        // Length can increase
        private static void MpMul1
        (
        uint[]  piulD,      // InOut| D
        ref int ciulD,      // InOut| # of digits in D
        uint    iulX        // In    | X
        ) {
            SQLDebug.Check(iulX > x_uiZero);
            uint ulCarry = 0;
            int iData;
            ulong dwlAccum;

            for (iData = 0; iData < ciulD; iData ++) {
                ulong ulTemp = (ulong)piulD[iData];
                dwlAccum = ulCarry + ulTemp * (ulong)iulX;
                ulCarry = HI (dwlAccum);
                piulD[iData] = LO (dwlAccum);
            }

            // If overflow occurs, increase precision
            if (ulCarry != 0) {
                piulD[iData] = ulCarry;
                ciulD ++;
            }
        }

        // Multi-precision one super-digit divide in place.
        // U = U / D,
        // R = U % D
        // Length of U can decrease
        private static void MpDiv1
        (
        uint[]      rgulU,      // InOut| U
        ref int     ciulU,      // InOut| # of digits in U
        uint        iulD,       // In    | D
        out uint    iulR        // Out    | R
        ) {
            SQLDebug.Check(rgulU.Length == x_cNumeMax);

            uint    ulCarry = 0;
            ulong   dwlAccum;
            ulong   ulD = (ulong)iulD;
            int     idU = ciulU;

            SQLDebug.Check(iulD != 0, "iulD != 0", "Divided by zero!");
            SQLDebug.Check(iulD > 0, "iulD > 0", "Invalid data: less than zero");
            SQLDebug.Check(ciulU > 0, "ciulU > 0", "No data in the array");

            while (idU > 0) {
                idU --;
                dwlAccum = (((ulong)ulCarry) << 32) + (ulong)(rgulU[idU]);
                rgulU[idU] = (uint)(dwlAccum / ulD);
                ulCarry = (uint) (dwlAccum - (ulong)rgulU[idU] * ulD);  // (ULONG) (dwlAccum % iulD)
            }
            iulR = ulCarry;
            MpNormalize (rgulU, ref ciulU);
        }

        internal static ulong DWL(uint lo, uint hi) {
            return(ulong)lo + ( ((ulong)hi) << 32 );
        }

        private static uint HI(ulong x) {
            return(uint)(x >> 32);
        }

        private static uint LO(ulong x) {
            return(uint)x;
        }

        // Multi-precision divide.
        // Q = U / D,
        // R = U % D,
        // U and D not changed.
        // It is Ok for U and R to have the same location in memory,
        // but then U will be changed.
        // Assumes that there is enough room in Q and R for results.

        // Drawbacks of this implementation:
        //    1)    Need one extra super-digit (ULONG) in R
        //    2)    As it modifies D during work, then it do (probably) unnecessary
        //        work to restore it
        //    3)    Always get Q and R - if R is unnecessary, can be slightly faster
        // Most of this can be fixed if it'll be possible to have a working buffer. But
        // then we'll use alloca() or there will be limit on the upper size of numbers
        // (maybe not a problem in SQL Server).
        //
        private static void MpDiv
        (
        uint[]  rgulU,      // In    | U
        int     ciulU,      // In    | # of digits in U
        uint[]  rgulD,      // In    | D
        int     ciulD,      // In    | # of digits in D
        uint[]  rgulQ,      // Out    | Q
        out int ciulQ,      // Out    | # of digits in Q
        uint[]  rgulR,      // Out    | R
        out int ciulR       // Out    | # of digits in R
        ) {
            SQLDebug.Check(ciulU > 0, "ciulU > 0", "In method MpDiv");
            SQLDebug.Check(ciulD > 0, "ciulD > 0", "In method MpDiv");
            SQLDebug.Check(rgulU.Length == x_cNumeMax);
            SQLDebug.Check(rgulD.Length == x_cNumeMax);

            // Division by zero?
            if (ciulD == 1 && rgulD[0] == 0) {
                ciulQ = ciulR = 0;
            }

            // Check for simplest case, so it'll be fast
            else if (ciulU == 1 && ciulD == 1) {
                MpSet (rgulQ, out ciulQ, rgulU[0] / rgulD[0]);
                MpSet (rgulR, out ciulR, rgulU[0] % rgulD[0]);
            }

            // If D > U then do not divide at all
            else if (ciulD > ciulU) {
                MpMove (rgulU, ciulU, rgulR, out ciulR);        // R = U
                MpSet (rgulQ, out ciulQ, 0);                    // Q = 0
            }

            // Try to divide faster - check for remaining good sizes (8 / 4, 8 / 8)
            else if (ciulU <= 2) {
                ulong dwlU, dwlD, dwlT;

                dwlU = DWL (rgulU[0], rgulU[1]);
                dwlD = rgulD[0];
                if (ciulD > 1)
                    dwlD += ( ((ulong)rgulD[1]) << 32 );
                dwlT = dwlU / dwlD;
                rgulQ[0] = LO (dwlT);
                rgulQ[1] = HI (dwlT);
                ciulQ = (HI (dwlT) != 0) ? 2 : 1;
                dwlT = dwlU % dwlD;
                rgulR[0] = LO (dwlT);
                rgulR[1] = HI (dwlT);
                ciulR = (HI (dwlT) != 0) ? 2 : 1;
            }

            // If we are dividing by one digit - use simpler routine
            else if (ciulD == 1) {
                MpMove (rgulU, ciulU, rgulQ, out ciulQ);        // Q = U
                uint remainder;
                MpDiv1 (rgulQ, ref ciulQ, rgulD[0], out remainder);     // Q = Q / D, R = Q % D
                rgulR[0] = remainder;
                ciulR = 1;
            }

            // Worst case. Knuth, "The Art of Computer Programming", 3rd edition, vol.II, Alg.D, pg 272
            else {
                ciulQ = ciulR = 0;

                uint D1, ulDHigh, ulDSecond;
                int iulRindex;

                if (rgulU != rgulR)
                    MpMove (rgulU, ciulU, rgulR, out ciulR);        // R = U

                ciulQ = ciulU - ciulD + 1;
                ulDHigh = rgulD[ciulD-1];

                // D1.    Normalize so high digit of D >= BASE/2 - that guarantee
                //        that QH will not be too far from the correct digit later in D3
                rgulR[ciulU] = 0;
                iulRindex = ciulU;
                D1 = (uint)(x_ulInt32Base / ((ulong)ulDHigh + 1));
                if (D1 > 1) {
                    MpMul1 (rgulD, ref ciulD, D1);
                    ulDHigh = rgulD[ciulD-1];
                    MpMul1 (rgulR, ref ciulR, D1);
                }
                ulDSecond = rgulD[ciulD-2];
                // D2 already done - iulRindex initialized before normalization of R.
                // D3-D7. Loop on iulRindex - obtaining digits one-by-one, as "in paper"
                do {
                    uint QH, RH;
                    int iulDindex, iulRwork;
                    ulong dwlAccum, dwlMulAccum;

                    // D3. Calculate Q hat - estimation of the next digit
                    dwlAccum = DWL (rgulR[iulRindex-1], rgulR[iulRindex]);
                    if (ulDHigh == rgulR[iulRindex])
                        QH = (uint)(x_ulInt32Base - 1);
                    else
                        QH = (uint)(dwlAccum / ulDHigh);
                    ulong ulTemp = (ulong)QH;
                    RH = (uint)(dwlAccum - ulTemp * (ulong)ulDHigh);

                    while (ulDSecond * ulTemp > DWL (rgulR[iulRindex-2], RH)) {
                        QH --;
                        if (RH >= (uint) - ((int) ulDHigh))
                            break;
                        RH += ulDHigh;
                        ulTemp = (ulong)QH;
                    }

                    // D4. Multiply and subtract: (some digits of) R -= D * QH
                    for (dwlAccum = x_ulInt32Base, dwlMulAccum = 0, iulDindex = 0, iulRwork = iulRindex - ciulD;
                        iulDindex < ciulD; iulDindex ++, iulRwork ++) {
                        ulong ulTemp2 = (ulong)rgulD[iulDindex];
                        dwlMulAccum += (ulong)QH * ulTemp2;
                        dwlAccum += (ulong)rgulR[iulRwork] - LO (dwlMulAccum);
                        dwlMulAccum = (ulong)(HI (dwlMulAccum));
                        rgulR[iulRwork] = LO (dwlAccum);
                        dwlAccum = HI (dwlAccum) + x_ulInt32Base - 1;
                    }
                    dwlAccum += (ulong)rgulR[iulRwork] - dwlMulAccum;
                    rgulR[iulRwork] = LO (dwlAccum);
                    rgulQ[iulRindex-ciulD] = QH;

                    // D5. Test remainder. Carry indicates result<0, therefore QH 1 too large
                    if (HI (dwlAccum) == 0) {
                        // D6. Add back - probabilty is 2**(-31). R += D. Q[digit] -= 1
                        uint ulCarry;

                        rgulQ[iulRindex-ciulD] = QH - 1;
                        for (ulCarry = 0, iulDindex = 0, iulRwork = iulRindex - ciulD;
                            iulDindex < ciulD; iulDindex ++, iulRwork ++) {
                            dwlAccum = (ulong)rgulD[iulDindex] + (ulong)rgulR[iulRwork] + (ulong)ulCarry;
                            ulCarry = HI (dwlAccum);
                            rgulR[iulRwork] = LO (dwlAccum);
                        }
                        rgulR[iulRwork] += ulCarry;
                    }
                    // D7. Loop on iulRindex
                    iulRindex --;
                }
                while (iulRindex >= ciulD);
                // Normalize results
                MpNormalize (rgulQ, ref ciulQ);
                ciulR = ciulD;
                MpNormalize (rgulR, ref ciulR);
                // D8. Unnormalize: Divide D and R to get result
                if (D1 > 1) {
                    uint ret;
                    MpDiv1 (rgulD, ref ciulD, D1, out ret);
                    MpDiv1 (rgulR, ref ciulR, D1, out ret);
                }
            }
        }

        //    CmpCompareNm()
        //
        //    Compare the value of two numerics
        //
        //    Complexity: O(pn) p: precision  n: length
        //
        //  Parameters:
        //        this    - IN Operand1
        //        snumOp    - IN operand2
        //
        //    Returns:
        //        EComparison.LT    - this < snumOp
        //        EComparison.EQ        - this = snumOp
        //        EComparison.GT    - this > snumOp
        //
        private EComparison CompareNm
        (
        SqlDecimal snumOp
        ) {
            AssertValid();
            snumOp.AssertValid();

            //Signs of the two numeric operands
            int     Sign1;
            int     Sign2;

            int     iFinalResult;   //Final result of comparision: positive = greater
                                    //than, 0 = equal, negative = less than

            //Initialize the sign values to be 1(positive) or -1(negative)
            Sign1 = IsPositive ? 1 : -1;
            Sign2 = snumOp.IsPositive ? 1 : -1;

            if (Sign1 != Sign2) //If different sign, the positive one is greater
                return Sign1 == 1 ? EComparison.GT : EComparison.LT;

            else { //same sign, have to compare absolute values
                //Temporary memory to hold the operand since it is const
                //but its scale may get adjusted during comparison
                int         ScaleDiff;
                SqlDecimal  snumArg1 = this;
                SqlDecimal  snumArg2 = snumOp;

                //First make the two operands the same scale if necessary
                ScaleDiff = ((int) m_bScale) - ((int) snumOp.m_bScale);

                if (ScaleDiff < 0) {
                    //If increasing the scale of operand1 caused an overflow,
                    //then its absolute value is greater than that of operand2.
                    try {
                        snumArg1.AdjustScale(-ScaleDiff, true);
                    }
                    catch (OverflowException) {
                        return(Sign1 > 0) ? EComparison.GT : EComparison.LT;
                    }
                }
                else if (ScaleDiff > 0) {
                    //If increasing the scale of operand2 caused an overflow, then
                    //operand1's absolute value is less than that of operand2.
                    try {
                        snumArg2.AdjustScale(ScaleDiff, true);
                    }
                    catch (OverflowException) {
                        return(Sign1 > 0) ? EComparison.LT : EComparison.GT;
                    }
                }

                //Compare the absolute value of the two numerics
                //Note: We are sure that scale of arguments is the same,
                //      so LAbsCmp() will not modify its argument.
                int lResult = snumArg1.LAbsCmp(snumArg2);
                if (0 == lResult)
                    return EComparison.EQ;

                //if both positive, result same as result from LAbsCmp;
                //if both negative, result reverse of result from LAbsCmp
                iFinalResult = Sign1 * lResult;

                if (iFinalResult < 0)
                    return EComparison.LT;
                else
                    return EComparison.GT;
            }
        }

        private static void CheckValidPrecScale(byte bPrec, byte bScale) {
            if (bPrec < 1 || bPrec > MaxPrecision || bScale < 0 || bScale > MaxScale || bScale > bPrec)
                throw new SqlTypeException(SQLResource.InvalidPrecScaleMessage);
        }

        private static void CheckValidPrecScale(int iPrec, int iScale) {
            if (iPrec < 1 || iPrec > MaxPrecision || iScale < 0 || iScale > MaxScale || iScale > iPrec)
                throw new SqlTypeException(SQLResource.InvalidPrecScaleMessage);
        }

        // Overloading comparison operators
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlBoolean operator==(SqlDecimal x, SqlDecimal y) {
            return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.CompareNm(y) == EComparison.EQ);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlBoolean operator!=(SqlDecimal x, SqlDecimal y) {
            return ! (x == y);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlBoolean operator<(SqlDecimal x, SqlDecimal y) {
            return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.CompareNm(y) == EComparison.LT);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlBoolean operator>(SqlDecimal x, SqlDecimal y) {
            return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.CompareNm(y) == EComparison.GT);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlBoolean operator<=(SqlDecimal x, SqlDecimal y) {
            if (x.IsNull || y.IsNull)
                return SqlBoolean.Null;
            else {
                EComparison result = x.CompareNm(y);
                return new SqlBoolean(result == EComparison.LT || result == EComparison.EQ);
            }
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlBoolean operator>=(SqlDecimal x, SqlDecimal y) {
            if (x.IsNull || y.IsNull)
                return SqlBoolean.Null;
            else {
                EComparison result = x.CompareNm(y);
                return new SqlBoolean(result == EComparison.GT || result == EComparison.EQ);
            }
        }


        //--------------------------------------------------
        // Alternative methods for overloaded operators
        //--------------------------------------------------

        // Alternative method for operator +
        public static SqlDecimal Add(SqlDecimal x, SqlDecimal y) {
            return x + y;
        }
        // Alternative method for operator -
        public static SqlDecimal Subtract(SqlDecimal x, SqlDecimal y) {
            return x - y;
        }

        // Alternative method for operator *
        public static SqlDecimal Multiply(SqlDecimal x, SqlDecimal y) {
            return x * y;
        }

        // Alternative method for operator /
        public static SqlDecimal Divide(SqlDecimal x, SqlDecimal y) {
            return x / y;
        }

        // Alternative method for operator ==
        public static SqlBoolean Equals(SqlDecimal x, SqlDecimal y) {
            return (x == y);
        }

        // Alternative method for operator !=
        public static SqlBoolean NotEquals(SqlDecimal x, SqlDecimal y) {
            return (x != y);
        }

        // Alternative method for operator <
        public static SqlBoolean LessThan(SqlDecimal x, SqlDecimal y) {
            return (x < y);
        }

        // Alternative method for operator >
        public static SqlBoolean GreaterThan(SqlDecimal x, SqlDecimal y) {
            return (x > y);
        }

        // Alternative method for operator <=
        public static SqlBoolean LessThanOrEqual(SqlDecimal x, SqlDecimal y) {
            return (x <= y);
        }

        // Alternative method for operator >=
        public static SqlBoolean GreaterThanOrEqual(SqlDecimal x, SqlDecimal y) {
            return (x >= y);
        }

        // Alternative method for conversions.

        public SqlBoolean ToSqlBoolean() {
            return (SqlBoolean)this;
        }

        public SqlByte ToSqlByte() {
            return (SqlByte)this;
        }

        public SqlDouble ToSqlDouble() {
            return (SqlDouble)this;
        }

        public SqlInt16 ToSqlInt16() {
            return (SqlInt16)this;
        }

        public SqlInt32 ToSqlInt32() {
            return (SqlInt32)this;
        }

        public SqlInt64 ToSqlInt64() {
            return (SqlInt64)this;
        }

        public SqlMoney ToSqlMoney() {
            return (SqlMoney)this;
        }

        public SqlSingle ToSqlSingle() {
            return (SqlSingle)this;
        }

        public SqlString ToSqlString() {
            return (SqlString)this;
        }

        private static char ChFromDigit(uint uiDigit) {
            SQLDebug.Check(uiDigit < 10);
            return(char)(uiDigit + '0');
        }

        // Store data back from rguiData[] to m_data*
        private void StoreFromWorkingArray(uint[] rguiData) {
            SQLDebug.Check(rguiData.Length == 4);
            m_data1 = rguiData[0];
            m_data2 = rguiData[1];
            m_data3 = rguiData[2];
            m_data4 = rguiData[3];
        }

        private void SetToZero() {
            SQLDebug.Check(m_bPrec >= 1);
            m_bLen = 1;
            m_data1 =
            m_data2 =
            m_data3 =
            m_data4 = 0;
            m_bStatus = (byte) (x_bNotNull | x_bPositive);
            AssertValid();
        }

        // Truncate to integer
        private void MakeInteger(out bool fFraction) {
            uint ulRem;
            int iAdjust = m_bScale;

            fFraction = false;

            while (iAdjust > 0) {
                if (iAdjust >= 9) {
                    ulRem = DivByULong (x_rgulShiftBase[8]);
                    iAdjust -= 9;
                }
                else {
                    ulRem = DivByULong (x_rgulShiftBase[iAdjust-1]);
                    iAdjust = 0;
                }

                // Check for remainder and set fFraction flag.
                if (ulRem != 0)
                    fFraction = true;
            }

            m_bScale = 0;
            AssertValid();
        }

        // Builtin functions

        // Abs - absolute value
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal Abs(SqlDecimal n) {
            n.AssertValid();

            if (n.IsNull)
                return SqlDecimal.Null;

            n.SetPositive();
            n.AssertValid();
            return n;
        }

        // Ceiling - next smallest integer greater than or equal to the numeric
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal Ceiling(SqlDecimal n) {
            n.AssertValid();

            if (n.IsNull)
                return SqlDecimal.Null;

            if (n.m_bScale == 0)
                return n;

            bool fFraction;    //Fractional flag

            n.MakeInteger(out fFraction);

            //When the numeric has fraction and is positive, adjust by adding 1.
            //Otherwise return the integral part.
            if (fFraction && n.IsPositive) {
                n.AddULong(1);
            }

            if (n.FZero())//if result is zero, sign should be positive
                n.SetPositive();
            n.AssertValid();
            return n;
        }

        // Floor - next largest integer smaller or equal to the numeric
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal Floor(SqlDecimal n) {
            n.AssertValid();

            if (n.IsNull)
                return SqlDecimal.Null;

            if (n.m_bScale == 0)
                return n;

            bool fFraction;    //Fractional flag

            n.MakeInteger(out fFraction);

            //When the numeric has fraction and is negative, subtract 1 by calling AddULong(1)
            //Otherwise return the integral part.
            if (fFraction && !n.IsPositive) {
                n.AddULong(1);
            }

            if (n.FZero())//if result is zero, sign should be positive
                n.SetPositive();
            n.AssertValid();
            return n;
        }

        // Sign -   1 if positive, -1 if negative
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlInt32 Sign(SqlDecimal n) {
            n.AssertValid();

            if (n.IsNull)
                return SqlInt32.Null;

            if (n == new SqlDecimal(0))
                return SqlInt32.Zero;
            else
                return n.IsNull ? SqlInt32.Null :
                    (n.IsPositive ? new SqlInt32(1) : new SqlInt32(-1));
        }

        private static SqlDecimal Round(SqlDecimal n, int lPosition, bool fTruncate) {
            if (n.IsNull)
                return SqlDecimal.Null;

            if (lPosition >= 0) {
                //If round to the right of decimal number
                lPosition = Math.Min(NUMERIC_MAX_PRECISION, lPosition);
                if (lPosition >= n.m_bScale)
                    return n;   //No need to round
            }
            else {
                //If round to the left of the decimal point
                lPosition = Math.Max(-NUMERIC_MAX_PRECISION, lPosition);

                //Return +0.00 if truncation of integer part
                if (lPosition < n.m_bScale - n.m_bPrec) {
                    n.SetToZero();
                    return n;
                }
            }

            uint ulRem = 0;                                         // Remainder: the highest significant digit to be truncated
            int lAdjust = Math.Abs(lPosition - (int)n.m_bScale);    // Precision adjustment
            uint ulLastDivBase = 1;                                 //

            //Compute the integral part of the numeric
            while (lAdjust > 0) {
                if (lAdjust >= 9) {
                    ulRem = n.DivByULong (x_rgulShiftBase[8]);
                    ulLastDivBase = x_rgulShiftBase[8];
                    lAdjust -= 9;
                }
                else {
                    ulRem = n.DivByULong (x_rgulShiftBase[lAdjust-1]);
                    ulLastDivBase = x_rgulShiftBase[lAdjust-1];
                    lAdjust = 0;
                }
            }

            // The rounding only depends on the first digit after the rounding position
            if (ulLastDivBase > 1) {
                ulRem /= (ulLastDivBase / 10);
            }

            //If result is zero, return
            if (n.FZero() && (fTruncate || ulRem < 5))
                {
                n.SetPositive();
                n.AssertValid();
                return n;
                }

            // Adjust by adding 1 if remainder is larger than 5
            if (ulRem >= 5 && !fTruncate)
                n.AddULong(1);

            // Convert back to original scale
            lAdjust = Math.Abs(lPosition - n.m_bScale);

            while (lAdjust-- > 0) {
                n.MultByULong(x_ulBase10);
            }

            n.AssertValid();
            return n;
        }

        // Round - Round the numeric to a specific digit
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal Round(SqlDecimal n, int position) {
            n.AssertValid();
            return Round(n, position, false);
        }

        // Truncate - Truncate the numeric to a specific digit
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal Truncate(SqlDecimal n, int position) {
            n.AssertValid();
            return Round(n, position, true);
        }

        // Power - Compute the power of a numeric
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static SqlDecimal Power(SqlDecimal n, double exp) {
            n.AssertValid();

            if (n.IsNull)
                return SqlDecimal.Null;

            byte prec = n.Precision;
            int scale = n.Scale;
            double dBaseNum = n.ToDouble();

            n = new SqlDecimal(Math.Pow(dBaseNum, exp));
            n.AdjustScale(scale - (int)n.Scale, true);

            n.m_bPrec = MaxPrecision;

            return n;
        }


        // IComparable
        // Compares this object to another object, returning an integer that
        // indicates the relationship.
        // Returns a value less than zero if this < object, zero if this = object,
        // or a value greater than zero if this > object.
        // null is considered to be less than any instance.
        // If object is not of same type, this method throws an ArgumentException.
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public int CompareTo(Object value) {
            if (value is SqlDecimal) {
                SqlDecimal i = (SqlDecimal)value;

                return CompareTo(i);
            }
            throw ADP.WrongType(value.GetType(), typeof(SqlDecimal));
        }

        public int CompareTo(SqlDecimal value) {
            // If both Null, consider them equal.
            // Otherwise, Null is less than anything.
            if (IsNull)
                return value.IsNull ? 0  : -1;
            else if (value.IsNull)
                return 1;

            if (this < value) return -1;
            if (this > value) return 1;
            return 0;
        }

        // Compares this instance with a specified object
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public override bool Equals(Object value) {
            if (!(value is SqlDecimal)) {
                return false;
            }

            SqlDecimal i = (SqlDecimal)value;

            if (i.IsNull || IsNull)
                return (i.IsNull && IsNull);
            else
                return (this == i).Value;
        }

        // For hashing purpose
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public override int GetHashCode() {
            if (IsNull)
                return 0;

            SqlDecimal ssnumTemp;
            int        lActualPrec;

            // First, "normalize" numeric, so that values with different
            // scale/precision will have the same representation.
            ssnumTemp = this;
            lActualPrec = ssnumTemp.CalculatePrecision();
            ssnumTemp.AdjustScale(NUMERIC_MAX_PRECISION - lActualPrec, true);

            // Now evaluate the hash
            int cDwords = ssnumTemp.m_bLen;
            int ulValue = 0;
            int ulHi;

            // Size of CRC window (hashing bytes, ssstr, sswstr, numeric)
            const int x_cbCrcWindow = 4;
            // const int iShiftVal = (sizeof ulValue) * (8*sizeof(char)) - x_cbCrcWindow;
            const int iShiftVal = 4 * 8 - x_cbCrcWindow;

            int[] rgiData = ssnumTemp.Data;

            for (int i = 0; i < cDwords; i++)
                {
                ulHi = (ulValue >> iShiftVal) & 0xff;
                ulValue <<= x_cbCrcWindow;
                ulValue = ulValue ^ rgiData[i] ^ ulHi;
                }
            return ulValue;
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        XmlSchema IXmlSerializable.GetSchema() { return null; }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        void IXmlSerializable.ReadXml(XmlReader reader) {
            string isNull = reader.GetAttribute("nil", XmlSchema.InstanceNamespace);
            if (isNull != null && XmlConvert.ToBoolean(isNull)) {
                // VSTFDevDiv# 479603 - SqlTypes read null value infinitely and never read the next value. Fix - Read the next value.
                reader.ReadElementString();
                m_bStatus = (byte) (x_bReverseNullMask & m_bStatus);
            }
            else {
                SqlDecimal dec = Parse(reader.ReadElementString());
                this.m_bStatus = dec.m_bStatus;
                this.m_bLen = dec.m_bLen;
                this.m_bPrec = dec.m_bPrec;
                this.m_bScale = dec.m_bScale;
                this.m_data1 = dec.m_data1;
                this.m_data2 = dec.m_data2;
                this.m_data3 = dec.m_data3;
                this.m_data4 = dec.m_data4;
            }
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        void IXmlSerializable.WriteXml(XmlWriter writer) {
            if (IsNull) {
                writer.WriteAttributeString("xsi", "nil", XmlSchema.InstanceNamespace, "true");
            }
            else {
                writer.WriteString(ToString());
            }
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet) {
            return new XmlQualifiedName("decimal", XmlSchema.Namespace);
        }

        // These values are defined at last, because they need to call ctors, which use other
        // constant values. Those constants must be defined before callint ctor.

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly SqlDecimal Null = new SqlDecimal(true);

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly SqlDecimal MinValue    = SqlDecimal.Parse("-99999999999999999999999999999999999999");
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly SqlDecimal MaxValue    = SqlDecimal.Parse("99999999999999999999999999999999999999");

    } // SqlDecimal

} // namespace System.Data.SqlTypes
