import math

def rearrange(bitString32):
	if len(bitString32) != 32:
		raise ValueError("Need length 32")
	newString = ""
	for i in [3,2,1,0]:
		newString += bitString32[8*i:8*i+8]
	return newString

def reformatHex(i):
	hexrep = format(i,'08x')
	thing = ""
	for i in [3,2,1,0]:
		thing += hexrep[2*i:2*i+2]
	return thing

def pad(bitString):
	startLength = len(bitString)
	bitString += '1'
	while len(bitString) % 512 != 448:
		bitString += '0'
	lastPart = format(startLength,'064b')
	bitString += rearrange(lastPart[32:]) + rearrange(lastPart[:32]) 
	return bitString

def getBlock(bitString):
	currPos = 0
	while currPos < len(bitString):
		currPart = bitString[currPos:currPos+512]
		mySplits = []
		for i in range(16):
			mySplits.append(int(rearrange(currPart[32*i:32*i+32]),2))
		yield mySplits
		currPos += 512
def not32(i):
	i_str = format(i,'032b')
	new_str = ''
	for c in i_str:
		new_str += '1' if c=='0' else '0'
	return int(new_str,2)

def sum32(a,b):
	return (a + b) % 2**32

def leftrot32(i,s):
	return (i << s) ^ (i >> (32-s))

def md5me(testString):
	bs =''
	for i in testString:
		bs += format(ord(i),'08b')
	bs = pad(bs)

	tvals = [int(2**32 * abs(math.sin(i+1))) for i in range(64)]

	a0 = 0x67452301
	b0 = 0xefcdab89
	c0 = 0x98badcfe
	d0 = 0x10325476

	s = [7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22, \
		5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20, \
		4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23, \
		6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21 ]

	for m in getBlock(bs):
		A = a0 
		B = b0
		C = c0
		D = d0
		for i in range(64):
			if i <= 15:
				#f = (B & C) | (not32(B) & D)
				f = D ^ (B & (C ^ D))
				g = i
			elif i<= 31:
				#f = (D & B) | (not32(D) & C)
				f = C ^ (D & (B ^ C))
				g = (5*i+1) % 16
			elif i <= 47:
				f = B ^ C ^ D
				g = (3*i+5) % 16
			else:
				f = C ^ (B | not32(D))
				g = (7*i) % 16
			dtemp = D
			D = C
			C = B
			B = sum32(B,leftrot32((A + f + tvals[i] + m[g]) % 2**32, s[i]))
			A = dtemp
		a0 = sum32(a0, A)
		b0 = sum32(b0, B)
		c0 = sum32(c0, C)
		d0 = sum32(d0, D)

	digest = reformatHex(a0) + reformatHex(b0) + reformatHex(c0) + reformatHex(d0)
	return digest

def test():
	assert md5me("") == "d41d8cd98f00b204e9800998ecf8427e"	
	assert md5me("The quick brown fox jumps over the lazy dog") == "9e107d9d372bb6826bd81d3542a419d6"
	print("Success.")


if __name__ == "__main__":
	test()