#include <rtl.h>
#include <time.h>
#include "../hrt_test/utils.h"

#define MAXINT 0x7fffFFFF
#define init_timespec(t) do {(t)->tv_sec=0; (t)->tv_nsec=0; } while (0)
#define THREE_SECS 3*1000*1000*1000LL

#define old_timespec_add_ns(t,n) do { \
        (t)->tv_nsec += n;  \
        timespec_normalize(t); \
} while (0)

#define alternative_timespec_normalize(t) {\
        while ((t)->tv_nsec >= NSECS_PER_SEC) { \
                (t)->tv_nsec -= NSECS_PER_SEC; \
                (t)->tv_sec++; \
        } \
        while ((t)->tv_nsec < 0) { \
                (t)->tv_nsec += NSECS_PER_SEC; \
                (t)->tv_sec--; \
        } \
} 

#define alternative_timespec_add_ns(t,n) do { \
  (t)->tv_nsec +=(n % NSECS_PER_SEC) ; \
  (t)->tv_sec += (n / NSECS_PER_SEC); \
  alternative_timespec_normalize(t); \
}  while (0)

int init_module(void) {
  struct timespec ts;
  hrtime_t i=0,j=0,value=0,last_ts_val=0,incr=10,last_incr=10,correct_value=0;
  
  init_timespec(&ts);
  
  printk("\n\n\n TEST START  \n\n\n");
  printk("I am proving RTLinux-3.1pre3 implementation of timespec_add_ns\n");
  old_timespec_add_ns(&ts,THREE_SECS);
  if (ts.tv_nsec>NSECS_PER_SEC || ts.tv_nsec < 0 || ts.tv_sec < 0 
      || (THREE_SECS!=timespec_to_ns(&ts))){
    printk(SETCOLOR_FAILURE"test failed! timespec_add_ns & timespec_normalize bugs\n");
    printk("tv_sec:%ld tv_nsec:%ld\n"SETCOLOR_NORMAL,(long)ts.tv_sec,(long)ts.tv_nsec); 
  }else {
    printk(SETCOLOR_SUCCESS"test succesfuly passed!\n"SETCOLOR_NORMAL);
  }

  printk("I am trying to do the same things with an \n");
  printk("alternative implementation of timespec_add_ns & timespec_normalize \n");
  
  init_timespec(&ts);
  
  init_timespec(&ts);
  for (i=0; i<=HRTIME_INFINITY;last_incr=incr, incr*=incr, i+=incr){
    for (j=i; j<i+incr;j+=last_incr){
      value=j;

      last_ts_val=timespec_to_ns(&ts);
      correct_value=value+last_ts_val;
      alternative_timespec_add_ns(&ts,value);

      if (ts.tv_nsec>NSECS_PER_SEC || ts.tv_nsec < 0
          || ts.tv_sec < 0 || (correct_value!=timespec_to_ns(&ts))){
        if (ts.tv_sec < 0 && (unsigned long)ts.tv_sec > (unsigned long) 2000000000) {
	  printk(SETCOLOR_SUCCESS"test succesfuly passed!\n"SETCOLOR_NORMAL);
          printk("timespec reaching values biggers of 2G for tv_sec field\n");
          goto next;
        }
        printk(SETCOLOR_FAILURE"test failed ! while adding %lu (ns) to\n",(unsigned long)value);
        printk("%lu (time spec)\n",(unsigned long)last_ts_val);
        printk("ts.tv_sec:%ld ts.tv_nsec:%ld \n"SETCOLOR_NORMAL,ts.tv_sec,ts.tv_nsec);
        goto next;
      }
    }
  }
  
next:  incr=10;last_incr=incr;
  printk("I am now testing an efficient implementation of timespec_add_ns\n");
  init_timespec(&ts);
  for (i=0; i<=HRTIME_INFINITY;last_incr=incr, incr*=incr, i+=incr){
    for (j=i; j<i+incr;j+=last_incr){
      value=j; 
 
      last_ts_val=timespec_to_ns(&ts);
      correct_value=value+last_ts_val;
      alternative_timespec_add_ns(&ts,value);
      
      if (ts.tv_nsec>NSECS_PER_SEC || ts.tv_nsec < 0 
	  || ts.tv_sec < 0 || (correct_value!=timespec_to_ns(&ts))){
	if (ts.tv_sec < 0 && (unsigned long)ts.tv_sec > (unsigned long) 2000000000) {
    printk(SETCOLOR_SUCCESS"test succesfuly passed!\n"SETCOLOR_NORMAL);
	  printk("timespec reaching values biggers of 2G for tv_sec field\n");
	  return 0;
	}
	printk(SETCOLOR_FAILURE"test failed ! while adding %lu (ns) to\n",(unsigned long)value);
	printk("%lu (time spec)\n",(unsigned long)last_ts_val);
	printk("ts.tv_sec:%ld ts.tv_nsec:%ld \n"SETCOLOR_NORMAL,ts.tv_sec,ts.tv_nsec);
	goto end;
      }
    }
  }


 end:  if (value>=HRTIME_INFINITY) {
          printk("test succed\n");
	  return 0;
 } else {
   printk("Test failed\n");
   return -1;
 }

}

void cleanup_module(void) {
  return;
}
