/*
  chip_cor.c

  Feature correlation Tool.
  The program generates histograms showing
  the positional relationship of two features.

  # Arguments:
  # feature_1 type, feature_1 strand, feature_2 type, feature_2 strand,
  # beginning of range, end of range, window width (histogram step_size),
  # count cut-off value

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

  Copyright (c) 2014 EPFL and SIB 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 DEBUG 1
*/
#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  64
#define POS_MAX 16
#define CNT_MAX 16
#define EXT_MAX 256

typedef struct _options_t {
    int cutOff;
    int normFact;
    int help;
    int debug;
    int oriented;
} 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;

typedef struct _histo_t {
    int *pos;
    long *val;
} histo_t, *histo_p_t;

int l5_p, l3_p;

char *RefFeature = NULL;
char *TarFeature = NULL;
int From = 0;
int To = 0;
int Win = 0;
int auto_cor = 0;               /* Autocorrelation Flag        */
                             /* For Histogram Normalization */
unsigned long Rtot = 0;         /* Total Reference Counts      */
unsigned long Ttot = 0;         /* Total Target Counts         */
unsigned long Len = 0;          /* Total Sequence Length       */

int ref_specs = 1;              /* if = 1 feature specs : <name> AND <strand> */
int tar_specs = 1;

int or_same_strand = 0;
int or_opposite_strand = 0;
int or_any_strand = 0;

char *norm_opt[3] =
    { "Raw Counts", "Count density", "Global Normalization" };

int histo_print(histo_p_t histo) {
    int i = 0;
    int j;
    if (options.debug && Rtot != 0)
        fprintf(stderr, " Len %lu   Rtot  %lu  Ttot %lu Len/Rtot %lu\n",
                Len, Rtot, Ttot, Len / Rtot);
    printf
        ("#Norm : %s , Reference Feature : %s, Target feature : %s , Total Sequence Len : %lu , Total Reference Counts : %lu , Total Target Counts : %lu\n",
         norm_opt[options.normFact], RefFeature, TarFeature, Len, Rtot,
         Ttot);
    for (j = l5_p; j <= l3_p; j++) {
        if (auto_cor && (histo->pos[i] == 0)) {
            if (options.normFact == 1)  /* Count density  */
                printf("%9.1f %9.3e\n", (float) histo->pos[i],
                       ((float) histo->val[i] -
                        Rtot) / ((float) Rtot * Win));
            else if (options.normFact == 2)     /* Global Normalization  */
                printf("%9.1f %9.3e\n", (float) histo->pos[i],
                       ((float) histo->val[i] -
                        Rtot) * Len / ((float) Rtot * Ttot * Win));
            else
                printf("%9.1f %10ld\n", (float) histo->pos[i],
                       histo->val[i] - Rtot);
        } else {
            if (options.normFact == 1)  /* Count density  */
                printf("%9.1f %9.3e\n", (float) histo->pos[i],
                       (float) histo->val[i] / ((float) Rtot * Win));
            else if (options.normFact == 2)     /* Global Normalization  */
                printf("%9.1f %9.3e\n", (float) histo->pos[i],
                       ((float) histo->val[i] * Len /
                        ((float) Rtot * Ttot * Win)));
            else
                printf("%9.1f %10ld\n", (float) histo->pos[i],
                       histo->val[i]);
        }
        i++;
    }
    free(histo->pos);
    free(histo->val);
    return 0;
}

int histo_init(histo_p_t histo) {
    /* Fit Range to accomodate an integer number of window sizes
       and make sure that one bin is always centered at 0 */
    int xb, xc, xe;
    xb = -Win / 2;
    xe = xb + Win - 1;
    xc = (xb + xe) / 2;

    if (options.debug)
        fprintf(stderr, " xb  %d, xe %d, xc %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;
    if (options.debug)
        fprintf(stderr, " New range: [%d, %d]\n\n", From, To);
    /* Initialize Histogram */
    histo->pos = (int *) malloc((l3_p - l5_p + 1) * sizeof(int));
    if (histo->pos == NULL) {
        fprintf(stderr, "Out of memory: %s(%d)\n", strerror(errno), errno);
        return 1;
    }
    histo->val = (long *) malloc((l3_p - l5_p + 1) * sizeof(long));
    if (histo->val == NULL) {
        fprintf(stderr, "Out of memory: %s(%d)\n", strerror(errno), errno);
        return 1;
    }
    int i = 0;
    int j;
    for (j = l5_p; j <= l3_p; j++) {
        histo->pos[i] = xc + j * Win;
        histo->val[i] = 0;
        i++;
    }
    return 0;
}

/*
   Feature correlation measurement.
   For each Reference Feature (e.g.: A+), which is
   found in the same set of sequences (e.g : NC_000001.9),
   examine all Target Features whose positions
   lie within the histogram range [From, To].
   The number of ref features found in the seq set
   being under examination is given by the ft_nb variable.
   j= 1 .. ft_nb   (nr of ref. features)
   For each ref. feature:
   k = ref_ft.ptr[j] is the index of the target feature which
   is located at its closest upstream position (with respect
   to the feature itself), that is the preceding target feature index.
   The index k is then decreased in order to examine further
   upstream (left) tar features within the given window range.
   k = (ref_ft.ptr[j] + 1) is the index of the target feature which
   is located at its closest downstream position (with respect to
   the ref. feature itself), namely the subsequent target feature.
   The index k is then increased in order to examine all possible
   downstream target features within the given window.
*/
int histo_update(histo_p_t histo, unsigned int ft_nb, unsigned int tar_nb) {
    long j, k, n;
#ifdef DEBUG
    printf(" histo_update: nb of ref features : %d\n", ft_nb);
    printf(" histo_update: nb of tar features : %d\n", tar_nb);
    printf(" histo_update: From: %d  To: %d\n", From, To);
#endif
    for (j = 1; j <= (long) ft_nb; j++) {
        k = ref_ft.ptr[j];
        if (!options.oriented || (ref_ft.str[j] == '+')) {
            /* Ref Feat Un-oriented or
               Oriented on Pos Strand
               Examin Upstream Tar pos */
            while (tar_ft.pos[k] > ref_ft.pos[j] + To) {
                k--;
            }
            while ((k >= 0) && tar_ft.pos[k] >= ref_ft.pos[j] + From) {
                if (!options.oriented
                    || (or_same_strand && tar_ft.str[k] == '+')
                    || (or_opposite_strand && tar_ft.str[k] == '-')
                    || or_any_strand) {
                    n = (tar_ft.pos[k] - ref_ft.pos[j] - From) / Win;
                } else {
                    n = -1;
                    Ttot--;     /* decrease Target feature count (oriented mode) */
                }
                if (n >= 0) {
                    histo->val[n] += ref_ft.cnt[j] * tar_ft.cnt[k];
                }
#ifdef DEBUG
                printf(" histo_update feat idx:%d (1): bin:%d  hval:%ld\n",
                       j, n, histo->val[n]);
#endif
                k--;            /* examine further upstream positions within the window range */
            }
        } else {                /* Reverse Loop - Ref Feature Oriented on Neg Strand (Examin Upstream Tar pos) */
            while (tar_ft.pos[k] > ref_ft.pos[j] - From) {
                k--;
            }
            while ((k >= 0) && (tar_ft.pos[k] >= ref_ft.pos[j] - To)) {
                if ((or_same_strand && tar_ft.str[k] == '-')
                    || (or_opposite_strand && tar_ft.str[k] == '+')
                    || or_any_strand) {
                    n = (ref_ft.pos[j] - tar_ft.pos[k] - From) / Win;
                } else {
                    n = -1;
                    Ttot--;     /* decrease Target feature count (oriented mode) */
                }
                if (n >= 0) {
                    histo->val[n] += ref_ft.cnt[j] * tar_ft.cnt[k];
                }
#ifdef DEBUG
                printf(" histo_update feat idx:%d (1): bin:%d  hval:%d\n",
                       j, n, histo->val[n]);
#endif
                k--;            /* examine further upstream positions within the window range */
            }
        }
        k = ref_ft.ptr[j] + 1;
        if (!options.oriented || (ref_ft.str[j] == '+')) {      /* Ref Feat Un-oriented or Oriented on Pos Strand (Examin Downstream Tar pos) */
            while ((tar_ft.pos[k] < ref_ft.pos[j] + From)
                   && k <= (long) tar_nb) {
                k++;
            }
            while ((tar_ft.pos[k] <= ref_ft.pos[j] + To)
                   && k <= (long) tar_nb) {
                if (!options.oriented
                    || (or_same_strand && tar_ft.str[k] == '+')
                    || (or_opposite_strand && tar_ft.str[k] == '-')
                    || or_any_strand) {
                    n = (tar_ft.pos[k] - ref_ft.pos[j] - From) / Win;
                } else {
                    n = -1;
                }
                if (n >= 0) {
                    histo->val[n] += ref_ft.cnt[j] * tar_ft.cnt[k];
                }
#ifdef DEBUG
                printf(" histo_update feat idx:%d (2): bin:%d  hval:%ld\n",
                       j, n, histo->val[n]);
#endif
                k++;            /* examine further downstream positions within the window range */
            }
        } else {                /* Reverse Loop - Ref Feature Oriented on Neg Strand (Examin Downstream Tar pos) */
            while ((tar_ft.pos[k] < ref_ft.pos[j] - To)
                   && k <= (long) tar_nb) {
                k++;
            }
            while ((tar_ft.pos[k] <= ref_ft.pos[j] - From)
                   && k <= (long) tar_nb) {
                if ((or_same_strand && tar_ft.str[k] == '-')
                    || (or_opposite_strand && tar_ft.str[k] == '+')
                    || or_any_strand) {
                    n = (ref_ft.pos[j] - tar_ft.pos[k] - From) / Win;
                } else {
                    n = -1;
                }
                if (n >= 0) {
                    histo->val[n] += ref_ft.cnt[j] * tar_ft.cnt[k];
                }
#ifdef DEBUG
                printf(" histo_update feat idx:%d (2): bin:%d  hval:%ld\n",
                       j, n, histo->val[n]);
#endif
                k++;            /* examine further downstream positions within the window range */
            }
        }
    }
    return 0;
}

int process_sga(FILE *input, char *iFile, histo_p_t histo) {
    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 -k4,4 ";
        /*fprintf(stderr, "Check whether file %s is properly sorted\n",
                iFile);*/
        if (strcat(sort_cmd, iFile) == NULL) {
            fprintf(stderr, "strcat failed\n");
            return 1;
        }
        int sys_code = system(sort_cmd);
        if (sys_code != 0) {
            fprintf(stderr,"system command %s : return code %d\n", sort_cmd, sys_code);
            fprintf(stderr, "sorting command command failed\n");
            return 1;
        }
    }
    if ((ref_ft.pos = (int *) calloc(rf_mLen, sizeof(int))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }
    if ((ref_ft.cnt = (int *) calloc(rf_mLen, sizeof(int))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }
    if ((ref_ft.ptr = (int *) calloc(rf_mLen, sizeof(int))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }
    if ((ref_ft.str = (char *) calloc(rf_mLen, sizeof(char))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }
    if ((tar_ft.pos = (int *) calloc(tf_mLen, sizeof(int))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }
    if ((tar_ft.cnt = (int *) calloc(tf_mLen, sizeof(int))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }
    if ((tar_ft.str = (char *) calloc(tf_mLen, sizeof(char))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }
    if ((s = malloc(mLen * sizeof(char))) == NULL) {
        perror("process_sga: malloc");
        return 1;
    }

    tar_ft.pos[0] = From - 1;
#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("process_file: 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("process_sga: realloc");
                return 1;
            }
            if ((ref_ft.cnt =
                 (int *) realloc(ref_ft.cnt,
                                 rf_mLen * sizeof(int))) == NULL) {
                perror("process_sga: realloc");
                return 1;
            }
            if ((ref_ft.ptr =
                 (int *) realloc(ref_ft.ptr,
                                 rf_mLen * sizeof(int))) == NULL) {
                perror("process_sga: realloc");
                return 1;
            }
            if ((ref_ft.str =
                 (char *) realloc(ref_ft.str,
                                  rf_mLen * sizeof(char))) == NULL) {
                perror("process_sga: 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("process_sga: realloc");
                return 1;
            }
            if ((tar_ft.cnt =
                 (int *) realloc(tar_ft.cnt,
                                 tf_mLen * sizeof(int))) == NULL) {
                perror("process_sga: realloc");
                return 1;
            }
            if ((tar_ft.str =
                 (char *) realloc(tar_ft.str,
                                  tf_mLen * sizeof(char))) == NULL) {
                perror("process_sga: realloc");
                return 1;
            }
        }
        if (strcmp(seq_id, seq_id_prev) != 0) {
            tar_ft.pos[k + 1] = ref_ft.pos[j] + To + Win + 1;
            histo_update(histo, j, k);
            strcpy(seq_id_prev, seq_id);
            j = 0;
            k = 0;
            Len += last_pos;
        }
        if (ref_specs) {
            if (strcmp(feature, ref_ft.ft) == 0 && strand == ref_ft.strand) {
                j++;
                ref_ft.pos[j] = pos;
                ref_ft.str[j] = strand;
                ref_ft.ptr[j] = k;
                if (cnt > options.cutOff)
                    ref_ft.cnt[j] = options.cutOff;
                else
                    ref_ft.cnt[j] = cnt;
                Rtot += ref_ft.cnt[j];
            }
        } else {
            if (strcmp(feature, ref_ft.ft) == 0) {
                if (options.oriented && strand == '0') {
                    fprintf(stderr,
                            "Wrong reference feature strand (0). If oriented strand processing is set, reference features must be oriented (+|-).\n");
                    return 1;
                }
                j++;
                ref_ft.pos[j] = pos;
                ref_ft.str[j] = strand;
                ref_ft.ptr[j] = k;
                if (cnt > options.cutOff)
                    ref_ft.cnt[j] = options.cutOff;
                else
                    ref_ft.cnt[j] = cnt;
                Rtot += ref_ft.cnt[j];
            }
        }
        if (tar_specs) {
            if (strcmp(feature, tar_ft.ft) == 0 && strand == tar_ft.strand) {
                k++;
                tar_ft.pos[k] = pos;
                tar_ft.str[k] = strand;
                if (cnt > options.cutOff)
                    tar_ft.cnt[k] = options.cutOff;
                else
                    tar_ft.cnt[k] = cnt;
                Ttot += tar_ft.cnt[k];
            }
        } else {
            if (strcmp(feature, tar_ft.ft) == 0) {
                k++;
                tar_ft.pos[k] = pos;
                tar_ft.str[k] = strand;
                if (cnt > options.cutOff)
                    tar_ft.cnt[k] = options.cutOff;
                else
                    tar_ft.cnt[k] = cnt;
                Ttot += tar_ft.cnt[k];
            }
        }
        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] + To + Win + 1;
    histo_update(histo, j, k);
    histo_print(histo);
    free(ref_ft.pos);
    free(ref_ft.cnt);
    free(ref_ft.str);
    free(ref_ft.ptr);
    free(tar_ft.pos);
    free(tar_ft.cnt);
    free(tar_ft.str);
    return 0;
}

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

    while (1) {
        int c = getopt(argc, argv, "c:n:dhoA:B:b:e:w:");
        if (c == -1)
            break;
        switch (c) {
        case 'c':
            options.cutOff = atoi(optarg);
            break;
        case 'n':
            options.normFact = atoi(optarg);
            break;
        case 'd':
            options.debug = 1;
            break;
        case 'h':
            options.help = 1;
            break;
        case 'o':
            options.oriented = 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<window> [<] <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 feature counts (default is %d)\n"
                "  \t\t -o     Oriented strand processing\n"
                "  \t\t -n     Histogram Normalization (default is %d)\n"
                "\n\tFeature Correlation Tool for ChIP-seq data analysis.\n"
                "\tThe program reads a ChIP-seq data file (or from stdin [<]) in SGA format (<SGA file>),\n"
                "\tand generates histograms showing the positional relationship of two features,\n"
                "\ta reference feature (<feature A>) and a target feature (<feature B>) respectively.\n"
                "\tThe feature specification must have the following format:\n"
                "      \t<feature> = <name> [<+|->]\n\n"
                "\tthe strand specification (+|-) being optional.\n"
                "\tThe <feature> parameter is a name that corresponds to the second field of the SGA file.\n"
                "\tIf no feature is given then all input tags are processed.\n"
                "\tThe SGA input file MUST BE sorted by sequence name (or chromosome id), position,\n"
                "\tand 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\n"
                "\tgiven range: <from>-<to> that should be greater than 0.\n"
                "\tA value can be optionally specified as a cut-off for the feature counts.\n"
                "\tThe window width (-w) defines the histogram step size or bin. It must be\n"
                "\tan integer greater than 0.\n"
                "\tFor Histogram Normalization the following options are available:\n"
                "  \t\t   Off - Raw Counts (default)\n"
                "  \t\t   Show Count Density (-n 1)\n"
                "  \t\t   Show Global Normalization (-n 2)\n\n", argv[0],
                VERSION, options.cutOff, options.normFact);
        return 1;
    }
    /*printf("\noptind: %d;   argc: %d;   argv[0]: %s;  argv[optind]: %s; argv[optind + 1]: %s; argv[optind + 2]: %s\n", optind, argc, argv[0], argv[optind], argv[optind + 1], argv[optind + 2]);
     **/
    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);
        fprintf(stderr, " Normalization type : %d\n\n", options.normFact);
    }
    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';
    if (!options.oriented) {
        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(+|-)>]\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(+|-)>]\n",
                TarFeature);
        return 1;
    }
    if (ref_ft.strand == '\0') {
        /* fprintf(stderr, " Warning Ref Feature \"%s\" !\n Feature Format: = <feature desc> [<strand(+|-)>]\n", RefFeature); */
        ref_specs = 0;
    }
    if (tar_ft.strand == '\0') {
        /* fprintf(stderr, " Warning Tar Feature \"%s\" !\n Feature Format: = <feature desc> [<strand(+|-)>]\n", TarFeature); */
        tar_specs = 0;
    }
    if ((strcmp(tar_ft.ft, ref_ft.ft) == 0)) {  /* Check For Auto-Correlation */
        if (ref_ft.strand == '\0' && tar_ft.strand == '\0'
            && (!options.oriented)) {
            auto_cor = 1;
            if (options.debug)
                fprintf(stderr, " Autocorrelation ON\n");
        } else if ((ref_ft.strand == '+' && tar_ft.strand == '+')
                   || (ref_ft.strand == '-' && tar_ft.strand == '-')) {
            auto_cor = 1;
            if (options.debug)
                fprintf(stderr, " Autocorrelation ON\n");
        } else {
            auto_cor = 0;
            if (options.debug)
                fprintf(stderr, " Autocorrelation OFF\n");
        }
    }
    if (options.oriented && !auto_cor) {
        if (tar_ft.strand == '+') {
            or_same_strand = 1;
        } else if (tar_ft.strand == '-') {
            or_opposite_strand = 1;
        } else {
            or_any_strand = 1;
        }
        tar_specs = 0;
    }
    if (histo_init(&histo) != 0) {
        return 1;
    }
    if (process_sga(input, argv[optind++], &histo) != 0) {
        return 1;
    }
    if (input != stdin) {
        fclose(input);
    }
    return 0;
}
