From 308664b284c6c7bce35b937501411cf794977364 Mon Sep 17 00:00:00 2001 From: Abhist17 Date: Mon, 28 Sep 2026 04:17:40 +0530 Subject: [PATCH 1/2] fix: return the smallest `x` with `F(x) >= p` in `stats/base/dists/discrete-uniform/quantile` `a + floor(p*n)` is one past the quantile whenever `p*n` is an integer, and returns `b+1`, outside the support, for `p = 1`. Use `a - 1 + ceil(p*n)` instead, with `Q(0) = a` and a correction for `p*n` rounding up past an integer. --- .../dists/discrete-uniform/quantile/README.md | 10 +++--- .../discrete-uniform/quantile/docs/repl.txt | 2 +- .../quantile/docs/types/index.d.ts | 2 +- .../discrete-uniform/quantile/lib/factory.js | 13 ++++++-- .../discrete-uniform/quantile/lib/index.js | 2 +- .../discrete-uniform/quantile/lib/main.js | 16 +++++++-- .../discrete-uniform/quantile/manifest.json | 6 ++-- .../discrete-uniform/quantile/src/main.c | 18 ++++++++-- .../quantile/test/test.factory.js | 33 +++++++++++++++++++ .../quantile/test/test.native.js | 25 ++++++++++++++ .../quantile/test/test.quantile.js | 25 ++++++++++++++ 11 files changed, 135 insertions(+), 17 deletions(-) diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/README.md b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/README.md index 5ffc4a05b885..b44e9604b7a8 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/README.md +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/README.md @@ -26,20 +26,20 @@ limitations under the License. The [quantile function][quantile-function] for a [discrete uniform][discrete-uniform-distribution] random variable is - + ```math -Q(p) = a + \lfloor p \cdot ( b - a + 1 ) \rfloor +Q(p) = a - 1 + \lceil p \cdot ( b - a + 1 ) \rceil ``` - -for `0 <= p <= 1`, where `a` is the minimum support and `b` is the maximum support. The parameters must satisfy `a <= b`. +for `0 < p <= 1`, where `a` is the minimum support and `b` is the maximum support, and `Q(0) = a`. This is the smallest `x` in the support for which the cumulative distribution function is at least `p`. The parameters must satisfy `a <= b`. @@ -109,7 +109,7 @@ Returns a function for evaluating the quantile function of a [discrete uniform][ ```javascript var myquantile = quantile.factory( 0, 4 ); var y = myquantile( 0.8 ); -// returns 4 +// returns 3 y = myquantile( 0.3 ); // returns 1 diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/repl.txt b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/repl.txt index 910dad38d9ed..fc539fc64c97 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/repl.txt +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/repl.txt @@ -69,7 +69,7 @@ -------- > var myQuantile = {{alias}}.factory( 0, 4 ); > var y = myQuantile( 0.8 ) - 4 + 3 See Also -------- diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/types/index.d.ts b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/types/index.d.ts index 5e82b90be7d1..c1c9646a895a 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/types/index.d.ts +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/docs/types/index.d.ts @@ -121,7 +121,7 @@ interface Quantile { * * var myQuantile = quantile.factory( 0, 4 ); * y = myQuantile( 0.8 ); -* // returns 4 +* // returns 3 */ declare var quantile: Quantile; diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js index aa7d41ec13ae..677b606b8031 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js @@ -23,7 +23,7 @@ var constantFunction = require( '@stdlib/utils/constant-function' ); var isInteger = require( '@stdlib/math/base/assert/is-integer' ); var isnan = require( '@stdlib/math/base/assert/is-nan' ); -var floor = require( '@stdlib/math/base/special/floor' ); +var ceil = require( '@stdlib/math/base/special/ceil' ); // MAIN // @@ -69,10 +69,19 @@ function factory( a, b ) { * // returns */ function quantile( p ) { + var k; if ( isnan( p ) || p < 0.0 || p > 1.0 ) { return NaN; } - return a + floor( p * n ); + if ( p === 0.0 ) { + return a; + } + // Find the smallest `k` such that `k/n >= p`, correcting for `p*n` rounding up past an integer: + k = ceil( p * n ); + if ( ( k-1 ) / n >= p ) { + k -= 1; + } + return a + k - 1; } } diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/index.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/index.js index 12a34fccfd57..ec6bf125929b 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/index.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/index.js @@ -34,7 +34,7 @@ * * var myQuantile = quantile.factory( 0, 4 ); * y = myQuantile( 0.8 ); -* // returns 4 +* // returns 3 */ // MODULES // diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js index 352a77960abe..a74feb0d67a8 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js @@ -22,7 +22,7 @@ var isInteger = require( '@stdlib/math/base/assert/is-integer' ); var isnan = require( '@stdlib/math/base/assert/is-nan' ); -var floor = require( '@stdlib/math/base/special/floor' ); +var ceil = require( '@stdlib/math/base/special/ceil' ); // MAIN // @@ -73,6 +73,7 @@ var floor = require( '@stdlib/math/base/special/floor' ); */ function quantile( p, a, b ) { var n; + var k; if ( isnan( p ) || isnan( a ) || @@ -85,8 +86,19 @@ function quantile( p, a, b ) { ) { return NaN; } + if ( p === 0.0 ) { + return a; + } n = b - a + 1; - return a + floor( p * n ); + + // Find the smallest `k` such that `k/n >= p`, i.e., the smallest `x = a+k-1` such that `F(x) >= p`: + k = ceil( p * n ); + + // Correct for `p*n` rounding up past an integer (e.g., `0.3*10 = 3.0000000000000004`): + if ( ( k-1 ) / n >= p ) { + k -= 1; + } + return a + k - 1; } diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/manifest.json b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/manifest.json index f60b8c621079..af938de05469 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/manifest.json +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/manifest.json @@ -40,7 +40,7 @@ "dependencies": [ "@stdlib/math/base/napi/ternary", "@stdlib/math/base/assert/is-nan", - "@stdlib/math/base/special/floor" + "@stdlib/math/base/special/ceil" ] }, { @@ -57,7 +57,7 @@ "dependencies": [ "@stdlib/math/base/special/round", "@stdlib/math/base/assert/is-nan", - "@stdlib/math/base/special/floor" + "@stdlib/math/base/special/ceil" ] }, { @@ -74,7 +74,7 @@ "dependencies": [ "@stdlib/math/base/special/round", "@stdlib/math/base/assert/is-nan", - "@stdlib/math/base/special/floor" + "@stdlib/math/base/special/ceil" ] } ] diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c index ee7411bb0343..11b1b3c1103e 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c @@ -18,7 +18,7 @@ #include "stdlib/stats/base/dists/discrete-uniform/quantile.h" #include "stdlib/math/base/assert/is_nan.h" -#include "stdlib/math/base/special/floor.h" +#include "stdlib/math/base/special/ceil.h" #include /** @@ -34,6 +34,8 @@ * // returns 1.0 */ double stdlib_base_dists_discrete_uniform_quantile( const double p, const int32_t a, const int32_t b ) { + double n; + double k; if ( stdlib_base_is_nan( p ) || a > b || @@ -42,5 +44,17 @@ double stdlib_base_dists_discrete_uniform_quantile( const double p, const int32_ ) { return 0.0/0.0; // NaN } - return a + stdlib_base_floor( p * ( b - a + 1.0 ) ); + if ( p == 0.0 ) { + return a; + } + n = b - a + 1.0; + + // Find the smallest `k` such that `k/n >= p`, i.e., the smallest `x = a+k-1` such that `F(x) >= p`: + k = stdlib_base_ceil( p * n ); + + // Correct for `p*n` rounding up past an integer (e.g., `0.3*10 = 3.0000000000000004`): + if ( ( k-1.0 ) / n >= p ) { + k -= 1.0; + } + return a + k - 1.0; } diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.factory.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.factory.js index 2c429a67806e..b074ed587203 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.factory.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.factory.js @@ -129,6 +129,39 @@ tape( 'if provided `a > b`, the created function always returns `NaN`', function t.end(); }); +tape( 'if provided `p = 0`, the created function returns `a`, and if provided `p = 1`, the created function returns `b`', function test( t ) { + var quantile; + + quantile = factory( 2, 5 ); + t.strictEqual( quantile( 0.0 ), 2, 'returns expected value' ); + t.strictEqual( quantile( 1.0 ), 5, 'returns expected value' ); + + quantile = factory( -3, -3 ); + t.strictEqual( quantile( 0.0 ), -3, 'returns expected value' ); + t.strictEqual( quantile( 1.0 ), -3, 'returns expected value' ); + + t.end(); +}); + +tape( 'the created function returns the smallest `x` such that `F(x) >= p` when `p*(b-a+1)` is an integer', function test( t ) { + var quantile; + var a; + var n; + var k; + var y; + + for ( a = -3; a <= 3; a += 3 ) { + for ( n = 1; n <= 10; n++ ) { + quantile = factory( a, a+n-1 ); + for ( k = 1; k <= n; k++ ) { + y = quantile( k/n ); + t.strictEqual( y, a+k-1, 'a: '+a+', b: '+(a+n-1)+', p: '+(k/n)+', y: '+y+', expected: '+(a+k-1) ); + } + } + } + t.end(); +}); + tape( 'the created function evaluates the quantile for `p` given a small range `b - a`', function test( t ) { var expected; var quantile; diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.native.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.native.js index 0c06f6cb3ff1..072e6a60938d 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.native.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.native.js @@ -77,6 +77,31 @@ tape( 'if provided `a > b`, the function returns `NaN`', opts, function test( t t.end(); }); +tape( 'if provided `p = 0`, the function returns `a`, and if provided `p = 1`, the function returns `b`', opts, function test( t ) { + t.strictEqual( quantile( 0.0, 2, 5 ), 2, 'returns expected value' ); + t.strictEqual( quantile( 1.0, 2, 5 ), 5, 'returns expected value' ); + t.strictEqual( quantile( 0.0, -3, -3 ), -3, 'returns expected value' ); + t.strictEqual( quantile( 1.0, -3, -3 ), -3, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns the smallest `x` such that `F(x) >= p` when `p*(b-a+1)` is an integer', opts, function test( t ) { + var a; + var n; + var k; + var y; + + for ( a = -3; a <= 3; a += 3 ) { + for ( n = 1; n <= 10; n++ ) { + for ( k = 1; k <= n; k++ ) { + y = quantile( k/n, a, a+n-1 ); + t.strictEqual( y, a+k-1, 'a: '+a+', b: '+(a+n-1)+', p: '+(k/n)+', y: '+y+', expected: '+(a+k-1) ); + } + } + } + t.end(); +}); + tape( 'the function evaluates the quantile for `p` given a small range `b - a`', opts, function test( t ) { var expected; var delta; diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.quantile.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.quantile.js index 67ffc297b1e4..75d278cd0449 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.quantile.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/test/test.quantile.js @@ -89,6 +89,31 @@ tape( 'if provided a non-integer value for `a` or `b`, the function returns `NaN t.end(); }); +tape( 'if provided `p = 0`, the function returns `a`, and if provided `p = 1`, the function returns `b`', function test( t ) { + t.strictEqual( quantile( 0.0, 2, 5 ), 2, 'returns expected value' ); + t.strictEqual( quantile( 1.0, 2, 5 ), 5, 'returns expected value' ); + t.strictEqual( quantile( 0.0, -3, -3 ), -3, 'returns expected value' ); + t.strictEqual( quantile( 1.0, -3, -3 ), -3, 'returns expected value' ); + t.end(); +}); + +tape( 'the function returns the smallest `x` such that `F(x) >= p` when `p*(b-a+1)` is an integer', function test( t ) { + var a; + var n; + var k; + var y; + + for ( a = -3; a <= 3; a += 3 ) { + for ( n = 1; n <= 10; n++ ) { + for ( k = 1; k <= n; k++ ) { + y = quantile( k/n, a, a+n-1 ); + t.strictEqual( y, a+k-1, 'a: '+a+', b: '+(a+n-1)+', p: '+(k/n)+', y: '+y+', expected: '+(a+k-1) ); + } + } + } + t.end(); +}); + tape( 'the function evaluates the quantile for `p` given a small range `b - a`', function test( t ) { var expected; var delta; From 1a142fbcc260ef8bd6fa5f10c8242423de7bf458 Mon Sep 17 00:00:00 2001 From: Abhist17 Date: Mon, 28 Sep 2026 09:45:42 +0530 Subject: [PATCH 2/2] docs: document the method in the JSDoc and C doc comments --- .../dists/discrete-uniform/quantile/lib/factory.js | 1 - .../dists/discrete-uniform/quantile/lib/main.js | 14 ++++++++++---- .../dists/discrete-uniform/quantile/src/main.c | 14 ++++++++++---- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js index 677b606b8031..661cdd7462e5 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/factory.js @@ -76,7 +76,6 @@ function factory( a, b ) { if ( p === 0.0 ) { return a; } - // Find the smallest `k` such that `k/n >= p`, correcting for `p*n` rounding up past an integer: k = ceil( p * n ); if ( ( k-1 ) / n >= p ) { k -= 1; diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js index a74feb0d67a8..e00de768d620 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/lib/main.js @@ -30,6 +30,16 @@ var ceil = require( '@stdlib/math/base/special/ceil' ); /** * Evaluates the quantile function for a discrete uniform distribution with minimum support `a` and maximum support `b` at a probability `p`. * +* ## Method +* +* The quantile is the smallest \\(x\\) in the support with \\(F(x) \geq p\\). As \\(F(x) = (x-a+1)/n\\), where \\(n = b-a+1\\), this is +* +* ```tex +* Q(p) = a - 1 + \lceil p n \rceil +* ``` +* +* for \\(p > 0\\), and \\(Q(0) = a\\). Because \\(pn\\) can round up past an integer (e.g., \\(0.3 \cdot 10 = 3.0000000000000004\\)), the function steps \\(k = \lceil pn \rceil\\) back by one when \\((k-1)/n \geq p\\), which is the same comparison the CDF makes. +* * @param {Probability} p - input value * @param {integer} a - minimum support * @param {integer} b - maximum support @@ -90,11 +100,7 @@ function quantile( p, a, b ) { return a; } n = b - a + 1; - - // Find the smallest `k` such that `k/n >= p`, i.e., the smallest `x = a+k-1` such that `F(x) >= p`: k = ceil( p * n ); - - // Correct for `p*n` rounding up past an integer (e.g., `0.3*10 = 3.0000000000000004`): if ( ( k-1 ) / n >= p ) { k -= 1; } diff --git a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c index 11b1b3c1103e..95d23b2bde10 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c +++ b/lib/node_modules/@stdlib/stats/base/dists/discrete-uniform/quantile/src/main.c @@ -24,6 +24,16 @@ /** * Evaluates the quantile function for a discrete uniform distribution with minimum support `a` and maximum support `b` at a probability `p`. * +* ## Method +* +* The quantile is the smallest `x` in the support with `F(x) \geq p`. As `F(x) = (x-a+1)/n`, where `n = b-a+1`, this is +* +* ```tex +* Q(p) = a - 1 + \lceil p n \rceil +* ``` +* +* for `p > 0`, and `Q(0) = a`. Because `pn` can round up past an integer (e.g., `0.3 \cdot 10 = 3.0000000000000004`), the function steps `k = \lceil pn \rceil` back by one when `(k-1)/n \geq p`, which is the same comparison the CDF makes. +* * @param p input probability * @param a minimum support * @param b maximum support @@ -48,11 +58,7 @@ double stdlib_base_dists_discrete_uniform_quantile( const double p, const int32_ return a; } n = b - a + 1.0; - - // Find the smallest `k` such that `k/n >= p`, i.e., the smallest `x = a+k-1` such that `F(x) >= p`: k = stdlib_base_ceil( p * n ); - - // Correct for `p*n` rounding up past an integer (e.g., `0.3*10 = 3.0000000000000004`): if ( ( k-1.0 ) / n >= p ) { k -= 1.0; }