// spcreconsole.cpp
//
// Copyright (c) 2005 - 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: Sanjay Ghemawat
//
// Heavily refactored for Symbian OS by Accenture.


#include <e32base.h>
#include <e32std.h>
#include <e32math.h>
#include <e32cons.h>			// Console
#include <bacline.h>
#include <cregex.h>
#include <tregexarg.h>
#include <tregexoptions.h>

//  Constants
_LIT(KTextConsoleTitle, "Console");
_LIT(KTextFailed, " failed, leave code = %d\n");
_LIT(KTextPressAnyKey, "[press any key]\n");
//  Global Variables
LOCAL_D CConsoleBase* console; // write all messages to this
TBool verboseMode = EFalse;

//  Local Functions

// CHECK dies with a fatal error if condition is not true.  It is *not*
// controlled by NDEBUG, so the check will be executed regardless of
// compilation mode.  Therefore, it is safe to do things like:
//    CHECK_EQ(fp->Write(x), 4)
#define CHECK(condition)								\
	{													\
	if (!(condition))									\
		{                                   			\
		_LIT(KCondition, #condition);					\
		console->Printf(_L("%d: Check failed: %S\n"),	\
						__LINE__, &KCondition());		\
		User::Leave(KErrGeneral);						\
		}												\
	}

#define CHECK_EQ(a, b)   CHECK(a == b)

static void Timing1L(TInt aNumIters)
	{
	console->Printf(_L("Test Timing 1, %d iterations.\n"), aNumIters);
	
	// Same pattern lots of times
	_LIT8(KPattern, "ruby:\\d+");
	_LIT8(KString,"ruby:1234");
	CRegEx* pattern = CRegEx::NewLC(KPattern());

	for (TInt j = aNumIters; j > 0; j--)
		{
		CHECK(pattern->FullMatchL(KString()));
		}
	CleanupStack::PopAndDestroy(pattern);
	}

static void Timing2L(TInt aNumIters)
	{
	console->Printf(_L("Test Timing 2, %d iterations.\n"), aNumIters);
	// Same pattern lots of times
	_LIT8(KPattern, "ruby:(\\d+)");
	_LIT8(KString,"ruby:1234");
	CRegEx* pattern = CRegEx::NewLC(KPattern());

	TInt i;
	for (TInt j = aNumIters; j > 0; j--)
		{
		CHECK(pattern->FullMatchL(KString(), &i));
		CHECK_EQ(i, 1234);
		}
	CleanupStack::PopAndDestroy(pattern);
	}

static void Timing3L(TInt aNumIters)
	{
	console->Printf(_L("Test Timing 3, %d iterations.\n"), aNumIters);
	_LIT8(KLine, "this is another line\n");
	HBufC8* text = HBufC8::NewLC(KLine().Length() * aNumIters);
	TPtr8 pText = text->Des();

	for (int j = aNumIters; j > 0; j--)
		{
		pText.Append(KLine());
		}

	CRegEx* lineMatcher = CRegEx::NewLC(_L8(".*\n"));

	TInt counter = 0;
	while (lineMatcher->ConsumeL(pText))
		{
		counter++;
		}
	CleanupStack::PopAndDestroy(2, text);
	CHECK_EQ(counter, aNumIters);
	console->Printf(_L("Matched %d lines\n"), counter);
	}


static void RadixTestsL()
	{
	console->Write(_L("Testing hex\n"));

	#define CHECK_HEX(type, value) 										\
		{																\
		type v;															\
		CRegEx* re = CRegEx::NewLC(_L8("([0-9a-fA-F]+)[uUlL]*"));		\
		CHECK(re->FullMatchL(_L8(#value), Hex(&v)));					\
		CHECK_EQ(v, 0x ## value);										\
		CleanupStack::PopAndDestroy(re);								\
		re = NULL;														\
		}

	CHECK_HEX(TInt64,     2baddeadbeefLL);
	CHECK_HEX(TUint8,     abU);
	CHECK_HEX(TUint16,    2badU);
	CHECK_HEX(TUint32,    deadbeefUL);
	CHECK_HEX(TUint,      cafebabeU);
	#undef CHECK_HEX

	console->Write(_L("Testing octal\n"));

	#define CHECK_OCTAL(type, value) 									\
		{																\
		type v;															\
		CRegEx* re = CRegEx::NewLC(_L8("([0-7]+)[uUlL]*"));				\
		CHECK(re->FullMatchL(_L8(#value), Octal(&v)));					\
		CHECK_EQ(v, 0 ## value);										\
		CleanupStack::PopAndDestroy(re);								\
		re = NULL;														\
		}

	CHECK_OCTAL(TInt64,		777777777777LL);
	CHECK_OCTAL(TUint8,		177U);
	CHECK_OCTAL(TUint16,	177777U);
	CHECK_OCTAL(TUint32,	3777777777UL);
	CHECK_OCTAL(TUint,		3777777777U);
	#undef CHECK_OCTAL

	console->Write(_L("Testing decimal\n"));
	
	#define CHECK_DECIMAL(type, value) 									\
		{																\
		type v;															\
		CRegEx* re = CRegEx::NewLC(_L8("(-?[0-9]+)[uUlL]*"));			\
		CHECK(re->FullMatchL(_L8(#value), &v));							\
		CHECK_EQ(v, value);												\
		CleanupStack::PopAndDestroy(re);								\
		re = NULL;														\
		}	
	
	CHECK_DECIMAL(TInt8,	-1);
	CHECK_DECIMAL(TUint8,	1U);
	CHECK_DECIMAL(TInt16,	-9999);
	CHECK_DECIMAL(TUint16,	9999U);
	CHECK_DECIMAL(TInt32,	-123456L);
	CHECK_DECIMAL(TUint32,	123456U);
	CHECK_DECIMAL(TInt64,	1234567890123456789LL);
	CHECK_DECIMAL(TInt,		-1234567890);
	CHECK_DECIMAL(TUint,	1234567890U);
	#undef CHECK_DECIMAL
}

static void TestReplaceL()
	{
	console->Write(_L("Testing ReplaceL\n"));

	struct ReplaceTest
		{
		TBuf8<32>	iRegExp;
		TBuf8<32>	iRewrite;
		TBuf8<64>	iOriginal;
		TBuf8<64>	iSingle;
		TBuf8<64>	iGlobal;
		TInt		iGlobalCount; // the expected return value from ReplaceAll
		};
	
	static const ReplaceTest tests[] =
	{
		// Test 1
		{
		_L8("(qu|[b-df-hj-np-tv-z]*)([a-z]+)"),
		_L8("\\2\\1ay"),
		_L8("the quick brown fox jumps over the lazy dogs."),
		_L8("ethay quick brown fox jumps over the lazy dogs."),
		_L8("ethay ickquay ownbray oxfay umpsjay overay ethay azylay ogsday."),
		9
		},
		// Test 2
		{
		_L8("\\w+"),
		_L8("\\0-NOSPAM"),
		_L8("paul.haahr@google.com"),
		_L8("paul-NOSPAM.haahr@google.com"),
		_L8("paul-NOSPAM.haahr-NOSPAM@google-NOSPAM.com-NOSPAM"),
		4
		},
		// Test 3
		{_L8("^"),
		_L8("(START)"),
		_L8("foo"),
		_L8("(START)foo"),
		_L8("(START)foo"),
		1
		},
		// Test 4
		{
		_L8("^"),
		_L8("(START)"),
		_L8(""),
		_L8("(START)"),
		_L8("(START)"),
		1
		},
		// Test 5
		{
		_L8("$"),
		_L8("(END)"),
		_L8(""),
		_L8("(END)"),
		_L8("(END)"),
		1
		},
		// Test 6
		{
		_L8("b"),
		_L8("bb"),
		_L8("ababababab"),
		_L8("abbabababab"),
		_L8("abbabbabbabbabb"),
		5
		},
		// Test 7
		{
		_L8("b"),
		_L8("bb"),
		_L8("bbbbbb"),
		_L8("bbbbbbb"),
		_L8("bbbbbbbbbbbb"),
		6
		},
		// Test 8
		{_L8("b+"),
		_L8("bb"),
		_L8("bbbbbb"),
		_L8("bb"),
		_L8("bb"),
		1
		},
		// Test 9
		{
		_L8("b*"),
		_L8("bb"),
		_L8("bbbbbb"),
		_L8("bb"),
		_L8("bb"),
		1
		},
		// Test 10
		{
		_L8("b*"),
		_L8("bb"),
		_L8("aaaaa"),
		_L8("bbaaaaa"),
		_L8("bbabbabbabbabbabb"),
		6
		},
		// Test 11
		{
		_L8("b*"),
		_L8("bb"),
		_L8("aa\naa\n"),
		_L8("bbaa\naa\n"),
		_L8("bbabbabb\nbbabbabb\nbb"),
		7
		},
		// Test 12
		{
		_L8("b*"),
		_L8("bb"),
		_L8("aa\raa\r"),
		_L8("bbaa\raa\r"),
		_L8("bbabbabb\rbbabbabb\rbb"),
		7
		},
		// Test 13
		{
		_L8("b*"),
		_L8("bb"),
		_L8("aa\r\naa\r\n"),
		_L8("bbaa\r\naa\r\n"),
		_L8("bbabbabb\r\nbbabbabb\r\nbb"),
		7
		},
#ifdef SUPPORT_UTF8
		// Test 14
		{	
		_L8("b*"),
		_L8("bb"),
		_L8("\xE3\x83\x9B\xE3\x83\xBC\xE3\x83\xA0\xE3\x81\xB8"),   // utf8
		_L8("bb\xE3\x83\x9B\xE3\x83\xBC\xE3\x83\xA0\xE3\x81\xB8"),
		_L8("bb\xE3\x83\x9B""bb""\xE3\x83\xBC""bb""\xE3\x83\xA0""bb""\xE3\x81\xB8""bb"),
		5
		},
		// Test 15
		{
		_L8("b*"),
		_L8("bb"),
		_L8("\xE3\x83\x9B\r\n\xE3\x83\xBC\r\xE3\x83\xA0\n\xE3\x81\xB8\r\n"),   // utf8
		_L8("bb\xE3\x83\x9B\r\n\xE3\x83\xBC\r\xE3\x83\xA0\n\xE3\x81\xB8\r\n"),
		_L8("bb\xE3\x83\x9B""bb\r\nbb""\xE3\x83\xBC""bb\rbb""\xE3\x83\xA0""bb\nbb""\xE3\x81\xB8""bb\r\nbb"),
		9
		},
#endif
		{
		KNullDesC8(),
		KNullDesC8(),
		KNullDesC8(),
		KNullDesC8(),
		KNullDesC8(),
		-1
		}
	};

#ifdef SUPPORT_UTF8
	const TBool supportUtf8 = ETrue;
#else
	const TBool supportUtf8 = EFalse;
#endif
  
	CRegEx* re = NULL;
	TRegExOptions options = TRegExOptions(EPcreNewlineCrLf);
	options.SetUtf8(supportUtf8);	
	TInt i =0;
	for (const ReplaceTest* t = tests; t->iGlobalCount > -1; ++t)
		{		
		console->Printf(_L("Replace Test %d\n"), ++i);
		re = CRegEx::NewLC(t->iRegExp, options);
		CHECK_EQ(re->Error(), KErrNone);
		TBuf8<64> one(t->iOriginal);
		CHECK(re->ReplaceL(t->iRewrite, one));
		CHECK_EQ(one, t->iSingle);
		TBuf8<64> all(t->iOriginal);
		const TInt replaceCount = re->GlobalReplaceL(t->iRewrite, all);
		CHECK_EQ(all, t->iGlobal);
		CHECK_EQ(replaceCount, t->iGlobalCount);		
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}

	// One final test: test \r\n replacement when we're not in CRLF mode
	{
	TRegExOptions options2 = TRegExOptions(EPcreNewlineCr);
	options2.SetUtf8(supportUtf8);
	re = CRegEx::NewLC(_L8("b*"), options2);
	CHECK_EQ(re->Error(), KErrNone);
	TBuf8<32> all(_L8("aa\r\naa\r\n"));
	CHECK_EQ(re->GlobalReplaceL(_L8("bb"), all), 9);
	CHECK_EQ(all, _L8("bbabbabb\rbb\nbbabbabb\rbb\nbb"));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	}
	{
	TRegExOptions options3 = TRegExOptions(EPcreNewlineLf);
	options3.SetUtf8(supportUtf8);
	re = CRegEx::NewLC(_L8("b*"), options3);
	CHECK_EQ(re->Error(), KErrNone);
	TBuf8<32> all(_L8("aa\r\naa\r\n"));
	CHECK_EQ(re->GlobalReplaceL(_L8("bb"), all), 9);
	CHECK_EQ(all, _L8("bbabbabb\rbb\nbbabbabb\rbb\nbb"));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	}

	// TODO: test what happens when no PCRE_NEWLINE_* flag is set.
	//       Alas, the answer depends on how pcre was compiled.
}

static void TestExtractL()
	{
	console->Write(_L("Testing ExtractL\n"));

	TBuf8<32> s;	
	CRegEx* re = NULL;
	
	re = CRegEx::NewLC(_L8("(.*)@([^.]*)"));
	CHECK(re->ExtractL(_L8("\\2!\\1"), _L8("boris@kremvax.ru"), s));
	CHECK_EQ(s, _L8("kremvax!boris"));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	// check the RE interface as well
	re = CRegEx::NewLC(_L8(".*"));
	CHECK(re->ExtractL(_L8("'\\0'"), _L8("foo"), s));
	CHECK_EQ(s, _L8("'foo'"));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("bar"));
	CHECK(!re->ExtractL(_L8("'\\0'"), _L8("baz"), s));
	CHECK_EQ(s, _L8("'foo'"));
	CleanupStack::PopAndDestroy(re);
	}

static void TestConsumeL()
	{
	console->Write(_L("Testing ConsumeL\n"));

	TBuf8<3> word;
	TBuf8<24> input(_L8("   aaa b!@#$@#$cccc"));

	// matches a word, possibly proceeded by whitespace
	CRegEx* re = CRegEx::NewLC(_L8("\\s*(\\w+)"));
	CHECK(re->ConsumeL(input, &word));
	CHECK_EQ(word, _L8("aaa"));
	CHECK(re->ConsumeL(input, &word));
	CHECK_EQ(word, _L8("b"));
	CHECK(!re->ConsumeL(input, &word));
	CleanupStack::PopAndDestroy(re);
	}

static void TestFindAndConsumeL()
	{
	console->Write(_L("Testing FindAndConsumeL\n"));

	TBuf8<4> word;
	TBuf8<24> input(_L8("   aaa b!@#$@#$cccc"));

	CRegEx* re = CRegEx::NewLC(_L8("(\\w+)")); // matches a word
	CHECK(re->FindAndConsumeL(input, &word));
	CHECK_EQ(word, _L8("aaa"));
	CHECK(re->FindAndConsumeL(input, &word));
	CHECK_EQ(word, _L8("b"));
	CHECK(re->FindAndConsumeL(input, &word));
	CHECK_EQ(word, _L8("cccc"));
	CHECK(!re->FindAndConsumeL(input, &word));
	CleanupStack::PopAndDestroy(re);
	}

static void TestMatchNumberPeculiarityL()
	{
	console->Write(_L("Testing match-number peculiarity\n"));

	TBuf8<3> word1;
	TBuf8<3> word2;
	TBuf8<3> word3;

	CRegEx* re = CRegEx::NewLC(_L8("(foo)|(bar)|(baz)"));
	CHECK(re->PartialMatchL(_L8("foo"), &word1, &word2, &word3));
	CHECK_EQ(word1, _L8("foo"));
	CHECK_EQ(word2, KNullDesC8());
	CHECK_EQ(word3, KNullDesC8());
	CHECK(re->PartialMatchL(_L8("bar"), &word1, &word2, &word3));
	CHECK_EQ(word1, KNullDesC8());
	CHECK_EQ(word2, _L8("bar"));
	CHECK_EQ(word3, KNullDesC8());
	CHECK(re->PartialMatchL(_L8("baz"), &word1, &word2, &word3));
	CHECK_EQ(word1, KNullDesC8());
	CHECK_EQ(word2, KNullDesC8());
	CHECK_EQ(word3, _L8("baz"));
	CHECK(!re->PartialMatchL(_L8("f"), &word1, &word2, &word3));

	CleanupStack::PopAndDestroy(re);
	re = NULL;

	TBuf8<12> a;
	re = CRegEx::NewLC(_L8("(foo)|hello"));
	CHECK(re->FullMatchL(_L8("hello"), &a));
	CHECK_EQ(a, KNullDesC8());
	CleanupStack::PopAndDestroy(re);
	}

static void TestRecursionL()
	{
	console->Write(_L("Testing recursion\n"));
	
	// Get one string that passes (sometimes), one that never does.
	TBuf8<12> good(_L8("abcdefghijk"));
	TBuf8<12> bad(_L8("acdefghijkl"));

	// According to pcretest, matching text_good against (\w+)*b
	// requires match_limit of at least 8192, and match_recursion_limit
	// of at least 37.

	TRegExOptions optionsMatchLimit;
	optionsMatchLimit.SetMatchLimit(8192);
	
	CRegEx* re = CRegEx::NewLC(_L8("(\\w+)*b"), optionsMatchLimit);
	CHECK(re->PartialMatchL(good));
	CHECK(re->PartialMatchL(bad) == EFalse);
	CHECK(re->FullMatchL(good) == EFalse);
	CHECK(re->FullMatchL(bad) == EFalse);	
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	optionsMatchLimit.SetMatchLimit(1024);
	CRegEx* re2 = CRegEx::NewLC(_L8("(\\w+)*b"), optionsMatchLimit);
	CHECK(re2->PartialMatchL(good) == EFalse);   // because of match_limit
	CHECK(re2->PartialMatchL(bad) == EFalse);
	CHECK(re2->FullMatchL(good) == EFalse);
	CHECK(re2->FullMatchL(bad) == EFalse);
	CleanupStack::PopAndDestroy(re2);
	re2 = NULL;
	
	TRegExOptions optionsMathLimitRecursion;
	optionsMathLimitRecursion.SetMatchLimitRecursion(50);
	CRegEx* re3 = CRegEx::NewLC(_L8("(\\w+)*b"), optionsMathLimitRecursion);
	CHECK(re3->PartialMatchL(good));
	CHECK(re3->PartialMatchL(bad) == EFalse);
	CHECK(re3->FullMatchL(good) == EFalse);
	CHECK(re3->FullMatchL(bad) == EFalse);
	CleanupStack::PopAndDestroy(re3);
	re3 = NULL;
	
	optionsMathLimitRecursion.SetMatchLimitRecursion(10);
	CRegEx* re4 = CRegEx::NewLC(_L8("(\\w+)*b"), optionsMathLimitRecursion);
	CHECK(re4->PartialMatchL(good) == EFalse);
	CHECK(re4->PartialMatchL(bad) == EFalse);
	CHECK(re4->FullMatchL(good) == EFalse);
	CHECK(re4->FullMatchL(bad) == EFalse);
	CleanupStack::PopAndDestroy(re4);
	re4 = NULL;	
	}

// A meta-quoted string, interpreted as a pattern, should always match
// the original unquoted string.
static void TestQuoteMetaL(const TDesC8& aUnquoted, TRegExOptions aOptions =
		TRegExOptions())
	{
	HBufC8* quoted = CRegEx::QuoteMetaL(aUnquoted);
	CleanupStack::PushL(quoted);

	CRegEx* re = CRegEx::NewLC(*quoted, aOptions);
	CHECK(re->FullMatchL(aUnquoted));
	CleanupStack::PopAndDestroy(2, quoted); // re
	}

// A string containing meaningful regexp characters, which is then meta-
// quoted, should not generally match a string the unquoted string does.
static void NegativeTestQuoteMetaL(const TDesC8& aUnquoted,
		const TDesC8& aNotMatch, TRegExOptions aOptions = TRegExOptions())
	{
	HBufC8* quoted = CRegEx::QuoteMetaL(aUnquoted);
	CleanupStack::PushL(quoted);

	CRegEx* re = CRegEx::NewLC(*quoted, aOptions);
	CHECK(!re->FullMatchL(aNotMatch));
	CleanupStack::PopAndDestroy(2, quoted); // re
	}

// Tests that quoted meta characters match their original strings,
// and that a few things that shouldn't match indeed do not.
static void TestQuotaMetaSimpleL()
	{
	TestQuoteMetaL(_L8("foo"));
	TestQuoteMetaL(_L8("foo.bar"));
	TestQuoteMetaL(_L8("foo\\.bar"));
	TestQuoteMetaL(_L8("[1-9]"));
	TestQuoteMetaL(_L8("1.5-2.0?"));
	TestQuoteMetaL(_L8("\\d"));
	TestQuoteMetaL(_L8("Who doesn't like ice cream?"));
	TestQuoteMetaL(_L8("((a|b)c?d*e+[f-h]i)"));
	TestQuoteMetaL(_L8("((?!)xxx).*yyy"));
	TestQuoteMetaL(_L8("(["));
	TestQuoteMetaL(_L8("foo\0bar"));
	}

static void TestQuoteMetaSimpleNegativeL()
	{
	NegativeTestQuoteMetaL(_L8("foo"), _L8("bar"));
	NegativeTestQuoteMetaL(_L8("..."), _L8("bar"));
	NegativeTestQuoteMetaL(_L8("\\."), _L8("."));
	NegativeTestQuoteMetaL(_L8("\\."), _L8(".."));
	NegativeTestQuoteMetaL(_L8("(a)"), _L8("a"));
	NegativeTestQuoteMetaL(_L8("(a|b)"), _L8("a"));
	NegativeTestQuoteMetaL(_L8("(a|b)"), _L8("(a)"));
	NegativeTestQuoteMetaL(_L8("(a|b)"), _L8("a|b"));
	NegativeTestQuoteMetaL(_L8("[0-9]"), _L8("0"));
	NegativeTestQuoteMetaL(_L8("[0-9]"), _L8("0-9"));
	NegativeTestQuoteMetaL(_L8("[0-9]"), _L8("[9]"));
	NegativeTestQuoteMetaL(_L8("((?!)xxx)"), _L8("xxx"));
	}

static void TestQuoteMetaLatin1L()
	{
	TestQuoteMetaL(_L8("3\xb2 = 9"));
	}


static void TestQuoteMetaUtf8L()
	{
#ifdef SUPPORT_UTF8
	TRegExOptions options;
	options.SetUtf8(ETrue);
	TestQuoteMetaL(_L8("Pl\xc3\xa1\x63ido Domingo"), options);
	TestQuoteMetaL(_L8("xyz"), options);            // No fancy utf8
	TestQuoteMetaL(_L8("\xc2\xb0"), options);       // 2-byte utf8 (degree symbol)
	TestQuoteMetaL(_L8("27\xc2\xb0 degrees"), options);  // As a middle character
	TestQuoteMetaL(_L8("\xe2\x80\xb3"), options);   // 3-byte utf8 (double prime)
	TestQuoteMetaL(_L8("\xf0\x9d\x85\x9f"), options); // 4-byte utf8 (music note)
	TestQuoteMetaL(_L8("27\xc2\xb0")); // Interpreted as Latin-1, but should still work
	NegativeTestQuoteMetaL(_L8("27\xc2\xb0"),               // 2-byte utf (degree symbol)
			_L8("27\\\xc2\\\xb0"),
			options);
#endif
	}


static void TestQuoteMetaAllL()
	{
	console->Write(_L("Testing QuoteMeta\n"));

	TestQuotaMetaSimpleL();
	TestQuoteMetaSimpleNegativeL();
	TestQuoteMetaLatin1L();
	TestQuoteMetaUtf8L();
	}

//
// Options tests contributed by
// Giuseppe Maxia, CTO, Stardata s.r.l.
// July 2005
//
static void GetOneOptionResultL(
                const TDesC& aOptionName,
                const TDesC8& aRegEx,
                const TDesC8& aString,
                TRegExOptions& aOptions,
                TBool aFull,
                const TDesC8& aExpected)
	{
	console->Printf(_L("Testing Option <%S>\n"), &aOptionName);
	
	if(verboseMode)
		{
		TBuf<32> str;
		str.Copy(aString);
		TBuf<32> rx;
		rx.Copy(aRegEx);
		TBuf<32> expected;
		expected.Copy(aExpected);

		console->Printf(_L("/%S/ finds \"%S\" within \"%S\" \n"), &rx, &expected, &str);
		}
	
  TBuf8<32> captured;
  CRegEx* re = CRegEx::NewLC(aRegEx, aOptions);
  
  if (aFull)
	  {
	  CHECK(re->FullMatchL(aString, &captured));
	  }    
  else
	  {
	  CHECK(re->PartialMatchL(aString, &captured));
	  }   
  CHECK_EQ(captured, aExpected);
  
  CleanupStack::PopAndDestroy(re);
}

static void TestOneOptionL(
                const TDesC& aOptionName,
                const TDesC8& aRegEx,
                const TDesC8& aString,
                const TRegExOptions& aOptions,
                TBool aFull,
                TBool aAssertive = ETrue)
	{
	
	console->Printf(_L("Testing Option <%S>\n"), &aOptionName);
	if(verboseMode)
		{
		_LIT(KMatches, "matches");
		_LIT(KNoMatch, "doesn't match");
		TBuf<32> str;
		str.Copy(aString);
		TBuf<64> rx;
		rx.Copy(aRegEx);
		
		console->Printf(_L("'%S' %S /%S/ \n"),
	                  &str,
	                  (aAssertive? &KMatches() : &KNoMatch()),
	                  &rx);	
		}

	CRegEx* re = NULL;
	TRAPD(err, re = CRegEx::NewLC(aRegEx, aOptions); CleanupStack::Pop());
	
	if(err)
		{
		CHECK_EQ(aAssertive, EFalse);
		return;
		}
	
	CleanupStack::PushL(re);
	
	if (aAssertive)
		{
		if (aFull)
			{
			CHECK(re->FullMatchL(aString));
			}     
		else
			{
			CHECK(re->PartialMatchL(aString));
			}
		}
	else
		{
		if (aFull)
			{
			CHECK(!re->FullMatchL(aString));
			}		  
		else
			{
			CHECK(!re->PartialMatchL(aString));
			}
		}
	CleanupStack::PopAndDestroy(re);	
	}

static void TestCaseLessL()
	{
	TRegExOptions options;
	TRegExOptions options2;
	TRegExOptions caseless;
	caseless.SetCaseless(ETrue);

	options.SetCaseless(ETrue);
	TestOneOptionL(_L("CASELESS (class)"), _L8("HELLO"), _L8("hello"), options, EFalse);
	TestOneOptionL(_L("CASELESS (class2)"), _L8("HELLO"), _L8("hello"), options2.SetCaseless(ETrue), EFalse);
	TestOneOptionL(_L("CASELESS (class)"), _L8("^[A-Z]+$"), _L8("Hello"), options, EFalse);

	TestOneOptionL(_L("CASELESS (function)"), _L8("HELLO"), _L8("hello"), caseless, EFalse);
	TestOneOptionL(_L("CASELESS (function)"), _L8("^[A-Z]+$"), _L8("Hello"), caseless, EFalse);
	options.SetCaseless(EFalse);
	TestOneOptionL(_L("no CASELESS"), _L8("HELLO"), _L8("hello"), options, EFalse, EFalse);
	}

static void TestMultilineL()
	{
	TRegExOptions options;
	TRegExOptions options2;
	TRegExOptions multiline;
	multiline.SetMultiline(ETrue);

	TBuf8<32> str(_L8("HELLO\n" "cruel\n" "world\n"));

	options.SetMultiline(ETrue);
	TestOneOptionL(_L("MULTILINE (class)"), _L8("^cruel$"), str, options, EFalse);
	TestOneOptionL(_L("MULTILINE (class2)"), _L8("^cruel$"), str, options2.SetMultiline(ETrue), EFalse);
	TestOneOptionL(_L("MULTILINE (function)"), _L8("^cruel$"), str, multiline, EFalse);
	options.SetMultiline(EFalse);
	TestOneOptionL(_L("no MULTILINE"), _L8("^cruel$"), str, options, EFalse, EFalse);
	}

static void TestDotAllL()
	{
	TRegExOptions options;
	TRegExOptions options2;
	TRegExOptions dotAll;
	dotAll.SetDotAll(ETrue);

	TBuf8<32> str(_L8("HELLO\n" "cruel\n" "world"));

	options.SetDotAll(ETrue);
	TestOneOptionL(_L("DOTALL (class)"), _L8("HELLO.*world"),str, options, ETrue);
	TestOneOptionL(_L("DOTALL (class2)"), _L8("HELLO.*world"),str, options2.SetDotAll(ETrue), ETrue);
	TestOneOptionL(_L("DOTALL (function)"), _L8("HELLO.*world"),str, dotAll, ETrue);
	options.SetDotAll(EFalse);
	TestOneOptionL(_L("no DOTALL"), _L8("HELLO.*world"),str, options, ETrue, EFalse);
	}

static void TestDollarEndOnlyL()
	{
	TRegExOptions options;
	TRegExOptions options2;
	TRegExOptions dollarEndOnly;
	dollarEndOnly.SetDollarEndOnly(ETrue);

	TBuf8<32> str(_L8("HELLO world\n"));

	TestOneOptionL(_L("no DOLLAR_ENDONLY"), _L8("world$"), str, options, EFalse);
	options.SetDollarEndOnly(ETrue);
	TestOneOptionL(_L("DOLLAR_ENDONLY 1"), _L8("world$"), str, options, EFalse, EFalse);
	TestOneOptionL(_L("DOLLAR_ENDONLY 2"), _L8("world$"), str, options2.SetDollarEndOnly(ETrue), EFalse, EFalse);
	}


static void TestExtraL()
	{	
	TRegExOptions options;

	TBuf8<32> str(_L8("HELLO"));
	options.SetExtra(ETrue);
	TestOneOptionL(_L("EXTRA 1"), _L8("\\HELL\\O"), str, options, ETrue, EFalse );
	TestOneOptionL(_L("EXTRA 2"), _L8("\\HELL\\O"), str, TRegExOptions().SetExtra(ETrue), ETrue, EFalse );
	options.SetExtra(EFalse);
	TestOneOptionL(_L("no EXTRA"), _L8("\\HELL\\O"), str, options, ETrue );
	}

static void TestExtendedL()
	{
	TRegExOptions options;
	TRegExOptions options2;
	TRegExOptions extended;
	extended.SetExtended(ETrue);

	TBuf8<32> str(_L8("HELLO world"));

	options.SetExtended(ETrue);
	TestOneOptionL(_L("EXTENDED (class)"), _L8("HELLO world"), str, options, EFalse, EFalse);
	TestOneOptionL(_L("EXTENDED (class2)"), _L8("HELLO world"), str, options2.SetExtended(ETrue), EFalse, EFalse);
	TestOneOptionL(_L("EXTENDED (class)"), _L8("^ HE L{2} O \\s+        \\w+ $      "), str, options, EFalse);

	TestOneOptionL(_L("EXTENDED (function)"), _L8("HELLO world"), str, extended, EFalse, EFalse);
	TestOneOptionL(_L("EXTENDED (function)"), _L8("^ HE L{2} O \\s+        \\w+ $      "), str, extended, EFalse);

	options.SetExtended(EFalse);
	TestOneOptionL(_L("no EXTENDED"), _L8("HELLO world"), str, options, EFalse);
	}

static void TestNoAutoCaptureL()
	{
	TRegExOptions options;
	TBuf8<32> str(_L8("HELLO world"));
	TBuf8<5> captured;
	console->Write(_L("Testing Option <no NO_AUTO_CAPTURE>\n"));
	if (verboseMode)
		{
		console->Write(_L("parentheses capture text\n"));
		}

	CRegEx* re = CRegEx::NewLC(_L8("(world|universe)$"), options);
	CHECK(re->ExtractL(_L8("\\1"), str , captured));
	CHECK_EQ(captured, _L8("world"));
	options.SetNoAutoCapture(ETrue);
	console->Write(_L("testing Option <NO_AUTO_CAPTURE>\n"));

	if (verboseMode)
		{
		console->Write(_L("parentheses do not capture text\n"));
		}

	re->ExtractL(_L8("\\1"),str, captured);
	CHECK_EQ(captured, _L8("world"));
	
	CleanupStack::PopAndDestroy(re);
	}

static void TestUngreedyL()
	{
	TRegExOptions options;
	TRegExOptions ungreedy;
	ungreedy.SetUngreedy(ETrue);

	TBuf8<32> str(_L8("HELLO, 'this' is the 'world'"));

	options.SetUngreedy(ETrue);
	GetOneOptionResultL(_L("UNGREEDY 1"), _L8("('.*')"), str, options, EFalse, _L8("'this'") );
	GetOneOptionResultL(_L("UNGREEDY 2"), _L8("('.*')"), str, ungreedy, EFalse, _L8("'this'") );
	GetOneOptionResultL(_L("UNGREEDY"), _L8("('.*?')"), str, options, EFalse, _L8("'this' is the 'world'") );

	options.SetUngreedy(EFalse);
	GetOneOptionResultL(_L("no UNGREEDY"), _L8("('.*')"), str, options, EFalse, _L8("'this' is the 'world'") );
	GetOneOptionResultL(_L("no UNGREEDY"), _L8("('.*?')"), str, options, EFalse, _L8("'this'") );
	}

static void TestAllOptionsL()
	{
	TBuf8<32> str(_L8("HELLO\n" "cruel\n" "world"));
	TRegExOptions options;
	options.SetAllOptions(EPcreCaseless | EPcreDotAll);

	TestOneOptionL(_L("all_options (CASELESS|DOTALL)"), _L8("^hello.*WORLD"), str , options, EFalse);
	options.SetAllOptions(0);
	TestOneOptionL(_L("all_options (0)"), _L8("^hello.*WORLD"), str , options, EFalse, EFalse);
	options.SetAllOptions(EPcreMultiline | EPcreExtended);

	TestOneOptionL(_L("all_options (MULTILINE|EXTENDED)"), _L8(" ^ c r u e l $ "), str, options, EFalse);
	TestOneOptionL(_L("all_options (MULTILINE|EXTENDED) with constructor"),
	_L8(" ^ c r u e l $ "),
	str,
	TRegExOptions(EPcreMultiline | EPcreExtended),
	EFalse);

	TRegExOptions multilineExtended = TRegExOptions().SetMultiline(ETrue);
	multilineExtended.SetExtended(ETrue);

	TestOneOptionL(_L("all_options (MULTILINE|EXTENDED) with concatenation"),
	_L8(" ^ c r u e l $ "),
	str,
	multilineExtended, EFalse);

	options.SetAllOptions(0);
	TestOneOptionL(_L("all_options (0)"), _L8("^ c r u e l $"), str, options, EFalse, EFalse);
	}

static void TestOptionsL()
	{
	console->Write(_L("Testing Options\n"));
	TestCaseLessL();
	TestMultilineL();
	TestDotAllL();
	TestDollarEndOnlyL();
	TestExtendedL();
	TestNoAutoCaptureL();
	TestUngreedyL();
	TestExtraL();
	TestAllOptionsL();
	}


LOCAL_C void MainL()
	{
	
	_LIT(KTimingArg1, "timing1");
	_LIT(KTimingArg2, "timing2");
	_LIT(KTimingArg3, "timing3");
	
	CCommandLineArguments* args = CCommandLineArguments::NewLC();

	// Treat any flag as --help
	if(args->Count() > 1 && args->Arg(1)[0] == '-')
		{
		console->Printf(_L("Usage: %S [%S|%S|%S num-iters]\n"), &(args->Arg(0)), &KTimingArg1(), &KTimingArg2(), &KTimingArg3());
		console->Write(_L("If 'timingX ###' is specified, run the given timing test\n"));
		console->Write(_L("with the given number of iterations, rather than running\n"));
		console->Write(_L("the default corectness test.\n"));
		CleanupStack::PopAndDestroy(args);
		return;		
		}
	if(args->Count() > 1)
		{
		if(args->Count() == 2)
			{
			console->Write(_L("timing mode needs a num-iters argument\n"));
			CleanupStack::PopAndDestroy(args);
			return;			
			}
		
		TLex lex(args->Arg(2));
		TInt iters;
		TInt err = lex.Val(iters);
		
		if(err || iters == 0)
			{
			CleanupStack::PopAndDestroy(args);
			return;	
			}
		
		if(args->Arg(1) == KTimingArg1)
			{
			Timing1L(iters);
			}
		else if(args->Arg(1) == KTimingArg2)
			{
			Timing2L(iters);
			}
		else if(args->Arg(1) == KTimingArg3)
			{
			Timing3L(iters);
			}		
		else
			{
			console->Printf(_L("Unknown argument '%S'\n"), &(args->Arg(1)));
			}
		CleanupStack::PopAndDestroy(args);
		return;			
		}
	CleanupStack::PopAndDestroy(args);
	
	console->Write(_L("Testing FullMatch\n"));

	CRegEx* re = NULL;
	TInt i;
	TBuf8<10> s;
	
	/***** FullMatch with no args *****/
	re = CRegEx::NewLC(_L8("h.*o"));
	CHECK(re->FullMatchL(_L8("hello")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("h.*o")); // Must be anchored at front
	CHECK(!re->FullMatchL(_L8("othello")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("h.*o")); // Must be anchored at end
	CHECK(!re->FullMatchL(_L8("hello!")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("a*")); // Fullmatch with normal op
	CHECK(re->FullMatchL(_L8("aaaa")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("a*?")); // Fullmatch with nongreedy op
	CHECK(re->FullMatchL(_L8("aaaa")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("a*?\\z")); // Two unusual ops
	CHECK(re->FullMatchL(_L8("aaaa")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	/***** FullMatch with args *****/

	// Zero-arg
	re = CRegEx::NewLC(_L8("\\d+"));
	CHECK(re->FullMatchL(_L8("1001")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	

	// Single-arg
	re = CRegEx::NewLC(_L8("(\\d+)"));
	CHECK(re->FullMatchL(_L8("1001"), &i));
	CHECK_EQ(i, 1001);
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(-?\\d+)"));
	CHECK(re->FullMatchL(_L8("-123"), &i));
	CHECK_EQ(i, -123);
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("()\\d+"));
	CHECK(!re->FullMatchL(_L8("10"), &i));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(\\d+)"));
	CHECK(!re->FullMatchL(_L8("1234567890123456789012345678901234567890"), &i));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	

	// Digits surrounding integer-arg
	re = CRegEx::NewLC(_L8("1(\\d*)4"));
	CHECK(re->FullMatchL(_L8("1234"), &i));
	CHECK_EQ(i, 23);
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(\\d)\\d+"));
	CHECK(re->FullMatchL(_L8("1234"), &i));
	CHECK_EQ(i, 1);
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(-\\d)\\d+"));
	CHECK(re->FullMatchL(_L8("-1234"), &i));
	CHECK_EQ(i, -1);
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(\\d)"));
	CHECK(re->PartialMatchL(_L8("1234"), &i));
	CHECK_EQ(i, 1);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("(-\\d)"));
	CHECK(re->PartialMatchL(_L8("-1234"), &i));
	CHECK_EQ(i, -1);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	// String-arg
	re = CRegEx::NewLC(_L8("h(.*)o"));
	CHECK(re->PartialMatchL(_L8("hello"), &s));
	CHECK_EQ(s, _L8("ell"));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	

	// Multi-arg
	re = CRegEx::NewLC(_L8("(\\w+):(\\d+)"));
	CHECK(re->PartialMatchL(_L8("ruby:1234"), &s, &i));
	CHECK_EQ(s.Length(), 4);
	CHECK_EQ(s, _L8("ruby"));
	CHECK_EQ(i, 1234);
	CleanupStack::PopAndDestroy(re);
	re = NULL;		

	// Ignore non-void* NULL arg
	re = CRegEx::NewLC(_L8("he(.*)lo"));
	CHECK(re->FullMatchL(_L8("hello"), (TDes8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("123"), (TInt8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;			

	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("12345"), (TInt16*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;			

	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("1234567890"), (TInt32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;			

	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("1234567890123456"), (TInt64*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("1234567890"), (TInt*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("123"), (TUint8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("12345"), (TUint16*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		

	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("1234567890"), (TUint32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("1234567890"), (TUint*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("123.4567890123456"), (TReal32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("123.4567890123456"), (TReal64*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(re->FullMatchL(_L8("123.4567890123456"), (TReal*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	// Fail on non-void* NULL arg if the match doesn't parse for the given type.
	re = CRegEx::NewLC(_L8("h(.*)lo"));
	CHECK(!re->FullMatchL(_L8("hello"), &s, (TDes8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TInt8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TInt16*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TInt32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	

	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TInt64*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TInt*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TUint8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TUint16*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TUint32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TUint*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TReal32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TReal64*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("hello"), (TReal*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("1234"), (TInt8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("123456"), (TInt16*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("12345678901"), (TInt32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("12345678901"), (TInt*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("1234"), (TUint8*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	

	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("123456"), (TUint16*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("12345678901"), (TUint32*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(.*)"));
	CHECK(!re->FullMatchL(_L8("12345678901"), (TUint*)NULL));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	

	// Ignored arg
	re = CRegEx::NewLC(_L8("(\\w+)(:)(\\d+)"));
	CHECK(re->FullMatchL(_L8("ruby:1234"), &s, (TAny*)NULL, &i));
	CleanupStack::PopAndDestroy(re);
	CHECK_EQ(s, _L8("ruby"));
	CHECK_EQ(i, 1234);
	re = NULL;		

// Type tests
		{
		TChar c;
		re = CRegEx::NewLC(_L8("(H)ello"));
		CHECK(re->FullMatchL(_L8("Hello"), &c));
		CHECK_EQ(c, 'H');
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		{
		TInt8 v;
		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-100"), &v));
		CHECK_EQ(v, -100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("127"), &v));
		CHECK_EQ(v, KMaxTInt8);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-128"), &v));
		CHECK_EQ(v, KMinTInt8);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("128"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("-129"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		{
		TUint8 v;
		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("128"), &v));
		CHECK_EQ(v, KMaxTInt8 + 1);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("255"), &v));
		CHECK_EQ(v, KMaxTUint8);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("256"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("-100"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		{
		TInt16 v;
		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-100"), &v));
		CHECK_EQ(v, -100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("32767"), &v));
		CHECK_EQ(v, KMaxTInt16);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-32768"), &v));
		CHECK_EQ(v, KMinTInt16);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("32768"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("-32769"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		{
		TUint16 v;
		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("32767"), &v));
		CHECK_EQ(v, KMaxTInt16);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("65535"), &v));
		CHECK_EQ(v, KMaxTUint16);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("655356"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		
		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("-100"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;		
		}		
		{
		TInt32 v;
		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-100"), &v));
		CHECK_EQ(v, -100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("2147483647"), &v));
		CHECK_EQ(v, KMaxTInt32);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-2147483648"), &v));
		CHECK_EQ(v, KMinTInt32);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("2147483648"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("-2147483649"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}	
		{
		TUint32 v;
		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("2147483647"), &v));
		CHECK_EQ(v, KMaxTInt32);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("4294967295"), &v));
		CHECK_EQ(v, KMaxTUint32);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("4294967296"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		
		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("-100"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;		
		}
		{
		TInt64 v;
		// Note  TLex treats TInt64 as unsigned
		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("9223372036854775807"), &v));
		CHECK_EQ(v, KMaxTInt64);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("-100"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("-9223372036854775808"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("9223372036854775808"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("-9223372036854775809"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		{
		TInt v;
		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-100"), &v));
		CHECK_EQ(v, -100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("2147483647"), &v));
		CHECK_EQ(v, KMaxTInt);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(re->FullMatchL(_L8("-2147483648"), &v));
		CHECK_EQ(v, KMinTInt);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("2147483648"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(-?\\d+)"));
		CHECK(!re->FullMatchL(_L8("-2147483649"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}	
		{
		TUint v;
		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);		
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("2147483647"), &v));
		CHECK_EQ(v, KMaxTInt);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(re->FullMatchL(_L8("4294967295"), &v));
		CHECK_EQ(v, KMaxTUint);		
		CleanupStack::PopAndDestroy(re);

		re = NULL;

		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("4294967296"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		
		re = CRegEx::NewLC(_L8("(\\d+)"));
		CHECK(!re->FullMatchL(_L8("-100"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;		
		}		
		{
		TReal32 v;
		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("-100"), &v));
		CHECK_EQ(v, -100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("3.4028234663852885981170418348452e+38"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("-1.17549435E-38f"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		{
		TReal64 v;
		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("100"), &v));
		CHECK_EQ(v, 100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("-100"), &v));
		CHECK_EQ(v, -100);
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("1.7976931348623157E+308"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;

		re = CRegEx::NewLC(_L8("(.*)"));
		CHECK(re->FullMatchL(_L8("-2.2250738585072015E-308"), &v));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		
	// Check that matching is fully anchored
	re = CRegEx::NewLC(_L8("(\\d+)"));
	CHECK(!re->FullMatchL(_L8("x1001"), &i));
	CleanupStack::PopAndDestroy(re);
	re = NULL;	
	
	re = CRegEx::NewLC(_L8("(\\d+)"));
	CHECK(!re->FullMatchL(_L8("1001x"), &i));
	CleanupStack::PopAndDestroy(re);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("x(\\d+)"));
	CHECK(re->FullMatchL(_L8("x1001"), &i));
	CleanupStack::PopAndDestroy(re);
	CHECK_EQ(i, 1001);
	re = NULL;		
	
	re = CRegEx::NewLC(_L8("(\\d+)x"));
	CHECK(re->FullMatchL(_L8("1001x"), &i));
	CleanupStack::PopAndDestroy(re);
	CHECK_EQ(i, 1001);
	re = NULL;
	
	// Braces
	re = CRegEx::NewLC(_L8("[0-9a-f+.-]{5,}"));
	CHECK(re->FullMatchL(_L8("0abcd")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("[0-9a-f+.-]{5,}"));
	CHECK(re->FullMatchL(_L8("0abcde")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("[0-9a-f+.-]{5,}"));
	CHECK(!re->FullMatchL(_L8("0abc")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	// Complicated RE
	re = CRegEx::NewLC(_L8("foo|bar|[A-Z]"));
	CHECK(re->FullMatchL(_L8("foo")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("foo|bar|[A-Z]"));
	CHECK(re->FullMatchL(_L8("bar")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("foo|bar|[A-Z]"));
	CHECK(re->FullMatchL(_L8("X")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	re = CRegEx::NewLC(_L8("foo|bar|[A-Z]"));
	CHECK(!re->FullMatchL(_L8("XY")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	
	// Check full-match handling (needs '$' tacked on internally)
	re = CRegEx::NewLC(_L8("fo|foo"));
	CHECK(re->FullMatchL(_L8("fo")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("fo|foo"));
	CHECK(re->FullMatchL(_L8("foo")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("fo|foo$"));
	CHECK(re->FullMatchL(_L8("fo")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("fo|foo$"));
	CHECK(re->FullMatchL(_L8("foo")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("foo$"));
	CHECK(re->FullMatchL(_L8("foo")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("foo\\$"));
	CHECK(!re->FullMatchL(_L8("foo$bar")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("fo|bar"));
	CHECK(!re->FullMatchL(_L8("fox")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	// Uncomment the following if we change the handling of '$' to
	// prevent it from matching a trailing newline
	if (false)
		{
		// Check that we don't get bitten by pcre's special handling of a
		// '\n' at the end of the string matching '$'
		re = CRegEx::NewLC(_L8("foo$"));
		CHECK(!re->PartialMatchL(_L8("foo\n")));
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}

	// Number of args
	TInt a[16];

	re = CRegEx::NewLC(_L8(""));
	CHECK(re->FullMatchL(_L8("")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	memset(a, 0, sizeof(0));
	re = CRegEx::NewLC(_L8("(\\d){1}"));
	CHECK(re->FullMatchL(_L8("1"), &a[0]));
	CHECK_EQ(a[0], 1);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	memset(a, 0, sizeof(0));
	re = CRegEx::NewLC(_L8("(\\d)(\\d)"));
	CHECK(re->FullMatchL(_L8("12"), &a[0], &a[1]));
	CHECK_EQ(a[0], 1);
	CHECK_EQ(a[1], 2);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	memset(a, 0, sizeof(0));
	re = CRegEx::NewLC(_L8("(\\d)(\\d)(\\d)"));
	CHECK(re->FullMatchL(_L8("123"), &a[0], &a[1], &a[2]));
	CHECK_EQ(a[0], 1);
	CHECK_EQ(a[1], 2);
	CHECK_EQ(a[2], 3);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	memset(a, 0, sizeof(0));
	re = CRegEx::NewLC(_L8("(\\d)(\\d)(\\d)(\\d)"));
	CHECK(re->FullMatchL(_L8("1234"), &a[0], &a[1], &a[2], &a[3]));
	CHECK_EQ(a[0], 1);
	CHECK_EQ(a[1], 2);
	CHECK_EQ(a[2], 3);
	CHECK_EQ(a[3], 4);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	// Currently maximum args == 4
	/*
	memset(a, 0, sizeof(0));
	re = CRegEx::NewLC(_L8("(\\d)(\\d)(\\d)(\\d)(\\d)"));
	CHECK(re->FullMatchL(_L8("12345"), &a[0], &a[1], &a[2], &a[3], &a[4]));
	CHECK_EQ(a[0], 1);
	CHECK_EQ(a[1], 2);
	CHECK_EQ(a[2], 3);
	CHECK_EQ(a[3], 4);
	CHECK_EQ(a[4], 5);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	memset(a, 0, sizeof(0));
	re = CRegEx::NewLC(_L8("(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)"));
	CHECK(re->FullMatchL(_L8("123456"), &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]));
	CHECK_EQ(a[0], 1);
	CHECK_EQ(a[1], 2);
	CHECK_EQ(a[2], 3);
	CHECK_EQ(a[3], 4);
	CHECK_EQ(a[4], 5);
	CHECK_EQ(a[5], 6);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	memset(a, 0, sizeof(0));
	re = CRegEx::NewLC(_L8("(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)"));
	CHECK(re->FullMatchL(_L8("1234567"), &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6]));
	CHECK_EQ(a[0], 1);
	CHECK_EQ(a[1], 2);
	CHECK_EQ(a[2], 3);
	CHECK_EQ(a[3], 4);
	CHECK_EQ(a[4], 5);
	CHECK_EQ(a[5], 6);
	CHECK_EQ(a[6], 7);
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	memset(a, 0, sizeof(0));
	re
			= CRegEx::NewLC(
					_L8("(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)"));
	CHECK(re->FullMatchL(_L8("1234567890123456"), &a[0], &a[1], &a[2], &a[3],
					&a[4], &a[5], &a[6], &a[7],
					&a[8], &a[9], &a[10], &a[11],
					&a[12],&a[13], &a[14], &a[15]));
	CHECK_EQ(a[0], 1);
	CHECK_EQ(a[1], 2);
	CHECK_EQ(a[2], 3);
	CHECK_EQ(a[3], 4);
	CHECK_EQ(a[4], 5);
	CHECK_EQ(a[5], 6);
	CHECK_EQ(a[6], 7);
	CHECK_EQ(a[7], 8);
	CHECK_EQ(a[8], 9);
	CHECK_EQ(a[9], 0);
	CHECK_EQ(a[10], 1);
	CHECK_EQ(a[11], 2);
	CHECK_EQ(a[12], 3);
	CHECK_EQ(a[13], 4);
	CHECK_EQ(a[14], 5);
	CHECK_EQ(a[15], 6);
	CleanupStack::PopAndDestroy(re);
	re = NULL;
	 */
	
	/***** PartialMatch *****/
	console->Write(_L("Testing PartialMatch\n"));

	re = CRegEx::NewLC(_L8("h.*o"));
	CHECK(re->PartialMatchL(_L8("hello")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("h.*o"));
	CHECK(re->PartialMatchL(_L8("othello")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("h.*o"));
	CHECK(re->PartialMatchL(_L8("hello!")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	re = CRegEx::NewLC(_L8("((((((((((((((((((((x))))))))))))))))))))"));
	CHECK(re->PartialMatchL(_L8("x")));
	CleanupStack::PopAndDestroy(re);
	re = NULL;

	/***** other tests *****/

	RadixTestsL();
	TestReplaceL();
	TestExtractL();
	TestConsumeL();
	TestFindAndConsumeL();
	TestQuoteMetaAllL();
	TestMatchNumberPeculiarityL();
	
	// Check the pattern() accessor
		{
		_LIT8(KPattern, "http://([^/]+)/.*");
		re = CRegEx::NewLC(KPattern());
		CHECK_EQ(KPattern(), re->Pattern());
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}	
		// Check RE error field.
		{
		re = CRegEx::NewLC(_L8("foo"));
		CHECK_EQ(re->Error(), KErrNone);  // Must have no error
		CleanupStack::PopAndDestroy(re);
		re = NULL;
		}
		
#ifdef SUPPORT_UTF8
	  // Check UTF-8 handling
		{
		console->Write(_L("Testing UTF-8 handling\n"));

		// Three Japanese characters (nihongo)
		TBuf8<9> utf8String(_L8("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"));
		TBuf8<5> utf8Pattern(_L8(".\xE6\x9C\xAC."));
		TRegExOptions utf8Options;
		utf8Options.SetUtf8(ETrue);	
	
		// Both should match in either mode, bytes or UTF-8
		CRegEx* re_test1 = CRegEx::NewLC(_L8("........."));
		CHECK(re_test1->FullMatchL(utf8String));
		CleanupStack::PopAndDestroy(re_test1);
		re_test1 = NULL;
		
		CRegEx* re_test2 = CRegEx::NewLC(_L8("..."), utf8Options);
		CHECK(re_test2->FullMatchL(utf8String));
		CleanupStack::PopAndDestroy(re_test2);
		re_test2 = NULL;		

		// Check that '.' matches one byte or UTF-8 character
		// according to the mode.
		TBuf8<3> ss;
		
		CRegEx* re_test3 = CRegEx::NewLC(_L8("(.)"));
		CHECK(re_test3->PartialMatchL(utf8String, &ss));
		CHECK_EQ(ss, _L8("\xE6"));
		CleanupStack::PopAndDestroy(re_test3);
		re_test3 = NULL;	
		
		CRegEx* re_test4 = CRegEx::NewLC(_L8("(.)"), utf8Options);
		CHECK(re_test4->PartialMatchL(utf8String, &ss));
		CHECK_EQ(ss, _L8("\xE6\x97\xA5"));
		CleanupStack::PopAndDestroy(re_test4);
		re_test4 = NULL;	
		
		// Check that string matches itself in either mode
		CRegEx* re_test5 = CRegEx::NewLC(utf8String);
		CHECK(re_test5->FullMatchL(utf8String));
		CleanupStack::PopAndDestroy(re_test5);
		re_test5 = NULL;	
		
		CRegEx* re_test6 = CRegEx::NewLC(utf8String, utf8Options);
		CHECK(re_test6->FullMatchL(utf8String));
		CleanupStack::PopAndDestroy(re_test6);
		re_test6 = NULL;			
		
		// Check that pattern matches string only in UTF8 mode
		CRegEx* re_test7 = CRegEx::NewLC(utf8Pattern);
		CHECK(!re_test7->FullMatchL(utf8String));
		CleanupStack::PopAndDestroy(re_test7);
		re_test7 = NULL;
		
		CRegEx* re_test8 = CRegEx::NewLC(utf8Pattern, utf8Options);
		CHECK(re_test8->FullMatchL(utf8String));
		CleanupStack::PopAndDestroy(re_test8);
		re_test8 = NULL;
		}

	// Check that ungreedy, UTF8 regular expressions don't match when they
	// oughtn't -- see bug 82246.
		{
		// This code always worked.
		_LIT8(KPattern, "\\w+X");
		_LIT8(KTarget, "a aX");

		TRegExOptions utf8Options;
		utf8Options.SetUtf8(ETrue);	
		
		CRegEx* matchSentence = CRegEx::NewLC(KPattern());
		CHECK(!matchSentence->FullMatchL(KTarget()));
		CleanupStack::PopAndDestroy(matchSentence);
		matchSentence = NULL;
		
		CRegEx* matchSentenceRe = CRegEx::NewLC(KPattern(), utf8Options);
		CHECK(!matchSentenceRe->FullMatchL(KTarget()));
		CleanupStack::PopAndDestroy(matchSentenceRe);
		matchSentenceRe = NULL;
		}
		{
		_LIT8(KPattern, "(?U)\\w+X");
		_LIT8(KTarget, "a aX");

		TRegExOptions utf8Options;
		utf8Options.SetUtf8(ETrue);	
		
		CRegEx* matchSentence = CRegEx::NewLC(KPattern());
		CHECK(!matchSentence->FullMatchL(KTarget()));
		CleanupStack::PopAndDestroy(matchSentence);
		matchSentence = NULL;
		
		CRegEx* matchSentenceRe = CRegEx::NewLC(KPattern(), utf8Options);
		CHECK(!matchSentenceRe->FullMatchL(KTarget()));
		CleanupStack::PopAndDestroy(matchSentenceRe);
		matchSentenceRe = NULL;
		}
		
#endif  /* def SUPPORT_UTF8 */	

  console->Write(_L("Testing error reporting\n"));
	  {
	  TRAPD(err, re = CRegEx::NewLC(_L8("a\\1")); CleanupStack::PopAndDestroy());
	  CHECK(err != KErrNone);
	  re = NULL;
	  }
	  {
	  TRAPD(err, re = CRegEx::NewLC(_L8("a[x")); CleanupStack::PopAndDestroy());
	  CHECK(err != KErrNone);
	  re = NULL;
	  }
	  {
	  TRAPD(err, re = CRegEx::NewLC(_L8("a[z-a]")); CleanupStack::PopAndDestroy());
	  CHECK(err != KErrNone);
	  re = NULL;
	  }
	  {
	  TRAPD(err, re = CRegEx::NewLC(_L8("a[[:foobar:]]")); CleanupStack::PopAndDestroy());
	  CHECK(err != KErrNone);
	  re = NULL;
	  }
	  {
	  TRAPD(err, re = CRegEx::NewLC(_L8("a(b")); CleanupStack::PopAndDestroy());
	  CHECK(err != KErrNone);
	  re = NULL;
	  }
	  {
	  TRAPD(err, re = CRegEx::NewLC(_L8("a\\")); CleanupStack::PopAndDestroy());
	  CHECK(err != KErrNone);
	  re = NULL;
	  }
	  
	  // Test that recursion is stopped
	  TestRecursionL();
	  
	  // Test Options
	  TestOptionsL();

	  // Done
	  console->Write(_L("OK\n"));
	}
LOCAL_C void DoStartL()
	{
	// Create active scheduler (to run active objects)
	CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
	CleanupStack::PushL(scheduler);
	CActiveScheduler::Install(scheduler);

	MainL();

	// Delete active scheduler
	CleanupStack::PopAndDestroy(scheduler);
	}

//  Global Functions

GLDEF_C TInt E32Main()
	{
	// Create cleanup stack
	__UHEAP_MARK;
	CTrapCleanup* cleanup = CTrapCleanup::New();
	// Create output console
	TRAPD(createError, console = Console::NewL(KTextConsoleTitle, TSize(
							KConsFullScreen, KConsFullScreen)));
	if (createError)
		return createError;
	// Run application code inside TRAP harness, wait keypress when terminated
	TRAPD(mainError, DoStartL());
	if (mainError)
		console->Printf(KTextFailed, mainError);
	console->Printf(KTextPressAnyKey);
	console->Getch();

	delete console;
	delete cleanup;
	__UHEAP_MARKEND;
	return KErrNone;
	}
