
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                                  Main                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
// (c) 2021 Miroslav Nemecek, Panda38@seznam.cz, panda38.sweb.cz
// This source code may be used freely without restrictions for any purpose,
// including commercial.

#include "include.h"

// random generator
u64 RandSeed = 12345678;

///////////////////////////////////////////////////////////////////////////////
// get random number (Microsoft LCG, seed = seed*214013 + 2531011)

u64 Rand()
{
	RandSeed = RandSeed*214013 + 2531011;
	return RandSeed;
}

u32 Rand32()
{
	return (u32)(Rand() >> 32);
}

// random number with limitation
u64 Rand(u64 max)
{
	if (max == 0) return 0;

	u8 bits = ::Bits64(max);

	u64 res;
	do {
		res = Rand() >> (64 - bits);
	} while (res > max);

	return res;
}

// random bit string of integer (bits = max. number of bits, returns real number of bits)
//   Output number is not simple random number in given number of bits,
//   we would get many big numbers, but very low small numbers.
//   So at first, we prepare random number of bits of the number,
//   and then generate random number of random number of bits.
int RandBits(void* dst, int bits, bool noneg /* = false */)
{
	// prepare real number of bits
	//  We split generated random number into parts:
	//    8 bits ... number of shifts 0..255
	//    1 bit ... flag of negative number
	//    'bits' ... number of bits
	int b = (int)Rand((bits*2+1)*256+255); // generate random number of bits*2*256
	int rol = (b & 0xff);		// number of shifts 0..255
	b >>= 8;					// number of bits*2
	bool neg = (b & 1) != 0;	// negative flag
	if (noneg) neg = false;		// do not generate negative number
	b >>= 1;					// destroy negative flag
	int i = b;					// number of bits

	// clear output number (use -1 as negative number, use 0 as positive number)
	memset(dst, neg ? -1 : 0, bits/8);

	// special case if rol=0: set only 1 bit
	if (rol == 0)
	{
		if (i < bits) // number of bits is OK ? (if not, return 0 or -1)
		{
			u8* d = (u8*)dst;
			d[i/8] ^= (1 << (i & 3)); // flip 1 random bit
		}
		return b;
	}

	// write 32-bit random samples
	u32* d32 = (u32*)dst;
	while (i >= 32) // while remain 32 bits or more
	{
		*d32++ = Rand32(); // write random 32 bits
		i -= 32;
		bits -= 32;
	}

	// write 8-bit samples
	u8* d = (u8*)d32;
	while (i >= 8) // while remain 8 bits or more
	{
		*d++ = (u8)(Rand32()>>24); // write random 8 bits
		i -= 8;
		bits -= 8;
	}

	// write remaining number of bits
	if (i > 0) // while some bits remain
	{
		*d = (u8)(((1 << i) - 1) & (Rand32()>>24)); // write 1 random bit
		if (neg) *d = ~ *d; // correction if number should be negative
	}

	// return real number of generated bits
	return b;
}

///////////////////////////////////////////////////////////////////////////////
// main function

#ifdef _MSC_VER

int main(int argc, char* argv[])
{
#define TESTNUM 1000000000	// number of tests

#else

// Initialize Raspberry Pico
int main()
{
	stdio_init_all();

	const uint LED_PIN = 25;
	gpio_init(LED_PIN);
	gpio_set_dir(LED_PIN, GPIO_OUT);

	// some delay for user to connect terminal
	int i;
	for (i = 5; i > 0; i--)
	{
		gpio_put(LED_PIN, 1);
		sleep_ms(500);
		gpio_put(LED_PIN, 0);
		sleep_ms(500);
	}

#define TESTNUM 10000000	// number of tests

#endif

	// create or verify bit tables (returns FALSE on table error)
	if (!InitBitTab())
	{
		printf("Invalid bit tables!\n");
#ifdef _MSC_VER
		return 1;
#else
		while(True) {}
#endif
	}

	// initialize constants
	fix32::ConstInit();
	fix64::ConstInit();
	fix128::ConstInit();
	fix64B::ConstInit();
	fix128B::ConstInit();
	fix256::ConstInit();

	// check double integer library

/* Debug output:
Check dbl uint32 ... OK
        123 + 7545 = 7668 (should be 7668)
Check dbl sint32 ... OK
        123 * -7545 = -928035 (should be -928035)
Check dbl fix32 ... OK
        123.45 / 3.1415 = 39.2966 (should be 39.2965)
Check dbl uint64 ... OK
        897656 % 75452 = 67684 (should be 67684)
Check dbl sint64 ... OK
        -1239851456 * 754554789 = -935535853773422784 (should be -935535853773422784)
Check dbl fix64 ... OK
        sin_deg(58.76534523) = 0.855050776 (should be 0.855050780)
Check quad uint64B ... OK
        45872341 xor 664334789 = 623052048 (should be 623052048)
Check quad sint64B ... OK
        (-7545545)^2 = 56935249347025 (should be 56935249347025)
Check quad fix64B ... OK
        457.55 ^ 1.14 = 1078.698207313 (should be 1078.698209953)
Check quad dbl uint128 ... OK
        65544765565 << 12 = 268471359754240 (should be 268471359754240)
Check quad dbl sint128 ... OK
        sqrt_int(65544765565) = 256017 remainder 61276 (should be 256017 remainder 61276)
quad uint256
        28! = 304888344611713860501504000000 (should be 304888344611713860501504000000)
quad fix256
        exp(3.1415926535897932384626433832795028842) = 23.14069263277926900572908636794854738013
                                (should be 23.14069263277926900572908636794854738033)
*/

	Check_dbluint32(TESTNUM);
	{
		uint32 a, b, c;
		a = 123;
		b = 7545;
		c = a+b;
		c.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t123 + 7545 = %s (should be 7668)\n", TextBuf);
	}

	Check_dblsint32(TESTNUM);
	{
		sint32 a, b, c;
		a = (s16)123;
		b = (s16)-7545;
		c = a*b;
		c.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t123 * -7545 = %s (should be -928035)\n", TextBuf);
	}

	Check_dblfix32(TESTNUM);
	{
		fix32 a, b, c;
		a = 123.45;
		b = 3.1415;
		c = a/b;
		c.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t123.45 / 3.1415 = %s (should be 39.2965)\n", TextBuf);
	}

	Check_dbluint64(TESTNUM);
	{
		uint64 a, b, c;
		a = 897656;
		b = 75452;
		c = a % b;
		c.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t897656 %% 75452 = %s (should be 67684)\n", TextBuf);
	}

	Check_dblsint64(TESTNUM);
	{
		sint64 a, b, c;
		a = (s32)-1239851456;
		b = (s32)754554789;
		c = a*b;
		c.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t-1239851456 * 754554789 = %s (should be -935535853773422784)\n", TextBuf);
	}

	Check_dblfix64(TESTNUM);
	{
		fix64 a, b;
		a = 58.76534523;
		a.DegRad();
		b.Sin(a);
		b.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\tsin_deg(58.76534523) = %s (should be 0.855050780)\n", TextBuf);
	}

	Check_quaduint64B(TESTNUM);
	{
		uint64B a, b, c;
		a.Set(45872341 & 0xffff, 45872341 >> 16, 0, 0);
		b.Set(664334789 & 0xffff, 664334789 >> 16, 0, 0);
		c.Xor(a, b);
		c.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t45872341 xor 664334789 = %s (should be 623052048)\n", TextBuf);
	}

	Check_quadsint64B(TESTNUM);
	{
		sint64B a;
		a.Set((-7545545) & 0xffff, (-7545545) >> 16, -1, -1);
		a.Sqr();
		a.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t(-7545545)^2 = %s (should be 56935249347025)\n", TextBuf);
	}

	Check_quadfix64B(TESTNUM);
	{
		fix64B a, b, c;
		a = 457.55;
		b = 1.14;
		c.Pow(a, b);
		c.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t457.55 ^ 1.14 = %s (should be 1078.698209953)\n", TextBuf);
	}

	Check_quadudbl128(TESTNUM);
	{
		uint128 a;
		a.Set(65544765565);
		a.LShift(12);
		a.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t65544765565 << 12 = %s (should be 268471359754240)\n", TextBuf);
	}

	Check_quadsdbl128(TESTNUM);
	{
		sint128 a, b, r;
		a.Set(65544765565);
		b.Sqrt(a, &r);
		b.ToText(TextBuf, TEXTBUF_SIZE);
		r.ToText(TextBuf2, TEXTBUF_SIZE);
		printf("\tsqrt_int(65544765565) = %s remainder %s (should be 256017 remainder 61276)\n", TextBuf, TextBuf2);
	}

	// uint256
	printf("quad uint256\n");
	{
		uint256 a;
		a.Fact(28);
		a.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\t28! = %s (should be 304888344611713860501504000000)\n", TextBuf);
	}

	// fix256
	printf("quad fix256\n");
	{
		fix256 a;
		a.Set(fix256::Pi);
		a.Exp();
		a.ToText(TextBuf, TEXTBUF_SIZE);
		printf("\texp(3.1415926535897932384626433832795028842) = %s\n\t\t\t\t(should be 23.14069263277926900572908636794854738033)\n", TextBuf);
	}


#ifdef _MSC_VER
	return 0;
#else
	while (true)
	{
		gpio_put(LED_PIN, 1);
		sleep_ms(250);
		gpio_put(LED_PIN, 0);
		sleep_ms(250);
	}
#endif
}
