                         /* matchnum.c,  6 Apr 10 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "global.h"
#include "utils.h"
#include "errors.h"
#include "isprop.h"
#include "fltval.h"
#include "levkeydist.h"
#include "printnum.h"
#include "hashtab.h"
#include "usage.h"
#include "words.h"
#include "chkfile.h"
#include "matchnum.h"


BOOL found_match;
BOOL do_print_match = TRUE;
BOOL do_print_value = FALSE;
BOOL do_print_word = FALSE;
BOOL match_mantissa = FALSE;
BOOL match_value = TRUE;
int min_levkey_dist = 2;
int num_simp_digits;
DIGIT_CHAR simp_digit_seq[MAX_NUM_DIGITS];


void save_simp_digit(DIGIT_CHAR ch)
{
if ((num_simp_digits == 0) && is_zero(ch)) // don't save leading zeroes
   return;

// printf("%c[%d]<%d>", ch, ch, is_dec_point(ch));

if (is_dec_point(ch))
   return;
if (num_simp_digits < MAX_NUM_DIGITS)
   {
   simp_digit_seq[num_simp_digits]=ch;
   num_simp_digits++;
   }
}


void simplify_match_num(DIGIT_CHAR *some_num,
                        int len_some_num)
{
// Remove any trailing zeroes.

int dig_num;

num_simp_digits=0;

for (dig_num=0; dig_num < len_some_num; dig_num++)
   save_simp_digit(some_num[dig_num]);

for (dig_num=num_simp_digits; dig_num > 0; dig_num--)
   if (is_zero(simp_digit_seq[dig_num-1]))
      num_simp_digits--;
   else
      break;
}


void print_approx_match(struct num_db_rec *num_ptr,
			DIGIT_CHAR *match_num,
			int min_signif,
			int least_signif)
{
if (!do_print_match)
   return;

if (do_trace_progress && !have_printed_filename)
   {
   printf("%s\n", cur_filename);
   have_printed_filename=TRUE;
   }
printf("mantissa ");
print_number(match_num, least_signif, "");
printf(" approximates ");
print_number(num_ptr->mantissa, num_ptr->len_mantissa, "");
printf("\n");
}


void print_match(enum match_enum match_kind,
		 struct num_db_rec *num_ptr,
                 DIGIT_CHAR *match_num,
                 int signif_dig,
                 DIGIT_CHAR *exponent)
{
DIGIT_CHAR output_num[MAX_NUM_DIGITS];

if (!do_print_match)
   return;

memcpy(output_num, match_num, signif_dig);
if (signif_dig == MAX_NUM_DIGITS)
   signif_dig--;
output_num[signif_dig]=NULL_CHAR;

if (do_trace_progress && !have_printed_filename)
   {
   printf("%s\n", cur_filename);
   have_printed_filename=TRUE;
   }
if (do_print_value)
   {
   print_number(match_num, signif_dig, exponent);
   printf(" ");
   }

printf(num_ptr->msg, output_num, signif_dig);
}


BOOL save_match(enum match_enum match_kind,
		struct num_db_rec *spec_num_ptr,
                fp_t seen_val,
		DIGIT_CHAR *mantissa,
                int len_mantissa,
                DIGIT_CHAR *exponent)
{
if (!(((match_kind == NO_MATCH) && do_summary_no_match) ||
      ((match_kind != NO_MATCH) && do_summary_match) ||
      ((match_kind == NDB_MATCH) && do_mk_ndb)))
   return FALSE;

if (cnt_seen_num(match_kind, spec_num_ptr, seen_val,
				mantissa, len_mantissa, exponent))
   return TRUE;

return FALSE;
}


void handle_match(enum match_enum match_kind,
		  struct num_db_rec *spec_num_ptr,
                  fp_t seen_val,
		  DIGIT_CHAR *mantissa,
                  int len_mantissa,
                  DIGIT_CHAR *exponent)
{
// Found a match, now figure out what actions to take.
int snum,
    need_total;

// print_match(match_kind, spec_num_ptr, mantissa, len_mantissa, exponent);
if ((spec_num_ptr == NULL) || (spec_num_ptr->need_info == NULL))
   {
   print_match(match_kind, spec_num_ptr, mantissa, len_mantissa, exponent);
   (void)save_match(match_kind, spec_num_ptr,
				seen_val, mantissa, len_mantissa, "");
   if (do_print_word)
      print_word_buf();
   }
else
   {
   // Is this the first time we have seen a value in this need sequence?
   if (spec_num_ptr->need_info->seen_need_num == NULL)
      {
      spec_num_ptr->need_info->seen_need_num=get_heap(sizeof(short)*
					spec_num_ptr->need_info->list_len);
      for (snum=0; snum < spec_num_ptr->need_info->list_len; snum++)
         spec_num_ptr->need_info->seen_need_num[snum]=0;
      }
   spec_num_ptr->need_info->seen_need_num[spec_num_ptr->need_num]++;
   // Have we seen each value at least once?
   need_total=0;
   for (snum=0; snum < spec_num_ptr->need_info->list_len; snum++)
      if (spec_num_ptr->need_info->seen_need_num[snum] != 0)
	 need_total++;
   if (need_total == spec_num_ptr->need_info->list_len)
      {
      // Report a !need match and reduce count for each value
      print_match(match_kind, spec_num_ptr, mantissa, len_mantissa, exponent);
      (void)save_match(NEED_MATCH, spec_num_ptr,
				seen_val, mantissa, len_mantissa, "");
      for (snum=0; snum < spec_num_ptr->need_info->list_len; snum++)
         spec_num_ptr->need_info->seen_need_num[snum]--;
      if (do_print_word)
         print_word_buf();
      }
   }
}


void reset_need_counts(void)
{
// Reset any !need count information to NULL.
// Called at the end of processing some input character sequence (e.g., a file)
struct num_db_rec *spec_num_ptr;
char digit;

for (digit='1'; digit <= '9'; digit++)
   {
   spec_num_ptr=special_numbers.num_info[digit - '0'];
   while (spec_num_ptr != NULL)
      {
      if ((spec_num_ptr->need_info != NULL) &&
          (spec_num_ptr->need_info->seen_need_num != NULL))
	 {
// TODO
// Values that did not match against any other entry in the ndb
// need to be added to the N_MATCH list.
	 free(spec_num_ptr->need_info->seen_need_num);
	 spec_num_ptr->need_info->seen_need_num=NULL;
	 }
      spec_num_ptr=spec_num_ptr->next;
      }
   }
}


void match_dp_mantissa(struct num_db_rec *spec_num_ptr)
{
// See if a floating-point literal's mantissa matches
// i.e., ignore any exponent.
int min_signif,   // take into account significant digit option
    least_signif; // of the two mantissa

if (num_simp_digits < spec_num_ptr->simp_mant_len)
   least_signif=num_simp_digits;
else
   least_signif=spec_num_ptr->simp_mant_len;

if (match_signif_digits < least_signif)
   min_signif=match_signif_digits;
else
   min_signif=least_signif;

// printf("%d %d %d\n", num_simp_digits, spec_num_ptr->simp_mant_len, min_signif);
// printf("%s : %s\n", spec_num_ptr->simp_mant, simp_digit_seq);
if (memcmp(spec_num_ptr->simp_mant, simp_digit_seq, min_signif) == 0)
   {
   found_match=TRUE;
   handle_match(EXACT_SIGNIF, spec_num_ptr,
				cur_fp_val, simp_digit_seq, min_signif, "");
   if ((min_signif < least_signif) &&
        (memcmp(spec_num_ptr->simp_mant, simp_digit_seq, least_signif) != 0))
      print_approx_match(spec_num_ptr, simp_digit_seq,
						 min_signif, least_signif);
   }
else if (levkey_dist(spec_num_ptr->simp_mant, simp_digit_seq, least_signif) <=
							 min_levkey_dist)
   {
// printf("levkey dist %d\n",  levkey_dist(spec_num_ptr->simp_mant, simp_digit_seq, least_signif));
   found_match=TRUE;
   handle_match(LEV_SIGNIF, spec_num_ptr,
				cur_fp_val, simp_digit_seq, least_signif, "");
   }
}


void match_fp_val(struct num_db_rec *spec_num_ptr,
		  DIGIT_CHAR * mantissa,
		  int len_mantissa,
		  DIGIT_CHAR * exponent)
{
// fp_print(cur_fp_val); printf(" ");
// fp_print(spec_num_ptr->num_valu); printf("\n");

if (fp_almost_eq(cur_fp_val, spec_num_ptr->num_valu, spec_num_ptr->match_fuzz))
   {
   found_match=TRUE;
   handle_match(VALUE_MATCH, spec_num_ptr,
			     cur_fp_val, mantissa, len_mantissa, exponent);
   }
}


void match_dp_number(DIGIT_CHAR * mantissa,
		     int len_mantissa,
		     DIGIT_CHAR * exponent)
{
struct num_db_rec *spec_num_ptr=special_numbers.num_info[simp_digit_seq[0] - '0'];
BOOL always_try_match = num_simp_digits >= match_signif_digits;

found_match=FALSE;

if (spec_num_ptr == NULL)
   return;

while (spec_num_ptr)
   {
   if (((num_simp_digits >= spec_num_ptr->simp_mant_len) ||
        always_try_match) &&
       ((num_simp_digits <= 3+spec_num_ptr->simp_mant_len) ||
        (num_simp_digits > MAX_NUM_DIGITS)))
      {
      if (match_value)
         match_fp_val(spec_num_ptr, mantissa, len_mantissa, exponent);

      if (match_mantissa)
         match_dp_mantissa(spec_num_ptr);
      }
   spec_num_ptr=spec_num_ptr->next;
   }
}


void match_number(DIGIT_CHAR * mantissa,
                  int len_mantissa,
		  DIGIT_CHAR * exponent)
{
simplify_match_num(mantissa, len_mantissa);
if (has_dp && (num_simp_digits > 0))
   {
   if (do_usage_value)
      cnt_val_usage(mantissa, len_mantissa, exponent);
   match_dp_number(mantissa, len_mantissa, exponent);
   if (!found_match)
      {
//print_number(mantissa, len_mantissa, exponent);
//printf(" unmatched\n");
      (void)save_match(NO_MATCH, NULL,
			cur_fp_val, mantissa, len_mantissa, exponent);
      }
   }
}


void init_match(void)
{
}

