module std::core::builtins @test;

fn void test_anycast()
{
	int a;
	any b = &a;
	assert(anycast(b, int)!! == &a);
	assert(@catch(anycast(b, double)) == TYPE_MISMATCH);
}

fn void test_bitcast()
{
	int a = 123;
	float z = bitcast(a, float);
	assert(bitcast(z, int) == a);
}

enum Tester
{
	ABC,
	DEF,
}

fn void test_enum_by_name()
{
	assert(enum_by_name(Tester, "ABC")!! == Tester.ABC);
	assert(enum_by_name(Tester, "DEF")!! == Tester.DEF);
	assert(@catch(enum_by_name(Tester, "GHI")) == NOT_FOUND);
}


faultdef SOME_FAULT, ABC_FAULT;

fn void test_try_catch()
{
	int val;
	int? x = ABC_FAULT~;
	assert(@try_catch(val, x, ABC_FAULT)!!);
	assert(val == 0);
	assert(!@catch(@try_catch(val, x, ABC_FAULT)));
	x = SOME_FAULT~;
	assert(@catch(@try_catch(val, x, ABC_FAULT)) == SOME_FAULT);
	x = 3;
	assert(!@try_catch(val, x, ABC_FAULT)!!);
	assert(val == 3);
}

fn void test_try_set()
{
	assert(enum_by_name(Tester, "ABC")!! == Tester.ABC);

	Tester val;
	if (catch @try(val, enum_by_name(Tester, "ABC"))) abort("Test failure");
	assert(val == Tester.ABC);

	Tester another;
	if (catch err = @try(another, enum_by_name(Tester, "GHI")))
	{
		assert(err == NOT_FOUND);
		return;
	}

	abort("Test failure");
}

fn void test_likely()
{
	int a = 2;
	int b = 43;
	if (@likely(a > b, 0.5)) a++;
	if (@likely(a < b)) b++;
}

fn void test_unlikely()
{
	int a = 2;
	int b = 43;
	if (@unlikely(a > b, 0.5)) a++;
	if (@unlikely(a < b)) b++;
}

fn void test_expect()
{
	int a = 2;
	int b = 43;
	int c = @expect(a, 2, 0.5);
	int d = @expect(b, 2);
}


int abc;

fn void test_prefetch()
{
	@prefetch(&abc);
}

fn void test_hash()
{
	(char){}.hash();
	(char[<100>]){}.hash();
	(char[100]){}.hash();
	(ichar){}.hash();
	(ichar[<100>]){}.hash();
	(ichar[100]){}.hash();
	(short){}.hash();
	(short[<100>]){}.hash();
	(short[100]){}.hash();
	(ushort){}.hash();
	(ushort[<100>]){}.hash();
	(ushort[100]){}.hash();
	(int){}.hash();
	(int[<100>]){}.hash();
	(int[100]){}.hash();
	(uint){}.hash();
	(uint[<100>]){}.hash();
	(uint[100]){}.hash();
	(long){}.hash();
	(long[<20>]){}.hash();
	(long[100]){}.hash();
	(ulong){}.hash();
	(ulong[<20>]){}.hash();
	(ulong[100]){}.hash();
	(int128){}.hash();
	(int128[<20>]){}.hash();
	(int128[100]){}.hash();
	(uint128){}.hash();
	(uint128[<20>]){}.hash();
	(uint128[100]){}.hash();
	(bool){}.hash();
	(bool[<100>]){}.hash();
	(bool[100]){}.hash();
	String x = "abc";
	char[] y = "abc";
	assert(x.hash() == y.hash());
	assert(int.typeid.hash());
}

fn void test_hash_repeat()
{
	assert((char){}.hash() == (char){}.hash());
	assert((char[<100>]){}.hash() == (char[<100>]){}.hash());
	assert((char[100]){}.hash() == (char[100]){}.hash());
	assert((ichar){}.hash() == (ichar){}.hash());
	assert((ichar[<100>]){}.hash() == (ichar[<100>]){}.hash());
	assert((ichar[100]){}.hash() == (ichar[100]){}.hash());
	assert((short){}.hash() == (short){}.hash());
	assert((short[<100>]){}.hash() == (short[<100>]){}.hash());
	assert((short[100]){}.hash() == (short[100]){}.hash());
	assert((ushort){}.hash() == (ushort){}.hash());
	assert((ushort[<100>]){}.hash() == (ushort[<100>]){}.hash());
	assert((ushort[100]){}.hash() == (ushort[100]){}.hash());
	assert((int){}.hash() == (int){}.hash());
	assert((int[<100>]){}.hash() == (int[<100>]){}.hash());
	assert((int[100]){}.hash() == (int[100]){}.hash());
	assert((uint){}.hash() == (uint){}.hash());
	assert((uint[<100>]){}.hash() == (uint[<100>]){}.hash());
	assert((uint[100]){}.hash() == (uint[100]){}.hash());
	assert((long){}.hash() == (long){}.hash());
	assert((long[<20>]){}.hash() == (long[<20>]){}.hash());
	assert((long[100]){}.hash() == (long[100]){}.hash());
	assert((ulong){}.hash() == (ulong){}.hash());
	assert((ulong[<20>]){}.hash() == (ulong[<20>]){}.hash());
	assert((ulong[100]){}.hash() == (ulong[100]){}.hash());
	assert((int128){}.hash() == (int128){}.hash());
	assert((int128[<20>]){}.hash() == (int128[<20>]){}.hash());
	assert((int128[100]){}.hash() == (int128[100]){}.hash());
	assert((uint128){}.hash() == (uint128){}.hash());
	assert((uint128[<20>]){}.hash() == (uint128[<20>]){}.hash());
	assert((uint128[100]){}.hash() == (uint128[100]){}.hash());
	assert((bool){}.hash() == (bool){}.hash());
	assert((bool[<100>]){}.hash() == (bool[<100>]){}.hash());
	assert((bool[100]){}.hash() == (bool[100]){}.hash());
	assert(int.typeid.hash() == int.typeid.hash());
}

fn void test_hash_vector() @test @if($feature(SLOW_TESTS))
{
	test_hash_vector_internal{char}();
	test_hash_vector_internal{ichar}();
	test_hash_vector_internal{short}();
	test_hash_vector_internal{ushort}();
	test_hash_vector_internal{int}();
	test_hash_vector_internal{uint}();
	test_hash_vector_internal{long}();
	test_hash_vector_internal{ulong}();
	test_hash_vector_internal{int128}();
	test_hash_vector_internal{uint128}();
	test_hash_vector_internal{bool}();
}

fn void test_builtin_string_hashing() => @pool()
{
	var $x = "";
    ulong l;
    $for var $i = 0; $i < 65; ++$i:   // 65 is a good length to reliably test all branches w/o being excessive
        l = string::tformat("%s%s", $x, $i).hash();
        var $r = $$str_hash(@sprintf("%s%s", $x, $i));
        assert((uint)l == (uint)$r, "Builtin $$str_hash mismatch against String.hash()");
        $x = $x +++ "a";
    $endfor
}

fn void test_ct_clz()
{
	assert(@clz((ulong)0) == ulong.sizeof * 8);
	assert(@clz((char)1) == (char.sizeof * 8) - 1);
	assert(@clz((uint)0x8000_0000) == 0);
	assert(@clz((ushort)0x0100) == 7);
	assert(@clz((long)-1) == 0);
	assert(@clz((uint128)0x87) == 120);
	assert(@clz((char)(0x44 - 0x40)) == 5);
}

fn void test_bitsizeof()
{
	assert(bitsizeof(uint128) == 128);
	assert(bitsizeof(ulong) == 64);
	assert(bitsizeof(int) == 32);
	assert(bitsizeof(short) == 16);
	assert(bitsizeof(char) == 8);
	assert(bitsizeof(char[200]) == 1600);

	// Checks that they may be converted
	int x = bitsizeof(char);
	int y = @bitsizeof(1);

	assert(@bitsizeof((char)0x07) == 8);
	assert(@bitsizeof(0x1000ul) == 64);
	assert(@bitsizeof(0) == 32);
	assert(@bitsizeof((char[*])"abcdefghi") == 72);
}

fn void test_ct_min_max()
{
	assert(@min(4, 5) == 4);
	assert(@max(1.234, 1.2345) == 1.2345);
	assert(@min(4, 5, 6, 7, 8.90, 3.14) == 3.14);
	assert(@max(0, 0, 1.234, 1.2345, 0.2) == 1.2345);
	assert(@max(127.9999999, 45 + 46, bitsizeof(uint128)) == 128);
}

const usz[] MY_OK_VALUES = { 128, 256, 384, 512 };
fn void test_in()
{
    $assert @in(384, ...MY_OK_VALUES);
    $assert !@in(123, ...MY_OK_VALUES);
    $assert @in(384, 128, 256, 384, 512);
    $assert @in("love", "joy", "cheer", "love", "friend");
    $assert !@in("hate", "joy", "cheer", "love", "friend");
}


module std::core::builtins;

fn void test_hash_vector_internal() <Type>
{
	int[] $lens = {1, 2, 4, 7, 8, 13, 16, 23, 32, 43, 64, 103};
	$foreach $vec_len : $lens:
	{
		$if $vec_len * Type.sizeof * 8  <= $$MAX_VECTOR_SIZE:
		{
			Type[<$vec_len>] vec1, vec2;
			for (int val = 0; val < $vec_len; val++)
			{
				$if Type == bool:
					vec1[val] = ((val & 0x3) == 0);
				$else
				{
					Type tval = (Type)val;
					vec1[val] = (tval | (tval << ((Type.sizeof - 1) * 8)));
				}
				$endif
			}
			vec2 = vec1;
			assert(vec1.hash() == vec2.hash());
			for (int val = 0; val < $vec_len; val++)
			{
				$if Type == bool:
					vec2[val] = !vec2[val];
				$else
					vec2[val] ^= ((Type)0b0010_0000 << ((Type.sizeof - 1) * 8));
				$endif

				assert(vec1.hash() != vec2.hash());

				$if Type == bool:
					vec2[val] = !vec2[val];
				$else
					vec2[val] ^= ((Type)0b0010_0000 << ((Type.sizeof - 1) * 8));
				$endif
			}
		}
		$endif
	}
	$endforeach
}
