1 /**
2  * \file    drawMath.c
3  * \brief   outputs the math of a model as a dot graph
4  * \author  Sarah Keating
5  *
6  * <!--------------------------------------------------------------------------
7  * This sample program is distributed under a different license than the rest
8  * of libSBML.  This program uses the open-source MIT license, as follows:
9  *
10  * Copyright (c) 2013-2018 by the California Institute of Technology
11  * (California, USA), the European Bioinformatics Institute (EMBL-EBI, UK)
12  * and the University of Heidelberg (Germany), with support from the National
13  * Institutes of Health (USA) under grant R01GM070923.  All rights reserved.
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a
16  * copy of this software and associated documentation files (the "Software"),
17  * to deal in the Software without restriction, including without limitation
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19  * and/or sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice shall be included in
23  * all copies or substantial portions of the Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  *
33  * Neither the name of the California Institute of Technology (Caltech), nor
34  * of the European Bioinformatics Institute (EMBL-EBI), nor of the University
35  * of Heidelberg, nor the names of any contributors, may be used to endorse
36  * or promote products derived from this software without specific prior
37  * written permission.
38  * ------------------------------------------------------------------------ -->
39  */
40 
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 
45 #include <sbml/util/util.h>
46 #include <sbml/SBMLTypes.h>
47 
48 #include "FormulaGraphvizFormatter.h"
49 
50 
51 static int noClusters = 0;
52 
53 FILE * fout;
54 
55 /**
56  * @return the given formula AST as a directed graph.  The caller
57  * owns the returned string and is responsible for freeing it.
58  */
59 char *
SBML_formulaToDot(const ASTNode_t * tree)60 SBML_formulaToDot (const ASTNode_t *tree)
61 {
62   StringBuffer_t *sb = StringBuffer_create(128);
63   char           *name;
64   char           *s;
65 
66   if (FormulaGraphvizFormatter_isFunction(tree)
67     || ASTNode_isOperator(tree))
68   {
69     FormulaGraphvizFormatter_visit(NULL, tree, sb);
70   }
71   else
72   {
73     name = FormulaGraphvizFormatter_format(tree);
74     StringBuffer_append(sb, name);
75   }
76 
77   StringBuffer_append(sb, "}\n");
78 
79   s = StringBuffer_getBuffer(sb);
80   free(sb);
81 
82   return s;
83 }
84 
85 
86 /**
87  * @return true (non-zero) if the given ASTNode is to formatted as a
88  * function.
89  */
90 int
FormulaGraphvizFormatter_isFunction(const ASTNode_t * node)91 FormulaGraphvizFormatter_isFunction (const ASTNode_t *node)
92 {
93   return
94     ASTNode_isFunction  (node) ||
95     ASTNode_isLambda    (node) ||
96     ASTNode_isLogical   (node) ||
97     ASTNode_isRelational(node);
98 }
99 
100 
101 /**
102  * Formats the given ASTNode as a directed graph token and returns the result as
103  * a string.
104  */
105 char *
FormulaGraphvizFormatter_format(const ASTNode_t * node)106 FormulaGraphvizFormatter_format (const ASTNode_t *node)
107 {
108   StringBuffer_t *p = StringBuffer_create(128);
109   char           *s = NULL;
110 
111   if (ASTNode_isOperator(node))
112   {
113     s = FormulaGraphvizFormatter_formatOperator(node);
114   }
115   else if (ASTNode_isFunction(node))
116   {
117     s = FormulaGraphvizFormatter_formatFunction(node);
118   }
119   else if (ASTNode_isInteger(node))
120   {
121     StringBuffer_appendInt(p, ASTNode_getInteger(node));
122     s = StringBuffer_toString(p);
123   }
124   else if (ASTNode_isRational(node))
125   {
126     s = FormulaGraphvizFormatter_formatRational(node);
127   }
128   else if (ASTNode_isReal(node))
129   {
130     s = FormulaGraphvizFormatter_formatReal(node);
131   }
132   else if ( !ASTNode_isUnknown(node) )
133   {
134     if (ASTNode_getName(node) == NULL)
135     {
136       StringBuffer_append(p, "unknown");
137     }
138     else
139     {
140       StringBuffer_append(p, ASTNode_getName(node));
141     }
142     s = StringBuffer_toString(p);
143   }
144 
145   StringBuffer_free(p);
146 
147   return s;
148 }
149 
150 
151 /**
152  * Since graphviz will interpret identical names as referring to
153  * the same node presentation-wise it is better if each function node
154  * has a unique name.
155  *
156  * Returns the name with the name of the first child
157  * prepended
158  *
159  * THIS COULD BE DONE BETTER
160  */
161 char *
FormulaGraphvizFormatter_getUniqueName(const ASTNode_t * node)162 FormulaGraphvizFormatter_getUniqueName (const ASTNode_t *node)
163 {
164   StringBuffer_t *p = StringBuffer_create(128);
165   char           *s = NULL;
166 
167   if (ASTNode_isOperator(node))
168   {
169     s = FormulaGraphvizFormatter_OperatorGetUniqueName(node);
170   }
171   else if (ASTNode_isFunction(node))
172   {
173     s = FormulaGraphvizFormatter_FunctionGetUniqueName(node);
174   }
175   else if (ASTNode_isInteger(node))
176   {
177     StringBuffer_appendInt(p, ASTNode_getInteger(node));
178     s = StringBuffer_toString(p);
179   }
180   else if (ASTNode_isRational(node))
181   {
182     s = FormulaGraphvizFormatter_formatRational(node);
183   }
184   else if (ASTNode_isReal(node))
185   {
186     s = FormulaGraphvizFormatter_formatReal(node);
187   }
188   else if ( !ASTNode_isUnknown(node) )
189   {
190     StringBuffer_append(p, ASTNode_getName(node));
191     s = StringBuffer_toString(p);
192   }
193 
194   StringBuffer_free(p);
195 
196   return s;
197 }
198 
199 /**
200  * Formats the given ASTNode as a directed graph function name and returns the
201  * result as a string.
202  */
203 char *
FormulaGraphvizFormatter_formatFunction(const ASTNode_t * node)204 FormulaGraphvizFormatter_formatFunction (const ASTNode_t *node)
205 {
206   char           *s;
207   StringBuffer_t *p   = StringBuffer_create(128);
208   ASTNodeType_t  type = ASTNode_getType(node);
209 
210   switch (type)
211   {
212     case AST_FUNCTION_ARCCOS:
213       s =  "acos";
214       break;
215 
216     case AST_FUNCTION_ARCSIN:
217       s =  "asin";
218       break;
219 
220     case AST_FUNCTION_ARCTAN:
221       s =  "atan";
222       break;
223 
224     case AST_FUNCTION_CEILING:
225       s =  "ceil";
226       break;
227 
228     case AST_FUNCTION_LN:
229       s =  "log";
230       break;
231 
232     case AST_FUNCTION_POWER:
233       s =  "pow";
234       break;
235 
236     default:
237       if (ASTNode_getName(node) == NULL)
238       {
239         StringBuffer_append(p, "unknown");
240       }
241       else
242       {
243         StringBuffer_append(p, ASTNode_getName(node));
244       }
245       s = StringBuffer_toString(p);
246       break;
247   }
248 
249   StringBuffer_free(p);
250 
251   return s;
252 }
253 
254 
255 /**
256  * Since graphviz will interpret identical names as referring to
257  * the same node presentation-wise it is better if each function node
258  * has a unique name.
259  *
260  * Returns the name of the function with the name of the first child
261  * prepended
262  *
263  * THIS COULD BE DONE BETTER
264  */
265 char *
FormulaGraphvizFormatter_FunctionGetUniqueName(const ASTNode_t * node)266 FormulaGraphvizFormatter_FunctionGetUniqueName (const ASTNode_t *node)
267 {
268   char           *s;
269   StringBuffer_t *p   = StringBuffer_create(128);
270   ASTNodeType_t  type = ASTNode_getType(node);
271 
272   if (ASTNode_getNumChildren(node) != 0)
273   {
274 	const char* name = ASTNode_getName(ASTNode_getChild(node,0));
275 	if (name != NULL)
276     StringBuffer_append(p, name);
277   }
278   else
279   {
280     StringBuffer_append(p, "unknown");
281   }
282 
283   switch (type)
284   {
285     case AST_FUNCTION_ARCCOS:
286       StringBuffer_append(p, "acos");
287       break;
288 
289     case AST_FUNCTION_ARCSIN:
290       StringBuffer_append(p, "asin");
291       break;
292 
293     case AST_FUNCTION_ARCTAN:
294       StringBuffer_append(p, "atan");
295       break;
296 
297     case AST_FUNCTION_CEILING:
298       StringBuffer_append(p, "ceil");
299       break;
300 
301     case AST_FUNCTION_LN:
302       StringBuffer_append(p, "log");
303       break;
304 
305     case AST_FUNCTION_POWER:
306       StringBuffer_append(p, "pow");
307       break;
308 
309     default:
310       if (ASTNode_getName(node) != NULL)
311       {
312         StringBuffer_append(p, ASTNode_getName(node));
313       }
314       break;
315   }
316 
317   s = StringBuffer_toString(p);
318 
319   StringBuffer_free(p);
320 
321   return s;
322 }
323 
324 
325 /**
326  * Formats the given ASTNode as a directed graph operator and returns the result
327  * as a string.
328  */
329 char *
FormulaGraphvizFormatter_formatOperator(const ASTNode_t * node)330 FormulaGraphvizFormatter_formatOperator (const ASTNode_t *node)
331 {
332   char           *s;
333   ASTNodeType_t  type = ASTNode_getType(node);
334   StringBuffer_t *p   = StringBuffer_create(128);
335 
336   switch (type)
337   {
338     case AST_TIMES:
339       s =  "times";
340       break;
341 
342     case AST_DIVIDE:
343       s =  "divide";
344       break;
345 
346     case AST_PLUS:
347       s =  "plus";
348       break;
349 
350     case AST_MINUS:
351       s =  "minus";
352       break;
353 
354     case AST_POWER:
355       s =  "power";
356       break;
357 
358     default:
359       StringBuffer_appendChar(p, ASTNode_getCharacter(node));
360       s = StringBuffer_toString(p);
361       break;
362   }
363 
364   StringBuffer_free(p);
365 
366   return s;
367 }
368 
369 /**
370  * Since graphviz will interpret identical names as referring to
371  * the same node presentation-wise it is better if each function node
372  * has a unique name.
373  *
374  * Returns the name of the operator with the name of the first child
375  * prepended
376  *
377  * THIS COULD BE DONE BETTER
378  */
379 char *
FormulaGraphvizFormatter_OperatorGetUniqueName(const ASTNode_t * node)380 FormulaGraphvizFormatter_OperatorGetUniqueName (const ASTNode_t *node)
381 {
382   char           *s;
383   char           number[10];
384   StringBuffer_t *p   = StringBuffer_create(128);
385   ASTNodeType_t  type = ASTNode_getType(node);
386 
387   if (FormulaGraphvizFormatter_isFunction(ASTNode_getChild(node,0))
388     || ASTNode_isOperator(ASTNode_getChild(node,0)))
389   {
390     StringBuffer_append(p, "func");
391   }
392   else
393   {
394     if (ASTNode_isInteger(ASTNode_getChild(node, 0)))
395     {
396       sprintf(number, "%d", (int)ASTNode_getInteger(ASTNode_getChild(node, 0)));
397       StringBuffer_append(p, number);
398     }
399     else if (ASTNode_isReal(ASTNode_getChild(node, 0)))
400     {
401       sprintf(number, "%ld", ASTNode_getNumerator(ASTNode_getChild(node, 0)));
402       StringBuffer_append(p, number);
403     }
404     else
405     {
406       StringBuffer_append(p, ASTNode_getName(ASTNode_getChild(node,0)));
407     }
408   }
409 
410   switch (type)
411   {
412     case AST_TIMES:
413       StringBuffer_append(p,  "times");
414       break;
415 
416     case AST_DIVIDE:
417       StringBuffer_append(p,  "divide");
418       break;
419 
420     case AST_PLUS:
421       StringBuffer_append(p,  "plus");
422       break;
423 
424     case AST_MINUS:
425       StringBuffer_append(p,  "minus");
426       break;
427 
428     case AST_POWER:
429       StringBuffer_append(p,  "power");
430       break;
431 
432     default:
433       StringBuffer_appendChar(p, ASTNode_getCharacter(node));
434       break;
435   }
436 
437   s = StringBuffer_toString(p);
438 
439   StringBuffer_free(p);
440 
441   return s;
442 }
443 
444 
445 /**
446  * Formats the given ASTNode as a rational number and returns the result as
447  * a string.  This amounts to:
448  *
449  *   "(numerator/denominator)"
450  */
451 char *
FormulaGraphvizFormatter_formatRational(const ASTNode_t * node)452 FormulaGraphvizFormatter_formatRational (const ASTNode_t *node)
453 {
454   char           *s;
455   StringBuffer_t *p = StringBuffer_create(128);
456 
457   StringBuffer_appendChar( p, '(');
458   StringBuffer_appendInt ( p, ASTNode_getNumerator(node)   );
459   StringBuffer_appendChar( p, '/');
460   StringBuffer_appendInt ( p, ASTNode_getDenominator(node) );
461   StringBuffer_appendChar( p, ')');
462 
463   s = StringBuffer_toString(p);
464 
465   StringBuffer_free(p);
466 
467   return s;
468 }
469 
470 
471 /**
472  * Formats the given ASTNode as a real number and returns the result as
473  * a string.
474  */
475 char *
FormulaGraphvizFormatter_formatReal(const ASTNode_t * node)476 FormulaGraphvizFormatter_formatReal (const ASTNode_t *node)
477 {
478   StringBuffer_t *p    = StringBuffer_create(128);
479   double         value = ASTNode_getReal(node);
480   int            sign;
481   char           *s;
482 
483   if (util_isNaN(value))
484   {
485     s =  "NaN";
486   }
487   else if ((sign = util_isInf(value)) != 0)
488   {
489     if (sign == -1)
490     {
491       s = "-INF";
492     }
493     else
494     {
495       s =  "INF";
496     }
497   }
498   else if (util_isNegZero(value))
499   {
500     s =  "-0";
501   }
502   else
503   {
504     StringBuffer_appendReal(p, value);
505     s = StringBuffer_toString(p);
506   }
507 
508   StringBuffer_free(p);
509 
510   return s;
511 }
512 
513 
514 /**
515  * Visits the given ASTNode node.  This function is really just a
516  * dispatcher to either FormulaGraphvizFormatter_visitFunction() or
517  * FormulaGraphvizFormatter_visitOther().
518  */
519 void
FormulaGraphvizFormatter_visit(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)520 FormulaGraphvizFormatter_visit (const ASTNode_t *parent,
521                                 const ASTNode_t *node,
522                                 StringBuffer_t  *sb )
523 {
524   if (ASTNode_isLog10(node))
525   {
526     FormulaGraphvizFormatter_visitLog10(parent, node, sb);
527   }
528   else if (ASTNode_isSqrt(node))
529   {
530     FormulaGraphvizFormatter_visitSqrt(parent, node, sb);
531   }
532   else if (FormulaGraphvizFormatter_isFunction(node))
533   {
534     FormulaGraphvizFormatter_visitFunction(parent, node, sb);
535   }
536   else if (ASTNode_isUMinus(node))
537   {
538     FormulaGraphvizFormatter_visitUMinus(parent, node, sb);
539   }
540   else
541   {
542     FormulaGraphvizFormatter_visitOther(parent, node, sb);
543   }
544 }
545 
546 
547 /**
548  * Visits the given ASTNode as a function.  For this node only the
549  * traversal is preorder.
550  * Writes the function as a directed graph and appends the result
551  * to the StringBuffer.
552  */
553 void
FormulaGraphvizFormatter_visitFunction(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)554 FormulaGraphvizFormatter_visitFunction (const ASTNode_t *parent,
555                                         const ASTNode_t *node,
556                                         StringBuffer_t  *sb )
557 {
558   unsigned int numChildren = ASTNode_getNumChildren(node);
559   unsigned int n;
560   char         *name;
561   char         *uniqueName;
562 
563   uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
564   name       = FormulaGraphvizFormatter_format(node);
565 
566   StringBuffer_append(sb, uniqueName);
567   StringBuffer_append(sb, " [shape=box, label=");
568   StringBuffer_append(sb, name);
569   StringBuffer_append(sb, "];\n");
570 
571   if (parent != NULL)
572   {
573     name = FormulaGraphvizFormatter_getUniqueName(node);
574     uniqueName = FormulaGraphvizFormatter_getUniqueName(parent);
575 
576     if(strcmp(name, uniqueName))
577     {
578       StringBuffer_append(sb, uniqueName);
579       StringBuffer_append(sb, " -> ");
580       StringBuffer_append(sb, name);
581       StringBuffer_append(sb, ";\n");
582     }
583     free(name);
584     free(uniqueName);
585   }
586 
587   if (numChildren > 0)
588   {
589     FormulaGraphvizFormatter_visit( node, ASTNode_getChild(node, 0), sb );
590   }
591 
592   for (n = 1; n < numChildren; n++)
593   {
594     FormulaGraphvizFormatter_visit( node, ASTNode_getChild(node, n), sb );
595   }
596 
597 }
598 
599 
600 /**
601  * Visits the given ASTNode as the function "log(10, x)" and in doing so,
602  * formats it as "log10(x)" (where x is any subexpression).
603  * Writes the function as a directed graph and appends the result
604  * to the StringBuffer.
605  *
606  * A seperate function may not be strictly speaking necessary for graphs
607  */
608 void
FormulaGraphvizFormatter_visitLog10(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)609 FormulaGraphvizFormatter_visitLog10 (const ASTNode_t *parent,
610                                      const ASTNode_t *node,
611                                      StringBuffer_t  *sb )
612 {
613   char *uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
614   char *name       = FormulaGraphvizFormatter_format(node);
615 
616   StringBuffer_append(sb, uniqueName);
617   StringBuffer_append(sb, " [shape=box, label=");
618   StringBuffer_append(sb, name);
619   StringBuffer_append(sb, "];\n");
620 
621   FormulaGraphvizFormatter_visit(node, ASTNode_getChild(node, 1), sb);
622 }
623 
624 
625 /**
626  * Visits the given ASTNode as the function "root(2, x)" and in doing so,
627  * formats it as "sqrt(x)" (where x is any subexpression).
628  * Writes the function as a directed graph and appends the result
629  * to the StringBuffer.
630  *
631  * A seperate function may not be strictly speaking necessary for graphs
632  */
633 void
FormulaGraphvizFormatter_visitSqrt(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)634 FormulaGraphvizFormatter_visitSqrt (const ASTNode_t *parent,
635                                     const ASTNode_t *node,
636                                     StringBuffer_t  *sb )
637 {
638   char *uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
639   char *name       = FormulaGraphvizFormatter_format(node);
640 
641   StringBuffer_append(sb, uniqueName);
642   StringBuffer_append(sb, " [shape=box, label=");
643   StringBuffer_append(sb, name);
644   StringBuffer_append(sb, "];\n");
645 
646   FormulaGraphvizFormatter_visit(node, ASTNode_getChild(node, 1), sb);
647 }
648 
649 
650 /**
651  * Visits the given ASTNode as a unary minus.  For this node only the
652  * traversal is preorder.
653  * Writes the function as a directed graph and appends the result
654  * to the StringBuffer.
655  */
656 void
FormulaGraphvizFormatter_visitUMinus(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)657 FormulaGraphvizFormatter_visitUMinus (const ASTNode_t *parent,
658                                       const ASTNode_t *node,
659                                       StringBuffer_t  *sb )
660 {
661   char *uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
662   char *name       = FormulaGraphvizFormatter_format(node);
663 
664   StringBuffer_append(sb, uniqueName);
665   StringBuffer_append(sb, " [shape=box, label=");
666   StringBuffer_append(sb, name);
667   StringBuffer_append(sb, "];\n");
668 
669   if (parent != NULL)
670   {
671     uniqueName = FormulaGraphvizFormatter_getUniqueName(parent);
672     name       = FormulaGraphvizFormatter_getUniqueName(node);
673 
674     if(strcmp(name, uniqueName))
675     {
676       StringBuffer_append(sb, uniqueName);
677       StringBuffer_append(sb, " -> ");
678       StringBuffer_append(sb, name);
679       StringBuffer_append(sb, ";\n");
680     }
681   }
682 
683   FormulaGraphvizFormatter_visit ( node, ASTNode_getLeftChild(node), sb );
684 }
685 
686 
687 /**
688  * Visits the given ASTNode and continues the inorder traversal.
689  * Writes the function as a directed graph and appends the result
690  * to the StringBuffer.
691  */
692 void
FormulaGraphvizFormatter_visitOther(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)693 FormulaGraphvizFormatter_visitOther (const ASTNode_t *parent,
694                                      const ASTNode_t *node,
695                                      StringBuffer_t  *sb )
696 {
697   unsigned int numChildren = ASTNode_getNumChildren(node);
698   char         *name;
699   char         *uniqueName;
700 
701   if (numChildren > 0)
702   {
703     uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
704     name       = FormulaGraphvizFormatter_format(node);
705 
706     StringBuffer_append(sb, uniqueName);
707     StringBuffer_append(sb, " [shape=box, label=");
708     StringBuffer_append(sb, name);
709     StringBuffer_append(sb, "];\n");
710     free(uniqueName);
711 
712     FormulaGraphvizFormatter_visit( node, ASTNode_getLeftChild(node), sb );
713   }
714 
715   if (parent != NULL)
716   {
717     name       = FormulaGraphvizFormatter_getUniqueName(node);
718     uniqueName = FormulaGraphvizFormatter_getUniqueName(parent);
719 
720     if(strcmp(name, uniqueName))
721     {
722       StringBuffer_append(sb, uniqueName);
723       StringBuffer_append(sb, " -> ");
724       StringBuffer_append(sb, name);
725       StringBuffer_append(sb, ";\n");
726     }
727     free(name);
728     free(uniqueName);
729   }
730 
731   if (numChildren > 1)
732   {
733     FormulaGraphvizFormatter_visit( node, ASTNode_getRightChild(node), sb );
734   }
735 }
736 
737 
738 void
printFunctionDefinition(unsigned int n,FunctionDefinition_t * fd)739 printFunctionDefinition (unsigned int n, FunctionDefinition_t *fd)
740 {
741   const ASTNode_t *math;
742   char *formula;
743 
744 
745   if ( FunctionDefinition_isSetMath(fd) )
746   {
747     math = FunctionDefinition_getMath(fd);
748 
749     /* Print function body. */
750     if (ASTNode_getNumChildren(math) == 0)
751     {
752       printf("(no body defined)");
753     }
754     else
755     {
756       math    = ASTNode_getChild(math, ASTNode_getNumChildren(math) - 1);
757       formula = SBML_formulaToDot(math);
758       fprintf(fout, "subgraph cluster%u {\n", noClusters);
759       fprintf(fout, "label=\"FunctionDefinition: %s\";\n%s\n", FunctionDefinition_getId(fd), formula);
760       free(formula);
761       noClusters++;
762     }
763   }
764 }
765 
766 
767 void
printRuleMath(unsigned int n,Rule_t * r)768 printRuleMath (unsigned int n, Rule_t *r)
769 {
770   char *formula;
771 
772   if ( Rule_isSetMath(r) )
773   {
774     formula = SBML_formulaToDot( Rule_getMath(r));
775     fprintf(fout, "subgraph cluster%u {\n", noClusters);
776     fprintf(fout, "label=\"Rule: %u\";\n%s\n", n, formula);
777     free(formula);
778     noClusters++;
779   }
780 }
781 
782 
783 void
printReactionMath(unsigned int n,Reaction_t * r)784 printReactionMath (unsigned int n, Reaction_t *r)
785 {
786   char         *formula;
787   KineticLaw_t *kl;
788 
789 
790   if (Reaction_isSetKineticLaw(r))
791   {
792     kl = Reaction_getKineticLaw(r);
793 
794     if ( KineticLaw_isSetMath(kl) )
795     {
796       formula = SBML_formulaToDot( KineticLaw_getMath(kl) );
797       fprintf(fout, "subgraph cluster%u {\n", noClusters);
798       fprintf(fout, "label=\"Reaction: %s\";\n%s\n", Reaction_getId(r), formula);
799       free(formula);
800       noClusters++;
801     }
802   }
803 }
804 
805 
806 void
printEventAssignmentMath(unsigned int n,EventAssignment_t * ea)807 printEventAssignmentMath (unsigned int n, EventAssignment_t *ea)
808 {
809   const char *variable;
810   char       *formula;
811 
812 
813   if ( EventAssignment_isSetMath(ea) )
814   {
815     variable = EventAssignment_getVariable(ea);
816     formula  = SBML_formulaToDot( EventAssignment_getMath(ea) );
817     fprintf(fout, "subgraph cluster%u {\n", noClusters);
818     fprintf(fout, "label=\"EventAssignment: %u\";\n", n);
819     fprintf(fout, "%s [shape=box];\n%s -> %s\n", variable, variable, formula);
820     noClusters++;
821     free(formula);
822   }
823 }
824 
825 
826 void
printEventMath(unsigned int n,Event_t * e)827 printEventMath (unsigned int n, Event_t *e)
828 {
829   char         *formula;
830   unsigned int i;
831 
832 
833   if ( Event_isSetDelay(e) )
834   {
835     formula = SBML_formulaToDot( Delay_getMath(Event_getDelay(e)) );
836     fprintf(fout, "subgraph cluster%u {\n", noClusters);
837     fprintf(fout, "label=\"Event %s delay:\";\n%s\n", Event_getId(e), formula);
838     free(formula);
839     noClusters++;
840   }
841 
842   if ( Event_isSetTrigger(e) )
843   {
844     formula = SBML_formulaToDot( Trigger_getMath(Event_getTrigger(e)) );
845     fprintf(fout, "subgraph cluster%u {\n", noClusters);
846     fprintf(fout, "label=\"Event %s trigger:\";\n%s\n", Event_getId(e), formula);
847     noClusters++;
848     free(formula);
849   }
850 
851   for (i = 0; i < Event_getNumEventAssignments(e); ++i)
852   {
853     printEventAssignmentMath(i + 1, Event_getEventAssignment(e, i));
854   }
855 }
856 
857 
858 void
printMath(Model_t * m)859 printMath (Model_t *m)
860 {
861   unsigned int  n;
862 
863   /* a digraph must have a name thus
864    * need to check that Model_getId does not return NULL
865    * and provide a name if it does
866    */
867 
868   if (Model_getId(m) != NULL) {
869     fprintf(fout, "digraph %s {\n", Model_getId(m));
870   }
871   else {
872     fprintf(fout, "digraph example {\n");
873   }
874   fprintf(fout, "compound=true;\n");
875 
876   for (n = 0; n < Model_getNumFunctionDefinitions(m); ++n)
877   {
878     printFunctionDefinition(n + 1, Model_getFunctionDefinition(m, n));
879   }
880 
881   for (n = 0; n < Model_getNumRules(m); ++n)
882   {
883     printRuleMath(n + 1, Model_getRule(m, n));
884   }
885 
886   printf("\n");
887 
888   for (n = 0; n < Model_getNumReactions(m); ++n)
889   {
890     printReactionMath(n + 1, Model_getReaction(m, n));
891   }
892 
893   printf("\n");
894 
895   for (n = 0; n < Model_getNumEvents(m); ++n)
896   {
897     printEventMath(n + 1, Model_getEvent(m, n));
898   }
899 
900   fprintf(fout, "}\n");
901 }
902 
903 
904 int
main(int argc,char * argv[])905 main (int argc, char *argv[])
906 {
907   SBMLDocument_t *d;
908   Model_t        *m;
909 
910   if (argc != 3)
911   {
912     printf("\n  usage: drawMath <sbml filename> <output dot filename>\n\n");
913     return 1;
914   }
915 
916   d = readSBML(argv[1]);
917   m = SBMLDocument_getModel(d);
918 
919   SBMLDocument_printErrors(d, stdout);
920 
921   if ((fout  = fopen( argv[2], "w" )) == NULL )
922   {
923     printf( "The output file was not opened\n" );
924   }
925   else
926   {
927     printMath(m);
928     fclose(fout);
929   }
930 
931   SBMLDocument_free(d);
932 
933   return 0;
934 }
935