1 /*
2
3 PhyML: a program that computes maximum likelihood phylogenies from
4 DNA or AA homologous sequences.
5
6 Copyright (C) Stephane Guindon. Oct 2003 onward.
7
8 All parts of the source except where indicated are distributed under
9 the GNU public licence. See http://www.opensource.org for details.
10
11 */
12
13 #include "spr.h"
14 #include "utilities.h"
15 #include "lk.h"
16 #include "optimiz.h"
17 #include "bionj.h"
18 #include "models.h"
19 #include "free.h"
20 #include "help.h"
21 #include "simu.h"
22 #include "eigen.h"
23 #include "pars.h"
24 #include "alrt.h"
25 #include "mixt.h"
26 #include "invitee.h"
27
28 #ifdef MPI
29 #include "mpi_boot.h"
30 #endif
31
32 #ifdef BEAGLE
33 #include "beagle_utils.h"
34 #endif
35
36
37
38 #if (defined PHYML || EVOLVE)
39
main(int argc,char ** argv)40 int main(int argc, char **argv)
41 {
42 calign *cdata;
43 option *io;
44 t_tree *tree;
45 int num_data_set;
46 int num_tree,num_rand_tree;
47 t_mod *mod;
48 time_t t_beg,t_end;
49 phydbl best_lnL;
50 int r_seed;
51 char *most_likely_tree=NULL;
52 int orig_random_input_tree;
53
54
55 #ifdef MPI
56 int rc;
57 rc = MPI_Init(&argc,&argv);
58 if (rc != MPI_SUCCESS)
59 {
60 PhyML_Fprintf(stderr,"\n. Err. starting MPI program. Terminating.\n");
61 MPI_Abort(MPI_COMM_WORLD, rc);
62 }
63 if(MPI_Comm_size(MPI_COMM_WORLD,&Global_numTask) != MPI_SUCCESS) MPI_Abort(MPI_COMM_WORLD, rc);
64 if(MPI_Comm_rank(MPI_COMM_WORLD,&Global_myRank) != MPI_SUCCESS) MPI_Abort(MPI_COMM_WORLD, rc);
65 PhyML_Fprintf(stdout,"\n\n. Running the analysis on %d CPU%s.",
66 Global_numTask,
67 Global_numTask>1?"s.":".");
68 #endif
69
70 #ifdef QUIET
71 setvbuf(stdout,NULL,_IOFBF,2048);
72 #endif
73
74 tree = NULL;
75 mod = NULL;
76 best_lnL = UNLIKELY;
77
78
79 io = (option *)Get_Input(argc,argv);
80 if(!io) return(0);
81 else if(io->use_xml == YES)
82 {
83 Free(io);
84 return(0);
85 }
86
87 #ifdef EVOLVE
88 io->colalias = NO;
89 #endif
90
91 r_seed = (io->r_seed < 0)?(time(NULL)):(io->r_seed);
92 srand(r_seed);
93 io->r_seed = r_seed;
94
95 if(io->in_tree == 2) Test_Multiple_Data_Set_Format(io);
96 else io->n_trees = 1;
97
98 if(io->n_trees == 0 && io->in_tree == 2)
99 {
100 PhyML_Printf("\n== Err.: the input tree file does not provide a tree in valid format.");
101 Exit("\n");
102 }
103
104 if((io->n_data_sets > 1) && (io->n_trees > 1))
105 {
106 io->n_data_sets = MIN(io->n_trees,io->n_data_sets);
107 io->n_trees = MIN(io->n_trees,io->n_data_sets);
108 }
109
110 for(num_data_set=0;num_data_set<io->n_data_sets;num_data_set++)
111 {
112 best_lnL = UNLIKELY;
113 Get_Seq(io);
114 Make_Model_Complete(io->mod);
115 Set_Model_Name(io->mod);
116 Print_Settings(io);
117 mod = io->mod;
118 orig_random_input_tree = io->mod->s_opt->random_input_tree;
119
120
121 if(io->data)
122 {
123 if(io->n_data_sets > 1) PhyML_Printf("\n. Data set [#%d]\n",num_data_set+1);
124 cdata = Compact_Data(io->data,io);
125
126 Free_Seq(io->data,cdata->n_otu);
127
128 for(num_tree=(io->n_trees == 1)?(0):(num_data_set);num_tree < io->n_trees;num_tree++)
129 {
130 if(io->mod->s_opt->random_input_tree == NO) io->mod->s_opt->n_rand_starts = 1;
131
132 if(orig_random_input_tree == YES && io->n_trees > 1)
133 {
134 PhyML_Printf("\n== Cannot combine random starting trees with multiple input trees.");
135 Exit("\n");
136 }
137
138 for(num_rand_tree=0;num_rand_tree<io->mod->s_opt->n_rand_starts;num_rand_tree++)
139 {
140 if((io->mod->s_opt->random_input_tree) && (io->mod->s_opt->topo_search != NNI_MOVE))
141 if(!io->quiet) PhyML_Printf("\n\n. [Random start %3d/%3d]",num_rand_tree+1,io->mod->s_opt->n_rand_starts);
142
143 Init_Model(cdata,mod,io);
144
145 #ifdef M4
146 if(io->mod->use_m4mod) M4_Init_Model(mod->m4mod,cdata,mod);
147 #endif
148
149 // Make the initial tree
150 switch(io->in_tree)
151 {
152 case 0 : case 1 : { tree = Dist_And_BioNJ(cdata,mod,io); break; }
153 case 2 : { tree = Read_User_Tree(cdata,mod,io); break; }
154 }
155
156 if(io->mod->s_opt->opt_topo == YES) Remove_Duplicates(cdata,io,tree);
157
158 if(io->fp_in_constraint_tree != NULL)
159 {
160 char *s;
161
162 PhyML_Printf("\n. Reading constraint tree file...");
163
164 io->cstr_tree = Read_Tree_File_Phylip(io->fp_in_constraint_tree);
165
166 if(io->cstr_tree->n_root != NULL)
167 {
168 PhyML_Printf("\n== The constraint tree file must be unrooted");
169 Exit("\n");
170 }
171
172 s = Add_Taxa_To_Constraint_Tree(io->fp_in_constraint_tree,cdata);
173 fflush(NULL);
174 Free_Tree(tree);
175 tree = Read_Tree(&s);
176 io->in_tree = 2;
177 Free(s);
178 Check_Constraint_Tree_Taxa_Names(io->cstr_tree,cdata);
179 Alloc_Bip(io->cstr_tree);
180 Get_Bip(io->cstr_tree->a_nodes[0],
181 io->cstr_tree->a_nodes[0]->v[0],
182 io->cstr_tree);
183 if(tree->has_branch_lengths == NO) Add_BioNJ_Branch_Lengths(tree,cdata,mod,NULL);
184 }
185
186
187 if(!tree) continue;
188
189 time(&t_beg);
190 time(&(tree->t_beg));
191
192 tree->mod = mod;
193 tree->io = io;
194 tree->data = cdata;
195 tree->n_pattern = tree->data->crunch_len;
196 tree->n_root = NULL;
197 tree->e_root = NULL;
198 tree->n_tot_bl_opt = 0;
199
200 Set_Both_Sides(YES,tree);
201
202 if((!num_data_set) && (!num_tree) && (!num_rand_tree)) Check_Memory_Amount(tree);
203
204 if(io->cstr_tree && !Check_Topo_Constraints(tree,io->cstr_tree))
205 {
206 PhyML_Printf("\n\n== The initial tree does not satisfy the topological constraint.");
207 PhyML_Printf("\n== Please use the user input tree option with an adequate tree topology.");
208 Exit("\n");
209 }
210
211 Connect_CSeqs_To_Nodes(tree->data,tree->io,tree);
212 Make_Tree_For_Pars(tree);
213 Make_Tree_For_Lk(tree);
214 Make_Spr(tree);
215 Br_Len_Not_Involving_Invar(tree);
216 Unscale_Br_Len_Multiplier_Tree(tree);
217
218
219 #ifdef BEAGLE
220 if(mod->bootstrap == YES)
221 {
222 PhyML_Printf("\n== PhyML-BEAGLE does not support bootstrap analysis yet... ");
223 Exit("\n");
224 }
225 if(mod->ras->invar == YES)
226 {
227 PhyML_Printf("\n== PhyML-BEAGLE does not support invariant site models yet... ");
228 Exit("\n");
229 }
230 #endif
231
232 #ifdef PHYML
233 if(tree->io->print_json_trace == YES) JSON_Tree_Io(tree,tree->io->fp_out_json_trace);
234
235
236
237 Set_Update_Eigen(YES,tree->mod);
238 Lk(NULL,tree);
239 Set_Update_Eigen(NO,tree->mod);
240
241
242 if(tree->mod->s_opt->opt_topo)
243 {
244 Global_Spr_Search(tree);
245 if(tree->n_root) Add_Root(tree->a_edges[0],tree);
246 }
247 else
248 {
249 #ifdef BEAGLE
250 tree->b_inst = create_beagle_instance(tree, io->quiet, io);
251 #endif
252 if(tree->mod->s_opt->opt_subst_param || tree->mod->s_opt->opt_bl) Round_Optimize(tree,ROUND_MAX);
253 }
254
255
256 if(tree->mod->gamma_mgf_bl) Best_Root_Position_IL_Model(tree);
257
258 Set_Both_Sides(YES,tree);
259 Lk(NULL,tree);
260 Pars(NULL,tree);
261 Get_Tree_Size(tree);
262 PhyML_Printf("\n\n. Log likelihood of the current tree: %.*f.",DECIMAL_DIG,tree->c_lnL);
263
264 if(tree->io->ancestral == YES) Ancestral_Sequences(tree,YES);
265
266 Check_Br_Lens(tree);
267 Br_Len_Involving_Invar(tree);
268 Rescale_Br_Len_Multiplier_Tree(tree);
269
270
271 #elif defined EVOLVE
272 Evolve(tree->data,tree->mod,tree);
273 Exit("\n. Exiting 'evolve'\n");
274 #endif
275
276 if(!tree->n_root) Get_Best_Root_Position(tree);
277
278 /* Print the tree estimated using the current random (or BioNJ) starting tree */
279 /* if(io->mod->s_opt->n_rand_starts > 1) */
280 if(orig_random_input_tree == YES)
281 {
282 Print_Tree(io->fp_out_trees,tree);
283 fflush(NULL);
284 }
285
286 /* Record the most likely tree in a string of characters */
287 if(tree->c_lnL > best_lnL)
288 {
289 best_lnL = tree->c_lnL;
290 if(most_likely_tree) Free(most_likely_tree);
291 most_likely_tree = Write_Tree(tree);
292
293 time(&t_end);
294
295 Print_Fp_Out(io->fp_out_stats,t_beg,t_end,tree,
296 io,num_data_set+1,
297 (orig_random_input_tree == YES)?(num_rand_tree):(num_tree),
298 (num_rand_tree == io->mod->s_opt->n_rand_starts-1)?(YES):(NO), io->precision);
299
300 if(tree->io->print_site_lnl) Print_Site_Lk(tree,io->fp_out_lk);
301 }
302
303
304
305 /* Start from BioNJ tree */
306 if((num_rand_tree == io->mod->s_opt->n_rand_starts-1) && (tree->mod->s_opt->random_input_tree))
307 {
308 /* Do one more iteration in the loop, but don't randomize the tree */
309 tree->mod->s_opt->n_rand_starts++;
310 tree->mod->s_opt->random_input_tree = NO;
311 }
312 #ifdef BEAGLE
313 finalize_beagle_instance(tree);
314 #endif
315 Free_One_Spr(tree->best_spr);
316 Free_Spr_List_One_Edge(tree);
317 Free_Spr_List_All_Edge(tree);
318 Free_Tree_Pars(tree);
319 Free_Tree_Lk(tree);
320 Free_Tree(tree);
321 } //Tree done
322
323 if(io->n_data_sets == 1) rewind(io->fp_out_tree);
324 if(most_likely_tree) PhyML_Fprintf(io->fp_out_tree,"%s\n",most_likely_tree);
325
326
327 /* Launch bootstrap analysis */
328 if(io->do_boot || io->do_tbe)
329 {
330 if(!io->quiet) PhyML_Printf("\n\n. Launch bootstrap analysis on the most likely tree...");
331
332 #ifdef MPI
333 MPI_Bcast (most_likely_tree, strlen(most_likely_tree)+1, MPI_CHAR, 0, MPI_COMM_WORLD);
334 if(!io->quiet) PhyML_Printf("\n\n. The bootstrap analysis will use %d CPU%c.",Global_numTask,Global_numTask>1?'s':'\0');
335 #endif
336
337 most_likely_tree = Bootstrap_From_String(most_likely_tree,cdata,mod,io);
338
339 PhyML_Printf("\n\n. Completed the bootstrap analysis succesfully."); fflush(NULL);
340 }
341 else
342 if(io->ratio_test != NO)
343 {
344 /* Launch aLRT */
345 most_likely_tree = aLRT_From_String(most_likely_tree,cdata,mod,io);
346 }
347
348
349 /* Print the most likely tree in the output file */
350 if(!io->quiet) PhyML_Printf("\n\n. Printing the most likely tree in file '%s'.", Basename(io->out_tree_file));
351 if(io->n_data_sets == 1) rewind(io->fp_out_tree);
352
353 t_tree *dum;
354 dum = Read_Tree(&most_likely_tree);
355 dum->data = cdata;
356 dum->mod = mod;
357 dum->io = io;
358 Connect_CSeqs_To_Nodes(cdata,io,dum);
359 Insert_Duplicates(dum);
360 Free(most_likely_tree);
361 most_likely_tree = Write_Tree(dum);
362 Free_Tree(dum);
363
364 PhyML_Fprintf(io->fp_out_tree,"%s\n",most_likely_tree);
365
366 if(io->n_trees > 1 && io->n_data_sets > 1) break;
367 }
368 Free_Calign(cdata);
369 }
370 else
371 {
372 PhyML_Printf("\n== No data was found.\n");
373 PhyML_Printf("\n== Err. in file %s at line %d\n",__FILE__,__LINE__);
374 Exit("\n");
375 }
376 Free_Model_Complete(mod);
377 }
378
379 if(most_likely_tree) Free(most_likely_tree);
380
381 if(mod->s_opt->n_rand_starts > 1) PhyML_Printf("\n. Best log likelihood: %f\n",best_lnL);
382
383 Free_Optimiz(mod->s_opt);
384 Free_Model_Basic(mod);
385
386 if(io->fp_in_constraint_tree) fclose(io->fp_in_constraint_tree);
387 if(io->fp_in_align) fclose(io->fp_in_align);
388 if(io->fp_in_tree) fclose(io->fp_in_tree);
389 if(io->fp_out_lk) fclose(io->fp_out_lk);
390 if(io->fp_out_tree) fclose(io->fp_out_tree);
391 if(io->fp_out_trees) fclose(io->fp_out_trees);
392 if(io->fp_out_stats) fclose(io->fp_out_stats);
393 if(io->fp_out_trace) fclose(io->fp_out_trace);
394 if(io->fp_out_json_trace) fclose(io->fp_out_json_trace);
395
396 if(io->fp_in_constraint_tree != NULL) Free_Tree(io->cstr_tree);
397 Free_Input(io);
398
399 time(&t_end);
400 Print_Time_Info(t_beg,t_end);
401
402 #ifdef MPI
403 MPI_Finalize();
404 #endif
405
406 return 0;
407 }
408
409 #elif(M4)
410 #include "m4.h"
main(int argc,char ** argv)411 int main(int argc, char **argv)
412 {
413 M4_main(argc, argv);
414 return 1;
415 }
416
417 #elif(PART)
418 #include "mg.h"
main(int argc,char ** argv)419 int main(int argc, char **argv)
420 {
421 PART_main(argc, argv);
422 return 1;
423 }
424
425 /* #elif(PHYTIME) */
426 /* #include "times.h" */
427 /* int main(int argc, char **argv) */
428 /* { */
429 /* TIMES_main(argc, argv); */
430 /* return 1; */
431 /* } */
432
433 #elif(PHYCONT)
434 #include "continuous.h"
main(int argc,char ** argv)435 int main(int argc, char **argv)
436 {
437 CONT_main(argc, argv);
438 return 1;
439 }
440
441 #elif(RF)
main(int argc,char ** argv)442 int main(int argc, char **argv)
443 {
444 option *io;
445 int r_seed;
446
447
448 io = (option *)Get_Input(argc,argv);
449 if(!io) return(0);
450
451 r_seed = (io->r_seed < 0)?(time(NULL)):(io->r_seed);
452 srand(r_seed);
453 io->r_seed = r_seed;
454
455 Get_Seq(io);
456 Shuffle_Sites(io->mod->ras->pinvar->v,io->data,io->n_otu);
457 Print_Seq(stdout,io->data,io->n_otu);
458
459 /* t_tree *tree1, *tree2; */
460 /* FILE *fp_tree1, *fp_tree2; */
461 /* int i,j; */
462
463 /* fp_tree1 = (FILE *)fopen(argv[1],"r"); */
464 /* fp_tree2 = (FILE *)fopen(argv[2],"r"); */
465
466 /* tree1 = Read_Tree_File_Phylip(fp_tree1); */
467 /* tree2 = Read_Tree_File_Phylip(fp_tree2); */
468
469
470 /* Prune_Tree(tree1,tree2); */
471 /* Prune_Tree(tree2,tree1); */
472
473
474 /* PhyML_Printf("%s\n%s", */
475 /* Write_Tree(tree1), */
476 /* Write_Tree(tree2)); */
477
478
479 /* Match_Nodes_In_Small_Tree(tree1,tree2); */
480
481 /* For(i,2*tree1->n_otu-2) */
482 /* { */
483 /* printf("\n. Node %d in tree1 matches node %d in tree2",i,(tree1->noeud[i]->match_node)?(tree1->noeud[i]->match_node->num):(-1)); */
484 /* } */
485
486
487
488
489 /* t_tree *tree1, *tree2; */
490 /* FILE *fp_tree1, *fp_tree2; */
491 /* int i,j,rf,n_edges,n_common,bip_size; */
492 /* phydbl thresh; */
493 /* t_edge *b; */
494
495
496 /* fp_tree1 = (FILE *)fopen(argv[1],"r"); */
497 /* fp_tree2 = (FILE *)fopen(argv[2],"r"); */
498 /* thresh = (phydbl)atof(argv[3]); */
499
500 /* tree1 = Read_Tree_File(fp_tree1); */
501 /* tree2 = Read_Tree_File(fp_tree2); */
502
503 /* Get_Rid_Of_Prefix('_',tree1); */
504
505 /* /\* Find_Common_Tips(tree1,tree2); *\/ */
506
507 /* Alloc_Bip(tree1); */
508 /* Alloc_Bip(tree2); */
509
510 /* Get_Bip(tree1->noeud[0],tree1->noeud[0]->v[0],tree1); */
511 /* Get_Bip(tree2->noeud[0],tree2->noeud[0]->v[0],tree2); */
512
513 /* /\* PhyML_Printf("\n. rf=%f\n",Compare_Bip_On_Existing_Edges(thresh,tree1,tree2)); *\/ */
514 /* For(i,2*tree1->n_otu-3) tree1->a_edges[i]->bip_score = 0; */
515 /* For(i,2*tree2->n_otu-3) tree2->a_edges[i]->bip_score = 0; */
516
517 /* rf = 0; */
518 /* n_edges = 0; */
519
520 /* /\* First tree *\/ */
521 /* For(i,2*tree1->n_otu-3) */
522 /* { */
523 /* /\* Consider the branch only if the corresponding bipartition has size > 1 *\/ */
524 /* b = tree1->a_edges[i]; */
525 /* bip_size = MIN(b->left->bip_size[b->l_r],b->rght->bip_size[b->r_l]); */
526
527 /* if(bip_size > 1) */
528 /* { */
529 /* /\* with non-zero length *\/ */
530 /* if(tree1->a_edges[i]->l > thresh) */
531 /* { */
532 /* n_edges++; */
533 /* /\* This t_edge is not found in tree2 *\/ */
534 /* if(!tree1->a_edges[i]->bip_score) rf++; ; */
535 /* } */
536 /* } */
537 /* } */
538
539
540 /* /\* Second tree *\/ */
541 /* For(i,2*tree2->n_otu-3) */
542 /* { */
543 /* b = tree2->a_edges[i]; */
544 /* bip_size = MIN(b->left->bip_size[b->l_r],b->rght->bip_size[b->r_l]); */
545
546 /* if(bip_size > 1) */
547 /* { */
548 /* if(tree2->a_edges[i]->l > thresh) */
549 /* { */
550 /* n_edges++; */
551 /* /\* This t_edge is not found in tree1 *\/ */
552 /* if(!tree2->a_edges[i]->bip_score) rf++; ; */
553 /* } */
554 /* } */
555 /* } */
556
557 /* if(!n_edges) */
558 /* { */
559 /* Exit("\n. No comparable internal edges were found.\n"); */
560 /* } */
561 /* else */
562 /* { */
563 /* PhyML_Printf("\n. Robinson and Foulds distance: %f.",(double)rf/(n_edges)); */
564 /* /\* PhyML_Printf("\n. %d internal edges were processed (%d in the first tree, %d in the second).\n",n_edges,n_edges_t1,n_edges-n_edges_t1); *\/ */
565 /* PhyML_Printf("\n"); */
566 /* } */
567
568 return 1;
569 }
570
571 #elif(TIPORDER)
572 #include "tiporder.h"
main(int argc,char ** argv)573 int main(int argc, char **argv)
574 {
575 TIPO_main(argc, argv);
576 return 1;
577 }
578
579 #elif(TEST)
580 #include "xml.h"
main(int argc,char ** argv)581 int main(int argc, char **argv)
582 {
583 option *io;
584 int i;
585 int year;
586
587 io = (option *)Get_Input(argc,argv);
588 if(!io) return(0);
589
590 Get_Seq(io);
591
592 for(i=0;i<io->n_otu;i++)
593 {
594 sscanf(io->data[i]->name,"%d",&year);
595 PhyML_Printf("\n<clade id=\"clad%d\">",i+1);
596 PhyML_Printf("\n\t<taxon value=\"%s\"/>",io->data[i]->name);
597 PhyML_Printf("\n</clade>");
598 PhyML_Printf("\n<calibration id=\"cal%d\">",i+1);
599 PhyML_Printf("\n\t<lower>%d</lower>",0);
600 PhyML_Printf("\n\t<upper>%d</upper>",0);
601 PhyML_Printf("\n\t<appliesto clade.id=\"clad%d\"/>",i+1);
602 PhyML_Printf("\n</calibration>");
603 }
604
605 /* FILE *fp; */
606 /* char *name,*date; */
607 /* int i; */
608
609 /* name = (char *)mCalloc(T_MAX_NAME,sizeof(char)); */
610 /* date = (char *)mCalloc(T_MAX_NAME,sizeof(char)); */
611
612 /* i = 0; */
613 /* fp = Openfile(argv[1],READ); */
614 /* do */
615 /* { */
616 /* if(fscanf(fp,"%s",name) == EOF) break; */
617 /* if(fscanf(fp,"%s",date) == EOF) break; */
618 /* PhyML_Printf("\n<clade id=\"clad%d\">",i+1); */
619 /* PhyML_Printf("\n\t<taxon value=\"%s\"/>",name); */
620 /* PhyML_Printf("\n</clade>"); */
621 /* PhyML_Printf("\n<calibration id=\"cal%d\">",i+1); */
622 /* PhyML_Printf("\n\t<lower>%s</lower>",date); */
623 /* PhyML_Printf("\n\t<upper>%s</upper>",date); */
624 /* PhyML_Printf("\n\t<appliesto clade.id=\"clad%d\"/>",i+1); */
625 /* PhyML_Printf("\n</calibration>"); */
626 /* ++i; */
627 /* } */
628 /* while(1); */
629
630 }
631
632 #elif(INVITEE)
633 #include "invitee.h"
main(int argc,char ** argv)634 int main(int argc, char **argv)
635 {
636 /*My_Function(argc, argv);*/
637 /* PhyTime_XML(argc, argv); */
638 Get_Input(argc,argv);
639 return 1;
640 }
641
642 #elif(GEO)
643 #include "geo.h"
main(int argc,char ** argv)644 int main(int argc, char **argv)
645 {
646 GEO_Main(argc,argv);
647 return 1;
648 }
649
650 #elif(defined PHYREX || PHYREXSIM)
651 #include "phyrex.h"
main(int argc,char ** argv)652 int main(int argc, char **argv)
653 {
654 PHYREX_Main(argc,argv);
655 return 1;
656 }
657
658 #elif(PHYTIME)
659 #include "date.h"
main(int argc,char ** argv)660 int main(int argc, char **argv)
661 {
662 DATE_Main(argc,argv);
663 return 1;
664 }
665
666 #elif(CHECKPOINT)
667 #include "checkpoint.h"
main(int argc,char ** argv)668 int main(int argc, char **argv)
669 {
670 CHECK_Main(argc,argv);
671 return 1;
672 }
673
674 #endif
675
676