Update VS project files
[mono.git] / mcs / class / System.Data / System.Data.SqlTypes / SqlDecimal.cs
index f50b98f5717a502d809018879ff0d5186f7d3107..82669cd5c76725d9bd6ecae74b3f90656c9eb96f 100644 (file)
@@ -8,14 +8,49 @@
 // (C) Copyright 2002 Tim Coleman
 //
 
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if !TARGET_JVM
 using Mono.Data.Tds.Protocol;
+#endif
 using System;
+using System.Xml;
 using System.Text;
+using System.Xml.Schema;
 using System.Globalization;
+using System.Xml.Serialization;
 
 namespace System.Data.SqlTypes
 {
+#if NET_2_0
+       [SerializableAttribute]
+       [XmlSchemaProvider ("GetXsdType")]
+#endif
        public struct SqlDecimal : INullable, IComparable
+#if NET_2_0
+                               , IXmlSerializable
+#endif
        {
                #region Fields
 
@@ -33,11 +68,9 @@ namespace System.Data.SqlTypes
                const ulong LIT_GUINT64_HIGHBIT = 0x8000000000000000;
                const ulong LIT_GUINT32_HIGHBIT = 0x80000000;
                const byte DECIMAL_MAX_INTFACTORS = 9;
-               static uint [] constantsDecadeInt32Factors = new uint [10]
-                       {
-                               1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 
-                               10000000u, 100000000u, 1000000000u
-                       };
+               static uint [] constantsDecadeInt32Factors = new uint [10] {
+                       1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 
+                       10000000u, 100000000u, 1000000000u};
 
                public static readonly byte MaxPrecision = 38; 
                public static readonly byte MaxScale = 38;
@@ -53,9 +86,9 @@ namespace System.Data.SqlTypes
                // This should be -99999999999999999999999999999999999999
                public static readonly SqlDecimal MinValue = new SqlDecimal (MaxPrecision, 
                                                                             (byte)0, false,
-                                                                            -1,                                                
+                                                                            -1,
                                                                             160047679,
-                                                                            1518781562, 
+                                                                            1518781562,
                                                                             1262177448);
 
                public static readonly SqlDecimal Null;
@@ -64,7 +97,7 @@ namespace System.Data.SqlTypes
 
                #region Constructors
 
-               public SqlDecimal (decimal value) 
+               public SqlDecimal (decimal value)
                {
                        int[] binData = Decimal.GetBits (value);
 
@@ -81,16 +114,12 @@ namespace System.Data.SqlTypes
                        this.value[2] = binData[2];
                        this.value[3] = 0;
 
-                       if (value >= 0)
-                               positive = true;
-                       else 
-                               positive = false;
-
+                       positive = (value >= 0);
                        notNull = true;
                        precision = GetPrecision (value);
                }
-                               
-               public SqlDecimal (double value) : this ((decimal)value) 
+
+               public SqlDecimal (double dVal) : this ((decimal) dVal)
                {
                        SqlDecimal n = this;
                        int digits = 17 - precision;
@@ -104,12 +133,20 @@ namespace System.Data.SqlTypes
                        this.scale = n.scale;
                        this.value = n.value;
                }
-               public SqlDecimal (int value) : this ((decimal)value) { }
-               public SqlDecimal (long value) : this ((decimal)value) { }
 
-               public SqlDecimal (byte bPrecision, byte bScale, bool fPositive, int[] bits) : this (bPrecision, bScale, fPositive, bits[0], bits[1], bits[2], bits[3]) { }
+               public SqlDecimal (int value) : this ((decimal) value)
+               {
+               }
 
-               public SqlDecimal (byte bPrecision, byte bScale, bool fPositive, int data1, int data2, int data3, int data4) 
+               public SqlDecimal (long value) : this ((decimal) value)
+               {
+               }
+
+               public SqlDecimal (byte bPrecision, byte bScale, bool fPositive, int[] bits) : this (bPrecision, bScale, fPositive, bits[0], bits[1], bits[2], bits[3])
+               {
+               }
+
+               public SqlDecimal (byte bPrecision, byte bScale, bool fPositive, int data1, int data2, int data3, int data4)
                {
                        this.precision = bPrecision;
                        this.scale = bScale;
@@ -120,13 +157,16 @@ namespace System.Data.SqlTypes
                        this.value[2] = data3;
                        this.value[3] = data4;
                        notNull = true;
-                       
+
                        if (precision < scale)
-                               throw new ArgumentException(Locale.GetText ("Invalid scale"));
+                               throw new SqlTypeException (Locale.GetText ("Invalid presicion/scale combination."));
 
-                       if (this.ToDouble () > (Math.Pow (10, 38) - 1)  || 
+                       if (precision > 38)
+                               throw new SqlTypeException (Locale.GetText ("Invalid precision/scale combination."));
+
+                       if (this.ToDouble () > (Math.Pow (10, 38) - 1) ||
                            this.ToDouble () < -(Math.Pow (10, 38)))
-                               throw new SqlTypeException ("Can't convert to SqlDecimal, Out of range ");
+                               throw new OverflowException ("Can't convert to SqlDecimal, Out of range ");
                }
 
                #endregion
@@ -134,8 +174,7 @@ namespace System.Data.SqlTypes
                #region Properties
 
                public byte[] BinData {
-                       get { 
-
+                       get {
                                byte [] b = new byte [value.Length * 4];
                                
                                int j = 0;
@@ -151,8 +190,8 @@ namespace System.Data.SqlTypes
                        }
                }
 
-               public int[] Data { 
-                       get { 
+               public int[] Data {
+                       get {
                                if (this.IsNull)
                                        throw new SqlNullValueException ();
                                // Data should always return clone, not to be modified
@@ -160,28 +199,29 @@ namespace System.Data.SqlTypes
                                ret [0] = value [0];
                                ret [1] = value [1];
                                ret [2] = value [2];
+                               ret [3] = value [3];
                                return ret;
                        }
                }
 
-               public bool IsNull { 
+               public bool IsNull {
                        get { return !notNull; }
                }
 
-               public bool IsPositive { 
+               public bool IsPositive {
                        get { return positive; }
                }
 
-               public byte Precision { 
+               public byte Precision {
                        get { return precision; }
                }
 
-               public byte Scale { 
+               public byte Scale {
                        get { return scale; }
                }
 
-               public decimal Value { 
-                       get { 
+               public decimal Value {
+                       get {
                                if (this.IsNull) 
                                        throw new SqlNullValueException ();
 
@@ -198,9 +238,9 @@ namespace System.Data.SqlTypes
 
                public static SqlDecimal Abs (SqlDecimal n)
                {
-                               return new SqlDecimal (n.Precision, n.Scale, true, 
-                                                      n.BinData [0], n.BinData [1], 
-                                                      n.BinData [2], n.BinData [3]);
+                       if (!n.notNull)
+                               return n;
+                       return new SqlDecimal (n.Precision, n.Scale, true, n.Data);
                }
 
                public static SqlDecimal Add (SqlDecimal x, SqlDecimal y)
@@ -210,35 +250,40 @@ namespace System.Data.SqlTypes
 
                public static SqlDecimal AdjustScale (SqlDecimal n, int digits, bool fRound)
                {
-                       byte prec = n.Precision;
+                       byte prec = n.Precision;
                        if (n.IsNull)
                                throw new SqlNullValueException ();
 
-                       int [] data;
-                       byte newScale;
-                       if (digits > 0) {
-                               prec = (byte)(prec + digits);
-                               decimal d = n.Value;
-                               if (digits > 0)
-                                       for (int i = 0; i < digits; i++)
-                                               d *= 10;
-                               data = Decimal.GetBits (d);
-                               data [3] = 0;
-                               newScale = (byte) (n.scale + digits);
+                       byte scale;
+                       if (digits == 0)
+                               return n;
+                       else if (digits > 0) {
+                               prec = (byte)(prec + digits);
+                               scale = (byte) (n.scale + digits);
+                               // use Math.Pow once the Ctr (double) is fixed to  handle
+                               // values greater than Decimal.MaxValue
+                               // the current code creates too many sqldecimal objects 
+                               //n = n * (new SqlDecimal ((double)Math.Pow (10, digits)));
+                               for (int i = 0; i < digits; i++)
+                                       n *= 10;
                        } else {
+                               if (n.Scale < Math.Abs (digits))
+                                       throw new SqlTruncateException ();
+
                                if (fRound)
                                        n = Round (n, digits + n.scale);
                                else
-                                       n = Truncate (n, digits + n.scale);
-                               data = n.Data;
-                               newScale = n.scale;
+                                       n = Round (Truncate (n, digits + n.scale), digits + n.scale);
+                               scale = n.scale;
                        }
 
-                       return new SqlDecimal (prec, newScale, n.positive, data);
+                       return new SqlDecimal (prec, scale, n.positive, n.Data);
                }
 
                public static SqlDecimal Ceiling (SqlDecimal n)
                {
+                       if (!n.notNull)
+                               return n;
                        return AdjustScale (n, -(n.Scale), true);
                }
 
@@ -246,17 +291,34 @@ namespace System.Data.SqlTypes
                {
                        if (value == null)
                                return 1;
-                       else if (!(value is SqlDecimal))
+                       if (!(value is SqlDecimal))
                                throw new ArgumentException (Locale.GetText ("Value is not a System.Data.SqlTypes.SqlDecimal"));
-                       else if (((SqlDecimal)value).IsNull)
+
+                       return CompareTo ((SqlDecimal) value);
+               }
+
+#if NET_2_0
+               public
+#endif
+               int CompareTo (SqlDecimal value)
+               {
+                       if (value.IsNull)
                                return 1;
                        else
-                               return this.Value.CompareTo (((SqlDecimal)value).Value);
+                               return this.Value.CompareTo (value.Value);
                }
 
                public static SqlDecimal ConvertToPrecScale (SqlDecimal n, int precision, int scale)
                {
-                       return new SqlDecimal ((byte)precision, (byte)scale, n.IsPositive, n.Data);
+                       int prec = n.Precision;
+                       int sc = n.Scale;
+                       n = AdjustScale (n, scale-n.scale, true);
+                       if ((n.Scale >= sc) && (precision < n.Precision))
+                               throw new SqlTruncateException ();
+                       else{
+                               prec = precision;
+                               return new SqlDecimal ((byte)prec, n.scale, n.IsPositive, n.Data);
+                       }
                }
 
                public static SqlDecimal Divide (SqlDecimal x, SqlDecimal y)
@@ -268,8 +330,8 @@ namespace System.Data.SqlTypes
                {
                        if (!(value is SqlDecimal))
                                return false;
-                       else if (this.IsNull && ((SqlDecimal)value).IsNull)
-                               return true;                    
+                       else if (this.IsNull)
+                               return ((SqlDecimal)value).IsNull;
                        else if (((SqlDecimal)value).IsNull)
                                return false;
                        else
@@ -286,6 +348,7 @@ namespace System.Data.SqlTypes
                        return AdjustScale (n, -(n.Scale), false);
                }
 
+#if !TARGET_JVM
                internal static SqlDecimal FromTdsBigDecimal (TdsBigDecimal x)
                {
                        if (x == null)
@@ -293,6 +356,7 @@ namespace System.Data.SqlTypes
                        else
                                return new SqlDecimal (x.Precision, x.Scale, !x.IsNegative, x.Data);
                }
+#endif
 
                public override int GetHashCode ()
                {
@@ -301,8 +365,8 @@ namespace System.Data.SqlTypes
                        result = 91 * result + this.Data[1];
                        result = 91 * result + this.Data[2];
                        result = 91 * result + this.Data[3];
-                       result = 91 * result + (int)this.Scale;
-                       result = 91 * result + (int)this.Precision;
+                       result = 91 * result + (int) this.Scale;
+                       result = 91 * result + (int) this.Precision;
 
                        return result;
                }
@@ -341,7 +405,7 @@ namespace System.Data.SqlTypes
                {
                        if (s == null)
                                throw new ArgumentNullException (Locale.GetText ("string s"));
-                       else 
+                       else
                                return new SqlDecimal (Decimal.Parse (s));
                }
 
@@ -365,14 +429,9 @@ namespace System.Data.SqlTypes
 
                public static SqlInt32 Sign (SqlDecimal n)
                {
-                       SqlInt32 result = 0;
-
-                       if (n >= new SqlDecimal (0))
-                               result = 1;
-                       else
-                               result = -1;
-
-                       return result;
+                       if (n.IsNull)
+                               return SqlInt32.Null;
+                       return (SqlInt32) (n.IsPositive ? 1 : -1);
                }
 
                public static SqlDecimal Subtract (SqlDecimal x, SqlDecimal y)
@@ -408,47 +467,47 @@ namespace System.Data.SqlTypes
 
                public SqlBoolean ToSqlBoolean ()
                {
-                       return ((SqlBoolean)this);
+                       return ((SqlBoolean) this);
                }
                
                public SqlByte ToSqlByte ()
                {
-                       return ((SqlByte)this);
+                       return ((SqlByte) this);
                }
 
                public SqlDouble ToSqlDouble ()
                {
-                       return ((SqlDouble)this);
+                       return ((SqlDouble) this);
                }
 
                public SqlInt16 ToSqlInt16 ()
                {
-                       return ((SqlInt16)this);
+                       return ((SqlInt16) this);
                }
 
                public SqlInt32 ToSqlInt32 ()
                {
-                       return ((SqlInt32)this);
+                       return ((SqlInt32) this);
                }
 
                public SqlInt64 ToSqlInt64 ()
                {
-                       return ((SqlInt64)this);
+                       return ((SqlInt64) this);
                }
 
                public SqlMoney ToSqlMoney ()
                {
-                       return ((SqlMoney)this);
+                       return ((SqlMoney) this);
                }
 
                public SqlSingle ToSqlSingle ()
                {
-                       return ((SqlSingle)this);
+                       return ((SqlSingle) this);
                }
 
                public SqlString ToSqlString ()
                {
-                       return ((SqlString)this);
+                       return ((SqlString) this);
                }
 
                public override string ToString ()
@@ -463,22 +522,20 @@ namespace System.Data.SqlTypes
                        hi += (ulong)((ulong)this.Data [3] << 32);
 
                        uint rest = 0;
-                       String result = "";
                        StringBuilder Result = new StringBuilder ();
                        for (int i = 0; lo != 0 || hi != 0; i++) {
-                       
                                Div128By32 (ref hi, ref lo, 10, ref rest);
                                Result.Insert (0, rest.ToString ());
                        }
 
-                       while (Result.Length < this.Precision)
-                               Result.Append ("0");
-
                        while (Result.Length > this.Precision)
-                              Result.Remove (Result.Length - 1, 1);
+                               Result.Remove (Result.Length - 1, 1);
  
                        if (this.Scale > 0)
-                               Result.Insert (Result.Length - this.Scale, ".");
+                               Result.Insert (Result.Length - this.Scale, ".");
+
+                       if (!positive)
+                               Result.Insert (0, '-');
 
                        return Result.ToString ();
                }
@@ -495,7 +552,7 @@ namespace System.Data.SqlTypes
                {
                        ulong a = 0;
                        ulong b = 0;
-                       ulong c = 0;    
+                       ulong c = 0;
                        
                        a = (uint)(hi >> 32);
                        b = a / divider;
@@ -525,12 +582,12 @@ namespace System.Data.SqlTypes
                [MonoTODO("Find out what is the right way to set scale and precision")]
                private static SqlDecimal DecimalDiv (SqlDecimal x, SqlDecimal y)
                {
-                       ulong lo = 0; 
-                       ulong hi = 0;                   
+                       ulong lo = 0;
+                       ulong hi = 0;
                        int sc = 0; // scale
                        int texp = 0;
-                       int rc = 0;
                        byte prec = 0; // precision
+                       bool positive = ! (x.positive ^ y.positive);
 
                        prec = x.Precision >= y.Precision ? x.Precision : y.Precision;
                        DecimalDivSub (ref x, ref y, ref lo, ref hi, ref texp);
@@ -543,11 +600,11 @@ namespace System.Data.SqlTypes
                        while (prec < sc) {
                                Div128By32(ref hi, ref lo, 10, ref r);
                                sc--;
-                       }                               
-                               
+                       }
+
                        if (r >= 5) 
                                lo++;
-                      
+
                        while ((((double)hi) * Math.Pow(2,64) + lo) - Math.Pow (10, prec) > 0)
                                prec++;
 
@@ -557,13 +614,13 @@ namespace System.Data.SqlTypes
                                if (r >= 5)
                                        lo++;
                        }
-                               
-                       int resultLo = (int)lo;
-                       int resultMi = (int)(lo >> 32);
-                       int resultMi2 = (int)(hi);
-                       int resultHi = (int)(hi >> 32);
 
-                       return new SqlDecimal (prec, (byte)sc, true, resultLo,
+                       int resultLo = (int) lo;
+                       int resultMi = (int) (lo >> 32);
+                       int resultMi2 = (int) (hi);
+                       int resultHi = (int) (hi >> 32);
+
+                       return new SqlDecimal (prec, (byte) sc, positive, resultLo,
                                                       resultMi, resultMi2,
                                                       resultHi);
                }
@@ -578,18 +635,14 @@ namespace System.Data.SqlTypes
                        uint overhang = 0;
                        int sc = 0;
                        int i = 0;
-                       int rc = 0;
                        int roundBit = 0;
 
                        sc = scale;
                        if (texp > 0) {
-
-                               // reduce exp 
+                               // reduce exp 
                                while (texp > 0 && sc <= maxScale) {
-
-                                               overhang = (uint)(chi >> 64);
+                                       overhang = (uint)(chi >> 64);
                                        while (texp > 0 && (((clo & 1) == 0) || overhang > 0)) {
-                                               
                                                if (--texp == 0)
                                                        roundBit = (int)(clo & 1);
                                                RShift128 (ref clo, ref chi);
@@ -613,10 +666,7 @@ namespace System.Data.SqlTypes
 
                                        // 10^i/2^i=5^i 
                                        factor = constantsDecadeInt32Factors [i] >> i; 
-                                       System.Console.WriteLine ("***");
                                        Mult128By32 (ref clo, ref chi, factor, 0);
-                                       System.Console.WriteLine ((((double)chi) * Math.Pow (2,64) + clo));
-
                                }
 
                                while (texp > 0) {
@@ -654,7 +704,6 @@ namespace System.Data.SqlTypes
                private static void Normalize128(ref ulong clo, ref ulong chi, ref int scale, int roundFlag, int roundBit)
                {
                        int sc = scale;
-                       int deltaScale;
                        
                        scale = sc;
                        if ((roundFlag != 0) && (roundBit != 0)) 
@@ -683,13 +732,13 @@ namespace System.Data.SqlTypes
                        int bshift = 0;
                        int extraBit = 0;
 
-                       xhi = (ulong)((ulong)x.Data [3] << 32) | (ulong)x.Data [2];
-                       xmi = (ulong)((ulong)x.Data [1] << 32) | (ulong)x.Data [0];
-                       xlo = (uint)0;                  
-                       ylo = (uint)y.Data [0];
-                       ymi = (uint)y.Data [1];
-                       ymi2 = (uint)y.Data [2];
-                       yhi = (uint)y.Data [3];
+                       xhi = (ulong) ((ulong) x.Data [3] << 32) | (ulong) x.Data [2];
+                       xmi = (ulong) ((ulong) x.Data [1] << 32) | (ulong) x.Data [0];
+                       xlo = (uint) 0;
+                       ylo = (uint) y.Data [0];
+                       ymi = (uint) y.Data [1];
+                       ymi2 = (uint) y.Data [2];
+                       yhi = (uint) y.Data [3];
                        
                        if (ylo == 0 && ymi == 0 && ymi2 == 0 && yhi == 0)
                                throw new DivideByZeroException ();
@@ -700,11 +749,11 @@ namespace System.Data.SqlTypes
                        }
                        
                        // enlarge dividend to get maximal precision
-                       for (ashift = 0; (xhi & LIT_GUINT64_HIGHBIT) == 0; ++ashift)
+                       for (ashift = 0; (xhi & LIT_GUINT64_HIGHBIT) == 0; ++ashift)
                                LShift128 (ref xmi, ref xhi);
                        
                        // ensure that divisor is at least 2^95 
-                       for (bshift = 0; (yhi & LIT_GUINT32_HIGHBIT) == 0; ++bshift) 
+                       for (bshift = 0; (yhi & LIT_GUINT32_HIGHBIT) == 0; ++bshift) 
                                LShift128 (ref ylo, ref ymi, ref ymi2, ref yhi);
                        
                        thi = ((ulong)yhi) << 32 | (ulong)ymi2;
@@ -727,18 +776,18 @@ namespace System.Data.SqlTypes
                                chi += LIT_GUINT64_HIGHBIT;
                                exp--;
                        }
-                        
+
                        // try loss free right shift
                        while (exp > 0 && (clo & 1) == 0) {
                                RShift128 (ref clo, ref chi);
                                exp--;
-                       }                       
+                       }
                }
 
                // From decimal.c
+               /*
                private static void RShift192(ref ulong lo, ref ulong mi, ref ulong hi)
                {
-                       
                        lo >>= 1;
                        if ((mi & 1) != 0)
                                lo |= LIT_GUINT64_HIGHBIT;
@@ -749,6 +798,7 @@ namespace System.Data.SqlTypes
 
                        hi >>= 1;
                }
+               */
 
                // From decimal.c
                private static void RShift128(ref ulong lo, ref ulong hi)
@@ -783,7 +833,7 @@ namespace System.Data.SqlTypes
                        
                        mi <<= 1;
                        if ((lo & LIT_GUINT32_HIGHBIT) != 0) 
-                              mi++;
+                               mi++;
 
                        lo <<= 1;
                }
@@ -792,7 +842,7 @@ namespace System.Data.SqlTypes
                private static void Div192By128To128 (ulong xlo, ulong xmi, ulong xhi,
                                               uint ylo, uint ymi, uint ymi2, 
                                               uint yhi, ref ulong clo, ref ulong chi)
-               {
+               {
                        ulong rlo, rmi, rhi; // remainders
                        uint h, c;
 
@@ -833,8 +883,8 @@ namespace System.Data.SqlTypes
                private static uint Div192By128To32WithRest(ref ulong xlo, ref ulong xmi,
                                                ref ulong xhi, uint ylo, 
                                                uint ymi, uint ymi2, uint yhi)
-               {
-                       ulong rlo, rmi, rhi; // remainder                      
+               {
+                       ulong rlo, rmi, rhi; // remainder
                        ulong tlo = 0;
                        ulong thi = 0;
                        uint c;
@@ -859,16 +909,16 @@ namespace System.Data.SqlTypes
                        xmi = rmi;
                        xhi = rhi;
 
-                       return c;                       
+                       return c;
                }
 
                // From decimal.c
+               /*
                private static void Mult192By32 (ref ulong clo, ref ulong cmi, ref ulong chi, ulong factor, int roundBit)
                {
                        ulong a = 0;
                        uint h0 = 0;
                        uint h1 = 0;
-                       uint h2 = 0;
 
                        a = ((ulong)(uint)clo) * factor;
 
@@ -883,7 +933,7 @@ namespace System.Data.SqlTypes
                        clo = ((ulong)h1) << 32 | h0;
 
                        a >>= 32;
-                       a += ((ulong)(uint)cmi) * factor;                      
+                       a += ((ulong)(uint)cmi) * factor;
                        h0 = (uint)a;
                        
                        a >>= 32;
@@ -892,7 +942,7 @@ namespace System.Data.SqlTypes
                        
                        cmi = ((ulong)h1) << 32 | h0;
                        a >>= 32;
-                       a += ((ulong)(uint)chi) * factor;                      
+                       a += ((ulong)(uint)chi) * factor;
                        h0 = (uint)a;
 
                        a >>= 32;
@@ -900,6 +950,7 @@ namespace System.Data.SqlTypes
                        h1 = (uint)a;
                        chi = ((ulong)h1) << 32 | h0;
                }
+               */
 
                // From decimal.c
                private static void Mult128By32 (ref ulong clo, ref ulong chi, uint factor, int roundBit)
@@ -907,7 +958,6 @@ namespace System.Data.SqlTypes
                        ulong a = 0;
                        uint h0 = 0;
                        uint h1 = 0;
-                       uint h2 = 0;
 
                        a = ((ulong)(uint)clo) * factor;
 
@@ -923,16 +973,15 @@ namespace System.Data.SqlTypes
                        clo = ((ulong)h1) << 32 | h0;
 
                        a >>= 32;
-                       a += ((ulong)(uint)chi) * factor;                      
+                       a += ((ulong)(uint)chi) * factor;
                        h0 = (uint)a;
                        
                        a >>= 32;
                        a += (chi >> 32) * factor;
                        h1 = (uint)a;
-                       
+
                        chi = ((ulong)h1) << 32 | h0;
                }
-                                                
 
                // From decimal.c
                private static void Mult128By32To128(uint xlo, uint xmi, uint xmi2, uint xhi,
@@ -979,7 +1028,7 @@ namespace System.Data.SqlTypes
                        xhi += yhi;
                        clo = xlo;
                        cmi = xmi;
-                       chi = xhi;                            
+                       chi = xhi;
                }
 
                // From decimal.c
@@ -1012,52 +1061,61 @@ namespace System.Data.SqlTypes
 
                public static SqlDecimal Truncate (SqlDecimal n, int position)
                {
-                       int prec = n.Precision;// + (position - n.Scale);
-                       int sc = position;
-                       return new SqlDecimal ((byte)prec, (byte)sc,
-                                              n.IsPositive, n.Data);
+                       int diff = n.scale - position;
+                       if (diff == 0)
+                               return n;
+                       int [] data = n.Data;
+                       decimal d = new decimal (data [0], data [1], data [2], !n.positive, 0);
+                       decimal x = 10;
+                       for (int i = 0; i < diff; i++, x *= 10)
+                               d = d - d % x;
+                       data = Decimal.GetBits (d);
+                       data [3] = 0;
+                       return new SqlDecimal (n.precision, n.scale, n.positive, data);
                }
 
                public static SqlDecimal operator + (SqlDecimal x, SqlDecimal y)
                {
-                       // if one of them is negative, perform subtraction
-                       if (x.IsPositive && !y.IsPositive) return x - y;
-                       if (y.IsPositive && !x.IsPositive) return y - x;
-               
-                       // adjust the scale to the smaller of the two beforehand
-                       if (x.Scale > y.Scale)
-                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
-                       else if (y.Scale > x.Scale)
-                               y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
+                       if (x.IsNull || y.IsNull)
+                               return SqlDecimal.Null;
+                        //if one of them is negative, perform subtraction
+                       if (x.IsPositive && !y.IsPositive){
+                               y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
+                               return (x - y);
+                       }
+                       if (!x.IsPositive && y.IsPositive){
+                               x = new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
+                               return (y - x);
+                       }
+                       if (!x.IsPositive && !y.IsPositive){
+                               x = new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
+                               y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
+                               x = (x + y);
+                               return new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
+                       }
+                       // adjust the scale to the larger of the two beforehand
+                       if (x.scale > y.scale)
+                               y = SqlDecimal.AdjustScale (y, x.scale - y.scale, false); 
+                       else if (y.scale > x.scale)
+                               x = SqlDecimal.AdjustScale (x, y.scale - x.scale, false); 
 
-                       // set the precision to the greater of the two
-                       byte resultPrecision;
-                       if (x.Precision > y.Precision)
-                               resultPrecision = x.Precision;
-                       else
-                               resultPrecision = y.Precision;
-                               
-                       int[] xData = x.Data;
-                       int[] yData = y.Data;
-                       int[] resultBits = new int[4];
+                       byte resultPrecision = (byte)(Math.Max (x.Scale, y.Scale) +
+                                                Math.Max (x.Precision - x.Scale, y.Precision - y.Scale) + 1);
 
-                       ulong res; 
+                       if (resultPrecision > MaxPrecision)
+                               resultPrecision = MaxPrecision;
+                       
+                       int [] xData = x.Data; 
+                       int [] yData = y.Data;
+                       int [] resultBits = new int[4];
                        ulong carry = 0;
-
-                       // add one at a time, and carry the results over to the next
-                       for (int i = 0; i < 4; i +=1)
-                       {
-                               carry = 0;
-                               res = (ulong)(xData[i]) + (ulong)(yData[i]) + carry;
-                               if (res > Int32.MaxValue)
-                               {
-                                       carry = res - Int32.MaxValue;
-                                       res = Int32.MaxValue;
-                               }
-                               resultBits [i] = (int)res;
+                       ulong res = 0;
+                       for (int i = 0; i < 4; i++){
+                               res = (ulong)((uint)xData [i]) + (ulong)((uint)yData [i]) + carry;
+                               resultBits [i] = (int) (res & (UInt32.MaxValue));
+                               carry = res >> 32;
                        }
 
-                       // if we have carry left, then throw an exception
                        if (carry > 0)
                                throw new OverflowException ();
                        else
@@ -1066,8 +1124,10 @@ namespace System.Data.SqlTypes
 
                public static SqlDecimal operator / (SqlDecimal x, SqlDecimal y)
                {
-                       //                      return new SqlDecimal (x.Value / y.Value);
-                       return DecimalDiv (x, y);
+                       if (x.IsNull || y.IsNull)
+                               return SqlDecimal.Null;
+
+                       return DecimalDiv (x, y);
                }
 
                public static SqlBoolean operator == (SqlDecimal x, SqlDecimal y)
@@ -1075,17 +1135,19 @@ namespace System.Data.SqlTypes
                        if (x.IsNull || y.IsNull) 
                                return SqlBoolean.Null;
 
+                       if (x.IsPositive != y.IsPositive)
+                               return SqlBoolean.False;
+
                        if (x.Scale > y.Scale)
                                y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, false);
                        else if (y.Scale > x.Scale)
                                x = SqlDecimal.AdjustScale(y, y.Scale - x.Scale, false);
 
                        for (int i = 0; i < 4; i += 1)
-                       {
                                if (x.Data[i] != y.Data[i])
-                                       return new SqlBoolean (false);
-                       }
-                       return new SqlBoolean (true);
+                                       return SqlBoolean.False;
+
+                       return SqlBoolean.True;
                }
 
                public static SqlBoolean operator > (SqlDecimal x, SqlDecimal y)
@@ -1093,6 +1155,9 @@ namespace System.Data.SqlTypes
                        if (x.IsNull || y.IsNull) 
                                return SqlBoolean.Null;
 
+                       if (x.IsPositive != y.IsPositive)
+                               return new SqlBoolean (x.IsPositive);
+
                        if (x.Scale > y.Scale)
                                y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, false);
                        else if (y.Scale > x.Scale)
@@ -1110,9 +1175,12 @@ namespace System.Data.SqlTypes
 
                public static SqlBoolean operator >= (SqlDecimal x, SqlDecimal y)
                {
-                       if (x.IsNull || y.IsNull) 
+                       if (x.IsNull || y.IsNull)
                                return SqlBoolean.Null;
 
+                       if (x.IsPositive != y.IsPositive)
+                               return new SqlBoolean (x.IsPositive);
+
                        if (x.Scale > y.Scale)
                                y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
                        else if (y.Scale > x.Scale)
@@ -1133,6 +1201,9 @@ namespace System.Data.SqlTypes
                        if (x.IsNull || y.IsNull) 
                                return SqlBoolean.Null;
 
+                       if (x.IsPositive != y.IsPositive)
+                               return SqlBoolean.True;
+
                        if (x.Scale > y.Scale)
                                x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
                        else if (y.Scale > x.Scale)
@@ -1141,45 +1212,46 @@ namespace System.Data.SqlTypes
                        for (int i = 0; i < 4; i += 1)
                        {
                                if (x.Data[i] != y.Data[i])
-                                       return new SqlBoolean (true);
+                                       return SqlBoolean.True;
                        }
-                       return new SqlBoolean (false);
+                       return SqlBoolean.False;
                }
 
                public static SqlBoolean operator < (SqlDecimal x, SqlDecimal y)
                {
-
                        if (x.IsNull || y.IsNull) 
                                return SqlBoolean.Null;
 
+                       if (x.IsPositive != y.IsPositive)
+                               return new SqlBoolean (y.IsPositive);
+
                        if (x.Scale > y.Scale)
                                y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
                        else if (y.Scale > x.Scale)
                                x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
 
-                       for (int i = 3; i >= 0; i -= 1)
-                       {
+                       for (int i = 3; i >= 0; i -= 1) {
                                if (x.Data[i] == 0 && y.Data[i] == 0) 
                                        continue;
-
                                return new SqlBoolean (x.Data[i] < y.Data[i]);
                        }
                        return new SqlBoolean (false);
-
                }
 
                public static SqlBoolean operator <= (SqlDecimal x, SqlDecimal y)
                {
-                       if (x.IsNull || y.IsNull) 
+                       if (x.IsNull || y.IsNull)
                                return SqlBoolean.Null;
 
+                       if (x.IsPositive != y.IsPositive)
+                               return new SqlBoolean (y.IsPositive);
+
                        if (x.Scale > y.Scale)
                                y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
                        else if (y.Scale > x.Scale)
                                x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
 
-                       for (int i = 3; i >= 0; i -= 1)
-                       {
+                       for (int i = 3; i >= 0; i -= 1) {
                                if (x.Data[i] == 0 && y.Data[i] == 0) 
                                        continue;
                                else
@@ -1190,19 +1262,15 @@ namespace System.Data.SqlTypes
 
                public static SqlDecimal operator * (SqlDecimal x, SqlDecimal y)
                {
-                       // adjust the scale to the smaller of the two beforehand
-                       if (x.Scale > y.Scale)
-                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
-                       else if (y.Scale > x.Scale)
-                               y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
+                       if (x.IsNull || y.IsNull)
+                               return SqlDecimal.Null;
 
                        // set the precision to the greater of the two
-                       byte resultPrecision;
-                       if (x.Precision > y.Precision)
-                               resultPrecision = x.Precision;
-                       else
-                               resultPrecision = y.Precision;
-                               
+                       byte resultPrecision = (byte)(x.Precision + y.Precision + 1);
+                       byte resultScale = (byte)(x.Scale + y.Scale);
+                       if (resultPrecision > MaxPrecision)
+                               resultPrecision = MaxPrecision;
+
                        int[] xData = x.Data;
                        int[] yData = y.Data;
                        int[] resultBits = new int[4];
@@ -1210,63 +1278,104 @@ namespace System.Data.SqlTypes
                        ulong res; 
                        ulong carry = 0;
 
-                       // multiply one at a time, and carry the results over to the next
-                       for (int i = 0; i < 4; i +=1)
-                       {
-                               carry = 0;
-                               res = (ulong)(xData[i]) * (ulong)(yData[i]) + carry;
-                               if (res > Int32.MaxValue)
-                               {
-                                       carry = res - Int32.MaxValue;
-                                       res = Int32.MaxValue;
-                               }
-                               resultBits [i] = (int)res;
+                       for (int i=0; i<4; ++i) {
+                               res = 0;
+                               for (int j=i; j<=i; ++j)
+                                       res += ((ulong)(uint) xData[j]) *  ((ulong)(uint) yData[i-j]);
+                               resultBits [i] = (int) ((res + carry) & UInt32.MaxValue);
+                               carry = res >> 32;
                        }
-
+                       
                        // if we have carry left, then throw an exception
                        if (carry > 0)
                                throw new OverflowException ();
                        else
-                               return new SqlDecimal (resultPrecision, x.Scale, (x.IsPositive == y.IsPositive), resultBits);
-                               
+                               return new SqlDecimal (resultPrecision, resultScale, (x.IsPositive == y.IsPositive), resultBits);
                }
 
                public static SqlDecimal operator - (SqlDecimal x, SqlDecimal y)
                {
-                       if (x.IsPositive && !y.IsPositive) return x + y;
-                       if (!x.IsPositive && y.IsPositive) return -(x + y);
-                       if (!x.IsPositive && !y.IsPositive) return y - x;
+                       if (x.IsNull || y.IsNull)
+                               return SqlDecimal.Null;
 
-                       // otherwise, x is positive and y is positive
-                       bool resultPositive = (bool)(x > y);
-                       int[] yData = y.Data;
+                       if (x.IsPositive && !y.IsPositive){
+                               y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
+                               return x + y;
+                       }
+                       if (!x.IsPositive && y.IsPositive){
+                               x = new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
+                               x = (x + y);
+                               return new SqlDecimal (x.Precision, x.Scale, false, x.Data);
+                       }
+                       if (!x.IsPositive && !y.IsPositive){
+                               y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
+                               x = new SqlDecimal(x.Precision, x.Scale, !x.IsPositive, x.Data);
+                               return (y - x);
+                       }
+                       // adjust the scale to the larger of the two beforehand
+                       if (x.scale > y.scale)
+                               y = SqlDecimal.AdjustScale (y, x.scale - y.scale, false); 
+                       else if (y.scale > x.scale)
+                               x = SqlDecimal.AdjustScale (x, y.scale - x.scale, false);
+
+                       //calculation of the new Precision for the result
+                       byte resultPrecision = (byte)(Math.Max (x.Scale, y.Scale) +
+                                       Math.Max (x.Precision - x.Scale, y.Precision - y.Scale));
+
+                       int[] op1_Data;
+                       int[] op2_Data;
+                       if (x >= y) {
+                               op1_Data = x.Data;
+                               op2_Data = y.Data;
+                       } else {
+                               op1_Data = y.Data;
+                               op2_Data = x.Data;
+                       }
 
-                       for (int i = 0; i < 4; i += 1) yData[i] = -yData[i];
+                       ulong res = 0;
+                       int carry = 0;
+                       int[] resultBits = new int[4];
 
-                       SqlDecimal yInverse = new SqlDecimal (y.Precision, y.Scale, y.IsPositive, yData);
 
-                       if (resultPositive)
-                               return x + yInverse;
+                       /*
+                        if ((uint)op2_Data [i] > (uint)op1_Data [i]) {
+                                carry = UInt32.MaxValue;
+                                op2_Data [i] = op2_Data [i] >> 1;
+                        } else
+                                carr = 0;
+                               res = (uint)carry; +(ulong)((uint)op1_Data [i]) - (ulong)((uint)op2_Data [i]) 
+                       */
+
+                       for (int i = 0; i < 4; i += 1) {
+                               res = (ulong)((uint)op1_Data [i]) - (ulong)((uint)op2_Data [i]) + (ulong)carry;
+                               carry = 0;
+                               if ((uint)op2_Data [i] > (uint)op1_Data [i])
+                                       carry = -1;
+                               resultBits [i] = (int)res;
+                       }
+
+                       if (carry > 0)
+                               throw new OverflowException ();
                        else
-                               return -(x + yInverse);
+                               return new SqlDecimal (resultPrecision, x.Scale, (x>=y).Value, resultBits);
                }
 
-               public static SqlDecimal operator - (SqlDecimal n)
+               public static SqlDecimal operator - (SqlDecimal x)
                {
-                       return new SqlDecimal (n.Precision, n.Scale, !n.IsPositive, n.Data);
+                       return new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
                }
 
                public static explicit operator SqlDecimal (SqlBoolean x)
                {
-                       if (x.IsNull) 
+                       if (x.IsNull)
                                return Null;
                        else
                                return new SqlDecimal ((decimal)x.ByteValue);
                }
 
-               public static explicit operator Decimal (SqlDecimal n)
+               public static explicit operator Decimal (SqlDecimal x)
                {
-                       return n.Value;
+                       return x.Value;
                }
 
                public static explicit operator SqlDecimal (SqlDouble x)
@@ -1296,6 +1405,18 @@ namespace System.Data.SqlTypes
                        }
                }
 
+#if NET_2_0
+               public static explicit operator SqlDecimal (double x)
+               {
+                       return new SqlDecimal (x);
+               }
+
+               public static implicit operator SqlDecimal (long x)
+               {
+                       return new SqlDecimal (x);
+               }
+#endif
+
                public static implicit operator SqlDecimal (decimal x)
                {
                        return new SqlDecimal (x);
@@ -1303,43 +1424,109 @@ namespace System.Data.SqlTypes
 
                public static implicit operator SqlDecimal (SqlByte x)
                {
-                       if (x.IsNull) 
+                       if (x.IsNull)
                                return Null;
                        else
-                               return new SqlDecimal ((decimal)x.Value);
+                               return new SqlDecimal ((decimal) x.Value);
                }
 
                public static implicit operator SqlDecimal (SqlInt16 x)
                {
-                       if (x.IsNull) 
+                       if (x.IsNull)
                                return Null;
                        else
-                               return new SqlDecimal ((decimal)x.Value);
+                               return new SqlDecimal ((decimal) x.Value);
                }
 
                public static implicit operator SqlDecimal (SqlInt32 x)
                {
-                       if (x.IsNull) 
+                       if (x.IsNull)
                                return Null;
                        else
-                               return new SqlDecimal ((decimal)x.Value);
+                               return new SqlDecimal ((decimal) x.Value);
                }
 
                public static implicit operator SqlDecimal (SqlInt64 x)
                {
-                       if (x.IsNull) 
+                       if (x.IsNull)
                                return Null;
                        else
-                               return new SqlDecimal ((decimal)x.Value);
+                               return new SqlDecimal ((decimal) x.Value);
                }
 
                public static implicit operator SqlDecimal (SqlMoney x)
                {
-                       if (x.IsNull) 
+                       if (x.IsNull)
                                return Null;
                        else
-                               return new SqlDecimal ((decimal)x.Value);
+                               return new SqlDecimal ((decimal) x.Value);
+               }
+
+#if NET_2_0
+               public static XmlQualifiedName GetXsdType (XmlSchemaSet schemaSet)
+               {
+                       if (schemaSet != null && schemaSet.Count == 0) {
+                               XmlSchema xs = new XmlSchema ();
+                               XmlSchemaComplexType ct = new XmlSchemaComplexType ();
+                               ct.Name = "decimal";
+                               xs.Items.Add (ct);
+                               schemaSet.Add (xs);
+                       }
+                       return new XmlQualifiedName ("decimal", "http://www.w3.org/2001/XMLSchema");
+               }
+               
+               XmlSchema IXmlSerializable.GetSchema ()
+               {
+                       return null;
+               }
+               
+               void IXmlSerializable.ReadXml (XmlReader reader)
+               {
+                       SqlDecimal retval;
+
+                       if (reader == null)
+                               return;
+
+                       switch (reader.ReadState) {
+                       case ReadState.EndOfFile:
+                       case ReadState.Error:
+                       case ReadState.Closed:
+                               return;
+                       }
+
+                       // Skip XML declaration and prolog
+                       // or do I need to validate for the <SqlInt32> tag?
+                       reader.MoveToContent ();
+                       if (reader.EOF)
+                               return;
+
+                       reader.Read ();
+                       if (reader.NodeType == XmlNodeType.EndElement)
+                               return;
+
+                       if (reader.Value.Length > 0) {
+                               if (String.Compare ("Null", reader.Value) == 0) {
+                                       // means a null reference/invalid value
+                                       notNull = false;
+                                       return; 
+                               }
+                               // FIXME: do we need to handle the FormatException?
+                               retval = new SqlDecimal (Decimal.Parse (reader.Value));
+
+                               // SqlDecimal.Data returns a clone'd array
+                               this.value = retval.Data; 
+                               this.notNull = true;
+                               this.scale = retval.Scale;
+                               this.precision = retval.Precision;
+                               this.positive = retval.IsPositive;
+                       }
+               }
+
+               void IXmlSerializable.WriteXml (XmlWriter writer)
+               {
+                       writer.WriteString (this.Value.ToString ());
                }
+#endif
 
                #endregion
        }