2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Data / System.Data.SqlTypes / SqlDecimal.cs
index 0f67b2d1d5b8438e0e2ecd618c34dc1a38a27060..85e33561c69900eaabfafab059244793f1846237 100644 (file)
@@ -3,12 +3,37 @@
 //
 // Author:
 //   Tim Coleman <tim@timcoleman.com>
+//   Ville Palo <vi64pa@koti.soon.fi>
 //
 // (C) Copyright 2002 Tim Coleman
 //
 
-using Mono.Data.TdsClient.Internal;
+//
+// 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.
+//
+
+using Mono.Data.Tds.Protocol;
 using System;
+using System.Text;
 using System.Globalization;
 
 namespace System.Data.SqlTypes
@@ -28,11 +53,34 @@ namespace System.Data.SqlTypes
                const int SCALE_SHIFT = 16;
                const int SIGN_SHIFT = 31;
                const int RESERVED_SS32_BITS = 0x7F00FFFF;
+               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
+                       };
 
                public static readonly byte MaxPrecision = 38; 
-               public static readonly byte MaxScale = 28;
-               public static readonly SqlDecimal MaxValue = new SqlDecimal (79228162514264337593543950335.0);
-               public static readonly SqlDecimal MinValue = new SqlDecimal (-79228162514264337593543950335.0);
+               public static readonly byte MaxScale = 38;
+
+               // This should be 99999999999999999999999999999999999999
+               public static readonly SqlDecimal MaxValue = new SqlDecimal (MaxPrecision, 
+                                                                            (byte)0, 
+                                                                            true, 
+                                                                            (int)-1, 
+                                                                            160047679,
+                                                                            1518781562, 
+                                                                            1262177448);
+               // This should be -99999999999999999999999999999999999999
+               public static readonly SqlDecimal MinValue = new SqlDecimal (MaxPrecision, 
+                                                                            (byte)0, false,
+                                                                            -1,                                                
+                                                                            160047679,
+                                                                            1518781562, 
+                                                                            1262177448);
+
                public static readonly SqlDecimal Null;
 
                #endregion
@@ -50,16 +98,35 @@ namespace System.Data.SqlTypes
                        if (this.scale > MaxScale || ((uint)binData [3] & RESERVED_SS32_BITS) != 0)
                                throw new ArgumentException(Locale.GetText ("Invalid scale"));
 
-                       this.positive = ((binData[3] >> SIGN_SHIFT) > 0);
                        this.value = new int[4];
                        this.value[0] = binData[0];
                        this.value[1] = binData[1];
                        this.value[2] = binData[2];
                        this.value[3] = 0;
+
+                       if (value >= 0)
+                               positive = true;
+                       else 
+                               positive = false;
+
                        notNull = true;
+                       precision = GetPrecision (value);
+               }
+                               
+               public SqlDecimal (double value) : this ((decimal)value) 
+               {
+                       SqlDecimal n = this;
+                       int digits = 17 - precision;
+                       if (digits > 0)
+                               n = AdjustScale (this, digits, false);
+                       else
+                               n = Round (this, 17);
+                       this.notNull = n.notNull;
+                       this.positive = n.positive;
+                       this.precision = n.precision;
+                       this.scale = n.scale;
+                       this.value = n.value;
                }
-
-               public SqlDecimal (double value) : this ((decimal)value) { }
                public SqlDecimal (int value) : this ((decimal)value) { }
                public SqlDecimal (long value) : this ((decimal)value) { }
 
@@ -76,23 +143,51 @@ namespace System.Data.SqlTypes
                        this.value[2] = data3;
                        this.value[3] = data4;
                        notNull = true;
+
+                       if (precision < scale)
+                               throw new SqlTypeException (Locale.GetText ("Invalid presicion/scale combination."));
+
+                       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 ");
                }
 
                #endregion
 
                #region Properties
 
-               [MonoTODO]
                public byte[] BinData {
-                       get { throw new NotImplementedException (); }
+                       get { 
+
+                               byte [] b = new byte [value.Length * 4];
+                               
+                               int j = 0;
+                               for (int i = 0; i < value.Length; i++) {
+
+                                       b [j++] = (byte)(0xff & value [i]);
+                                       b [j++] = (byte)(0xff & value [i] >> 8);
+                                       b [j++] = (byte)(0xff & value [i] >> 16);
+                                       b [j++] = (byte)(0xff & value [i] >> 24);
+                               }
+
+                               return b;
+                       }
                }
 
                public int[] Data { 
                        get { 
                                if (this.IsNull)
                                        throw new SqlNullValueException ();
-                               else
-                                       return (value);
+                               // Data should always return clone, not to be modified
+                               int [] ret = new int [4];
+                               ret [0] = value [0];
+                               ret [1] = value [1];
+                               ret [2] = value [2];
+                               ret [3] = value [3];
+                               return ret;
                        }
                }
 
@@ -120,7 +215,7 @@ namespace System.Data.SqlTypes
                                if (this.value[3] > 0)
                                        throw new OverflowException ();
 
-                               return new decimal (value[0], value[1], value[2], positive, scale);
+                               return new decimal (value[0], value[1], value[2], !positive, scale);
                        }
                }
 
@@ -128,10 +223,11 @@ namespace System.Data.SqlTypes
 
                #region Methods
 
-               [MonoTODO]
                public static SqlDecimal Abs (SqlDecimal n)
                {
-                       throw new NotImplementedException();
+                       if (!n.notNull)
+                               return n;
+                       return new SqlDecimal (n.Precision, n.Scale, true, n.Data);
                }
 
                public static SqlDecimal Add (SqlDecimal x, SqlDecimal y)
@@ -139,16 +235,42 @@ namespace System.Data.SqlTypes
                        return (x + y);
                }
 
-               [MonoTODO]
                public static SqlDecimal AdjustScale (SqlDecimal n, int digits, bool fRound)
                {
-                       throw new NotImplementedException ();
+                       byte prec = n.Precision;
+                       if (n.IsNull)
+                               throw new SqlNullValueException ();
+
+                       int [] data;
+                       byte newScale;
+                       if (digits == 0)
+                               return n;
+                       else 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);
+                       } else {
+                               if (fRound)
+                                       n = Round (n, digits + n.scale);
+                               else
+                                       n = Round (Truncate (n, digits + n.scale), digits + n.scale);
+                               data = n.Data;
+                               newScale = n.scale;
+                       }
+
+                       return new SqlDecimal (prec, newScale, n.positive, data);
                }
 
-               [MonoTODO]
                public static SqlDecimal Ceiling (SqlDecimal n)
                {
-                       throw new NotImplementedException();
+                       if (!n.notNull)
+                               return n;
+                       return AdjustScale (n, -(n.Scale), true);
                }
 
                public int CompareTo (object value)
@@ -163,10 +285,11 @@ namespace System.Data.SqlTypes
                                return this.Value.CompareTo (((SqlDecimal)value).Value);
                }
 
-               [MonoTODO]
                public static SqlDecimal ConvertToPrecScale (SqlDecimal n, int precision, int scale)
                {
-                       throw new NotImplementedException ();
+//                     return new SqlDecimal ((byte)precision, (byte)scale, n.IsPositive, n.Data);
+                       // FIXME: precision
+                       return AdjustScale (n, scale - n.scale, true);
                }
 
                public static SqlDecimal Divide (SqlDecimal x, SqlDecimal y)
@@ -178,6 +301,10 @@ namespace System.Data.SqlTypes
                {
                        if (!(value is SqlDecimal))
                                return false;
+                       else if (this.IsNull && ((SqlDecimal)value).IsNull)
+                               return true;                    
+                       else if (((SqlDecimal)value).IsNull)
+                               return false;
                        else
                                return (bool) (this == (SqlDecimal)value);
                }
@@ -187,10 +314,9 @@ namespace System.Data.SqlTypes
                        return (x == y);
                }
 
-               [MonoTODO]
                public static SqlDecimal Floor (SqlDecimal n)
                {
-                       throw new NotImplementedException ();
+                       return AdjustScale (n, -(n.Scale), false);
                }
 
                internal static SqlDecimal FromTdsBigDecimal (TdsBigDecimal x)
@@ -203,7 +329,15 @@ namespace System.Data.SqlTypes
 
                public override int GetHashCode ()
                {
-                       return (int)this.Value;
+                       int result = 10;
+                       result = 91 * result + this.Data[0];
+                       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;
+
+                       return result;
                }
 
                public static SqlBoolean GreaterThan (SqlDecimal x, SqlDecimal y)
@@ -236,28 +370,42 @@ namespace System.Data.SqlTypes
                        return (x != y);
                }
 
-               [MonoTODO]
                public static SqlDecimal Parse (string s)
                {
-                       throw new NotImplementedException ();                  
+                       if (s == null)
+                               throw new ArgumentNullException (Locale.GetText ("string s"));
+                       else 
+                               return new SqlDecimal (Decimal.Parse (s));
                }
 
-               [MonoTODO]
                public static SqlDecimal Power (SqlDecimal n, double exp)
                {
-                       throw new NotImplementedException ();
+                       if (n.IsNull)
+                               return SqlDecimal.Null;
+
+                       return new SqlDecimal (Math.Pow (n.ToDouble (), exp));
                }
 
-               [MonoTODO]
                public static SqlDecimal Round (SqlDecimal n, int position)
                {
-                       throw new NotImplementedException ();
+                       if (n.IsNull)
+                               throw new SqlNullValueException ();
+
+                       decimal d = n.Value;
+                       d = Decimal.Round (d, position);
+                       return new SqlDecimal (d);
                }
 
-               [MonoTODO]
                public static SqlInt32 Sign (SqlDecimal n)
                {
-                       throw new NotImplementedException ();
+                       SqlInt32 result = 0;
+
+                       if (n >= new SqlDecimal (0))
+                               result = 1;
+                       else
+                               result = -1;
+
+                       return result;
                }
 
                public static SqlDecimal Subtract (SqlDecimal x, SqlDecimal y)
@@ -265,9 +413,30 @@ namespace System.Data.SqlTypes
                        return (x - y);
                }
 
+               private byte GetPrecision (decimal value)
+               {
+                       string str = value.ToString ();
+                       byte result = 0;
+
+                       foreach (char c in str) {
+                               
+                               if (c >= '0' && c <= '9')
+                                       result++;
+                       }
+                       
+                       return result;
+               }
+
                public double ToDouble ()
                {
-                       return ((double)this.Value);
+                       // FIXME: This is wrong way to do this
+                       double d = (uint)this.Data [0];
+                       d += ((uint)this.Data [1]) * Math.Pow (2, 32);
+                       d += ((uint)this.Data [2]) * Math.Pow (2, 64);
+                       d += ((uint)this.Data [3]) * Math.Pow (2, 96);
+                       d = d / Math.Pow (10, scale);
+
+                       return d;
                }
 
                public SqlBoolean ToSqlBoolean ()
@@ -318,15 +487,579 @@ namespace System.Data.SqlTypes
                public override string ToString ()
                {
                        if (this.IsNull)
-                               return String.Empty;
+                               return "Null";
+                       
+                       // convert int [4] --> ulong [2]
+                       ulong lo = (uint)this.Data [0];
+                       lo += (ulong)((ulong)this.Data [1] << 32);
+                       ulong hi = (uint)this.Data [2];
+                       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);
+                       if (this.Scale > 0)
+                               Result.Insert (Result.Length - this.Scale, ".");
+
+                       if (!positive)
+                               Result.Insert (0, '-');
+
+                       return Result.ToString ();
+               }
+
+               // From decimal.c
+               private static int Div128By32(ref ulong hi, ref ulong lo, uint divider)
+               {
+                       uint t = 0;
+                       return Div128By32 (ref hi, ref lo, divider, ref t);
+               }
+
+               // From decimal.c
+               private static int Div128By32(ref ulong hi, ref ulong lo, uint divider, ref uint rest)
+               {
+                       ulong a = 0;
+                       ulong b = 0;
+                       ulong c = 0;    
+                       
+                       a = (uint)(hi >> 32);
+                       b = a / divider;
+                       a -= b * divider;
+                       a <<= 32;
+                       a |= (uint)hi;
+                       c = a / divider;
+                       a -= c * divider;
+                       a <<= 32;
+                       hi = b << 32 | (uint)c;
+                       
+                       a |= (uint)(lo >> 32);
+                       b = a / divider;
+                       a -= b * divider;
+                       a <<= 32;
+                       a |= (uint)lo;
+                       c = a / divider;
+                       a -= c * divider;
+                       lo = b << 32 | (uint)c;
+                       rest = (uint)a;
+                       a <<= 1;
+
+                       return (a > divider || (a == divider && (c & 1) == 1)) ? 1 : 0;
+
+               }
+
+               [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;                   
+                       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);
+
+                       sc = x.Scale - y.Scale;
+
+                       Rescale128 (ref lo, ref hi, ref sc, texp, 0, 38, 1);
+
+                       uint r = 0;
+                       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++;
+
+                       while ((prec + sc) > MaxScale) {
+                               Div128By32(ref hi, ref lo, 10, ref r);
+                               sc--;
+                               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, positive, resultLo,
+                                                      resultMi, resultMi2,
+                                                      resultHi);
+               }
+
+               // From decimal.c
+               private static void Rescale128 (ref ulong clo, ref ulong chi, 
+                                            ref int scale, int texp,
+                                            int minScale, int maxScale,
+                                            int roundFlag)
+               {
+                       uint factor = 0;
+                       uint overhang = 0;
+                       int sc = 0;
+                       int i = 0;
+                       int rc = 0;
+                       int roundBit = 0;
+
+                       sc = scale;
+                       if (texp > 0) {
+
+                               // reduce exp 
+                               while (texp > 0 && sc <= maxScale) {
+
+                                               overhang = (uint)(chi >> 64);
+                                       while (texp > 0 && (((clo & 1) == 0) || overhang > 0)) {
+                                               
+                                               if (--texp == 0)
+                                                       roundBit = (int)(clo & 1);
+                                               RShift128 (ref clo, ref chi);
+
+                                               overhang = (uint)(chi >> 32);
+                                       }
+
+                                       if (texp > DECIMAL_MAX_INTFACTORS)
+                                               i = DECIMAL_MAX_INTFACTORS;
+                                       else 
+                                               i = texp;
+
+                                       if (sc + i > maxScale) 
+                                               i = maxScale - sc;
+
+                                       if (i == 0)
+                                               break;
+
+                                       texp -= i;
+                                       sc += i;
+
+                                       // 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) {
+                                       if (--texp == 0) 
+                                               roundBit = (int)(clo & 1);
+                                       RShift128 (ref clo, ref chi);
+                               }
+                       }
+       
+                       while (sc > maxScale) {
+                               i = scale - maxScale;
+                               if (i > DECIMAL_MAX_INTFACTORS)
+                                       i = DECIMAL_MAX_INTFACTORS;
+                               sc -= i;
+                               roundBit = Div128By32 (ref clo, ref chi, 
+                                                      constantsDecadeInt32Factors[i]);
+                       }
+
+                       while (sc < minScale) {
+                               if (roundFlag == 0)
+                                       roundBit = 0;
+                               i = minScale - sc;
+                               if (i > DECIMAL_MAX_INTFACTORS)
+                                       i = DECIMAL_MAX_INTFACTORS;
+                               sc += i;
+                               Mult128By32 (ref clo, ref chi, 
+                                            constantsDecadeInt32Factors[i], roundBit);
+                               roundBit = 0;
+                       }
+                       scale = sc;
+                       Normalize128 (ref clo, ref chi, ref sc, roundFlag, roundBit);
+               }
+
+               // From decimal.c
+               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)) 
+                               RoundUp128 (ref clo, ref chi); 
+               }
+
+               // From decimal.c
+               private static void RoundUp128(ref ulong lo, ref ulong hi)
+               {
+                           if ((++lo) == 0) 
+                                   ++hi;
+               }
+               
+               // From decimal.c
+               private static void DecimalDivSub (ref SqlDecimal x, ref SqlDecimal y, ref ulong clo, ref ulong chi, ref int exp)
+               {
+                       ulong xlo, xmi, xhi;
+                       ulong tlo = 0; 
+                       ulong tmi = 0;
+                       ulong thi = 0;;
+                       uint ylo = 0;
+                       uint ymi = 0;
+                       uint ymi2 = 0;
+                       uint yhi = 0;
+                       int ashift = 0; 
+                       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];
+                       
+                       if (ylo == 0 && ymi == 0 && ymi2 == 0 && yhi == 0)
+                               throw new DivideByZeroException ();
+
+                       if (xmi == 0 && xhi == 0) {
+                               clo = chi = 0;
+                               return;
+                       }
+                       
+                       // enlarge dividend to get maximal precision
+                       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) 
+                               LShift128 (ref ylo, ref ymi, ref ymi2, ref yhi);
+                       
+                       thi = ((ulong)yhi) << 32 | (ulong)ymi2;
+                       tmi = ((ulong)ymi) << 32 | (ulong)ylo;
+                       tlo = 0;
+
+                       if (xhi > thi || (xhi == thi && xmi >=tmi)) {
+                               Sub192(xlo, xmi, xhi, tlo, tmi, thi, ref xlo, ref xmi, ref xhi);
+                               extraBit = 1;
+                       } else {
+                               extraBit = 0;
+                       }
+                       
+                       Div192By128To128 (xlo, xmi, xhi, ylo, ymi, ymi2, yhi, ref clo, ref chi);
+                       exp = 128 + ashift - bshift;
+
+                       if (extraBit != 0) {
+                               RShift128 (ref clo, ref chi);
+                               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;
+                       
+                       mi >>= 1;
+                       if ((hi & 1) != 0)
+                               mi |= LIT_GUINT64_HIGHBIT;
+
+                       hi >>= 1;
+               }
+
+               // From decimal.c
+               private static void RShift128(ref ulong lo, ref ulong hi)
+               {
+                       lo >>=1;
+                       if ((hi & 1) != 0) 
+                               lo |= LIT_GUINT64_HIGHBIT;
+                       hi >>= 1;
+               }
+               
+               // From decimal.c
+               private static void LShift128(ref ulong lo, ref ulong hi)
+               {
+                       hi <<= 1;
+
+                       if ((lo & LIT_GUINT64_HIGHBIT) != 0) 
+                               hi++;
+
+                       lo <<= 1;
+               }
+               
+               // From decimal.c
+               private static void LShift128(ref uint lo, ref uint mi, ref uint mi2, ref uint hi)
+               {
+                       hi <<= 1;
+                       if ((mi2 & LIT_GUINT32_HIGHBIT) != 0) 
+                               hi++;
+
+                       mi2 <<= 1;
+                       if ((mi & LIT_GUINT32_HIGHBIT) != 0) 
+                               mi2++;
+                       
+                       mi <<= 1;
+                       if ((lo & LIT_GUINT32_HIGHBIT) != 0) 
+                              mi++;
+
+                       lo <<= 1;
+               }
+
+               // From decimal.c
+               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;
+
+                       rlo = xlo;
+                       rmi = xmi;
+                       rhi = xhi;
+
+                       h = Div192By128To32WithRest (ref rlo, ref rmi, ref rhi, ylo, ymi, ymi2, yhi);
+
+                       // mid 32 bit
+                       rhi = (rhi << 32) | (rmi >> 32);
+                       rmi = (rmi << 32) | (rlo >> 32);
+                       rlo <<= 32;
+
+                       chi = (((ulong)h) << 32) | Div192By128To32WithRest (
+                               ref rlo, ref rmi, ref rhi, ylo, ymi, ymi2, yhi);
+
+                       // low 32 bit
+                       rhi = (rhi << 32) | (rmi >> 32);
+                       rmi = (rmi << 32) | (rlo >> 32);
+                       rlo <<= 32;
+
+                       h = Div192By128To32WithRest (ref rlo, ref rmi, ref rhi,
+                                                    ylo, ymi, ymi2, yhi);
+
+                       // estimate lowest 32 bit (two last bits may be wrong)
+                       if (rhi >= yhi) 
+                               c =  0xFFFFFFFF;
+                       else {
+                               rhi <<= 32;
+                               c = (uint)(rhi / yhi);
+                       }
+                               
+                       clo = (((ulong)h) << 32) | c;
+               }
+                       
+               // From decimal.c
+               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 tlo = 0;
+                       ulong thi = 0;
+                       uint c;
+                       
+                       rlo = xlo;
+                       rmi = xmi;
+                       rhi = xhi;
+
+                       if (rhi >= (((ulong)yhi << 32)))
+                               c = 0xFFFFFFFF;
                        else
-                               return value.ToString ();
+                               c = (uint) (rhi / yhi);
+
+                       Mult128By32To128 (ylo, ymi, ymi2, yhi, c, ref tlo, ref thi);
+                       Sub192 (rlo, rmi, rhi, 0, tlo, thi, ref rlo, ref rmi, ref rhi);
+
+                       while (((long)rhi) < 0) {
+                               c--;
+                               Add192 (rlo, rmi, rhi, 0, (((ulong)ymi) << 32) | ylo, yhi | ymi2, ref rlo, ref rmi, ref rhi);
+                       }
+                       xlo = rlo;
+                       xmi = rmi;
+                       xhi = rhi;
+
+                       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;
+
+                       if (roundBit != 0)
+                               a += factor / 2;
+
+                       h0 = (uint)a;
+                       a >>= 32;
+                       a += (clo >> 32) * factor;
+                       h1 = (uint)a;
+                       
+                       clo = ((ulong)h1) << 32 | h0;
+
+                       a >>= 32;
+                       a += ((ulong)(uint)cmi) * factor;                      
+                       h0 = (uint)a;
+                       
+                       a >>= 32;
+                       a += (cmi >> 32) * factor;
+                       h1 = (uint)a;
+                       
+                       cmi = ((ulong)h1) << 32 | h0;
+                       a >>= 32;
+                       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 Mult128By32 (ref ulong clo, ref ulong chi, uint factor, int roundBit)
+               {
+                       ulong a = 0;
+                       uint h0 = 0;
+                       uint h1 = 0;
+                       uint h2 = 0;
+
+                       a = ((ulong)(uint)clo) * factor;
+
+                       if (roundBit != 0)
+                               a += factor / 2;
+
+                       h0 = (uint)a;
+
+                       a >>= 32;
+                       a += (clo >> 32) * factor;
+                       h1 = (uint)a;
+                       
+                       clo = ((ulong)h1) << 32 | h0;
+
+                       a >>= 32;
+                       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,
+                                        uint factor, ref ulong clo, ref ulong chi)
+               {
+                       ulong a;
+                       uint h0, h1, h2;
+
+                       a = ((ulong)xlo) * factor;
+                       h0 = (uint)a;
+                       
+                       a >>= 32;
+                       a += ((ulong)xmi) * factor;
+                       h1 = (uint)a;
+
+                       a >>= 32;
+                       a += ((ulong)xmi2) * factor;
+                       h2 = (uint)a;
+
+                       a >>= 32;
+                       a += ((ulong)xhi) * factor;
+                       
+                       clo = ((ulong)h1) << 32 | h0;
+                       chi = a | h2;
+               }
+
+               // From decimal.c
+               private static void Add192 (ulong xlo, ulong xmi, ulong xhi,
+                                    ulong ylo, ulong ymi, ulong yhi,
+                                    ref ulong clo, ref ulong cmi, ref ulong chi)
+               {
+                       xlo += ylo;
+                       if (xlo < ylo) {
+                               xmi++;
+                               if (xmi == 0)
+                                       xhi++;
+                       }
+
+                       xmi += ymi;
+
+                       if (xmi < ymi)
+                               xmi++;
+
+                       xhi += yhi;
+                       clo = xlo;
+                       cmi = xmi;
+                       chi = xhi;                            
+               }
+
+               // From decimal.c
+               private static void Sub192 (ulong xlo, ulong xmi, ulong xhi,
+                                    ulong ylo, ulong ymi, ulong yhi,
+                                    ref ulong lo, ref ulong mi, ref ulong hi)
+               {
+                       
+                       ulong clo = 0;
+                       ulong cmi = 0;
+                       ulong chi = 0;
+                       
+                       clo = xlo - ylo;
+                       cmi = xmi - ymi;
+                       chi = xhi - yhi;
+
+                       if (xlo < ylo) {
+                               if (cmi == 0)
+                                       chi--;
+                               cmi--;
+                       }
+
+                       if (xmi < ymi)
+                               chi--;
+
+                       lo = clo;
+                       mi = cmi;
+                       hi = chi;
                }
 
-               [MonoTODO]
                public static SqlDecimal Truncate (SqlDecimal n, int position)
                {
-                       throw new NotImplementedException ();
+                       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)
@@ -334,12 +1067,12 @@ namespace System.Data.SqlTypes
                        // 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);
+
+                       // adjust the scale to the larger of the two beforehand
+                       if (x.scale > y.scale)
+                               y = SqlDecimal.AdjustScale (y, x.scale - y.scale, true); // FIXME: should be false (fix it after AdjustScale(,,false) is fixed)
+                       else if (y.scale > x.scale)
+                               x = SqlDecimal.AdjustScale (x, y.scale - x.scale, true); // FIXME: should be false (fix it after AdjustScale(,,false) is fixed)
 
                        // set the precision to the greater of the two
                        byte resultPrecision;
@@ -375,10 +1108,10 @@ namespace System.Data.SqlTypes
                                return new SqlDecimal (resultPrecision, x.Scale, x.IsPositive, resultBits);
                }
 
-               [MonoTODO]
                public static SqlDecimal operator / (SqlDecimal x, SqlDecimal y)
                {
-                       throw new NotImplementedException ();
+                       //                      return new SqlDecimal (x.Value / y.Value);
+                       return DecimalDiv (x, y);
                }
 
                public static SqlBoolean operator == (SqlDecimal x, SqlDecimal y)
@@ -387,9 +1120,9 @@ namespace System.Data.SqlTypes
                                return SqlBoolean.Null;
 
                        if (x.Scale > y.Scale)
-                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
+                               y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, false);
                        else if (y.Scale > x.Scale)
-                               y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
+                               x = SqlDecimal.AdjustScale(y, y.Scale - x.Scale, false);
 
                        for (int i = 0; i < 4; i += 1)
                        {
@@ -405,11 +1138,11 @@ namespace System.Data.SqlTypes
                                return SqlBoolean.Null;
 
                        if (x.Scale > y.Scale)
-                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
+                               y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, false);
                        else if (y.Scale > x.Scale)
-                               y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
+                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, false);
 
-                       for (int i = 3; i >= 0; i -= 1)
+                       for (int i = 3; i >= 0; i--)
                        {
                                if (x.Data[i] == 0 && y.Data[i] == 0) 
                                        continue;
@@ -425,9 +1158,9 @@ namespace System.Data.SqlTypes
                                return SqlBoolean.Null;
 
                        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);
+                       else if (y.Scale > x.Scale)
+                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
 
                        for (int i = 3; i >= 0; i -= 1)
                        {
@@ -459,13 +1192,14 @@ namespace System.Data.SqlTypes
 
                public static SqlBoolean operator < (SqlDecimal x, SqlDecimal y)
                {
+
                        if (x.IsNull || y.IsNull) 
                                return SqlBoolean.Null;
 
                        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);
+                       else if (y.Scale > x.Scale)
+                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
 
                        for (int i = 3; i >= 0; i -= 1)
                        {
@@ -475,6 +1209,7 @@ namespace System.Data.SqlTypes
                                return new SqlBoolean (x.Data[i] < y.Data[i]);
                        }
                        return new SqlBoolean (false);
+
                }
 
                public static SqlBoolean operator <= (SqlDecimal x, SqlDecimal y)
@@ -483,9 +1218,9 @@ namespace System.Data.SqlTypes
                                return SqlBoolean.Null;
 
                        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);
+                       else if (y.Scale > x.Scale)
+                               x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
 
                        for (int i = 3; i >= 0; i -= 1)
                        {
@@ -580,23 +1315,29 @@ namespace System.Data.SqlTypes
 
                public static explicit operator SqlDecimal (SqlDouble x)
                {
-                       if (x.IsNull) 
-                               return Null;
-                       else
-                               return new SqlDecimal ((decimal)x.Value);
+                       checked {
+                               if (x.IsNull) 
+                                       return Null;
+                               else
+                                       return new SqlDecimal ((double)x.Value);
+                       }
                }
 
                public static explicit operator SqlDecimal (SqlSingle x)
                {
-                       if (x.IsNull) 
-                               return Null;
-                       else
-                               return new SqlDecimal ((decimal)x.Value);
+                       checked {
+                               if (x.IsNull) 
+                                       return Null;
+                               else
+                                       return new SqlDecimal ((double)x.Value);
+                       }
                }
 
                public static explicit operator SqlDecimal (SqlString x)
                {
-                       return Parse (x.Value);
+                       checked {
+                               return Parse (x.Value);
+                       }
                }
 
                public static implicit operator SqlDecimal (decimal x)
@@ -647,4 +1388,3 @@ namespace System.Data.SqlTypes
                #endregion
        }
 }
-