1#![cfg(feature = "std")]
9use num_traits::Float;
10
11pub struct Logspace<F> {
15 sign: F,
16 base: F,
17 start: F,
18 step: F,
19 index: usize,
20 len: usize,
21}
22
23impl<F> Iterator for Logspace<F>
24where
25 F: Float,
26{
27 type Item = F;
28
29 #[inline]
30 fn next(&mut self) -> Option<F> {
31 if self.index >= self.len {
32 None
33 } else {
34 let i = self.index;
36 self.index += 1;
37 let exponent = self.start + self.step * F::from(i).unwrap();
38 Some(self.sign * self.base.powf(exponent))
39 }
40 }
41
42 #[inline]
43 fn size_hint(&self) -> (usize, Option<usize>) {
44 let n = self.len - self.index;
45 (n, Some(n))
46 }
47}
48
49impl<F> DoubleEndedIterator for Logspace<F>
50where
51 F: Float,
52{
53 #[inline]
54 fn next_back(&mut self) -> Option<F> {
55 if self.index >= self.len {
56 None
57 } else {
58 self.len -= 1;
60 let i = self.len;
61 let exponent = self.start + self.step * F::from(i).unwrap();
62 Some(self.sign * self.base.powf(exponent))
63 }
64 }
65}
66
67impl<F> ExactSizeIterator for Logspace<F> where Logspace<F>: Iterator {}
68
69#[inline]
80pub fn logspace<F>(base: F, a: F, b: F, n: usize) -> Logspace<F>
81where
82 F: Float,
83{
84 let step = if n > 1 {
85 let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail.");
86 (b - a) / num_steps
87 } else {
88 F::zero()
89 };
90 Logspace {
91 sign: base.signum(),
92 base: base.abs(),
93 start: a,
94 step,
95 index: 0,
96 len: n,
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::logspace;
103
104 #[test]
105 #[cfg(feature = "approx")]
106 fn valid() {
107 use crate::{arr1, Array1};
108 use approx::assert_abs_diff_eq;
109
110 let array: Array1<_> = logspace(10.0, 0.0, 3.0, 4).collect();
111 assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]));
112
113 let array: Array1<_> = logspace(10.0, 3.0, 0.0, 4).collect();
114 assert_abs_diff_eq!(array, arr1(&[1e3, 1e2, 1e1, 1e0]));
115
116 let array: Array1<_> = logspace(-10.0, 3.0, 0.0, 4).collect();
117 assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]));
118
119 let array: Array1<_> = logspace(-10.0, 0.0, 3.0, 4).collect();
120 assert_abs_diff_eq!(array, arr1(&[-1e0, -1e1, -1e2, -1e3]));
121 }
122
123 #[test]
124 fn iter_forward() {
125 let mut iter = logspace(10.0f64, 0.0, 3.0, 4);
126
127 assert!(iter.size_hint() == (4, Some(4)));
128
129 assert!((iter.next().unwrap() - 1e0).abs() < 1e-5);
130 assert!((iter.next().unwrap() - 1e1).abs() < 1e-5);
131 assert!((iter.next().unwrap() - 1e2).abs() < 1e-5);
132 assert!((iter.next().unwrap() - 1e3).abs() < 1e-5);
133 assert!(iter.next().is_none());
134
135 assert!(iter.size_hint() == (0, Some(0)));
136 }
137
138 #[test]
139 fn iter_backward() {
140 let mut iter = logspace(10.0f64, 0.0, 3.0, 4);
141
142 assert!(iter.size_hint() == (4, Some(4)));
143
144 assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5);
145 assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5);
146 assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5);
147 assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5);
148 assert!(iter.next_back().is_none());
149
150 assert!(iter.size_hint() == (0, Some(0)));
151 }
152}