Subversion Repositories slepc-dev

Rev

Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
352 dsic.upv.es!antodo 1
/*
354 dsic.upv.es!jroman 2
      Implements the Cayley spectral transform.
1376 slepc 3
 
4
   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1672 slepc 5
   SLEPc - Scalable Library for Eigenvalue Problem Computations
6
   Copyright (c) 2002-2009, Universidad Politecnica de Valencia, Spain
1376 slepc 7
 
1672 slepc 8
   This file is part of SLEPc.
9
 
10
   SLEPc is free software: you can redistribute it and/or modify it under  the
11
   terms of version 3 of the GNU Lesser General Public License as published by
12
   the Free Software Foundation.
13
 
14
   SLEPc  is  distributed in the hope that it will be useful, but WITHOUT  ANY
15
   WARRANTY;  without even the implied warranty of MERCHANTABILITY or  FITNESS
16
   FOR  A  PARTICULAR PURPOSE. See the GNU Lesser General Public  License  for
17
   more details.
18
 
19
   You  should have received a copy of the GNU Lesser General  Public  License
20
   along with SLEPc. If not, see <http://www.gnu.org/licenses/>.
1376 slepc 21
   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
352 dsic.upv.es!antodo 22
*/
1376 slepc 23
 
1521 slepc 24
#include "private/stimpl.h"          /*I "slepcst.h" I*/
352 dsic.upv.es!antodo 25
 
26
typedef struct {
27
  PetscScalar tau;
359 dsic.upv.es!antodo 28
  PetscTruth  tau_set;
352 dsic.upv.es!antodo 29
  Vec         w2;
30
} ST_CAYLEY;
31
 
32
#undef __FUNCT__  
33
#define __FUNCT__ "STApply_Cayley"
476 dsic.upv.es!antodo 34
PetscErrorCode STApply_Cayley(ST st,Vec x,Vec y)
352 dsic.upv.es!antodo 35
{
476 dsic.upv.es!antodo 36
  PetscErrorCode ierr;
37
  ST_CAYLEY      *ctx = (ST_CAYLEY *) st->data;
38
  PetscScalar    tau = ctx->tau;
352 dsic.upv.es!antodo 39
 
354 dsic.upv.es!jroman 40
  PetscFunctionBegin;
352 dsic.upv.es!antodo 41
  if (st->shift_matrix == STMATMODE_INPLACE) { tau = tau + st->sigma; };
42
 
43
  if (st->B) {
44
    /* generalized eigenproblem: y = (A - sB)^-1 (A + tB)x */
45
    ierr = MatMult(st->A,x,st->w);CHKERRQ(ierr);
46
    ierr = MatMult(st->B,x,ctx->w2);CHKERRQ(ierr);
828 dsic.upv.es!antodo 47
    ierr = VecAXPY(st->w,tau,ctx->w2);CHKERRQ(ierr);    
352 dsic.upv.es!antodo 48
    ierr = STAssociatedKSPSolve(st,st->w,y);CHKERRQ(ierr);
49
  }
50
  else {
51
    /* standard eigenproblem: y = (A - sI)^-1 (A + tI)x */
52
    ierr = MatMult(st->A,x,st->w);CHKERRQ(ierr);
828 dsic.upv.es!antodo 53
    ierr = VecAXPY(st->w,tau,x);CHKERRQ(ierr);
352 dsic.upv.es!antodo 54
    ierr = STAssociatedKSPSolve(st,st->w,y);CHKERRQ(ierr);
55
  }
56
  PetscFunctionReturn(0);
57
}
58
 
59
#undef __FUNCT__  
780 dsic.upv.es!jroman 60
#define __FUNCT__ "STApplyTranspose_Cayley"
61
PetscErrorCode STApplyTranspose_Cayley(ST st,Vec x,Vec y)
62
{
63
  PetscErrorCode ierr;
64
  ST_CAYLEY      *ctx = (ST_CAYLEY *) st->data;
65
  PetscScalar    tau = ctx->tau;
66
 
67
  PetscFunctionBegin;
68
  if (st->shift_matrix == STMATMODE_INPLACE) { tau = tau + st->sigma; };
69
 
70
  if (st->B) {
71
    /* generalized eigenproblem: y = (A + tB)^T (A - sB)^-T x */
72
    ierr = STAssociatedKSPSolve(st,x,st->w);CHKERRQ(ierr);
73
    ierr = MatMult(st->A,st->w,y);CHKERRQ(ierr);
74
    ierr = MatMult(st->B,st->w,ctx->w2);CHKERRQ(ierr);
828 dsic.upv.es!antodo 75
    ierr = VecAXPY(y,tau,ctx->w2);CHKERRQ(ierr);    
780 dsic.upv.es!jroman 76
  }
77
  else {
78
    /* standard eigenproblem: y =  (A + tI)^T (A - sI)^-T x */
79
    ierr = STAssociatedKSPSolve(st,x,st->w);CHKERRQ(ierr);
80
    ierr = MatMult(st->A,st->w,y);CHKERRQ(ierr);
828 dsic.upv.es!antodo 81
    ierr = VecAXPY(y,tau,st->w);CHKERRQ(ierr);
780 dsic.upv.es!jroman 82
  }
83
  PetscFunctionReturn(0);
84
}
85
 
86
#undef __FUNCT__  
1358 slepc 87
#define __FUNCT__ "STBilinearMatMult_Cayley"
88
PetscErrorCode STBilinearMatMult_Cayley(Mat B,Vec x,Vec y)
352 dsic.upv.es!antodo 89
{
476 dsic.upv.es!antodo 90
  PetscErrorCode ierr;
1358 slepc 91
  ST             st;
92
  ST_CAYLEY      *ctx;
93
  PetscScalar    tau;
352 dsic.upv.es!antodo 94
 
354 dsic.upv.es!jroman 95
  PetscFunctionBegin;
1358 slepc 96
  ierr = MatShellGetContext(B,(void**)&st);CHKERRQ(ierr);
97
  ctx = (ST_CAYLEY *) st->data;
98
  tau = ctx->tau;
99
 
352 dsic.upv.es!antodo 100
  if (st->shift_matrix == STMATMODE_INPLACE) { tau = tau + st->sigma; };
101
 
102
  if (st->B) {
103
    /* generalized eigenproblem: y = (A + tB)x */
354 dsic.upv.es!jroman 104
    ierr = MatMult(st->A,x,y);CHKERRQ(ierr);
352 dsic.upv.es!antodo 105
    ierr = MatMult(st->B,x,ctx->w2);CHKERRQ(ierr);
828 dsic.upv.es!antodo 106
    ierr = VecAXPY(y,tau,ctx->w2);CHKERRQ(ierr);    
352 dsic.upv.es!antodo 107
  }
108
  else {
109
    /* standard eigenproblem: y = (A + tI)x */
354 dsic.upv.es!jroman 110
    ierr = MatMult(st->A,x,y);CHKERRQ(ierr);
828 dsic.upv.es!antodo 111
    ierr = VecAXPY(y,tau,x);CHKERRQ(ierr);
352 dsic.upv.es!antodo 112
  }
113
  PetscFunctionReturn(0);
114
}
115
 
116
#undef __FUNCT__  
1358 slepc 117
#define __FUNCT__ "STGetBilinearForm_Cayley"
118
PetscErrorCode STGetBilinearForm_Cayley(ST st,Mat *B)
119
{
120
  PetscErrorCode ierr;
121
  PetscInt       n,m;
122
 
123
  PetscFunctionBegin;
124
  ierr = MatGetLocalSize(st->B,&n,&m);CHKERRQ(ierr);
1422 slepc 125
  ierr = MatCreateShell(((PetscObject)st)->comm,n,m,PETSC_DETERMINE,PETSC_DETERMINE,st,B);CHKERRQ(ierr);
1358 slepc 126
  ierr = MatShellSetOperation(*B,MATOP_MULT,(void(*)(void))STBilinearMatMult_Cayley);CHKERRQ(ierr);  
127
  PetscFunctionReturn(0);
128
}
129
 
130
#undef __FUNCT__  
352 dsic.upv.es!antodo 131
#define __FUNCT__ "STBackTransform_Cayley"
476 dsic.upv.es!antodo 132
PetscErrorCode STBackTransform_Cayley(ST st,PetscScalar *eigr,PetscScalar *eigi)
352 dsic.upv.es!antodo 133
{
134
  ST_CAYLEY   *ctx = (ST_CAYLEY *) st->data;
135
#ifndef PETSC_USE_COMPLEX
136
  PetscScalar t,i,r;
137
  PetscFunctionBegin;
138
  PetscValidPointer(eigr,2);
139
  PetscValidPointer(eigi,3);
354 dsic.upv.es!jroman 140
  if (*eigi == 0.0) *eigr = (ctx->tau + *eigr * st->sigma) / (*eigr - 1.0);
352 dsic.upv.es!antodo 141
  else {
142
    r = *eigr;
143
    i = *eigi;
144
    r = st->sigma * (r * r + i * i - r) + ctx->tau * (r - 1);
145
    i = - st->sigma * i - ctx->tau * i;
146
    t = i * i + r * (r - 2.0) + 1.0;    
147
    *eigr = r / t;
148
    *eigi = i / t;    
149
  }
150
#else
151
  PetscFunctionBegin;
152
  PetscValidPointer(eigr,2);
153
  *eigr = (ctx->tau + *eigr * st->sigma) / (*eigr - 1.0);
154
#endif
155
  PetscFunctionReturn(0);
156
}
157
 
158
#undef __FUNCT__  
1029 slepc 159
#define __FUNCT__ "STPostSolve_Cayley"
160
PetscErrorCode STPostSolve_Cayley(ST st)
352 dsic.upv.es!antodo 161
{
476 dsic.upv.es!antodo 162
  PetscErrorCode ierr;
352 dsic.upv.es!antodo 163
 
164
  PetscFunctionBegin;
165
  if (st->shift_matrix == STMATMODE_INPLACE) {
828 dsic.upv.es!antodo 166
    if (st->B) {
167
      ierr = MatAXPY(st->A,st->sigma,st->B,st->str);CHKERRQ(ierr);
168
    } else {
169
      ierr = MatShift(st->A,st->sigma); CHKERRQ(ierr);
170
    }
352 dsic.upv.es!antodo 171
    st->setupcalled = 0;
172
  }
173
  PetscFunctionReturn(0);
174
}
175
 
176
#undef __FUNCT__  
177
#define __FUNCT__ "STSetUp_Cayley"
476 dsic.upv.es!antodo 178
PetscErrorCode STSetUp_Cayley(ST st)
352 dsic.upv.es!antodo 179
{
476 dsic.upv.es!antodo 180
  PetscErrorCode ierr;
181
  ST_CAYLEY      *ctx = (ST_CAYLEY *) st->data;
352 dsic.upv.es!antodo 182
 
183
  PetscFunctionBegin;
184
 
359 dsic.upv.es!antodo 185
  if (st->mat) { ierr = MatDestroy(st->mat);CHKERRQ(ierr); }
186
  if (!ctx->tau_set) { ctx->tau = st->sigma; }
187
  if (ctx->tau == 0.0 &&  st->sigma == 0.0) {
188
    SETERRQ(1,"Values of shift and antishift cannot be zero simultaneously");
189
  }
190
 
352 dsic.upv.es!antodo 191
  switch (st->shift_matrix) {
192
  case STMATMODE_INPLACE:
359 dsic.upv.es!antodo 193
    st->mat = PETSC_NULL;
352 dsic.upv.es!antodo 194
    if (st->sigma != 0.0) {
195
      if (st->B) {
828 dsic.upv.es!antodo 196
        ierr = MatAXPY(st->A,-st->sigma,st->B,st->str);CHKERRQ(ierr);
352 dsic.upv.es!antodo 197
      } else {
828 dsic.upv.es!antodo 198
        ierr = MatShift(st->A,-st->sigma);CHKERRQ(ierr);
352 dsic.upv.es!antodo 199
      }
200
    }
1241 slepc 201
    ierr = KSPSetOperators(st->ksp,st->A,st->A,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr);
352 dsic.upv.es!antodo 202
    break;
203
  case STMATMODE_SHELL:
204
    ierr = STMatShellCreate(st,&st->mat);CHKERRQ(ierr);
205
    ierr = KSPSetOperators(st->ksp,st->mat,st->mat,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr);
206
    break;
207
  default:
208
    ierr = MatDuplicate(st->A,MAT_COPY_VALUES,&st->mat);CHKERRQ(ierr);
209
    if (st->sigma != 0.0) {
210
      if (st->B) {
828 dsic.upv.es!antodo 211
        ierr = MatAXPY(st->mat,-st->sigma,st->B,st->str);CHKERRQ(ierr);
352 dsic.upv.es!antodo 212
      } else {
828 dsic.upv.es!antodo 213
        ierr = MatShift(st->mat,-st->sigma);CHKERRQ(ierr);
352 dsic.upv.es!antodo 214
      }
215
    }
1241 slepc 216
    ierr = KSPSetOperators(st->ksp,st->mat,st->mat,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr);
352 dsic.upv.es!antodo 217
  }
218
  if (st->B) {
219
   if (ctx->w2) { ierr = VecDestroy(ctx->w2);CHKERRQ(ierr); }
220
   ierr = MatGetVecs(st->B,&ctx->w2,PETSC_NULL);CHKERRQ(ierr);
359 dsic.upv.es!antodo 221
  }
352 dsic.upv.es!antodo 222
  ierr = KSPSetUp(st->ksp);CHKERRQ(ierr);
223
  PetscFunctionReturn(0);
224
}
225
 
226
#undef __FUNCT__  
227
#define __FUNCT__ "STSetShift_Cayley"
476 dsic.upv.es!antodo 228
PetscErrorCode STSetShift_Cayley(ST st,PetscScalar newshift)
352 dsic.upv.es!antodo 229
{
476 dsic.upv.es!antodo 230
  PetscErrorCode ierr;
231
  ST_CAYLEY      *ctx = (ST_CAYLEY *) st->data;
1241 slepc 232
  MatStructure   flg;
352 dsic.upv.es!antodo 233
 
234
  PetscFunctionBegin;
1241 slepc 235
  if (!ctx->tau_set) { ctx->tau = newshift; }
236
  if (ctx->tau == 0.0 &&  newshift == 0.0) {
237
    SETERRQ(1,"Values of shift and antishift cannot be zero simultaneously");
238
  }
352 dsic.upv.es!antodo 239
 
240
  /* Nothing to be done if STSetUp has not been called yet */
241
  if (!st->setupcalled) PetscFunctionReturn(0);
242
 
1241 slepc 243
  /* Check if the new KSP matrix has the same zero structure */
244
  if (st->B && st->str == DIFFERENT_NONZERO_PATTERN && (st->sigma == 0.0 || newshift == 0.0)) {
245
    flg = DIFFERENT_NONZERO_PATTERN;
246
  } else {
247
    flg = SAME_NONZERO_PATTERN;
248
  }
249
 
352 dsic.upv.es!antodo 250
  switch (st->shift_matrix) {
251
  case STMATMODE_INPLACE:
252
    /* Undo previous operations */
253
    if (st->sigma != 0.0) {
828 dsic.upv.es!antodo 254
      if (st->B) {
255
        ierr = MatAXPY(st->A,st->sigma,st->B,st->str);CHKERRQ(ierr);
256
      } else {
257
        ierr = MatShift(st->A,st->sigma);CHKERRQ(ierr);
258
      }
352 dsic.upv.es!antodo 259
    }
260
    /* Apply new shift */
261
    if (newshift != 0.0) {
828 dsic.upv.es!antodo 262
      if (st->B) {
263
        ierr = MatAXPY(st->A,-newshift,st->B,st->str);CHKERRQ(ierr);
264
      } else {
265
        ierr = MatShift(st->A,-newshift);CHKERRQ(ierr);
266
      }
352 dsic.upv.es!antodo 267
    }
1241 slepc 268
    ierr = KSPSetOperators(st->ksp,st->A,st->A,flg);CHKERRQ(ierr);
352 dsic.upv.es!antodo 269
    break;
270
  case STMATMODE_SHELL:
1241 slepc 271
    ierr = KSPSetOperators(st->ksp,st->mat,st->mat,DIFFERENT_NONZERO_PATTERN);CHKERRQ(ierr);    
352 dsic.upv.es!antodo 272
    break;
273
  default:
354 dsic.upv.es!jroman 274
    ierr = MatCopy(st->A, st->mat,SUBSET_NONZERO_PATTERN); CHKERRQ(ierr);
352 dsic.upv.es!antodo 275
    if (newshift != 0.0) {  
828 dsic.upv.es!antodo 276
      if (st->B) { ierr = MatAXPY(st->mat,-newshift,st->B,st->str);CHKERRQ(ierr); }
277
      else { ierr = MatShift(st->mat,-newshift);CHKERRQ(ierr); }
352 dsic.upv.es!antodo 278
    }
1241 slepc 279
    ierr = KSPSetOperators(st->ksp,st->mat,st->mat,flg);CHKERRQ(ierr);    
352 dsic.upv.es!antodo 280
  }
281
  st->sigma = newshift;
282
  ierr = KSPSetUp(st->ksp);CHKERRQ(ierr);
283
  PetscFunctionReturn(0);
284
}
285
 
286
#undef __FUNCT__  
359 dsic.upv.es!antodo 287
#define __FUNCT__ "STSetFromOptions_Cayley"
476 dsic.upv.es!antodo 288
PetscErrorCode STSetFromOptions_Cayley(ST st)
289
{
290
  PetscErrorCode ierr;
291
  PetscScalar    tau;
292
  PetscTruth     flg;
293
  ST_CAYLEY      *ctx = (ST_CAYLEY *) st->data;
359 dsic.upv.es!antodo 294
 
295
  PetscFunctionBegin;
296
  ierr = PetscOptionsHead("ST Cayley Options");CHKERRQ(ierr);
297
  ierr = PetscOptionsScalar("-st_antishift","Value of the antishift","STSetAntishift",ctx->tau,&tau,&flg); CHKERRQ(ierr);
298
  if (flg) {
299
    ierr = STCayleySetAntishift(st,tau);CHKERRQ(ierr);
300
  }
301
  ierr = PetscOptionsTail();CHKERRQ(ierr);
302
  PetscFunctionReturn(0);
303
}
304
 
305
EXTERN_C_BEGIN
306
#undef __FUNCT__  
307
#define __FUNCT__ "STCayleySetAntishift_Cayley"
476 dsic.upv.es!antodo 308
PetscErrorCode STCayleySetAntishift_Cayley(ST st,PetscScalar newshift)
359 dsic.upv.es!antodo 309
{
310
  ST_CAYLEY *ctx = (ST_CAYLEY *) st->data;
311
 
312
  PetscFunctionBegin;
313
  ctx->tau = newshift;
314
  ctx->tau_set = PETSC_TRUE;
315
  PetscFunctionReturn(0);
316
}
317
EXTERN_C_END
318
 
319
#undef __FUNCT__  
352 dsic.upv.es!antodo 320
#define __FUNCT__ "STCayleySetAntishift"
553 dsic.upv.es!jroman 321
/*@
322
   STCayleySetAntishift - Sets the value of the anti-shift for the Cayley
323
   spectral transformation.
324
 
325
   Collective on ST
326
 
327
   Input Parameters:
328
+  st  - the spectral transformation context
329
-  tau - the anti-shift
330
 
331
   Options Database Key:
332
.  -st_antishift - Sets the value of the anti-shift
333
 
334
   Level: intermediate
335
 
336
   Note:
337
   In the generalized Cayley transform, the operator can be expressed as
338
   OP = inv(A - sigma B)*(A + tau B). This function sets the value of tau.
339
   Use STSetShift() for setting sigma.
340
 
341
.seealso: STSetShift()
342
@*/
343
PetscErrorCode STCayleySetAntishift(ST st,PetscScalar tau)
352 dsic.upv.es!antodo 344
{
476 dsic.upv.es!antodo 345
  PetscErrorCode ierr, (*f)(ST,PetscScalar);
359 dsic.upv.es!antodo 346
 
347
  PetscFunctionBegin;
348
  PetscValidHeaderSpecific(st,ST_COOKIE,1);
349
  ierr = PetscObjectQueryFunction((PetscObject)st,"STCayleySetAntishift_C",(void (**)(void))&f);CHKERRQ(ierr);
350
  if (f) {
553 dsic.upv.es!jroman 351
    ierr = (*f)(st,tau);CHKERRQ(ierr);
359 dsic.upv.es!antodo 352
  }
353
  PetscFunctionReturn(0);
354
}
355
 
356
#undef __FUNCT__  
357
#define __FUNCT__ "STView_Cayley"
476 dsic.upv.es!antodo 358
PetscErrorCode STView_Cayley(ST st,PetscViewer viewer)
359 dsic.upv.es!antodo 359
{
476 dsic.upv.es!antodo 360
  PetscErrorCode ierr;
361
  ST_CAYLEY      *ctx = (ST_CAYLEY *) st->data;
352 dsic.upv.es!antodo 362
 
363
  PetscFunctionBegin;
359 dsic.upv.es!antodo 364
#if !defined(PETSC_USE_COMPLEX)
365
  ierr = PetscViewerASCIIPrintf(viewer,"  antishift: %g\n",ctx->tau);CHKERRQ(ierr);
366
#else
367
  ierr = PetscViewerASCIIPrintf(viewer,"  antishift: %g+%g i\n",PetscRealPart(ctx->tau),PetscImaginaryPart(ctx->tau));CHKERRQ(ierr);
368
#endif
438 dsic.upv.es!antodo 369
  ierr = STView_Default(st,viewer);CHKERRQ(ierr);
352 dsic.upv.es!antodo 370
  PetscFunctionReturn(0);
371
}
372
 
373
#undef __FUNCT__  
374
#define __FUNCT__ "STDestroy_Cayley"
476 dsic.upv.es!antodo 375
PetscErrorCode STDestroy_Cayley(ST st)
352 dsic.upv.es!antodo 376
{
476 dsic.upv.es!antodo 377
  PetscErrorCode ierr;
378
  ST_CAYLEY      *ctx = (ST_CAYLEY *) st->data;
352 dsic.upv.es!antodo 379
 
380
  PetscFunctionBegin;
381
  if (ctx->w2) { ierr = VecDestroy(ctx->w2);CHKERRQ(ierr); }
382
  ierr = PetscFree(ctx);CHKERRQ(ierr);
383
  PetscFunctionReturn(0);
384
}
385
 
386
EXTERN_C_BEGIN
387
#undef __FUNCT__  
388
#define __FUNCT__ "STCreate_Cayley"
476 dsic.upv.es!antodo 389
PetscErrorCode STCreate_Cayley(ST st)
352 dsic.upv.es!antodo 390
{
476 dsic.upv.es!antodo 391
  PetscErrorCode ierr;
392
  ST_CAYLEY      *ctx;
352 dsic.upv.es!antodo 393
 
394
  PetscFunctionBegin;
395
  ierr = PetscNew(ST_CAYLEY,&ctx); CHKERRQ(ierr);
396
  PetscLogObjectMemory(st,sizeof(ST_CAYLEY));
1358 slepc 397
  st->data                 = (void *) ctx;
352 dsic.upv.es!antodo 398
 
1358 slepc 399
  st->ops->apply           = STApply_Cayley;
400
  st->ops->getbilinearform = STGetBilinearForm_Cayley;
401
  st->ops->applytrans      = STApplyTranspose_Cayley;
402
  st->ops->postsolve       = STPostSolve_Cayley;
403
  st->ops->backtr          = STBackTransform_Cayley;
404
  st->ops->setfromoptions  = STSetFromOptions_Cayley;
405
  st->ops->setup           = STSetUp_Cayley;
406
  st->ops->setshift        = STSetShift_Cayley;
407
  st->ops->destroy         = STDestroy_Cayley;
408
  st->ops->view            = STView_Cayley;
352 dsic.upv.es!antodo 409
 
410
  st->checknullspace      = STCheckNullSpace_Default;
411
 
412
  ctx->tau                = 0.0;
359 dsic.upv.es!antodo 413
  ctx->tau_set            = PETSC_FALSE;
352 dsic.upv.es!antodo 414
 
359 dsic.upv.es!antodo 415
  ierr = PetscObjectComposeFunctionDynamic((PetscObject)st,"STCayleySetAntishift_C","STCayleySetAntishift_Cayley",
416
                    STCayleySetAntishift_Cayley);CHKERRQ(ierr);
417
 
352 dsic.upv.es!antodo 418
  PetscFunctionReturn(0);
419
}
420
EXTERN_C_END
421