2002-11-13 Tim Coleman <tim@timcoleman.com>
[mono.git] / mcs / class / Mono.Data.TdsClient / Mono.Data.TdsTypes / TdsDecimal.cs
1 //
2 // Mono.Data.TdsTypes.TdsDecimal
3 //
4 // Author:
5 //   Tim Coleman <tim@timcoleman.com>
6 //
7 // (C) Copyright Tim Coleman, 2002
8 //
9
10 using Mono.Data.TdsClient;
11 using System;
12 using System.Data.SqlTypes;
13 using System.Globalization;
14
15 namespace Mono.Data.TdsTypes {
16         public struct TdsDecimal : INullable, IComparable
17         {
18                 #region Fields
19
20                 int[] value;
21                 byte precision;
22                 byte scale;
23                 bool positive;
24
25                 private bool notNull;
26
27                 // borrowed from System.Decimal
28                 const int SCALE_SHIFT = 16;
29                 const int SIGN_SHIFT = 31;
30                 const int RESERVED_SS32_BITS = 0x7F00FFFF;
31
32                 public static readonly byte MaxPrecision = 38; 
33                 public static readonly byte MaxScale = 28;
34                 public static readonly TdsDecimal MaxValue = new TdsDecimal (79228162514264337593543950335.0);
35                 public static readonly TdsDecimal MinValue = new TdsDecimal (-79228162514264337593543950335.0);
36                 public static readonly TdsDecimal Null;
37
38                 #endregion
39
40                 #region Constructors
41
42                 public TdsDecimal (decimal value) 
43                 {
44                         int[] binData = Decimal.GetBits (value);
45
46                         this.precision = MaxPrecision; // this value seems unclear
47
48                         this.scale = (byte)(binData[3] >> SCALE_SHIFT);
49                         if (this.scale > MaxScale || (this.scale & RESERVED_SS32_BITS) != 0)
50                                 throw new ArgumentException(Locale.GetText ("Invalid scale"));
51
52                         this.positive = ((binData[3] >> SIGN_SHIFT) > 0);
53                         this.value = new int[4];
54                         this.value[0] = binData[0];
55                         this.value[1] = binData[1];
56                         this.value[2] = binData[2];
57                         this.value[3] = 0;
58                         notNull = true;
59                 }
60
61                 public TdsDecimal (double value) : this ((decimal)value) { }
62                 public TdsDecimal (int value) : this ((decimal)value) { }
63                 public TdsDecimal (long value) : this ((decimal)value) { }
64
65                 public TdsDecimal (byte bPrecision, byte bScale, bool fPositive, int[] bits) : this (bPrecision, bScale, fPositive, bits[0], bits[1], bits[2], bits[3]) { }
66
67                 public TdsDecimal (byte bPrecision, byte bScale, bool fPositive, int data1, int data2, int data3, int data4) 
68                 {
69                         this.precision = bPrecision;
70                         this.scale = bScale;
71                         this.positive = fPositive;
72                         this.value = new int[4];
73                         this.value[0] = data1;
74                         this.value[1] = data2;
75                         this.value[2] = data3;
76                         this.value[3] = data4;
77                         notNull = true;
78                 }
79
80                 #endregion
81
82                 #region Properties
83
84                 [MonoTODO]
85                 public byte[] BinData {
86                         get { throw new NotImplementedException (); }
87                 }
88
89                 public int[] Data { 
90                         get { 
91                                 if (this.IsNull)
92                                         throw new TdsNullValueException ();
93                                 else
94                                         return (value);
95                         }
96                 }
97
98                 public bool IsNull { 
99                         get { return !notNull; }
100                 }
101
102                 public bool IsPositive { 
103                         get { return positive; }
104                 }
105
106                 public byte Precision { 
107                         get { return precision; }
108                 }
109
110                 public byte Scale { 
111                         get { return scale; }
112                 }
113
114                 public decimal Value { 
115                         get { 
116                                 if (this.IsNull) 
117                                         throw new TdsNullValueException ();
118                                 else 
119                                         if (this.value[3] > 0)
120                                                 throw new OverflowException ();
121                                         else
122                                                 System.Console.WriteLine( "boo!" );
123                                                 return new decimal (value[0], value[1], value[2], !positive, scale);
124                         }
125                 }
126
127                 #endregion
128
129                 #region Methods
130
131                 [MonoTODO]
132                 public static TdsDecimal Abs (TdsDecimal n)
133                 {
134                         throw new NotImplementedException();
135                 }
136
137                 public static TdsDecimal Add (TdsDecimal x, TdsDecimal y)
138                 {
139                         return (x + y);
140                 }
141
142                 [MonoTODO]
143                 public static TdsDecimal AdjustScale (TdsDecimal n, int digits, bool fRound)
144                 {
145                         throw new NotImplementedException ();
146                 }
147
148                 [MonoTODO]
149                 public static TdsDecimal Ceiling (TdsDecimal n)
150                 {
151                         throw new NotImplementedException();
152                 }
153
154                 public int CompareTo (object value)
155                 {
156                         if (value == null)
157                                 return 1;
158                         else if (!(value is TdsDecimal))
159                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Data.TdsTypes.TdsDecimal"));
160                         else if (((TdsDecimal)value).IsNull)
161                                 return 1;
162                         else
163                                 return this.Value.CompareTo (((TdsDecimal)value).Value);
164                 }
165
166                 [MonoTODO]
167                 public static TdsDecimal ConvertToPrecScale (TdsDecimal n, int precision, int scale)
168                 {
169                         throw new NotImplementedException ();
170                 }
171
172                 public static TdsDecimal Divide (TdsDecimal x, TdsDecimal y)
173                 {
174                         return (x / y);
175                 }
176
177                 public override bool Equals (object value)
178                 {
179                         if (!(value is TdsDecimal))
180                                 return false;
181                         else
182                                 return (bool) (this == (TdsDecimal)value);
183                 }
184
185                 public static TdsBoolean Equals (TdsDecimal x, TdsDecimal y)
186                 {
187                         return (x == y);
188                 }
189
190                 [MonoTODO]
191                 public static TdsDecimal Floor (TdsDecimal n)
192                 {
193                         throw new NotImplementedException ();
194                 }
195
196                 public override int GetHashCode ()
197                 {
198                         return (int)this.Value;
199                 }
200
201                 public static TdsBoolean GreaterThan (TdsDecimal x, TdsDecimal y)
202                 {
203                         return (x > y);
204                 }
205
206                 public static TdsBoolean GreaterThanOrEqual (TdsDecimal x, TdsDecimal y)
207                 {
208                         return (x >= y);
209                 }
210
211                 public static TdsBoolean LessThan (TdsDecimal x, TdsDecimal y)
212                 {
213                         return (x < y);
214                 }
215
216                 public static TdsBoolean LessThanOrEqual (TdsDecimal x, TdsDecimal y)
217                 {
218                         return (x <= y);
219                 }
220
221                 public static TdsDecimal Multiply (TdsDecimal x, TdsDecimal y)
222                 {
223                         return (x * y);
224                 }
225
226                 public static TdsBoolean NotEquals (TdsDecimal x, TdsDecimal y)
227                 {
228                         return (x != y);
229                 }
230
231                 [MonoTODO]
232                 public static TdsDecimal Parse (string s)
233                 {
234                         throw new NotImplementedException ();
235                 }
236
237                 [MonoTODO]
238                 public static TdsDecimal Power (TdsDecimal n, double exp)
239                 {
240                         throw new NotImplementedException ();
241                 }
242
243                 [MonoTODO]
244                 public static TdsDecimal Round (TdsDecimal n, int position)
245                 {
246                         throw new NotImplementedException ();
247                 }
248
249                 [MonoTODO]
250                 public static TdsInt32 Sign (TdsDecimal n)
251                 {
252                         throw new NotImplementedException ();
253                 }
254
255                 public static TdsDecimal Subtract (TdsDecimal x, TdsDecimal y)
256                 {
257                         return (x - y);
258                 }
259
260                 public double ToDouble ()
261                 {
262                         return ((double)this.Value);
263                 }
264
265                 public TdsBoolean ToTdsBoolean ()
266                 {
267                         return ((TdsBoolean)this);
268                 }
269                 
270                 public TdsByte ToTdsByte ()
271                 {
272                         return ((TdsByte)this);
273                 }
274
275                 public TdsDouble ToTdsDouble ()
276                 {
277                         return ((TdsDouble)this);
278                 }
279
280                 public TdsInt16 ToTdsInt16 ()
281                 {
282                         return ((TdsInt16)this);
283                 }
284
285                 public TdsInt32 ToTdsInt32 ()
286                 {
287                         return ((TdsInt32)this);
288                 }
289
290                 public TdsInt64 ToTdsInt64 ()
291                 {
292                         return ((TdsInt64)this);
293                 }
294
295                 public TdsMoney ToTdsMoney ()
296                 {
297                         return ((TdsMoney)this);
298                 }
299
300                 public TdsSingle ToTdsSingle ()
301                 {
302                         return ((TdsSingle)this);
303                 }
304
305                 public TdsString ToTdsString ()
306                 {
307                         return ((TdsString)this);
308                 }
309
310                 public override string ToString ()
311                 {
312                         if (this.IsNull)
313                                 return String.Empty;
314                         else
315                                 return value.ToString ();
316                 }
317
318                 [MonoTODO]
319                 public static TdsDecimal Truncate (TdsDecimal n, int position)
320                 {
321                         throw new NotImplementedException ();
322                 }
323
324                 public static TdsDecimal operator + (TdsDecimal x, TdsDecimal y)
325                 {
326                         // if one of them is negative, perform subtraction
327                         if (x.IsPositive && !y.IsPositive) return x - y;
328                         if (y.IsPositive && !x.IsPositive) return y - x;
329                 
330                         // adjust the scale to the smaller of the two beforehand
331                         if (x.Scale > y.Scale)
332                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
333                         else if (y.Scale > x.Scale)
334                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
335
336                         // set the precision to the greater of the two
337                         byte resultPrecision;
338                         if (x.Precision > y.Precision)
339                                 resultPrecision = x.Precision;
340                         else
341                                 resultPrecision = y.Precision;
342                                 
343                         int[] xData = x.Data;
344                         int[] yData = y.Data;
345                         int[] resultBits = new int[4];
346
347                         ulong res; 
348                         ulong carry = 0;
349
350                         // add one at a time, and carry the results over to the next
351                         for (int i = 0; i < 4; i +=1)
352                         {
353                                 carry = 0;
354                                 res = (ulong)(xData[i]) + (ulong)(yData[i]) + carry;
355                                 if (res > Int32.MaxValue)
356                                 {
357                                         carry = res - Int32.MaxValue;
358                                         res = Int32.MaxValue;
359                                 }
360                                 resultBits [i] = (int)res;
361                         }
362
363                         // if we have carry left, then throw an exception
364                         if (carry > 0)
365                                 throw new OverflowException ();
366                         else
367                                 return new TdsDecimal (resultPrecision, x.Scale, x.IsPositive, resultBits);
368                 }
369
370                 [MonoTODO]
371                 public static TdsDecimal operator / (TdsDecimal x, TdsDecimal y)
372                 {
373                         throw new NotImplementedException ();
374                 }
375
376                 public static TdsBoolean operator == (TdsDecimal x, TdsDecimal y)
377                 {
378                         if (x.IsNull || y.IsNull) 
379                                 return TdsBoolean.Null;
380
381                         if (x.Scale > y.Scale)
382                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
383                         else if (y.Scale > x.Scale)
384                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
385
386                         for (int i = 0; i < 4; i += 1)
387                         {
388                                 if (x.Data[i] != y.Data[i])
389                                         return new TdsBoolean (false);
390                         }
391                         return new TdsBoolean (true);
392                 }
393
394                 public static TdsBoolean operator > (TdsDecimal x, TdsDecimal y)
395                 {
396                         if (x.IsNull || y.IsNull) 
397                                 return TdsBoolean.Null;
398
399                         if (x.Scale > y.Scale)
400                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
401                         else if (y.Scale > x.Scale)
402                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
403
404                         for (int i = 3; i >= 0; i -= 1)
405                         {
406                                 if (x.Data[i] == 0 && y.Data[i] == 0) 
407                                         continue;
408                                 else
409                                         return new TdsBoolean (x.Data[i] > y.Data[i]);
410                         }
411                         return new TdsBoolean (false);
412                 }
413
414                 public static TdsBoolean operator >= (TdsDecimal x, TdsDecimal y)
415                 {
416                         if (x.IsNull || y.IsNull) 
417                                 return TdsBoolean.Null;
418
419                         if (x.Scale > y.Scale)
420                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
421                         else if (y.Scale > x.Scale)
422                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
423
424                         for (int i = 3; i >= 0; i -= 1)
425                         {
426                                 if (x.Data[i] == 0 && y.Data[i] == 0) 
427                                         continue;
428                                 else
429                                         return new TdsBoolean (x.Data[i] >= y.Data[i]);
430                         }
431                         return new TdsBoolean (true);
432                 }
433
434                 public static TdsBoolean operator != (TdsDecimal x, TdsDecimal y)
435                 {
436                         if (x.IsNull || y.IsNull) 
437                                 return TdsBoolean.Null;
438
439                         if (x.Scale > y.Scale)
440                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
441                         else if (y.Scale > x.Scale)
442                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
443
444                         for (int i = 0; i < 4; i += 1)
445                         {
446                                 if (x.Data[i] != y.Data[i])
447                                         return new TdsBoolean (true);
448                         }
449                         return new TdsBoolean (false);
450                 }
451
452                 public static TdsBoolean operator < (TdsDecimal x, TdsDecimal y)
453                 {
454                         if (x.IsNull || y.IsNull) 
455                                 return TdsBoolean.Null;
456
457                         if (x.Scale > y.Scale)
458                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
459                         else if (y.Scale > x.Scale)
460                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
461
462                         for (int i = 3; i >= 0; i -= 1)
463                         {
464                                 if (x.Data[i] == 0 && y.Data[i] == 0) 
465                                         continue;
466
467                                 return new TdsBoolean (x.Data[i] < y.Data[i]);
468                         }
469                         return new TdsBoolean (false);
470                 }
471
472                 public static TdsBoolean operator <= (TdsDecimal x, TdsDecimal y)
473                 {
474                         if (x.IsNull || y.IsNull) 
475                                 return TdsBoolean.Null;
476
477                         if (x.Scale > y.Scale)
478                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
479                         else if (y.Scale > x.Scale)
480                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
481
482                         for (int i = 3; i >= 0; i -= 1)
483                         {
484                                 if (x.Data[i] == 0 && y.Data[i] == 0) 
485                                         continue;
486                                 else
487                                         return new TdsBoolean (x.Data[i] <= y.Data[i]);
488                         }
489                         return new TdsBoolean (true);
490                 }
491
492                 public static TdsDecimal operator * (TdsDecimal x, TdsDecimal y)
493                 {
494                         // adjust the scale to the smaller of the two beforehand
495                         if (x.Scale > y.Scale)
496                                 x = TdsDecimal.AdjustScale(x, y.Scale - x.Scale, true);
497                         else if (y.Scale > x.Scale)
498                                 y = TdsDecimal.AdjustScale(y, x.Scale - y.Scale, true);
499
500                         // set the precision to the greater of the two
501                         byte resultPrecision;
502                         if (x.Precision > y.Precision)
503                                 resultPrecision = x.Precision;
504                         else
505                                 resultPrecision = y.Precision;
506                                 
507                         int[] xData = x.Data;
508                         int[] yData = y.Data;
509                         int[] resultBits = new int[4];
510
511                         ulong res; 
512                         ulong carry = 0;
513
514                         // multiply one at a time, and carry the results over to the next
515                         for (int i = 0; i < 4; i +=1)
516                         {
517                                 carry = 0;
518                                 res = (ulong)(xData[i]) * (ulong)(yData[i]) + carry;
519                                 if (res > Int32.MaxValue)
520                                 {
521                                         carry = res - Int32.MaxValue;
522                                         res = Int32.MaxValue;
523                                 }
524                                 resultBits [i] = (int)res;
525                         }
526
527                         // if we have carry left, then throw an exception
528                         if (carry > 0)
529                                 throw new OverflowException ();
530                         else
531                                 return new TdsDecimal (resultPrecision, x.Scale, (x.IsPositive == y.IsPositive), resultBits);
532                                 
533                 }
534
535                 public static TdsDecimal operator - (TdsDecimal x, TdsDecimal y)
536                 {
537                         if (x.IsPositive && !y.IsPositive) return x + y;
538                         if (!x.IsPositive && y.IsPositive) return -(x + y);
539                         if (!x.IsPositive && !y.IsPositive) return y - x;
540
541                         // otherwise, x is positive and y is positive
542                         bool resultPositive = (bool)(x > y);
543                         int[] yData = y.Data;
544
545                         for (int i = 0; i < 4; i += 1) yData[i] = -yData[i];
546
547                         TdsDecimal yInverse = new TdsDecimal (y.Precision, y.Scale, y.IsPositive, yData);
548
549                         if (resultPositive)
550                                 return x + yInverse;
551                         else
552                                 return -(x + yInverse);
553                 }
554
555                 public static TdsDecimal operator - (TdsDecimal n)
556                 {
557                         return new TdsDecimal (n.Precision, n.Scale, !n.IsPositive, n.Data);
558                 }
559
560                 public static explicit operator TdsDecimal (TdsBoolean x)
561                 {
562                         if (x.IsNull) 
563                                 return Null;
564                         else
565                                 return new TdsDecimal ((decimal)x.ByteValue);
566                 }
567
568                 public static explicit operator Decimal (TdsDecimal n)
569                 {
570                         return n.Value;
571                 }
572
573                 public static explicit operator TdsDecimal (TdsDouble x)
574                 {
575                         if (x.IsNull) 
576                                 return Null;
577                         else
578                                 return new TdsDecimal ((decimal)x.Value);
579                 }
580
581                 public static explicit operator TdsDecimal (TdsSingle x)
582                 {
583                         if (x.IsNull) 
584                                 return Null;
585                         else
586                                 return new TdsDecimal ((decimal)x.Value);
587                 }
588
589                 [MonoTODO]
590                 public static explicit operator TdsDecimal (TdsString x)
591                 {
592                         throw new NotImplementedException ();
593                 }
594
595                 public static implicit operator TdsDecimal (decimal x)
596                 {
597                         return new TdsDecimal (x);
598                 }
599
600                 public static implicit operator TdsDecimal (TdsByte x)
601                 {
602                         if (x.IsNull) 
603                                 return Null;
604                         else
605                                 return new TdsDecimal ((decimal)x.Value);
606                 }
607
608                 public static implicit operator TdsDecimal (TdsInt16 x)
609                 {
610                         if (x.IsNull) 
611                                 return Null;
612                         else
613                                 return new TdsDecimal ((decimal)x.Value);
614                 }
615
616                 public static implicit operator TdsDecimal (TdsInt32 x)
617                 {
618                         if (x.IsNull) 
619                                 return Null;
620                         else
621                                 return new TdsDecimal ((decimal)x.Value);
622                 }
623
624                 public static implicit operator TdsDecimal (TdsInt64 x)
625                 {
626                         if (x.IsNull) 
627                                 return Null;
628                         else
629                                 return new TdsDecimal ((decimal)x.Value);
630                 }
631
632                 public static implicit operator TdsDecimal (TdsMoney x)
633                 {
634                         if (x.IsNull) 
635                                 return Null;
636                         else
637                                 return new TdsDecimal ((decimal)x.Value);
638                 }
639
640                 #endregion
641         }
642 }
643