#--------------------------------------------------------------------
#
#   Test libfib.py
#
#--------------------------------------------------------------------

from libfib import *

class Failure(Exception):
  
  def __init__(self, what, expected, received):
    self.what = what
    self.expected = expected
    self.received = received
  
  def __str__(self):
    return "%s test failed, expected %r, received %r" % (self.what, self.expected, self.received)

def test_int_fib_conversion():
  print("Int/Fib Conversion, Unsigned")
  for i in range(F(9)):
    f = int_to_fib(i)
    j = fib_to_int(f)
    print(i, f, j)
    if i != j:
      raise Failure("int/fib conversion", i, j)

def test_int_fib_conversion_signed():
  print("Signed Int/Fib Conversion")
  n = 8
  Fnplus1 = F(n + 1)
  imin = Fnplus1 // 2
  print("test_int_fib_conversion_signed: imin =", imin)
  for i in range(-imin + 1, imin):
    f = int_to_fib_signed(i, n)
    u = i
    if i < 0:
      u += Fnplus1
    g = int_to_fib(u)
    j = fib_to_int_signed(g, n)
    print(i, u, f, g, j)
    if f != g:
      raise Failure("Signed Int/Fib Conversion", g, f)
    if j != i:
      raise Failure("Signed Int/Fib Conversion", i, j)

def is_normalised(f):
  for i in range(len(f) - 1):
    if f[i] == 1 and f[i+1] == 1:
      return False
  return True

def test_normalisation():
  print("Normalisation")
  for bits in range(256):
    f = list(map(int, bin(bits)[2:]))
    g = normalise_fib(f)
    i = fib_to_int(f)
    j = fib_to_int(g)
    print(bits, i, f, g, j)
    if i != j or not is_normalised(g):
      raise Failure("Normalisation", i, j)

def test_negation(n):
  print("Negation -", n, "bits")
  Fnplus1 = F(n + 1)
  for i in range(Fnplus1):
    f = zero_extend_fib(int_to_fib(i), n)
    g = negate_fib(f)
    j = fib_to_int(g)
    k = (Fnplus1 - i) % Fnplus1
    print(i, f, g, j)
    if j != k:
      raise Failure("Negation", k, j)

def negation_tests():
  test_negation(1)
  test_negation(2)
  test_negation(4)
  test_negation(8)

def test_compare_unsigned():
  print("Unsigned Comparison")
  for i in range(1, F(5)):
    for j in range(F(5)):
      f1 = int_to_fib(i)
      f2 = int_to_fib(j)
      c = compare_fibs_unsigned(f1, f2)
      d = -1 if i < j else 0 if i == j else 1
      print(i, j, f1, f2, c)
      if c != d:
        raise Failure("Unsigned Comparison", d, c)

def test_first_negative():
  print("First Negative")
  for n in range(1, 17):
    Fnplus1 = F(n + 1)
    f = first_negative_fib(n)
    i = fib_to_int(f)
    k = Fnplus1 // 2
    print(n, Fnplus1, f, i)
    if i != k:
      raise Failure("First Negative", k, i)

def test_fib_sign():
  print("Sign Test")
  n = 8
  imin = F(n + 1) // 2
  imax = F(n + 1)
  for i in range(F(n + 1)):
    f = int_to_fib(i)
    s = fib_sign(f, n)
    t = 1 if i >= imin else 0
    print(i, t, f, s)
    if s != t:
      raise Failure("Sign Test", t, s)

def test_compare_signed():
  print("Signed Comparison")
  for i in range(1, F(5)):
    for j in range(F(5)):
      f1 = int_to_fib(i)
      f2 = int_to_fib(j)
      c = compare_fibs_signed(f1, f2)
      d = -1 if i < j else 0 if i == j else 1
      print(i, j, f1, f2, c)
      if c != d:
        raise Failure("Signed Comparison", d, c)

def test_addition():
  n = 8
  Fnplus1 = F(n + 1)
  for i in range(Fnplus1):
    for j in range(Fnplus1):
      f1 = int_to_fib(i)
      f2 = int_to_fib(j)
      g = add_fibs(f1, f2)
      k = fib_to_int(g)
      print(i, j, f1, f2, g, k)
      if k != i + j:
        raise Failure(i + j, k)

def main():
  try:
    test_int_fib_conversion()
    test_int_fib_conversion_signed()
    test_normalisation()
    negation_tests()
    test_compare_unsigned()
    test_first_negative()
    test_fib_sign()
    test_compare_signed()
    test_addition()
  except Failure as e:
    print("***", e)
  else:
    print("All tests passed.")

main()


  