Tcl Library Source Code

Check-in [7538200f88]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2018 Conference, Houston/TX, US, Oct 15-19
Send your abstracts to tclconference@googlegroups.com
or submit via the online form by Aug 20.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add a package for trigonometric functions that use angles in degrees and additional trigonometric and hyperbolic functions, including their inverses.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:7538200f88f56f530829e2574509ef2dc3ae87741ee7797b7110b643216b1fa0
User & Date: arjenmarkus 2018-07-22 11:38:30
Context
2018-08-14
19:16
Fix issue [a16b1095974e071d], error in mime.tcl check-in: 074ec6a961 user: pooryorick tags: trunk
2018-07-22
11:38
Add a package for trigonometric functions that use angles in degrees and additional trigonometric and hyperbolic functions, including their inverses. check-in: 7538200f88 user: arjenmarkus tags: trunk
2018-07-19
19:31
Change the criterium for detecting if two circles touch - use the mean of the two radii check-in: 6124e077a8 user: arjenmarkus tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to modules/math/ChangeLog.







1
2
3
4
5
6
7






2018-07-19  Arjen Markus <arjenmarkus@users.sourceforge.net>
	* math_geometry.man: Describe the new criterium

2018-06-23  Arjen Markus <arjenmarkus@users.sourceforge.net>
	* geometry_circle.tcl: Use the mean radius as criterium for touching circles (suggestion Andreas Kupries)

2018-06-18  Arjen Markus <arjenmarkus@users.sourceforge.net>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
2018-07-22  Arjen Markus <arjenmarkus@users.sourceforge.net>
	* trig.tcl: Implementation of (additional) trigonometric functions - package math::trig
	* trig.test: Trst for the (additional) trigonometric functions
	* trig.man: Documentation for the (additional) trigonometric functions
	* pkgIndex.tcl: Add the new package

2018-07-19  Arjen Markus <arjenmarkus@users.sourceforge.net>
	* math_geometry.man: Describe the new criterium

2018-06-23  Arjen Markus <arjenmarkus@users.sourceforge.net>
	* geometry_circle.tcl: Use the mean radius as criterium for touching circles (suggestion Andreas Kupries)

2018-06-18  Arjen Markus <arjenmarkus@users.sourceforge.net>

Changes to modules/math/pkgIndex.tcl.

24
25
26
27
28
29
30

31
32
33
package ifneeded math::statistics        1.2.0 [list source [file join $dir statistics.tcl]]
package ifneeded math::linearalgebra     1.1.6 [list source [file join $dir linalg.tcl]]
package ifneeded math::calculus::symdiff 1.0.1 [list source [file join $dir symdiff.tcl]]
package ifneeded math::bigfloat          2.0.2 [list source [file join $dir bigfloat2.tcl]]
package ifneeded math::numtheory         1.1.1 [list source [file join $dir numtheory.tcl]]
package ifneeded math::decimal           1.0.3 [list source [file join $dir decimal.tcl]]
package ifneeded math::geometry          1.3.0 [list source [file join $dir geometry.tcl]]

if {![package vsatisfies [package require Tcl] 8.6]} {return}
package ifneeded math::exact             1.0   [list source [file join $dir exact.tcl]]
package ifneeded math::PCA               1.0   [list source [file join $dir pca.tcl]]







>



24
25
26
27
28
29
30
31
32
33
34
package ifneeded math::statistics        1.2.0 [list source [file join $dir statistics.tcl]]
package ifneeded math::linearalgebra     1.1.6 [list source [file join $dir linalg.tcl]]
package ifneeded math::calculus::symdiff 1.0.1 [list source [file join $dir symdiff.tcl]]
package ifneeded math::bigfloat          2.0.2 [list source [file join $dir bigfloat2.tcl]]
package ifneeded math::numtheory         1.1.1 [list source [file join $dir numtheory.tcl]]
package ifneeded math::decimal           1.0.3 [list source [file join $dir decimal.tcl]]
package ifneeded math::geometry          1.3.0 [list source [file join $dir geometry.tcl]]
package ifneeded math::trig              1.0   [list source [file join $dir trig.tcl]]
if {![package vsatisfies [package require Tcl] 8.6]} {return}
package ifneeded math::exact             1.0   [list source [file join $dir exact.tcl]]
package ifneeded math::PCA               1.0   [list source [file join $dir pca.tcl]]

Added modules/math/trig.man.







































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
[vset VERSION 1.0.0]
[manpage_begin math::trig n [vset VERSION]]
[keywords math trigonometry]
[copyright {2018 Arjen Markus}]
[moddesc   {Tcl Math Library}]
[titledesc {Trigonometric anf hyperbolic functions}]
[category  Mathematics]
[require Tcl 8.5]
[require math::trig [vset VERSION]]
[description]
[para]
The [term math::trig] package defines a set of trigonomic and hyperbolic functions
and their inverses. In addition it defines versions of the trigonomic functions
that take arguments in degrees instead of radians.
[para]
For easy use these functions may be imported into the [term tcl::mathfunc] namespace,
so that they can be used directly in the [term expr] command.

[section "FUNCTIONS"]
The functions [term radian_reduced] and [term degree_reduced] return a reduced angle, in
respectively radians and degrees, in the intervals [lb]0, 2pi) and [lb]0, 360):

[list_begin definitions]

[call [cmd ::math::trig::radian_reduced] [arg angle]]
Return the equivalent angle in the interval [lb]0, 2pi).
[list_begin arguments]
[arg_def float angle] Angle (in radians)
[list_end]

[call [cmd ::math::trig::degree_reduced] [arg angle]]
Return the equivalent angle in the interval [lb]0, 360).
[list_begin arguments]
[arg_def float angle] Angle (in degrees)
[list_end]

[list_end]


The following trigonomic functions are defined in addition to the ones defined
in the [term expr] command:

[list_begin definitions]

[call [cmd ::math::trig::cosec] [arg angle]]
Calculate the cosecant of the angle (1/cos(angle))
[list_begin arguments]
[arg_def float angle] Angle (in radians)
[list_end]

[call [cmd ::math::trig::sec] [arg angle]]
Calculate the secant of the angle (1/sin(angle))
[list_begin arguments]
[arg_def float angle] Angle (in radians)
[list_end]

[call [cmd ::math::trig::cotan] [arg angle]]
Calculate the cotangent of the angle (1/tan(angle))
[list_begin arguments]
[arg_def float angle] Angle (in radians)
[list_end]

[list_end]


For these functions also the inverses are defined:

[list_begin definitions]

[call [cmd ::math::trig::acosec] [arg value]]
Calculate the arc cosecant of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::asec] [arg value]]
Calculate the arc secant of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::acotan] [arg value]]
Calculate the arc cotangent of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[list_end]

The following hyperbolic and inverse hyperbolic functions are defined:

[list_begin definitions]

[call [cmd ::math::trig::cosech] [arg value]]
Calculate the hyperbolic cosecant of the value (1/sinh(value))
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::sech] [arg value]]
Calculate the hyperbolic secant of the value (1/cosh(value))
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::cotanh] [arg value]]
Calculate the hyperbolic cotangent of the value (1/tanh(value))
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::asinh] [arg value]]
Calculate the arc hyperbolic sine of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::acosh] [arg value]]
Calculate the arc hyperbolic cosine of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::atanh] [arg value]]
Calculate the arc hyperbolic tangent of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::acosech] [arg value]]
Calculate the arc hyperbolic cosecant of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::asech] [arg value]]
Calculate the arc hyperbolic secant of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[call [cmd ::math::trig::acotanh] [arg value]]
Calculate the arc hyperbolic cotangent of the value
[list_begin arguments]
[arg_def float value] Value of the argument
[list_end]

[list_end]

The following versions of the common trigonometric functions and their
inverses are defined:

[list_begin definitions]

[call [cmd ::math::trig::sind] [arg angle]]
Calculate the sine of the angle (in degrees)
[list_begin arguments]
[arg_def float angle] Angle (in degrees)
[list_end]

[call [cmd ::math::trig::cosd] [arg angle]]
Calculate the cosine of the angle (in degrees)
[list_begin arguments]
[arg_def float angle] Angle (in radians)
[list_end]

[call [cmd ::math::trig::tand] [arg angle]]
Calculate the cotangent of the angle (in degrees)
[list_begin arguments]
[arg_def float angle] Angle (in degrees)
[list_end]

[call [cmd ::math::trig::cosecd] [arg angle]]
Calculate the cosecant of the angle (in degrees)
[list_begin arguments]
[arg_def float angle] Angle (in degrees)
[list_end]

[call [cmd ::math::trig::secd] [arg angle]]
Calculate the secant of the angle (in degrees)
[list_begin arguments]
[arg_def float angle] Angle (in degrees)
[list_end]

[call [cmd ::math::trig::cotand] [arg angle]]
Calculate the cotangent of the angle (in degrees)
[list_begin arguments]
[arg_def float angle] Angle (in degrees)
[list_end]

[list_end]

[vset CATEGORY {math :: trig}]
[include ../doctools2base/include/feedback.inc]
[manpage_end]

Added modules/math/trig.tcl.





































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# trig.tcl --
#     Package for additional trigonometric and hyperbolic functions
#
#     Besides the ordinary functions that take/return angles in radians,
#     also versions that use angles in degrees.
#
#     See tickets https://core.tcl-lang.org/tcllib/tktview/df3676c9821ba917c5cfda68c2ea0ad7a6947ac9
#     and https://core.tcl-lang.org/tcllib/tktview/21fef042b91ca252fc2a20ead5cd05c2b51b1776
#
package provide math::trig 1.0

# ::math::trig --
#     Create the namespace
#
namespace eval ::math::trig {
    namespace export radian_reduced degree_reduced \
                     cosec sec cotan acosec asec acotan \
                     cosech sech cotanh asinh acosh atanh acosech asech acotanh \
                     sind cosd tand cosecd secd cotand acosecd asecd acotand

    variable pi     [expr {acos(-1.0)}]
    variable twopi  [expr {2.0 * acos(-1.0)}]
    variable halfpi [expr {0.5 * acos(-1.0)}]
    variable torad  [expr {acos(-1.0) / 180.0}]
    variable todeg  [expr {180.0 / acos(-1.0)}]
}

# radian_reduced --
#     Reduce the given value to a value in the interval [0,2pi]
#
# Arguments:
#     angle      Angle in radians that should be reduced
#
# Note:
#     If the angle is very large, then numerical accuracy will
#     diminish, up to a point where the answer is meaningless.
#     This is not (yet) taken care of.
#
proc ::math::trig::radian_reduced {angle} {
    variable twopi

    set n       [expr {int($angle/$twopi)}]
    set reduced [expr {$angle - $n * $twopi}]
    if { $reduced < 0.0 } {
        set reduced [expr {$reduced + $twopi}]
    }

    return $reduced
}

# degree_reduced --
#     Reduce the given value to a value in the interval [0,360]
#
# Arguments:
#     angle      Angle in degrees that should be reduced
#
# Note:
#     If the angle is very large, then numerical accuracy will
#     diminish, up to a point where the answer is meaningless.
#     This is not (yet) taken care of.
#
proc ::math::trig::degree_reduced {angle} {

    set n       [expr {int($angle/360.0)}]
    set reduced [expr {$angle - $n * 360.0}]
    if { $reduced < 0.0 } {
        set reduced [expr {$reduced + 360.0}]
    }

    return $reduced
}


# cosec, sec, cotan --
#     Calculate the cosecans, secans and cotangent
#
# Arguments:
#     angle      Angle in radians
#
proc ::math::trig::cosec {angle} {
    return [expr {1.0 / sin($angle)}]
}

proc ::math::trig::sec {angle} {
    return [expr {1.0 / cos($angle)}]
}

proc ::math::trig::cotan {angle} {
    variable halfpi
    return [expr {tan($halfpi - $angle)}]
}


# cosech, sech, cotanh --
#     Calculate the hyperbolic cosecans, secans and cotangent
#
# Arguments:
#     value      Argument value
#
proc ::math::trig::cosech {value} {
    return [expr {1.0 / sinh($value)}]
}

proc ::math::trig::sech {value} {
    return [expr {1.0 / cosh($value)}]
}

proc ::math::trig::cotanh {value} {
    return [expr {1.0/tanh($value)}]
}


# asinh, acosh, atanh --
#     Calculate the arc hyperbolic sine, cosine and tangent
#
# Arguments:
#     value      Argument value
#
proc ::math::trig::asinh {value} {
    return [expr {log($value + sqrt($value**2 + 1.0))}]
}

proc ::math::trig::acosh {value} {
    if { $value < 1.0 } {
        return -code error "acosh: argument should be larger/equal 1.0"
    }
    return [expr {log($value + sqrt($value**2 - 1.0))}]
}

proc ::math::trig::atanh {value} {
    if { $value <= -1.0 || $value >= 1.0} {
        return -code error "atanh: argument should be between -1.0 and 1.0"
    }
    return [expr {0.5 * log((1.0 + $value) / (1.0 - $value))}]
}


# acosec, asec, acotan --
#     Calculate the arc cosecans, secans and cotangent
#
# Arguments:
#     value      Value for which the angle is sought
#
proc ::math::trig::acosec {value} {
    return [expr {asin(1.0/$value)}]
}

proc ::math::trig::asec {value} {
    return [expr {1.0 / acos($value)}]
}

proc ::math::trig::acotan {value} {
    variable halfpi
    return [expr {atan($halfpi - $angle)}]
}


# acosech, asech, acotanh --
#     Calculate the arc hyperbolic cosecans, secans and cotangent
#
# Arguments:
#     value      Value for which the angle is sought
#
proc ::math::trig::acosech {value} {
    return [asinh [expr {1.0/$value}]]
}

proc ::math::trig::asech {value} {
    return [acosh [expr {1.0/$value}]]
}

proc ::math::trig::acotanh {value} {
    return [atanh [expr {1.0/$value}]]
}

# cossind --
#     Reduce the angle (in degrees) to ensure exact results
#     for multiples of 90 degrees.
#
# Arguments:
#     angle           Angle in degrees
#
# Result:
#     sine and cosine of the angle
#
proc ::math::trig::cossind {angle} {
    variable torad

    set angle [::math::trig::degree_reduced $angle]

    if { $angle <= 45.0 || $angle >= 315.0 } {
        set sind [expr {sin($angle*$torad)}]
        set cosd [expr {cos($angle*$torad)}]
    } elseif { $angle <= 135.0 } {
        set sind [expr {cos(($angle-90.0)*$torad)}]
        set cosd [expr {-sin(($angle-90.0)*$torad)}]
    } elseif { $angle <= 225.0 } {
        set sind [expr {-sin(($angle-180.0)*$torad)}]
        set cosd [expr {-cos(($angle-180.0)*$torad)}]
    } else { ;#elseif { $angle <= 315.0 }
        set sind [expr {-cos(($angle-270.0)*$torad)}]
        set cosd [expr {sin(($angle-270.0)*$torad)}]
    }

    return [list $sind $cosd]
}

# cosd, sind, tand, cosecd, secd, cotand --
#     Trigonometric functions taking the angle in degrees
#
# Arguments:
#     angle            Angle in degrees
#
# Result:
#     cosine, sine, etc. of the given angle
#
proc ::math::trig::cosd {angle} {
    return [lindex [cossind $angle] 1]
}

proc ::math::trig::sind {angle} {
    return [lindex [cossind $angle] 0]
}

proc ::math::trig::tand {angle} {
    lassign [cossind $angle] s c
    return [expr {$s / $c}]
}

proc ::math::trig::cosecd {angle} {
    lassign [cossind $angle] s c
    return [expr {1.0 / $s}]
}

proc ::math::trig::secd {angle} {
    lassign [cossind $angle] s c
    return [expr {1.0 / $c}]
}

proc ::math::trig::cotand {angle} {
    lassign [cossind $angle] s c
    return [expr {$c / $s}]
}

# inverse trigonometric functions
# Do not bother with exactitude in this case
#
proc ::math::trig::acosd {value} {
    variable todeg
    return [expr {$todeg * acos($value)}]
}

proc ::math::trig::asind {value} {
    variable todeg
    return [expr {$todeg * asin($value)}]
}

proc ::math::trig::atand {value} {
    variable todeg
    return [expr {$todeg * atan($value)}]
}

proc ::math::trig::acosecd {value} {
    variable todeg
    return [expr {$todeg * asin(1.0/$value)}]
}

proc ::math::trig::asecd {value} {
    variable todeg
    return [expr {$todeg * acos(1.0/$value)}]
}
proc ::math::trig::acotand {value} {
    variable todeg
    return [expr {$todeg * atan(1.0/$value)}]
}

# tests --
if {0} {
foreach angle {0 30 45 60 90 120 135 150 180 210 225 240 270 300 315 330 360} {
    puts "$angle -- [::math::trig::sind $angle] -- [::math::trig::cosd $angle] [::math::trig::tand $angle]"
}

# simple test
foreach angle {-10 -7 -4 -1 0 2 5 8 11} {
    puts "$angle -- [::math::trig::radian_reduced $angle]"
}
foreach angle {-10 -7 -4 -1 0 2 5 8 11} {
    puts "$angle -- [expr {101.0*$angle}] -- [::math::trig::degree_reduced [expr {101.0*$angle}]]"
}
}

Added modules/math/trig.test.



































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# -*- tcl -*-
# trig.test --
#    Test cases for the ::math::trig package
#
# Note:
#    The tests assume tcltest 2.1, in order to compare
#    floating-point results

# -------------------------------------------------------------------------

source [file join \
        [file dirname [file dirname [file join [pwd] [info script]]]] \
        devtools testutilities.tcl]

testsNeedTcl     8.5
testsNeedTcltest 2.1

support {
    useLocal math.tcl math
    useLocal linalg.tcl math::linearalgebra
}
testing {
    useLocal trig.tcl math::trig
}
#
# Create and register (in that order!) custom matching procedures
#
proc matchTolerant { expected actual } {
   set match 1
   foreach a $actual e $expected {
      if { abs($e-$a)>0.0001*abs($e) &&
           abs($e-$a)>0.0001*abs($a)     } {
         set match 0
         break
      }
   }
   return $match
}

proc matchExact { expected actual } {
   set match 1
   foreach a $actual e $expected {
      if { abs($e-$a) != 0.0 } {
         set match 0
         break
      }
   }
   return $match
}

customMatch tolerant   matchTolerant
customMatch exact      matchExact

#
# Test cases: reduction routines
#
set pi [expr {acos(-1.0)}]
test "Trig-1.1" "Reduction function - radians" -match tolerant -body {
    global pi
    set result {}
    foreach a {-4 -3 -2 -1 0 1 2 3 4} {
        set angle [expr {$a * $pi}]
        lappend result [::math::trig::radian_reduced $angle]
    }
    return $result
} -result [list 0.0 $pi 0.0 $pi 0.0 $pi 0.0 $pi 0.0]

test "Trig-1.2" "Reduction function - degrees" -match tolerant -body {
    set result {}
    foreach a {-4 -3 -2 -1 0 1 2 3 4} {
        set angle [expr {$a * 180.0}]
        lappend result [::math::trig::degree_reduced $angle]
    }
    return $result
} -result [list 0.0 180.0 0.0 180.0 0.0 180.0 0.0 180.0 0.0]

#
# Test cases: additional trigonometric functions and their inverses
#
test "Trig-2.1" "Additional trigonometric functions - cosecant" -match tolerant -body {
    set result {}
    for {set i 1} {$i <= 100} {incr i} {
        set angle [expr {0.1 * $i}]
        lappend result [expr { sin($angle) * [::math::trig::cosec $angle]}]
    }
    return $result
} -result [lrepeat 100 1.0]

test "Trig-2.2" "Additional trigonometric functions - secant" -match tolerant -body {
    set result {}
    for {set i 0} {$i < 100} {incr i} {
        set angle [expr {0.1 * $i}]
        lappend result [expr { cos($angle) * [::math::trig::sec $angle]}]
    }
    return $result
} -result [lrepeat 100 1.0]

test "Trig-2.3" "Additional trigonometric functions - cotangent" -match tolerant -body {
    set result {}
    for {set i 1} {$i <= 100} {incr i} {
        set angle [expr {0.1 * $i}]
        lappend result [expr { tan($angle) * [::math::trig::cotan $angle]}]
    }
    return $result
} -result [lrepeat 100 1.0]

# Make sure the values are in the interval (0,pi) for sec and cotan to avoid infinity
# and (-pi/2,pi/2) for cosec
set argvalues_pi {}
set argvalues_halfpi {}
for {set i 1} {$i <= 30} {incr i} {
    set angle_pi     [expr {0.1 * $i}]
    set angle_halfpi [expr {0.1 * ($i-15)}]
    lappend argvalues $angle
}

test "Trig-3.1" "Inverse trigonometric functions - arc-cosecant" -match tolerant -body {
    global argvalues_halfpi
    set result {}
    foreach angle $argvalues_halfpi {
        set value [::math::trig::cosec $angle]
        lappend result [::math::trig::acosec $value]
    }
    return $result
} -result $argvalues_halfpi

test "Trig-3.2" "Inverse trigonometric functions - arc-secant" -match tolerant -body {
    global argvalues_pi
    set result {}
    foreach angle $argvalues_pi {
        set value [::math::trig::sec $angle]
        lappend result [::math::trig::asec $value]
    }
    return $result
} -result $argvalues_pi

test "Trig-3.3" "Inverse trigonometric functions - arc-cotangent" -match tolerant -body {
    global argvalues_pi
    set result {}
    foreach angle $argvalues_pi {
        set value [::math::trig::cotan $angle]
        lappend result [::math::trig::acotan $value]
    }
    return $result
} -result $argvalues_pi

#
# Test cases: hyperbolic functions and their inverses
#
test "Trig-4.1" "Additional hyperbolic functions - hyperbolic cosecant" -match tolerant -body {
    set result {}
    for {set i -20} {$i <= 20} {incr i} {
        set arg   [expr {$i * 0.2 + 0.01}] ;# Avoid zero
        lappend result [expr {sinh($arg) * [::math::trig::cosech $arg]}]
    }
    return $result
} -result [lrepeat 41 1.0]

test "Trig-4.2" "Additional hyperbolic functions - hyperbolic secant" -match tolerant -body {
    set result {}
    for {set i -20} {$i <= 20} {incr i} {
        set arg   [expr {$i * 0.2 + 0.01}] ;# Avoid zero, for symmetry only
        lappend result [expr {cosh($arg) * [::math::trig::sech $arg]}]
    }
    return $result
} -result [lrepeat 41 1.0]

test "Trig-4.3" "Additional hyperbolic functions - hyperbolic contangent" -match tolerant -body {
    set result {}
    for {set i -20} {$i <= 20} {incr i} {
        set arg   [expr {$i * 0.2 + 0.01}] ;# Avoid zero
        lappend result [expr {tanh($arg) * [::math::trig::cotanh $arg]}]
    }
    return $result
} -result [lrepeat 41 1.0]


set argvalues {}
set argvalues_pos {}
for {set i -20} {$i <= 20} {incr i} {
    lappend argvalues     [expr {$i * 0.2 + 0.01}] ;# Avoid zero
    lappend argvalues_pos [expr {abs($i * 0.2 + 0.01)}] ;# Positive values
}

test "Trig-5.1" "Inverse hyperbolic functions - hyperbolic arc sine" -match tolerant -body {
    global argvalues
    set result {}
    foreach arg $argvalues {
        set value [expr {sinh($arg)}]
        lappend result [::math::trig::asinh $value]
    }
    return $result
} -result $argvalues

test "Trig-5.2" "Inverse hyperbolic functions - hyperbolic arc cosine" -match tolerant -body {
    global argvalues
    set result {}
    foreach arg $argvalues {
        set value [expr {cosh($arg)}]
        lappend result [::math::trig::acosh $value]
    }
    return $result
} -result $argvalues_pos

test "Trig-5.3" "Inverse hyperbolic functions - hyperbolic arc tangent" -match tolerant -body {
    global argvalues
    set result {}
    foreach arg $argvalues {
        set value [expr {tanh($arg)}]
        lappend result [::math::trig::atanh $value]
    }
    return $result
} -result $argvalues

test "Trig-5.4" "Inverse hyperbolic functions - hyperbolic arc cosecant" -match tolerant -body {
    global argvalues
    set result {}
    foreach arg $argvalues {
        set value [::math::trig::cosech $arg]
        lappend result [::math::trig::acosech $value]
    }
    return $result
} -result $argvalues

test "Trig-5.5" "Inverse hyperbolic functions - hyperbolic arc secant" -match tolerant -body {
    global argvalues
    set result {}
    foreach arg $argvalues {
        set value [::math::trig::sech $arg]
        lappend result [::math::trig::asech $value]
    }
    return $result
} -result $argvalues_pos

test "Trig-5.6" "Inverse hyperbolic functions - hyperbolic arc cotangent" -match tolerant -body {
    global argvalues
    set result {}
    foreach arg $argvalues {
        set value [::math::trig::cotanh $arg]
        lappend result [::math::trig::acotanh $value]
    }
    return $result
} -result $argvalues

#
# Test cases: trigonometric functions - degree variants
#
set hsqrt2      [expr {0.5 * sqrt(2.0)}]
set hsqrt3      [expr {0.5 * sqrt(3.0)}]
set minhsqrt2   [expr {-0.5 * sqrt(2.0)}]
set minhsqrt3   [expr {-0.5 * sqrt(3.0)}]
set invsqrt3    [expr {1.0 / sqrt(3.0)}]
set sqrt3       [expr {sqrt(3.0)}]
set minsqrt3    [expr {-sqrt(3.0)}]
set mininvsqrt3 [expr {-1.0 / sqrt(3.0)}]
set minhsqrt3   [expr {-0.5 * sqrt(3.0)}]
set minhsqrt2   [expr {-0.5 * sqrt(2.0)}]

test "Trig-6.1" "Trigonometric functions - multiples of 90 degrees" -match exact -body {
    set result {}
    foreach angle {0  90  180 270 360 -90 -180 -270 -360} {
        lappend result [::math::trig::sind $angle] [::math::trig::cosd $angle]
    }
    foreach angle {0 180 360 -180 -360} {
        lappend result [::math::trig::tand $angle]
    }
    return $result
} -result {0.0 1.0  1.0 0.0  0.0 -1.0  -1.0 0.0  0.0 1.0 -1.0 0.0  0.0 -1.0  1.0 0.0  0.0 1.0
           0.0 0.0 0.0 0.0  0.0}
#          0        90       180       270       360     -90       -180      -270     -360
#          0   180 360 -180 -360

test "Trig-6.2" "Trigonometric functions - sine (degrees)" -match tolerant -body {
    set result {}
    foreach angle {0  30  45  60  90 120 135 150 180 210 225 240 270 300 315 330 360} {
        lappend result [::math::trig::sind $angle]
    }
    return $result
} -result [list 0.0 0.5 $hsqrt2 $hsqrt3 1.0  $hsqrt3 $hsqrt2 0.5  0.0  -0.5  $minhsqrt2 $minhsqrt3 -1.0 $minhsqrt3 $minhsqrt2 -0.5  0.0]
#               0   30  45      60      90   120     135     150  180  210   225        240       270  300         315        330   360

test "Trig-6.3" "Trigonometric functions - cosine (degrees)" -match tolerant -body {
    set result {}
    foreach angle {0  30  45  60  90 120 135 150 180 210 225 240 270 300 315 330 360} {
        lappend result [::math::trig::cosd $angle]
    }
    return $result
} -result [list 1.0 $hsqrt3 $hsqrt2 0.5 0.0 -0.5 $minhsqrt2 $minhsqrt3 -1.0 $minhsqrt3 $minhsqrt2 -0.5  0.0  0.5 $hsqrt2 $hsqrt3 1.0]
#               0   30      45      60  90  120  135        150        180   210       225        240   270  300 315     330     360

test "Trig-6.4" "Trigonometric functions - tangent (degrees)" -match tolerant -body {
    set result {}
    foreach angle {0  30  45  60  120 135 150 180} {
        lappend result [::math::trig::tand $angle]
    }
    return $result
} -result [list 0.0 $invsqrt3 1.0 $sqrt3 $minsqrt3 -1.0 $mininvsqrt3 0.0]
#               0   30        45      60 120       135  150          180

# Inverse trigonometric functions with degrees
# Make sure the values are in the interval (0,180) for cos, sec and cotan to avoid infinity
# and (-pi/2,pi/2) for sin, cosec and tan
set argvalues_180 {}
set argvalues_90 {}
for {set i 1} {$i <= 30} {incr i} {
    set angle_180     [expr {0.1 * $i}]
    set angle_90      [expr {0.1 * ($i-15)}]
    lappend argvalues $angle
}

test "Trig-7.1" "Inverse trigonometric functions (degrees) - arc-sine" -match tolerant -body {
    global argvalues_90
    set result {}
    foreach angle $argvalues_90 {
        set value [::math::trig::sind $angle]
        lappend result [::math::trig::asind $value]
    }
    return $result
} -result $argvalues_90

test "Trig-7.2" "Inverse trigonometric functions (degrees) - arc-cosine" -match tolerant -body {
    global argvalues_180
    set result {}
    foreach angle $argvalues_180 {
        set value [::math::trig::cosd $angle]
        lappend result [::math::trig::acosd $value]
    }
    return $result
} -result $argvalues_180

test "Trig-7.3" "Inverse trigonometric functions (degrees) - arc-tangent" -match tolerant -body {
    global argvalues_90
    set result {}
    foreach angle $argvalues_90 {
        set value [::math::trig::tand $angle]
        lappend result [::math::trig::atand $value]
    }
    return $result
} -result $argvalues_90

test "Trig-7.4" "Inverse trigonometric functions (degrees) - arc-secant" -match tolerant -body {
    global argvalues_180
    set result {}
    foreach angle $argvalues_180 {
        set value [::math::trig::secd $angle]
        lappend result [::math::trig::asecd $value]
    }
    return $result
} -result $argvalues_180

test "Trig-7.5" "Inverse trigonometric functions (degrees) - arc-cosecant" -match tolerant -body {
    global argvalues_90
    set result {}
    foreach angle $argvalues_90 {
        set value [::math::trig::cosecd $angle]
        lappend result [::math::trig::acosecd $value]
    }
    return $result
} -result $argvalues_90

test "Trig-7.6" "Inverse trigonometric functions (degrees) - arc-cotangent" -match tolerant -body {
    global argvalues_180
    set result {}
    foreach angle $argvalues_180 {
        set value [::math::trig::cotand $angle]
        lappend result [::math::trig::acotand $value]
    }
    return $result
} -result $argvalues_180