1use fts_core::models::Point;
2
3#[derive(Debug)]
5#[cfg_attr(test, derive(PartialEq))]
6pub struct Segment {
7 pub q0: f64,
9 pub q1: f64,
11 pub p0: f64,
13 pub p1: f64,
15}
16
17impl Segment {
18 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 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 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 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 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 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 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}