]> git.lyx.org Git - lyx.git/blob - src/mathed/math_support.C
Make Helge happy: no more crash on arrow up/down in math macro
[lyx.git] / src / mathed / math_support.C
1 /**
2  * \file math_support.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "math_support.h"
15 #include "math_data.h"
16 #include "math_inset.h"
17 #include "math_mathmlstream.h"
18 #include "math_parser.h"
19
20 #include "debug.h"
21 #include "LColor.h"
22
23 #include "frontends/Painter.h"
24 #include "frontends/font_metrics.h"
25 #include "frontends/lyx_gui.h"
26
27 #include <map>
28 #include <sstream>
29
30
31 using std::string;
32 using std::max;
33 using std::endl;
34
35
36 ///
37 class Matrix {
38 public:
39         ///
40         Matrix(int, double, double);
41         ///
42         void transform(double &, double &);
43 private:
44         ///
45         double m_[2][2];
46 };
47
48
49 Matrix::Matrix(int code, double x, double y)
50 {
51         double const cs = (code & 1) ? 0 : (1 - code);
52         double const sn = (code & 1) ? (2 - code) : 0;
53         m_[0][0] =  cs * x;
54         m_[0][1] =  sn * x;
55         m_[1][0] = -sn * y;
56         m_[1][1] =  cs * y;
57 }
58
59
60 void Matrix::transform(double & x, double & y)
61 {
62         double xx = m_[0][0] * x + m_[0][1] * y;
63         double yy = m_[1][0] * x + m_[1][1] * y;
64         x = xx;
65         y = yy;
66 }
67
68
69
70 namespace {
71
72 /*
73  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
74  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4 = square polyline
75  */
76
77
78 double const parenthHigh[] = {
79         2, 13,
80         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
81         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
82         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
83         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
84         0.9840, 0.9986,
85         0
86 };
87
88
89 double const parenth[] = {
90         2, 13,
91         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
92         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
93         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
94         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
95         0.9930, 0.9919,
96         0
97 };
98
99
100 double const brace[] = {
101         2, 21,
102         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
103         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
104         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
105         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
106         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
107         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
108         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
109         0
110 };
111
112
113 double const arrow[] = {
114         4, 7,
115         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
116         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
117         0.9500, 0.7500,
118         3, 0.5000, 0.1500, 0.5000, 0.9500,
119         0
120 };
121
122
123 double const Arrow[] = {
124         4, 7,
125         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
126         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
127         0.9500, 0.7500,
128         3, 0.3500, 0.5000, 0.3500, 0.9500,
129         3, 0.6500, 0.5000, 0.6500, 0.9500,
130         0
131 };
132
133
134 double const udarrow[] = {
135         2, 3,
136         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
137         2, 3,
138         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
139         1, 0.5, 0.2,  0.5, 0.8,
140         0
141 };
142
143
144 double const Udarrow[] = {
145         2, 3,
146         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
147         2, 3,
148         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
149         1, 0.35, 0.2, 0.35, 0.8,
150         1, 0.65, 0.2, 0.65, 0.8,
151         0
152 };
153
154
155 double const brack[] = {
156         2, 4,
157         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
158         0
159 };
160
161
162 double const corner[] = {
163         2, 3,
164         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
165         0
166 };
167
168
169 double const angle[] = {
170         2, 3,
171         1, 0,  0.05, 0.5,  1, 1,
172         0
173 };
174
175
176 double const slash[] = {
177         1, 0.95, 0.05, 0.05, 0.95,
178         0
179 };
180
181
182 double const hline[] = {
183         1, 0.00, 0.5, 1.0, 0.5,
184         0
185 };
186
187
188 double const ddot[] = {
189         1, 0.2, 0.5,  0.3, 0.5,
190         1, 0.7, 0.5,  0.8, 0.5,
191         0
192 };
193
194
195 double const dddot[] = {
196         1, 0.1, 0.5,  0.2, 0.5,
197         1, 0.45, 0.5, 0.55, 0.5,
198         1, 0.8, 0.5,  0.9, 0.5,
199         0
200 };
201
202
203 double const hline3[] = {
204         1, 0.1,   0,  0.15,  0,
205         1, 0.475, 0,  0.525, 0,
206         1, 0.85,  0,  0.9,   0,
207         0
208 };
209
210
211 double const dline3[] = {
212         1, 0.1,   0.1,   0.15,  0.15,
213         1, 0.475, 0.475, 0.525, 0.525,
214         1, 0.85,  0.85,  0.9,   0.9,
215         0
216 };
217
218
219 double const hlinesmall[] = {
220         1, 0.4, 0.5, 0.6, 0.5,
221         0
222 };
223
224
225 double const ring[] = {
226         2, 5,
227         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
228         0
229 };
230
231
232 double const vert[] = {
233         1, 0.5, 0.05,  0.5, 0.95,
234         0
235 };
236
237
238 double const  Vert[] = {
239         1, 0.3, 0.05,  0.3, 0.95,
240         1, 0.7, 0.05,  0.7, 0.95,
241         0
242 };
243
244
245 double const tilde[] = {
246         2, 4,
247         0.00, 0.8,  0.25, 0.2,  0.75, 0.8,  1.00, 0.2,
248         0
249 };
250
251
252 struct deco_struct {
253         double const * data;
254         int angle;
255 };
256
257 struct named_deco_struct {
258         char const * name;
259         double const * data;
260         int angle;
261 };
262
263 named_deco_struct deco_table[] = {
264         // Decorations
265         {"widehat",             angle,    3 },
266         {"widetilde",           tilde,    0 },
267         {"underbar",            hline,    0 },
268         {"underline",           hline,    0 },
269         {"overline",            hline,    0 },
270         {"underbrace",          brace,    1 },
271         {"overbrace",           brace,    3 },
272         {"overleftarrow",       arrow,    1 },
273         {"overrightarrow",      arrow,    3 },
274         {"overleftrightarrow",  udarrow,  1 },
275         {"xleftarrow",          arrow,    1 },
276         {"xrightarrow",         arrow,    3 },
277         {"underleftarrow",      arrow,    1 },
278         {"underrightarrow",     arrow,    3 },
279         {"underleftrightarrow", udarrow,  1 },
280
281         // Delimiters
282         {"(",              parenth,    0 },
283         {")",              parenth,    2 },
284         {"{",              brace,      0 },
285         {"}",              brace,      2 },
286         {"[",              brack,      0 },
287         {"]",              brack,      2 },
288         {"|",              vert,       0 },
289         {"/",              slash,      0 },
290         {"vert",           vert,       0 },
291         {"Vert",           Vert,       0 },
292         {"'",              slash,      1 },
293         {"backslash",      slash,      1 },
294         {"langle",         angle,      0 },
295         {"lceil",          corner,     0 },
296         {"lfloor",         corner,     1 },
297         {"rangle",         angle,      2 },
298         {"rceil",          corner,     3 },
299         {"rfloor",         corner,     2 },
300         {"downarrow",      arrow,      2 },
301         {"Downarrow",      Arrow,      2 },
302         {"uparrow",        arrow,      0 },
303         {"Uparrow",        Arrow,      0 },
304         {"updownarrow",    udarrow,    0 },
305         {"Updownarrow",    Udarrow,    0 },
306
307         // Accents
308         {"ddot",           ddot,       0 },
309         {"dddot",          dddot,      0 },
310         {"hat",            angle,      3 },
311         {"grave",          slash,      1 },
312         {"acute",          slash,      0 },
313         {"tilde",          tilde,      0 },
314         {"bar",            hline,      0 },
315         {"dot",            hlinesmall, 0 },
316         {"check",          angle,      1 },
317         {"breve",          parenth,    1 },
318         {"vec",            arrow,      3 },
319         {"mathring",       ring,       0 },
320
321         // Dots
322         {"dots",           hline3,     0 },
323         {"ldots",          hline3,     0 },
324         {"cdots",          hline3,     0 },
325         {"vdots",          hline3,     1 },
326         {"ddots",          dline3,     0 },
327         {"dotsb",          hline3,     0 },
328         {"dotsc",          hline3,     0 },
329         {"dotsi",          hline3,     0 },
330         {"dotsm",          hline3,     0 },
331         {"dotso",          hline3,     0 }
332 };
333
334
335 std::map<string, deco_struct> deco_list;
336
337 // sort the table on startup
338 class init_deco_table {
339 public:
340         init_deco_table() {
341                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
342                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
343                         deco_struct d;
344                         d.data  = p->data;
345                         d.angle = p->angle;
346                         deco_list[p->name]= d;
347                 }
348         }
349 };
350
351 static init_deco_table dummy;
352
353
354 deco_struct const * search_deco(string const & name)
355 {
356         std::map<string, deco_struct>::const_iterator p = deco_list.find(name);
357         return (p == deco_list.end()) ? 0 : &(p->second);
358 }
359
360
361 } // namespace anon
362
363
364 void mathed_char_dim(LyXFont const & font, unsigned char c, Dimension & dim)
365 {
366         dim.des = font_metrics::descent(c, font);
367         dim.asc = font_metrics::ascent(c, font);
368         dim.wid = mathed_char_width(font, c);
369 }
370
371
372 int mathed_char_ascent(LyXFont const & font, unsigned char c)
373 {
374         return font_metrics::ascent(c, font);
375 }
376
377
378 int mathed_char_descent(LyXFont const & font, unsigned char c)
379 {
380         return font_metrics::descent(c, font);
381 }
382
383
384 int mathed_char_width(LyXFont const & font, unsigned char c)
385 {
386         return font_metrics::width(c, font);
387 }
388
389
390 void mathed_string_dim(LyXFont const & font, string const & s, Dimension & dim)
391 {
392 #if 1
393         dim.asc = 0;
394         dim.des = 0;
395         for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
396                 dim.asc = max(dim.asc, font_metrics::ascent(*it, font));
397                 dim.des = max(dim.des, font_metrics::descent(*it, font));
398         }
399 #else
400         dim.asc = font_metrics::maxAscent(font);
401         dim.des = font_metrics::maxDescent(font);
402 #endif
403         dim.wid = font_metrics::width(s, font);
404 }
405
406
407 int mathed_string_width(LyXFont const & font, string const & s)
408 {
409         return font_metrics::width(s, font);
410 }
411
412
413 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
414         string const & name)
415 {
416         if (name == ".") {
417                 pi.pain.line(x + w/2, y, x + w/2, y + h,
418                           LColor::cursor, Painter::line_onoffdash);
419                 return;
420         }
421
422         deco_struct const * mds = search_deco(name);
423         if (!mds) {
424                 lyxerr << "Deco was not found. Programming error?" << endl;
425                 lyxerr << "name: '" << name << "'" << endl;
426                 return;
427         }
428
429         int const n = (w < h) ? w : h;
430         int const r = mds->angle;
431         double const * d = mds->data;
432
433         if (h > 70 && (name == "(" || name == ")"))
434                 d = parenthHigh;
435
436         Matrix mt(r, w, h);
437         Matrix sqmt(r, n, n);
438
439         if (r > 0 && r < 3)
440                 y += h;
441
442         if (r >= 2)
443                 x += w;
444
445         for (int i = 0; d[i]; ) {
446                 int code = int(d[i++]);
447                 if (code & 1) {  // code == 1 || code == 3
448                         double xx = d[i++];
449                         double yy = d[i++];
450                         double x2 = d[i++];
451                         double y2 = d[i++];
452                         if (code == 3)
453                                 sqmt.transform(xx, yy);
454                         else
455                                 mt.transform(xx, yy);
456                         mt.transform(x2, y2);
457                         pi.pain.line(
458                                 int(x + xx + 0.5), int(y + yy + 0.5),
459                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
460                                 LColor::math);
461                 } else {
462                         int xp[32];
463                         int yp[32];
464                         int const n = int(d[i++]);
465                         for (int j = 0; j < n; ++j) {
466                                 double xx = d[i++];
467                                 double yy = d[i++];
468 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
469                                 if (code == 4)
470                                         sqmt.transform(xx, yy);
471                                 else
472                                         mt.transform(xx, yy);
473                                 xp[j] = int(x + xx + 0.5);
474                                 yp[j] = int(y + yy + 0.5);
475                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
476                         }
477                         pi.pain.lines(xp, yp, n, LColor::math);
478                 }
479         }
480 }
481
482
483 // In the future maybe we use a better fonts renderer
484 void drawStr(PainterInfo & pi, LyXFont const & font,
485         int x, int y, string const & str)
486 {
487         pi.pain.text(x, y, str, font);
488 }
489
490
491 void drawStrRed(PainterInfo & pi, int x, int y, string const & str)
492 {
493         LyXFont f = pi.base.font;
494         f.setColor(LColor::latex);
495         pi.pain.text(x, y, str, f);
496 }
497
498
499 void drawStrBlack(PainterInfo & pi, int x, int y, string const & str)
500 {
501         LyXFont f = pi.base.font;
502         f.setColor(LColor::foreground);
503         pi.pain.text(x, y, str, f);
504 }
505
506
507 void drawChar(PainterInfo & pi, LyXFont const & font, int x, int y, char c)
508 {
509         pi.pain.text(x, y, c, font);
510 }
511
512
513 void math_font_max_dim(LyXFont const & font, int & asc, int & des)
514 {
515         asc = font_metrics::maxAscent(font);
516         des = font_metrics::maxDescent(font);
517 }
518
519
520 struct fontinfo {
521         string cmd_;
522         LyXFont::FONT_FAMILY family_;
523         LyXFont::FONT_SERIES series_;
524         LyXFont::FONT_SHAPE  shape_;
525         LColor::color        color_;
526 };
527
528
529 LyXFont::FONT_FAMILY const inh_family = LyXFont::INHERIT_FAMILY;
530 LyXFont::FONT_SERIES const inh_series = LyXFont::INHERIT_SERIES;
531 LyXFont::FONT_SHAPE  const inh_shape  = LyXFont::INHERIT_SHAPE;
532
533
534 // mathnormal should be the first, otherwise the fallback further down
535 // does not work
536 fontinfo fontinfos[] = {
537         // math fonts
538         {"mathnormal",    LyXFont::ROMAN_FAMILY, LyXFont::MEDIUM_SERIES,
539                           LyXFont::ITALIC_SHAPE, LColor::math},
540         {"mathbf",        inh_family, LyXFont::BOLD_SERIES,
541                           inh_shape, LColor::math},
542         {"mathcal",       LyXFont::CMSY_FAMILY, inh_series,
543                           inh_shape, LColor::math},
544         {"mathfrak",      LyXFont::EUFRAK_FAMILY, inh_series,
545                           inh_shape, LColor::math},
546         {"mathrm",        LyXFont::ROMAN_FAMILY, inh_series,
547                           LyXFont::UP_SHAPE, LColor::math},
548         {"mathsf",        LyXFont::SANS_FAMILY, inh_series,
549                           inh_shape, LColor::math},
550         {"mathbb",        LyXFont::MSB_FAMILY, inh_series,
551                           inh_shape, LColor::math},
552         {"mathtt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
553                           inh_shape, LColor::math},
554         {"mathit",        inh_family, inh_series,
555                           LyXFont::ITALIC_SHAPE, LColor::math},
556         {"cmex",          LyXFont::CMEX_FAMILY, inh_series,
557                           inh_shape, LColor::math},
558         {"cmm",           LyXFont::CMM_FAMILY, inh_series,
559                           inh_shape, LColor::math},
560         {"cmr",           LyXFont::CMR_FAMILY, inh_series,
561                           inh_shape, LColor::math},
562         {"cmsy",          LyXFont::CMSY_FAMILY, inh_series,
563                           inh_shape, LColor::math},
564         {"eufrak",        LyXFont::EUFRAK_FAMILY, inh_series,
565                           inh_shape, LColor::math},
566         {"msa",           LyXFont::MSA_FAMILY, inh_series,
567                           inh_shape, LColor::math},
568         {"msb",           LyXFont::MSB_FAMILY, inh_series,
569                           inh_shape, LColor::math},
570         {"wasy",          LyXFont::WASY_FAMILY, inh_series,
571                           inh_shape, LColor::none},
572
573         // Text fonts
574         {"text",          inh_family, inh_series,
575                           inh_shape, LColor::foreground},
576         {"textbf",        inh_family, LyXFont::BOLD_SERIES,
577                           inh_shape, LColor::foreground},
578         {"textit",        inh_family, inh_series,
579                           LyXFont::ITALIC_SHAPE, LColor::foreground},
580         {"textmd",        inh_family, LyXFont::MEDIUM_SERIES,
581                           inh_shape, LColor::foreground},
582         {"textnormal",    inh_family, inh_series,
583                           LyXFont::UP_SHAPE, LColor::foreground},
584         {"textrm",        LyXFont::ROMAN_FAMILY,
585                           inh_series, LyXFont::UP_SHAPE,LColor::foreground},
586         {"textsc",        inh_family, inh_series,
587                           LyXFont::SMALLCAPS_SHAPE, LColor::foreground},
588         {"textsf",        LyXFont::SANS_FAMILY, inh_series,
589                           inh_shape, LColor::foreground},
590         {"textsl",        inh_family, inh_series,
591                           LyXFont::SLANTED_SHAPE, LColor::foreground},
592         {"texttt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
593                           inh_shape, LColor::foreground},
594         {"textup",        inh_family, inh_series,
595                           LyXFont::UP_SHAPE, LColor::foreground},
596
597         // TIPA support
598         {"textipa",       inh_family, inh_series,
599                           inh_shape, LColor::foreground},
600
601         // LyX internal usage
602         {"lyxtex",        inh_family, inh_series,
603                           LyXFont::UP_SHAPE, LColor::latex},
604         {"lyxert",        LyXFont::TYPEWRITER_FAMILY, inh_series,
605                           LyXFont::UP_SHAPE, LColor::latex},
606         {"lyxsymbol",     LyXFont::SYMBOL_FAMILY, inh_series,
607                           inh_shape, LColor::math},
608         {"lyxboldsymbol", LyXFont::SYMBOL_FAMILY, LyXFont::BOLD_SERIES,
609                           inh_shape, LColor::math},
610         {"lyxblacktext",  LyXFont::ROMAN_FAMILY, LyXFont::MEDIUM_SERIES,
611                           LyXFont::UP_SHAPE, LColor::foreground},
612         {"lyxnochange",   inh_family, inh_series,
613                           inh_shape, LColor::foreground},
614         {"lyxfakebb",     LyXFont::TYPEWRITER_FAMILY, LyXFont::BOLD_SERIES,
615                           LyXFont::UP_SHAPE, LColor::math},
616         {"lyxfakecal",    LyXFont::SANS_FAMILY, LyXFont::MEDIUM_SERIES,
617                           LyXFont::ITALIC_SHAPE, LColor::math},
618         {"lyxfakefrak",   LyXFont::ROMAN_FAMILY, LyXFont::BOLD_SERIES,
619                           LyXFont::ITALIC_SHAPE, LColor::math}
620 };
621
622
623 fontinfo * lookupFont(string const & name)
624 {
625         //lyxerr << "searching font '" << name << "'" << endl;
626         int const n = sizeof(fontinfos) / sizeof(fontinfo);
627         for (int i = 0; i < n; ++i)
628                 if (fontinfos[i].cmd_ == name) {
629                         //lyxerr << "found '" << i << "'" << endl;
630                         return fontinfos + i;
631                 }
632         return 0;
633 }
634
635
636 fontinfo * searchFont(string const & name)
637 {
638         fontinfo * f = lookupFont(name);
639         return f ? f : fontinfos;
640         // this should be mathnormal
641         //return searchFont("mathnormal");
642 }
643
644
645 bool isFontName(string const & name)
646 {
647         return lookupFont(name);
648 }
649
650
651 LyXFont getFont(string const & name)
652 {
653         LyXFont font;
654         augmentFont(font, name);
655         return font;
656 }
657
658
659 void fakeFont(string const & orig, string const & fake)
660 {
661         fontinfo * forig = searchFont(orig);
662         fontinfo * ffake = searchFont(fake);
663         if (forig && ffake) {
664                 forig->family_ = ffake->family_;
665                 forig->series_ = ffake->series_;
666                 forig->shape_  = ffake->shape_;
667                 forig->color_  = ffake->color_;
668         } else {
669                 lyxerr << "Can't fake font '" << orig << "' with '"
670                        << fake << "'" << endl;
671         }
672 }
673
674
675 void augmentFont(LyXFont & font, string const & name)
676 {
677         static bool initialized = false;
678         if (!initialized) {
679                 initialized = true;
680                 // fake fonts if necessary
681                 if (!lyx_gui::font_available(getFont("mathfrak")))
682                         fakeFont("mathfrak", "lyxfakefrak");
683                 if (!lyx_gui::font_available(getFont("mathcal")))
684                         fakeFont("mathcal", "lyxfakecal");
685         }
686         fontinfo * info = searchFont(name);
687         if (info->family_ != inh_family)
688                 font.setFamily(info->family_);
689         if (info->series_ != inh_series)
690                 font.setSeries(info->series_);
691         if (info->shape_ != inh_shape)
692                 font.setShape(info->shape_);
693         if (info->color_ != LColor::none)
694                 font.setColor(info->color_);
695 }
696
697
698 string asString(MathArray const & ar)
699 {
700         std::ostringstream os;
701         WriteStream ws(os);
702         ws << ar;
703         return os.str();
704 }
705
706
707 void asArray(string const & str, MathArray & ar)
708 {
709         mathed_parse_cell(ar, str);
710 }
711
712
713 string asString(MathInset const & inset)
714 {
715         std::ostringstream os;
716         WriteStream ws(os);
717         inset.write(ws);
718         return os.str();
719 }
720
721
722 string asString(MathAtom const & at)
723 {
724         std::ostringstream os;
725         WriteStream ws(os);
726         at->write(ws);
727         return os.str();
728 }