#include <string.h>

#include "quorum-internal.h"

#include "test-utils.h"

#define X_P(x) ((BYTECODE_OP_X_P << 6) | (x))
#define GE_K(k) ((BYTECODE_OP_GE_K << 6) | (k))
#define PREFIX(p) ((BYTECODE_OP_PREFIX << 6) | (p))

static void
test_one_of_n (void)
{
  /* Quorum with 1-of-n everywhere. Despite the hierarchy, this
     boils down to requiring at least one cosignature, by any of
     the six witnesses.

            q
	   / \
         g0   g2
        / |   /|\
       w0 w1 w4|w5
              g1
              / \
             w2 w3
  */
  static const uint8_t one_of_n[] =
    {
      X_P(0), X_P(1), BYTECODE_INSTR_ADD, GE_K(1), /* g0 */
      X_P(4), X_P(5), BYTECODE_INSTR_ADD,
      X_P(2), X_P(3), BYTECODE_INSTR_ADD, GE_K(1), /* g1 */
      BYTECODE_INSTR_ADD, GE_K(1), /* g2 */
      BYTECODE_INSTR_ADD, GE_K(1), /* q */
    };

  uint8_t stack[4];
  bool witnesses[6] = { 0 };
  
  struct sigsum_quorum quorum = { sizeof (one_of_n), one_of_n, sizeof (stack) };
  if (_sigsum_quorum_eval (&quorum, 6, witnesses, stack))
    die ("1-of-n policy, no cosignatures: unexpected success");

  /* Exhaustive test, representing witness j using bit j of i.
     There are a total of 64 cases, with the failing no-witness
     case (corresponding to i == 0) handled above.
  */
  for (unsigned i = 1; i < 64; i++)
    {
      for (unsigned j = 0; j < 6; j++)
	witnesses[j] = (i >> j) & 1;
      if (!_sigsum_quorum_eval (&quorum, 6, witnesses, stack))
	die ("1-of-n policy, failed for case %u", i);
    }
}

static void
test_two_of_n (void)
{
  /* Similar quorum with 2-of-n everywhere. */
  static const uint8_t two_of_n[] =
    {
      X_P(0), X_P(1), BYTECODE_INSTR_ADD, GE_K(2), /* g0 */
      X_P(4), X_P(5), BYTECODE_INSTR_ADD,
      X_P(2), X_P(3), BYTECODE_INSTR_ADD, GE_K(2), /* g1 */
      BYTECODE_INSTR_ADD, GE_K(2), /* g2 */
      BYTECODE_INSTR_ADD, GE_K(2), /* q */
    };

  uint8_t stack[4];
  bool witnesses[6] = { 0 };

  struct sigsum_quorum quorum = { sizeof (two_of_n), two_of_n, sizeof (stack) };
  /* Exhaustive test */
  for (unsigned i = 0; i < 64; i++)
    {
      for (unsigned j = 0; j < 6; j++)
	witnesses[j] = (i >> j) & 1;
      /* Expected answer: We must have both groups g0 and
	 g2. For g0, we have must have both w0 and w1. For
	 g2, we have this truth table */

      static  const bool truth[16] =
      {
	false, false, false, false,	/* both w4 and w5 missing */
	false, false, false, true,	/* w4, valid if both w2, w3 */
	false, false, false, true,	/* w5, valid if both w2, w3 */
	true, true, true, true,		/* both of w4 and w5 */
      };
      bool exp = (i&3) == 3 && truth[i>>2];
      bool res = _sigsum_quorum_eval (&quorum, 6, witnesses, stack);
      if (exp && !res)
	die ("2-of-n policy, failed for case %d", i);
      if (!exp && res)
	die ("2-of-n policy, expected error for case %d, got success", i);
    }
}

static void
test_two_of_n_prefix (void)
{
  /* Similar quorum with 2-of-n everywhere, but assign the odd
     witnesses indices higher than 64, to force use of prefix
     instructions. */
  static const uint8_t two_of_n[] =
    {
      X_P(0), PREFIX(1), X_P(1), BYTECODE_INSTR_ADD, GE_K(2), /* g0 */
      X_P(4), PREFIX(1), X_P(5), BYTECODE_INSTR_ADD,
      X_P(2), PREFIX(1), X_P(3), BYTECODE_INSTR_ADD, GE_K(2), /* g1 */
      BYTECODE_INSTR_ADD, GE_K(2), /* g2 */
      BYTECODE_INSTR_ADD, GE_K(2), /* q */
    };

  uint8_t stack[4];
  bool witnesses[70] = { 0 };

  struct sigsum_quorum quorum = { sizeof (two_of_n), two_of_n, sizeof (stack) };
  /* Exhaustive test */
  for (unsigned i = 0; i < 64; i++)
    {
      for (unsigned j = 0; j < 6; j++)
	witnesses[((j & 1) << 6) + j] = (i >> j) & 1;
      /* Expected answer: We must have both groups g0 and
	 g2. For g0, we have must have both w0 and w1. For
	 g2, we have this truth table */

      static  const bool truth[16] =
      {
	false, false, false, false,	/* both w4 and w5 missing */
	false, false, false, true,	/* w4, valid if both w2, w3 */
	false, false, false, true,	/* w5, valid if both w2, w3 */
	true, true, true, true,		/* both of w4 and w5 */
      };
      bool exp = (i&3) == 3 && truth[i>>2];
      bool res = _sigsum_quorum_eval (&quorum, 70, witnesses, stack);
      if (exp && !res)
	die ("2-of-n (prefix) policy, failed for case %d", i);
      if (!exp && res)
	die ("2-of-n (prefix) policy, expected error for case %d, got success", i);
    }
}

static void
test_none_member (void)
{
  uint8_t bytecode[] = { BYTECODE_INSTR_TRUE, X_P(0), BYTECODE_INSTR_ADD, GE_K(2) };
  uint8_t stack[2];
  bool witness;

  struct sigsum_quorum quorum = { sizeof (bytecode), bytecode, sizeof (stack) };
  witness = false;
  if (_sigsum_quorum_eval (&quorum, 1, &witness, stack))
    die ("quorum with none-member, expected error with missing witness, got success");

  witness = true;
  if (!_sigsum_quorum_eval (&quorum, 1, &witness, stack))
    die ("quorum with none-member, failed with one witness present");
}

int
main (void)
{
  test_one_of_n ();
  test_two_of_n ();
  test_two_of_n_prefix ();
  test_none_member ();

  return 0;
}
