/*
 ============================================================================
 Name        : get_rnd_point.c
 Author      : Josef Brychta
 Description : get random point
 ============================================================================
 */

#include "libec.h"
#include "print.h"
#include "time.h"

int fp_square_root (fp_t sqrt1, fp_t sqrt2, fp_src_t n);

void get_random_point_on_curve (ec_params * curve_params, prj_pt * out_point);
void
get_random_point_on_curve (ec_params * curve_params, prj_pt * out_point)
{
  nn nn_tmp;

  fp x, y, fp_tmp1, fp_tmp2;
  fp_ctx_src_t ctx;

  ctx = &(curve_params->ec_fp);
  fp_init (&x, ctx);
  fp_init (&y, ctx);
  fp_init (&fp_tmp1, ctx);
  fp_init (&fp_tmp2, ctx);

  nn_init (&nn_tmp, 0);
  nn_set_word_value (&nn_tmp, WORD (3));
  while (1)
    {

      fp_get_random (&x, ctx);
      fp_copy (&fp_tmp1, &x);
      fp_copy (&fp_tmp2, &x);

      fp_pow (&fp_tmp1, &fp_tmp1, &nn_tmp);
      fp_mul (&fp_tmp2, &fp_tmp2, &(curve_params->ec_curve.a));
      fp_add (&fp_tmp1, &fp_tmp1, &fp_tmp2);
      fp_add (&fp_tmp1, &fp_tmp1, &(curve_params->ec_curve.b));

      if (fp_square_root (&y, &fp_tmp2, &fp_tmp1) == 0)
	{

	  if (!is_on_curve (&x, &y, &(curve_params->ec_curve)))
	    {

	      ext_printf ("Error: Tonelli-Shanks found a bad "
			  "solution to curve equation ...\n");
	      continue;
	    }
	  break;
	}
    }

  fp_one (&fp_tmp1);
  prj_pt_init_from_coords (out_point, &(curve_params->ec_curve), &x, &y,
			   &fp_tmp1);

  fp_uninit (&x);
  fp_uninit (&y);
  fp_uninit (&fp_tmp1);
  fp_uninit (&fp_tmp2);
  nn_uninit (&nn_tmp);
}

int check_curve (const u8 * curve_name);
int
check_curve (const u8 * curve_name)
{
  u64 t1, t2;
  int ret = 0;

  ec_params curve_params;
  prj_pt A;

  const ec_str_params *the_curve_const_parameters =
    ec_get_curve_params_by_name (curve_name,
				 (u8) local_strnlen ((const char *)
						     curve_name,
						     MAX_CURVE_NAME_LEN) + 1);
  if (the_curve_const_parameters == NULL)
    {
      ext_printf ("Error: error when importing curve %s "
		  "parameters ...\n", curve_name);
      ret = -1;
      goto out;
    }
  import_params (&curve_params, the_curve_const_parameters);

  if (get_ms_time (&t1))
    {
      ext_printf ("Error: cannot get time with get_ms_time\n");
      ret = -1;
      goto out;
    }

  for (int i = 0; i < 1000; i++)
    {
      get_random_point_on_curve (&curve_params, &A);
    }

  if (get_ms_time (&t2))
    {
      ext_printf ("Error: cannot get time with get_ms_time\n");
      ret = -1;
      goto out;
    }
  ext_printf ("%.4f\n", (double) (t2 - t1) / (double) (1000ULL));
  if (get_ms_time (&t1))
    {
      ext_printf ("Error: cannot get time with get_ms_time\n");
      ret = -1;
      goto out;
    }

out:
  return ret;
}

int
main ()
{
  unsigned int i;
  u8 curve_name[MAX_CURVE_NAME_LEN] = { 0 };

  for (i = 0; i < EC_CURVES_NUM; i++)
    {

      ec_get_curve_name_by_type (ec_maps[i].type, curve_name,
				 sizeof (curve_name));

      ext_printf ("%s;", curve_name);
      if (check_curve (curve_name))
	{
	  ext_printf ("Error: error performing check on "
		      "curve %s\n", curve_name);
	  return -1;
	}
    }
  return 0;
}
