Skip to content

Commit 4058413

Browse files
committed
test: add native tests
--- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: passed - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed ---
1 parent 512bfe6 commit 4058413

File tree

2 files changed

+379
-0
lines changed

2 files changed

+379
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2024 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var addon = require( './../src/addon.node' );
24+
25+
26+
// MAIN //
27+
28+
/**
29+
* Evaluates the Gaussian hypergeometric function.
30+
*
31+
* @private
32+
* @param {number} a - input value
33+
* @param {number} b - input value
34+
* @param {number} c - input value
35+
* @param {number} x - input value
36+
* @returns {number} function value
37+
*
38+
* @example
39+
* var v = hyp2f1( 1.0, 1.0, 1.0, 0.0 );
40+
* // returns 1.0
41+
*
42+
* @example
43+
* var v = hyp2f1( 10.0, 7.4, -1.8, -0.99 );
44+
* // returns ~0.423
45+
*
46+
* @example
47+
* var v = hyp2f1( 10.0, 1.0, -1.8, -0.99 );
48+
* // returns ~-0.875
49+
*
50+
* @example
51+
* var v = hyp2f1( 2.0, 3.0, 5.0, 0.99 );
52+
* // returns ~27.699
53+
*
54+
* @example
55+
* var v = hyp2f1( 3.0, 4.0, 7.0, 1.0 );
56+
* // returns +Infinity
57+
*
58+
* @example
59+
* var v = hyp2f1( NaN, 3.0, 2.0, 0.5 );
60+
* // returns NaN
61+
*
62+
* @example
63+
* var v = hyp2f1( 1.0, NaN, 2.0, 0.5 );
64+
* // returns NaN
65+
*/
66+
function hyp2f1( a, b, c, x ) {
67+
return addon( a, b, c, x );
68+
}
69+
70+
71+
// EXPORTS //
72+
73+
module.exports = hyp2f1;
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var resolve = require( 'path' ).resolve;
24+
var tape = require( 'tape' );
25+
var abs = require( '@stdlib/math/base/special/abs' );
26+
var isnanf = require( '@stdlib/math/base/assert/is-nanf' );
27+
var EPS = require( '@stdlib/constants/float64/eps' );
28+
var PINF = require( '@stdlib/constants/float64/pinf' );
29+
var tryRequire = require( '@stdlib/utils/try-require' );
30+
31+
32+
// VARIABLES //
33+
34+
var hyp2f1 = tryRequire( resolve( __dirname, './../lib/native.js' ) );
35+
var opts = {
36+
'skip': ( hyp2f1 instanceof Error )
37+
};
38+
39+
40+
// FIXTURES //
41+
42+
var basic = require( './fixtures/python/basic.json' );
43+
var edgeCases1 = require( './fixtures/python/edge_cases1.json' );
44+
var edgeCases2 = require( './fixtures/python/edge_cases2.json' );
45+
var edgeCases3 = require( './fixtures/python/edge_cases3.json' );
46+
var edgeCases4 = require( './fixtures/python/edge_cases4.json' );
47+
var outliers = require( './fixtures/python/outliers.json' );
48+
49+
50+
// TESTS //
51+
52+
tape( 'main export is a function', opts, function test( t ) {
53+
t.ok( true, __filename );
54+
t.strictEqual( typeof hyp2f1, 'function', 'main export is a function' );
55+
t.end();
56+
});
57+
58+
tape( 'the function returns `1` if `x` is 0', opts, function test( t ) {
59+
var v;
60+
61+
v = hyp2f1( 1.0, 1.0, 1.0, 0.0 );
62+
t.strictEqual( v, 1.0, 'returns expected value' );
63+
64+
v = hyp2f1( 1.5, 2.5, 3.5, 0.0 );
65+
t.strictEqual( v, 1.0, 'returns expected value' );
66+
67+
v = hyp2f1( -1, 4, 2, 0.0 );
68+
t.strictEqual( v, 1.0, 'returns expected value' );
69+
70+
t.end();
71+
});
72+
73+
tape( 'the function returns `NaN` if provided `NaN`', opts, function test( t ) {
74+
var v;
75+
76+
v = hyp2f1( NaN, 3.0, 2.0, 0.5 );
77+
t.equal( isnanf( v ), true, 'returns expected value' );
78+
79+
v = hyp2f1( 0.0, NaN, 2.0, 0.5 );
80+
t.equal( isnanf( v ), true, 'returns expected value' );
81+
82+
v = hyp2f1( 0.0, 3.0, NaN, 0.5 );
83+
t.equal( isnanf( v ), true, 'returns expected value' );
84+
85+
v = hyp2f1( 0.0, 3.0, 2.0, NaN );
86+
t.equal( isnanf( v ), true, 'returns expected value' );
87+
88+
t.end();
89+
});
90+
91+
tape( 'the function returns `1` if either `a` or `b` is 0 and `c` is not 0', opts, function test( t ) {
92+
var v;
93+
94+
v = hyp2f1( 0.0, 3.0, 2.0, 0.5 );
95+
t.strictEqual( v, 1.0, 'returns expected value' );
96+
97+
v = hyp2f1( 0.0, -2.0, 4.0, 0.3 );
98+
t.strictEqual( v, 1.0, 'returns expected value' );
99+
100+
v = hyp2f1( 3.0, 0.0, 2.0, 0.5 );
101+
t.strictEqual( v, 1.0, 'returns expected value' );
102+
103+
v = hyp2f1( -2.0, 0.0, 4.0, 0.3 );
104+
t.strictEqual( v, 1.0, 'returns expected value' );
105+
106+
t.end();
107+
});
108+
109+
tape( 'the function returns `PINF` when `c <= a + b`, `x === 1`, and neither `a` nor `b` are nonpositive integers', opts, function test( t ) {
110+
var v;
111+
112+
v = hyp2f1( 3.0, 4.0, 7.0, 1.0 );
113+
console.log(v);
114+
t.strictEqual( v, PINF, 'returns expected value' );
115+
116+
v = hyp2f1( 3.5, 4.5, 8.0, 1.0 );
117+
t.strictEqual( v, PINF, 'returns expected value' );
118+
119+
v = hyp2f1( 3.5, 4.5, 8.0, 1.0 );
120+
t.strictEqual( v, PINF, 'returns expected value' );
121+
122+
t.end();
123+
});
124+
125+
tape( 'the function correctly evaluates the hypergeometric function', opts, function test( t ) {
126+
var expected;
127+
var delta;
128+
var tol;
129+
var a;
130+
var b;
131+
var c;
132+
var x;
133+
var v;
134+
var i;
135+
136+
a = basic.a;
137+
b = basic.b;
138+
c = basic.c;
139+
x = basic.x;
140+
expected = basic.expected;
141+
142+
for ( i = 0; i < x.length; i++ ) {
143+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
144+
if ( expected[ i ] === 'PINF' ) {
145+
t.equal( v, PINF, 'returns expected value' );
146+
continue;
147+
}
148+
delta = abs( v - expected[ i ] );
149+
tol = EPS * abs( expected[ i ] );
150+
t.ok( delta <= tol, 'within tolerance. a: ' + a[ i ] + ' b: ' + b[ i ] + ' c: ' + c[ i ] + ' x: ' + x[ i ] + '. Value: ' + v + '. Expected: ' + expected[ i ] + '. Delta: ' + delta + '. Tolerance: ' + tol + '.' );
151+
}
152+
t.end();
153+
});
154+
155+
tape( 'the function correctly evaluates the hypergeometric function', opts, function test( t ) {
156+
var expected;
157+
var delta;
158+
var tol;
159+
var a;
160+
var b;
161+
var c;
162+
var x;
163+
var v;
164+
var i;
165+
166+
a = edgeCases1.a;
167+
b = edgeCases1.b;
168+
c = edgeCases1.c;
169+
x = edgeCases1.x;
170+
expected = edgeCases1.expected;
171+
172+
for ( i = 0; i < x.length; i++ ) {
173+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
174+
if ( expected[ i ] === 'PINF' ) {
175+
t.equal( v, PINF, 'returns expected value' );
176+
continue;
177+
}
178+
delta = abs( v - expected[ i ] );
179+
tol = 1.2 * EPS * abs( expected[ i ] );
180+
t.ok( delta <= tol, 'within tolerance. a: ' + a[ i ] + ' b: ' + b[ i ] + ' c: ' + c[ i ] + ' x: ' + x[ i ] + '. Value: ' + v + '. Expected: ' + expected[ i ] + '. Delta: ' + delta + '. Tolerance: ' + tol + '.' );
181+
}
182+
t.end();
183+
});
184+
185+
tape( 'the function correctly evaluates the hypergeometric function', opts, function test( t ) {
186+
var expected;
187+
var delta;
188+
var tol;
189+
var a;
190+
var b;
191+
var c;
192+
var x;
193+
var v;
194+
var i;
195+
196+
a = edgeCases2.a;
197+
b = edgeCases2.b;
198+
c = edgeCases2.c;
199+
x = edgeCases2.x;
200+
expected = edgeCases2.expected;
201+
202+
for ( i = 0; i < x.length; i++ ) {
203+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
204+
if ( expected[ i ] === 'PINF' ) {
205+
t.equal( v, PINF, 'returns expected value' );
206+
continue;
207+
}
208+
delta = abs( v - expected[ i ] );
209+
tol = 24.0 * EPS * abs( expected[ i ] );
210+
t.ok( delta <= tol, 'within tolerance. a: ' + a[ i ] + ' b: ' + b[ i ] + ' c: ' + c[ i ] + ' x: ' + x[ i ] + '. Value: ' + v + '. Expected: ' + expected[ i ] + '. Delta: ' + delta + '. Tolerance: ' + tol + '.' );
211+
}
212+
t.end();
213+
});
214+
215+
tape( 'the function correctly evaluates the hypergeometric function', opts, function test( t ) {
216+
var expected;
217+
var delta;
218+
var tol;
219+
var a;
220+
var b;
221+
var c;
222+
var x;
223+
var v;
224+
var i;
225+
226+
a = edgeCases3.a;
227+
b = edgeCases3.b;
228+
c = edgeCases3.c;
229+
x = edgeCases3.x;
230+
expected = edgeCases3.expected;
231+
232+
for ( i = 0; i < x.length; i++ ) {
233+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
234+
if ( expected[ i ] === 'PINF' ) {
235+
t.equal( v, PINF, 'returns expected value' );
236+
continue;
237+
}
238+
delta = abs( v - expected[ i ] );
239+
tol = 32.0 * EPS * abs( expected[ i ] );
240+
t.ok( delta <= tol, 'within tolerance. a: ' + a[ i ] + ' b: ' + b[ i ] + ' c: ' + c[ i ] + ' x: ' + x[ i ] + '. Value: ' + v + '. Expected: ' + expected[ i ] + '. Delta: ' + delta + '. Tolerance: ' + tol + '.' );
241+
}
242+
t.end();
243+
});
244+
245+
tape( 'the function correctly evaluates the hypergeometric function', opts, function test( t ) {
246+
var expected;
247+
var delta;
248+
var tol;
249+
var a;
250+
var b;
251+
var c;
252+
var x;
253+
var v;
254+
var i;
255+
256+
a = edgeCases4.a;
257+
b = edgeCases4.b;
258+
c = edgeCases4.c;
259+
x = edgeCases4.x;
260+
expected = edgeCases4.expected;
261+
262+
for ( i = 0; i < x.length; i++ ) {
263+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
264+
if ( expected[ i ] === 'PINF' ) {
265+
t.equal( v, PINF, 'returns expected value' );
266+
continue;
267+
}
268+
delta = abs( v - expected[ i ] );
269+
tol = 84.0 * EPS * abs( expected[ i ] );
270+
t.ok( delta <= tol, 'within tolerance. a: ' + a[ i ] + ' b: ' + b[ i ] + ' c: ' + c[ i ] + ' x: ' + x[ i ] + '. Value: ' + v + '. Expected: ' + expected[ i ] + '. Delta: ' + delta + '. Tolerance: ' + tol + '.' );
271+
}
272+
t.end();
273+
});
274+
275+
tape( 'the function correctly evaluates the hypergeometric function', opts, function test( t ) {
276+
var expected;
277+
var delta;
278+
var tol;
279+
var a;
280+
var b;
281+
var c;
282+
var x;
283+
var v;
284+
var i;
285+
286+
a = outliers.a;
287+
b = outliers.b;
288+
c = outliers.c;
289+
x = outliers.x;
290+
expected = outliers.expected;
291+
292+
for ( i = 0; i < x.length; i++ ) {
293+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
294+
delta = abs( v - expected[ i ] );
295+
296+
/*
297+
* NOTE: the tolerance is set high in this case due to:
298+
*
299+
* 1. The expected values having a very large range, being either very small or very large.
300+
* 2. The function making a large number of internal calls, leading to accumulated numerical errors.
301+
*/
302+
tol = 345877.0 * EPS * abs( expected[ i ] );
303+
t.ok( delta <= tol, 'within tolerance. a: ' + a[ i ] + ' b: ' + b[ i ] + ' c: ' + c[ i ] + ' x: ' + x[ i ] + '. Value: ' + v + '. Expected: ' + expected[ i ] + '. Delta: ' + delta + '. Tolerance: ' + tol + '.' );
304+
}
305+
t.end();
306+
});

0 commit comments

Comments
 (0)