Actual source code: rgring.c
slepc-3.22.2 2024-12-02
1: /*
2: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3: SLEPc - Scalable Library for Eigenvalue Problem Computations
4: Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain
6: This file is part of SLEPc.
7: SLEPc is distributed under a 2-clause BSD license (see LICENSE).
8: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9: */
10: /*
11: Ring region, similar to the ellipse but with a start and end angle,
12: together with the width
13: */
15: #include <slepc/private/rgimpl.h>
16: #include <petscdraw.h>
18: typedef struct {
19: PetscScalar center; /* center of the ellipse */
20: PetscReal radius; /* radius of the ellipse */
21: PetscReal vscale; /* vertical scale of the ellipse */
22: PetscReal start_ang; /* start angle */
23: PetscReal end_ang; /* end angle */
24: PetscReal width; /* ring width */
25: } RG_RING;
27: static PetscErrorCode RGRingSetParameters_Ring(RG rg,PetscScalar center,PetscReal radius,PetscReal vscale,PetscReal start_ang,PetscReal end_ang,PetscReal width)
28: {
29: RG_RING *ctx = (RG_RING*)rg->data;
31: PetscFunctionBegin;
32: ctx->center = center;
33: if (radius == (PetscReal)PETSC_DETERMINE) {
34: ctx->radius = 1.0;
35: } else if (radius != (PetscReal)PETSC_CURRENT) {
36: PetscCheck(radius>0.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"The radius argument must be > 0.0");
37: ctx->radius = radius;
38: }
39: if (vscale == (PetscReal)PETSC_DETERMINE) {
40: ctx->vscale = 1.0;
41: } else if (vscale != (PetscReal)PETSC_CURRENT) {
42: PetscCheck(vscale>0.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"The vscale argument must be > 0.0");
43: ctx->vscale = vscale;
44: }
45: if (start_ang == (PetscReal)PETSC_DETERMINE) {
46: ctx->start_ang = 0.0;
47: } else if (start_ang != (PetscReal)PETSC_CURRENT) {
48: PetscCheck(start_ang>=0.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"The right-hand side angle argument must be >= 0.0");
49: PetscCheck(start_ang<=1.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"The right-hand side angle argument must be <= 1.0");
50: ctx->start_ang = start_ang;
51: }
52: if (end_ang == (PetscReal)PETSC_DETERMINE) {
53: ctx->end_ang = 1.0;
54: } else if (end_ang != (PetscReal)PETSC_CURRENT) {
55: PetscCheck(end_ang>=0.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"The left-hand side angle argument must be >= 0.0");
56: PetscCheck(end_ang<=1.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"The left-hand side angle argument must be <= 1.0");
57: ctx->end_ang = end_ang;
58: }
59: #if !defined(PETSC_USE_COMPLEX)
60: PetscCheck(ctx->start_ang+ctx->end_ang==1.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_WRONG,"In real scalars the region must be symmetric wrt real axis");
61: #endif
62: if (width == (PetscReal)PETSC_DETERMINE) {
63: ctx->width = 0.1;
64: } else if (width != (PetscReal)PETSC_CURRENT) {
65: PetscCheck(width>0.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"The width argument must be > 0.0");
66: ctx->width = width;
67: }
68: PetscFunctionReturn(PETSC_SUCCESS);
69: }
71: /*@
72: RGRingSetParameters - Sets the parameters defining the ring region.
74: Logically Collective
76: Input Parameters:
77: + rg - the region context
78: . center - center of the ellipse
79: . radius - radius of the ellipse
80: . vscale - vertical scale of the ellipse
81: . start_ang - the right-hand side angle
82: . end_ang - the left-hand side angle
83: - width - width of the ring
85: Options Database Keys:
86: + -rg_ring_center - Sets the center
87: . -rg_ring_radius - Sets the radius
88: . -rg_ring_vscale - Sets the vertical scale
89: . -rg_ring_startangle - Sets the right-hand side angle
90: . -rg_ring_endangle - Sets the left-hand side angle
91: - -rg_ring_width - Sets the width of the ring
93: Notes:
94: The values of center, radius and vscale have the same meaning as in the
95: ellipse region. The startangle and endangle define the span of the ring
96: (by default it is the whole ring), while the width is the separation
97: between the two concentric ellipses (above and below the radius by
98: width/2).
100: The start and end angles are expressed as a fraction of the circumference.
101: The allowed range is [0..1], with 0 corresponding to 0 radians, 0.25 to
102: pi/2 radians, and so on. It is allowed to have startangle>endangle, in
103: which case the ring region crosses over the zero angle.
105: In the case of complex scalars, a complex center can be provided in the
106: command line with [+/-][realnumber][+/-]realnumberi with no spaces, e.g.
107: -rg_ring_center 1.0+2.0i
109: When PETSc is built with real scalars, the center is restricted to a real value,
110: and the start and end angles must be such that the region is symmetric with
111: respect to the real axis.
113: For all arguments except center, you can use PETSC_CURRENT to keep the current
114: value, and PETSC_DETERMINE to set them to a default value (1 for radius, vscale,
115: end_ang, 0 for start_and, and 0.1 for width).
117: Level: advanced
119: .seealso: RGRingGetParameters()
120: @*/
121: PetscErrorCode RGRingSetParameters(RG rg,PetscScalar center,PetscReal radius,PetscReal vscale,PetscReal start_ang,PetscReal end_ang,PetscReal width)
122: {
123: PetscFunctionBegin;
131: PetscTryMethod(rg,"RGRingSetParameters_C",(RG,PetscScalar,PetscReal,PetscReal,PetscReal,PetscReal,PetscReal),(rg,center,radius,vscale,start_ang,end_ang,width));
132: PetscFunctionReturn(PETSC_SUCCESS);
133: }
135: static PetscErrorCode RGRingGetParameters_Ring(RG rg,PetscScalar *center,PetscReal *radius,PetscReal *vscale,PetscReal *start_ang,PetscReal *end_ang,PetscReal *width)
136: {
137: RG_RING *ctx = (RG_RING*)rg->data;
139: PetscFunctionBegin;
140: if (center) *center = ctx->center;
141: if (radius) *radius = ctx->radius;
142: if (vscale) *vscale = ctx->vscale;
143: if (start_ang) *start_ang = ctx->start_ang;
144: if (end_ang) *end_ang = ctx->end_ang;
145: if (width) *width = ctx->width;
146: PetscFunctionReturn(PETSC_SUCCESS);
147: }
149: /*@
150: RGRingGetParameters - Gets the parameters that define the ring region.
152: Not Collective
154: Input Parameter:
155: . rg - the region context
157: Output Parameters:
158: + center - center of the region
159: . radius - radius of the region
160: . vscale - vertical scale of the region
161: . start_ang - the right-hand side angle
162: . end_ang - the left-hand side angle
163: - width - width of the ring
165: Level: advanced
167: .seealso: RGRingSetParameters()
168: @*/
169: PetscErrorCode RGRingGetParameters(RG rg,PetscScalar *center,PetscReal *radius,PetscReal *vscale,PetscReal *start_ang,PetscReal *end_ang,PetscReal *width)
170: {
171: PetscFunctionBegin;
173: PetscUseMethod(rg,"RGRingGetParameters_C",(RG,PetscScalar*,PetscReal*,PetscReal*,PetscReal*,PetscReal*,PetscReal*),(rg,center,radius,vscale,start_ang,end_ang,width));
174: PetscFunctionReturn(PETSC_SUCCESS);
175: }
177: static PetscErrorCode RGView_Ring(RG rg,PetscViewer viewer)
178: {
179: RG_RING *ctx = (RG_RING*)rg->data;
180: int winw,winh;
181: PetscBool isdraw,isascii;
182: PetscDraw draw;
183: PetscDrawAxis axis;
184: PetscReal cx,cy,radius,width,ab,cd,lx,ly,w,end_ang,x1,y1,x2,y2,r,theta,scale=1.2;
185: char str[50];
187: PetscFunctionBegin;
188: PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERDRAW,&isdraw));
189: PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
190: if (isascii) {
191: PetscCall(SlepcSNPrintfScalar(str,sizeof(str),ctx->center,PETSC_FALSE));
192: PetscCall(PetscViewerASCIIPrintf(viewer," center: %s, radius: %g, vscale: %g, start angle: %g, end angle: %g, ring width: %g\n",str,RGShowReal(ctx->radius),RGShowReal(ctx->vscale),(double)ctx->start_ang,(double)ctx->end_ang,(double)ctx->width));
193: } else if (isdraw) {
194: PetscCall(PetscViewerDrawGetDraw(viewer,0,&draw));
195: PetscCall(PetscDrawCheckResizedWindow(draw));
196: PetscCall(PetscDrawGetWindowSize(draw,&winw,&winh));
197: winw = PetscMax(winw,1); winh = PetscMax(winh,1);
198: PetscCall(PetscDrawClear(draw));
199: PetscCall(PetscDrawSetTitle(draw,"Ring region"));
200: PetscCall(PetscDrawAxisCreate(draw,&axis));
201: cx = PetscRealPart(ctx->center)*rg->sfactor;
202: cy = PetscImaginaryPart(ctx->center)*rg->sfactor;
203: radius = ctx->radius*rg->sfactor;
204: width = ctx->width*rg->sfactor;
205: lx = 2*(radius+width);
206: ly = 2*(radius+width)*ctx->vscale;
207: ab = cx;
208: cd = cy;
209: w = scale*PetscMax(lx/winw,ly/winh)/2;
210: PetscCall(PetscDrawAxisSetLimits(axis,ab-w*winw,ab+w*winw,cd-w*winh,cd+w*winh));
211: PetscCall(PetscDrawAxisDraw(axis));
212: PetscCall(PetscDrawAxisDestroy(&axis));
213: /* draw outer ellipse */
214: PetscCall(PetscDrawEllipse(draw,cx,cy,2*(radius+width),2*(radius+width)*ctx->vscale,PETSC_DRAW_ORANGE));
215: /* remove inner part */
216: PetscCall(PetscDrawEllipse(draw,cx,cy,2*(radius-width),2*(radius-width)*ctx->vscale,PETSC_DRAW_WHITE));
217: if (ctx->start_ang!=ctx->end_ang) {
218: /* remove section from end_ang to start_ang */
219: end_ang = (ctx->start_ang<ctx->end_ang)? ctx->end_ang-1: ctx->end_ang;
220: theta = end_ang;
221: r = scale*(radius+width);
222: if (ctx->vscale>1) r *= ctx->vscale;
223: x1 = PetscMin(PetscMax(ab+r*PetscCosReal(2.0*PETSC_PI*theta),ab-w*winw),ab+w*winw);
224: y1 = PetscMin(PetscMax(cd+r*PetscSinReal(2.0*PETSC_PI*theta),cd-w*winh),cd+w*winh);
225: do {
226: theta = PetscMin(PetscFloorReal(8*theta+1)/8,ctx->start_ang);
227: x2 = PetscMin(PetscMax(ab+r*PetscCosReal(2.0*PETSC_PI*theta),ab-w*winw),ab+w*winw);
228: y2 = PetscMin(PetscMax(cd+r*PetscSinReal(2.0*PETSC_PI*theta),cd-w*winh),cd+w*winh);
229: PetscCall(PetscDrawTriangle(draw,cx,cy,x1,y1,x2,y2,PETSC_DRAW_WHITE,PETSC_DRAW_WHITE,PETSC_DRAW_WHITE));
230: x1 = x2; y1 = y2;
231: } while (theta<ctx->start_ang);
232: }
233: PetscCall(PetscDrawFlush(draw));
234: PetscCall(PetscDrawSave(draw));
235: PetscCall(PetscDrawPause(draw));
236: }
237: PetscFunctionReturn(PETSC_SUCCESS);
238: }
240: static PetscErrorCode RGIsTrivial_Ring(RG rg,PetscBool *trivial)
241: {
242: RG_RING *ctx = (RG_RING*)rg->data;
244: PetscFunctionBegin;
245: if (rg->complement) *trivial = PetscNot(ctx->radius);
246: else *trivial = PetscNot(ctx->radius<PETSC_MAX_REAL);
247: PetscFunctionReturn(PETSC_SUCCESS);
248: }
250: static PetscErrorCode RGComputeContour_Ring(RG rg,PetscInt n,PetscScalar *cr,PetscScalar *ci)
251: {
252: RG_RING *ctx = (RG_RING*)rg->data;
253: PetscReal theta,start_ang;
254: PetscInt i,n2=n/2;
256: PetscFunctionBegin;
257: start_ang = (ctx->start_ang>ctx->end_ang)? ctx->start_ang-1: ctx->start_ang;
258: for (i=0;i<n;i++) {
259: if (i < n2) {
260: theta = ((ctx->end_ang-start_ang)*i/n2 + start_ang)*2.0*PETSC_PI;
261: #if defined(PETSC_USE_COMPLEX)
262: cr[i] = ctx->center + (ctx->radius+ctx->width/2.0)*PetscCMPLX(PetscCosReal(theta),ctx->vscale*PetscSinReal(theta));
263: #else
264: if (cr) cr[i] = ctx->center + (ctx->radius+ctx->width/2.0)*PetscCosReal(theta);
265: if (ci) ci[i] = (ctx->radius+ctx->width/2.0)*ctx->vscale*PetscSinReal(theta);
266: #endif
267: } else {
268: theta = ((ctx->end_ang-start_ang)*(n-i)/n2 + start_ang)*2.0*PETSC_PI;
269: #if defined(PETSC_USE_COMPLEX)
270: cr[i] = ctx->center + (ctx->radius-ctx->width/2.0)*PetscCMPLX(PetscCosReal(theta),ctx->vscale*PetscSinReal(theta));
271: #else
272: if (cr) cr[i] = ctx->center + (ctx->radius-ctx->width/2.0)*PetscCosReal(theta);
273: if (ci) ci[i] = (ctx->radius-ctx->width/2.0)*ctx->vscale*PetscSinReal(theta);
274: #endif
275: }
276: }
277: PetscFunctionReturn(PETSC_SUCCESS);
278: }
280: static PetscErrorCode RGComputeBoundingBox_Ring(RG rg,PetscReal *a,PetscReal *b,PetscReal *c,PetscReal *d)
281: {
282: RG_RING *ctx = (RG_RING*)rg->data;
284: PetscFunctionBegin;
285: /* current implementation does not return a tight bounding box */
286: if (a) *a = PetscRealPart(ctx->center) - (ctx->radius+ctx->width/2.0);
287: if (b) *b = PetscRealPart(ctx->center) + (ctx->radius+ctx->width/2.0);
288: if (c) *c = PetscImaginaryPart(ctx->center) - (ctx->radius+ctx->width/2.0)*ctx->vscale;
289: if (d) *d = PetscImaginaryPart(ctx->center) + (ctx->radius+ctx->width/2.0)*ctx->vscale;
290: PetscFunctionReturn(PETSC_SUCCESS);
291: }
293: static PetscErrorCode RGComputeQuadrature_Ring(RG rg,RGQuadRule quad,PetscInt n,PetscScalar *z,PetscScalar *zn,PetscScalar *w)
294: {
295: RG_RING *ctx = (RG_RING*)rg->data;
296: PetscReal max_w=0.0;
297: PetscScalar tmp,tmp2;
298: PetscInt i,j;
300: PetscFunctionBegin;
301: if (quad == RG_QUADRULE_CHEBYSHEV) {
302: #if defined(PETSC_USE_COMPLEX)
303: PetscReal theta;
304: for (i=0;i<n;i++) {
305: theta = PETSC_PI*(i+0.5)/n;
306: zn[i] = PetscCosReal(theta);
307: w[i] = PetscCosReal((n-1)*theta)/n;
308: theta = (ctx->start_ang*2.0+(ctx->end_ang-ctx->start_ang)*(PetscRealPart(zn[i])+1.0))*PETSC_PI;
309: z[i] = rg->sfactor*(ctx->center + ctx->radius*PetscCMPLX(PetscCosReal(theta),ctx->vscale*PetscSinReal(theta)));
310: }
311: #else
312: SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Integration points on a vertical line require complex arithmetic");
313: #endif
314: } else { /* RG_QUADRULE_TRAPEZOIDAL */
315: for (i=0;i<n;i++) {
316: zn[i] = (z[i]-rg->sfactor*ctx->center)/(rg->sfactor*ctx->radius);
317: tmp = 1.0; tmp2 = 1.0;
318: for (j=0;j<n;j++) {
319: tmp *= z[j];
320: if (i != j) tmp2 *= z[j]-z[i];
321: }
322: w[i] = tmp/tmp2;
323: max_w = PetscMax(PetscAbsScalar(w[i]),max_w);
324: }
325: for (i=0;i<n;i++) w[i] /= (PetscScalar)max_w;
326: }
327: PetscFunctionReturn(PETSC_SUCCESS);
328: }
330: static PetscErrorCode RGCheckInside_Ring(RG rg,PetscReal px,PetscReal py,PetscInt *inside)
331: {
332: RG_RING *ctx = (RG_RING*)rg->data;
333: PetscReal dx,dy,r;
335: PetscFunctionBegin;
336: /* outer ellipse */
337: #if defined(PETSC_USE_COMPLEX)
338: dx = (px-PetscRealPart(ctx->center))/(ctx->radius+ctx->width/2.0);
339: dy = (py-PetscImaginaryPart(ctx->center))/(ctx->radius+ctx->width/2.0);
340: #else
341: dx = (px-ctx->center)/(ctx->radius+ctx->width/2.0);
342: dy = py/(ctx->radius+ctx->width/2.0);
343: #endif
344: r = 1.0-dx*dx-(dy*dy)/(ctx->vscale*ctx->vscale);
345: *inside = PetscSign(r);
346: /* inner ellipse */
347: #if defined(PETSC_USE_COMPLEX)
348: dx = (px-PetscRealPart(ctx->center))/(ctx->radius-ctx->width/2.0);
349: dy = (py-PetscImaginaryPart(ctx->center))/(ctx->radius-ctx->width/2.0);
350: #else
351: dx = (px-ctx->center)/(ctx->radius-ctx->width/2.0);
352: dy = py/(ctx->radius-ctx->width/2.0);
353: #endif
354: r = -1.0+dx*dx+(dy*dy)/(ctx->vscale*ctx->vscale);
355: *inside *= PetscSign(r);
356: if (*inside == 1) { /* check angles */
357: #if defined(PETSC_USE_COMPLEX)
358: dx = (px-PetscRealPart(ctx->center));
359: dy = (py-PetscImaginaryPart(ctx->center));
360: #else
361: dx = px-ctx->center;
362: dy = py;
363: #endif
364: if (dx == 0) {
365: if (dy == 0) r = -1;
366: else if (dy > 0) r = 0.25;
367: else r = 0.75;
368: } else if (dx > 0) {
369: r = PetscAtanReal((dy/ctx->vscale)/dx);
370: if (dy >= 0) r /= 2*PETSC_PI;
371: else r = r/(2*PETSC_PI)+1;
372: } else r = PetscAtanReal((dy/ctx->vscale)/dx)/(2*PETSC_PI)+0.5;
373: if (ctx->start_ang>ctx->end_ang) {
374: if (r>ctx->end_ang && r<ctx->start_ang) *inside = -1;
375: } else {
376: if (r<ctx->start_ang || r>ctx->end_ang) *inside = -1;
377: }
378: }
379: PetscFunctionReturn(PETSC_SUCCESS);
380: }
382: static PetscErrorCode RGIsAxisymmetric_Ring(RG rg,PetscBool vertical,PetscBool *symm)
383: {
384: RG_RING *ctx = (RG_RING*)rg->data;
386: PetscFunctionBegin;
387: if (vertical) *symm = (PetscRealPart(ctx->center) == 0.0 && PetscAbs(ctx->start_ang+ctx->end_ang-PetscRealConstant(1.0)) == 0.5)? PETSC_TRUE: PETSC_FALSE;
388: else *symm = (PetscImaginaryPart(ctx->center) == 0.0 && ctx->start_ang+ctx->end_ang == 1.0)? PETSC_TRUE: PETSC_FALSE;
389: PetscFunctionReturn(PETSC_SUCCESS);
390: }
392: static PetscErrorCode RGSetFromOptions_Ring(RG rg,PetscOptionItems *PetscOptionsObject)
393: {
394: PetscScalar s;
395: PetscReal r1,r2,r3,r4,r5;
396: PetscBool flg1,flg2,flg3,flg4,flg5,flg6;
398: PetscFunctionBegin;
399: PetscOptionsHeadBegin(PetscOptionsObject,"RG Ring Options");
401: PetscCall(RGRingGetParameters(rg,&s,&r1,&r2,&r3,&r4,&r5));
402: PetscCall(PetscOptionsScalar("-rg_ring_center","Center of ellipse","RGRingSetParameters",s,&s,&flg1));
403: PetscCall(PetscOptionsReal("-rg_ring_radius","Radius of ellipse","RGRingSetParameters",r1,&r1,&flg2));
404: PetscCall(PetscOptionsReal("-rg_ring_vscale","Vertical scale of ellipse","RGRingSetParameters",r2,&r2,&flg3));
405: PetscCall(PetscOptionsReal("-rg_ring_startangle","Right-hand side angle","RGRingSetParameters",r3,&r3,&flg4));
406: PetscCall(PetscOptionsReal("-rg_ring_endangle","Left-hand side angle","RGRingSetParameters",r4,&r4,&flg5));
407: PetscCall(PetscOptionsReal("-rg_ring_width","Width of ring","RGRingSetParameters",r5,&r5,&flg6));
408: if (flg1 || flg2 || flg3 || flg4 || flg5 || flg6) PetscCall(RGRingSetParameters(rg,s,r1,r2,r3,r4,r5));
410: PetscOptionsHeadEnd();
411: PetscFunctionReturn(PETSC_SUCCESS);
412: }
414: static PetscErrorCode RGDestroy_Ring(RG rg)
415: {
416: PetscFunctionBegin;
417: PetscCall(PetscFree(rg->data));
418: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGRingSetParameters_C",NULL));
419: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGRingGetParameters_C",NULL));
420: PetscFunctionReturn(PETSC_SUCCESS);
421: }
423: SLEPC_EXTERN PetscErrorCode RGCreate_Ring(RG rg)
424: {
425: RG_RING *ring;
427: PetscFunctionBegin;
428: PetscCall(PetscNew(&ring));
429: ring->center = 0.0;
430: ring->radius = PETSC_MAX_REAL;
431: ring->vscale = 1.0;
432: ring->start_ang = 0.0;
433: ring->end_ang = 1.0;
434: ring->width = 0.1;
435: rg->data = (void*)ring;
437: rg->ops->istrivial = RGIsTrivial_Ring;
438: rg->ops->computecontour = RGComputeContour_Ring;
439: rg->ops->computebbox = RGComputeBoundingBox_Ring;
440: rg->ops->computequadrature = RGComputeQuadrature_Ring;
441: rg->ops->checkinside = RGCheckInside_Ring;
442: rg->ops->isaxisymmetric = RGIsAxisymmetric_Ring;
443: rg->ops->setfromoptions = RGSetFromOptions_Ring;
444: rg->ops->view = RGView_Ring;
445: rg->ops->destroy = RGDestroy_Ring;
446: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGRingSetParameters_C",RGRingSetParameters_Ring));
447: PetscCall(PetscObjectComposeFunction((PetscObject)rg,"RGRingGetParameters_C",RGRingGetParameters_Ring));
448: PetscFunctionReturn(PETSC_SUCCESS);
449: }