Click here to Skip to main content
15,895,656 members
Articles / Programming Languages / C#

The List Trifecta, Part 1

Rate me:
Please Sign up or sign in to vote.
4.97/5 (21 votes)
20 May 2016LGPL321 min read 37.1K   161   40  
The A-list is an all-purpose list, a data structure that can support most standard list operation in O(log n) time and does lots of other stuff, too
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Loyc.Essentials;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using Loyc.Math;
using Loyc.Threading;
using Loyc.Collections;

namespace Loyc
{
	public delegate string WriterDelegate(string format, params object[] args);

	/// <summary>Contains global functions that don't really belong in any class.</summary>
	public static class G
	{
		public static void Swap<T>(ref T a, ref T b)
		{
			T tmp = a;
			a = b;
			b = tmp;
		}
		public static int BinarySearch<T>(IList<T> list, T value, Comparison<T> pred)
		{
			int lo = 0, hi = list.Count, i;
			while(lo < hi) 
			{
				i = (lo+hi)/2;
				int cmp = pred(list[i], value);
				if (cmp < 0)
					lo = i+1;
				else if (cmp > 0)
					hi = i;
				else
					return i;
			}
			return ~lo;
		}
		public static int BinarySearch<T>(IList<T> list, T value) where T : IComparable<T>
		{
			return BinarySearch<T>(list, value, ToComparison<T>());
		}
		public static int BinarySearch<T>(IList<T> list, T value, IComparer<T> pred)
		{
			return BinarySearch<T>(list, value, ToComparison(pred));
		}
		public static Comparison<T> ToComparison<T>(IComparer<T> pred)
		{
			return delegate(T a, T b) { return pred.Compare(a, b); };
		}
		public static Comparison<T> ToComparison<T>() where T : IComparable<T>
		{
			return delegate(T a, T b) { return a.CompareTo(b); };
		}
		public static List<string> SplitCommandLineArguments(string listString)
		{
			List<string> list = new List<string>();
			string regex = "(?=[^\\s\n])" // Match at least one non-whitespace character
			             + "[^\\s\n\"]*"  // Optional unquoted text
						   // (Optional quoted text, then optional unquoted text)*
			             + "(\"[^\"\n]*(\"[^\\s\n\"]*)?)*";

			Match m = Regex.Match(listString, regex, RegexOptions.IgnorePatternWhitespace);
			for (; m.Success; m = m.NextMatch()) {
				string s = m.ToString();
				if (s.StartsWith("\"") && s.EndsWith("\""))
					s = s.Substring(1, s.Length - 2);
				list.Add(s);
			}
			return list;
		}


		public static Pair<T1, T2> Pair<T1, T2>(T1 a, T2 b) { return new Pair<T1, T2>(a, b); }
		public static Triplet<T1, T2, T3> Triplet<T1, T2, T3>(T1 a, T2 b, T3 c) { return new Triplet<T1, T2, T3>(a, b, c); }

		/// <summary>Same as Debug.Assert except that the argument is evaluated 
		/// even in a Release build.</summary>
		public static bool Verify(bool condition)
		{
			Debug.Assert(condition);
			return condition;
		}
		public static void RequireArg(bool condition)
		{
			if (!condition)
				throw new ArgumentException();
		}
		public static void RequireArg(bool condition, string argName, object argValue)
		{
			if (!condition)
				throw new ArgumentException(Localize.From("Invalid argument ({0} = '{1}')", argName, argValue));
		}
		public static void Require(bool condition)
		{
			if (!condition)
				throw new Exception(Localize.From("A required condition was false"));
		}
		public static void Require(bool condition, string msg)
		{
			if (!condition)
				throw new Exception(Localize.From("Error: {0}", msg));
		}

		public static int TryParseHex(string s, out int value)
			{ return TryParseHex(s, 0, out value); }
		public static int TryParseHex(string s, int startAt, out int value)
		{
			value = 0;
			for (int i = startAt; i < s.Length; i++)
			{
				int digit = HexDigitValue(s[i]);
				if (digit == -1)
					return i - startAt;
				else
					value = value * 16 + digit;
			}
			return s.Length - startAt;
		}
		public static int HexDigitValue(char c)
		{
			if (c >= '0' && c <= '9')
				return c - '0';
			if (c >= 'A' && c <= 'F')
				return c - 'A' + 10;
			if (c >= 'a' && c <= 'f')
				return c - 'a' + 10;
			else
				return -1;
		}
		public static char HexDigitChar(int value)
		{
			Debug.Assert((uint)value < 16);
			if ((uint)value < 10)
				return (char)('0' + value);
			else
				return (char)('A' - 10 + value);
		}

		public static string EscapeCStyle(string s, EscapeC flags, char quoteType = '\0')
		{
			StringBuilder s2 = new StringBuilder(s.Length+1);
			
			for (int i = 0; i < s.Length; i++) {
				char c = s[i];
				if (c > 255 && (flags & (EscapeC.Unicode | EscapeC.NonAscii)) != 0) {
					s2.AppendFormat((IFormatProvider)null, @"\u{0:x0000}", (int)c);
				} else if (c == '\"' && (flags & EscapeC.DoubleQuotes) != 0) {
					s2.Append("\\\"");
				} else if (c == '\'' && (flags & EscapeC.SingleQuotes) != 0) {
					s2.Append("\\\'");
				} else if (c == quoteType) {
					s2.Append('\\');
					s2.Append(c);
				} else if (c < 32) {
					if (c == '\n')
						s2.Append(@"\n");
					else if (c == '\r')
						s2.Append(@"\r");
					else if (c == '\0')
						s2.Append(@"\0");
					else {
						if ((flags & EscapeC.ABFV) != 0) {
							if (c == '\a') { // 7 (alert)
								s2.Append(@"\a");
								continue;
							} 
							if (c == '\b') { // 8 (backspace)
								s2.Append(@"\b"); 
								continue;
							} 
							if (c == '\f') { // 12 (form feed)
								s2.Append(@"\f"); 
								continue; 
							} 
							if (c == '\v') { // 11 (vertical tab)
								s2.Append(@"\v"); 
								continue;
							} 
						}
						if ((flags & EscapeC.Control) != 0) {
							if (c == '\t')
								s2.Append(@"\t");
							else
								s2.AppendFormat(null, @"\x{0:X2}", (int)c);
						} else
							s2.Append(c);
					}
				} else if (c == '\\') {
					s2.Append(@"\\");
				} else if (c > 127 && (flags & EscapeC.NonAscii) != 0) {
					s2.AppendFormat(null, @"\x{0:X2}", (int)c);
				} else
					s2.Append(c);
			}
			return s2.ToString();
		}
		/// <summary>Unescapes a string that uses C-style escape sequences, e.g. "\n\r" becomes @"\n\r".</summary>
		public static string UnescapeCStyle(string s)
		{
			return UnescapeCStyle(s, 0, s.Length, false);
		}

		/// <summary>Unescapes a string that uses C-style escape sequences, e.g. "\n\r" becomes @"\n\r".</summary>
		/// <param name="removeUnnecessaryBackslashes">Causes the backslash before 
		/// an unrecognized escape sequence to be removed, e.g. "\z" => "z".</param>
		public static string UnescapeCStyle(string s, int index, int length, bool removeUnnecessaryBackslashes)
		{
			StringBuilder s2 = new StringBuilder(length);
			for (int i = index; i < index + length; ) {
				int oldi = i;
				char c = UnescapeChar(s, ref i);
				if (removeUnnecessaryBackslashes && c == '\\' && i == oldi + 1)
					continue;
				s2.Append(c);
			}
			return s2.ToString();
		}

		public static char UnescapeChar(ref UString s)
		{
			int i = s.InternalStart, i0 = i;
			char c = UnescapeChar(s.InternalString, ref i);
			s = new UString(s.InternalString, i, s.Length - (i - i0));
			return c;
		}

		/// <summary>Unescapes a single character of a string, e.g. 
		/// <c>int = 3; UnescapeChar("foo\\n", ref i) == '\n'</c>. Returns the 
		/// character at 'index' if it is not a backslash, or if it is a 
		/// backslash but no escape sequence could be discerned.</summary>
		/// <exception cref="IndexOutOfRangeException">The index was invalid.</exception>
		public static char UnescapeChar(string s, ref int i)
		{
			char c = s[i++];
			if (c == '\\' && i < s.Length) {
				int len, code;
				switch (s[i++]) {
				case 'u':
					len = System.Math.Min(4, s.Length - i);
					i += G.TryParseHex(s.Substring(i, len), out code);
					return (char)code;
				case 'x':
					len = System.Math.Min(2, s.Length - i);
					i += G.TryParseHex(s.Substring(i, len), out code);
					return (char)code;
				case '\\':
					return '\\';
				case '\"':
					return '\"';
				case '\'':
					return '\'';
				case '`':
					return '`';
				case 't':
					return '\t';
				case 'n':
					return '\n';
				case 'r':
					return '\r';
				case 'a':
					return '\a';
				case 'b':
					return '\b';
				case 'f':
					return '\f';
				case 'v':
					return '\v';
				case '0':
					return '\0';
				default:
					i--; break;
				}
			}
			return c;
		}

		/// <summary>Helper function for a using statement that temporarily 
		/// modifies a thread-local variable.</summary>
		/// <param name="variable">Variable to change</param>
		/// <param name="newValue">New value</param>
		/// <example>
		/// // Temporarily change the target of compiler error messages
		/// using (var _ = G.PushTLV(CompilerOutput.Writer, CustomWriter))
		/// {
		///		Warning.Write(SourcePos.Nowhere, "This message will go to a custom writer");
		/// }
		/// Warning.Write(SourcePos.Nowhere, "But this message will go to the original one");
		/// </example>
		public static PushedTLV<T> PushTLV<T>(ThreadLocalVariable<T> variable, T newValue)
		{
			return new PushedTLV<T>(variable, newValue);
		}


		/// <summary>Tries to parse a string to an integer. Unlike <see cref="Int32.TryParse"/>,
		/// this method allows parsing to start at any point in the string, it 
		/// allows non-numeric data after the number, and it can parse numbers that
		/// are not in base 10.</summary>
		/// <param name="base">Number base, e.g. 10 for decimal and 2 for binary.</param>
		/// <param name="index">Location at which to start parsing</param>
		/// <param name="skipSpaces">Whether to skip spaces before parsing. Only 
		/// the ' ' and '\t' characters are treated as spaces. No space is allowed 
		/// between '-' and the digits of a negative number, even with this flag.</param>
		/// <param name="stopBeforeOverflow">Changes overflow handling behavior
		/// so that the result does not overflow, and the digit(s) at the end of
		/// the string, that would have caused overflow, are ignored. In this case,
		/// the return value is still false.</param>
		/// <returns>True if a number was found starting at the specified index
		/// and it was successfully converted to a number, or false if not.</returns>
		/// <remarks>
		/// This method never throws. If parsing fails, index is left unchanged, 
		/// except that spaces are still skipped if you set the skipSpaces flag. 
		/// If base>36, parsing can succeed but digits above 35 (Z) cannot occur 
		/// in the output number. If the input number cannot fit in 'result', the 
		/// return value is false but index increases anyway, and 'result' is a 
		/// bitwise truncated version of the number.
		/// <para/>
		/// When parsing input such as "12.34", the parser stops and returns true
		/// at the dot (with a result of 12 in this case).
		/// </remarks>
		public static bool TryParseAt(string s, ref int index, out int result, int @base = 10, bool skipSpaces = true)
		{
			long resultL;
			bool ok = TryParseAt(s, ref index, out resultL, @base, skipSpaces);
			result = (int)resultL;
			return ok && result == resultL;
		}
		/// <inheritdoc cref="TryParseAt(this string, ref int, out int, int, bool)"/>
		public static bool TryParseAt(string s, ref int index, out long result, int @base = 10, bool skipSpaces = true)
		{
			if (skipSpaces)
				index = SkipSpaces(s, index);

			if ((uint)index >= (uint)s.Length) {
				result = 0;
				return false;
			}

			bool negative = false;
			int i = index;
			if (s[i] == '-') {
				negative = true;
				i++;
			}

			ulong resultU;
			bool ok = TryParseAt(s, ref i, out resultU, @base, 0);
			result = negative ? -(long)resultU : (long)resultU;
			if (!(negative && i == index + 1))
				index = i;
			return ok && ((result < 0) == negative || result == 0);
		}

		/// <summary>Flags that can be used with the overload of TryParseAt() 
		/// that parses unsigned integers.</summary>
		public enum ParseFlag
		{
			/// <summary>Skip spaces before the number. Without this flag, spaces make parsing fail.</summary>
			SkipSpacesInFront = 1,
			/// <summary>Skip spaces inside the number. Without this flag, spaces make parsing stop.</summary>
			SkipSpacesInsideNumber = 2,
			/// <summary>Changes overflow handling behavior so that the result does 
			/// not overflow, and the digit(s) at the end of the string, that would 
			/// have caused overflow, are ignored. In this case, the return value is 
			/// still false.</summary>
			StopBeforeOverflow = 4,
			/// <summary>Skip underscores inside number. Without this flag, underscores make parsing stop.</summary>
			SkipUnderscores = 8,
		}

		/// <inheritdoc cref="TryParseAt(this string, ref int, out int, int, bool)"/>
		public static bool TryParseAt(string s, ref int index, out ulong result, int @base = 10, ParseFlag flags = 0)
		{
			result = 0;
			if ((flags & ParseFlag.SkipSpacesInFront) != 0)
				index = SkipSpaces(s, index);
			
			bool overflow = false;
			int i;
			for (i = index; (uint)i < (uint)s.Length; i++)
			{
				uint digit;
				char c = s[i];
				if (c >= '0' && c <= '9') {
					digit = (uint)(c - '0');
                } else if ((c == ' ' || c == '\t') && (flags & ParseFlag.SkipSpacesInsideNumber) != 0)
                    continue;
                else if (c == '_' && (flags & ParseFlag.SkipUnderscores) != 0)
                    continue;
                else if (@base > 10) {
					if (c >= 'a' && c <= 'z')
						digit = (uint)(c - ('a' - 10));
					else if (c >= 'A' && c <= 'Z')
						digit = (uint)(c - ('A' - 10));
					else {
						break;
					}
					if (digit >= @base)
						break;
				} else
					break;

				ulong next;
				try {
					next = checked(result * (uint)@base + digit);
				} catch (OverflowException) {
					overflow = true;
					if ((flags & ParseFlag.StopBeforeOverflow) != 0) {
						index = i;
						return false;
					}
					next = result * (uint)@base + digit;
				}
				result = next;
			}
			if (i == index) {
				Debug.Assert(result == 0);
				return false;
			}
			index = i;
			return !overflow;
		}

		/// <summary>Gets the index of the first non-space character after the specified index.</summary>
		/// <remarks>Only ' ' and '\t' are treated as spaces. If the index is invalid, it is returned unchanged.</remarks>
		public static int SkipSpaces(string s, int index)
		{
			for (;;) {
				if ((uint)index >= (uint)s.Length)
					break;
				char c = s[index];
				if (c == ' ' || c == '\t')
					index++;
				else
					break;
			}
			return index;
		}
	}

	[Flags()]
	public enum EscapeC
	{
		Minimal = 0,  // Only \r, \n, \0 and backslash are escaped.
		Unicode = 2,  // Escape all characters with codes above 255 as \uNNNN
		NonAscii = 1, // Escape all characters with codes above 127 as \xNN
		Control = 4,  // Escape all characters with codes below 32  as \xNN, and also \t
		ABFV = 8,     // Use \a \b \f and \v (overrides \xNN)
		DoubleQuotes = 16, // Escape double quotes as \"
		SingleQuotes = 32, // Escape single quotes as \'
		Quotes = 48,
	}

	[TestFixture]
	public class GTests : Assert
	{
		[Test] public void TestSwap()
		{
			int a = 7, b = 13;
			G.Swap(ref a, ref b);
			Assert.AreEqual(7, b);
			Assert.AreEqual(13, a);
		}
		[Test] public void TestBinarySearch()
		{
			int[] list = new int[] { };
			Assert.AreEqual(~0, G.BinarySearch(list, 15));
			Assert.AreEqual(~0, G.BinarySearch(list, -15));
			list = new int[] { 5 };
			Assert.AreEqual(0, G.BinarySearch(list, 5));
			Assert.AreEqual(~0, G.BinarySearch(list, 0));
			Assert.AreEqual(~1, G.BinarySearch(list, 10));
			list = new int[] { 5, 7 };
			Assert.AreEqual(~0, G.BinarySearch(list, 0));
			Assert.AreEqual( 0, G.BinarySearch(list, 5));
			Assert.AreEqual(~1, G.BinarySearch(list, 6));
			Assert.AreEqual( 1, G.BinarySearch(list, 7));
			Assert.AreEqual(~2, G.BinarySearch(list, 10));
			list = new int[] { 1, 5, 7, 13, 17, 29, 29, 31 };
			Assert.AreEqual(~0, G.BinarySearch(list, -1));
			Assert.AreEqual( 0, G.BinarySearch(list, 1));
			Assert.AreEqual(~1, G.BinarySearch(list, 2));
			Assert.AreEqual( 1, G.BinarySearch(list, 5));
			Assert.AreEqual(~2, G.BinarySearch(list, 6));
			Assert.AreEqual( 2, G.BinarySearch(list, 7));
			Assert.AreEqual(~3, G.BinarySearch(list, 10));
			Assert.AreEqual( 3, G.BinarySearch(list, 13));
			Assert.AreEqual(~4, G.BinarySearch(list, 16));
			Assert.AreEqual( 4, G.BinarySearch(list, 17));
			Assert.AreEqual(~5, G.BinarySearch(list, 28));
			int i = G.BinarySearch(list, 29);
			Assert.IsTrue(i == 5 || i == 6);
			Assert.AreEqual(~7, G.BinarySearch(list, 30));
			Assert.AreEqual( 7, G.BinarySearch(list, 31));
			Assert.AreEqual(~8, G.BinarySearch(list, 1000));
		}
		[Test] public void TestPredicatedBinarySearch()
		{
			Comparison<int> p = G.ToComparison<int>();
			int[] list = new int[] { };
			Assert.AreEqual(~0, G.BinarySearch<int>(list, 15, p));
			Assert.AreEqual(~0, G.BinarySearch<int>(list, -15, p));
			list = new int[] { 5 };
			Assert.AreEqual(0, G.BinarySearch<int>(list, 5, p));
			Assert.AreEqual(~0, G.BinarySearch<int>(list, 0, p));
			Assert.AreEqual(~1, G.BinarySearch<int>(list, 10, p));
			list = new int[] { 5, 7 };
			Assert.AreEqual(~0, G.BinarySearch<int>(list, 0, p));
			Assert.AreEqual( 0, G.BinarySearch<int>(list, 5, p));
			Assert.AreEqual(~1, G.BinarySearch<int>(list, 6, p));
			Assert.AreEqual( 1, G.BinarySearch<int>(list, 7, p));
			Assert.AreEqual(~2, G.BinarySearch<int>(list, 10, p));
			list = new int[] { 1, 5, 7, 13, 17, 29, 29, 31 };
			Assert.AreEqual(~0, G.BinarySearch<int>(list, -1, p));
			Assert.AreEqual( 0, G.BinarySearch<int>(list, 1, p));
			Assert.AreEqual(~1, G.BinarySearch<int>(list, 2, p));
			Assert.AreEqual( 1, G.BinarySearch<int>(list, 5, p));
			Assert.AreEqual(~2, G.BinarySearch<int>(list, 6, p));
			Assert.AreEqual( 2, G.BinarySearch<int>(list, 7, p));
			Assert.AreEqual(~3, G.BinarySearch<int>(list, 10, p));
			Assert.AreEqual( 3, G.BinarySearch<int>(list, 13, p));
			Assert.AreEqual(~4, G.BinarySearch<int>(list, 16, p));
			Assert.AreEqual( 4, G.BinarySearch<int>(list, 17, p));
			Assert.AreEqual(~5, G.BinarySearch<int>(list, 28, p));
			int i = G.BinarySearch<int>(list, 29, p);
			Assert.IsTrue(i == 5 || i == 6);
			Assert.AreEqual(~7, G.BinarySearch<int>(list, 30, p));
			Assert.AreEqual( 7, G.BinarySearch<int>(list, 31, p));
			Assert.AreEqual(~8, G.BinarySearch<int>(list, 1000, p));
		}
		[Test] public void TestSplitCommandLineArguments()
		{
			// Give it some easy and some difficult arguments
			string input = "123: apple \t banana=\"a b\" Carrot\n"
				+ "!@#$%^&*() \"duck\" \"error \"foo\"!\ngrape   ";
			List<string> expected = new List<string>();
			expected.Add("123:");
			expected.Add("apple");
			expected.Add("banana=\"a b\"");
			expected.Add("Carrot");
			expected.Add("!@#$%^&*()");
			expected.Add("duck");
			expected.Add("\"error \"foo\"!");
			expected.Add("grape");
			
			List<string> output = G.SplitCommandLineArguments(input);
			Assert.AreEqual(output.Count, expected.Count);
			for (int i = 0; i < expected.Count; i++)
				Assert.AreEqual(output[i], expected[i]);
		}
		[Test] public void TestUnescape()
		{
			Assert.AreEqual("", G.UnescapeCStyle(""));
			Assert.AreEqual("foobar", G.UnescapeCStyle("foobar"));
			Assert.AreEqual("foo\nbar", G.UnescapeCStyle(@"foo\nbar"));
			Assert.AreEqual("\u2222\n\r\t", G.UnescapeCStyle(@"\u2222\n\r\t"));
			Assert.AreEqual("\a\b\f\vA", G.UnescapeCStyle(@"\a\b\f\v\x41"));
			Assert.AreEqual("ba\\z", G.UnescapeCStyle(@"ba\z", 0, 4, false));
			Assert.AreEqual("baz", G.UnescapeCStyle(@"ba\z", 0, 4, true));
			Assert.AreEqual("!!\n!!", G.UnescapeCStyle(@"<'!!\n!!'>", 2, 6, true));
		}

		[Test]
		public void TestTryParseAt()
		{
			TestParse(true, "0", 0, 0, 1);
			TestParse(true, "-0", 0, 0, 2);
			TestParse(true, "0", 0, 0, 1, 3, false);
			TestParse(true, "123", 123, 0, 3);
			TestParse(true, "??123", 123, 2, 5);
			TestParse(true, "??  123abc", 123, 2, 7);
			TestParse(true, "??\t 123  abc", 123, 2, 7);
			TestParse(true, "210", 21, 0, 3, 3);
			TestParse(true, " \t 210", 21, 0, 6, 3);
			TestParse(false," \t 210", 0, 0, 0, 3, false);
			TestParse(true, "1 -2", -2, 1, 4, 10, true);
			TestParse(false,"1 -2", 0, 1, 1, 10, false);
			TestParse(true, "1 -22", -22, 1, 5, 10, true);
			TestParse(true, "1 -22", -10, 1, 5, 4, true);
			TestParse(false,"1 -22", 0, 1, 1, 4, false);
			TestParse(true, "F9", 0xF9, 0, 2, 16);
			TestParse(true, "f9", 0xF9, 0, 2, 16);
			TestParse(true, "abcdef", 0xabcdef, 0, 6, 16);
			TestParse(true, "ABCDEF", 0xabcdef, 0, 6, 16);
			TestParse(true, "abcdefgh", 0xabcdef, 0, 6, 16);
			TestParse(true, "ABCDEFGH", 0xabcdef, 0, 6, 16);
			TestParse(true, "az", 10*36+35, 0, 2, 36);
			TestParse(true, "AZ1234", 103501020304, 0, 6, 100);
			TestParse(true, " -AZ1234", -103501020304, 0, 8, 100);
			string s;
			TestParse(true, s = long.MaxValue.ToString(), long.MaxValue, 0, s.Length);
			TestParse(true, s = long.MinValue.ToString(), long.MinValue, 0, s.Length);
			TestParse(true, "111100010001000100010001000100010001", 0xF11111111, 0, 36, 2);
			TestParse(false, "", 0, 0, 0);
			TestParse(false, "?!", 0, 0, 0);
			TestParse(false, " eh?", 0, 0, 1);
			TestParse(false, "123 eh?", 0, 3, 4);
			TestParse(false, "10123456789abcdef", 0x0123456789abcdef, 0, 17, 16);
			TestParse(false, "- 1", 0, 0, 0);

			int i, result;
			i = 1;
			IsTrue(G.TryParseAt(" -AZ", ref i, out result, 100, false));
			AreEqual(-1035, result);
			AreEqual(i, 4);
			i = 0;
			IsFalse(G.TryParseAt(" -A123456Z", ref i, out result, 100, true));
			AreEqual(unchecked((int)-1001020304050635), result);
			AreEqual(i, 10);
			i = 1;
			IsTrue(G.TryParseAt(s = "0" + int.MinValue.ToString(), ref i, out result));
			AreEqual(int.MinValue, result);
			AreEqual(i, s.Length);
			i = 0;
			IsFalse(G.TryParseAt(s = ((long)int.MinValue - 1).ToString(), ref i, out result));
			AreEqual(int.MaxValue, result);
			AreEqual(i, s.Length);
		}
		private void TestParse(bool expectSuccess, string input, long expected, int i, int i_out, int @base = 10, bool skipSpaces = true)
		{
			long result;
			bool success = G.TryParseAt(input, ref i, out result, @base, skipSpaces);
			AreEqual(expected, result);
			AreEqual(expectSuccess, success);
			AreEqual(i_out, i);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions