/****************************************************************************** ** @source AJAX range functions ** ** @author Copyright (C) 1999 Alan Bleasby ** @version 1.0 ** @modified Aug 21 ajb First version ** @modified 7 Sept 1999 GWW - String range edit functions added ** @modified 5 Nov 1999 GWW - store text after pairs of numbers ** @@ ** ** This library is free software; you can redistribute it and/or ** modify it under the terms of the GNU Library General Public ** License as published by the Free Software Foundation; either ** version 2 of the License, or (at your option) any later version. ** ** This library 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 ** Library General Public License for more details. ** ** You should have received a copy of the GNU Library General Public ** License along with this library; if not, write to the ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, ** Boston, MA 02111-1307, USA. ******************************************************************************/ /* ==================================================================== */ /* ========================== include files =========================== */ /* ==================================================================== */ #include "ajax.h" #include #include #include #include /* ==================================================================== */ /* ========================== private data ============================ */ /* ==================================================================== */ /* ==================================================================== */ /* ======================== private functions ========================= */ /* ==================================================================== */ /* ==================================================================== */ /* ========================= constructors ============================= */ /* ==================================================================== */ /* @section Range Constructors ************************************************ ** ** All constructors return a new object by pointer. It is the responsibility ** of the user to first destroy any previous object. The target pointer ** does not need to be initialised to NULL, but it is good programming practice ** to do so anyway. ** ******************************************************************************/ /* @func ajRangeNewI ********************************************************** ** ** Default constructor for AJAX range objects. ** ** @param [r] n [ajuint] number of ranges ** ** @return [AjPRange] Pointer to a range object ** @category new [AjPRange] Default constructor for range objects ** @@ ******************************************************************************/ AjPRange ajRangeNewI(ajuint n) { AjPRange thys; AJNEW0(thys); thys->n = n; if(n>0) { thys->start = AJALLOC0(n*sizeof(ajuint)); thys->end = AJALLOC0(n*sizeof(ajuint)); thys->text = AJALLOC0(n*sizeof(AjPStr *)); } return thys; } /* @func ajRangeCopy ********************************************************** ** ** Copy constructor for AJAX range objects. ** ** @param [r] src [const AjPRange] Source range ** ** @return [AjPRange] Pointer to a range object ** @category new [AjPRange] Copy constructor for range objects ** @@ ******************************************************************************/ AjPRange ajRangeCopy(const AjPRange src) { AjPRange thys; ajuint i; ajuint n; AJNEW0(thys); n = src->n; thys->n = n; if(src->n > 0) { thys->start = AJALLOC0(n*sizeof(ajuint)); thys->end = AJALLOC0(n*sizeof(ajuint)); thys->text = AJALLOC0(n*sizeof(AjPStr *)); for(i=0; i < n; i++) { thys->start[i] = src->start[i]; thys->end[i] = src->end[i]; ajStrAssignS(&thys->text[i], src->text[i]); } } return thys; } /* @section Range Destructors ************************************************ ** ** Default destructor for AJAX range objects ** ******************************************************************************/ /* @func ajRangeDel *********************************************************** ** ** Default destructor for AJAX range objects. ** ** @param [d] thys [AjPRange *] range structure ** ** @return [void] ** @category delete [AjPRange] Default destructor for range objects ** @@ ******************************************************************************/ void ajRangeDel(AjPRange *thys) { ajuint i; if(!*thys) return; if((*thys)->n > 0) { AJFREE((*thys)->start); AJFREE((*thys)->end); for(i=0; i < (*thys)->n; i++) ajStrDel(&(*thys)->text[i]); } AJFREE((*thys)->text); AJFREE(*thys); return; } /* @section Range Functions ************************************************ ** ** Other functions for AJAX range objects ** ******************************************************************************/ /* @func ajRangeGet *********************************************************** ** ** Create a range object from a string ** ** @param [r] str [const AjPStr] range string ** ** @return [AjPRange] range object ** @category new [AjPRange] Create a range object from a string ** @@ ******************************************************************************/ AjPRange ajRangeGet(const AjPStr str) { return ajRangeGetLimits(str, 1, UINT_MAX, 0, 0); } /* @func ajRangeGetLimits ***************************************************** ** ** Create a range object from a string ** ** @param [r] str [const AjPStr] range string ** @param [r] imin [ajuint] Minimum value ** @param [r] imax [ajuint] Maximum value ** @param [r] minsize [ajuint] Minimum number of values ** @param [r] size [ajuint] Required number of values, zero for unlimited ** ** @return [AjPRange] range object ** @category new [AjPRange] Create a range object from a string ** @@ ******************************************************************************/ AjPRange ajRangeGetLimits(const AjPStr str, ajuint imin, ajuint imax, ajuint minsize, ajuint size) { AjPRange ret = NULL; AjPStr c1 = NULL; AjPStr c2 = NULL; AjPStr c3 = NULL; static AjPStr s =NULL; const char *cp; char *p; ajuint n; ajuint e; ajuint f; ajuint t; ajuint i; AjBool doneone = ajFalse; const char *nondigit="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" " \t\n\r!@#$%^&*()_-+=|\\~`{[}]:;\"'<,>.?/"; const char *digit="0123456789"; ajStrAssignS(&s, str); /* clean up the ranges string */ ajStrTrimWhite(&s); /* is this a file of ranges? (does it start with a '@' ?) */ if(*(ajStrGetPtr(s)) == '@') { /* knock off the '@' */ ajStrKeepRange(&s, 1, ajStrGetLen(s)); ret = ajRangeFileLimits(s, imin, imax, minsize, size); } else { /* get some copies of the string for parsing with strtok */ ajStrAssignS(&c1, s); ajStrAssignS(&c2, s); ajStrAssignS(&c3, s); cp = ajStrGetPtr(c1); p = ajSysFuncStrtok(cp, nondigit); n = 0; if(p) { /* * count the pairs of numbers so that we know the size of * arrays to create */ ++n; while((p=ajSysFuncStrtok(NULL, nondigit))) ++n; if(n%2) { ajWarn("Odd integer(s) in range specification [%d]",n); return NULL; } if(size) { if(n != size) { ajWarn("Range specification requires exactly %d pairs", size); return NULL; } } else if (n < minsize) { ajWarn("Range specification requires at least %d pairs", minsize); return NULL; } ret=ajRangeNewI((e=n>>1)); /* get the pairs of numbers and put them in the AjPRange object */ cp = ajStrGetPtr(c2); p = ajSysFuncStrtok(cp, nondigit); if(!sscanf(p,"%d",&f)) { ajWarn("Bad range value [%s]",p); ajRangeDel(&ret); return NULL; } p = ajSysFuncStrtok(NULL, nondigit); if(!sscanf(p,"%d",&t)) { ajWarn("Bad range value [%s]",p); ajRangeDel(&ret); return NULL; } if(f>t) { ajWarn("From range [%d] greater than To range [%d]",f,t); ajRangeDel(&ret); return NULL; } if (f < imin) { ajWarn("From range [%d] less than minimum [%d]",f,imin); ajRangeDel(&ret); return NULL; } if (t > imax) { ajWarn("To range [%d] greater than maximum [%d]",t,imax); ajRangeDel(&ret); return NULL; } ret->start[0]=f; ret->end[0]=t; for(i=1;it) { ajWarn("From range [%d] greater than To range [%d]",f,t); ajRangeDel(&ret); return NULL; } ret->start[i] = f; ret->end[i] = t; } /* now get any strings after the pairs of ranges */ cp = ajStrGetPtr(c3); if(!isdigit((ajint)*cp)) { doneone = ajTrue; p = ajSysFuncStrtok(cp, digit); } for(i=0; itext[i]), p); ajStrTrimWhite(&(ret->text[i])); } } } else { if(size) { ajWarn("Range specification requires exactly %d pairs", size); return NULL; } else if (0 < minsize) { ajWarn("Range specification requires at least %d pairs", minsize); return NULL; } ret=ajRangeNewI(0); } ajStrDel(&c1); ajStrDel(&c2); ajStrDel(&c3); } ajStrDel(&s); return ret; } /* @func ajRangeFile ********************************************************** ** ** Load a range object from a file ** ** The format of the range file is: ** Comment lines start with '#' in the first column. ** Comment lines and blank lines are ignored. ** The line may start with white-space. ** There are two positive numbers per line separated by white-space. ** The second number must be greater or equal to the first number. ** There is optional text after the two numbers. ** White-space before or after the text is removed. ** ** e.g.: ** ** # this is my set of ranges ** 12 23 ** 4 5 this is like 12-23, but smaller ** 67 10348 interesting region ** ** @param [r] name [const AjPStr] range file name ** ** @return [AjPRange] range object ** @category new [AjPRange] Create a range object from a file ** @@ ******************************************************************************/ AjPRange ajRangeFile(const AjPStr name) { return ajRangeFileLimits(name, 1, UINT_MAX, 0, 0); } /* @func ajRangeFileLimits **************************************************** ** ** Load a range object from a file ** ** The format of the range file is: ** Comment lines start with '#' in the first column. ** Comment lines and blank lines are ignored. ** The line may start with white-space. ** There are two positive numbers per line separated by white-space. ** The second number must be greater or equal to the first number. ** There is optional text after the two numbers. ** White-space before or after the text is removed. ** ** e.g.: ** ** # this is my set of ranges ** 12 23 ** 4 5 this is like 12-23, but smaller ** 67 10348 interesting region ** ** @param [r] name [const AjPStr] range file name ** @param [r] imin [ajuint] Minimum value ** @param [r] imax [ajuint] Maximum value ** @param [r] minsize [ajuint] Minimum number of values ** @param [r] size [ajuint] Required number of values, zero for unlimited ** ** @return [AjPRange] range object ** @category new [AjPRange] Create a range object from a file ** @@ ******************************************************************************/ AjPRange ajRangeFileLimits(const AjPStr name, ajuint imin, ajuint imax, ajuint minsize, ajuint size) { AjPRange ret = NULL; AjPFile infile; AjPStr line = NULL; char whiteSpace[] = " \t\n\r"; char notSpace[] = "\n\r"; AjPStrTok tokens; ajuint n = 0; /* ranges found so far */ ajuint k; ajuint numone; ajuint numtwo; AjPStr one; AjPStr two; AjPStr text; AjPList onelist; AjPList twolist; AjPList textlist; onelist = ajListstrNew(); twolist = ajListstrNew(); textlist = ajListstrNew(); if((infile = ajFileNewInNameS(name)) == NULL) return NULL; while(ajReadlineTrim(infile, &line)) { ajStrTrimWhite(&line); if(!ajStrFindC(line, "#")) continue; if(!ajStrGetLen(line)) continue; /* ** parse the numbers out of the line and store in temporary ** list (we may be reading data from stdin, so we can't read ** in once to count the number of ajRange elements, close ** file, open it again and read the data again to populate ** ajRange) */ tokens = ajStrTokenNewC(line, whiteSpace); one = ajStrNew(); ajStrTokenNextParse(&tokens, &one); ajListstrPushAppend(onelist, one); two = ajStrNew(); ajStrTokenNextParse(&tokens, &two); if(ajStrGetLen(two)) ajListstrPushAppend(twolist, two); else { ajWarn("Odd integer(s) in range specification:\n%S\n", line); return NULL; } /* get any remaining text and store in temporary list */ text = ajStrNew(); ajStrTokenNextParseC(&tokens, notSpace, &text); ajStrTrimWhite(&text); ajListstrPushAppend(textlist, text); ajStrTokenDel( &tokens); } /* now we know how many pairs of numbers to store, create ajRange object */ n = ajListstrGetLength(onelist); if(size) { if(n != size) { ajWarn("Range specification requires exactly %d pairs", size); return NULL; } } else if (n < minsize) { ajWarn("Range specification requires at least %d pairs", minsize); return NULL; } ret = ajRangeNewI(n); /* populate ajRange object from lists and check numbers are valid */ for(k = 0; k < n; k++) { ajListstrPop(onelist, &one); if(!ajStrToUint(one, &numone)) { ajWarn("Bad range value [%S]",one); ajRangeDel(&ret); return NULL; } ajListstrPop(twolist, &two); if(!ajStrToUint(two, &numtwo)) { ajWarn("Bad range value [%S]",two); ajRangeDel(&ret); return NULL; } ajStrDel(&one); ajStrDel(&two); if(numone > numtwo) { ajWarn("From range [%d] greater than To range [%d]", numone, numtwo); ajRangeDel(&ret); return NULL; } if (numone < imin) { ajWarn("From range [%d] less than minimum [%d]", numone,imin); ajRangeDel(&ret); return NULL; } if (numtwo > imax) { ajWarn("To range [%d] greater than maximum [%d]", numtwo,imax); ajRangeDel(&ret); return NULL; } ret->start[k] = numone; ret->end[k] = numtwo; /* do the text */ ajListstrPop(textlist, &text); ret->text[k] = text; } ajListstrFreeData(&onelist); ajListstrFreeData(&twolist); ajListstrFreeData(&textlist); return ret; } /* @func ajRangeNumber ******************************************************** ** ** Return the number of ranges in a range object ** ** @param [r] thys [const AjPRange] range object ** ** @return [ajuint] number of ranges ** @category use [AjPRange] Return the number of ranges in a range object ** @@ ******************************************************************************/ ajuint ajRangeNumber(const AjPRange thys) { return thys->n; } /* @func ajRangeValues ******************************************************** ** ** Return (as parameters) start and end values in a range ** ** @param [r] thys [const AjPRange] range object ** @param [r] element [ajuint] range element (0 to n-1) ** @param [w] start [ajuint *] start value ** @param [w] end [ajuint *] end value ** ** @return [AjBool] true if range exists ** @category use [AjPRange] Return (as parameters) start and end values ** in a range ** @@ ******************************************************************************/ AjBool ajRangeValues(const AjPRange thys, ajuint element, ajuint *start, ajuint *end) { if(element>=thys->n) return ajFalse; if(thys->start[element] < 1) return ajFalse; if(thys->end[element] < 1) return ajFalse; if(thys->start[element] > thys->end[element]) return ajFalse; *start = thys->start[element]; *end = thys->end[element]; return ajTrue; } /* @func ajRangeText ********************************************************** ** ** Return (as parameters) text value of a range ** The text value of a range is any non-digit after the pair of range numbers ** eg. in a pair of range '10-20 potential exon 50-60 repeat' ** the text values of the two ranges are: 'potential exon' and 'repeat' ** ** @param [r] thys [const AjPRange] range object ** @param [r] element [ajuint] range element (0 to n-1) ** @param [w] text [AjPStr *] text value ** ** @return [AjBool] true if range exists ** @category use [AjPRange] Return (as parameters) text value of a range ** @@ ******************************************************************************/ AjBool ajRangeText(const AjPRange thys, ajuint element, AjPStr * text) { if(element>=thys->n) return ajFalse; if(thys->text[element]) ajStrAssignS(text,thys->text[element]); else *text = ajStrNew(); return ajTrue; } /* @func ajRangeChange ******************************************************** ** ** Set the values of a start and end in a (preexisting) range element ** ** @param [w] thys [AjPRange] range object ** @param [r] element [ajuint] range element (0 to n-1) ** @param [r] start [ajuint] start value ** @param [r] end [ajuint] end value ** ** @return [AjBool] true if range exists ** @category modify [AjPRange] Set the values of a start and end in a ** range element ** @@ ******************************************************************************/ AjBool ajRangeChange(AjPRange thys, ajuint element, ajuint start, ajuint end) { if(element>=thys->n) return ajFalse; thys->start[element] = start; thys->end[element] = end; return ajTrue; } /* @func ajRangeBegin ********************************************************* ** ** Sets the range values relative to the Begin value. ** Used when a sequence has -sbegin= and -send= parameters set ** and we have extracted the specified subsequence. ** So if -sbeg 11 has been set and the range is 11-12 ** the resulting range is changed to 1-2 ** ** @param [u] thys [AjPRange] range object ** @param [r] begin [ajuint] begin parameter obtained from ajSeqGetBegin(seq) ** ** @return [AjBool] true if region values modified ** @category modify [AjPRange] Sets the range values relative to the ** Begin value ** @@ ******************************************************************************/ AjBool ajRangeBegin(AjPRange thys, ajuint begin) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; nr = ajRangeNumber(thys); for(i=0; i 1) result = ajTrue; ajRangeValues(thys, i, &st, &en); st -= begin-1; en -= begin-1; ajRangeChange(thys, i, st, en); } return result; } /* @func ajRangeSeqExtractList ************************************************ ** ** Extract the range from a sequence and place the resulting text in a ** list of strings. ** ** N.B. the resulting list will be regions of the input sequence listed ** in the order specified in the set of ranges. If these are not in ascending ** order, the resulting list of strings will not be in ascending order either. ** ** @param [r] thys [const AjPRange] range object ** @param [r] seq [const AjPSeq] sequence to extract from ** @param [w] outliststr [AjPList] resulting list of strings ** ** @return [AjBool] true if result is not the whole sequence ** @category use [AjPRange] PushApp substrings defined by range onto list ** @@ ******************************************************************************/ AjBool ajRangeSeqExtractList(const AjPRange thys, const AjPSeq seq, AjPList outliststr) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr str; nr = ajRangeNumber(thys); if(nr) { for(i=0; i posend) return 0; /* no overlap ~~~~ |--------| */ if(start >= pos && end <= posend) return 1; /* internal overlap |-~~~~~--| */ if(start < pos && end > posend) return 2; /* complete overlap ~~~~~|~~~~~~~~|~~ */ if(start < pos && end >= pos ) return 3; /* overlap at left ~~~~~|~~~-----| */ if(start >= pos && end > posend ) return 4; /* overlap at right |----~~~~|~~~ */ ajFatal("ajrangeoverlapsingle error"); return -1; } /* @func ajRangeOverlaps ****************************************************** ** ** Detect overlaps of a set of ranges to a region of a sequence ** @param [r] thys [const AjPRange] range object ** @param [r] pos [ajuint] postion in sequence of start of region of sequence ** @param [r] length [ajuint] length of region of sequence ** ** @return [ajuint] Number of ranges in range object with overlaps ** to the region ** @category use [AjPRange] Detect overlaps of a set of ranges to a seq region ** @@ ******************************************************************************/ ajuint ajRangeOverlaps(const AjPRange thys, ajuint pos, ajuint length) { ajuint nr; ajuint i; ajuint st; ajuint en; ajuint result = 0; nr = ajRangeNumber(thys); for(i=0; in==0) { ajDebug("ajRangeDefault n:%d begin:%u end:%u\n", thys->n, ajSeqGetBegin(s),ajSeqGetEnd(s)); return ajTrue; } ajDebug("ajRangeDefault n:%d start:%d end:%d begin:%u end:%u\n", thys->n, thys->start[0], thys->end[0], ajSeqGetBegin(s),ajSeqGetEnd(s)); if(thys->n==1 && thys->start[0]==ajSeqGetBegin(s) && thys->end[0]==ajSeqGetEnd(s)) return ajTrue; return ajFalse; }