2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / corlib / System / Int32.cs
1 //
2 // System.Int32.cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) Ximian, Inc.  http://www.ximian.com
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Globalization;
31 using System.Threading;
32
33 namespace System {
34         
35         [Serializable]
36 #if NET_2_0
37         [System.Runtime.InteropServices.ComVisible (true)]
38 #endif
39         public struct Int32 : IFormattable, IConvertible, IComparable
40 #if NET_2_0
41                 , IComparable<Int32>, IEquatable <Int32>
42 #endif
43         {
44
45                 public const int MaxValue = 0x7fffffff;
46                 public const int MinValue = -2147483648;
47                 
48                 // This field is looked up by name in the runtime
49                 internal int m_value;
50
51                 public int CompareTo (object value)
52                 {
53                         if (value == null)
54                                 return 1;
55                         
56                         if (!(value is System.Int32))
57                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Int32"));
58
59                         int xv = (int) value;
60                         if (m_value == xv)
61                                 return 0;
62                         if (m_value > xv)
63                                 return 1;
64                         else
65                                 return -1;
66                 }
67
68                 public override bool Equals (object obj)
69                 {
70                         if (!(obj is System.Int32))
71                                 return false;
72
73                         return ((int) obj) == m_value;
74                 }
75
76                 public override int GetHashCode ()
77                 {
78                         return m_value;
79                 }
80
81 #if NET_2_0
82                 public int CompareTo (int value)
83                 {
84                         if (m_value == value)
85                                 return 0;
86                         if (m_value > value)
87                                 return 1;
88                         else
89                                 return -1;
90                 }
91
92                 public bool Equals (int obj)
93                 {
94                         return obj == m_value;
95                 }
96 #endif
97
98                 internal static bool ProcessTrailingWhitespace (bool tryParse, string s, int position, ref Exception exc)
99                 {
100                         int len = s.Length;
101                         
102                         for (int i = position; i < len; i++){
103                                 char c = s [i];
104                                 
105                                 if (!Char.IsWhiteSpace (c)){
106                                         if (!tryParse)
107                                                 exc = GetFormatException ();
108                                         return false;
109                                 }
110                         }
111                         return true;
112                 }
113
114                 internal static bool Parse (string s, bool tryParse, out int result, out Exception exc)
115                 {
116                         int val = 0;
117                         int len;
118                         int i, sign = 1;
119                         bool digits_seen = false;
120
121                         result = 0;
122                         exc = null;
123
124                         if (s == null) {
125                                 if (!tryParse)
126                                         exc = new ArgumentNullException ("s");
127                                 return false;
128                         }
129
130                         len = s.Length;
131
132                         char c;
133                         for (i = 0; i < len; i++){
134                                 c = s [i];
135                                 if (!Char.IsWhiteSpace (c))
136                                         break;
137                         }
138                         
139                         if (i == len) {
140                                 if (!tryParse)
141                                         exc = GetFormatException ();
142                                 return false;
143                         }
144
145                         c = s [i];
146                         if (c == '+')
147                                 i++;
148                         else if (c == '-'){
149                                 sign = -1;
150                                 i++;
151                         }
152                         
153                         for (; i < len; i++){
154                                 c = s [i];
155
156                                 if (c == '\0') {
157                                         i = len;
158                                         continue;
159                                 }
160                                 
161                                 if (c >= '0' && c <= '9'){
162                                         byte d = (byte) (c - '0');
163                                                 
164                                         if (val > (MaxValue/10))
165                                                 goto overflow;
166                                         
167                                         if (val == (MaxValue/10)){
168                                                 if ((d > (MaxValue % 10)) && (sign == 1 || (d > ((MaxValue % 10) + 1))))
169                                                         goto overflow;
170                                                 if (sign == -1)
171                                                         val = (val * sign * 10) - d;
172                                                 else
173                                                         val = (val * 10) + d;
174
175                                                 if (ProcessTrailingWhitespace (tryParse, s, i + 1, ref exc)){
176                                                         result = val;
177                                                         return true;
178                                                 }
179                                                 goto overflow;
180                                         } else 
181                                                 val = val * 10 + d;
182                                         
183                                         digits_seen = true;
184                                 } else if (!ProcessTrailingWhitespace (tryParse, s, i, ref exc))
185                                         return false;
186                         }
187                         if (!digits_seen) {
188                                 if (!tryParse)
189                                         exc = GetFormatException ();
190                                 return false;
191                         }
192
193                         if (sign == -1)
194                                 result = val * sign;
195                         else
196                                 result = val;
197
198                         return true;
199
200                 overflow:
201                         if (!tryParse)
202                                 exc = new OverflowException ("Value is too large");
203                         return false;
204                 }
205
206                 public static int Parse (string s, IFormatProvider provider)
207                 {
208                         return Parse (s, NumberStyles.Integer, provider);
209                 }
210
211                 public static int Parse (string s, NumberStyles style)
212                 {
213                         return Parse (s, style, null);
214                 }
215
216                 internal static bool CheckStyle (NumberStyles style, bool tryParse, ref Exception exc)
217                 {
218                         if ((style & NumberStyles.AllowHexSpecifier) != 0) {
219                                 NumberStyles ne = style ^ NumberStyles.AllowHexSpecifier;
220                                 if ((ne & NumberStyles.AllowLeadingWhite) != 0)
221                                         ne ^= NumberStyles.AllowLeadingWhite;
222                                 if ((ne & NumberStyles.AllowTrailingWhite) != 0)
223                                         ne ^= NumberStyles.AllowTrailingWhite;
224                                 if (ne != 0) {
225                                         if (!tryParse)
226                                                 exc = new ArgumentException (
227                                                         "With AllowHexSpecifier only " + 
228                                                         "AllowLeadingWhite and AllowTrailingWhite " + 
229                                                         "are permitted.");
230                                         return false;
231                                 }
232                         } else if ((uint) style > (uint) NumberStyles.Any){
233                                 if (!tryParse)
234                                         exc = new ArgumentException ("Not a valid number style");
235                                 return false;
236                         }
237
238                         return true;
239                 }
240                 
241                 internal static bool JumpOverWhite (ref int pos, string s, bool reportError, bool tryParse, ref Exception exc)
242                 {
243                         while (pos < s.Length && Char.IsWhiteSpace (s [pos]))
244                                 pos++;
245
246                         if (reportError && pos >= s.Length) {
247                                 if (!tryParse)
248                                         exc = GetFormatException ();
249                                 return false;
250                         }
251
252                         return true;
253                 }
254
255                 internal static void FindSign (ref int pos, string s, NumberFormatInfo nfi, 
256                                       ref bool foundSign, ref bool negative)
257                 {
258                         if ((pos + nfi.NegativeSign.Length) <= s.Length &&
259                                 s.IndexOf (nfi.NegativeSign, pos, nfi.NegativeSign.Length) == pos) {
260                                 negative = true;
261                                 foundSign = true;
262                                 pos += nfi.NegativeSign.Length;
263                         } 
264                         else if ((pos + nfi.PositiveSign.Length) < s.Length &&
265                                 s.IndexOf (nfi.PositiveSign, pos, nfi.PositiveSign.Length) == pos) {
266                                 negative = false;
267                                 pos += nfi.PositiveSign.Length;
268                                 foundSign = true;
269                         } 
270                 }
271
272                 internal static void FindCurrency (ref int pos,
273                                                  string s, 
274                                                  NumberFormatInfo nfi,
275                                                  ref bool foundCurrency)
276                 {
277                         if ((pos + nfi.CurrencySymbol.Length) <= s.Length &&
278                              s.Substring (pos, nfi.CurrencySymbol.Length) == nfi.CurrencySymbol) {
279                                 foundCurrency = true;
280                                 pos += nfi.CurrencySymbol.Length;
281                         } 
282                 }
283
284                 internal static bool FindExponent (ref int pos, string s)
285                 {
286                                 int i = s.IndexOfAny(new char [] {'e', 'E'}, pos);
287                                 if (i < 0)
288                                                 return false;
289                                 if (++i == s.Length)
290                                                 return false;
291                                 if (s [i] == '+' || s [i] == '-')
292                                                 if (++i == s.Length)
293                                                                 return false;
294                                 if (!Char.IsDigit (s [i]))
295                                                 return false;
296                                 for (; i < s.Length; ++i)
297                                                 if (!Char.IsDigit (s [i])) 
298                                                                 break;
299                                 pos = i;
300                                 return true;
301                 }
302
303                 internal static bool FindOther (ref int pos,
304                                               string s, 
305                                               string other)
306                 {
307                         if ((pos + other.Length) <= s.Length &&
308                              s.Substring (pos, other.Length) == other) {
309                                 pos += other.Length;
310                                 return true;
311                         } 
312
313                         return false;
314                 }
315
316                 internal static bool ValidDigit (char e, bool allowHex)
317                 {
318                         if (allowHex)
319                                 return Char.IsDigit (e) || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
320
321                         return Char.IsDigit (e);
322                 }
323                 
324                 internal static Exception GetFormatException ()
325                 {
326                         return new FormatException ("Input string was not in the correct format");
327                 }
328                 
329                 internal static bool Parse (string s, NumberStyles style, IFormatProvider fp, bool tryParse, out int result, out Exception exc)
330                 {
331                         result = 0;
332                         exc = null;
333
334                         if (s == null) {
335                                 if (!tryParse)
336                                         exc = new ArgumentNullException ();
337                                 return false;
338                         }
339
340                         if (s.Length == 0) {
341                                 if (!tryParse)
342                                         exc = GetFormatException ();
343                                 return false;
344                         }
345
346                         NumberFormatInfo nfi = null;
347                         if (fp != null) {
348                                 Type typeNFI = typeof (System.Globalization.NumberFormatInfo);
349                                 nfi = (NumberFormatInfo) fp.GetFormat (typeNFI);
350                         }
351                         if (nfi == null)
352                                 nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;
353
354                         if (!CheckStyle (style, tryParse, ref exc))
355                                 return false;
356
357                         bool AllowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0;
358                         bool AllowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0;
359                         bool AllowThousands = (style & NumberStyles.AllowThousands) != 0;
360                         bool AllowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) != 0;
361                         bool AllowParentheses = (style & NumberStyles.AllowParentheses) != 0;
362                         bool AllowTrailingSign = (style & NumberStyles.AllowTrailingSign) != 0;
363                         bool AllowLeadingSign = (style & NumberStyles.AllowLeadingSign) != 0;
364                         bool AllowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) != 0;
365                         bool AllowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) != 0;
366                         bool AllowExponent = (style & NumberStyles.AllowExponent) != 0;
367
368                         int pos = 0;
369
370                         if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
371                                 return false;
372
373                         bool foundOpenParentheses = false;
374                         bool negative = false;
375                         bool foundSign = false;
376                         bool foundCurrency = false;
377
378                         // Pre-number stuff
379                         if (AllowParentheses && s [pos] == '(') {
380                                 foundOpenParentheses = true;
381                                 foundSign = true;
382                                 negative = true; // MS always make the number negative when there parentheses
383                                                  // even when NumberFormatInfo.NumberNegativePattern != 0!!!
384                                 pos++;
385                                 if (AllowLeadingWhite && !!JumpOverWhite (ref pos, s, true, tryParse, ref exc))
386                                         return false;
387
388                                 if (s.Substring (pos, nfi.NegativeSign.Length) == nfi.NegativeSign) {
389                                         if (!tryParse)
390                                                 exc = GetFormatException ();
391                                         return false;
392                                 }
393                                 
394                                 if (s.Substring (pos, nfi.PositiveSign.Length) == nfi.PositiveSign) {
395                                         if (!tryParse)
396                                                 exc = GetFormatException ();
397                                         return false;
398                                 }
399                         }
400
401                         if (AllowLeadingSign && !foundSign) {
402                                 // Sign + Currency
403                                 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
404                                 if (foundSign) {
405                                         if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
406                                                 return false;
407                                         if (AllowCurrencySymbol) {
408                                                 FindCurrency (ref pos, s, nfi,
409                                                               ref foundCurrency);
410                                                 if (foundCurrency && AllowLeadingWhite &&
411                                                                 !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
412                                                         return false;
413                                         }
414                                 }
415                         }
416                         
417                         if (AllowCurrencySymbol && !foundCurrency) {
418                                 // Currency + sign
419                                 FindCurrency (ref pos, s, nfi, ref foundCurrency);
420                                 if (foundCurrency) {
421                                         if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
422                                                 return false;
423                                         if (foundCurrency) {
424                                                 if (!foundSign && AllowLeadingSign) {
425                                                         FindSign (ref pos, s, nfi, ref foundSign,
426                                                                   ref negative);
427                                                         if (foundSign && AllowLeadingWhite &&
428                                                                         !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
429                                                                 return false;
430                                                 }
431                                         }
432                                 }
433                         }
434
435                         int number = 0;
436                         int nDigits = 0;
437                         bool decimalPointFound = false;
438                         int digitValue;
439                         char hexDigit;
440                                 
441                         // Number stuff
442                         do {
443
444                                 if (!ValidDigit (s [pos], AllowHexSpecifier)) {
445                                         if (AllowThousands &&
446                                             FindOther (ref pos, s, nfi.NumberGroupSeparator))
447                                             continue;
448                                         else
449                                         if (!decimalPointFound && AllowDecimalPoint &&
450                                             FindOther (ref pos, s, nfi.NumberDecimalSeparator)) {
451                                             decimalPointFound = true;
452                                             continue;
453                                         }
454
455                                         break;
456                                 }
457                                 else if (AllowHexSpecifier) {
458                                         nDigits++;
459                                         hexDigit = s [pos++];
460                                         if (Char.IsDigit (hexDigit))
461                                                 digitValue = (int) (hexDigit - '0');
462                                         else if (Char.IsLower (hexDigit))
463                                                 digitValue = (int) (hexDigit - 'a' + 10);
464                                         else
465                                                 digitValue = (int) (hexDigit - 'A' + 10);
466
467                                         uint unumber = (uint)number;
468                                         if (tryParse){
469                                                 if ((unumber & 0xf0000000) != 0)
470                                                         return false;
471                                                 
472                                                 number = (int) (unumber * 16u + (uint) digitValue);
473                                         } else {
474                                                 number = (int)checked (unumber * 16u + (uint)digitValue);
475                                         }
476                                 }
477                                 else if (decimalPointFound) {
478                                         nDigits++;
479                                         // Allows decimal point as long as it's only 
480                                         // followed by zeroes.
481                                         if (s [pos++] != '0') {
482                                                 if (!tryParse)
483                                                         exc = new OverflowException ("Value too large or too " +
484                                                                         "small.");
485                                                 return false;
486                                         }
487                                 }
488                                 else {
489                                         nDigits++;
490
491                                         try {
492                                                 // Calculations done as negative
493                                                 // (abs (MinValue) > abs (MaxValue))
494                                                 number = checked (
495                                                         number * 10 - 
496                                                         (int) (s [pos++] - '0')
497                                                         );
498                                         } catch (OverflowException) {
499                                                 if (!tryParse)
500                                                         exc = new OverflowException ("Value too large or too " +
501                                                                         "small.");
502                                                 return false;
503                                         }
504                                 }
505                         } while (pos < s.Length);
506
507                         // Post number stuff
508                         if (nDigits == 0) {
509                                 if (!tryParse)
510                                         exc = GetFormatException ();
511                                 return false;
512                         }
513
514                         if (AllowExponent) 
515                                         FindExponent(ref pos, s);
516
517                         if (AllowTrailingSign && !foundSign) {
518                                 // Sign + Currency
519                                 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
520                                 if (foundSign) {
521                                         if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
522                                                 return false;
523                                         if (AllowCurrencySymbol)
524                                                 FindCurrency (ref pos, s, nfi,
525                                                               ref foundCurrency);
526                                 }
527                         }
528                         
529                         if (AllowCurrencySymbol && !foundCurrency) {
530                                 // Currency + sign
531                                 FindCurrency (ref pos, s, nfi, ref foundCurrency);
532                                 if (foundCurrency) {
533                                         if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
534                                                 return false;
535                                         if (!foundSign && AllowTrailingSign)
536                                                 FindSign (ref pos, s, nfi, ref foundSign,
537                                                           ref negative);
538                                 }
539                         }
540                         
541                         if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
542                                 return false;
543
544                         if (foundOpenParentheses) {
545                                 if (pos >= s.Length || s [pos++] != ')') {
546                                         if (!tryParse)
547                                                 exc = GetFormatException ();
548                                         return false;
549                                 }
550                                 if (AllowTrailingWhite && pos < s.Length &&
551                                                 !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
552                                         return false;
553                         }
554
555                         if (pos < s.Length && s [pos] != '\u0000') {
556                                 if (!tryParse)
557                                         exc = GetFormatException ();
558                                 return false;
559                         }
560                         
561                         if (!negative && !AllowHexSpecifier){
562                                 if (tryParse){
563                                         long lval = -number;
564
565                                         if (lval < MinValue || lval > MaxValue)
566                                                 return false;
567                                         number = (int) lval;
568                                 } else
569                                         number = checked (-number);
570                         }
571                         
572                         result = number;
573
574                         return true;
575                 }
576
577                 public static int Parse (string s) 
578                 {
579                         Exception exc;
580                         int res;
581
582                         if (!Parse (s, false, out res, out exc))
583                                 throw exc;
584
585                         return res;
586                 }
587
588                 public static int Parse (string s, NumberStyles style, IFormatProvider provider) 
589                 {
590                         Exception exc;
591                         int res;
592
593                         if (!Parse (s, style, provider, false, out res, out exc))
594                                 throw exc;
595
596                         return res;
597                 }
598
599 #if NET_2_0
600                 public static bool TryParse (string s, out int result) 
601                 {
602                         Exception exc;
603                         
604                         if (!Parse (s, true, out result, out exc)) {
605                                 result = 0;
606                                 return false;
607                         }
608
609                         return true;
610                 }
611
612                 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out int result) 
613                 {
614                         Exception exc;
615                         if (!Parse (s, style, provider, true, out result, out exc)) {
616                                 result = 0;
617                                 return false;
618                         }
619
620                         return true;
621                 }
622 #endif
623
624                 public override string ToString ()
625                 {
626                         return NumberFormatter.NumberToString (m_value, null);
627                 }
628
629                 public string ToString (IFormatProvider provider)
630                 {
631                         return NumberFormatter.NumberToString (m_value, provider);
632                 }
633
634                 public string ToString (string format)
635                 {
636                         return ToString (format, null);
637                 }
638
639                 public string ToString (string format, IFormatProvider provider)
640                 {
641                         return NumberFormatter.NumberToString (format, m_value, provider);
642                 }
643
644                 // =========== IConvertible Methods =========== //
645
646                 public TypeCode GetTypeCode ()
647                 {
648                         return TypeCode.Int32;
649                 }
650
651                 bool IConvertible.ToBoolean (IFormatProvider provider)
652                 {
653                         return System.Convert.ToBoolean (m_value);
654                 }
655
656                 byte IConvertible.ToByte (IFormatProvider provider)
657                 {
658                         return System.Convert.ToByte (m_value);
659                 }
660
661                 char IConvertible.ToChar (IFormatProvider provider)
662                 {
663                         return System.Convert.ToChar (m_value);
664                 }
665
666                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
667                 {
668                         return System.Convert.ToDateTime (m_value);
669                 }
670
671                 decimal IConvertible.ToDecimal (IFormatProvider provider)
672                 {
673                         return System.Convert.ToDecimal (m_value);
674                 }
675
676                 double IConvertible.ToDouble (IFormatProvider provider)
677                 {
678                         return System.Convert.ToDouble (m_value);
679                 }
680
681                 short IConvertible.ToInt16 (IFormatProvider provider)
682                 {
683                         return System.Convert.ToInt16 (m_value);
684                 }
685
686                 int IConvertible.ToInt32 (IFormatProvider provider)
687                 {
688                         return m_value;
689                 }
690
691                 long IConvertible.ToInt64 (IFormatProvider provider)
692                 {
693                         return System.Convert.ToInt64 (m_value);
694                 }
695
696 #if ONLY_1_1
697 #pragma warning disable 3019
698                 [CLSCompliant (false)]
699 #endif
700                 sbyte IConvertible.ToSByte (IFormatProvider provider)
701                 {
702                         return System.Convert.ToSByte (m_value);
703                 }
704 #if ONLY_1_1
705 #pragma warning restore 3019
706 #endif
707
708                 float IConvertible.ToSingle (IFormatProvider provider)
709                 {
710                         return System.Convert.ToSingle (m_value);
711                 }
712
713                 object IConvertible.ToType (Type type, IFormatProvider provider)
714                 {
715                         return System.Convert.ToType (m_value, type, provider, false);
716                 }
717
718 #if ONLY_1_1
719 #pragma warning disable 3019
720                 [CLSCompliant (false)]
721 #endif
722                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
723                 {
724                         return System.Convert.ToUInt16 (m_value);
725                 }
726 #if ONLY_1_1
727 #pragma warning restore 3019
728 #endif
729
730 #if ONLY_1_1
731 #pragma warning disable 3019
732                 [CLSCompliant (false)]
733 #endif
734                 uint IConvertible.ToUInt32 (IFormatProvider provider)
735                 {
736                         return System.Convert.ToUInt32 (m_value);
737                 }
738 #if ONLY_1_1
739 #pragma warning restore 3019
740 #endif
741
742 #if ONLY_1_1
743 #pragma warning disable 3019
744                 [CLSCompliant (false)]
745 #endif
746                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
747                 {
748                         return System.Convert.ToUInt64 (m_value);
749                 }
750 #if ONLY_1_1
751 #pragma warning restore 3019
752 #endif
753         }
754 }