1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "AssemblyCellRenderer.h"
23 
24 #include <QFont>
25 #include <QPainter>
26 
27 #include <U2Core/Timer.h>
28 #include <U2Core/U2SafePoints.h>
29 
30 namespace U2 {
31 
32 namespace {
33 
34 typedef QPair<char, char> TwoChars;
35 
initDefaultColorSheme()36 static QMap<char, QColor> initDefaultColorSheme() {
37     QMap<char, QColor> colors;
38 
39     // TODO other chars ??
40     // TODO = symbol
41     colors['a'] = colors['A'] = QColor("#FCFF92");
42     colors['c'] = colors['C'] = QColor("#70F970");
43     colors['g'] = colors['G'] = QColor("#4EADE1");
44     colors['t'] = colors['T'] = QColor("#FF99B1");
45 
46     // TODO: more meaningful colors
47     colors['w'] = colors['W'] =
48         colors['r'] = colors['R'] =
49             colors['m'] = colors['M'] =
50                 colors['k'] = colors['K'] =
51                     colors['y'] = colors['Y'] =
52                         colors['s'] = colors['S'] =
53 
54                             colors['b'] = colors['B'] =
55                                 colors['v'] = colors['V'] =
56                                     colors['d'] = colors['D'] =
57                                         colors['h'] = colors['H'] = QColor("#FFAA60");
58 
59     colors['-'] = QColor("#FBFBFB");
60     colors['N'] = QColor("#FBFBFB");
61 
62     return colors;
63 }
64 
initExtendedPairs()65 static QMap<char, TwoChars> initExtendedPairs() {
66     QMap<char, TwoChars> pairs;
67 
68     pairs['w'] = pairs['W'] = TwoChars('T', 'A');
69     pairs['r'] = pairs['R'] = TwoChars('G', 'A');
70     pairs['m'] = pairs['M'] = TwoChars('C', 'A');
71     pairs['k'] = pairs['K'] = TwoChars('G', 'T');
72     pairs['y'] = pairs['Y'] = TwoChars('T', 'C');
73     pairs['s'] = pairs['S'] = TwoChars('G', 'C');
74     return pairs;
75 }
76 
isGap(char c)77 inline static bool isGap(char c) {
78     // TODO : get rid of hardcoded values!
79     // TODO: smarter analysis. Don't forget about '=' symbol and IUPAC codes
80     return (c == '-' || c == 'N');
81 }
82 
83 }  // namespace
84 
85 static const QMap<char, QColor> nucleotideColorScheme = initDefaultColorSheme();
86 static const QList<char> assemblyAlphabet = nucleotideColorScheme.keys();
87 static const QMap<char, TwoChars> extendedPairs = initExtendedPairs();
88 
drawBackground(QPixmap & img,const QSize & size,const QColor & topColor,const QColor & bottomColor)89 static void drawBackground(QPixmap &img, const QSize &size, const QColor &topColor, const QColor &bottomColor) {
90     QPainter p(&img);
91 
92     // TODO invent something greater
93     QLinearGradient linearGrad(QPointF(0, 0), QPointF(size.width(), size.height()));
94     linearGrad.setColorAt(0, QColor::fromRgb(topColor.red() - 70, topColor.green() - 70, topColor.blue() - 70));
95     linearGrad.setColorAt(1, bottomColor);
96     QBrush br(linearGrad);
97 
98     QRect rect = QRect(QPoint(), size);
99     p.fillRect(rect, br);
100 }
101 
drawText(QPixmap & img,const QSize & size,char c,const QFont & font,const QColor & color)102 static void drawText(QPixmap &img, const QSize &size, char c, const QFont &font, const QColor &color) {
103     QPainter p(&img);
104     p.setFont(font);
105     p.setPen(color);
106     QRect rect = QRect(QPoint(), size);
107     p.drawText(rect, Qt::AlignCenter, QString(c));
108 }
109 
drawCell(QPixmap & img,const QSize & size,const QColor & topColor,const QColor & bottomColor,bool text,char c,const QFont & font,const QColor & textColor)110 void AssemblyCellRenderer::drawCell(QPixmap &img, const QSize &size, const QColor &topColor, const QColor &bottomColor, bool text, char c, const QFont &font, const QColor &textColor) {
111     drawBackground(img, size, topColor, bottomColor);
112 
113     if (text) {
114         drawText(img, size, c, font, textColor);
115     }
116 }
117 
118 class NucleotideColorsRenderer : public AssemblyCellRenderer {
119 public:
NucleotideColorsRenderer()120     NucleotideColorsRenderer()
121         : AssemblyCellRenderer(), colorScheme(nucleotideColorScheme),
122           images(), unknownChar(), size(), devicePixelRatio(0), text(false), font() {
123     }
~NucleotideColorsRenderer()124     virtual ~NucleotideColorsRenderer() {
125     }
126 
127     virtual void render(const QSize &size, int devicePixelRatio, bool text, const QFont &font);
128 
129     virtual QPixmap cellImage(char c);
130     virtual QPixmap cellImage(const U2AssemblyRead &read, char c);
131     virtual QPixmap cellImage(const U2AssemblyRead &read, char c, char ref);
132 
133 private:
134     void update();
135 
136 private:
137     QMap<char, QColor> colorScheme;
138 
139     // images cache
140     QHash<char, QPixmap> images;
141     QPixmap unknownChar;
142 
143     // cached cells parameters
144     QSize size;
145     int devicePixelRatio;
146     bool text;
147     QFont font;
148 };
149 
render(const QSize & _size,int _devicePixelRatio,bool _text,const QFont & _font)150 void NucleotideColorsRenderer::render(const QSize &_size, int _devicePixelRatio, bool _text, const QFont &_font) {
151     GTIMER(c1, t1, "NucleotideColorsRenderer::render");
152 
153     if (_size != size || _devicePixelRatio != devicePixelRatio || _text != text || (text && _font != font)) {
154         // update cache
155         size = _size, devicePixelRatio = _devicePixelRatio, text = _text, font = _font;
156         update();
157     }
158 }
159 
update()160 void NucleotideColorsRenderer::update() {
161     images.clear();
162 
163     foreach (char c, colorScheme.keys()) {
164         QPixmap img(size * devicePixelRatio);
165         img.setDevicePixelRatio(devicePixelRatio);
166         QColor textColor = isGap(c) ? Qt::red : Qt::black;
167         if (extendedPairs.contains(c)) {
168             // char from extended alphabet, draw gradient
169             TwoChars pair = extendedPairs.value(c);
170             drawCell(img, size, colorScheme.value(pair.first), colorScheme.value(pair.second), text, c, font, textColor);
171         } else {
172             // normal char
173             drawCell(img, size, colorScheme.value(c), text, c, font, textColor);
174         }
175         images.insert(c, img);
176     }
177 
178     unknownChar = QPixmap(size * devicePixelRatio);
179     unknownChar.setDevicePixelRatio(devicePixelRatio);
180     drawCell(unknownChar, size, QColor("#FBFBFB"), text, '?', font, Qt::red);
181 }
182 
cellImage(char c)183 QPixmap NucleotideColorsRenderer::cellImage(char c) {
184     c = (!nucleotideColorScheme.contains(c)) ? 'N' : c;
185     return images.value(c, unknownChar);
186 }
187 
cellImage(const U2AssemblyRead &,char c)188 QPixmap NucleotideColorsRenderer::cellImage(const U2AssemblyRead &, char c) {
189     return cellImage(c);
190 }
191 
cellImage(const U2AssemblyRead &,char c,char)192 QPixmap NucleotideColorsRenderer::cellImage(const U2AssemblyRead &, char c, char) {
193     return cellImage(c);
194 }
195 
196 class ComplementColorsRenderer : public AssemblyCellRenderer {
197 public:
ComplementColorsRenderer()198     ComplementColorsRenderer()
199         : AssemblyCellRenderer(),
200           directImages(), complementImages(), unknownChar(),
201           size(), devicePixelRatio(0), text(false), font() {
202     }
203 
~ComplementColorsRenderer()204     virtual ~ComplementColorsRenderer() {
205     }
206 
207     virtual void render(const QSize &size, int devicePixelRatio, bool text, const QFont &font);
208 
209     virtual QPixmap cellImage(char c);
210     virtual QPixmap cellImage(const U2AssemblyRead &read, char c);
211     virtual QPixmap cellImage(const U2AssemblyRead &read, char c, char ref);
212 
213 private:
214     void update();
215 
216 private:
217     // images cache
218     QHash<char, QPixmap> directImages, complementImages;
219     QPixmap unknownChar;
220 
221     // cached cells parameters
222     QSize size;
223     int devicePixelRatio;
224     bool text;
225     QFont font;
226 
227 private:
228     static const QColor directColor, complementColor;
229 };
230 
231 const QColor ComplementColorsRenderer::directColor("#4EADE1");
232 const QColor ComplementColorsRenderer::complementColor("#70F970");
233 
render(const QSize & _size,int _devicePixelRatio,bool _text,const QFont & _font)234 void ComplementColorsRenderer::render(const QSize &_size, int _devicePixelRatio, bool _text, const QFont &_font) {
235     GTIMER(c1, t1, "ComplementColorsRenderer::render");
236 
237     if (_size != size || _devicePixelRatio != devicePixelRatio || _text != text || (text && _font != font)) {
238         // update cache
239         size = _size, devicePixelRatio = _devicePixelRatio, text = _text, font = _font;
240         update();
241     }
242 }
243 
update()244 void ComplementColorsRenderer::update() {
245     directImages.clear();
246     complementImages.clear();
247 
248     foreach (char c, assemblyAlphabet) {
249         QPixmap dimg(size * devicePixelRatio), cimg(size * devicePixelRatio);
250         dimg.setDevicePixelRatio(devicePixelRatio);
251         cimg.setDevicePixelRatio(devicePixelRatio);
252         QColor dcolor = directColor, ccolor = complementColor, textColor = Qt::black;
253 
254         if (isGap(c)) {
255             dcolor = ccolor = QColor("#FBFBFB");
256             textColor = Qt::red;
257         }
258 
259         drawCell(dimg, size, dcolor, text, c, font, textColor);
260         drawCell(cimg, size, ccolor, text, c, font, textColor);
261 
262         directImages.insert(c, dimg);
263         complementImages.insert(c, cimg);
264     }
265 
266     unknownChar = QPixmap(size * devicePixelRatio);
267     unknownChar.setDevicePixelRatio(devicePixelRatio);
268     drawCell(unknownChar, size, QColor("#FBFBFB"), text, '?', font, Qt::red);
269 }
270 
cellImage(char c)271 QPixmap ComplementColorsRenderer::cellImage(char c) {
272     c = (!nucleotideColorScheme.contains(c)) ? 'N' : c;
273     return directImages.value(c, unknownChar);
274 }
275 
cellImage(const U2AssemblyRead & read,char c)276 QPixmap ComplementColorsRenderer::cellImage(const U2AssemblyRead &read, char c) {
277     c = (!nucleotideColorScheme.contains(c)) ? 'N' : c;
278 
279     if (ReadFlagsUtils::isComplementaryRead(read->flags)) {
280         return complementImages.value(c, unknownChar);
281     } else {
282         return directImages.value(c, unknownChar);
283     }
284 }
285 
cellImage(const U2AssemblyRead & read,char c,char)286 QPixmap ComplementColorsRenderer::cellImage(const U2AssemblyRead &read, char c, char) {
287     return cellImage(read, c);
288 }
289 
290 class DiffNucleotideColorsRenderer : public AssemblyCellRenderer {
291 public:
292     DiffNucleotideColorsRenderer();
~DiffNucleotideColorsRenderer()293     virtual ~DiffNucleotideColorsRenderer() {
294     }
295 
296     virtual void render(const QSize &size, int devicePixelRatio, bool text, const QFont &font);
297 
298     virtual QPixmap cellImage(char c);
299     virtual QPixmap cellImage(const U2AssemblyRead &read, char c);
300     virtual QPixmap cellImage(const U2AssemblyRead &read, char c, char ref);
301 
302 private:
303     void update();
304 
305 private:
306     QMap<char, QColor> colorScheme;
307 
308     // images cache
309     QHash<char, QPixmap> highlightedImages;
310     QHash<char, QPixmap> normalImages;
311     QPixmap unknownChar;
312 
313     // cached cells parameters
314     QSize size;
315     int devicePixelRatio;
316     bool text;
317     QFont font;
318 };
319 
320 class PairedColorsRenderer : public AssemblyCellRenderer {
321 public:
PairedColorsRenderer()322     PairedColorsRenderer()
323         : AssemblyCellRenderer(),
324           pairedImages(), unpairedImages(), unknownChar(),
325           size(), devicePixelRatio(0), text(false), font() {
326     }
327 
~PairedColorsRenderer()328     virtual ~PairedColorsRenderer() {
329     }
330 
331     virtual void render(const QSize &size, int devicePixelRatio, bool text, const QFont &font);
332 
333     virtual QPixmap cellImage(char c);
334     virtual QPixmap cellImage(const U2AssemblyRead &read, char c);
335     virtual QPixmap cellImage(const U2AssemblyRead &read, char c, char ref);
336 
337 private:
338     void update();
339 
340 private:
341     // images cache
342     QHash<char, QPixmap> pairedImages, unpairedImages;
343     QPixmap unknownChar;
344 
345     // cached cells parameters
346     QSize size;
347     int devicePixelRatio;
348     bool text;
349     QFont font;
350 
351 private:
352     static const QColor pairedColor, unpairedColor;
353 };
354 
355 const QColor PairedColorsRenderer::pairedColor("#4EE1AD");
356 const QColor PairedColorsRenderer::unpairedColor("#BBBBBB");
357 
render(const QSize & _size,int _devicePixelRatio,bool _text,const QFont & _font)358 void PairedColorsRenderer::render(const QSize &_size, int _devicePixelRatio, bool _text, const QFont &_font) {
359     GTIMER(c1, t1, "PairedReadsColorsRenderer::render");
360 
361     if (_size != size || _devicePixelRatio != devicePixelRatio || _text != text || (text && _font != font)) {
362         // update cache
363         size = _size, devicePixelRatio = _devicePixelRatio, text = _text, font = _font;
364         update();
365     }
366 }
367 
update()368 void PairedColorsRenderer::update() {
369     pairedImages.clear();
370     unpairedImages.clear();
371 
372     foreach (char c, assemblyAlphabet) {
373         QPixmap pimg(size * devicePixelRatio), npimg(size * devicePixelRatio);
374         pimg.setDevicePixelRatio(devicePixelRatio);
375         npimg.setDevicePixelRatio(devicePixelRatio);
376         QColor pcolor = pairedColor, ucolor = unpairedColor, textColor = Qt::black;
377 
378         if (isGap(c)) {
379             pcolor = ucolor = QColor("#FBFBFB");
380             textColor = Qt::red;
381         }
382 
383         drawCell(pimg, size, pcolor, text, c, font, textColor);
384         drawCell(npimg, size, ucolor, text, c, font, textColor);
385 
386         pairedImages.insert(c, pimg);
387         unpairedImages.insert(c, npimg);
388     }
389 
390     unknownChar = QPixmap(size * devicePixelRatio);
391     unknownChar.setDevicePixelRatio(devicePixelRatio);
392     drawCell(unknownChar, size, QColor("#FBFBFB"), text, '?', font, Qt::red);
393 }
394 
cellImage(char c)395 QPixmap PairedColorsRenderer::cellImage(char c) {
396     c = (!nucleotideColorScheme.contains(c)) ? 'N' : c;
397     return unpairedImages.value(c, unknownChar);
398 }
399 
cellImage(const U2AssemblyRead & read,char c)400 QPixmap PairedColorsRenderer::cellImage(const U2AssemblyRead &read, char c) {
401     c = (!nucleotideColorScheme.contains(c)) ? 'N' : c;
402 
403     if (ReadFlagsUtils::isPairedRead(read->flags)) {
404         return pairedImages.value(c, unknownChar);
405     } else {
406         return unpairedImages.value(c, unknownChar);
407     }
408 }
409 
cellImage(const U2AssemblyRead & read,char c,char)410 QPixmap PairedColorsRenderer::cellImage(const U2AssemblyRead &read, char c, char) {
411     return cellImage(read, c);
412 }
413 
DiffNucleotideColorsRenderer()414 DiffNucleotideColorsRenderer::DiffNucleotideColorsRenderer()
415     : AssemblyCellRenderer(), colorScheme(nucleotideColorScheme),
416       highlightedImages(), normalImages(), unknownChar(), size(), devicePixelRatio(0), text(false), font() {
417 }
418 
render(const QSize & _size,int _devicePixelRatio,bool _text,const QFont & _font)419 void DiffNucleotideColorsRenderer::render(const QSize &_size, int _devicePixelRatio, bool _text, const QFont &_font) {
420     GTIMER(c1, t1, "DiffNucleotideColorsRenderer::render");
421 
422     if (_size != size || _devicePixelRatio != devicePixelRatio || _text != text || (text && _font != font)) {
423         // update cache
424         size = _size, devicePixelRatio = _devicePixelRatio, text = _text, font = _font;
425         update();
426     }
427 }
428 
update()429 void DiffNucleotideColorsRenderer::update() {
430     highlightedImages.clear();
431     normalImages.clear();
432 
433     QColor normalColor("#BBBBBB");
434 
435     foreach (char c, colorScheme.keys()) {
436         QPixmap himg(size * devicePixelRatio), nimg(size * devicePixelRatio);
437         himg.setDevicePixelRatio(devicePixelRatio);
438         nimg.setDevicePixelRatio(devicePixelRatio);
439         // make gaps more noticeable
440         QColor textColor = isGap(c) ? Qt::white : Qt::black;
441         QColor highlightColor = isGap(c) ? QColor("#CC4E4E") : colorScheme.value(c);
442 
443         if (extendedPairs.contains(c)) {
444             // char from extended alphabet, draw gradient
445             TwoChars pair = extendedPairs.value(c);
446             drawCell(himg, size, colorScheme.value(pair.first), colorScheme.value(pair.second), text, c, font, textColor);
447         } else {
448             // normal char
449             drawCell(himg, size, highlightColor, text, c, font, textColor);
450         }
451         drawCell(nimg, size, normalColor, text, c, font, textColor);
452 
453         highlightedImages.insert(c, himg);
454         normalImages.insert(c, nimg);
455     }
456 
457     unknownChar = QPixmap(size * devicePixelRatio);
458     unknownChar.setDevicePixelRatio(devicePixelRatio);
459     drawCell(unknownChar, size, QColor("#FBFBFB"), text, '?', font, Qt::red);
460 }
461 
cellImage(char c)462 QPixmap DiffNucleotideColorsRenderer::cellImage(char c) {
463     c = (!nucleotideColorScheme.contains(c)) ? 'N' : c;
464     return highlightedImages.value(c, unknownChar);
465 }
466 
cellImage(const U2AssemblyRead &,char c)467 QPixmap DiffNucleotideColorsRenderer::cellImage(const U2AssemblyRead &, char c) {
468     return cellImage(c);
469 }
470 
cellImage(const U2AssemblyRead &,char c,char ref)471 QPixmap DiffNucleotideColorsRenderer::cellImage(const U2AssemblyRead &, char c, char ref) {
472     c = (!nucleotideColorScheme.contains(c)) ? 'N' : c;
473 
474     if (c == ref) {
475         return normalImages.value(c, unknownChar);
476     } else {
477         return highlightedImages.value(c, unknownChar);
478     }
479     return cellImage(c);
480 }
481 
482 //////////////////////////////////////////////////////////////////////////
483 // factories
484 
AssemblyCellRendererFactory(const QString & _id,const QString & _name)485 AssemblyCellRendererFactory::AssemblyCellRendererFactory(const QString &_id, const QString &_name)
486     : id(_id), name(_name) {
487 }
488 
489 QString AssemblyCellRendererFactory::ALL_NUCLEOTIDES = "ASSEMBLY_RENDERER_ALL_NUCLEOTIDES";
490 QString AssemblyCellRendererFactory::DIFF_NUCLEOTIDES = "ASSEMBLY_RENDERER_DIFF_NUCLEOTIDES";
491 QString AssemblyCellRendererFactory::STRAND_DIRECTION = "ASSEMBLY_RENDERER_STRAND_DIRECTION";
492 QString AssemblyCellRendererFactory::PAIRED = "ASSEMBLY_RENDERER_PAIRED";
493 
494 class NucleotideColorsRendererFactory : public AssemblyCellRendererFactory {
495 public:
NucleotideColorsRendererFactory(const QString & _id,const QString & _name)496     NucleotideColorsRendererFactory(const QString &_id, const QString &_name)
497         : AssemblyCellRendererFactory(_id, _name) {
498     }
499 
create()500     virtual AssemblyCellRenderer *create() {
501         return new NucleotideColorsRenderer();
502     }
503 };
504 
505 class DiffNucleotideColorsRendererFactory : public AssemblyCellRendererFactory {
506 public:
DiffNucleotideColorsRendererFactory(const QString & _id,const QString & _name)507     DiffNucleotideColorsRendererFactory(const QString &_id, const QString &_name)
508         : AssemblyCellRendererFactory(_id, _name) {
509     }
510 
create()511     virtual AssemblyCellRenderer *create() {
512         return new DiffNucleotideColorsRenderer();
513     }
514 };
515 
516 class ComplementColorsRendererFactory : public AssemblyCellRendererFactory {
517 public:
ComplementColorsRendererFactory(const QString & _id,const QString & _name)518     ComplementColorsRendererFactory(const QString &_id, const QString &_name)
519         : AssemblyCellRendererFactory(_id, _name) {
520     }
521 
create()522     virtual AssemblyCellRenderer *create() {
523         return new ComplementColorsRenderer();
524     }
525 };
526 
527 class PairedColorsRendererFactory : public AssemblyCellRendererFactory {
528 public:
PairedColorsRendererFactory(const QString & _id,const QString & _name)529     PairedColorsRendererFactory(const QString &_id, const QString &_name)
530         : AssemblyCellRendererFactory(_id, _name) {
531     }
532 
create()533     virtual AssemblyCellRenderer *create() {
534         return new PairedColorsRenderer();
535     }
536 };
537 
538 //////////////////////////////////////////////////////////////////////////
539 // registry
540 
AssemblyCellRendererFactoryRegistry(QObject * parent)541 AssemblyCellRendererFactoryRegistry::AssemblyCellRendererFactoryRegistry(QObject *parent)
542     : QObject(parent) {
543     initBuiltInRenderers();
544 }
545 
getFactoryById(const QString & id) const546 AssemblyCellRendererFactory *AssemblyCellRendererFactoryRegistry::getFactoryById(const QString &id) const {
547     foreach (AssemblyCellRendererFactory *f, factories) {
548         if (f->getId() == id) {
549             return f;
550         }
551     }
552     return nullptr;
553 }
554 
addFactory(AssemblyCellRendererFactory * f)555 void AssemblyCellRendererFactoryRegistry::addFactory(AssemblyCellRendererFactory *f) {
556     assert(getFactoryById(f->getId()) == nullptr);
557     factories << f;
558 }
559 
initBuiltInRenderers()560 void AssemblyCellRendererFactoryRegistry::initBuiltInRenderers() {
561     addFactory(new NucleotideColorsRendererFactory(AssemblyCellRendererFactory::ALL_NUCLEOTIDES,
562                                                    tr("Nucleotide")));
563     addFactory(new DiffNucleotideColorsRendererFactory(AssemblyCellRendererFactory::DIFF_NUCLEOTIDES,
564                                                        tr("Difference")));
565     addFactory(new ComplementColorsRendererFactory(AssemblyCellRendererFactory::STRAND_DIRECTION,
566                                                    tr("Strand direction")));
567     addFactory(new PairedColorsRendererFactory(AssemblyCellRendererFactory::PAIRED,
568                                                tr("Paired reads")));
569 }
570 
~AssemblyCellRendererFactoryRegistry()571 AssemblyCellRendererFactoryRegistry::~AssemblyCellRendererFactoryRegistry() {
572     foreach (AssemblyCellRendererFactory *f, factories) {
573         delete f;
574     }
575 }
576 
577 }  // namespace U2
578