1use super::Point;
2
3#[derive(Debug)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[cfg_attr(test, derive(PartialEq))]
7pub struct Segment {
8 pub q0: f64,
10 pub q1: f64,
12 pub p0: f64,
14 pub p1: f64,
16}
17
18impl Segment {
19 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 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 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 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 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 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 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}