fts_solver/types/demand/
segment.rs

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