diff --git a/docs/build.sh b/docs/build.sh
index 63d5e20..3f59413 100644
--- a/docs/build.sh
+++ b/docs/build.sh
@@ -1,2 +1,3 @@
cp docs/index.html target/doc
-cp docs/index.css target/doc
\ No newline at end of file
+cp docs/index.css target/doc
+cp docs/compute_methods.svg target/doc
diff --git a/docs/compute_methods.svg b/docs/compute_methods.svg
new file mode 100644
index 0000000..1436e97
--- /dev/null
+++ b/docs/compute_methods.svg
@@ -0,0 +1,423 @@
+
+
+
+
diff --git a/examples/examples/rectangle/integraal.rs b/examples/examples/rectangle/integraal.rs
index 8a4bcdf..fa22bee 100644
--- a/examples/examples/rectangle/integraal.rs
+++ b/examples/examples/rectangle/integraal.rs
@@ -8,7 +8,7 @@ fn main() {
n_step: 100_001,
};
let function = FunctionDescriptor::Closure(Box::new(|x: f64| 2.0 * x));
- let method = ComputeMethod::Rectangle;
+ let method = ComputeMethod::RectangleLeft;
// build the integral
let mut integral = Integraal::default();
diff --git a/integraal/src/parameters.rs b/integraal/src/parameters.rs
index f9eddbe..8353574 100644
--- a/integraal/src/parameters.rs
+++ b/integraal/src/parameters.rs
@@ -36,10 +36,30 @@ pub enum FunctionDescriptor {
}
/// Numerical integration method enum
+///
+/// # Note on computations
+///
+/// For the considered integral to be consistent across compute methods for a given description,
+/// the left-rectangle (resp. right-rectangle) has to ignore the last (resp. first) value given
+/// in the descriptors. This can be visualized in the following example:
+///
+/// ![COMPARISON](../compute_methods.svg)
+///
+/// Out of 11 samples, both methods compute the area of 10 polygons. In the case where the domain
+/// is uniform & described using a step, the eleventh sample value is useless (for a left-rectangle
+/// method).
+///
+/// The crate assumes that the first and last samples making up your domain corresponds to the
+/// limits of the integral. Therefore, these values will be ignored when computing the integral
+/// using rectangles.
#[derive(Debug, Clone, Copy)]
pub enum ComputeMethod {
- /// Rectangle method -- [reference](https://en.wikipedia.org/wiki/Riemann_sum)
- Rectangle,
+ /// Rectangle method, using the left rule --
+ /// [reference](https://en.wikipedia.org/wiki/Riemann_sum#Left_rule)
+ RectangleLeft,
+ /// Rectangle method, using the right rule --
+ /// [reference](https://en.wikipedia.org/wiki/Riemann_sum#Right_rule)
+ RectangleRight,
/// Trapezoid method [reference](https://en.wikipedia.org/wiki/Trapezoidal_rule)
Trapezoid,
#[cfg(feature = "montecarlo")]
diff --git a/integraal/src/structure/implementations.rs b/integraal/src/structure/implementations.rs
index c923b33..05edb36 100644
--- a/integraal/src/structure/implementations.rs
+++ b/integraal/src/structure/implementations.rs
@@ -51,12 +51,18 @@ impl<'a> Integraal<'a> {
// because the domain may be not uniform, we have to compute step values
match &self.method {
- Some(ComputeMethod::Rectangle) => (1..n_sample)
+ Some(ComputeMethod::RectangleLeft) => (1..n_sample)
.map(|idx| {
let step = args[idx] - args[idx - 1];
step * vals[idx - 1]
})
.sum(),
+ Some(ComputeMethod::RectangleRight) => (1..n_sample)
+ .map(|idx| {
+ let step = args[idx] - args[idx - 1];
+ step * vals[idx]
+ })
+ .sum(),
Some(ComputeMethod::Trapezoid) => (1..n_sample)
.map(|idx| {
let step = args[idx] - args[idx - 1];
@@ -88,8 +94,13 @@ impl<'a> Integraal<'a> {
// we can use the uniform domain's step & number of step to compute areas
match &self.method {
- Some(ComputeMethod::Rectangle) => {
- (0..*n_step).map(|step_id| vals[step_id] * step).sum()
+ Some(ComputeMethod::RectangleLeft) => {
+ // ignore the last value since its a left rule
+ (0..*n_step - 1).map(|step_id| vals[step_id] * step).sum()
+ }
+ Some(ComputeMethod::RectangleRight) => {
+ // ignore the last value since its a left rule
+ (1..*n_step).map(|step_id| vals[step_id] * step).sum()
}
Some(ComputeMethod::Trapezoid) => (1..*n_step)
.map(|step_id| {
@@ -109,12 +120,18 @@ impl<'a> Integraal<'a> {
Some(FunctionDescriptor::Closure(closure)),
Some(DomainDescriptor::Explicit(args)),
) => match &self.method {
- Some(ComputeMethod::Rectangle) => (1..args.len())
+ Some(ComputeMethod::RectangleLeft) => (1..args.len())
.map(|idx| {
let step = args[idx] - args[idx - 1];
step * closure(args[idx - 1])
})
.sum(),
+ Some(ComputeMethod::RectangleRight) => (1..args.len())
+ .map(|idx| {
+ let step = args[idx] - args[idx - 1];
+ step * closure(args[idx])
+ })
+ .sum(),
Some(ComputeMethod::Trapezoid) => (1..args.len())
.map(|idx| {
let step = args[idx] - args[idx - 1];
@@ -139,7 +156,13 @@ impl<'a> Integraal<'a> {
) => {
// compute args
match &self.method {
- Some(ComputeMethod::Rectangle) => (0..*n_step)
+ Some(ComputeMethod::RectangleLeft) => (0..*n_step - 1)
+ .map(|step_id| {
+ let x = start + step * step_id as f64;
+ closure(x) * step
+ })
+ .sum(),
+ Some(ComputeMethod::RectangleRight) => (1..*n_step)
.map(|step_id| {
let x = start + step * step_id as f64;
closure(x) * step
diff --git a/integraal/src/structure/tests.rs b/integraal/src/structure/tests.rs
index bb41ee6..fe6b2cf 100644
--- a/integraal/src/structure/tests.rs
+++ b/integraal/src/structure/tests.rs
@@ -22,7 +22,7 @@ macro_rules! generate_sample_descriptors {
($f: ident, $d: ident, $c: ident) => {
let $f = FunctionDescriptor::Closure(Box::new(|x| x));
let $d = DomainDescriptor::Explicit(&[]);
- let $c = ComputeMethod::Rectangle;
+ let $c = ComputeMethod::RectangleLeft;
};
}
@@ -69,7 +69,7 @@ fn missing_parameters() {
#[test]
fn inconsistent_parameters() {
- let method = ComputeMethod::Rectangle;
+ let method = ComputeMethod::RectangleLeft;
let function = FunctionDescriptor::Values(vec![1., 1., 1., 1., 1., 1.]);
let domain = vec![0.0, 0.1, 0.2, 0.3, 0.4]; // missing the last x value
let domain = DomainDescriptor::Explicit(&domain);
@@ -158,7 +158,7 @@ macro_rules! generate_test {
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
-mod a_rectangle {
+mod a_rectangleleft {
use super::*;
generate_test!(
@@ -168,7 +168,7 @@ mod a_rectangle {
.collect(),
FunctionDescriptor::Closure(Box::new(f64::sin)),
DomainDescriptor::Explicit(&domain),
- ComputeMethod::Rectangle,
+ ComputeMethod::RectangleLeft,
RECTANGLE_TOLERANCE
);
@@ -180,7 +180,7 @@ mod a_rectangle {
step: STEP,
n_step: (1000. * std::f64::consts::PI) as usize,
},
- ComputeMethod::Rectangle,
+ ComputeMethod::RectangleLeft,
RECTANGLE_TOLERANCE
);
@@ -191,7 +191,7 @@ mod a_rectangle {
.collect(),
FunctionDescriptor::Values(domain.iter().copied().map(f64::sin).collect()),
DomainDescriptor::Explicit(&domain),
- ComputeMethod::Rectangle,
+ ComputeMethod::RectangleLeft,
RECTANGLE_TOLERANCE
);
@@ -207,7 +207,62 @@ mod a_rectangle {
step: STEP,
n_step: (1000. * std::f64::consts::PI) as usize,
},
- ComputeMethod::Rectangle,
+ ComputeMethod::RectangleLeft,
+ RECTANGLE_TOLERANCE
+ );
+}
+
+#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
+mod a_rectangleright {
+ use super::*;
+
+ generate_test!(
+ ClosureExplicit,
+ let domain: Vec = (0..(std::f64::consts::PI * 1000.) as usize)
+ .map(|step_id| step_id as f64 * STEP)
+ .collect(),
+ FunctionDescriptor::Closure(Box::new(f64::sin)),
+ DomainDescriptor::Explicit(&domain),
+ ComputeMethod::RectangleRight,
+ RECTANGLE_TOLERANCE
+ );
+
+ generate_test!(
+ ClosureUniform,
+ FunctionDescriptor::Closure(Box::new(f64::sin)),
+ DomainDescriptor::Uniform {
+ start: 0.,
+ step: STEP,
+ n_step: (1000. * std::f64::consts::PI) as usize,
+ },
+ ComputeMethod::RectangleRight,
+ RECTANGLE_TOLERANCE
+ );
+
+ generate_test!(
+ ValuesExplicit,
+ let domain: Vec = (0..(std::f64::consts::PI * 1000.) as usize)
+ .map(|step_id| step_id as f64 * STEP)
+ .collect(),
+ FunctionDescriptor::Values(domain.iter().copied().map(f64::sin).collect()),
+ DomainDescriptor::Explicit(&domain),
+ ComputeMethod::RectangleRight,
+ RECTANGLE_TOLERANCE
+ );
+
+ generate_test!(
+ ValuesUniform,
+ FunctionDescriptor::Values(
+ (0..(1000. * std::f64::consts::PI) as usize)
+ .map(|step_id| (step_id as f64 * STEP).sin())
+ .collect()
+ ),
+ DomainDescriptor::Uniform {
+ start: 0.,
+ step: STEP,
+ n_step: (1000. * std::f64::consts::PI) as usize,
+ },
+ ComputeMethod::RectangleRight,
RECTANGLE_TOLERANCE
);
}
@@ -282,7 +337,7 @@ fn B_Closure_Explicit_Rectangle() {
.map(|step_id| -1. + f64::from(step_id) * STEP)
.collect();
let domaind = DomainDescriptor::Explicit(&domain);
- let computem = ComputeMethod::Rectangle;
+ let computem = ComputeMethod::RectangleLeft;
let mut integraal = Integraal::default();
let res = integraal
.function(functiond)