DWC.Algorithms.NumberUtilities

/*
    Copyright (c) 2004 DigiTec Web Consultants, LLC.  All rights reserved.

    The use of this software is for test and performance purposes only.
    You may not use this software in any commercial applications without
    the express permission of the copyright holder.  You may add to or
    modify the code contained here-in to cause it to run slower without
    contacting the copyright holder, however, any attempts to make this
    code run faster should be documented on:
   
    http://weblogs.asp.net/justin_rogers/articles/104504.aspx
   
    I reserve the right to change or modify the publicly available version
    of this code at any time.  I will not provide version protection, so
    if you have reliance on a particular build of this software, then make
    your own back-ups.
   
    You must laugh, at least a little, when reading this licensing agreement,
    unless of course you don't have a sense of humor.  In all seriousness,
    excluding the laughter, laughter in itself does not void this license
    agreement, nor compromise it's ability to legally bind you.
   
    You must not remove this notice, or any other, from this software.
*/

/*
    I hate those things, but you have to have them.  Here is the documentation:
   
    DWC.Algorithms.NumberUtilities - This is where i'm hosting the best of the
    best in terms of performance for parsing strings to integers.
   
    DWC.Algorithms.NumberUtilitiesDeprecated - Everyone wants a piece of this
    pie, so I'm including examination of as many algorithms as possible.  These
    are all algorithms that work, but don't make the cut.
   
    Community - This is where I'll host code that I haven't reformated and have
    copy/pasted directly from the community.  This code can be sloppy, not work,
    or kick the pants off of the currently accepted algorithm.  They will
    remain parked here until they can either replace an accepted algorithm and
    have gone through a full sanity check pass, or they simply need to stay
    for historical correctness of the testing and performance procedures.
   
    ExecutableContent_TestClassRunner - When you compile this guy as an executable
    you probably want some numbers.  I'm including some of my test cases so you
    can see what I'm using to get numbers.  In general, as long as you don't change
    methods between testing two methods you should get results that can easily be
    compared.  Issues with the GC can often compromise good test results, but I've
    taken the liberty of trying to remove as many GC hiccups, burps, and coughs as
    possible.
   
    Test Builds:
        csc /t:exe /unsafe /optimize+ {source}

    Test Library:
        csc /t:library /unsafe /optimize+ {source}

    Release Builds:
        // Remove the NumberUtilitiesDeprecated class
        // Remove the Community class
        csc /t:exe /optimize+ {source}
       
    Release Library:
        // Remove the NumberUtilitiesDeprecated class
        // Remove the Community class
        // Remove the ExecutableContent_TestClassRunner class
        csc /t:library /optimize+ {source}
*/

using System;
using System.Globalization;
using System.Runtime.InteropServices;
using DWC.Algorithms;

namespace DWC.Algorithms {
    public sealed class NumberUtilities {
        private const sbyte   d1    = 1;
        private const sbyte   d2    = 10;
        private const sbyte   d3    = 100;
        private const short d4      = 1000;
        private const short d5      = 10000;
        private const int  d6       = 100000;
        private const int  d7       = 1000000;
        private const int  d8       = 10000000;
        private const int  d9       = 100000000;
        private const int  d10      = 1000000000;
        private const long d11      = 10000000000;
        private const long d12      = 100000000000;
        private const long d13      = 1000000000000;
        private const long d14      = 10000000000000;
        private const long d15      = 100000000000000;
        private const long d16      = 1000000000000000;
        private const long d17      = 10000000000000000;
        private const long d18      = 100000000000000000;
        private const long d19      = 1000000000000000000;
       
        public static readonly int SByteAllowableChars   = (int) Math.Ceiling(Math.Log10(SByte.MaxValue));
        public static readonly int ByteAllowableChars   = (int) Math.Ceiling(Math.Log10(Byte.MaxValue));
        public static readonly int Int16AllowableChars  = (int) Math.Ceiling(Math.Log10(Int16.MaxValue));
        public static readonly int UInt16AllowableChars  = (int) Math.Ceiling(Math.Log10(UInt16.MaxValue));
        public static readonly int Int32AllowableChars  = (int) Math.Ceiling(Math.Log10(Int32.MaxValue));
        public static readonly int UInt32AllowableChars  = (int) Math.Ceiling(Math.Log10(UInt32.MaxValue));

       
#region Upgrade these to CLR types
        public static readonly int longAllowableChars   = (int) Math.Ceiling(Math.Log10(Int64.MaxValue));
#endregion
        public static readonly int ByteLastChar         = (int) Byte.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int SByteLastChar        = (int) SByte.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';

        public static readonly int shortLastCharPos     = (int) Int16.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int shortLastCharNeg     = (int) Int16.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';
        public static readonly int intLastCharPos       = (int) Int32.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int intLastCharNeg       = (int) Int32.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';
        public static readonly int longLastCharPos      = (int) Int64.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int longLastCharNeg      = (int) Int64.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';
       
        public static readonly int maxAllowableChars    = Int32AllowableChars;
        public static readonly int maxLastCharPos       = intLastCharPos;
        public static readonly int maxLastCharNeg       = intLastCharNeg;

        private NumberUtilities() {
        }
       
        public static bool TryParseByte(string integer, out Byte value) {
            int range = integer.Length;
            int parseBegin = range - 1;
            value = 0;

            if ( range > ByteAllowableChars || range == 0 ) {
                return false;
            }

            if ( parseBegin >= 0 ) {
                ushort accumulator = (ushort) (integer[parseBegin--] - '0');
               
                if ( accumulator > 9 ) { return false; }

                value += (Byte) (accumulator * d1);
                if ( parseBegin >= 0 ) {
                    accumulator = (ushort) (integer[parseBegin--] - '0');

                    if ( accumulator > 9 ) { return false; }

                    value += (Byte) (accumulator * d2);
                    if ( parseBegin >= 0 ) {
                        accumulator = (ushort) (integer[parseBegin--] - '0');

                        if ( accumulator > ByteLastChar ) { return false; }

                        Byte preValue = value;
                        value += (Byte) (accumulator * d3);
                        if ( preValue > value ) {
                            return false;
                        }
                    }
                }
            }
           
            return true;
        }

        public static bool TryParseSByte(string integer, bool allowneg, out SByte value) {
            int range = integer.Length;
            int parseEnd = 0;
            int parseBegin = range - 1;
            value = 0;

            if ( range > 0 && integer[0] == '-' ) {
                if ( !allowneg ) {
                    return false;
                }
                parseEnd++;
                range--;
            }

            if ( range > SByteAllowableChars || range == 0 ) {
                return false;
            }

            if ( parseBegin >= parseEnd ) {
                ushort accumulator = (ushort) (integer[parseBegin--] - '0');
               
                if ( accumulator > 9 ) { return false; }

                value += (SByte) (accumulator * d1);
                if ( parseBegin >= parseEnd ) {
                    accumulator = (ushort) (integer[parseBegin--] - '0');

                    if ( accumulator > 9 ) { return false; }

                    value += (SByte) (accumulator * d2);
                    if ( parseBegin >= parseEnd ) {
                        accumulator = (ushort) (integer[parseBegin--] - '0');

                        if ( accumulator > SByteLastChar ) { return false; }

                        value += (SByte) (accumulator * d3);
                    }
                }
            }
           
            if ( value == SByte.MinValue ) {
                // Do nothing, we wrapped and this is a negative
                // number anyway.  This only happens when you
                // specify a negative value of size MinValue
                // within the string
            } else if ( value < 0 ) {
                // We overflowed!  This'll happen if you specify
                // an integer value too large that isn't actually
                // a negative value of size MinValue
                return false;
            } else if (parseEnd == 1) {
                value = (SByte) (-value); // invert to negative
            }

            return true;
        }

        public static bool TryParseInt16(string integer, bool allowneg, out Int16 value) {
            int range = integer.Length;
            int parseEnd = 0;
            int parseBegin = range - 1;
            value = 0;

            if ( range > 0 && integer[0] == '-' ) {
                if ( !allowneg ) {
                    return false;
                }
                parseEnd++;
                range--;
            }

            if ( range > Int16AllowableChars || range == 0 ) {
                return false;
            }

            if ( parseBegin >= parseEnd ) {
                ushort accumulator = (ushort) (integer[parseBegin--] - '0');
               
                if ( accumulator > 9 ) { return false; }

                value += (short) (accumulator * d1);
                if ( parseBegin >= parseEnd ) {
                    accumulator = (ushort) (integer[parseBegin--] - '0');

                    if ( accumulator > 9 ) { return false; }

                    value += (short) (accumulator * d2);
                    if ( parseBegin >= parseEnd ) {
                        accumulator = (ushort) (integer[parseBegin--] - '0');

                        if ( accumulator > 9 ) { return false; }

                        value += (short) (accumulator * d3);
                        if ( parseBegin >= parseEnd ) {
                            accumulator = (ushort) (integer[parseBegin--] - '0');

                            if ( accumulator > 9 ) { return false; }

                            value += (short) (accumulator * d4);
                            if ( parseBegin >= parseEnd ) {
                                accumulator = (ushort) (integer[parseBegin--] - '0');

                                if ( accumulator > shortLastCharNeg ) { return false; }

                                value += (short) (accumulator * d5);
                            }
                        }
                    }
                }
            }
           
            if ( value == Int16.MinValue ) {
                // Do nothing, we wrapped and this is a negative
                // number anyway.  This only happens when you
                // specify a negative value of size MinValue
                // within the string
            } else if ( value < 0 ) {
                // We overflowed!  This'll happen if you specify
                // an integer value too large that isn't actually
                // a negative value of size MinValue
                return false;
            } else if (parseEnd == 1) {
                value = (short) -value; // invert to negative
            }

            return true;
        }

        public static bool TryParseInt32(string integer, bool allowneg, out Int32 value) {
            int range = integer.Length;
            int parseEnd = 0;
            int parseBegin = range - 1;
            value = 0;

            if ( range > 0 && integer[0] == '-' ) {
                if ( !allowneg ) {
                    return false;
                }
                parseEnd++;
                range--;
            }

            if ( range > Int32AllowableChars || range == 0 ) {
                return false;
            }

            /* METHOD 1...  This is actually pretty good
            ushort accumulator1    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator2    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator3    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator4    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator5    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator6    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator7    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator8    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator9    = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
            ushort accumulator10   = (parseBegin >= parseEnd) ? (ushort) (integer[parseBegin--] - '0') : (ushort) 0;
           
            if (
                accumulator1  > 9  ||
                accumulator2  > 9  ||
                accumulator3  > 9  ||
                accumulator4  > 9  ||
                accumulator5  > 9  ||
                accumulator6  > 9  ||
                accumulator7  > 9  ||
                accumulator8  > 9  ||
                accumulator9  > 9  ||
                accumulator10 > 9  ){
                return false;
            }
            */
           
            /* METHOD 2...  This is faster than 1 so far
            ushort accumulator1 = 0;
            ushort accumulator2 = 0;
            ushort accumulator3 = 0;
            ushort accumulator4 = 0;
            ushort accumulator5 = 0;
            ushort accumulator6 = 0;
            ushort accumulator7 = 0;
            ushort accumulator8 = 0;
            ushort accumulator9 = 0;
            ushort accumulator10 = 0;
           
            if ( parseBegin >= parseEnd ) {
                accumulator1 = (ushort) (integer[parseBegin--] - '0');
               
                if ( accumulator1 > 9 ) { return false; }

                if ( parseBegin >= parseEnd ) {
                    accumulator2 = (ushort) (integer[parseBegin--] - '0');

                    if ( accumulator2 > 9 ) { return false; }

                    if ( parseBegin >= parseEnd ) {
                        accumulator3 = (ushort) (integer[parseBegin--] - '0');

                        if ( accumulator3 > 9 ) { return false; }

                        if ( parseBegin >= parseEnd ) {
                            accumulator4 = (ushort) (integer[parseBegin--] - '0');

                            if ( accumulator4 > 9 ) { return false; }

                            if ( parseBegin >= parseEnd ) {
                                accumulator5 = (ushort) (integer[parseBegin--] - '0');

                                if ( accumulator5 > 9 ) { return false; }

                                if ( parseBegin >= parseEnd ) {
                                    accumulator6 = (ushort) (integer[parseBegin--] - '0');

                                    if ( accumulator6 > 9 ) { return false; }

                                    if ( parseBegin >= parseEnd ) {
                                        accumulator7 = (ushort) (integer[parseBegin--] - '0');

                                        if ( accumulator7 > 9 ) { return false; }

                                        if ( parseBegin >= parseEnd ) {
                                            accumulator8 = (ushort) (integer[parseBegin--] - '0');

                                            if ( accumulator8 > 9 ) { return false; }

                                            if ( parseBegin >= parseEnd ) {
                                                accumulator9 = (ushort) (integer[parseBegin--] - '0');

                                                if ( accumulator9 > 9 ) { return false; }

                                                if ( parseBegin >= parseEnd ) {
                                                    accumulator10 = (ushort) (integer[parseBegin--] - '0');

                                                    if ( accumulator10 > 9 ) { return false; }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
           
           
           
            // Process the last character with overflow protection
            if ( range == maxAllowableChars ) {
                if ( parseEnd == 1 && accumulator10 > maxLastCharNeg ) {
                    return false;
                } else {
                    if ( accumulator10 > maxLastCharPos ) {
                        return false;
                    }
                }
                // A small perf increase to get rid of 1 of our
                // mults
                value = accumulator10 * d10;
            }
           
            value +=
                accumulator1 * d1 +
                accumulator2 * d2 +
                accumulator3 * d3 +
                accumulator4 * d4 +
                accumulator5 * d5 +
                accumulator6 * d6 +
                accumulator7 * d7 +
                accumulator8 * d8 +
                accumulator9 * d9;
            */

            /* METHOD 3...  Probably the fastest, but oh so ugly */
            if ( parseBegin >= parseEnd ) {
                ushort accumulator = (ushort) (integer[parseBegin--] - '0');
               
                if ( accumulator > 9 ) { return false; }

                value += accumulator * d1;
                if ( parseBegin >= parseEnd ) {
                    accumulator = (ushort) (integer[parseBegin--] - '0');

                    if ( accumulator > 9 ) { return false; }

                    value += accumulator * d2;
                    if ( parseBegin >= parseEnd ) {
                        accumulator = (ushort) (integer[parseBegin--] - '0');

                        if ( accumulator > 9 ) { return false; }

                        value += accumulator * d3;
                        if ( parseBegin >= parseEnd ) {
                            accumulator = (ushort) (integer[parseBegin--] - '0');

                            if ( accumulator > 9 ) { return false; }

                            value += accumulator * d4;
                            if ( parseBegin >= parseEnd ) {
                                accumulator = (ushort) (integer[parseBegin--] - '0');

                                if ( accumulator > 9 ) { return false; }

                                value += accumulator * d5;
                                if ( parseBegin >= parseEnd ) {
                                    accumulator = (ushort) (integer[parseBegin--] - '0');

                                    if ( accumulator > 9 ) { return false; }

                                    value += accumulator * d6;
                                    if ( parseBegin >= parseEnd ) {
                                        accumulator = (ushort) (integer[parseBegin--] - '0');

                                        if ( accumulator > 9 ) { return false; }

                                        value += accumulator * d7;
                                        if ( parseBegin >= parseEnd ) {
                                            accumulator = (ushort) (integer[parseBegin--] - '0');

                                            if ( accumulator > 9 ) { return false; }

                                            value += accumulator * d8;
                                            if ( parseBegin >= parseEnd ) {
                                                accumulator = (ushort) (integer[parseBegin--] - '0');

                                                if ( accumulator > 9 ) { return false; }

                                                value += accumulator * d9;
                                                if ( parseBegin >= parseEnd ) {
                                                    accumulator = (ushort) (integer[parseBegin--] - '0');

                                                    if ( accumulator > maxLastCharNeg ) { return false; }
                                                   
                                                    value += accumulator * d10;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
           
            if ( value == Int32.MinValue ) {
                // Do nothing, we wrapped and this is a negative
                // number anyway.  This only happens when you
                // specify a negative value of size MinValue
                // within the string
            } else if ( value < 0 ) {
                // We overflowed!  This'll happen if you specify
                // an integer value too large that isn't actually
                // a negative value of size MinValue
                return false;
            } else if (parseEnd == 1) {
                value = -value; // invert to negative
            }

            return true;
        }
    }

    public unsafe sealed class NumberUtilitiesDeprecated {
        private static readonly long[,] preMultiplied2D = new long[,]
        {
            {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
            {0, 10, 20, 30, 40, 50, 60, 70, 80, 90},
            {0, 100, 200, 300, 400, 500, 600, 700, 800, 900},
            {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000},
            {0, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000}
        };

        private static readonly long[] preMultiplied1D = new long[]
        {
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
            0, 10, 20, 30, 40, 50, 60, 70, 80, 90,
            0, 100, 200, 300, 400, 500, 600, 700, 800, 900,
            0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000,
            0, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000
        };
        private static GCHandle preMultiplied1DGCHandle;
        private static long* preMultiplied1DPointer;

        private static readonly ushort[] Int16Max = new ushort[] { 9, 9, 9, 9, 3 };
        private static GCHandle Int16MaxGCHandle;
        private static ushort* Int16MaxPointer;

        public static readonly int SByteAllowableChars   = (int) Math.Ceiling(Math.Log10(SByte.MaxValue));
        public static readonly int ByteAllowableChars   = (int) Math.Ceiling(Math.Log10(Byte.MaxValue));
        public static readonly int Int16AllowableChars  = (int) Math.Ceiling(Math.Log10(Int16.MaxValue));
        public static readonly int UInt16AllowableChars  = (int) Math.Ceiling(Math.Log10(UInt16.MaxValue));
       
#region Upgrade these to CLR types
        public static readonly int intAllowableChars    = (int) Math.Ceiling(Math.Log10(Int32.MaxValue));
        public static readonly int longAllowableChars   = (int) Math.Ceiling(Math.Log10(Int64.MaxValue));
#endregion
        public static readonly int ByteLastChar         = (int) Byte.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int SByteLastChar        = (int) SByte.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';

        public static readonly int shortLastCharPos     = (int) Int16.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int shortLastCharNeg     = (int) Int16.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';
        public static readonly int intLastCharPos       = (int) Int32.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int intLastCharNeg       = (int) Int32.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';
        public static readonly int longLastCharPos      = (int) Int64.MaxValue.ToString(CultureInfo.InvariantCulture)[0] - '0';
        public static readonly int longLastCharNeg      = (int) Int64.MinValue.ToString(CultureInfo.InvariantCulture)[1] - '0';
       
        public static readonly int maxAllowableChars    = intAllowableChars;
        public static readonly int maxLastCharPos       = intLastCharPos;
        public static readonly int maxLastCharNeg       = intLastCharNeg;

        static NumberUtilitiesDeprecated() {
            /* Used by TryParseIn16_Elegance Method 1 */
            preMultiplied1DGCHandle = GCHandle.Alloc(preMultiplied1D, GCHandleType.Pinned);
            preMultiplied1DPointer = (long*) Marshal.UnsafeAddrOfPinnedArrayElement(preMultiplied1D, 0);
            Int16MaxGCHandle = GCHandle.Alloc(Int16Max, GCHandleType.Pinned);
            Int16MaxPointer = (ushort*) Marshal.UnsafeAddrOfPinnedArrayElement(Int16Max, 0);
        }

        public static bool TryParseInt(string integer, bool allowneg, out int value) {
            int parseEnd = 0;
            int parseBegin = integer.Length - 1;
            int range = parseBegin - parseEnd + 1;
            int mult = 1; value = 0;

            if ( range > 0 && integer[0] == '-' ) {
                if ( !allowneg ) {
                    return false;
                }
                parseEnd++;
                range = parseBegin - parseEnd + 1;
            }

            if ( range > maxAllowableChars || range == 0 ) {
                return false;
            } else if ( range == maxAllowableChars ) {
                parseEnd++;
            }

            for(int i = parseBegin; i >= parseEnd; i--) {
                int val = (int) (integer[i] - '0');
                switch(val) {
                    case 0:
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                    case 9:
                        value += val * mult;
                        mult *= 10;
                        break;
                    default:
                        return false;
                }
            }

            // Process the last character with overflow protection
            if ( range == maxAllowableChars ) {
                int val = (int) (integer[parseEnd-1] - '0');

                // Pos testing
                if ( parseEnd == 1 && val > maxLastCharPos ) {
                    return false;
                } else {
                    // Neg testing
                    if ( parseEnd == 2 && val > maxLastCharNeg ) {
                        return false;
                    }
                }
                value += val * mult;
            }

            if ( value == Int32.MinValue ) {
                // Do nothing, we wrapped and this is a negative
                // number anyway.  This only happens when you
                // specify a negative value of size MinValue
                // within the string
            } else if ( value < 0 ) {
                // We overflowed!  This'll happen if you specify
                // an integer value too large that isn't actually
                // a negative value of size MinValue
                return false;
            } else if (parseEnd == 1 || (range == maxAllowableChars && parseEnd == 2)) {
                value = -value; // invert to negative
            }

            return true;
        }

        public static unsafe bool TryParseInt16_Elegance(string integer, bool allowneg, out Int16 value) {
            int range = integer.Length;

            fixed(char* intString = integer) {
                char* parseEnd = intString;
                char* parseBegin = intString + range - 1;
                value = 0;

                if ( range > 0 && *parseEnd == '-' ) {
                    if ( !allowneg ) {
                        return false;
                    }
                    parseEnd++;
                    range--;;
                }

                if ( range > Int16AllowableChars || range == 0 ) {
                    return false;
                }

                long* basedvalue = preMultiplied1DPointer;
                ushort* validator = Int16MaxPointer;
                while(parseBegin >= parseEnd) {
                    ushort accumulator = (ushort) (*parseBegin-- - '0');
                    if ( accumulator > *validator++ ) { return false; }

                    value += (short) *(basedvalue+accumulator);
                    basedvalue+=10;
                }
            }

            /* Method 2, note we don't use the preCalced GC Pins here
            fixed(long* a = preMultiplied1D) {
                fixed(ushort* b = Int16Max) {
                    long* basedvalue = a;
                    ushort* validator = b;
                    while(parseBegin >= parseEnd) {
                        ushort accumulator = (ushort) (integer[parseBegin--] - '0');
                        if ( accumulator > *validator++ ) { return false; }

                        value += (short) *(basedvalue+accumulator);
                        basedvalue+=10;
                    }
                }
            }
            */

            if ( value == Int16.MinValue ) {
                // Do nothing, we wrapped and this is a negative
                // number anyway.  This only happens when you
                // specify a negative value of size MinValue
                // within the string
            } else if ( value < 0 ) {
                // We overflowed!  This'll happen if you specify
                // an integer value too large that isn't actually
                // a negative value of size MinValue
                return false;
            } else if (range < integer.Length) {
                value = (short) -value; // invert to negative
            }

            return true;
        }
       
        public static bool TryParseInt16_UseArray(string integer, bool allowneg, out Int16 value) {
            int parseEnd = 0;
            int parseBegin = integer.Length - 1;
            int range = parseBegin - parseEnd + 1;
            value = 0;

            if ( range > 0 && integer[0] == '-' ) {
                if ( !allowneg ) {
                    return false;
                }
                parseEnd++;
                range = parseBegin - parseEnd + 1;
            }

            if ( range > Int16AllowableChars || range == 0 ) {
                return false;
            }
           
            int x = 0;
           
            if ( parseBegin >= parseEnd ) {
                ushort accumulator = (ushort) (integer[parseBegin--] - '0');
               
                if ( accumulator > 9 ) { return false; }

                value += (short) preMultiplied1D[x + accumulator];
                x+= 10;
               
                if ( parseBegin >= parseEnd ) {
                    accumulator = (ushort) (integer[parseBegin--] - '0');

                    if ( accumulator > 9 ) { return false; }

                    value += (short) preMultiplied1D[x + accumulator];
                    x+= 10;
                    if ( parseBegin >= parseEnd ) {
                        accumulator = (ushort) (integer[parseBegin--] - '0');

                        if ( accumulator > 9 ) { return false; }

                        value += (short) preMultiplied1D[x + accumulator];
                        x+= 10;
                        if ( parseBegin >= parseEnd ) {
                            accumulator = (ushort) (integer[parseBegin--] - '0');

                            if ( accumulator > 9 ) { return false; }

                            value += (short) preMultiplied1D[x + accumulator];
                            x+= 10;
                            if ( parseBegin >= parseEnd ) {
                                accumulator = (ushort) (integer[parseBegin--] - '0');

                                if ( accumulator > shortLastCharNeg ) { return false; }

                                value += (short) preMultiplied1D[x + accumulator];
                            }
                        }
                    }
                }
            }
           
            if ( value == Int16.MinValue ) {
                // Do nothing, we wrapped and this is a negative
                // number anyway.  This only happens when you
                // specify a negative value of size MinValue
                // within the string
            } else if ( value < 0 ) {
                // We overflowed!  This'll happen if you specify
                // an integer value too large that isn't actually
                // a negative value of size MinValue
                return false;
            } else if (parseEnd == 1) {
                value = (short) -value; // invert to negative
            }

            return true;
        }
    }
}

public class ExecutableContent_TestClassRunner {
    private static void SanityCheck(bool fullCheckLight, bool fullCheckHeavy) {
        Byte retValByte;
        SByte retValSByte;
        Int16 retValInt16;
        UInt16 retValUInt16;
        Int32 retValInt32;
        UInt32 retValUInt32;
       
        bool passFullCheck = true;
        bool[] expectedOutputSigned = new bool[] {
            false, false, false,                    /* Wrapping test */
            true, true, true, true,                 /* Min/Max tests */
            false, false, false,                    /* Extra large negatives */
            true, true, true, true, true, true      /* Within range negatives */
        };
        bool[] expectedOutputUnsigned = new bool[] {
            false, false, false,                    /* Wrapping test */
            true, true, true, true,                 /* Min/Max tests */
            true, true, true, true, true, true      /* Within range negatives */
        };
       
        Console.WriteLine("*** Community.TryParseInt32 ***");
        Console.WriteLine(Community.TryParseInt("9999999999", out retValInt32));
        Console.WriteLine(Community.TryParseInt("8888888888", out retValInt32));
        Console.WriteLine(Community.TryParseInt("7777777777", out retValInt32));

        Console.WriteLine(Community.TryParseInt(Int32.MaxValue.ToString(), out retValInt32));
        Console.WriteLine(retValInt32 == Int32.MaxValue);
        Console.WriteLine(Community.TryParseInt(Int32.MinValue.ToString(), out retValInt32));
        Console.WriteLine(retValInt32 == Int32.MinValue);

        Console.WriteLine(Community.TryParseInt("-7777777777", out retValInt32));
        Console.WriteLine(Community.TryParseInt("-8888888888", out retValInt32));
        Console.WriteLine(Community.TryParseInt("-9999999999", out retValInt32));

        Console.WriteLine(Community.TryParseInt("-999999999", out retValInt32));
        Console.WriteLine(retValInt32 == -999999999);
        Console.WriteLine(Community.TryParseInt("-99999999", out retValInt32));
        Console.WriteLine(retValInt32 == -99999999);
        Console.WriteLine(Community.TryParseInt("-9999999", out retValInt32));
        Console.WriteLine(retValInt32 == -9999999);

        Console.WriteLine("*** TryParseByte ***");
        Console.WriteLine(NumberUtilities.TryParseByte("999", out retValByte));
        Console.WriteLine(NumberUtilities.TryParseByte("888", out retValByte));
        Console.WriteLine(NumberUtilities.TryParseByte("777", out retValByte));

        Console.WriteLine(NumberUtilities.TryParseByte(Byte.MaxValue.ToString(), out retValByte));
        Console.WriteLine(retValByte == Byte.MaxValue);
        Console.WriteLine(NumberUtilities.TryParseByte(Byte.MinValue.ToString(), out retValByte));
        Console.WriteLine(retValByte == Byte.MinValue);

        Console.WriteLine(NumberUtilities.TryParseByte("-777", out retValByte));
        Console.WriteLine(NumberUtilities.TryParseByte("-888", out retValByte));
        Console.WriteLine(NumberUtilities.TryParseByte("-999", out retValByte));

        passFullCheck = true;
        for(int i = Byte.MinValue; i <= Byte.MaxValue; i++) {
            if ( !NumberUtilities.TryParseByte(i.ToString(), out retValByte) ) {
                passFullCheck = false;
            } else {
                if ( i != retValByte ) {
                    passFullCheck = false;
                }
            }
        }
        Console.WriteLine("Pass Full Check: {0}", passFullCheck);

        Console.WriteLine("*** TryParseSByte ***");
        Console.WriteLine(NumberUtilities.TryParseSByte("999", true, out retValSByte));
        Console.WriteLine(NumberUtilities.TryParseSByte("888", true, out retValSByte));
        Console.WriteLine(NumberUtilities.TryParseSByte("777", true, out retValSByte));

        Console.WriteLine(NumberUtilities.TryParseSByte(SByte.MaxValue.ToString(), true, out retValSByte));
        Console.WriteLine(retValSByte == SByte.MaxValue);
        Console.WriteLine(NumberUtilities.TryParseSByte(SByte.MinValue.ToString(), true, out retValSByte));
        Console.WriteLine(retValSByte == SByte.MinValue);

        Console.WriteLine(NumberUtilities.TryParseSByte("-777", true, out retValSByte));
        Console.WriteLine(NumberUtilities.TryParseSByte("-888", true, out retValSByte));
        Console.WriteLine(NumberUtilities.TryParseSByte("-999", true, out retValSByte));

        Console.WriteLine(NumberUtilities.TryParseSByte("-99", true, out retValSByte));
        Console.WriteLine(retValSByte == -99);
        Console.WriteLine(NumberUtilities.TryParseSByte("-99", true, out retValSByte));
        Console.WriteLine(retValSByte == -99);
        Console.WriteLine(NumberUtilities.TryParseSByte("-9", true, out retValSByte));
        Console.WriteLine(retValSByte == -9);
       
        passFullCheck = true;
        for(int i = SByte.MinValue; i <= SByte.MaxValue; i++) {
            if ( !NumberUtilities.TryParseSByte(i.ToString(), true, out retValSByte) ) {
                passFullCheck = false;
            } else {
                if ( i != retValSByte ) {
                    passFullCheck = false;
                }
            }
        }
        Console.WriteLine("Pass Full Check: {0}", passFullCheck);

        Console.WriteLine("*** TryParseInt16 ***");
        Console.WriteLine(NumberUtilities.TryParseInt16("99999", true, out retValInt16));
        Console.WriteLine(NumberUtilities.TryParseInt16("88888", true, out retValInt16));
        Console.WriteLine(NumberUtilities.TryParseInt16("77777", true, out retValInt16));

        Console.WriteLine(NumberUtilities.TryParseInt16(Int16.MaxValue.ToString(), true, out retValInt16));
        Console.WriteLine(retValInt16 == Int16.MaxValue);
        Console.WriteLine(NumberUtilities.TryParseInt16(Int16.MinValue.ToString(), true, out retValInt16));
        Console.WriteLine(retValInt16 == Int16.MinValue);

        Console.WriteLine(NumberUtilities.TryParseInt16("-77777", true, out retValInt16));
        Console.WriteLine(NumberUtilities.TryParseInt16("-88888", true, out retValInt16));
        Console.WriteLine(NumberUtilities.TryParseInt16("-99999", true, out retValInt16));

        Console.WriteLine(NumberUtilities.TryParseInt16("-9999", true, out retValInt16));
        Console.WriteLine(retValInt16 == -9999);
        Console.WriteLine(NumberUtilities.TryParseInt16("-999", true, out retValInt16));
        Console.WriteLine(retValInt16 == -999);
        Console.WriteLine(NumberUtilities.TryParseInt16("-99", true, out retValInt16));
        Console.WriteLine(retValInt16 == -99);

        Console.WriteLine("*** TryParseInt16_Elegance ***");
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("99999", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("88888", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("77777", true, out retValInt16));

        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance(Int16.MaxValue.ToString(), true, out retValInt16));
        Console.WriteLine(retValInt16 == Int16.MaxValue);
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance(Int16.MinValue.ToString(), true, out retValInt16));
        Console.WriteLine(retValInt16 == Int16.MinValue);

        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("-77777", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("-88888", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("-99999", true, out retValInt16));

        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("-9999", true, out retValInt16));
        Console.WriteLine(retValInt16 == -9999);
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("-999", true, out retValInt16));
        Console.WriteLine(retValInt16 == -999);
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_Elegance("-99", true, out retValInt16));
        Console.WriteLine(retValInt16 == -99);


        Console.WriteLine("*** TryParseInt16_UseArray ***");
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("99999", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("88888", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("77777", true, out retValInt16));

        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray(Int16.MaxValue.ToString(), true, out retValInt16));
        Console.WriteLine(retValInt16 == Int16.MaxValue);
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray(Int16.MinValue.ToString(), true, out retValInt16));
        Console.WriteLine(retValInt16 == Int16.MinValue);

        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("-77777", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("-88888", true, out retValInt16));
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("-99999", true, out retValInt16));

        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("-9999", true, out retValInt16));
        Console.WriteLine(retValInt16 == -9999);
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("-999", true, out retValInt16));
        Console.WriteLine(retValInt16 == -999);
        Console.WriteLine(NumberUtilitiesDeprecated.TryParseInt16_UseArray("-99", true, out retValInt16));
        Console.WriteLine(retValInt16 == -99);

        Console.WriteLine("*** TryParseInt32 ***");
        Console.WriteLine(NumberUtilities.TryParseInt32("9999999999", true, out retValInt32));
        Console.WriteLine(NumberUtilities.TryParseInt32("8888888888", true, out retValInt32));
        Console.WriteLine(NumberUtilities.TryParseInt32("7777777777", true, out retValInt32));

        Console.WriteLine(NumberUtilities.TryParseInt32(Int32.MaxValue.ToString(), true, out retValInt32));
        Console.WriteLine(retValInt32 == Int32.MaxValue);
        Console.WriteLine(NumberUtilities.TryParseInt32(Int32.MinValue.ToString(), true, out retValInt32));
        Console.WriteLine(retValInt32 == Int32.MinValue);

        Console.WriteLine(NumberUtilities.TryParseInt32("-7777777777", true, out retValInt32));
        Console.WriteLine(NumberUtilities.TryParseInt32("-8888888888", true, out retValInt32));
        Console.WriteLine(NumberUtilities.TryParseInt32("-9999999999", true, out retValInt32));

        Console.WriteLine(NumberUtilities.TryParseInt32("-999999999", true, out retValInt32));
        Console.WriteLine(retValInt32 == -999999999);
        Console.WriteLine(NumberUtilities.TryParseInt32("-99999999", true, out retValInt32));
        Console.WriteLine(retValInt32 == -99999999);
        Console.WriteLine(NumberUtilities.TryParseInt32("-9999999", true, out retValInt32));
        Console.WriteLine(retValInt32 == -9999999);
    }

    private static void Main(string[] args) {
        SanityCheck(true, false);

        SpeedCheckInt16();
        SpeedCheckInt32();
    }
   
    private static void SpeedCheckInt16() {
        short retVal;
        int startRange = Int16.MinValue;
        int endRange = Int16.MaxValue;
        long range = endRange - startRange;
        string[] strings = new string[range];
        DateTime start, end;
       
        Console.WriteLine("Preallocate strings");
        for(int i = 0; i < range; i++) {
            strings[i] = (i + startRange).ToString();
        }
        Console.WriteLine("Preallocation complete");

        start = DateTime.Now;
        for(int loop = 0; loop < 500; loop++) {
            for(int i = 0; i < range; i++) {
                string tryParse = strings[i];
                if ( NumberUtilities.TryParseInt16(tryParse, true, out retVal) ) {
                    if ( (i+startRange) != retVal ) {
                        Console.WriteLine("Equality failure in TryParseInt16: {0}, {1}", i, retVal);
                        return;
                    }
                } else {
                    Console.WriteLine("Processing failure {0}", i);
                    return;
                }
            }
        }
        end = DateTime.Now;
        Console.WriteLine("TryParseInt16: {0}", end - start);
       
        start = DateTime.Now;
        for(int loop = 0; loop < 500; loop++) {
            for(int i = 0; i < range; i++) {
                string tryParse = strings[i];
                if ( NumberUtilitiesDeprecated.TryParseInt16_Elegance(tryParse, true, out retVal) ) {
                    if ( (i+startRange) != retVal ) {
                        Console.WriteLine("Equality failure in TryParseInt16_Elegance: {0}, {1}", i, retVal);
                        return;
                    }
                } else {
                    Console.WriteLine("Processing failure {0}", i);
                    return;
                }
            }
        }
        end = DateTime.Now;
        Console.WriteLine("TryParseInt16_Elegance: {0}", end - start);

        start = DateTime.Now;
        for(int loop = 0; loop < 500; loop++) {
            for(int i = 0; i < range; i++) {
                string tryParse = strings[i];
                if ( NumberUtilitiesDeprecated.TryParseInt16_UseArray(tryParse, true, out retVal) ) {
                    if ( (i+startRange) != retVal ) {
                        Console.WriteLine("Equality failure in TryParseInt16_UseArray: {0}, {1}", i, retVal);
                        return;
                    }
                } else {
                    Console.WriteLine("Processing failure {0}", i);
                    return;
                }
            }
        }
        end = DateTime.Now;
        Console.WriteLine("TryParseInt16_UseArray: {0}", end - start);
    }
   
    private static void SpeedCheckInt32() {
        int retVal;
        int startRange = Int32.MinValue / 1000;
        int endRange = Int32.MaxValue / 1000;
        long range = endRange - startRange;
        string[] strings = new string[range];
        DateTime start, end;
       
        Console.WriteLine("Preallocate strings");
        for(int i = 0; i < range; i++) {
            strings[i] = (i + startRange).ToString();
        }
        Console.WriteLine("Preallocation complete");

        start = DateTime.Now;
        for(int i = 0; i < range; i++) {
            string tryParse = strings[i];
            if ( NumberUtilitiesDeprecated.TryParseInt(tryParse, true, out retVal) ) {
                if ( (i+startRange) != retVal ) {
                    Console.WriteLine("Equality failure in TryParseInt: {0}, {1}", i, retVal);
                    return;
                }
            } else {
                Console.WriteLine("Processing failure {0}", i);
                return;
            }
        }
        end = DateTime.Now;
        Console.WriteLine("TryParseInt: {0}", end - start);
       
        start = DateTime.Now;
        for(int i = 0; i < range; i++) {
            string tryParse = strings[i];
            if ( NumberUtilities.TryParseInt32(tryParse, true, out retVal) ) {
                if ( (i+startRange) != retVal ) {
                    Console.WriteLine("Equality failure in TryParseInt32: {0}, {1}", i, retVal);
                    return;
                }
            } else {
                Console.WriteLine("Processing failure {0}", i);
                return;
            }
        }
        end = DateTime.Now;
        Console.WriteLine("TryParseInt32: {0}", end - start);
    }
}

public sealed class Community {
    const int tenthBound = Int32.MaxValue / 10;
    const int digitBound = Int32.MaxValue % 10;

    //
    // only for Int32
    //
    public static bool TryParseInt(string s, out int value)
    {

    value = 0;

    int digit;

    //
    // since we are being silly, this should be less costly
    // than making multiple calls to s.Length. maybe?
    //
    int max = s.Length;

    if( max == 0 || max > 10 )
    return false;

    bool checkEnd = max == 10; // is this max allowable length?

    max = checkEnd? 9 : max; // we only loop 9 times if it's max length

    for( int i = 0; i < max; i++ )
    {

    digit = s[i] - '0';

    if( digit < 0 || digit > 9 )
    return false;

    value = value * 10 + digit;

    }

    if( checkEnd )
    {

    if( value > tenthBound )
    return false; // x10 will result in overflow
    else
    {

    digit = s[9] - '0';

    if( digit < 0 || digit > 9 )
    return false;

    if( value == tenthBound && digit > digitBound )
    return false; // x10 + digit will result in overflow
    else
    value = value * 10 + digit;

    }
    }

    return true;

    }
}

Published Tuesday, March 30, 2004 9:49 PM by Justin Rogers
Filed under:

Comments

Friday, April 30, 2004 4:13 PM by Chris Stueck

# re: DWC.Algorithms.NumberUtilities

How do I compile this under visual studio.net??I have not built a library yet.

Thanks.
Friday, April 30, 2004 7:49 PM by Justin Rogers

# re: DWC.Algorithms.NumberUtilities

You'd create what I think VS calls a new Class Library project and copy the above code into it's own .CS file and compile. It should just work from there.

If you want to include the source directly in your project (aka not as a library), you can do that also. Just make the above source its own source file and then compile your application as normal.
Friday, August 24, 2012 9:37 AM by trx pro pack

# re: DWC.Algorithms.NumberUtilities

trx training education