/*
* Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include "tbasicmathsfb.h"
#include "t_input.h"
#include "t_output.h"
#include <bigint.h>
#include <random.h>

CTestAction* CBasicMathsFB::NewL(RFs& aFs, CConsoleBase& aConsole, 
	Output& aOut, const TTestActionSpec& aTestActionSpec)
	{
	CTestAction* self = CBasicMathsFB::NewLC(aFs, aConsole,
		aOut, aTestActionSpec);
	CleanupStack::Pop();
	return self;
	}

CTestAction* CBasicMathsFB::NewLC(RFs& aFs, CConsoleBase& aConsole, 
	Output& aOut, const TTestActionSpec& aTestActionSpec)
	{
	CBasicMathsFB* self = new(ELeave) CBasicMathsFB(aFs, aConsole, aOut);
	CleanupStack::PushL(self);
	self->ConstructL(aTestActionSpec);
	return self;
	}

CBasicMathsFB::~CBasicMathsFB()
	{
	delete iBody;
	}

CBasicMathsFB::CBasicMathsFB(RFs& aFs, CConsoleBase& aConsole, Output& aOut)
	: CTestAction(aConsole, aOut), iFs(aFs)
	{
	}

void CBasicMathsFB::ConstructL(const TTestActionSpec& aTestActionSpec)
	{
	CTestAction::ConstructL(aTestActionSpec);

	iBody = HBufC8::NewL(aTestActionSpec.iActionBody.Length());
	iBody->Des().Copy(aTestActionSpec.iActionBody);

	//HBufC8* length = Input::ParseElementHexL(*iBody, _L8("<bits>"));
	TUint bits = Input::ParseIntElement(*iBody, _L8("<bits>"), _L8("</bits>"));
	// the final /7 gives the number of times we have to increment by 7 to get
	// to that number of bytes and hence bits.
	iIterations = ((bits+7)/8)/7 + 1;
	}

void CBasicMathsFB::DoPerformPrerequisite(TRequestStatus& aStatus)
	{
	TRequestStatus* status = &aStatus;
	User::RequestComplete(status, KErrNone);
	iActionState = CTestAction::EAction;
	}

void CBasicMathsFB::DoPerformPostrequisite(TRequestStatus& aStatus)
	{
	TRequestStatus* status = &aStatus;
	iFinished = ETrue;
	User::RequestComplete(status, KErrNone);
	}

void CBasicMathsFB::DoReportAction(void)
	{
	}

void CBasicMathsFB::DoCheckResult(TInt)
	{
	}

void CBasicMathsFB::PerformAction(TRequestStatus& aStatus)
	{
	__UHEAP_MARK;
	TRequestStatus* status = &aStatus;
	iResult = ETrue;

	//min max values for NewRandomLC call
	RInteger min = RInteger::NewL(10);
	CleanupStack::PushL(min);
	RInteger max = RInteger::NewL(100);
	CleanupStack::PushL(max);
	
	//Generate iIterations*7 byte random sequences we are using 7 as it's a generator
	//mod 8.  Thus we'll cycle through every value (0-7) every 8 iterations.
	//This gives us a better feeling that certain byte lengths (and thus bit
	//lengths as the byte is chosen randomly) don't have errors.
	for(TUint i=1; i<iIterations; i++)
		{ 
		HBufC8* buf = HBufC8::NewMaxLC(i*7);
		TPtr8 ptr = buf->Des();
		TRandom::RandomL(ptr);

		//This is this iteration's random number
		RInteger initial = RInteger::NewL(ptr);
		CleanupStack::PushL(initial);

		//get a number x | 10 < x < 100
		RInteger crange = RInteger::NewRandomL(min, max);
		CleanupStack::PushL(crange);
		TUint range = crange.ConvertToLongL();
		CleanupStack::PopAndDestroy(); //crange

		AddSub(initial, range);
		MulDiv(initial, range);
		//GCD
		CleanupStack::PopAndDestroy(); //initial
		CleanupStack::PopAndDestroy();//buf
		iConsole.Printf(_L("."));
		}
	
	//Test a single iteration where the initial random number is less than a
	//word so the division and modulo routines that take words rather than
	//TIntegers can run.
	//do
		{
		//This is this iteration's random number
		RInteger initial = RInteger::NewRandomL(31);
		CleanupStack::PushL(initial);
		//get a number x | 10 < x < 100
		RInteger crange = RInteger::NewRandomL(min, max);
		CleanupStack::PushL(crange);
		TUint range = crange.ConvertToLongL();
		CleanupStack::PopAndDestroy(&crange); //crange

		AddSub(initial, range);
		MulDiv(initial, range);
		CleanupStack::PopAndDestroy(&initial); //initial
		iConsole.Printf(_L("."));
		} //while (0);

	CleanupStack::PopAndDestroy();//max
	CleanupStack::PopAndDestroy(); //min
	
	MiscDivL();
	
	User::RequestComplete(status, KErrNone);
	iActionState = CTestAction::EPostrequisite;
	__UHEAP_MARK;
	}

void CBasicMathsFB::AddSub(const TInteger& aInitial, TUint aRange)
	{
	__UHEAP_MARK;
	//This is the copy we are going to do stuff to
	RInteger a = RInteger::NewL(aInitial);
	CleanupStack::PushL(a);

	// compute a*aRange using doubling
	TUint j=1;
	for(; j<aRange; j++)
		{
		a += aInitial;
		}

	//b = a*aRange;
	RInteger b = RInteger::NewL(a);
	CleanupStack::PushL(b);
	//compute (a*aRange)/aRange using subtraction
	for(j=1; j<aRange; j++)
		{
		b -= aInitial;
		}
	// b should be the same as the initial value
	if( b != aInitial )
		{
		iResult = EFalse;
		iOut.writeString(_L("AddSub Failure:"));
		iOut.writeNewLine();
		}

	RInteger c = RInteger::NewL(aInitial);
	CleanupStack::PushL(c);
	// compute a*aRange using normal multiplication
	c *= aRange;
	
	// c and a should now be the same
	if( c != a )
		{ 
		iResult = EFalse;
		}

	RInteger d = RInteger::NewL(a);
	CleanupStack::PushL(d);
	//compute (a*aRange)/aRange using normal division
	d /= aRange;
	if( d != aInitial )
		{
		iResult = EFalse;
		}
	RInteger e = RInteger::NewL(a);
	CleanupStack::PushL(e);
	e %= aRange;
	// (a*aRange)%aRange == 0
	if( e != 0 )
		{
		iResult = EFalse;
		}
	CleanupStack::PopAndDestroy(5); //e,d,c,b,a
	__UHEAP_MARKEND;
	}

void CBasicMathsFB::MulDiv(const TInteger& aInitial, TUint aRange)
	{
	__UHEAP_MARK;
	//This is the copy we are going to do stuff to
	RInteger a = RInteger::NewL(aInitial);
	CleanupStack::PushL(a);

	//compute a = aInitial^aRange using repeated multiplication
	TUint j=1;
	for(; j<aRange; j++)
		{
		a *= aInitial;
		}

	//b = a
	RInteger b = RInteger::NewL(a);
	CleanupStack::PushL(b);
	//try to find aInitial by repeatedly dividing b by aInitial aRange times
	for(j=1; j<aRange; j++)
		{
		TRAPD(res, b /= aInitial);
		//the first time through aInitial is 0 so this is expected
		if(res == KErrDivideByZero && aInitial.IsZero())
			{
			break;
			}
		else if(res == KErrDivideByZero && aInitial.NotZero())
			{
			iResult = EFalse;
			}
		else if(res != KErrNone)
			{
			User::Leave(res);
			}
		}
	// b should be the same as the initial value
	if( b != aInitial )
		{
		iResult = EFalse;
		}

	//tests division by something smaller than a word
	if(aInitial.WordCount() <= 1)
		{
		RInteger dividend = RInteger::NewL(a);
		CleanupStack::PushL(dividend);
		for(j=1; j<aRange; j++)
			{
			RInteger quotient;
			//try to find aInitial by repeatedly dividing dividend by aInitial aRange times 
			TRAPD(res, quotient = dividend.DividedByL(aInitial.ConvertToLongL()));
			//the first time through aInitial is 0 so this is expected
			if(res == KErrDivideByZero && aInitial.IsZero())
				{
				break;
				}
			else if(res == KErrDivideByZero && aInitial.NotZero())
				{
				iResult = EFalse;
				}
			else if(res != KErrNone)
				{
				User::Leave(res);
				}
			dividend.Set(quotient);
			}
		if( dividend != aInitial )
			{
			iResult = EFalse;
			}

		TUint remainder=1;
		TRAPD(res, remainder = a.ModuloL(aInitial.ConvertToLongL()));
		//the first time through aInitial is 0
		if(res != KErrDivideByZero && res != KErrNone)
			{
			User::Leave(res);
			}
		else if(res == KErrDivideByZero && aInitial.NotZero())
			{
			iResult = EFalse;
			}
		//else we have an expected divide by zero, ignore it.
		if(remainder != 0)
			{
			iResult = EFalse;
			}

		CleanupStack::PopAndDestroy(&dividend);
		}

	RInteger c = RInteger::NewL(aRange);
	CleanupStack::PushL(c);
	RInteger d = aInitial.ExponentiateL(c);
	CleanupStack::PushL(d);
	// c and a should now be the same
	if( d != a )
		{ 
		iResult = EFalse;
		}

	RInteger e = RInteger::NewL(a);
	CleanupStack::PushL(e);
	TRAPD(res, e %= aInitial);
	//the first time through aInitial is 0
	if(res != KErrDivideByZero && res != KErrNone)
		{
		User::Leave(res);
		}
	else if(res == KErrDivideByZero && aInitial.NotZero())
		{
		iResult = EFalse;
		}
	//else we have an expected divide by zero, ignore it.

	// (aInitial^aRange)%aInitial == 0
	if( e != 0 )
		{
		iResult = EFalse;
		}
	CleanupStack::PopAndDestroy(5);//e,d,c,b,a
	__UHEAP_MARKEND;
	}

void CBasicMathsFB::MiscDivL()
	{
	__UHEAP_MARK;
		
	TUint seed = 10;
	TUint diviser = 2;
	TInt dividendInt = 10;
	
	RInteger dividend = RInteger::NewL(seed);
	CleanupStack::PushL(dividend);
	
	TInt longInt = dividend.ConvertToLongL();

	// Test for inequality FALSE
	TBool res0 = dividend != dividendInt;
	if (res0)
		{
		iResult = EFalse;
		}
	
	// Test for inequality TRUE
	res0 = dividend != TInt(diviser);
	if (!res0)
		{
		iResult = EFalse;
		}
	
	// Test for equality TRUE
	res0 = dividend >= dividend; 
	if (!res0)
		{
		iResult = EFalse;
		}
	
	RInteger quotient;
	CleanupStack::PushL(quotient);
	// 10 / 2 = 5
	TRAPD(res, quotient = dividend.DividedByL(diviser));
	if (res != KErrNone)
		{
		User::Leave(res);
		}
	else if (quotient != (dividendInt/diviser))
		{
		iResult = EFalse;
		}

	// Test for greater value TRUE and equality FALSE
	res0 = dividend >= quotient; 
	if (!res0)
		{
		iResult = EFalse;
		}
	
	// Test for greater value FALSE and equality FALSE
	res0 = quotient >= dividend; 
	if (res0)
		{
		iResult = EFalse;
		}
	
	// 10 / 10 = 1
	TRAPD(res1, dividend /= dividendInt);
	if (res1 != KErrNone)
		{
		User::Leave(res);
		}	
	else if (dividend != (dividendInt/seed))
		{
		iResult = EFalse;
		}
	
	// 1 % 10 = 1 (dividend = 1, due to last step) 
	TRAPD(res2, dividend %= dividendInt);
	if (res2 != KErrNone)
		{
		User::Leave(res);
		}	
	else if (dividend != (dividendInt/seed))
		{
		iResult = EFalse;
		}
	
	// 1 x 1 = 1 (dividend = 1, due to last step)
	RInteger squaredInt = dividend.SquaredL();
	CleanupStack::PushL(squaredInt);
	if ( squaredInt != (dividendInt/seed))
		{
		iResult = EFalse;
		}
	
	RInteger expSeed = RInteger::NewL(10);
	CleanupStack::PushL(expSeed);
	RInteger exponent = RInteger::NewL(3);
	CleanupStack::PushL(exponent);
	RInteger expResult;
	CleanupStack::PushL(expResult);
	TRAPD(res3, expResult = expSeed.ExponentiateL(exponent));
		if (res3 != KErrNone)
			{
			User::Leave(res);
			}
		else if (expResult != (10*10*10))
			{
			iResult = EFalse;
			}
		
	CleanupStack::PopAndDestroy(6, &dividend); // dividend, quotient, squardInt, expSeed, exponent, expResult
	__UHEAP_MARKEND;
	}
