import unittest
import random
import scanner.logSetup as logSetup
from bitstring import Bits

import pyximport
print("Have Cython")
pyximport.install()

import deduplicator.cyHamDb as hamDb

def hamming(a, b):

	tot = 0

	x = (a ^ b)
	while x > 0:
		tot += x & 1
		x >>= 1
	return tot


def b2i(binaryStringIn):
	if len(binaryStringIn) != 64:
		print("ERROR: Passed string not 64 characters. String length = %s" % len(binaryStringIn))
		print("ERROR: String value '%s'" % binaryStringIn)
		raise ValueError("Input strings must be 64 chars long!")

	val = Bits(bin=binaryStringIn)
	return val.int

def b2u(binaryStringIn):
	if len(binaryStringIn) != 64:
		print("ERROR: Passed string not 64 characters. String length = %s" % len(binaryStringIn))
		print("ERROR: String value '%s'" % binaryStringIn)
		raise ValueError("Input strings must be 64 chars long!")

	val = Bits(bin=binaryStringIn)
	return val.uint

# Node ID numbers are derived from the list ordering.
# This will generate a single node with 64 children.
TEST_DATA_FLAT = [
	"0000000000000000000000000000000000000000000000000000000000000000", # 0
	"0000000000000000000000000000000000000000000000000000000000000001", # 1
	"0000000000000000000000000000000000000000000000000000000000000011", # 2
	"0000000000000000000000000000000000000000000000000000000000000111", # 3
	"0000000000000000000000000000000000000000000000000000000000001111", # 4
	"0000000000000000000000000000000000000000000000000000000000011111", # 5
	"0000000000000000000000000000000000000000000000000000000000111111", # 6
	"0000000000000000000000000000000000000000000000000000000001111111", # 7
	"0000000000000000000000000000000000000000000000000000000011111111", # 8
	"0000000000000000000000000000000000000000000000000000000111111111", # 9
	"0000000000000000000000000000000000000000000000000000001111111111", # 10
	"0000000000000000000000000000000000000000000000000000011111111111", # 11
	"0000000000000000000000000000000000000000000000000000111111111111", # 12
	"0000000000000000000000000000000000000000000000000001111111111111", # 13
	"0000000000000000000000000000000000000000000000000011111111111111", # 14
	"0000000000000000000000000000000000000000000000000111111111111111", # 15
	"0000000000000000000000000000000000000000000000001111111111111111", # 16
	"0000000000000000000000000000000000000000000000011111111111111111", # 17
	"0000000000000000000000000000000000000000000000111111111111111111", # 18
	"0000000000000000000000000000000000000000000001111111111111111111", # 19
	"0000000000000000000000000000000000000000000011111111111111111111", # 20
	"0000000000000000000000000000000000000000000111111111111111111111", # 21
	"0000000000000000000000000000000000000000001111111111111111111111", # 22
	"0000000000000000000000000000000000000000011111111111111111111111", # 23
	"0000000000000000000000000000000000000000111111111111111111111111", # 24
	"0000000000000000000000000000000000000001111111111111111111111111", # 25
	"0000000000000000000000000000000000000011111111111111111111111111", # 26
	"0000000000000000000000000000000000000111111111111111111111111111", # 27
	"0000000000000000000000000000000000001111111111111111111111111111", # 28
	"0000000000000000000000000000000000011111111111111111111111111111",
	"0000000000000000000000000000000000111111111111111111111111111111",
	"0000000000000000000000000000000001111111111111111111111111111111",
	"0000000000000000000000000000000011111111111111111111111111111111",
	"0000000000000000000000000000000111111111111111111111111111111111",
	"0000000000000000000000000000001111111111111111111111111111111111",
	"0000000000000000000000000000011111111111111111111111111111111111",
	"0000000000000000000000000000111111111111111111111111111111111111",
	"0000000000000000000000000001111111111111111111111111111111111111",
	"0000000000000000000000000011111111111111111111111111111111111111",
	"0000000000000000000000000111111111111111111111111111111111111111",
	"0000000000000000000000001111111111111111111111111111111111111111",
	"0000000000000000000000011111111111111111111111111111111111111111",
	"0000000000000000000000111111111111111111111111111111111111111111",
	"0000000000000000000001111111111111111111111111111111111111111111",
	"0000000000000000000011111111111111111111111111111111111111111111",
	"0000000000000000000111111111111111111111111111111111111111111111",
	"0000000000000000001111111111111111111111111111111111111111111111",
	"0000000000000000011111111111111111111111111111111111111111111111",
	"0000000000000000111111111111111111111111111111111111111111111111",
	"0000000000000001111111111111111111111111111111111111111111111111",
	"0000000000000011111111111111111111111111111111111111111111111111",
	"0000000000000111111111111111111111111111111111111111111111111111",
	"0000000000001111111111111111111111111111111111111111111111111111",
	"0000000000011111111111111111111111111111111111111111111111111111",
	"0000000000111111111111111111111111111111111111111111111111111111",
	"0000000001111111111111111111111111111111111111111111111111111111",
	"0000000011111111111111111111111111111111111111111111111111111111",
	"0000000111111111111111111111111111111111111111111111111111111111",
	"0000001111111111111111111111111111111111111111111111111111111111",
	"0000011111111111111111111111111111111111111111111111111111111111",
	"0000111111111111111111111111111111111111111111111111111111111111",
	"0001111111111111111111111111111111111111111111111111111111111111",
	"0011111111111111111111111111111111111111111111111111111111111111",
	"0111111111111111111111111111111111111111111111111111111111111111",
	"1111111111111111111111111111111111111111111111111111111111111111",
]


# THis will generate a single, 64-node long branch, with no forks.
TEST_DATA_Narrow = [
	"0000000000000000000000000000000000000000000000000000000000000000",  # 0
	"0000000000000000000000000000000000000000000000000000000000000001",  # 1
	"0000000000000000000000000000000000000000000000000000000000000010",  # 2
	"0000000000000000000000000000000000000000000000000000000000000100",  # 3
	"0000000000000000000000000000000000000000000000000000000000001000",  # 4
	"0000000000000000000000000000000000000000000000000000000000010000",  # 5
	"0000000000000000000000000000000000000000000000000000000000100000",  # 6
	"0000000000000000000000000000000000000000000000000000000001000000",  # 7
	"0000000000000000000000000000000000000000000000000000000010000000",  # 8
	"0000000000000000000000000000000000000000000000000000000100000000",  # 9
	"0000000000000000000000000000000000000000000000000000001000000000",  # 10
	"0000000000000000000000000000000000000000000000000000010000000000",  # 11
	"0000000000000000000000000000000000000000000000000000100000000000",  # 12
	"0000000000000000000000000000000000000000000000000001000000000000",  # 13
	"0000000000000000000000000000000000000000000000000010000000000000",  # 14
	"0000000000000000000000000000000000000000000000000100000000000000",  # 15
	"0000000000000000000000000000000000000000000000001000000000000000",  # 16
	"0000000000000000000000000000000000000000000000010000000000000000",  # 17
	"0000000000000000000000000000000000000000000000100000000000000000",  # 18
	"0000000000000000000000000000000000000000000001000000000000000000",  # 19
	"0000000000000000000000000000000000000000000010000000000000000000",  # 20
	"0000000000000000000000000000000000000000000100000000000000000000",  # 21
	"0000000000000000000000000000000000000000001000000000000000000000",  # 22
	"0000000000000000000000000000000000000000010000000000000000000000",  # 23
	"0000000000000000000000000000000000000000100000000000000000000000",  # 24
	"0000000000000000000000000000000000000001000000000000000000000000",  # 25
	"0000000000000000000000000000000000000010000000000000000000000000",  # 26
	"0000000000000000000000000000000000000100000000000000000000000000",  # 27
	"0000000000000000000000000000000000001000000000000000000000000000",  # 28
	"0000000000000000000000000000000000010000000000000000000000000000",
	"0000000000000000000000000000000000100000000000000000000000000000",
	"0000000000000000000000000000000001000000000000000000000000000000",
	"0000000000000000000000000000000010000000000000000000000000000000",
	"0000000000000000000000000000000100000000000000000000000000000000",
	"0000000000000000000000000000001000000000000000000000000000000000",
	"0000000000000000000000000000010000000000000000000000000000000000",
	"0000000000000000000000000000100000000000000000000000000000000000",
	"0000000000000000000000000001000000000000000000000000000000000000",
	"0000000000000000000000000010000000000000000000000000000000000000",
	"0000000000000000000000000100000000000000000000000000000000000000",
	"0000000000000000000000001000000000000000000000000000000000000000",
	"0000000000000000000000010000000000000000000000000000000000000000",
	"0000000000000000000000100000000000000000000000000000000000000000",
	"0000000000000000000001000000000000000000000000000000000000000000",
	"0000000000000000000010000000000000000000000000000000000000000000",
	"0000000000000000000100000000000000000000000000000000000000000000",
	"0000000000000000001000000000000000000000000000000000000000000000",
	"0000000000000000010000000000000000000000000000000000000000000000",
	"0000000000000000100000000000000000000000000000000000000000000000",
	"0000000000000001000000000000000000000000000000000000000000000000",
	"0000000000000010000000000000000000000000000000000000000000000000",
	"0000000000000100000000000000000000000000000000000000000000000000",
	"0000000000001000000000000000000000000000000000000000000000000000",
	"0000000000010000000000000000000000000000000000000000000000000000",
	"0000000000100000000000000000000000000000000000000000000000000000",
	"0000000001000000000000000000000000000000000000000000000000000000",
	"0000000010000000000000000000000000000000000000000000000000000000",
	"0000000100000000000000000000000000000000000000000000000000000000",
	"0000001000000000000000000000000000000000000000000000000000000000",
	"0000010000000000000000000000000000000000000000000000000000000000",
	"0000100000000000000000000000000000000000000000000000000000000000",
	"0001000000000000000000000000000000000000000000000000000000000000",
	"0010000000000000000000000000000000000000000000000000000000000000",
	"0100000000000000000000000000000000000000000000000000000000000000",
	"1000000000000000000000000000000000000000000000000000000000000000",
]

class TestSequenceFunctions_FlatTree(unittest.TestCase):

	def __init__(self, *args, **kwargs):
		logSetup.initLogging()
		super().__init__(*args, **kwargs)

	def setUp(self):

		self.tree = hamDb.BkHammingTree()
		for x in range(4):
			with self.tree.writer_context():
				self.tree.dropTree()
				self.buildTestTree()

	def buildTestTree(self):
		self.tree = hamDb.BkHammingTree()
		for nodeId, node_hash in enumerate(TEST_DATA_FLAT):
			print("Inserting node id: ", nodeId, "hash", node_hash, "value: ", b2i(node_hash))
			node_hash = b2i(node_hash)
			self.tree.unlocked_insert(node_hash, nodeId)

	def test_1(self):
		tgtHash = "0000000000000000000000000000000000000000000000000000000000000001"
		tgtHash = b2i(tgtHash)
		ret = self.tree.getWithinDistance(tgtHash, 0)
		self.assertEqual(ret, set((1, )))

	def test_2(self):
		tgtHash = "0000000000000000000000000000000000000000001111111111111111111111"
		tgtHash = b2i(tgtHash)
		ret = self.tree.getWithinDistance(tgtHash, 0)
		self.assertEqual(ret, set((22, )))

	def test_3(self):
		tgtHash = "1111111111111111111111111111111111111111111111111111111111111111"
		tgtHash = b2i(tgtHash)
		ret = self.tree.getWithinDistance(tgtHash, 0)
		self.assertEqual(ret, set((64, )))

class TestSequenceFunctions_TallTree(unittest.TestCase):

	def __init__(self, *args, **kwargs):
		logSetup.initLogging()
		super().__init__(*args, **kwargs)

	def setUp(self):

		self.tree = hamDb.BkHammingTree()
		for x in range(4):
			with self.tree.writer_context():
				self.tree.dropTree()
				self.buildTestTree()

	def buildTestTree(self):
		self.tree = hamDb.BkHammingTree()
		for nodeId, node_hash in enumerate(TEST_DATA_Narrow):
			print("Inserting node id: ", nodeId, "hash", node_hash, "value: ", b2i(node_hash))
			node_hash = b2i(node_hash)
			self.tree.insert(node_hash, nodeId)

	def test_1(self):
		tgtHash = "0000000000000000000000000000000000000000000000000000000000000001"
		tgtHash = b2i(tgtHash)
		ret = self.tree.getWithinDistance(tgtHash, 0)
		self.assertEqual(ret, set((1, )))

	def test_2(self):
		tgtHash = "0000000000000000000000000000000000000000001000000000000000000000"
		tgtHash = b2i(tgtHash)
		ret = self.tree.getWithinDistance(tgtHash, 0)
		self.assertEqual(ret, set((22, )))

	def test_3(self):
		tgtHash = "1000000000000000000000000000000000000000000000000000000000000000"
		tgtHash = b2i(tgtHash)
		ret = self.tree.getWithinDistance(tgtHash, 0)
		self.assertEqual(ret, set((64, )))

class TestSignConversion(unittest.TestCase):

	def __init__(self, *args, **kwargs):
		logSetup.initLogging()
		super().__init__(*args, **kwargs)


	def test_signModification_1(self):
		for val in TEST_DATA_Narrow:
			tgtHash = b2i(val)
			x = hamDb.explicitUnsignCast(tgtHash)
			self.assertEqual(x, b2u(val))
			x = hamDb.explicitSignCast(x)
			self.assertEqual(x, tgtHash)

	def test_signModification_2(self):
		for val in TEST_DATA_FLAT:
			tgtHash = b2i(val)
			x = hamDb.explicitUnsignCast(tgtHash)
			self.assertEqual(x, b2u(val))
			x = hamDb.explicitSignCast(x)
			self.assertEqual(x, tgtHash)

	def test_signModification_3(self):
		random.seed(500000)
		for x in range(50000):
			val = "".join([random.choice(("1", "0")) for z in range(64)])
			tgtHash = b2i(val)
			x = hamDb.explicitUnsignCast(tgtHash)
			self.assertEqual(x, b2u(val))
			x = hamDb.explicitSignCast(x)
			self.assertEqual(x, tgtHash)