// コピペ用sha1
// WTFPL or NYSL

#include <stdio.h>

void CalcSHA1(void *result, const void *message, size_t length)
{
	typedef unsigned int u32;
	typedef unsigned char u8;
	u32 W[80] = { 0 };
	u32 H [] = { 0x67452301u, 0xEFCDAB89u, 0x98BADCFEu, 0x10325476u, 0xC3D2E1F0u };
	u32 blockCount = (length + 9) + 63 >> 6;
	
	for (int block = 0; block < blockCount; block++)
	{
		// feed
		{
			const u8* src = static_cast <const u8*>(message);
			for (int i = 0; i < 16; i++)
			{
				W[i] = 0;
				for (int j = 0; j < 4; j++)
				{
					int p = block * 64 + i * 4 + j;
					if (p < length) { W[i] |= src[p] << ((~j & 3) << 3); }
					else if (p == length) { W[i] |= 0x80 << ((~j & 3) << 3); }
				}
			}

			if (block == blockCount - 1)
			{
				W[14] = length >> 29;
				W[15] = length << 3;
			}
		}

#		define ROL(v,n) ((v)<<(n)|(v)>>(32-(n)))
#		define REP(i,s,e) for (int i = (s); i < (e); i++)
#		define CALCW(i) { W[i] = ROL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); }
#		define ROUND(a) { T = ROL(A, 5) + E + W[i] + (a); E = D; D = C; C = ROL(B, 30); B = A; A = T; }

		u32 A = H[0], B = H[1], C = H[2], D = H[3], E = H[4], T = 0;
		REP(i, 16, 80) W[i] = ROL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
		REP(i, 0, 20) ROUND((B & C | ~B & D) + 0x5A827999u);
		REP(i, 20, 40) ROUND((B ^ C ^ D) + 0x6ED9EBA1u);
		REP(i, 40, 60) ROUND((B & C | B & D | C & D) + 0x8F1BBCDCu);
		REP(i, 60, 80) ROUND((B ^ C ^ D) + 0xCA62C1D6u);
		H[0] += A; H[1] += B; H[2] += C; H[3] += D; H[4] += E;
	}

	reinterpret_cast<u32 *>(result)[0] = H[4];
	reinterpret_cast<u32 *>(result)[1] = H[3];
	reinterpret_cast<u32 *>(result)[2] = H[2];
	reinterpret_cast<u32 *>(result)[3] = H[1];
	reinterpret_cast<u32 *>(result)[4] = H[0];
}

void test(const char* text, size_t len, const char* expected)
{
	unsigned char sha1[20];
	CalcSHA1(sha1, text, len);
	printf("result:   ");
	for (int i = 19; i >= 0; i--) printf("%02x", sha1[i]);
	printf("\n"); \
	printf("expected: %s\n", expected);
}

int main()
{
	#define RUNTEST(text, expected) test(text, sizeof(text) - 1, expected)

	RUNTEST("", "da39a3ee5e6b4b0d3255bfef95601890afd80709");
	RUNTEST("abc", "a9993e364706816aba3e25717850c26c9cd0d89d");
	RUNTEST("abcdbcdecdefdefgefghfghighijhi" "jkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1");
	RUNTEST(
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		"01234567012345670123456701234567" "01234567012345670123456701234567"
		, "dea356a2cddd90c7a7ecedc5ebb563934f460452");
}
