<commit_before>/*-
 * The MIT License
 *
 * Copyright (c) 2011 Seoul National University.
 *               2016 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Contact: <NAME> (<EMAIL>)
 */

#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pairix.h"

static PyObject *PairixError;
static PyObject *PairixWarning;

typedef struct {
    PyObject_HEAD
    pairix_t *tb;
    char *fn;
    PyObject *blocknames;
    int nblocks;
} PairixObject;

typedef struct {
    PyObject_HEAD
    PairixObject *tbobj;
    sequential_iter_t *iter;
} PairixIteratorObject;


static PyTypeObject Pairix_Type, PairixIterator_Type;

/* --- PairixIterator --------------------------------------------------- */

static PyObject *
pairixiter_create(PairixObject *parentidx, sequential_iter_t *iter)
{
    PairixIteratorObject *self;

    self = PyObject_New(PairixIteratorObject, &PairixIterator_Type);
    if (self == NULL)
        return NULL;

    Py_INCREF(parentidx);
    self->tbobj = parentidx;
    self->iter = iter;

    return (PyObject *)self;
}

static void
pairixiter_dealloc(PairixIteratorObject *self)
{
    destroy_sequential_iter(self->iter);
    Py_DECREF(self->tbobj);
    PyObject_Del(self);
}

static PyObject *
pairixiter_iter(PyObject *self)
{
    Py_INCREF(self);
    return self;
}

#if PY_MAJOR_VERSION < 3
# define PYOBJECT_FROM_STRING_AND_SIZE PyString_FromStringAndSize
#else
# define PYOBJECT_FROM_STRING_AND_SIZE PyUnicode_FromStringAndSize
#endif

#if PY_MAJOR_VERSION < 3
# define PYOBJECT_FROM_STRING PyString_FromString
#else
# define PYOBJECT_FROM_STRING PyUnicode_FromString
#endif

static PyObject *
pairixiter_iternext(PairixIteratorObject *self)
{
    const char *chunk;
    int len, i;
    char delimiter = ti_get_delimiter(self->tbobj->tb->idx);

    chunk = sequential_ti_read(self->iter, &len);

    if (chunk != NULL) {
        PyObject *ret, *column;
        Py_ssize_t colidx;
        const char *ptr, *begin;

        ret = PyList_New(0);
        if (ret == NULL)
            return NULL;

        colidx = 0;
        ptr = begin = chunk;
        for (i = len; i > 0; i--, ptr++)
            if (*ptr == delimiter) {
                column = PYOBJECT_FROM_STRING_AND_SIZE(begin,
                                                       (Py_ssize_t)(ptr - begin));
                if (column == NULL || PyList_Append(ret, column) == -1) {
                    Py_DECREF(ret);
                    return NULL;
                }

                Py_DECREF(column);
                begin = ptr + 1;
                colidx++;
            }

        column = PYOBJECT_FROM_STRING_AND_SIZE(begin, (Py_ssize_t)(ptr - begin));
        if (column == NULL || PyList_Append(ret, column) == -1) {
            Py_DECREF(ret);
            return NULL;
        }
        Py_DECREF(column);

        return ret;
    }
    else
        return NULL;
}

static PyMethodDef pairixiter_methods[] = {
    {NULL, NULL} /* sentinel */
};

static PyTypeObject PairixIterator_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "pypairix.iter",      /*tp_name*/
    sizeof(PairixIteratorObject), /*tp_basicsize*/
    0,                          /*tp_itemsize*/
    /* methods */
    (destructor)pairixiter_dealloc,  /*tp_dealloc*/
    0,                          /*tp_print*/
    0,                          /*tp_getattr*/
    0,                          /*tp_setattr*/
    0,                          /*tp_compare*/
    0,                          /*tp_repr*/
    0,                          /*tp_as_number*/
    0,                          /*tp_as_sequence*/
    0,                          /*tp_as_mapping*/
    0,                          /*tp_hash*/
    0,                          /*tp_call*/
    0,                          /*tp_str*/
    0,                          /*tp_getattro*/
    0,                          /*tp_setattro*/
    0,                          /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,         /*tp_flags*/
    0,                          /*tp_doc*/
    0,                          /*tp_traverse*/
    0,                          /*tp_clear*/
    0,                          /*tp_richcompare*/
    0,                          /*tp_weaklistoffset*/
    pairixiter_iter,             /*tp_iter*/
    (iternextfunc)pairixiter_iternext, /*tp_iternext*/
    pairixiter_methods,          /*tp_methods*/
    0,                          /*tp_members*/
    0,                          /*tp_getset*/
    0,                          /*tp_base*/
    0,                          /*tp_dict*/
    0,                          /*tp_descr_get*/
    0,                          /*tp_descr_set*/
    0,                          /*tp_dictoffset*/
    0,                          /*tp_init*/
    0,                          /*tp_alloc*/
    0,                          /*tp_new*/
    0,                          /*tp_free*/
    0,                          /*tp_is_gc*/
};

/* ------------------------build_index------------------------- */

int build_index(char *inputfilename, char *preset, int sc, int bc, int ec, int sc2, int bc2, int ec2, char *delimiter, char *meta_char, int line_skip, int force, int zero){

  if(force==0){
    char *fnidx = calloc(strlen(inputfilename) + 5, 1);
    strcat(strcpy(fnidx, inputfilename), ".px2");
    struct stat stat_px2, stat_input;
     if (stat(fnidx, &stat_px2) == 0){  // file exists.
         // Before complaining, check if the input file isn't newer. This is a common source of errors,
         // people tend not to notice that pairix failed
         stat(inputfilename, &stat_input);
         if ( stat_input.st_mtime <= stat_px2.st_mtime ) { free(fnidx); return(-4);}
     }
     free(fnidx);
  }
  if ( bgzf_is_bgzf(inputfilename)!=1 ) return(-3);
  else {
    ti_conf_t conf;
    if (strcmp(preset, "") == 0 && sc == 0 && bc == 0){
      int l = strlen(inputfilename);
      int strcasecmp(const char *s1, const char *s2);
      if (l>=7 && strcasecmp(inputfilename+l-7, ".gff.gz") == 0) conf = ti_conf_gff;
      else if (l>=7 && strcasecmp(inputfilename+l-7, ".bed.gz") == 0) conf = ti_conf_bed;
      else if (l>=7 && strcasecmp(inputfilename+l-7, ".sam.gz") == 0) conf = ti_conf_sam;
      else if (l>=7 && strcasecmp(inputfilename+l-7, ".vcf.gz") == 0) conf = ti_conf_vcf;
      else if (l>=10 && strcasecmp(inputfilename+l-10, ".psltbl.gz") == 0) conf = ti_conf_psltbl;
      else if (l>=9 && strcasecmp(inputfilename+l-9, ".pairs.gz") == 0) conf = ti_conf_pairs;
      else return(-5); // file extension not recognized and no preset specified
    }
    else if (strcmp(preset, "") == 0 && sc != 0 && bc != 0){
      conf.sc = sc;
      conf.bc = bc;
      conf.ec = ec;
      conf.sc2 = sc2;
      conf.bc2 = bc2;
      conf.ec2 = ec2;
      conf.delimiter = (delimiter)[0];
      conf.meta_char = (int)((meta_char)[0]);
      conf.line_skip = line_skip;
    }
    else if (strcmp(preset, "gff") == 0) conf = ti_conf_gff;
    else if (strcmp(preset, "bed") == 0) conf = ti_conf_bed;
    else if (strcmp(preset, "sam") == 0) conf = ti_conf_sam;
    else if (strcmp(preset, "vcf") == 0 || strcmp(preset, "vcf4") == 0) conf = ti_conf_vcf;
    else if (strcmp(preset, "psltbl") == 0) conf = ti_conf_psltbl;
    else if (strcmp(preset, "pairs") == 0) conf = ti_conf_pairs;
    else if (strcmp(preset, "merged_nodups") == 0) conf = ti_conf_merged_nodups;
    else if (strcmp(preset, "old_merged_nodups") == 0) conf = ti_conf_old_merged_nodups;
    else return(-2);  // wrong preset

    if(zero) conf.preset |= TI_FLAG_UCSC; // zero-based indexing

    return ti_index_build(inputfilename, &conf);  // -1 if failed
  }
}

static char indexer_docstring[] = "build_index(filename)\n\nBuild pairix index for a pairs file.\n\nRequired args: <filename (str)>. Optional args: <preset (str)> one of the following strings: 'gff', 'bed', 'sam', 'vcf', 'psltbl' (1D-indexing) or 'pairs', 'merged_nodups', 'old_merged_nodups' (2D-indexing). If preset is '', at least some of the custom parameters must be given instead (sc, bc, ec, sc2, bc2, ec2, delimiter, comment_char, line_skip). (default '') <sc (int)> first sequence (chromosome) column index (1-based). Zero (0) means not specified. If preset is given, preset overrides sc. If preset is not given, this one is required. (default 0) <bc (int)> first start position column index (1-based). Zero (0) means not specified. If preset is given, preset overrides bc. If preset is not given, this one is required. (default 0) <ec (int)> first end position column index (1-based). Zero (0) means not specified. If preset is given, preset overrides ec. (default 0) <sc2 (int)> second sequence (chromosome) column index (1-based). Zero (0) means not specified. If preset is given, preset overrides sc2. If sc, bc are specified but not sc2 and bc2, it is 1D-indexed. (default 0) <bc2 (int)> second start position column index (1-based). Zero (0) means not specified. If preset is given, preset overrides bc2. (default 0) <ec2 (int)> second end position column index (1-based). Zero (0) means not specified. If preset is given, preset overrides ec2. (default 0) <delimiter (str)> delimiter (e.g. '\\t' or ' ') (default '\\t'). If preset is given, preset overrides delimiter. <meta_char (str)> comment character. Lines beginning with this character are skipped when creating an index. If preset is given, preset overrides comment_char (default '#') <line_skip (int)> number of lines to skip in the beginning. (default 0) <force (int)> If 1, overwrite existing index file. If 0, do not overwrite unless the index file is older than the bgzipped file. (default 0). <zero (int)> If 1, create a zero-based index. (default one-based)\n";

static PyObject *indexer_build_index(PyObject *self, PyObject *args, PyObject *kwargs)
{
    // args: char *inputfilename, char *preset, int sc, int bc, int ec, int sc2, int bc2, int ec2, char *delimiter, char *meta_char, int line_skip, int force, int zero
    char *inputfilename, *preset, *delimiter, *meta_char;
    int sc, bc, ec, sc2, bc2, ec2, line_skip, force, zero, result;
    // default arg values<commit_msg>resolved conflict<commit_after>/*-
 * The MIT License
 *
 * Copyright (c) 2011 Seoul National University.
 *               2016 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Contact: <NAME> (<EMAIL>)
 */

#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pairix.h"

static PyObject *PairixError;
static PyObject *PairixWarning;

typedef struct {
    PyObject_HEAD
    pairix_t *tb;
    char *fn;
    PyObject *blocknames;
    int nblocks;
} PairixObject;

typedef struct {
    PyObject_HEAD
    PairixObject *tbobj;
    sequential_iter_t *iter;
} PairixIteratorObject;


static PyTypeObject Pairix_Type, PairixIterator_Type;

/* --- PairixIterator --------------------------------------------------- */

static PyObject *
pairixiter_create(PairixObject *parentidx, sequential_iter_t *iter)
{
    PairixIteratorObject *self;

    self = PyObject_New(PairixIteratorObject, &PairixIterator_Type);
    if (self == NULL)
        return NULL;

    Py_INCREF(parentidx);
    self->tbobj = parentidx;
    self->iter = iter;

    return (PyObject *)self;
}

static void
pairixiter_dealloc(PairixIteratorObject *self)
{
    destroy_sequential_iter(self->iter);
    Py_DECREF(self->tbobj);
    PyObject_Del(self);
}

static PyObject *
pairixiter_iter(PyObject *self)
{
    Py_INCREF(self);
    return self;
}

#if PY_MAJOR_VERSION < 3
# define PYOBJECT_FROM_STRING_AND_SIZE PyString_FromStringAndSize
#else
# define PYOBJECT_FROM_STRING_AND_SIZE PyUnicode_FromStringAndSize
#endif

#if PY_MAJOR_VERSION < 3
# define PYOBJECT_FROM_STRING PyString_FromString
#else
# define PYOBJECT_FROM_STRING PyUnicode_FromString
#endif

static PyObject *
pairixiter_iternext(PairixIteratorObject *self)
{
    const char *chunk;
    int len, i;
    char delimiter = ti_get_delimiter(self->tbobj->tb->idx);

    chunk = sequential_ti_read(self->iter, &len);

    if (chunk != NULL) {
        PyObject *ret, *column;
        Py_ssize_t colidx;
        const char *ptr, *begin;

        ret = PyList_New(0);
        if (ret == NULL)
            return NULL;

        colidx = 0;
        ptr = begin = chunk;
        for (i = len; i > 0; i--, ptr++)
            if (*ptr == delimiter) {
                column = PYOBJECT_FROM_STRING_AND_SIZE(begin,
                                                       (Py_ssize_t)(ptr - begin));
                if (column == NULL || PyList_Append(ret, column) == -1) {
                    Py_DECREF(ret);
                    return NULL;
                }

                Py_DECREF(column);
                begin = ptr + 1;
                colidx++;
            }

        column = PYOBJECT_FROM_STRING_AND_SIZE(begin, (Py_ssize_t)(ptr - begin));
        if (column == NULL || PyList_Append(ret, column) == -1) {
            Py_DECREF(ret);
            return NULL;
        }
        Py_DECREF(column);

        return ret;
    }
    else
        return NULL;
}

static PyMethodDef pairixiter_methods[] = {
    {NULL, NULL} /* sentinel */
};

static PyTypeObject PairixIterator_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "pypairix.iter",      /*tp_name*/
    sizeof(PairixIteratorObject), /*tp_basicsize*/
    0,                          /*tp_itemsize*/
    /* methods */
    (destructor)pairixiter_dealloc,  /*tp_dealloc*/
    0,                          /*tp_print*/
    0,                          /*tp_getattr*/
    0,                          /*tp_setattr*/
    0,                          /*tp_compare*/
    0,                          /*tp_repr*/
    0,                          /*tp_as_number*/
    0,                          /*tp_as_sequence*/
    0,                          /*tp_as_mapping*/
    0,                          /*tp_hash*/
    0,                          /*tp_call*/
    0,                          /*tp_str*/
    0,                          /*tp_getattro*/
    0,                          /*tp_setattro*/
    0,                          /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,         /*tp_flags*/
    0,                          /*tp_doc*/
    0,                          /*tp_traverse*/
    0,                          /*tp_clear*/
    0,                          /*tp_richcompare*/
    0,                          /*tp_weaklistoffset*/
    pairixiter_iter,             /*tp_iter*/
    (iternextfunc)pairixiter_iternext, /*tp_iternext*/
    pairixiter_methods,          /*tp_methods*/
    0,                          /*tp_members*/
    0,                          /*tp_getset*/
    0,                          /*tp_base*/
    0,                          /*tp_dict*/
    0,                          /*tp_descr_get*/
    0,                          /*tp_descr_set*/
    0,                          /*tp_dictoffset*/
    0,                          /*tp_init*/
    0,                          /*tp_alloc*/
    0,                          /*tp_new*/
    0,                          /*tp_free*/
    0,                          /*tp_is_gc*/
};

/* ------------------------build_index------------------------- */

int build_index(char *inputfilename, char *preset, int sc, int bc, int ec, int sc2, int bc2, int ec2, char *delimiter, char *meta_char, int line_skip, int force, int zero){

  if(force==0){
    char *fnidx = calloc(strlen(inputfilename) + 5, 1);
    strcat(strcpy(fnidx, inputfilename), ".px2");
    struct stat stat_px2, stat_input;
     if (stat(fnidx, &stat_px2) == 0){  // file exists.
         // Before complaining, check if the input file isn't newer. This is a common source of errors,
         // people tend not to notice that pairix failed
         stat(inputfilename, &stat_input);
         if ( stat_input.st_mtime <= stat_px2.st_mtime ) { free(fnidx); return(-4);}
     }
     free(fnidx);
  }
  if ( bgzf_is_bgzf(inputfilename)!=1 ) return(-3);
  else {
    ti_conf_t conf;
    if (strcmp(preset, "") == 0 && sc == 0 && bc == 0){
      int l = strlen(inputfilename);
      int strcasecmp(const char *s1, const char *s2);
      if (l>=7 && strcasecmp(inputfilename+l-7, ".gff.gz") == 0) conf = ti_conf_gff;
      else if (l>=7 && strcasecmp(inputfilename+l-7, ".bed.gz") == 0) conf = ti_conf_bed;
      else if (l>=7 && strcasecmp(inputfilename+l-7, ".sam.gz") == 0) conf = ti_conf_sam;
      else if (l>=7 && strcasecmp(inputfilename+l-7, ".vcf.gz") == 0) conf = ti_conf_vcf;
      else if (l>=10 && strcasecmp(inputfilename+l-10, ".psltbl.gz") == 0) conf = ti_conf_psltbl;
      else if (l>=9 && strcasecmp(inputfilename+l-9, ".pairs.gz") == 0) conf = ti_conf_pairs;
      else return(-5); // file extension not recognized and no preset specified
    }
    else if (strcmp(preset, "") == 0 && sc != 0 && bc != 0){
      conf.sc = sc;
      conf.bc = bc;
      conf.ec = ec;
      conf.sc2 = sc2;
      conf.bc2 = bc2;
      conf.ec2 = ec2;
      conf.delimiter = (delimiter)[0];
      conf.meta_char = (int)((meta_char)[0]);
      conf.line_skip = line_skip;
    }
    else if (strcmp(preset, "gff") == 0) conf = ti_conf_gff;
    else if (strcmp(preset, "bed") == 0) conf = ti_conf_bed;
    else if (strcmp(preset, "sam") == 0) conf = ti_conf_sam;
    else if (strcmp(preset, "vcf") == 0 || strcmp(preset, "vcf4") == 0) conf = ti_conf_vcf;
    else if (strcmp(preset, "psltbl") == 0) conf = ti_conf_psltbl;
    else if (strcmp(preset, "pairs") == 0) conf = ti_conf_pairs;
    else if (strcmp(preset, "merged_nodups") == 0) conf = ti_conf_merged_nodups;
    else if (strcmp(preset, "old_merged_nodups") == 0) conf = ti_conf_old_merged_nodups;
    else return(-2);  // wrong preset

    if(zero) conf.preset |= TI_FLAG_UCSC; // zero-based indexing

    return ti_index_build(inputfilename, &conf);  // -1 if failed
  }
}

static char indexer_docstring[] = (
"build_index(filename, preset, sc, bc, ec, sc2, bc2, ec2, delimiter, meta_char, force)\n\n"
"Build pairix index for a pairs file.\n\n"
"Parameters\n"
"----------\n"
"filename : str\n"
"    Name of bgzipped file to index.\n"
"preset : str, optional (default : '')\n"
"    One of the following strings: 'gff', 'bed', 'sam', 'vcf', 'psltbl' \n"
"   (1D-indexing) or 'pairs', 'merged_nodups', 'old_merged_nodups' (2D-indexing).\n"
"sc, bc, ec : int, optional\n"
"    First sequence, start, and end column indexes (1-based). Zero (0) means \n"
"    not specified. If preset is given, preset overrides these. If preset is \n"
"    not given, sc2 and bc2 are required.\n"
"sc2, bc2, ec2 : int, optional\n"
"    Second sequence, start, and end column indexes (1-based). Zero (0) \n"
"    means not specified. If preset is given, preset overrides these. If sc, \n"
"    bc are specified but not sc2 and bc2, the file will be 1D-indexed.\n"
"delimiter : str, optional (default: '\\t')\n"
"    Data delimiter, e.g. '\\t' or ' '. If preset is given, preset overrides \n"
"    delimiter.\n"
"meta_char : str, optional (default: '#')\n"
"    Comment character. Lines beginning with this character are skipped when \n"
"    creating an index. If preset is given, preset overrides comment_char.\n"
"line_skip : int, optional (default: 0)\n"
"    Number of lines to skip in the beginning\n"
"force : int, optional (default: 0)\n"
"    If 1, overwrite existing index file. If 0, do not overwrite unless the \n"
"    index file is older than the bgzipped file.\n"
"zero : int, optional (default 0)\n"
"    If 1, create a zero-based index. (default one-based)\n");
   

static PyObject *indexer_build_index(PyObject *self, PyObject *args, PyObject *kwargs)
{
    // args: char *inputfilename, char *preset, int sc, int bc, int ec, int sc2, int bc2, int ec2, char *delimiter, char *meta_char, int line_skip, int force, int zero
    char *inputfilename, *preset, *delimiter, *meta_char;
    int sc, bc, ec, sc2, bc2, ec2, line_skip, force, zero, result;
    // default arg values
{'fraction_non_alphanumeric': 0.12816752967053108, 'fraction_numerical': 0.013563671355908904, 'mean_word_length': 2.191109811375932, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4509682', 'n_tokens_mistral': 7077, 'n_tokens_neox': 6893, 'n_words': 2062}