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