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
mod c;
mod d;
mod p;
mod q;
mod r;

use strafe_type::{LogProbability64, Natural64, Probability64, Real64};

pub use self::{c::*, d::*, p::*, q::*, r::*};
use crate::traits::{Distribution, RNG};

/// # Distribution of the Wilcoxon Signed Rank Statistic
///
/// ## Description
///
/// Density, distribution function, quantile function and random generation for the distribution of
/// the Wilcoxon Signed Rank statistic obtained from a sample with size n.
///
/// ## Arguments
///
/// * n: number(s) of observations in the sample(s). A positive integer, or a vector of such integers.
///
/// ## Details
///
/// This distribution is obtained as follows. Let x be a sample of size n from a continuous
/// distribution symmetric about the origin. Then the Wilcoxon signed rank statistic is the
/// sum of the ranks of the absolute values $ x\[i\] $ for which $ x\[i\] $ is positive. This statistic
/// takes values between $ 0 $ and $ n(n+1)/2 $, and its mean and variance are $ n(n+1)/4 $ and
/// $ n(n+1)(2n+1)/24 $, respectively.
///
/// If either of the first two arguments is a vector, the recycling rule is used to do the
/// calculations for all combinations of the two up to the length of the longer vector.
///
/// ## Value
///
/// dsignrank gives the density, psignrank gives the distribution function, qsignrank gives the
/// quantile function, and rsignrank generates random deviates.
///
/// The length of the result is determined by nn for rsignrank, and is the maximum of the lengths
/// of the numerical arguments for the other functions.
///
/// The numerical arguments other than nn are recycled to the length of the result. Only the first
/// elements of the logical arguments are used.
///
/// ## Density Plot
///
/// ```rust
/// # use r2rs_base::traits::StatisticalSlice;
/// # use r2rs_nmath::{distribution::SignedRankBuilder, traits::Distribution};
/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
/// # use strafe_type::FloatConstraint;
/// let sgnrank = SignedRankBuilder::new().build();
/// let x = <[f64]>::sequence_by(-1.0, 2.0, 0.001);
/// let y = x
///     .iter()
///     .map(|x| sgnrank.density(x).unwrap())
///     .collect::<Vec<_>>();
///
/// let root = SVGBackend::new("density.svg", (1024, 768)).into_drawing_area();
/// Plot::new()
///     .with_options(PlotOptions {
///         x_axis_label: "x".to_string(),
///         y_axis_label: "density".to_string(),
///         ..Default::default()
///     })
///     .with_plottable(Line {
///         x,
///         y,
///         color: BLACK,
///         ..Default::default()
///     })
///     .plot(&root)
///     .unwrap();
/// # use std::fs::rename;
/// #     drop(root);
/// #     rename(
/// #             format!("density.svg"),
/// #             format!("src/distribution/signrank/doctest_out/density.svg"),
/// #     )
/// #     .unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("density", "src/distribution/signrank/doctest_out/density.svg")))]
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][density]"))]
///
/// ## Author(s)
///
/// Kurt Hornik; efficiency improvement by Ivo Ugrina.
///
/// ## See Also
///
/// wilcox.test to calculate the statistic from data, find p values and so on.
///
/// Distributions for standard distributions, including dwilcox for the distribution of two-sample
/// Wilcoxon rank sum statistic.
///
/// ## Examples
///
/// ```rust
/// # use r2rs_base::traits::StatisticalSlice;
/// # use r2rs_nmath::{distribution::SignedRankBuilder, traits::Distribution};
/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
/// # use strafe_type::FloatConstraint;
/// let plot_coords = [
///     [0.0, 0.5, 0.0, 0.5],
///     [0.5, 1.0, 0.0, 0.5],
///     [0.0, 0.5, 0.5, 1.0],
///     [0.5, 1.0, 0.5, 1.0],
/// ];
/// let ns = [4.0, 5.0, 10.0, 40.0];
///
/// let root = SVGBackend::new("densities.svg", (1024, 768)).into_drawing_area();
/// for i in 0..4 {
///     let n = ns[i];
///     let x = <[f64]>::sequence(0.0, n * (n + 1.0) / 2.0, 500);
///     let sgnrank = SignedRankBuilder::new().with_sample_size(n).build();
///     let y = x
///         .iter()
///         .map(|x| sgnrank.density(x).unwrap())
///         .collect::<Vec<_>>();
///
///     Plot::new()
///         .with_options(PlotOptions {
///             x_axis_label: "x".to_string(),
///             y_axis_label: "density".to_string(),
///             title: format!("n={n}"),
///             plot_left: plot_coords[i][0],
///             plot_right: plot_coords[i][1],
///             plot_top: plot_coords[i][2],
///             plot_bottom: plot_coords[i][3],
///             ..Default::default()
///         })
///         .with_plottable(Line {
///             x,
///             y,
///             color: BLACK,
///             ..Default::default()
///         })
///         .plot(&root)
///         .unwrap();
/// }
/// # use std::fs::rename;
/// #     drop(root);
/// #     rename(
/// #             format!("densities.svg"),
/// #             format!("src/distribution/signrank/doctest_out/densities.svg"),
/// #     )
/// #     .unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("densities", "src/distribution/signrank/doctest_out/densities.svg")))]
#[cfg_attr(
    feature = "doc_outputs",
    cfg_attr(all(), doc = "![Densities][densities]")
)]
pub struct SignedRank {
    sample_size: Natural64,
}

impl Distribution for SignedRank {
    fn density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dsignrank(x, self.sample_size, false)
    }

    fn log_density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dsignrank(x, self.sample_size, true)
    }

    fn probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> Probability64 {
        psignrank(q, self.sample_size, lower_tail)
    }

    fn log_probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> LogProbability64 {
        log_psignrank(q, self.sample_size, lower_tail)
    }

    fn quantile<P: Into<Probability64>>(&self, p: P, lower_tail: bool) -> Real64 {
        qsignrank(p, self.sample_size, lower_tail)
    }

    fn log_quantile<LP: Into<LogProbability64>>(&self, p: LP, lower_tail: bool) -> Real64 {
        log_qsignrank(p, self.sample_size, lower_tail)
    }

    fn random_sample<R: RNG>(&self, rng: &mut R) -> Real64 {
        rsignrank(self.sample_size, rng)
    }
}

pub struct SignedRankBuilder {
    sample_size: Option<Natural64>,
}

impl SignedRankBuilder {
    pub fn new() -> Self {
        Self { sample_size: None }
    }

    pub fn with_sample_size<N: Into<Natural64>>(&mut self, sample_size: N) -> &mut Self {
        self.sample_size = Some(sample_size.into());
        self
    }

    pub fn build(&self) -> SignedRank {
        let sample_size = self.sample_size.unwrap_or(1.0.into());

        SignedRank { sample_size }
    }
}

#[cfg(test)]
mod tests;

#[cfg(all(test, feature = "enable_proptest"))]
mod proptests;

#[cfg(all(test, feature = "enable_covtest"))]
mod covtests;