]> www.ginac.de Git - ginac.git/blob - ginac/inifcns_trans.cpp
01711a7fa3c9aae70cc9afc536ab651a157d2d34
[ginac.git] / ginac / inifcns_trans.cpp
1 /** @file inifcns_trans.cpp
2  *
3  *  Implementation of transcendental (and trigonometric and hyperbolic)
4  *  functions. */
5
6 /*
7  *  GiNaC Copyright (C) 1999 Johannes Gutenberg University Mainz, Germany
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include <vector>
25 #include <stdexcept>
26
27 #include "inifcns.h"
28 #include "ex.h"
29 #include "constant.h"
30 #include "numeric.h"
31 #include "power.h"
32
33 #ifndef NO_GINAC_NAMESPACE
34 namespace GiNaC {
35 #endif // ndef NO_GINAC_NAMESPACE
36
37 //////////
38 // exponential function
39 //////////
40
41 static ex exp_evalf(ex const & x)
42 {
43     BEGIN_TYPECHECK
44         TYPECHECK(x,numeric)
45     END_TYPECHECK(exp(x))
46     
47     return exp(ex_to_numeric(x)); // -> numeric exp(numeric)
48 }
49
50 static ex exp_eval(ex const & x)
51 {
52     // exp(0) -> 1
53     if (x.is_zero()) {
54         return exONE();
55     }
56     // exp(n*Pi*I/2) -> {+1|+I|-1|-I}
57     ex TwoExOverPiI=(2*x)/(Pi*I);
58     if (TwoExOverPiI.info(info_flags::integer)) {
59         numeric z=mod(ex_to_numeric(TwoExOverPiI),numeric(4));
60         if (z.is_equal(numZERO()))
61             return exONE();
62         if (z.is_equal(numONE()))
63             return ex(I);
64         if (z.is_equal(numTWO()))
65             return exMINUSONE();
66         if (z.is_equal(numTHREE()))
67             return ex(-I);
68     }
69     // exp(log(x)) -> x
70     if (is_ex_the_function(x, log))
71         return x.op(0);
72     
73     // exp(float)
74     if (x.info(info_flags::numeric) && !x.info(info_flags::rational))
75         return exp_evalf(x);
76     
77     return exp(x).hold();
78 }    
79
80 static ex exp_diff(ex const & x, unsigned diff_param)
81 {
82     GINAC_ASSERT(diff_param==0);
83
84     return exp(x);
85 }
86
87 REGISTER_FUNCTION(exp, exp_eval, exp_evalf, exp_diff, NULL);
88
89 //////////
90 // natural logarithm
91 //////////
92
93 static ex log_evalf(ex const & x)
94 {
95     BEGIN_TYPECHECK
96         TYPECHECK(x,numeric)
97     END_TYPECHECK(log(x))
98     
99     return log(ex_to_numeric(x)); // -> numeric log(numeric)
100 }
101
102 static ex log_eval(ex const & x)
103 {
104     if (x.info(info_flags::numeric)) {
105         // log(1) -> 0
106         if (x.is_equal(exONE()))
107             return exZERO();
108         // log(-1) -> I*Pi
109         if (x.is_equal(exMINUSONE()))
110             return (I*Pi);
111         // log(I) -> Pi*I/2
112         if (x.is_equal(I))
113             return (I*Pi*numeric(1,2));
114         // log(-I) -> -Pi*I/2
115         if (x.is_equal(-I))
116             return (I*Pi*numeric(-1,2));
117         // log(0) -> throw singularity
118         if (x.is_equal(exZERO()))
119             throw(std::domain_error("log_eval(): log(0)"));
120         // log(float)
121         if (!x.info(info_flags::rational))
122             return log_evalf(x);
123     }
124     
125     return log(x).hold();
126 }    
127
128 static ex log_diff(ex const & x, unsigned diff_param)
129 {
130     GINAC_ASSERT(diff_param==0);
131
132     return power(x, -1);
133 }
134
135 REGISTER_FUNCTION(log, log_eval, log_evalf, log_diff, NULL);
136
137 //////////
138 // sine (trigonometric function)
139 //////////
140
141 static ex sin_evalf(ex const & x)
142 {
143     BEGIN_TYPECHECK
144        TYPECHECK(x,numeric)
145     END_TYPECHECK(sin(x))
146     
147     return sin(ex_to_numeric(x)); // -> numeric sin(numeric)
148 }
149
150 static ex sin_eval(ex const & x)
151 {
152     // sin(n*Pi) -> 0
153     ex xOverPi=x/Pi;
154     if (xOverPi.info(info_flags::integer))
155         return exZERO();
156     
157     // sin((2n+1)*Pi/2) -> {+|-}1
158     ex xOverPiMinusHalf=xOverPi-exHALF();
159     if (xOverPiMinusHalf.info(info_flags::even))
160         return exONE();
161     else if (xOverPiMinusHalf.info(info_flags::odd))
162         return exMINUSONE();
163     
164     if (is_ex_exactly_of_type(x, function)) {
165         ex t=x.op(0);
166         // sin(asin(x)) -> x
167         if (is_ex_the_function(x, asin))
168             return t;
169         // sin(acos(x)) -> (1-x^2)^(1/2)
170         if (is_ex_the_function(x, acos))
171             return power(exONE()-power(t,exTWO()),exHALF());
172         // sin(atan(x)) -> x*(1+x^2)^(-1/2)
173         if (is_ex_the_function(x, atan))
174             return t*power(exONE()+power(t,exTWO()),exMINUSHALF());
175     }
176     
177     // sin(float) -> float
178     if (x.info(info_flags::numeric) && !x.info(info_flags::rational))
179         return sin_evalf(x);
180     
181     return sin(x).hold();
182 }
183
184 static ex sin_diff(ex const & x, unsigned diff_param)
185 {
186     GINAC_ASSERT(diff_param==0);
187     
188     return cos(x);
189 }
190
191 REGISTER_FUNCTION(sin, sin_eval, sin_evalf, sin_diff, NULL);
192
193 //////////
194 // cosine (trigonometric function)
195 //////////
196
197 static ex cos_evalf(ex const & x)
198 {
199     BEGIN_TYPECHECK
200         TYPECHECK(x,numeric)
201     END_TYPECHECK(cos(x))
202     
203     return cos(ex_to_numeric(x)); // -> numeric cos(numeric)
204 }
205
206 static ex cos_eval(ex const & x)
207 {
208     // cos(n*Pi) -> {+|-}1
209     ex xOverPi=x/Pi;
210     if (xOverPi.info(info_flags::even))
211         return exONE();
212     else if (xOverPi.info(info_flags::odd))
213         return exMINUSONE();
214     
215     // cos((2n+1)*Pi/2) -> 0
216     ex xOverPiMinusHalf=xOverPi-exHALF();
217     if (xOverPiMinusHalf.info(info_flags::integer))
218         return exZERO();
219     
220     if (is_ex_exactly_of_type(x, function)) {
221         ex t=x.op(0);
222         // cos(acos(x)) -> x
223         if (is_ex_the_function(x, acos))
224             return t;
225         // cos(asin(x)) -> (1-x^2)^(1/2)
226         if (is_ex_the_function(x, asin))
227             return power(exONE()-power(t,exTWO()),exHALF());
228         // cos(atan(x)) -> (1+x^2)^(-1/2)
229         if (is_ex_the_function(x, atan))
230             return power(exONE()+power(t,exTWO()),exMINUSHALF());
231     }
232     
233     // cos(float) -> float
234     if (x.info(info_flags::numeric) && !x.info(info_flags::rational))
235         return cos_evalf(x);
236     
237     return cos(x).hold();
238 }
239
240 static ex cos_diff(ex const & x, unsigned diff_param)
241 {
242     GINAC_ASSERT(diff_param==0);
243
244     return numMINUSONE()*sin(x);
245 }
246
247 REGISTER_FUNCTION(cos, cos_eval, cos_evalf, cos_diff, NULL);
248
249 //////////
250 // tangent (trigonometric function)
251 //////////
252
253 static ex tan_evalf(ex const & x)
254 {
255     BEGIN_TYPECHECK
256        TYPECHECK(x,numeric)
257     END_TYPECHECK(tan(x)) // -> numeric tan(numeric)
258     
259     return tan(ex_to_numeric(x));
260 }
261
262 static ex tan_eval(ex const & x)
263 {
264     // tan(n*Pi/3) -> {0|3^(1/2)|-(3^(1/2))}
265     ex ThreeExOverPi=numTHREE()*x/Pi;
266     if (ThreeExOverPi.info(info_flags::integer)) {
267         numeric z=mod(ex_to_numeric(ThreeExOverPi),numeric(3));
268         if (z.is_equal(numZERO()))
269             return exZERO();
270         if (z.is_equal(numONE()))
271             return power(exTHREE(),exHALF());
272         if (z.is_equal(numTWO()))
273             return -power(exTHREE(),exHALF());
274     }
275     
276     // tan((2n+1)*Pi/2) -> throw
277     ex ExOverPiMinusHalf=x/Pi-exHALF();
278     if (ExOverPiMinusHalf.info(info_flags::integer))
279         throw (std::domain_error("tan_eval(): infinity"));
280     
281     if (is_ex_exactly_of_type(x, function)) {
282         ex t=x.op(0);
283         // tan(atan(x)) -> x
284         if (is_ex_the_function(x, atan))
285             return t;
286         // tan(asin(x)) -> x*(1+x^2)^(-1/2)
287         if (is_ex_the_function(x, asin))
288             return t*power(exONE()-power(t,exTWO()),exMINUSHALF());
289         // tan(acos(x)) -> (1-x^2)^(1/2)/x
290         if (is_ex_the_function(x, acos))
291             return power(t,exMINUSONE())*power(exONE()-power(t,exTWO()),exHALF());
292     }
293     
294     // tan(float) -> float
295     if (x.info(info_flags::numeric) && !x.info(info_flags::rational)) {
296         return tan_evalf(x);
297     }
298     
299     return tan(x).hold();
300 }
301
302 static ex tan_diff(ex const & x, unsigned diff_param)
303 {
304     GINAC_ASSERT(diff_param==0);
305     
306     return (1+power(tan(x),exTWO()));
307 }
308
309 REGISTER_FUNCTION(tan, tan_eval, tan_evalf, tan_diff, NULL);
310
311 //////////
312 // inverse sine (arc sine)
313 //////////
314
315 static ex asin_evalf(ex const & x)
316 {
317     BEGIN_TYPECHECK
318        TYPECHECK(x,numeric)
319     END_TYPECHECK(asin(x))
320     
321     return asin(ex_to_numeric(x)); // -> numeric asin(numeric)
322 }
323
324 static ex asin_eval(ex const & x)
325 {
326     if (x.info(info_flags::numeric)) {
327         // asin(0) -> 0
328         if (x.is_zero())
329             return x;
330         // asin(1/2) -> Pi/6
331         if (x.is_equal(exHALF()))
332             return numeric(1,6)*Pi;
333         // asin(1) -> Pi/2
334         if (x.is_equal(exONE()))
335             return numeric(1,2)*Pi;
336         // asin(-1/2) -> -Pi/6
337         if (x.is_equal(exMINUSHALF()))
338             return numeric(-1,6)*Pi;
339         // asin(-1) -> -Pi/2
340         if (x.is_equal(exMINUSONE()))
341             return numeric(-1,2)*Pi;
342         // asin(float) -> float
343         if (!x.info(info_flags::rational))
344             return asin_evalf(x);
345     }
346     
347     return asin(x).hold();
348 }
349
350 static ex asin_diff(ex const & x, unsigned diff_param)
351 {
352     GINAC_ASSERT(diff_param==0);
353     
354     return power(1-power(x,exTWO()),exMINUSHALF());
355 }
356
357 REGISTER_FUNCTION(asin, asin_eval, asin_evalf, asin_diff, NULL);
358
359 //////////
360 // inverse cosine (arc cosine)
361 //////////
362
363 static ex acos_evalf(ex const & x)
364 {
365     BEGIN_TYPECHECK
366        TYPECHECK(x,numeric)
367     END_TYPECHECK(acos(x))
368     
369     return acos(ex_to_numeric(x)); // -> numeric acos(numeric)
370 }
371
372 static ex acos_eval(ex const & x)
373 {
374     if (x.info(info_flags::numeric)) {
375         // acos(1) -> 0
376         if (x.is_equal(exONE()))
377             return exZERO();
378         // acos(1/2) -> Pi/3
379         if (x.is_equal(exHALF()))
380             return numeric(1,3)*Pi;
381         // acos(0) -> Pi/2
382         if (x.is_zero())
383             return numeric(1,2)*Pi;
384         // acos(-1/2) -> 2/3*Pi
385         if (x.is_equal(exMINUSHALF()))
386             return numeric(2,3)*Pi;
387         // acos(-1) -> Pi
388         if (x.is_equal(exMINUSONE()))
389             return Pi;
390         // acos(float) -> float
391         if (!x.info(info_flags::rational))
392             return acos_evalf(x);
393     }
394     
395     return acos(x).hold();
396 }
397
398 static ex acos_diff(ex const & x, unsigned diff_param)
399 {
400     GINAC_ASSERT(diff_param==0);
401     
402     return numMINUSONE()*power(1-power(x,exTWO()),exMINUSHALF());
403 }
404
405 REGISTER_FUNCTION(acos, acos_eval, acos_evalf, acos_diff, NULL);
406
407 //////////
408 // inverse tangent (arc tangent)
409 //////////
410
411 static ex atan_evalf(ex const & x)
412 {
413     BEGIN_TYPECHECK
414         TYPECHECK(x,numeric)
415     END_TYPECHECK(atan(x))
416     
417     return atan(ex_to_numeric(x)); // -> numeric atan(numeric)
418 }
419
420 static ex atan_eval(ex const & x)
421 {
422     if (x.info(info_flags::numeric)) {
423         // atan(0) -> 0
424         if (x.is_equal(exZERO()))
425             return exZERO();
426         // atan(float) -> float
427         if (!x.info(info_flags::rational))
428             return atan_evalf(x);
429     }
430     
431     return atan(x).hold();
432 }    
433
434 static ex atan_diff(ex const & x, unsigned diff_param)
435 {
436     GINAC_ASSERT(diff_param==0);
437
438     return power(1+x*x, -1);
439 }
440
441 REGISTER_FUNCTION(atan, atan_eval, atan_evalf, atan_diff, NULL);
442
443 //////////
444 // inverse tangent (atan2(y,x))
445 //////////
446
447 static ex atan2_evalf(ex const & y, ex const & x)
448 {
449     BEGIN_TYPECHECK
450         TYPECHECK(y,numeric)
451         TYPECHECK(x,numeric)
452     END_TYPECHECK(atan2(y,x))
453     
454     return atan(ex_to_numeric(y),ex_to_numeric(x)); // -> numeric atan(numeric)
455 }
456
457 static ex atan2_eval(ex const & y, ex const & x)
458 {
459     if (y.info(info_flags::numeric) && !y.info(info_flags::rational) &&
460         x.info(info_flags::numeric) && !x.info(info_flags::rational)) {
461         return atan2_evalf(y,x);
462     }
463     
464     return atan2(y,x).hold();
465 }    
466
467 static ex atan2_diff(ex const & y, ex const & x, unsigned diff_param)
468 {
469     GINAC_ASSERT(diff_param<2);
470     
471     if (diff_param==0) {
472         // d/dy atan(y,x)
473         return x*pow(pow(x,2)+pow(y,2),-1);
474     }
475     // d/dx atan(y,x)
476     return -y*pow(pow(x,2)+pow(y,2),-1);
477 }
478
479 REGISTER_FUNCTION(atan2, atan2_eval, atan2_evalf, atan2_diff, NULL);
480
481 //////////
482 // hyperbolic sine (trigonometric function)
483 //////////
484
485 static ex sinh_evalf(ex const & x)
486 {
487     BEGIN_TYPECHECK
488        TYPECHECK(x,numeric)
489     END_TYPECHECK(sinh(x))
490     
491     return sinh(ex_to_numeric(x)); // -> numeric sinh(numeric)
492 }
493
494 static ex sinh_eval(ex const & x)
495 {
496     if (x.info(info_flags::numeric)) {
497         // sinh(0) -> 0
498         if (x.is_zero())
499             return exZERO();
500         // sinh(float) -> float
501         if (!x.info(info_flags::rational))
502             return sinh_evalf(x);
503     }
504     
505     if (is_ex_exactly_of_type(x, function)) {
506         ex t=x.op(0);
507         // sinh(asinh(x)) -> x
508         if (is_ex_the_function(x, asinh))
509             return t;
510         // sinh(acosh(x)) -> (x-1)^(1/2) * (x+1)^(1/2)
511         if (is_ex_the_function(x, acosh))
512             return power(t-exONE(),exHALF())*power(t+exONE(),exHALF());
513         // sinh(atanh(x)) -> x*(1-x^2)^(-1/2)
514         if (is_ex_the_function(x, atanh))
515             return t*power(exONE()-power(t,exTWO()),exMINUSHALF());
516     }
517     
518     return sinh(x).hold();
519 }
520
521 static ex sinh_diff(ex const & x, unsigned diff_param)
522 {
523     GINAC_ASSERT(diff_param==0);
524     
525     return cosh(x);
526 }
527
528 REGISTER_FUNCTION(sinh, sinh_eval, sinh_evalf, sinh_diff, NULL);
529
530 //////////
531 // hyperbolic cosine (trigonometric function)
532 //////////
533
534 static ex cosh_evalf(ex const & x)
535 {
536     BEGIN_TYPECHECK
537        TYPECHECK(x,numeric)
538     END_TYPECHECK(cosh(x))
539     
540     return cosh(ex_to_numeric(x)); // -> numeric cosh(numeric)
541 }
542
543 static ex cosh_eval(ex const & x)
544 {
545     if (x.info(info_flags::numeric)) {
546         // cosh(0) -> 1
547         if (x.is_zero())
548             return exONE();
549         // cosh(float) -> float
550         if (!x.info(info_flags::rational))
551             return cosh_evalf(x);
552     }
553     
554     if (is_ex_exactly_of_type(x, function)) {
555         ex t=x.op(0);
556         // cosh(acosh(x)) -> x
557         if (is_ex_the_function(x, acosh))
558             return t;
559         // cosh(asinh(x)) -> (1+x^2)^(1/2)
560         if (is_ex_the_function(x, asinh))
561             return power(exONE()+power(t,exTWO()),exHALF());
562         // cosh(atanh(x)) -> (1-x^2)^(-1/2)
563         if (is_ex_the_function(x, atanh))
564             return power(exONE()-power(t,exTWO()),exMINUSHALF());
565     }
566     
567     return cosh(x).hold();
568 }
569
570 static ex cosh_diff(ex const & x, unsigned diff_param)
571 {
572     GINAC_ASSERT(diff_param==0);
573     
574     return sinh(x);
575 }
576
577 REGISTER_FUNCTION(cosh, cosh_eval, cosh_evalf, cosh_diff, NULL);
578
579 //////////
580 // hyperbolic tangent (trigonometric function)
581 //////////
582
583 static ex tanh_evalf(ex const & x)
584 {
585     BEGIN_TYPECHECK
586        TYPECHECK(x,numeric)
587     END_TYPECHECK(tanh(x))
588     
589     return tanh(ex_to_numeric(x)); // -> numeric tanh(numeric)
590 }
591
592 static ex tanh_eval(ex const & x)
593 {
594     if (x.info(info_flags::numeric)) {
595         // tanh(0) -> 0
596         if (x.is_zero())
597             return exZERO();
598         // tanh(float) -> float
599         if (!x.info(info_flags::rational))
600             return tanh_evalf(x);
601     }
602     
603     if (is_ex_exactly_of_type(x, function)) {
604         ex t=x.op(0);
605         // tanh(atanh(x)) -> x
606         if (is_ex_the_function(x, atanh))
607             return t;
608         // tanh(asinh(x)) -> x*(1+x^2)^(-1/2)
609         if (is_ex_the_function(x, asinh))
610             return t*power(exONE()+power(t,exTWO()),exMINUSHALF());
611         // tanh(acosh(x)) -> (x-1)^(1/2)*(x+1)^(1/2)/x
612         if (is_ex_the_function(x, acosh))
613             return power(t-exONE(),exHALF())*power(t+exONE(),exHALF())*power(t,exMINUSONE());
614     }
615     
616     return tanh(x).hold();
617 }
618
619 static ex tanh_diff(ex const & x, unsigned diff_param)
620 {
621     GINAC_ASSERT(diff_param==0);
622     
623     return exONE()-power(tanh(x),exTWO());
624 }
625
626 REGISTER_FUNCTION(tanh, tanh_eval, tanh_evalf, tanh_diff, NULL);
627
628 //////////
629 // inverse hyperbolic sine (trigonometric function)
630 //////////
631
632 static ex asinh_evalf(ex const & x)
633 {
634     BEGIN_TYPECHECK
635        TYPECHECK(x,numeric)
636     END_TYPECHECK(asinh(x))
637     
638     return asinh(ex_to_numeric(x)); // -> numeric asinh(numeric)
639 }
640
641 static ex asinh_eval(ex const & x)
642 {
643     if (x.info(info_flags::numeric)) {
644         // asinh(0) -> 0
645         if (x.is_zero())
646             return exZERO();
647         // asinh(float) -> float
648         if (!x.info(info_flags::rational))
649             return asinh_evalf(x);
650     }
651     
652     return asinh(x).hold();
653 }
654
655 static ex asinh_diff(ex const & x, unsigned diff_param)
656 {
657     GINAC_ASSERT(diff_param==0);
658     
659     return power(1+power(x,exTWO()),exMINUSHALF());
660 }
661
662 REGISTER_FUNCTION(asinh, asinh_eval, asinh_evalf, asinh_diff, NULL);
663
664 //////////
665 // inverse hyperbolic cosine (trigonometric function)
666 //////////
667
668 static ex acosh_evalf(ex const & x)
669 {
670     BEGIN_TYPECHECK
671        TYPECHECK(x,numeric)
672     END_TYPECHECK(acosh(x))
673     
674     return acosh(ex_to_numeric(x)); // -> numeric acosh(numeric)
675 }
676
677 static ex acosh_eval(ex const & x)
678 {
679     if (x.info(info_flags::numeric)) {
680         // acosh(0) -> Pi*I/2
681         if (x.is_zero())
682             return Pi*I*numeric(1,2);
683         // acosh(1) -> 0
684         if (x.is_equal(exONE()))
685             return exZERO();
686         // acosh(-1) -> Pi*I
687         if (x.is_equal(exMINUSONE()))
688             return Pi*I;
689         // acosh(float) -> float
690         if (!x.info(info_flags::rational))
691             return acosh_evalf(x);
692     }
693     
694     return acosh(x).hold();
695 }
696
697 static ex acosh_diff(ex const & x, unsigned diff_param)
698 {
699     GINAC_ASSERT(diff_param==0);
700     
701     return power(x-1,exMINUSHALF())*power(x+1,exMINUSHALF());
702 }
703
704 REGISTER_FUNCTION(acosh, acosh_eval, acosh_evalf, acosh_diff, NULL);
705
706 //////////
707 // inverse hyperbolic tangent (trigonometric function)
708 //////////
709
710 static ex atanh_evalf(ex const & x)
711 {
712     BEGIN_TYPECHECK
713        TYPECHECK(x,numeric)
714     END_TYPECHECK(atanh(x))
715     
716     return atanh(ex_to_numeric(x)); // -> numeric atanh(numeric)
717 }
718
719 static ex atanh_eval(ex const & x)
720 {
721     if (x.info(info_flags::numeric)) {
722         // atanh(0) -> 0
723         if (x.is_zero())
724             return exZERO();
725         // atanh({+|-}1) -> throw
726         if (x.is_equal(exONE()) || x.is_equal(exONE()))
727             throw (std::domain_error("atanh_eval(): infinity"));
728         // atanh(float) -> float
729         if (!x.info(info_flags::rational))
730             return atanh_evalf(x);
731     }
732     
733     return atanh(x).hold();
734 }
735
736 static ex atanh_diff(ex const & x, unsigned diff_param)
737 {
738     GINAC_ASSERT(diff_param==0);
739     
740     return power(exONE()-power(x,exTWO()),exMINUSONE());
741 }
742
743 REGISTER_FUNCTION(atanh, atanh_eval, atanh_evalf, atanh_diff, NULL);
744
745 #ifndef NO_GINAC_NAMESPACE
746 } // namespace GiNaC
747 #endif // ndef NO_GINAC_NAMESPACE