/*
  chipextract.c

  Feature correlation and extraction Tool.
  The program correlates tag count distributions for two features (reference, target),
  and extracts target feature reads/tags that fall into a distance range relative to
  the reference feature.   
  
  # Arguments:
  # reference feature name, reference feature strand, 
  # target feature name, target feature strand,
  # beginning of range, end of range, SGA file with merged tag count distributions.
  #
  # Optional arguments :
  # Count cut-off value for target feature [default=1]
  # Window width (histogram bin size) [default=1]

  Giovanna Ambrosini, EPFL, Giovanna.Ambrosini@epfl.ch

  Copyright (c) 2015 EPFL and Swiss Institute of Bioinformatics.

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#ifdef DEBUG
#include <mcheck.h>
#endif

#include "version.h"

/*#define BUF_SIZE 4096 */
#define BUF_SIZE 8192
#define LINE_SIZE 1024
#define FT_MAX  64
#define SEQ_ID  32
#define POS_MAX 16
#define CNT_MAX 16
#define EXT_MAX 256

typedef struct _options_t {
  int cutOff;
  int help;
  int debug;
} options_t;

static options_t options;

typedef struct _feature_t {
   char ft[FT_MAX];
   char strand;
   int  *pos;
   int  *cnt;
   int  *ptr;
   char *str;
} feature_t, *feature_p_t;

feature_t ref_ft;
feature_t tar_ft;

feature_t tar_ft_plus;
feature_t tar_ft_minus;

typedef struct _table_t {
   int *pos;
   int *bin;
   long *val;
} table_t, *table_p_t;

int l5_p, l3_p;

char *RefFeature = NULL;
char *TarFeature = NULL;
int From = 0;
int To = 0;
int Win = 1;

unsigned long TarOffset = 0;

unsigned long ref_cnt = 0;   /* Table row count (reference) */
unsigned long Rtot = 0;      /* Total Reference Counts      */
unsigned long Ttot = 0;      /* Total Target Counts         */
unsigned long Len  = 0;      /* Total Sequence Length       */

int
table_init(table_p_t table)
{
  /* Initialize Histogram (table) */
  /* The windows or bins are placed such that one window will be 
     centered at pos 0 (odd window size), -0.5 even (window size).
     The whole range [$from,$to] will be shortened to an integer 
     number of window sizes.

     Example: $from = -20, $to = 20, $ win =5;
     Windows: [-17,-13], [-12,-8], [-7,-3], [-2,2], [3,7], [8,12], [13,17]
     New range: $from = -17, $to =17
  */

  /* begin (xb), end (xe), and center position (xe) of window near 0  */
  int xb, xc, xe;
  xb = -Win/2;
  xe = xb + Win - 1;
  xc = (xb + xe)/2;

  if (options.debug)
    fprintf(stderr, " xb  %d, xc %d, xe %d\n", xb, xe, xc);
  if (From > xb) {
    l5_p = (From - xb)/Win + 1;
  } else {
    l5_p = -(xb - From)/Win;
  }
  if (To >= xe) {
    l3_p = (To - xe)/Win;
  } else {
    l3_p = -(xe - To)/Win + 1;
  }
  if (options.debug)
    fprintf(stderr, " l5_p: %d  l3_p: %d\n", l5_p, l3_p);  
  /* New range  */
  From = xb + l5_p * Win;
  To = xe + l3_p * Win;

  fprintf(stderr, "New range: %d - %d\n", From, To);
  /* Allocate Table genomic positions (bp) */
  table->pos = (int *) malloc((l3_p - l5_p + 1) * sizeof(int));
  if (table->pos == NULL) {
   fprintf(stderr, "Out of memory: %s(%d)\n",strerror(errno), errno);
   return 1;
  }
  /* Allocate Table bins */
  table->bin = (int *) malloc((To - From + 1) * sizeof(int));
  if (table->bin == NULL) {
   fprintf(stderr, "Out of memory: %s(%d)\n",strerror(errno), errno);
   return 1;
  }
  /* Allocate Table count values */
  table->val = (long *) malloc((l3_p - l5_p + 1) * sizeof(long));
  if (table->val == NULL) {
   fprintf(stderr, "Out of memory: %s(%d)\n",strerror(errno), errno);
   return 1;
  }
  int i;
  for (i = 0; i <= To-From; i++) {
    table->bin[i] = (int)(i/Win);
  }
  int j;
  i = 0;
  for (j = l5_p; j <= l3_p; j++) {
    table->val[i] = 0;
    i++;
  }
  i = 0;
  for (j = l5_p; j <= l3_p; j++) {
    table->pos[i] = xc + j * Win;;
    i++;
  }
  /* Print out first table row (x coordinates) */
  i = 0;
  char blanks[10];
  memset(blanks, ' ', 10);
  blanks[9] = '\0';
  printf("%10s", blanks);
  for (j = l5_p; j <= l3_p; j++) {
    printf(" %9.1f", (float)table->pos[i]);
    i++;
  }
  printf("\n");
  TarOffset = abs(From) + abs(To) + 1;
  return 0;
}

void
print_counts_basic_basic(table_p_t table, unsigned int ref_nb, unsigned int tar_nb)
{
  long j, k, i, n;
  int from, to; 
  int m;

  /* fprintf(stderr, "print_counts_basic_basic: ref feat : %u  tar feat : %u\n", ref_nb, tar_nb); */
  //printf("print_counts_basic_basic: ref feat : %u  tar feat : %u\n", ref_nb, tar_nb);
  for (j = 1; j <= (long)ref_nb; j++) {
    i = 0;
    for (m = l5_p; m <= l3_p; m++) {
      table->val[i] = 0;
      i++;
    }
    k = ref_ft.ptr[j];
    from = ref_ft.pos[j] + From;
    to = ref_ft.pos[j] + To;
    /*printf("UPSTREAM: k = %ld, Ref pos = %ld, Tar pos = %ld, FROM = %d, TO =%d\n", k, ref_ft.pos[j], tar_ft.pos[k], from, to); */
    while (tar_ft.pos[k] > to) {k--;}
    while ((k >= 0) && tar_ft.pos[k] >= from) {
      n = tar_ft.pos[k] - from;
      /*printf("UPSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft.pos[k], from, table->bin[n]); */
      table->val[table->bin[n]] += tar_ft.cnt[k];
      /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
      k--; /* examine further upstream positions within the window range */
    }
    k = ref_ft.ptr[j] + 1;
    while ((tar_ft.pos[k] < from)) {k++;}
    while ((tar_ft.pos[k] <= to) && k <= (long)tar_nb) {
      n = tar_ft.pos[k] - from;
      /*printf("DOWNSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft.pos[k], from, table->bin[n]); */
      table->val[table->bin[n]] += tar_ft.cnt[k];
      /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
      k++; /* examine further downstream positions within the window range */
    }
    i = 0;
    ref_cnt++;
    printf("%10ld  ", ref_cnt);
    for (m = l5_p; m <= l3_p; m++) {
      printf(" %ld", table->val[i]);
      i++;
    }
    printf("\n");
  }
}

void
print_counts_oriented_plus(table_p_t table, unsigned int ref_nb, unsigned int tar_p_nb, unsigned int tar_m_nb)
{
  long j, k, i, n;
  int from, to; 
  int m;

  /*printf("print_counts_oriented_plus: ref feat : %u  tar feat (+): %u tar feat (-): %u\n", ref_nb, tar_p_nb, tar_m_nb); */
  for (j = 1; j <= (long)ref_nb; j++) {
    i = 0;
    for (m = l5_p; m <= l3_p; m++) {
      table->val[i] = 0;
      i++;
    }
    if (ref_ft.str[j] == '+') {
      k = ref_ft.ptr[j];
      from = ref_ft.pos[j] + From;
      to = ref_ft.pos[j] + To;
      /*printf("UPSTREAM: k = %ld, Ref pos = %ld, Tar pos = %ld, FROM = %d, TO =%d\n", k, ref_ft.pos[j], tar_ft_plus.pos[k], from, to); */
      while (tar_ft_plus.pos[k] > to) {k--;}
      while ((k >= 0) && tar_ft_plus.pos[k] >= from) {
        n = tar_ft_plus.pos[k] - from;
        /*printf("UPSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_plus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_plus.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k--; /* examine further upstream positions within the window range */
      }
      k = ref_ft.ptr[j] + 1;
      while ((tar_ft_plus.pos[k] < from)) {k++;}
      while ((tar_ft_plus.pos[k] <= to) && k <= (long)tar_p_nb) {
        n = tar_ft_plus.pos[k] - from;
        /*printf("DOWNSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_plus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_plus.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k++; /* examine further downstream positions within the window range */
      }
    } else { /* Ref strand = '-' */
      k = ref_ft.ptr[j];
      from = ref_ft.pos[j] - To;
      to = ref_ft.pos[j] - From;
      /*printf("UPSTREAM: k = %ld, Ref pos = %ld, Tar pos = %ld, FROM = %d, TO =%d\n", k, ref_ft.pos[j], tar_ft_minus.pos[k], from, to); */
      while (tar_ft_minus.pos[k] > to) {k--;}
      while ((k >= 0) && tar_ft_minus.pos[k] >= from) {
        n = to - tar_ft_minus.pos[k];
        /*printf("UPSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_minus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_minus.cnt[k];
        k--; /* examine further upstream positions within the window range */
      }
      k = ref_ft.ptr[j] + 1;
      while ((tar_ft_minus.pos[k] < from)) {k++;}
      while ((tar_ft_minus.pos[k] <= to) && k <= (long)tar_m_nb) {
        n = to - tar_ft_minus.pos[k];
        /*printf("DOWNSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_minus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_minus.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k++; /* examine further downstream positions within the window range */
      }
    }
    i = 0;
    ref_cnt++;
    printf("%10ld  ", ref_cnt);
    for (m = l5_p; m <= l3_p; m++) {
      printf(" %ld", table->val[i]);
      i++;
    }
    printf("\n");
  }
}

void
print_counts_oriented_minus(table_p_t table, unsigned int ref_nb, unsigned int tar_p_nb, unsigned int tar_m_nb)
{
  long j, k, i, n;
  int from, to; 
  int m;

  /*printf("print_counts_oriented_minus: ref feat : %u  tar feat (+): %u tar feat (-): %u\n", ref_nb, tar_p_nb, tar_m_nb); */
  for (j = 1; j <= (long)ref_nb; j++) {
    i = 0;
    for (m = l5_p; m <= l3_p; m++) {
      table->val[i] = 0;
      i++;
    }
    if (ref_ft.str[j] == '+') {
      k = ref_ft.ptr[j];
      from = ref_ft.pos[j] + From;
      to = ref_ft.pos[j] + To;
      /*printf("UPSTREAM: k = %ld, Ref pos = %ld, Tar pos = %ld, FROM = %d, TO =%d\n", k, ref_ft.pos[j], tar_ft_minus.pos[k], from, to); */
      while (tar_ft_minus.pos[k] > to) {k--;}
      while ((k >= 0) && tar_ft_minus.pos[k] >= from) {
        n = tar_ft_minus.pos[k] - from;
        /*printf("UPSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_minus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_minus.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k--; /* examine further upstream positions within the window range */
      }
      k = ref_ft.ptr[j] + 1;
      while ((tar_ft_minus.pos[k] < from)) {k++;}
      while ((tar_ft_minus.pos[k] <= to) && k <= (long)tar_m_nb) {
        n = tar_ft_minus.pos[k] - from;
        /*printf("DOWNSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_minus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_minus.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k++; /* examine further downstream positions within the window range */
      }
    } else { /* Ref strand = '-' */
      k = ref_ft.ptr[j];
      from = ref_ft.pos[j] - To;
      to = ref_ft.pos[j] - From;
      /*printf("UPSTREAM: k = %ld, Ref pos = %ld, Tar pos = %ld, FROM = %d, TO =%d\n", k, ref_ft.pos[j], tar_ft_plus.pos[k], from, to); */
      while (tar_ft_plus.pos[k] > to) {k--;}
      while ((k >= 0) && tar_ft_plus.pos[k] >= from) {
        n = to - tar_ft_plus.pos[k];
        /*printf("UPSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_plus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_plus.cnt[k];
        k--; /* examine further upstream positions within the window range */
      }
      k = ref_ft.ptr[j] + 1;
      while ((tar_ft_plus.pos[k] < from)) {k++;}
      while ((tar_ft_plus.pos[k] <= to) && k <= (long)tar_p_nb) {
        n = to - tar_ft_plus.pos[k];
        /*printf("DOWNSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_plus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft_plus.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k++; /* examine further downstream positions within the window range */
      }
    }
    i = 0;
    ref_cnt++;
    printf("%10ld  ", ref_cnt);
    for (m = l5_p; m <= l3_p; m++) {
      printf(" %ld", table->val[i]);
      i++;
    }
    printf("\n");
  }
}

void
print_counts_oriented_both(table_p_t table, unsigned int ref_nb, unsigned int tar_nb)
{
  long j, k, i, n;
  int from, to; 
  int m;

  /*printf("print_counts_oriented_both: ref feat : %u  tar feat : %u\n", ref_nb, tar_nb); */
  for (j = 1; j <= (long)ref_nb; j++) {
    i = 0;
    for (m = l5_p; m <= l3_p; m++) {
      table->val[i] = 0;
      i++;
    }
    if (ref_ft.str[j] == '+') {
      k = ref_ft.ptr[j];
      from = ref_ft.pos[j] + From;
      to = ref_ft.pos[j] + To;
      /*printf("UPSTREAM: k = %ld, Ref pos = %ld, Tar pos = %ld, FROM = %d, TO =%d\n", k, ref_ft.pos[j], tar_ft.pos[k], from, to); */
      while (tar_ft.pos[k] > to) {k--;}
      while ((k >= 0) && tar_ft.pos[k] >= from) {
        n = tar_ft.pos[k] - from;
        /*printf("UPSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k--; /* examine further upstream positions within the window range */
      }
      k = ref_ft.ptr[j] + 1;
      while ((tar_ft.pos[k] < from)) {k++;}
      while ((tar_ft.pos[k] <= to) && k <= (long)tar_nb) {
        n = tar_ft.pos[k] - from;
        /*printf("DOWNSTREAM: n = %ld, TAR pos = %d, FROM = %d, TO = %d, REF pos = %d, bin= %d\n", n, tar_ft.pos[k], from, to, ref_ft.pos[j], table->bin[n]);*/
        table->val[table->bin[n]] += tar_ft.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k++; /* examine further downstream positions within the window range */
      }
    } else { /* Ref strand = '-' */
      k = ref_ft.ptr[j];
      from = ref_ft.pos[j] - To;
      to = ref_ft.pos[j] - From;
      /*printf("UPSTREAM: k = %ld, Ref pos = %ld, Tar pos = %ld, FROM = %d, TO =%d\n", k, ref_ft.pos[j], tar_ft.pos[k], from, to); */
      while (tar_ft.pos[k] > to) {k--;}
      while ((k >= 0) && tar_ft.pos[k] >= from) {
        n = to - tar_ft.pos[k];
        /*printf("UPSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft.cnt[k];
        k--; /* examine further upstream positions within the window range */
      }
      k = ref_ft.ptr[j] + 1;
      while ((tar_ft.pos[k] < from)) {k++;}
      while ((tar_ft.pos[k] <= to) && k <= (long)tar_nb) {
        n = to - tar_ft.pos[k];
        /*printf("DOWNSTREAM: n = %ld, TAR pos = %ld, FROM = %d bin= %d\n", n, tar_ft_plus.pos[k], from, table->bin[n]); */
        table->val[table->bin[n]] += tar_ft.cnt[k];
        /*printf("table val[%d] = %d\n", table->bin[n], table->val[table->bin[n]]); */
        k++; /* examine further downstream positions within the window range */
      }
    }
    i = 0;
    ref_cnt++;
    printf("%10ld  ", ref_cnt);
    for (m = l5_p; m <= l3_p; m++) {
      printf(" %ld", table->val[i]);
      i++;
    }
    printf("\n");
  }
}

int
extract_basic_basic(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_basic: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_basic: malloc");
    return 1;
  }
  if ((tar_ft.pos = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_basic: malloc");
    return 1;
  }
  if ((tar_ft.cnt = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_basic: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_basic_basic: malloc");
    return 1;
  }

  tar_ft.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  /* while (fscanf(f,"%s %s %d %c %d %*s", seq_id, feature, &pos, &strand, &cnt) != EOF) { */
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char ext[EXT_MAX]; 
    char strand = '\0';
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_basic_basic: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_basic: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_basic: realloc");
        return 1;
      }
    }
    if (k >= tf_mLen - 1) {
      tf_mLen *= 2;
      if ((tar_ft.pos = (int *)realloc(tar_ft.pos, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_basic: realloc");
        return 1;
      }
      if ((tar_ft.cnt = (int *)realloc(tar_ft.cnt, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_basic: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_basic_basic(table, j, k);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k = 0;
      tar_ft.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0 && strand == ref_ft.strand) {
      j++;
      ref_ft.ptr[j] = k;
      ref_ft.pos[j] = pos;
    } else if (strcmp(feature, tar_ft.ft) == 0 && strand == tar_ft.strand) {
      k++;
      tar_ft.pos[k] = pos;
      if (cnt > options.cutOff)
        tar_ft.cnt[k] = options.cutOff;
      else
        tar_ft.cnt[k] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_basic_basic(table, j, k);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(tar_ft.pos);
  free(tar_ft.cnt);
  return 0;
}

int
extract_basic_both(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_both: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_both: malloc");
    return 1;
  }
  if ((tar_ft.pos = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_both: malloc");
    return 1;
  }
  if ((tar_ft.cnt = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_basic_both: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_basic_both: malloc");
    return 1;
  }

  tar_ft.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  /* while (fscanf(f,"%s %s %d %c %d %*s", seq_id, feature, &pos, &strand, &cnt) != EOF) { */
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char strand = '\0';
    char ext[EXT_MAX]; 
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_basic_both: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_both: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_both: realloc");
        return 1;
      }
    }
    if (k >= tf_mLen - 1) {
      tf_mLen *= 2;
      if ((tar_ft.pos = (int *)realloc(tar_ft.pos, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_both: realloc");
        return 1;
      }
      if ((tar_ft.cnt = (int *)realloc(tar_ft.cnt, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_basic_both: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_basic_basic(table, j, k);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k = 0;
      tar_ft.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0 && strand == ref_ft.strand) {
      j++;
      ref_ft.ptr[j] = k;
      ref_ft.pos[j] = pos;
    } else if (strcmp(feature, tar_ft.ft) == 0) {
      k++;
      tar_ft.pos[k] = pos;
      if (cnt > options.cutOff)
        tar_ft.cnt[k] = options.cutOff;
      else
        tar_ft.cnt[k] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_basic_basic(table, j, k);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(tar_ft.pos);
  free(tar_ft.cnt);
  return 0;
}

int
extract_both_basic(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_basic: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_basic: malloc");
    return 1;
  }
  if ((tar_ft.pos = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_basic: malloc");
    return 1;
  }
  if ((tar_ft.cnt = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_basic: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_both_basic: malloc");
    return 1;
  }

  tar_ft.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  /* while (fscanf(f,"%s %s %d %c %d %*s", seq_id, feature, &pos, &strand, &cnt) != EOF) { */
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char strand = '\0';
    char ext[EXT_MAX]; 
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_both_basic: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_basic: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_basic: realloc");
        return 1;
      }
    }
    if (k >= tf_mLen - 1) {
      tf_mLen *= 2;
      if ((tar_ft.pos = (int *)realloc(tar_ft.pos, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_basic: realloc");
        return 1;
      }
      if ((tar_ft.cnt = (int *)realloc(tar_ft.cnt, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_basic: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_basic_basic(table, j, k);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k = 0;
      tar_ft.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0) {
      j++;
      ref_ft.ptr[j] = k;
      ref_ft.pos[j] = pos;
    } else if (strcmp(feature, tar_ft.ft) == 0 && strand == tar_ft.strand) {
      k++;
      tar_ft.pos[k] = pos;
      if (cnt > options.cutOff)
        tar_ft.cnt[k] = options.cutOff;
      else
        tar_ft.cnt[k] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_basic_basic(table, j, k);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(tar_ft.pos);
  free(tar_ft.cnt);
  return 0;
}

int
extract_both_both(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_both: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_both: malloc");
    return 1;
  }
  if ((tar_ft.pos = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_both: malloc");
    return 1;
  }
  if ((tar_ft.cnt = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_both_both: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_both_both: malloc");
    return 1;
  }

  tar_ft.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char strand = '\0';
    char ext[EXT_MAX]; 
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_both_both: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_both: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_both: realloc");
        return 1;
      }
    }
    if (k >= tf_mLen - 1) {
      tf_mLen *= 2;
      if ((tar_ft.pos = (int *)realloc(tar_ft.pos, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_both: realloc");
        return 1;
      }
      if ((tar_ft.cnt = (int *)realloc(tar_ft.cnt, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_both_both: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_basic_basic(table, j, k);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k = 0;
      tar_ft.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0) {
      j++;
      ref_ft.ptr[j] = k;
      ref_ft.pos[j] = pos;
    } else if (strcmp(feature, tar_ft.ft) == 0) {
      k++;
      tar_ft.pos[k] = pos;
      if (cnt > options.cutOff)
        tar_ft.cnt[k] = options.cutOff;
      else
        tar_ft.cnt[k] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_basic_basic(table, j, k);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(tar_ft.pos);
  free(tar_ft.cnt);
  return 0;
}

int
extract_oriented_plus(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k1 = 0;
  unsigned int k2 = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_p_mLen = BUF_SIZE;
  size_t tf_m_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }
  if ((ref_ft.str = (char *)calloc(rf_mLen, sizeof(char))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }
  if ((tar_ft_plus.pos = (int *)calloc(tf_p_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }
  if ((tar_ft_plus.cnt = (int *)calloc(tf_p_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }
  if ((tar_ft_minus.pos = (int *)calloc(tf_m_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }
  if ((tar_ft_minus.cnt = (int *)calloc(tf_m_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_oriented_plus: malloc");
    return 1;
  }

  tar_ft_plus.pos[0] = -TarOffset;
  tar_ft_minus.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char strand = '\0';
    char ext[EXT_MAX]; 
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
      if ((ref_ft.str = (char *)realloc(ref_ft.str, rf_mLen * sizeof(char))) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
    }
    if (k1 >= tf_p_mLen - 1) {
      tf_p_mLen *= 2;
      if ((tar_ft_plus.pos = (int *)realloc(tar_ft_plus.pos, tf_p_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
      if ((tar_ft_plus.cnt = (int *)realloc(tar_ft_plus.cnt, tf_p_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
    }
    if (k2 >= tf_m_mLen - 1) {
      tf_m_mLen *= 2;
      if ((tar_ft_minus.pos = (int *)realloc(tar_ft_minus.pos, tf_m_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
      if ((tar_ft_minus.cnt = (int *)realloc(tar_ft_minus.cnt, tf_m_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_plus: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft_plus.pos[k1 + 1] = ref_ft.pos[j] + TarOffset;
      tar_ft_minus.pos[k2 + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_oriented_plus(table, j, k1, k2);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k1 = 0;
      k2 = 0;
      tar_ft_plus.pos[0] = -TarOffset;
      tar_ft_minus.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0) {
      j++;
      if (strand == '+') {
        ref_ft.ptr[j] = k1;
      } else if (strand == '-') {
        ref_ft.ptr[j] = k2;
      }
      ref_ft.pos[j] = pos;
      ref_ft.str[j] = strand;
    } else if (strcmp(feature, tar_ft.ft) == 0 && strand == '+') {
      k1++;
      tar_ft_plus.pos[k1] = pos;
      if (cnt > options.cutOff)
        tar_ft_plus.cnt[k1] = options.cutOff;
      else
        tar_ft_plus.cnt[k1] = cnt;
    } else if (strcmp(feature, tar_ft.ft) == 0 && strand == '-') {
      k2++;
      tar_ft_minus.pos[k2] = pos;
      if (cnt > options.cutOff)
        tar_ft_minus.cnt[k2] = options.cutOff;
      else
        tar_ft_minus.cnt[k2] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft_plus.pos[k1 + 1] = ref_ft.pos[j] + TarOffset;
  tar_ft_minus.pos[k2 + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_oriented_plus(table, j, k1, k2);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(ref_ft.str);
  free(tar_ft_plus.pos);
  free(tar_ft_plus.cnt);
  free(tar_ft_minus.pos);
  free(tar_ft_minus.cnt);
  return 0;
}

int
extract_oriented_minus(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k1 = 0;
  unsigned int k2 = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_p_mLen = BUF_SIZE;
  size_t tf_m_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }
  if ((ref_ft.str = (char *)calloc(rf_mLen, sizeof(char))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }
  if ((tar_ft_plus.pos = (int *)calloc(tf_p_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }
  if ((tar_ft_plus.cnt = (int *)calloc(tf_p_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }
  if ((tar_ft_minus.pos = (int *)calloc(tf_m_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }
  if ((tar_ft_minus.cnt = (int *)calloc(tf_m_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_oriented_minus: malloc");
    return 1;
  }

  tar_ft_plus.pos[0] = -TarOffset;
  tar_ft_minus.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  /* while (fscanf(f,"%s %s %d %c %d %*s", seq_id, feature, &pos, &strand, &cnt) != EOF) { */
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char strand = '\0';
    char ext[EXT_MAX]; 
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
      if ((ref_ft.str = (char *)realloc(ref_ft.str, rf_mLen * sizeof(char))) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
    }
    if (k1 >= tf_p_mLen - 1) {
      tf_p_mLen *= 2;
      if ((tar_ft_plus.pos = (int *)realloc(tar_ft_plus.pos, tf_p_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
      if ((tar_ft_plus.cnt = (int *)realloc(tar_ft_plus.cnt, tf_p_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
    }
    if (k2 >= tf_m_mLen - 1) {
      tf_m_mLen *= 2;
      if ((tar_ft_minus.pos = (int *)realloc(tar_ft_minus.pos, tf_m_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
      if ((tar_ft_minus.cnt = (int *)realloc(tar_ft_minus.cnt, tf_m_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_minus: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft_plus.pos[k1 + 1] = ref_ft.pos[j] + TarOffset;
      tar_ft_minus.pos[k2 + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_oriented_minus(table, j, k1, k2);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k1 = 0;
      k2 = 0;
      tar_ft_plus.pos[0] = -TarOffset;
      tar_ft_minus.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0) {
      j++;
      if (strand == '+') {
        ref_ft.ptr[j] = k2;
      } else if (strand == '-') {
        ref_ft.ptr[j] = k1;
      }
      ref_ft.pos[j] = pos;
      ref_ft.str[j] = strand;
    } else if (strcmp(feature, tar_ft.ft) == 0 && strand == '+') {
      k1++;
      tar_ft_plus.pos[k1] = pos;
      if (cnt > options.cutOff)
        tar_ft_plus.cnt[k1] = options.cutOff;
      else
        tar_ft_plus.cnt[k1] = cnt;
    } else if (strcmp(feature, tar_ft.ft) == 0 && strand == '-') {
      k2++;
      tar_ft_minus.pos[k2] = pos;
      if (cnt > options.cutOff)
        tar_ft_minus.cnt[k2] = options.cutOff;
      else
        tar_ft_minus.cnt[k2] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft_plus.pos[k1 + 1] = ref_ft.pos[j] + TarOffset;
  tar_ft_minus.pos[k2 + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_oriented_minus(table, j, k1, k2);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(ref_ft.str);
  free(tar_ft_plus.pos);
  free(tar_ft_plus.cnt);
  free(tar_ft_minus.pos);
  free(tar_ft_minus.cnt);
  return 0;
}

int
extract_oriented_zero(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_zero: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_zero: malloc");
    return 1;
  }
  if ((ref_ft.str = (char *)calloc(rf_mLen, sizeof(char))) == NULL) {
    perror("extract_oriented_zero: malloc");
    return 1;
  }
  if ((tar_ft.pos = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_zero: malloc");
    return 1;
  }
  if ((tar_ft.cnt = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_zero: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_oriented_zero: malloc");
    return 1;
  }

  tar_ft.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  /* while (fscanf(f,"%s %s %d %c %d %*s", seq_id, feature, &pos, &strand, &cnt) != EOF) { */
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char strand = '\0';
    char ext[EXT_MAX]; 
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_oriented_zero: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_zero: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_zero: realloc");
        return 1;
      }
      if ((ref_ft.str = (char *)realloc(ref_ft.str, rf_mLen * sizeof(char))) == NULL) {
        perror("extract_oriented_zero: realloc");
        return 1;
      }
    }
    if (k >= tf_mLen - 1) {
      tf_mLen *= 2;
      if ((tar_ft.pos = (int *)realloc(tar_ft.pos, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_zero: realloc");
        return 1;
      }
      if ((tar_ft.cnt = (int *)realloc(tar_ft.cnt, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_zero: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_oriented_both(table, j, k);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k = 0;
      tar_ft.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0) {
        if (strand == '+' || strand == '-') {
          j++;
          ref_ft.ptr[j] = k;
          ref_ft.pos[j] = pos;
          ref_ft.str[j] = strand;
        }
    } else if (strcmp(feature, tar_ft.ft) == 0 && strand == tar_ft.strand) {
        k++;
        tar_ft.pos[k] = pos;
        if (cnt > options.cutOff)
          tar_ft.cnt[k] = options.cutOff;
        else
          tar_ft.cnt[k] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_oriented_both(table, j, k);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(ref_ft.str);
  free(tar_ft.pos);
  free(tar_ft.cnt);
  return 0;
}

int
extract_oriented_both(FILE *input, char *iFile, table_p_t table) 
{
  unsigned int k = 0;
  unsigned int j = 0;
  char seq_id_prev[SEQ_ID] = "";
  int pos, cnt, last_pos = 0;
  char *s, *res, *buf;
  size_t rf_mLen = BUF_SIZE;
  size_t tf_mLen = BUF_SIZE;
  size_t mLen = LINE_SIZE;

  if (options.debug && input != stdin) {
    char sort_cmd[1024] = "sort -s -c -k1,1 -k3,3n ";
    fprintf(stderr, "Check whether file %s is properly sorted\n", iFile);
    if (strcat(sort_cmd, iFile) == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    if (strcat(sort_cmd, " 2>/tmp/sortcheck.out") == NULL) {
      fprintf(stderr, "strcat failed\n");
      return 1;
    }
    fprintf(stderr, "executing : %s\n", sort_cmd);
    int sys_code = system(sort_cmd);
    /*fprintf(stderr,"system command sort : return code %d\n", sys_code);*/
    if (sys_code != 0) {
      fprintf(stderr, "system command failed\n");
      return 1;
    }
    struct stat file_status;
    if(stat("/tmp/sortcheck.out", &file_status) != 0){
      fprintf(stderr, "could not stat\n");
      return 1;
    }
    if (file_status.st_size != 0) {
      fprintf(stderr, "SGA file %s is not properly sorted\n", iFile);
      return 1;
    } else {
      system("/bin/rm /tmp/sortcheck.out");
    }
  }
  if ((ref_ft.pos = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_both: malloc");
    return 1;
  }
  if ((ref_ft.ptr = (int *)calloc(rf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_both: malloc");
    return 1;
  }
  if ((ref_ft.str = (char *)calloc(rf_mLen, sizeof(char))) == NULL) {
    perror("extract_oriented_both: malloc");
    return 1;
  }
  if ((tar_ft.pos = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_both: malloc");
    return 1;
  }
  if ((tar_ft.cnt = (int *)calloc(tf_mLen, sizeof(int))) == NULL) {
    perror("extract_oriented_both: malloc");
    return 1;
  }
  if ((s = malloc(mLen * sizeof(char))) == NULL) {
    perror("extract_oriented_both: malloc");
    return 1;
  }

  tar_ft.pos[0] = -TarOffset;
#ifdef DEBUG
  int c = 1; 
#endif
  /* while (fscanf(f,"%s %s %d %c %d %*s", seq_id, feature, &pos, &strand, &cnt) != EOF) { */
  while ((res = fgets(s, (int) mLen, input)) != NULL) {
    size_t cLen = strlen(s);
    char seq_id[SEQ_ID] = "";
    char feature[FT_MAX] = ""; 
    char position[POS_MAX] = ""; 
    char count[CNT_MAX] = ""; 
    char strand = '\0';
    char ext[EXT_MAX]; 
    unsigned int i = 0;
 
    memset(ext, 0, (size_t)EXT_MAX);
    while (cLen + 1 == mLen && s[cLen - 1] != '\n') {
      mLen *= 2;
      if ((s = realloc(s, mLen)) == NULL) {
        perror("extract_oriented_both: realloc");
        return 1;
      }
      res = fgets(s + cLen, (int) (mLen - cLen), input);
      cLen = strlen(s);
    }
    if (s[cLen - 1] == '\n')
      s[cLen - 1] = 0;

    buf = s;
    /* Get SGA fields */
    /* SEQ ID */
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= SEQ_ID) {
        fprintf(stderr, "Seq ID is too long \"%s\" \n", buf);
        return 1;
      }
      seq_id[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* FEATURE */
    i = 0;
    while (*buf != 0 && !isspace(*buf)) {
      if (i >= FT_MAX) {
        fprintf(stderr, "Feature is too long \"%s\" \n", buf);
        return 1;
      }
      feature[i++] = *buf++;
    }
    while (isspace(*buf))
      buf++;
    /* Position */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= POS_MAX) {
        fprintf(stderr, "Position is too large \"%s\" \n", buf);
        return 1;
      }
      position[i++] = *buf++;
    }
    position[i] = 0;
    pos = atoi(position);
    while (isspace(*buf))
      buf++;
    /* Strand */
    strand = *buf++;
    while (isspace(*buf))
      buf++;
    /* Counts */
    i = 0;
    while (isdigit(*buf)) { 
      if (i >= CNT_MAX) {
        fprintf(stderr, "Count is too large \"%s\" \n", buf);
        return 1;
      }
      count[i++] = *buf++;
    }
    count[i] = 0;
    cnt = atoi(count);
    while (isspace(*buf))
      buf++;
    /* SGA Extension */
    i = 0;
    while (*buf != 0) {
      if (i >= EXT_MAX) {
        fprintf(stderr, "Extension is too long \"%s\" \n", buf);
        return 1;
      }
      ext[i++] = *buf++;
    }
#ifdef DEBUG
    printf(" [%d] seq ID: %s   Feat: %s%c  Pos: %d  Cnts: %d Ext: %s\n", c++, seq_id, feature, strand, pos, cnt, ext);
#endif
    if (j >= rf_mLen - 1) {
      rf_mLen *= 2;
      if (( ref_ft.pos = (int *)realloc(ref_ft.pos, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_both: realloc");
        return 1;
      }
      if ((ref_ft.ptr = (int *)realloc(ref_ft.ptr, rf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_both: realloc");
        return 1;
      }
      if ((ref_ft.str = (char *)realloc(ref_ft.str, rf_mLen * sizeof(char))) == NULL) {
        perror("extract_oriented_both: realloc");
        return 1;
      }
    }
    if (k >= tf_mLen - 1) {
      tf_mLen *= 2;
      if ((tar_ft.pos = (int *)realloc(tar_ft.pos, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_both: realloc");
        return 1;
      }
      if ((tar_ft.cnt = (int *)realloc(tar_ft.cnt, tf_mLen * sizeof(int))) == NULL) {
        perror("extract_oriented_both: realloc");
        return 1;
      }
    }
    if (strcmp(seq_id, seq_id_prev) != 0) {
      tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
      print_counts_oriented_both(table, j, k);
      strcpy(seq_id_prev, seq_id);
      j = 0;
      k = 0;
      tar_ft.pos[0] = -TarOffset;
      Len += last_pos;
    }
    if (strcmp(feature, ref_ft.ft) == 0) {
        if (strand == '+' || strand == '-') {
          j++;
          ref_ft.ptr[j] = k;
          ref_ft.pos[j] = pos;
          ref_ft.str[j] = strand;
        }
    } else if (strcmp(feature, tar_ft.ft) == 0) {
        k++;
        tar_ft.pos[k] = pos;
        if (cnt > options.cutOff)
          tar_ft.cnt[k] = options.cutOff;
        else
          tar_ft.cnt[k] = cnt;
    }
    last_pos = pos;
  } /* End of While */
  free(s);
  /* The last time (at EOF) */
  Len += last_pos;
  tar_ft.pos[k + 1] = ref_ft.pos[j] + TarOffset;
  print_counts_oriented_both(table, j, k);
  free(ref_ft.ptr);
  free(ref_ft.pos);
  free(ref_ft.str);
  free(tar_ft.pos);
  free(tar_ft.cnt);
  return 0;
}

int
main(int argc, char *argv[])
{
#ifdef DEBUG
  mcheck(NULL);
  mtrace();
#endif
  FILE *input;
  table_t table;
  options.cutOff = 1;
  int i = 0;

  while (1) {
    int c = getopt(argc, argv, "c:dhA:B:b:e:w:");
    if (c == -1)
      break;
    switch (c) {
      case 'c':
        options.cutOff = atoi(optarg);
        break;
      case 'd':
        options.debug = 1;
        break;
      case 'h':
        options.help = 1;
        break;
    case 'A':
        RefFeature = optarg;
	break;
    case 'B':
        TarFeature = optarg;
	break;
    case 'b':
        From =  atoi(optarg);
	break;
    case 'e':
        To =  atoi(optarg);
	break;
    case 'w':
        Win =  atoi(optarg);
	break;
      default:
        printf ("?? getopt returned character code 0%o ??\n", c);
    }
  }
  if (optind > argc || options.help == 1 || RefFeature == NULL  || TarFeature == NULL || From == To || Win == 0 || options.cutOff < 0) {
     fprintf(stderr, "Usage: %s [options] -A <feature A> -B <feature B> -b<from> -e<to> -w<bin> [<] <SGA file>\n"
         "       - version %s\n"
         "       where options are:\n"
         "  \t\t -h     Show this help text\n"
         "  \t\t -d     Print debug information and check SGA file\n"
         "  \t\t -c     Cut-Off value for target feature counts (default is %d)\n"
	 "\n\tFeature Correlation and Extraction Tool for ChIP-seq data analysis.\n"  
         "\tThe program reads a ChIP-seq data file (or from stdin [<]) in SGA format (<SGA file>),\n"
         "\tcontaining two features, a reference feature (<feature A>) and a target feature (<feature B>),\n"
         "\tcorrelates the genomic tag count distributions for the two features, and for each reference \n"         
	 "\tfeature, it extracts target feature tags that fall into a distance range ([from,to]) relative\n"
         "\tto the reference feature.\n"
         "\tThe feature specification must have the following format:\n"
         "      \t<feature> = <name> [<+|-|0[strandless]|a[any]|o[oriented]>]\n\n"
	 "\tmeaning that the feature field has a name and a strand specification.\n"
         "\tAccepted strand values are the following: +|-|0[strandless]|a[any]|o[oriented].\n"
         "\tIf the strand is not specified, it is set to a[any] by default.\n"
	 "\tThe <feature> name corresponds to the second field of the SGA file.\n"
	 "\tThe SGA input file MUST BE sorted by sequence name (or chromosome id), position, and strand.\n"
	 "\tOne should check the input SGA file with the following command:\n"
	 "\tsort -s -c -k1,1 -k3,3n -k4,4 <SGA file>.\n\n"
	 "\tIn debug mode (-d), the program performs the sorting order check.\n\n"
         "\tThe relative distance between the two features is analysed within a given range: <from>-<to>\n"
	 "\tthat should be greater than 0. A cut-off value may be specified for the target feature counts.\n"
	 "\tThe window width (-w) defines the histogram step size or bin. It must be an integer greater than 0.\n"
         "\tThe output is a table in text format consisting of all reference features (rows) with relative\n"
         "\ttarget tag counts in bins of a given size defined by window width (-w<bin>) (columns).\n\n",
              argv[0], VERSION, options.cutOff);
      return 1;
  }
  if (argc > optind) {
      if(!strcmp(argv[optind],"-")) {
          input = stdin;
      } else {
          input = fopen(argv[optind], "r");
          if (NULL == input) {
              fprintf(stderr, "Unable to open '%s': %s(%d)\n",
                  argv[optind], strerror(errno), errno);
             exit(EXIT_FAILURE);
          }
          if (options.debug)
             fprintf(stderr, "Processing file %s\n", argv[optind]);
      }
  } else {
      input = stdin;
  }
  if (options.debug) {
    fprintf(stderr, " Arguments:\n");
    fprintf(stderr, " Feature A (ref): %s\n", RefFeature);
    fprintf(stderr, " Feature B (tar): %s\n", TarFeature);
    fprintf(stderr, " Range : [%d, %d]\n", From, To);
    fprintf(stderr, " Sliding Window : %d\n", Win);
    fprintf(stderr, " Cut-off : %d\n", options.cutOff);
  }
  char *s = RefFeature;
  i = 0;
  while (*s != 0  && !isspace(*s)) {
    if (i >= FT_MAX) {
      fprintf(stderr, "Feature Description too long \"%s\" \n", RefFeature);
      return 1;
    }
    ref_ft.ft[i++] = *s++;
  }
  ref_ft.strand = '\0';
  while (isspace(*s++))
    ref_ft.strand = *s;
  s = TarFeature;
  i = 0;
  while (*s != 0  && !isspace(*s)) {
    if (i >= FT_MAX) {
      fprintf(stderr, " Feature Description too long \"%s\" \n", TarFeature);
      return 1;
    }
    tar_ft.ft[i++] = *s++;
  }
  tar_ft.strand = '\0';
  while (isspace(*s++))
  tar_ft.strand = *s;
  if (options.debug)
    fprintf(stderr, " Ref feature : %s %c (R)\n Tar Feature: %s %c (T)\n", ref_ft.ft, ref_ft.strand, tar_ft.ft, tar_ft.strand);
  size_t cLen = strlen(ref_ft.ft);
  if (!cLen) {
      fprintf(stderr, "Wrong Feature Description (ref) \"%s\". You must at least provide a name for your Ref Feature!\n Feature Specs Format: = <feature desc> [<strand(+|-|0|a|o)>]\n", RefFeature);
      return 1;
  }
  cLen = strlen(tar_ft.ft);
  if (!cLen) {
      fprintf(stderr, "Wrong Feature Description (tar) \"%s\". You must at least provide a name for you Target Feature! \n Feature Specs Format: <feature desc> [<strand(+|-|0|a|o)>]\n", TarFeature);
      return 1;
  }
  if (ref_ft.strand == '\0') {
      ref_ft.strand = 'a';
  }
  if (tar_ft.strand == '\0') {
      tar_ft.strand = 'a';
  }
  if (table_init(&table) != 0) {
    return 1;
  }
  int status = 0;
  if (ref_ft.strand == '+' || ref_ft.strand == '-' || ref_ft.strand == '0') {
    if (tar_ft.strand == '+' || tar_ft.strand == '-' || tar_ft.strand == '0')
      status = extract_basic_basic(input, argv[optind++], &table);
    else if (tar_ft.strand == 'a')
      status = extract_basic_both(input, argv[optind++], &table);
  } else if (ref_ft.strand == 'a') {
    if (tar_ft.strand == '+' || tar_ft.strand == '-' || tar_ft.strand == '0')
      status = extract_both_basic(input, argv[optind++], &table);
    else if (tar_ft.strand == 'a')
      status = extract_both_both(input, argv[optind++], &table);
  } else if (ref_ft.strand == 'o') {
    if (tar_ft.strand == '+')
      status = extract_oriented_plus(input, argv[optind++], &table);
    else if (tar_ft.strand == '-')
      status = extract_oriented_minus(input, argv[optind++], &table);
    else if (tar_ft.strand == '0')
      status = extract_oriented_zero(input, argv[optind++], &table);
    else if (tar_ft.strand == 'a')
      status = extract_oriented_both(input, argv[optind++], &table);
  } 
  fprintf(stderr, "Total Sequence Length: %ld\n", Len);
  if (input != stdin) {
      fclose(input);
  }
  free(table.bin);
  free(table.val);
  return status;
}
