fts_solver/types/demand/
segment.rs

1use super::Point;
2
3/// A single line segment satisfying q0 ≤ 0 ≤ q1 and p1 ≤ p0
4#[derive(Debug)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[cfg_attr(test, derive(PartialEq))]
7pub struct Segment {
8    /// The supply associated to this segment (q0 ≤ 0)
9    pub q0: f64,
10    /// The demand associated to this segment (q1 ≥ 0)
11    pub q1: f64,
12    /// The bidding price for the supply
13    pub p0: f64,
14    /// The asking price for the demand
15    pub p1: f64,
16}
17
18impl Segment {
19    /// Construct a simple demand segment from two neighboring points on a demand curve.
20    ///
21    /// Does not check if the points are properly ordered.
22    /// Additionally returns the amount the points were translated.
23    pub unsafe fn new_unchecked(a: Point, b: Point) -> (Self, f64) {
24        let Point {
25            quantity: mut q0,
26            price: p0,
27        } = a;
28        let Point {
29            quantity: mut q1,
30            price: p1,
31        } = b;
32
33        // This is subtle, but if you consider the possibilities:
34        // * If q0 > 0, translate == q0
35        // * If q1 < 0, translate == q1
36        // * If q0 < 0 and q1 > 0, translate == 0
37        // Accordingly, this will minimally translate the segment in order for it to contain q=0.
38        let translate = q0.max(0.0) + q1.min(0.0);
39        q0 -= translate;
40        q1 -= translate;
41
42        (Self { q0, q1, p0, p1 }, translate)
43    }
44
45    /// Construct a simple demand segment from two neighboring points on a demand curve,
46    /// performing validation to ensure the result is valid.
47    ///
48    /// Additionally returns the amount the points were translated.
49    pub fn new(a: Point, b: Point) -> Result<(Self, f64), (Self, f64)> {
50        let ok = a <= b;
51        let result = unsafe { Segment::new_unchecked(a, b) };
52        if ok { Ok(result) } else { Err(result) }
53    }
54
55    /// Compute the slope and p-intercept of the line segment.
56    pub fn slope_intercept(&self) -> (f64, f64) {
57        let qmid = (self.q0 + self.q1) / 2.0;
58        let pmid = (self.p0 + self.p1) / 2.0;
59        if self.q0 == self.q1 {
60            (f64::NEG_INFINITY, pmid)
61        } else {
62            let m = (self.p1 - self.p0) / (self.q1 - self.q0);
63            let b = if qmid.is_finite() {
64                pmid - m * qmid
65            } else {
66                pmid
67            };
68            (m, b)
69        }
70    }
71
72    /// Clip the segment to the provided interval.
73    ///
74    /// Does not validate the requested interval.
75    pub unsafe fn clip_unchecked(self, qmin: f64, qmax: f64) -> Self {
76        let (m, b) = self.slope_intercept();
77
78        let (q0, p0) = if m.is_infinite() || self.q0 >= qmin {
79            (self.q0, self.p0)
80        } else {
81            (qmin, m * qmin + b)
82        };
83
84        let (q1, p1) = if m.is_infinite() || self.q1 <= qmax {
85            (self.q1, self.p1)
86        } else {
87            (qmax, m * qmax + b)
88        };
89
90        Self { q0, q1, p0, p1 }
91    }
92
93    /// Clip the segment to the provided interval, returning a value if the interval is valid.
94    pub fn clip(self, qmin: f64, qmax: f64) -> Option<Self> {
95        if qmin <= 0.0 && qmax >= 0.0 {
96            Some(unsafe { self.clip_unchecked(qmin, qmax) })
97        } else {
98            None
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn supply_constructor() {
109        let a = Point {
110            quantity: -2.0,
111            price: 10.0,
112        };
113        let b = Point {
114            quantity: -1.0,
115            price: 0.0,
116        };
117        let (Segment { q0, q1, p0, p1 }, t) = Segment::new(a, b).expect("valid interval");
118
119        assert_eq!(t, -1.0);
120        assert_eq!(q0, -1.0);
121        assert_eq!(q1, 0.0);
122        assert_eq!(p0, 10.0);
123        assert_eq!(p1, 0.0);
124    }
125
126    #[test]
127    fn demand_constructor() {
128        let a = Point {
129            quantity: 1.0,
130            price: 10.0,
131        };
132        let b = Point {
133            quantity: 2.0,
134            price: 0.0,
135        };
136        let (Segment { q0, q1, p0, p1 }, t) = Segment::new(a, b).expect("valid interval");
137
138        assert_eq!(t, 1.0);
139        assert_eq!(q0, 0.0);
140        assert_eq!(q1, 1.0);
141        assert_eq!(p0, 10.0);
142        assert_eq!(p1, 0.0);
143    }
144
145    #[test]
146    fn arbitrage_constructor() {
147        let a = Point {
148            quantity: -2.0,
149            price: 10.0,
150        };
151        let b = Point {
152            quantity: 3.0,
153            price: 0.0,
154        };
155        let (Segment { q0, q1, p0, p1 }, t) = Segment::new(a, b).expect("valid interval");
156
157        assert_eq!(t, 0.0);
158        assert_eq!(q0, -2.0);
159        assert_eq!(q1, 3.0);
160        assert_eq!(p0, 10.0);
161        assert_eq!(p1, 0.0);
162    }
163
164    #[test]
165    fn bad_supply_constructor() {
166        let a = Point {
167            quantity: -1.0,
168            price: 10.0,
169        };
170        let b = Point {
171            quantity: -2.0,
172            price: 0.0,
173        };
174
175        assert!(Segment::new(a, b).is_err());
176    }
177
178    #[test]
179    fn bad_demand_constructor() {
180        let a = Point {
181            quantity: 2.0,
182            price: 10.0,
183        };
184        let b = Point {
185            quantity: 1.0,
186            price: 0.0,
187        };
188
189        assert!(Segment::new(a, b).is_err());
190    }
191
192    #[test]
193    fn bad_arbitrage_constructor() {
194        let a = Point {
195            quantity: 3.0,
196            price: 10.0,
197        };
198        let b = Point {
199            quantity: -2.0,
200            price: 0.0,
201        };
202
203        assert!(Segment::new(a, b).is_err());
204    }
205
206    #[test]
207    fn finite_slope() {
208        let a = Point {
209            quantity: -1.0,
210            price: 4.0,
211        };
212        let b = Point {
213            quantity: 1.0,
214            price: 0.0,
215        };
216
217        let (m, b) = Segment::new(a, b)
218            .expect("valid interval")
219            .0
220            .slope_intercept();
221
222        assert_eq!(m, -2.0);
223        assert_eq!(b, 2.0);
224    }
225
226    #[test]
227    fn infinite_slope() {
228        let a = Point {
229            quantity: -0.0,
230            price: 4.0,
231        };
232        let b = Point {
233            quantity: 0.0,
234            price: 0.0,
235        };
236
237        let (m, b) = Segment::new(a, b)
238            .expect("valid interval")
239            .0
240            .slope_intercept();
241
242        assert_eq!(m, f64::NEG_INFINITY);
243        assert_eq!(b, 2.0);
244    }
245
246    #[test]
247    fn zero_slope_neg() {
248        let a = Point {
249            quantity: f64::NEG_INFINITY,
250            price: 4.0,
251        };
252        let b = Point {
253            quantity: 1.0,
254            price: 0.0,
255        };
256
257        let (m, b) = Segment::new(a, b)
258            .expect("valid interval")
259            .0
260            .slope_intercept();
261
262        assert_eq!(m, 0.0);
263        assert_eq!(b, 2.0);
264    }
265
266    #[test]
267    fn zero_slope_pos() {
268        let a = Point {
269            quantity: -1.0,
270            price: 4.0,
271        };
272        let b = Point {
273            quantity: f64::INFINITY,
274            price: 0.0,
275        };
276
277        let (m, b) = Segment::new(a, b)
278            .expect("valid interval")
279            .0
280            .slope_intercept();
281
282        assert_eq!(m, 0.0);
283        assert_eq!(b, 2.0);
284    }
285
286    // Some simple data for the clip() tests
287    fn finite_data() -> Segment {
288        let a = Point {
289            quantity: -1.0,
290            price: 4.0,
291        };
292        let b = Point {
293            quantity: 1.0,
294            price: 0.0,
295        };
296
297        Segment::new(a, b).unwrap().0
298    }
299
300    #[test]
301    fn clip_0() {
302        let Segment { q0, q1, p0, p1 } = finite_data().clip(-5.0, 5.0).unwrap();
303        assert_eq!(q0, -1.0);
304        assert_eq!(q1, 1.0);
305        assert_eq!(p0, 4.0);
306        assert_eq!(p1, 0.0);
307    }
308
309    #[test]
310    fn demand_clip_1() {
311        let Segment { q0, q1, p0, p1 } = finite_data().clip(-5.0, 1.0).unwrap();
312        assert_eq!(q0, -1.0);
313        assert_eq!(q1, 1.0);
314        assert_eq!(p0, 4.0);
315        assert_eq!(p1, 0.0);
316    }
317
318    #[test]
319    fn demand_clip_2() {
320        let Segment { q0, q1, p0, p1 } = finite_data().clip(-5.0, 0.5).unwrap();
321        assert_eq!(q0, -1.0);
322        assert_eq!(q1, 0.5);
323        assert_eq!(p0, 4.0);
324        assert_eq!(p1, 1.0);
325    }
326
327    #[test]
328    fn demand_clip_3() {
329        let Segment { q0, q1, p0, p1 } = finite_data().clip(-5.0, 0.0).unwrap();
330        assert_eq!(q0, -1.0);
331        assert_eq!(q1, 0.0);
332        assert_eq!(p0, 4.0);
333        assert_eq!(p1, 2.0);
334    }
335
336    #[test]
337    fn supply_clip_1() {
338        let Segment { q0, q1, p0, p1 } = finite_data().clip(-1.0, 5.0).unwrap();
339        assert_eq!(q0, -1.0);
340        assert_eq!(q1, 1.0);
341        assert_eq!(p0, 4.0);
342        assert_eq!(p1, 0.0);
343    }
344
345    #[test]
346    fn supply_clip_2() {
347        let Segment { q0, q1, p0, p1 } = finite_data().clip(-0.5, 5.0).unwrap();
348        assert_eq!(q0, -0.5);
349        assert_eq!(q1, 1.0);
350        assert_eq!(p0, 3.0);
351        assert_eq!(p1, 0.0);
352    }
353
354    #[test]
355    fn supply_clip_3() {
356        let Segment { q0, q1, p0, p1 } = finite_data().clip(0.0, 5.0).unwrap();
357        assert_eq!(q0, 0.0);
358        assert_eq!(q1, 1.0);
359        assert_eq!(p0, 2.0);
360        assert_eq!(p1, 0.0);
361    }
362}