/* ----- encode/int16, derived from supercop/crypto_encode/try.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ntruprime.h>
#include "ntruprime_test.h"

#define fail ((ok = 0),printf)
static const char *encode_int16_checksums[] = {
  "7f59625680d504bdd9bff199c54792455ddfd077779962836a92543c88bea2fa",
  "cdc27065db4d2f6e2356756b9a0bae31699e6255f0caf0011ba18ef3d2229ed8",
} ;

static void (*crypto_encode)(unsigned char *,const void *);
#define crypto_encode_STRBYTES ntruprime_encode_int16_STRBYTES
#define crypto_encode_ITEMS ntruprime_encode_int16_ITEMS
#define crypto_encode_ITEMBYTES ntruprime_encode_int16_ITEMBYTES

static void *storage_encode_int16_s;
static unsigned char *test_encode_int16_s;
static void *storage_encode_int16_x;
static unsigned char *test_encode_int16_x;
static void *storage_encode_int16_s2;
static unsigned char *test_encode_int16_s2;
static void *storage_encode_int16_x2;
static unsigned char *test_encode_int16_x2;

#define precomputed_encode_int16_NUM 16

static const unsigned char precomputed_encode_int16_s[precomputed_encode_int16_NUM][crypto_encode_STRBYTES] = {
  {122,133},
  {137,93},
  {10,58},
  {63,76},
  {103,228},
  {99,188},
  {142,131},
  {155,71},
  {15,238},
  {223,68},
  {57,25},
  {23,39},
  {83,38},
  {147,10},
  {141,21},
  {210,235},
} ;

static const uint16_t precomputed_encode_int16_x[precomputed_encode_int16_NUM][crypto_encode_ITEMS] = {
  {34170},
  {23945},
  {14858},
  {19519},
  {58471},
  {48227},
  {33678},
  {18331},
  {60943},
  {17631},
  {6457},
  {10007},
  {9811},
  {2707},
  {5517},
  {60370},
} ;

static void test_encode_int16_impl(long long impl)
{
  unsigned char *s = test_encode_int16_s;
  unsigned char *x = test_encode_int16_x;
  unsigned char *s2 = test_encode_int16_s2;
  unsigned char *x2 = test_encode_int16_x2;
  long long slen = crypto_encode_STRBYTES;
  long long xwords = crypto_encode_ITEMS;
  long long xlen;

  if (targeti && strcmp(targeti,".") && strcmp(targeti,ntruprime_dispatch_encode_int16_implementation(impl))) return;
  if (targetn && atol(targetn) != impl) return;
  if (impl >= 0) {
    crypto_encode = ntruprime_dispatch_encode_int16(impl);
    printf("encode_int16 %lld implementation %s compiler %s\n",impl,ntruprime_dispatch_encode_int16_implementation(impl),ntruprime_dispatch_encode_int16_compiler(impl));
  } else {
    crypto_encode = ntruprime_encode_int16;
    printf("encode_int16 selected implementation %s compiler %s\n",ntruprime_encode_int16_implementation(),ntruprime_encode_int16_compiler());
  }
  for (long long checksumbig = 0;checksumbig < 2;++checksumbig) {
    long long loops = checksumbig ? 4096 : 1024;

    checksum_clear();

    for (long long loop = 0;loop < loops;++loop) {
      xlen = xwords*crypto_encode_ITEMBYTES;

      output_prepare(s2,s,slen);
      input_prepare(x2,x,xlen);
      endianness(x,xwords,crypto_encode_ITEMBYTES);
      secret(x,xlen);
      crypto_encode(s,x);
      public(x,xlen);
      endianness(x,xwords,crypto_encode_ITEMBYTES);
      public(s,slen);
      checksum(s,slen);
      output_compare(s2,s,slen,"crypto_encode");
      input_compare(x2,x,xlen,"crypto_encode");

      double_canary(s2,s,slen);
      double_canary(x2,x,xlen);
      endianness(x2,xwords,crypto_encode_ITEMBYTES);
      secret(x2,xlen);
      crypto_encode(s2,x2);
      public(x2,xlen);
      endianness(x2,xwords,crypto_encode_ITEMBYTES);
      public(s2,slen);
      if (memcmp(s2,s,slen) != 0) fail("failure: crypto_encode is nondeterministic\n");
    }
    checksum_expected(encode_int16_checksums[checksumbig]);
  }
  for (long long precomp = 0;precomp < precomputed_encode_int16_NUM;++precomp) {
    output_prepare(s2,s,crypto_encode_STRBYTES);
    input_prepare(x2,x,crypto_encode_ITEMS*crypto_encode_ITEMBYTES);
    memcpy(x,precomputed_encode_int16_x[precomp],crypto_encode_ITEMS*crypto_encode_ITEMBYTES);
    memcpy(x2,precomputed_encode_int16_x[precomp],crypto_encode_ITEMS*crypto_encode_ITEMBYTES);
    crypto_encode(s,x);
    if (memcmp(s,precomputed_encode_int16_s[precomp],crypto_encode_STRBYTES)) {
      fail("failure: crypto_encode fails precomputed test vectors\n");
      printf("expected s: ");
      for (long long pos = 0;pos < crypto_encode_STRBYTES;++pos) printf("%02x",((unsigned char *) precomputed_encode_int16_s[precomp])[pos]);
      printf("\n");
      printf("received s: ");
      for (long long pos = 0;pos < crypto_encode_STRBYTES;++pos) printf("%02x",s[pos]);
      printf("\n");
    }
    output_compare(s2,s,crypto_encode_STRBYTES,"crypto_encode");
    input_compare(x2,x,crypto_encode_ITEMS*crypto_encode_ITEMBYTES,"crypto_encode");
  }
}

void test_encode_int16(void)
{
  long long maxalloc = 0;
  if (targeto && strcmp(targeto,"encode")) return;
  if (targetp && strcmp(targetp,"int16")) return;
  storage_encode_int16_s = callocplus(crypto_encode_STRBYTES);
  test_encode_int16_s = aligned(storage_encode_int16_s,crypto_encode_STRBYTES);
  if (crypto_encode_STRBYTES > maxalloc) maxalloc = crypto_encode_STRBYTES;
  storage_encode_int16_x = callocplus(crypto_encode_ITEMS*crypto_encode_ITEMBYTES);
  test_encode_int16_x = aligned(storage_encode_int16_x,crypto_encode_ITEMS*crypto_encode_ITEMBYTES);
  if (crypto_encode_ITEMS*crypto_encode_ITEMBYTES > maxalloc) maxalloc = crypto_encode_ITEMS*crypto_encode_ITEMBYTES;
  storage_encode_int16_s2 = callocplus(maxalloc);
  test_encode_int16_s2 = aligned(storage_encode_int16_s2,crypto_encode_STRBYTES);
  storage_encode_int16_x2 = callocplus(maxalloc);
  test_encode_int16_x2 = aligned(storage_encode_int16_x2,crypto_encode_ITEMS*crypto_encode_ITEMBYTES);

  for (long long offset = 0;offset < 1;++offset) {
    if (targetoffset && atol(targetoffset) != offset) continue;
    if (offset && valgrind) break;
    printf("encode_int16 offset %lld\n",offset);
    for (long long impl = -1;impl < ntruprime_numimpl_encode_int16();++impl)
      forked(test_encode_int16_impl,impl);
    ++test_encode_int16_s;
    ++test_encode_int16_x;
    ++test_encode_int16_s2;
    ++test_encode_int16_x2;
  }
  free(storage_encode_int16_x2);
  free(storage_encode_int16_s2);
  free(storage_encode_int16_x);
  free(storage_encode_int16_s);
}
#undef crypto_encode_STRBYTES
#undef crypto_encode_ITEMS
#undef crypto_encode_ITEMBYTES

