/Users/andrewlamb/Software/arrow-rs/arrow-buffer/src/interval.rs
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | use crate::arith::derive_arith; |
19 | | use std::ops::Neg; |
20 | | |
21 | | /// Value of an IntervalMonthDayNano array |
22 | | /// |
23 | | /// ## Representation |
24 | | /// |
25 | | /// This type is stored as a single 128 bit integer, interpreted as three |
26 | | /// different signed integral fields: |
27 | | /// |
28 | | /// 1. The number of months (32 bits) |
29 | | /// 2. The number days (32 bits) |
30 | | /// 2. The number of nanoseconds (64 bits). |
31 | | /// |
32 | | /// Nanoseconds does not allow for leap seconds. |
33 | | /// |
34 | | /// Each field is independent (e.g. there is no constraint that the quantity of |
35 | | /// nanoseconds represents less than a day's worth of time). |
36 | | /// |
37 | | /// ```text |
38 | | /// ┌───────────────┬─────────────┬─────────────────────────────┐ |
39 | | /// │ Months │ Days │ Nanos │ |
40 | | /// │ (32 bits) │ (32 bits) │ (64 bits) │ |
41 | | /// └───────────────┴─────────────┴─────────────────────────────┘ |
42 | | /// 0 32 64 128 bit offset |
43 | | /// ``` |
44 | | /// Please see the [Arrow Spec](https://github.com/apache/arrow/blob/081b4022fe6f659d8765efc82b3f4787c5039e3c/format/Schema.fbs#L409-L415) for more details |
45 | | /// |
46 | | ///## Note on Comparing and Ordering for Calendar Types |
47 | | /// |
48 | | /// Values of `IntervalMonthDayNano` are compared using their binary |
49 | | /// representation, which can lead to surprising results. |
50 | | /// |
51 | | /// Spans of time measured in calendar units are not fixed in absolute size (e.g. |
52 | | /// number of seconds) which makes defining comparisons and ordering non trivial. |
53 | | /// For example `1 month` is 28 days for February but `1 month` is 31 days |
54 | | /// in December. |
55 | | /// |
56 | | /// This makes the seemingly simple operation of comparing two intervals |
57 | | /// complicated in practice. For example is `1 month` more or less than `30 |
58 | | /// days`? The answer depends on what month you are talking about. |
59 | | /// |
60 | | /// This crate defines comparisons for calendar types using their binary |
61 | | /// representation which is fast and efficient, but leads |
62 | | /// to potentially surprising results. |
63 | | /// |
64 | | /// For example a |
65 | | /// `IntervalMonthDayNano` of `1 month` will compare as **greater** than a |
66 | | /// `IntervalMonthDayNano` of `100 days` because the binary representation of `1 month` |
67 | | /// is larger than the binary representation of 100 days. |
68 | | #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] |
69 | | #[repr(C)] |
70 | | pub struct IntervalMonthDayNano { |
71 | | /// Number of months |
72 | | pub months: i32, |
73 | | /// Number of days |
74 | | pub days: i32, |
75 | | /// Number of nanoseconds |
76 | | pub nanoseconds: i64, |
77 | | } |
78 | | |
79 | | impl IntervalMonthDayNano { |
80 | | /// The additive identity i.e. `0`. |
81 | | pub const ZERO: Self = Self::new(0, 0, 0); |
82 | | |
83 | | /// The multiplicative identity, i.e. `1`. |
84 | | pub const ONE: Self = Self::new(1, 1, 1); |
85 | | |
86 | | /// The multiplicative inverse, i.e. `-1`. |
87 | | pub const MINUS_ONE: Self = Self::new(-1, -1, -1); |
88 | | |
89 | | /// The maximum value that can be represented |
90 | | pub const MAX: Self = Self::new(i32::MAX, i32::MAX, i64::MAX); |
91 | | |
92 | | /// The minimum value that can be represented |
93 | | pub const MIN: Self = Self::new(i32::MIN, i32::MIN, i64::MIN); |
94 | | |
95 | | /// Create a new [`IntervalMonthDayNano`] |
96 | | #[inline] |
97 | 6 | pub const fn new(months: i32, days: i32, nanoseconds: i64) -> Self { |
98 | 6 | Self { |
99 | 6 | months, |
100 | 6 | days, |
101 | 6 | nanoseconds, |
102 | 6 | } |
103 | 6 | } |
104 | | |
105 | | /// Computes the absolute value |
106 | | #[inline] |
107 | | pub fn wrapping_abs(self) -> Self { |
108 | | Self { |
109 | | months: self.months.wrapping_abs(), |
110 | | days: self.days.wrapping_abs(), |
111 | | nanoseconds: self.nanoseconds.wrapping_abs(), |
112 | | } |
113 | | } |
114 | | |
115 | | /// Computes the absolute value |
116 | | #[inline] |
117 | | pub fn checked_abs(self) -> Option<Self> { |
118 | | Some(Self { |
119 | | months: self.months.checked_abs()?, |
120 | | days: self.days.checked_abs()?, |
121 | | nanoseconds: self.nanoseconds.checked_abs()?, |
122 | | }) |
123 | | } |
124 | | |
125 | | /// Negates the value |
126 | | #[inline] |
127 | | pub fn wrapping_neg(self) -> Self { |
128 | | Self { |
129 | | months: self.months.wrapping_neg(), |
130 | | days: self.days.wrapping_neg(), |
131 | | nanoseconds: self.nanoseconds.wrapping_neg(), |
132 | | } |
133 | | } |
134 | | |
135 | | /// Negates the value |
136 | | #[inline] |
137 | 0 | pub fn checked_neg(self) -> Option<Self> { |
138 | | Some(Self { |
139 | 0 | months: self.months.checked_neg()?, |
140 | 0 | days: self.days.checked_neg()?, |
141 | 0 | nanoseconds: self.nanoseconds.checked_neg()?, |
142 | | }) |
143 | 0 | } |
144 | | |
145 | | /// Performs wrapping addition |
146 | | #[inline] |
147 | | pub fn wrapping_add(self, other: Self) -> Self { |
148 | | Self { |
149 | | months: self.months.wrapping_add(other.months), |
150 | | days: self.days.wrapping_add(other.days), |
151 | | nanoseconds: self.nanoseconds.wrapping_add(other.nanoseconds), |
152 | | } |
153 | | } |
154 | | |
155 | | /// Performs checked addition |
156 | | #[inline] |
157 | 0 | pub fn checked_add(self, other: Self) -> Option<Self> { |
158 | | Some(Self { |
159 | 0 | months: self.months.checked_add(other.months)?, |
160 | 0 | days: self.days.checked_add(other.days)?, |
161 | 0 | nanoseconds: self.nanoseconds.checked_add(other.nanoseconds)?, |
162 | | }) |
163 | 0 | } |
164 | | |
165 | | /// Performs wrapping subtraction |
166 | | #[inline] |
167 | | pub fn wrapping_sub(self, other: Self) -> Self { |
168 | | Self { |
169 | | months: self.months.wrapping_sub(other.months), |
170 | | days: self.days.wrapping_sub(other.days), |
171 | | nanoseconds: self.nanoseconds.wrapping_sub(other.nanoseconds), |
172 | | } |
173 | | } |
174 | | |
175 | | /// Performs checked subtraction |
176 | | #[inline] |
177 | 0 | pub fn checked_sub(self, other: Self) -> Option<Self> { |
178 | | Some(Self { |
179 | 0 | months: self.months.checked_sub(other.months)?, |
180 | 0 | days: self.days.checked_sub(other.days)?, |
181 | 0 | nanoseconds: self.nanoseconds.checked_sub(other.nanoseconds)?, |
182 | | }) |
183 | 0 | } |
184 | | |
185 | | /// Performs wrapping multiplication |
186 | | #[inline] |
187 | | pub fn wrapping_mul(self, other: Self) -> Self { |
188 | | Self { |
189 | | months: self.months.wrapping_mul(other.months), |
190 | | days: self.days.wrapping_mul(other.days), |
191 | | nanoseconds: self.nanoseconds.wrapping_mul(other.nanoseconds), |
192 | | } |
193 | | } |
194 | | |
195 | | /// Performs checked multiplication |
196 | 0 | pub fn checked_mul(self, other: Self) -> Option<Self> { |
197 | | Some(Self { |
198 | 0 | months: self.months.checked_mul(other.months)?, |
199 | 0 | days: self.days.checked_mul(other.days)?, |
200 | 0 | nanoseconds: self.nanoseconds.checked_mul(other.nanoseconds)?, |
201 | | }) |
202 | 0 | } |
203 | | |
204 | | /// Performs wrapping division |
205 | | #[inline] |
206 | | pub fn wrapping_div(self, other: Self) -> Self { |
207 | | Self { |
208 | | months: self.months.wrapping_div(other.months), |
209 | | days: self.days.wrapping_div(other.days), |
210 | | nanoseconds: self.nanoseconds.wrapping_div(other.nanoseconds), |
211 | | } |
212 | | } |
213 | | |
214 | | /// Performs checked division |
215 | 0 | pub fn checked_div(self, other: Self) -> Option<Self> { |
216 | | Some(Self { |
217 | 0 | months: self.months.checked_div(other.months)?, |
218 | 0 | days: self.days.checked_div(other.days)?, |
219 | 0 | nanoseconds: self.nanoseconds.checked_div(other.nanoseconds)?, |
220 | | }) |
221 | 0 | } |
222 | | |
223 | | /// Performs wrapping remainder |
224 | | #[inline] |
225 | | pub fn wrapping_rem(self, other: Self) -> Self { |
226 | | Self { |
227 | | months: self.months.wrapping_rem(other.months), |
228 | | days: self.days.wrapping_rem(other.days), |
229 | | nanoseconds: self.nanoseconds.wrapping_rem(other.nanoseconds), |
230 | | } |
231 | | } |
232 | | |
233 | | /// Performs checked remainder |
234 | 0 | pub fn checked_rem(self, other: Self) -> Option<Self> { |
235 | | Some(Self { |
236 | 0 | months: self.months.checked_rem(other.months)?, |
237 | 0 | days: self.days.checked_rem(other.days)?, |
238 | 0 | nanoseconds: self.nanoseconds.checked_rem(other.nanoseconds)?, |
239 | | }) |
240 | 0 | } |
241 | | |
242 | | /// Performs wrapping exponentiation |
243 | | #[inline] |
244 | | pub fn wrapping_pow(self, exp: u32) -> Self { |
245 | | Self { |
246 | | months: self.months.wrapping_pow(exp), |
247 | | days: self.days.wrapping_pow(exp), |
248 | | nanoseconds: self.nanoseconds.wrapping_pow(exp), |
249 | | } |
250 | | } |
251 | | |
252 | | /// Performs checked exponentiation |
253 | | #[inline] |
254 | | pub fn checked_pow(self, exp: u32) -> Option<Self> { |
255 | | Some(Self { |
256 | | months: self.months.checked_pow(exp)?, |
257 | | days: self.days.checked_pow(exp)?, |
258 | | nanoseconds: self.nanoseconds.checked_pow(exp)?, |
259 | | }) |
260 | | } |
261 | | } |
262 | | |
263 | | impl Neg for IntervalMonthDayNano { |
264 | | type Output = Self; |
265 | | |
266 | | #[cfg(debug_assertions)] |
267 | 0 | fn neg(self) -> Self::Output { |
268 | 0 | self.checked_neg().expect("IntervalMonthDayNano overflow") |
269 | 0 | } |
270 | | |
271 | | #[cfg(not(debug_assertions))] |
272 | | fn neg(self) -> Self::Output { |
273 | | self.wrapping_neg() |
274 | | } |
275 | | } |
276 | | |
277 | | derive_arith!( |
278 | | IntervalMonthDayNano, |
279 | | Add, |
280 | | AddAssign, |
281 | | add, |
282 | | add_assign, |
283 | | wrapping_add, |
284 | | checked_add |
285 | | ); |
286 | | derive_arith!( |
287 | | IntervalMonthDayNano, |
288 | | Sub, |
289 | | SubAssign, |
290 | | sub, |
291 | | sub_assign, |
292 | | wrapping_sub, |
293 | | checked_sub |
294 | | ); |
295 | | derive_arith!( |
296 | | IntervalMonthDayNano, |
297 | | Mul, |
298 | | MulAssign, |
299 | | mul, |
300 | | mul_assign, |
301 | | wrapping_mul, |
302 | | checked_mul |
303 | | ); |
304 | | derive_arith!( |
305 | | IntervalMonthDayNano, |
306 | | Div, |
307 | | DivAssign, |
308 | | div, |
309 | | div_assign, |
310 | | wrapping_div, |
311 | | checked_div |
312 | | ); |
313 | | derive_arith!( |
314 | | IntervalMonthDayNano, |
315 | | Rem, |
316 | | RemAssign, |
317 | | rem, |
318 | | rem_assign, |
319 | | wrapping_rem, |
320 | | checked_rem |
321 | | ); |
322 | | |
323 | | /// Value of an IntervalDayTime array |
324 | | /// |
325 | | /// ## Representation |
326 | | /// |
327 | | /// This type is stored as a single 64 bit integer, interpreted as two i32 |
328 | | /// fields: |
329 | | /// |
330 | | /// 1. the number of elapsed days |
331 | | /// 2. The number of milliseconds (no leap seconds), |
332 | | /// |
333 | | /// ```text |
334 | | /// ┌──────────────┬──────────────┐ |
335 | | /// │ Days │ Milliseconds │ |
336 | | /// │ (32 bits) │ (32 bits) │ |
337 | | /// └──────────────┴──────────────┘ |
338 | | /// 0 31 63 bit offset |
339 | | /// ``` |
340 | | /// |
341 | | /// Please see the [Arrow Spec](https://github.com/apache/arrow/blob/081b4022fe6f659d8765efc82b3f4787c5039e3c/format/Schema.fbs#L406-L408) for more details |
342 | | /// |
343 | | /// ## Note on Comparing and Ordering for Calendar Types |
344 | | /// |
345 | | /// Values of `IntervalDayTime` are compared using their binary representation, |
346 | | /// which can lead to surprising results. Please see the description of ordering on |
347 | | /// [`IntervalMonthDayNano`] for more details |
348 | | #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] |
349 | | #[repr(C)] |
350 | | pub struct IntervalDayTime { |
351 | | /// Number of days |
352 | | pub days: i32, |
353 | | /// Number of milliseconds |
354 | | pub milliseconds: i32, |
355 | | } |
356 | | |
357 | | impl IntervalDayTime { |
358 | | /// The additive identity i.e. `0`. |
359 | | pub const ZERO: Self = Self::new(0, 0); |
360 | | |
361 | | /// The multiplicative identity, i.e. `1`. |
362 | | pub const ONE: Self = Self::new(1, 1); |
363 | | |
364 | | /// The multiplicative inverse, i.e. `-1`. |
365 | | pub const MINUS_ONE: Self = Self::new(-1, -1); |
366 | | |
367 | | /// The maximum value that can be represented |
368 | | pub const MAX: Self = Self::new(i32::MAX, i32::MAX); |
369 | | |
370 | | /// The minimum value that can be represented |
371 | | pub const MIN: Self = Self::new(i32::MIN, i32::MIN); |
372 | | |
373 | | /// Create a new [`IntervalDayTime`] |
374 | | #[inline] |
375 | 0 | pub const fn new(days: i32, milliseconds: i32) -> Self { |
376 | 0 | Self { days, milliseconds } |
377 | 0 | } |
378 | | |
379 | | /// Computes the absolute value |
380 | | #[inline] |
381 | | pub fn wrapping_abs(self) -> Self { |
382 | | Self { |
383 | | days: self.days.wrapping_abs(), |
384 | | milliseconds: self.milliseconds.wrapping_abs(), |
385 | | } |
386 | | } |
387 | | |
388 | | /// Computes the absolute value |
389 | | #[inline] |
390 | | pub fn checked_abs(self) -> Option<Self> { |
391 | | Some(Self { |
392 | | days: self.days.checked_abs()?, |
393 | | milliseconds: self.milliseconds.checked_abs()?, |
394 | | }) |
395 | | } |
396 | | |
397 | | /// Negates the value |
398 | | #[inline] |
399 | | pub fn wrapping_neg(self) -> Self { |
400 | | Self { |
401 | | days: self.days.wrapping_neg(), |
402 | | milliseconds: self.milliseconds.wrapping_neg(), |
403 | | } |
404 | | } |
405 | | |
406 | | /// Negates the value |
407 | | #[inline] |
408 | 0 | pub fn checked_neg(self) -> Option<Self> { |
409 | | Some(Self { |
410 | 0 | days: self.days.checked_neg()?, |
411 | 0 | milliseconds: self.milliseconds.checked_neg()?, |
412 | | }) |
413 | 0 | } |
414 | | |
415 | | /// Performs wrapping addition |
416 | | #[inline] |
417 | | pub fn wrapping_add(self, other: Self) -> Self { |
418 | | Self { |
419 | | days: self.days.wrapping_add(other.days), |
420 | | milliseconds: self.milliseconds.wrapping_add(other.milliseconds), |
421 | | } |
422 | | } |
423 | | |
424 | | /// Performs checked addition |
425 | | #[inline] |
426 | 0 | pub fn checked_add(self, other: Self) -> Option<Self> { |
427 | | Some(Self { |
428 | 0 | days: self.days.checked_add(other.days)?, |
429 | 0 | milliseconds: self.milliseconds.checked_add(other.milliseconds)?, |
430 | | }) |
431 | 0 | } |
432 | | |
433 | | /// Performs wrapping subtraction |
434 | | #[inline] |
435 | | pub fn wrapping_sub(self, other: Self) -> Self { |
436 | | Self { |
437 | | days: self.days.wrapping_sub(other.days), |
438 | | milliseconds: self.milliseconds.wrapping_sub(other.milliseconds), |
439 | | } |
440 | | } |
441 | | |
442 | | /// Performs checked subtraction |
443 | | #[inline] |
444 | 0 | pub fn checked_sub(self, other: Self) -> Option<Self> { |
445 | | Some(Self { |
446 | 0 | days: self.days.checked_sub(other.days)?, |
447 | 0 | milliseconds: self.milliseconds.checked_sub(other.milliseconds)?, |
448 | | }) |
449 | 0 | } |
450 | | |
451 | | /// Performs wrapping multiplication |
452 | | #[inline] |
453 | | pub fn wrapping_mul(self, other: Self) -> Self { |
454 | | Self { |
455 | | days: self.days.wrapping_mul(other.days), |
456 | | milliseconds: self.milliseconds.wrapping_mul(other.milliseconds), |
457 | | } |
458 | | } |
459 | | |
460 | | /// Performs checked multiplication |
461 | 0 | pub fn checked_mul(self, other: Self) -> Option<Self> { |
462 | | Some(Self { |
463 | 0 | days: self.days.checked_mul(other.days)?, |
464 | 0 | milliseconds: self.milliseconds.checked_mul(other.milliseconds)?, |
465 | | }) |
466 | 0 | } |
467 | | |
468 | | /// Performs wrapping division |
469 | | #[inline] |
470 | | pub fn wrapping_div(self, other: Self) -> Self { |
471 | | Self { |
472 | | days: self.days.wrapping_div(other.days), |
473 | | milliseconds: self.milliseconds.wrapping_div(other.milliseconds), |
474 | | } |
475 | | } |
476 | | |
477 | | /// Performs checked division |
478 | 0 | pub fn checked_div(self, other: Self) -> Option<Self> { |
479 | | Some(Self { |
480 | 0 | days: self.days.checked_div(other.days)?, |
481 | 0 | milliseconds: self.milliseconds.checked_div(other.milliseconds)?, |
482 | | }) |
483 | 0 | } |
484 | | |
485 | | /// Performs wrapping remainder |
486 | | #[inline] |
487 | | pub fn wrapping_rem(self, other: Self) -> Self { |
488 | | Self { |
489 | | days: self.days.wrapping_rem(other.days), |
490 | | milliseconds: self.milliseconds.wrapping_rem(other.milliseconds), |
491 | | } |
492 | | } |
493 | | |
494 | | /// Performs checked remainder |
495 | 0 | pub fn checked_rem(self, other: Self) -> Option<Self> { |
496 | | Some(Self { |
497 | 0 | days: self.days.checked_rem(other.days)?, |
498 | 0 | milliseconds: self.milliseconds.checked_rem(other.milliseconds)?, |
499 | | }) |
500 | 0 | } |
501 | | |
502 | | /// Performs wrapping exponentiation |
503 | | #[inline] |
504 | | pub fn wrapping_pow(self, exp: u32) -> Self { |
505 | | Self { |
506 | | days: self.days.wrapping_pow(exp), |
507 | | milliseconds: self.milliseconds.wrapping_pow(exp), |
508 | | } |
509 | | } |
510 | | |
511 | | /// Performs checked exponentiation |
512 | | #[inline] |
513 | | pub fn checked_pow(self, exp: u32) -> Option<Self> { |
514 | | Some(Self { |
515 | | days: self.days.checked_pow(exp)?, |
516 | | milliseconds: self.milliseconds.checked_pow(exp)?, |
517 | | }) |
518 | | } |
519 | | } |
520 | | |
521 | | impl Neg for IntervalDayTime { |
522 | | type Output = Self; |
523 | | |
524 | | #[cfg(debug_assertions)] |
525 | 0 | fn neg(self) -> Self::Output { |
526 | 0 | self.checked_neg().expect("IntervalDayMillisecond overflow") |
527 | 0 | } |
528 | | |
529 | | #[cfg(not(debug_assertions))] |
530 | | fn neg(self) -> Self::Output { |
531 | | self.wrapping_neg() |
532 | | } |
533 | | } |
534 | | |
535 | | derive_arith!( |
536 | | IntervalDayTime, |
537 | | Add, |
538 | | AddAssign, |
539 | | add, |
540 | | add_assign, |
541 | | wrapping_add, |
542 | | checked_add |
543 | | ); |
544 | | derive_arith!( |
545 | | IntervalDayTime, |
546 | | Sub, |
547 | | SubAssign, |
548 | | sub, |
549 | | sub_assign, |
550 | | wrapping_sub, |
551 | | checked_sub |
552 | | ); |
553 | | derive_arith!( |
554 | | IntervalDayTime, |
555 | | Mul, |
556 | | MulAssign, |
557 | | mul, |
558 | | mul_assign, |
559 | | wrapping_mul, |
560 | | checked_mul |
561 | | ); |
562 | | derive_arith!( |
563 | | IntervalDayTime, |
564 | | Div, |
565 | | DivAssign, |
566 | | div, |
567 | | div_assign, |
568 | | wrapping_div, |
569 | | checked_div |
570 | | ); |
571 | | derive_arith!( |
572 | | IntervalDayTime, |
573 | | Rem, |
574 | | RemAssign, |
575 | | rem, |
576 | | rem_assign, |
577 | | wrapping_rem, |
578 | | checked_rem |
579 | | ); |