1 /* massXpert - the true massist's program. 2 -------------------------------------- 3 Copyright(C) 2006,2007 Filippo Rusconi 4 5 http://www.massxpert.org/massXpert 6 7 This file is part of the massXpert project. 8 9 The massxpert project is the successor to the "GNU polyxmass" 10 project that is an official GNU project package(see 11 www.gnu.org). The massXpert project is not endorsed by the GNU 12 project, although it is released ---in its entirety--- under the 13 GNU General Public License. A huge part of the code in massXpert 14 is actually a C++ rewrite of code in GNU polyxmass. As such 15 massXpert was started at the Centre National de la Recherche 16 Scientifique(FRANCE), that granted me the formal authorization to 17 publish it under this Free Software License. 18 19 This software is free software; you can redistribute it and/or 20 modify it under the terms of the GNU General Public 21 License version 3, as published by the Free Software Foundation. 22 23 24 This software is distributed in the hope that it will be useful, 25 but WITHOUT ANY WARRANTY; without even the implied warranty of 26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 27 General Public License for more details. 28 29 You should have received a copy of the GNU General Public License 30 along with this software; if not, write to the 31 32 Free Software Foundation, Inc., 33 34 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 35 */ 36 37 38 /////////////////////// Local includes 39 #include "crossLink.hpp" 40 #include "polymer.hpp" 41 42 43 namespace massXpert 44 { 45 CrossLink(const PolChemDef * polChemDef,Polymer * polymer,const QString & name,const QString & formula,const QString & comment)46 CrossLink::CrossLink(const PolChemDef *polChemDef, 47 Polymer *polymer, 48 const QString &name, 49 const QString &formula, 50 const QString &comment) 51 : CrossLinker(polChemDef, name, formula), mp_polymer(polymer), 52 m_comment(comment) 53 { 54 55 } 56 57 CrossLink(const CrossLinker & crossLinker,Polymer * polymer,const QString & comment)58 CrossLink::CrossLink(const CrossLinker &crossLinker, 59 Polymer *polymer, 60 const QString &comment) 61 : CrossLinker(crossLinker), 62 mp_polymer(polymer), m_comment(comment) 63 { 64 65 } 66 67 CrossLink(const CrossLink & crossLink)68 CrossLink::CrossLink(const CrossLink &crossLink) 69 : CrossLinker(crossLink), mp_polymer(crossLink.mp_polymer), 70 m_comment(crossLink.m_comment) 71 { 72 73 74 } 75 76 ~CrossLink()77 CrossLink::~CrossLink() 78 { 79 } 80 81 82 bool setMonomerAt(Monomer * monomer,int index)83 CrossLink::setMonomerAt(Monomer *monomer, int index) 84 { 85 Q_ASSERT(monomer); 86 Q_ASSERT(index >= 0 && index < m_monomerList.size()); 87 88 m_monomerList.replace(index, monomer); 89 90 return true; 91 } 92 93 94 bool appendMonomer(const Monomer * monomer)95 CrossLink::appendMonomer(const Monomer *monomer) 96 { 97 Q_ASSERT(monomer); 98 99 m_monomerList.append(monomer); 100 101 return true; 102 } 103 104 105 const Monomer * monomerAt(int index)106 CrossLink::monomerAt(int index) 107 { 108 Q_ASSERT(index >= 0 && index < m_monomerList.size()); 109 110 return m_monomerList.at(index); 111 } 112 113 114 bool removeMonomerAt(int index)115 CrossLink::removeMonomerAt(int index) 116 { 117 Q_ASSERT(index < m_monomerList.size()); 118 119 m_monomerList.removeAt(index); 120 121 return true; 122 } 123 124 125 void setPolymer(Polymer * polymer)126 CrossLink::setPolymer(Polymer *polymer) 127 { 128 Q_ASSERT(polymer); 129 130 mp_polymer = polymer; 131 } 132 133 134 Polymer * polymer()135 CrossLink::polymer() 136 { 137 return mp_polymer; 138 } 139 140 141 void setComment(const QString & comment)142 CrossLink::setComment(const QString &comment) 143 { 144 m_comment = comment; 145 } 146 147 148 const QString & comment() const149 CrossLink::comment() const 150 { 151 return m_comment; 152 } 153 154 155 bool operator ==(const CrossLink & other) const156 CrossLink::operator ==(const CrossLink &other) const 157 { 158 int tests = 0; 159 160 tests += CrossLinker::operator ==(other); 161 tests += mp_polymer.data() == other.mp_polymer.data(); 162 163 if (m_monomerList.size() != other.m_monomerList.size()) 164 return false; 165 166 for (int iter = 0; iter < m_monomerList.size(); ++iter) 167 { 168 if(m_monomerList.at(iter) != other.m_monomerList.at(iter)) 169 return false; 170 } 171 172 tests += m_comment == other.m_comment; 173 174 if (tests < 3) 175 return false; 176 else 177 return true; 178 } 179 180 181 int populateMonomerList(QString text)182 CrossLink::populateMonomerList(QString text) 183 { 184 // We do not own the monomers pointed to by the pointers in 185 // m_monomerList. 186 187 while(m_monomerList.size()) 188 m_monomerList.removeFirst(); 189 190 QStringList stringList = text.split(';', 191 QString::SkipEmptyParts, 192 Qt::CaseSensitive ); 193 194 // There must be at least 2 monomers to make a crossLink ! 195 196 if (stringList.size() < 2) 197 return -1; 198 199 for (int iter = 0; iter < stringList.size(); ++iter) 200 { 201 QString value = stringList.at(iter); 202 203 bool ok = false; 204 205 int index = value.toInt(&ok); 206 207 if(!index && !ok) 208 return -1; 209 210 Q_ASSERT(index >=0 && index < mp_polymer->size()); 211 212 m_monomerList.append(mp_polymer->at(index)); 213 } 214 215 return m_monomerList.size(); 216 } 217 218 219 220 QList<const Monomer *> * monomerList()221 CrossLink::monomerList() 222 { 223 return &m_monomerList; 224 } 225 226 227 // Returns the number of monomers involved in the cross-link. 228 int monomerIndexList(QList<int> * list)229 CrossLink::monomerIndexList(QList<int> *list) 230 { 231 if(!list) 232 qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__); 233 234 int count = 0; 235 236 QList<int> localIndexList; 237 238 for (int iter = 0; iter < m_monomerList.size(); ++iter) 239 { 240 const Monomer *monomer = m_monomerList.at(iter); 241 int index = mp_polymer->monomerIndex(monomer); 242 243 // qDebug() << __FILE__ << __LINE__ 244 // << "Other monomer index:" << index; 245 246 localIndexList.append(index); 247 248 ++count; 249 } 250 251 qSort(localIndexList.begin(), localIndexList.end()); 252 253 // Now that we have the minIndex and the maxIndex of the region 254 // involved by the cross-link, we can fill-in the integer list 255 // with all the values contained between these two borders.i 256 257 for(int iter = localIndexList.first(); 258 iter <= localIndexList.last(); ++iter) 259 { 260 // If we had a cross-link between monomers [4] and [10] of the 261 // polymer, then the indices appended to the list would be 4, 262 // 5, 6, 7, 8, 9 and 10. 263 list->append(iter); 264 } 265 266 return count; 267 } 268 269 270 QString monomerIndexText()271 CrossLink::monomerIndexText() 272 { 273 QString text = ";"; 274 275 for (int iter = 0; iter < m_monomerList.size(); ++iter) 276 { 277 const Monomer *monomer = m_monomerList.at(iter); 278 int index = mp_polymer->monomerIndex(monomer); 279 280 text += QString("%1;").arg(index); 281 } 282 283 return text; 284 } 285 286 287 QString monomerPosText()288 CrossLink::monomerPosText() 289 { 290 QString text = ";"; 291 292 for (int iter = 0; iter < m_monomerList.size(); ++iter) 293 { 294 const Monomer *monomer = m_monomerList.at(iter); 295 int index = mp_polymer->monomerIndex(monomer); 296 297 text += QString("%1;").arg(index + 1); 298 } 299 300 return text; 301 } 302 303 304 int involvesMonomer(const Monomer * monomer) const305 CrossLink::involvesMonomer(const Monomer *monomer) const 306 { 307 Q_ASSERT(monomer); 308 309 for (int iter = 0; iter < m_monomerList.size(); ++iter) 310 { 311 if(m_monomerList.at(iter) == monomer ) 312 { 313 return iter; 314 } 315 } 316 317 return -1; 318 } 319 320 321 const Monomer * firstMonomer() const322 CrossLink::firstMonomer() const 323 { 324 if (!m_monomerList.size()) 325 return 0; 326 327 return m_monomerList.first(); 328 } 329 330 int encompassedBy(int start,int end,int * in,int * out)331 CrossLink::encompassedBy(int start, int end, int *in, int *out) 332 { 333 // Iterate in the list of monomers, and for each monomer check if 334 // their index is contained in the stretch. 335 336 int countIn = 0; 337 int countOut = 0; 338 339 int localStart =(start < end ? start : end); 340 int localEnd =(end >= start ? end : start); 341 342 for (int iter = 0; iter < m_monomerList.size(); ++iter) 343 { 344 const Monomer *monomer = m_monomerList.at(iter); 345 346 int index = mp_polymer->monomerIndex(monomer); 347 348 if(index >= localStart && index <= localEnd) 349 ++countIn; 350 else 351 ++countOut; 352 } 353 354 Q_ASSERT((countIn + countOut) == m_monomerList.size()); 355 356 if (in) 357 *in = countIn; 358 if (out) 359 *out = countOut; 360 361 if (countOut && countIn) 362 return MXP_CROSS_LINK_ENCOMPASSED_PARTIAL; 363 364 if (countOut) 365 return MXP_CROSS_LINK_ENCOMPASSED_NO; 366 367 if (countIn) 368 return MXP_CROSS_LINK_ENCOMPASSED_FULL; 369 370 return MXP_CROSS_LINK_ENCOMPASSED_NO; 371 } 372 373 374 int encompassedBy(const CoordinateList & coordinateList,int * in,int * out)375 CrossLink::encompassedBy(const CoordinateList &coordinateList, 376 int *in, int *out) 377 { 378 // Iterate in the list of monomers involved in *this crossLink, 379 // and for each monomer check if their index is contained in any 380 // of the Coordinates [start--end] of the coordinateList passed as 381 // parameter. 382 383 int countIn = 0; 384 int countOut = 0; 385 386 for (int iter = 0; iter < m_monomerList.size(); ++iter) 387 { 388 const Monomer *monomer = m_monomerList.at(iter); 389 390 int index = mp_polymer->monomerIndex(monomer); 391 392 // qDebug() << __FILE__ << __LINE__ 393 // << " monomer at index:" << index 394 // << "tested:"; 395 396 // Is the index encompassed by any of the Coordinates of the 397 // CoordinateList ? 398 399 bool countedIn = false; 400 401 for(int jter = 0; jter < coordinateList.size(); ++jter) 402 { 403 Coordinates *coordinates = coordinateList.at(jter); 404 405 if (index >= coordinates->start() && 406 index <= coordinates->end()) 407 { 408 ++countIn; 409 countedIn = true; 410 411 // qDebug() << __FILE__ << __LINE__ 412 // << " encompassedBy YES by coordinates" 413 // << "coordinates->start()" << coordinates->start() 414 // << "coordinates->end()" << coordinates->end(); 415 416 break; 417 } 418 else 419 { 420 // qDebug() << __FILE__ << __LINE__ 421 // << " encompassedBy NO by coordinates" 422 // << "coordinates->start()" << coordinates->start() 423 // << "coordinates->end()" << coordinates->end(); 424 } 425 } 426 427 if(!countedIn) 428 ++countOut; 429 } 430 431 Q_ASSERT((countIn + countOut) == m_monomerList.size()); 432 433 if (in) 434 *in = countIn; 435 if (out) 436 *out = countOut; 437 438 if (countOut && countIn) 439 return MXP_CROSS_LINK_ENCOMPASSED_PARTIAL; 440 441 if (countOut) 442 return MXP_CROSS_LINK_ENCOMPASSED_NO; 443 444 if (countIn) 445 return MXP_CROSS_LINK_ENCOMPASSED_FULL; 446 447 return MXP_CROSS_LINK_ENCOMPASSED_NO; 448 } 449 450 451 bool validate()452 CrossLink::validate() 453 { 454 if (m_monomerList.size() <= 1) 455 { 456 qDebug() << __FILE__ << __LINE__ 457 << "A crossLink with a single monomer " 458 "cannot be created."; 459 460 return false; 461 } 462 463 if (mp_polymer.isNull()) 464 return false; 465 466 // If the crossLinker has at least one modif in its definition, then 467 // we have to make sure that the modification has as one of its 468 // targets the corresponding monomer in the list of monomer 469 // pointers. 470 471 //Indeed, there is some logic here. Either the m_modifList in the 472 // parent class CrossLinker contains no item, in which case 473 // everything is fine, or it does contain items. In the latter case, 474 // then, the number of items must match the number of monomers being 475 // crosslinked, and then we get to know which modif is attributable 476 // to which monomer, hence the possibility of a check. 477 478 if (m_modifList.size()) 479 { 480 if(m_modifList.size() != m_monomerList.size()) 481 { 482 qDebug() << __FILE__ << __LINE__ 483 << QObject::tr("The number of modification items " 484 "does not match the number of " 485 "monomers. This is an error."); 486 return false; 487 } 488 489 // At this point, we can make the check for each modif: 490 491 for(int iter = 0; iter < m_modifList.size(); ++iter) 492 { 493 Modif *modif = m_modifList.at(iter); 494 const Monomer *monomer = m_monomerList.at(iter); 495 496 if (!monomer->isModifTarget(*modif)) 497 { 498 // The monomer is not target for the modification that is 499 // implied by the crossLinking. 500 501 qDebug() << __FILE__ << __LINE__ 502 << QObject::tr("The monomer '%1' is not target of " 503 "crossLinker modif '%2'") 504 .arg(monomer->name()) 505 .arg(modif->name()); 506 507 return false; 508 } 509 } 510 } 511 512 return true; 513 } 514 515 516 bool calculateMasses()517 CrossLink::calculateMasses() 518 { 519 return CrossLinker::calculateMasses(); 520 } 521 522 523 bool accountMasses(double * mono,double * avg,int times)524 CrossLink::accountMasses(double *mono, double *avg, int times) 525 { 526 return CrossLinker::accountMasses(mono, avg, times); 527 } 528 529 530 bool accountMasses(Ponderable * ponderable,int times)531 CrossLink::accountMasses(Ponderable *ponderable, int times) 532 { 533 return CrossLinker::accountMasses(ponderable, times); 534 } 535 536 537 QString * prepareResultsTxtString()538 CrossLink::prepareResultsTxtString() 539 { 540 QString *text = new QString(); 541 542 *text += QObject::tr("\nCross-link:\n" 543 "===============\n" 544 "Cross-linker name: %1\n" 545 "Cross-link comment: %2\n") 546 .arg(m_name) 547 .arg(m_comment); 548 549 for (int iter = 0; iter < m_monomerList.size(); ++iter) 550 { 551 const Monomer *monomer = m_monomerList.at(iter); 552 553 int index = mp_polymer->monomerIndex(monomer); 554 555 *text += QObject::tr("Partner %1: %2 at position: %3\n") 556 .arg(iter + 1) 557 .arg(monomer->code()) 558 .arg(index + 1); 559 } 560 561 return text; 562 } 563 564 } // namespace massXpert 565