vortex_dtype/
ptype.rs

1//! Physical type definitions and behavior.
2
3use std::cmp::Ordering;
4use std::fmt::{Debug, Display, Formatter};
5use std::hash::Hash;
6use std::panic::RefUnwindSafe;
7
8use num_traits::bounds::UpperBounded;
9use num_traits::{FromPrimitive, Num, NumCast, ToPrimitive};
10use vortex_error::{VortexError, VortexResult, vortex_err};
11
12use crate::DType;
13use crate::DType::*;
14use crate::half::f16;
15use crate::nullability::Nullability::NonNullable;
16
17/// Physical type enum, represents the in-memory physical layout but might represent a different logical type.
18#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, prost::Enumeration)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
21#[repr(u8)]
22pub enum PType {
23    /// An 8-bit unsigned integer
24    U8 = 0,
25    /// A 16-bit unsigned integer
26    U16 = 1,
27    /// A 32-bit unsigned integer
28    U32 = 2,
29    /// A 64-bit unsigned integer
30    U64 = 3,
31    /// An 8-bit signed integer
32    I8 = 4,
33    /// A 16-bit signed integer
34    I16 = 5,
35    /// A 32-bit signed integer
36    I32 = 6,
37    /// A 64-bit signed integer
38    I64 = 7,
39    /// A 16-bit floating point number
40    F16 = 8,
41    /// A 32-bit floating point number
42    F32 = 9,
43    /// A 64-bit floating point number
44    F64 = 10,
45}
46
47/// A trait for native Rust types that correspond 1:1 to a PType
48pub trait NativePType:
49    Send
50    + Sync
51    + Clone
52    + Copy
53    + Debug
54    + Display
55    + Default
56    + RefUnwindSafe
57    + Num
58    + NumCast
59    + FromPrimitive
60    + ToBytes
61    + TryFromBytes
62    + 'static
63{
64    /// The PType that corresponds to this native type
65    const PTYPE: PType;
66
67    /// Whether this instance (`self`) is NaN
68    /// For integer types, this is always `false`
69    fn is_nan(self) -> bool;
70
71    /// Whether this instance (`self`) is Infinite
72    /// For integer types, this is always `false`
73    fn is_infinite(self) -> bool;
74
75    /// Compare another instance of this type to `self`, providing a total ordering
76    fn total_compare(self, other: Self) -> Ordering;
77
78    /// Test whether self is less than or equal to the other
79    #[inline]
80    fn is_le(self, other: Self) -> bool {
81        self.total_compare(other).is_le()
82    }
83
84    /// Test whether self is less than the other
85    #[inline]
86    fn is_lt(self, other: Self) -> bool {
87        self.total_compare(other).is_lt()
88    }
89
90    /// Test whether self is greater than or equal to the other
91    #[inline]
92    fn is_ge(self, other: Self) -> bool {
93        self.total_compare(other).is_ge()
94    }
95
96    /// Test whether self is greater than the other
97    #[inline]
98    fn is_gt(self, other: Self) -> bool {
99        self.total_compare(other).is_gt()
100    }
101
102    /// Whether another instance of this type (`other`) is bitwise equal to `self`
103    fn is_eq(self, other: Self) -> bool;
104}
105
106macro_rules! native_ptype {
107    ($T:ty, $ptype:tt) => {
108        impl NativePType for $T {
109            const PTYPE: PType = PType::$ptype;
110
111            #[inline]
112            fn is_nan(self) -> bool {
113                false
114            }
115
116            #[inline]
117            fn is_infinite(self) -> bool {
118                false
119            }
120
121            #[inline]
122            fn total_compare(self, other: Self) -> Ordering {
123                self.cmp(&other)
124            }
125
126            #[inline]
127            fn is_eq(self, other: Self) -> bool {
128                self == other
129            }
130        }
131    };
132}
133
134macro_rules! native_float_ptype {
135    ($T:ty, $ptype:tt) => {
136        impl NativePType for $T {
137            const PTYPE: PType = PType::$ptype;
138
139            #[inline]
140            fn is_nan(self) -> bool {
141                <$T>::is_nan(self)
142            }
143
144            #[inline]
145            fn is_infinite(self) -> bool {
146                <$T>::is_infinite(self)
147            }
148
149            #[inline]
150            fn total_compare(self, other: Self) -> Ordering {
151                self.total_cmp(&other)
152            }
153
154            #[inline]
155            fn is_eq(self, other: Self) -> bool {
156                self.to_bits() == other.to_bits()
157            }
158        }
159    };
160}
161
162native_ptype!(u8, U8);
163native_ptype!(u16, U16);
164native_ptype!(u32, U32);
165native_ptype!(u64, U64);
166native_ptype!(i8, I8);
167native_ptype!(i16, I16);
168native_ptype!(i32, I32);
169native_ptype!(i64, I64);
170native_float_ptype!(f16, F16);
171native_float_ptype!(f32, F32);
172native_float_ptype!(f64, F64);
173
174/// Macro to match over each PType, binding the corresponding native type (from `NativePType`)
175#[macro_export]
176macro_rules! match_each_native_ptype {
177    ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
178        macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
179        use $crate::PType;
180        use $crate::half::f16;
181        match $self {
182            PType::I8 => __with__! { i8 },
183            PType::I16 => __with__! { i16 },
184            PType::I32 => __with__! { i32 },
185            PType::I64 => __with__! { i64 },
186            PType::U8 => __with__! { u8 },
187            PType::U16 => __with__! { u16 },
188            PType::U32 => __with__! { u32 },
189            PType::U64 => __with__! { u64 },
190            PType::F16 => __with__! { f16 },
191            PType::F32 => __with__! { f32 },
192            PType::F64 => __with__! { f64 },
193        }
194    });
195    ($self:expr,
196     integral: | $_:tt $integral_enc:ident | { $($integral_body:tt)* }
197     floating_point: | $_2:tt $floating_point_enc:ident | { $($floating_point_body:tt)* }
198    ) => ({
199        macro_rules! __with_integer__ {( $_ $integral_enc:ident ) => ( { $($integral_body)* } )}
200        macro_rules! __with_floating_point__ {( $_ $floating_point_enc:ident ) => ( { $($floating_point_body)* } )}
201        use $crate::PType;
202        use $crate::half::f16;
203        match $self {
204            PType::I8 => __with_integer__! { i8 },
205            PType::I16 => __with_integer__! { i16 },
206            PType::I32 => __with_integer__! { i32 },
207            PType::I64 => __with_integer__! { i64 },
208            PType::U8 => __with_integer__! { u8 },
209            PType::U16 => __with_integer__! { u16 },
210            PType::U32 => __with_integer__! { u32 },
211            PType::U64 => __with_integer__! { u64 },
212            PType::F16 => __with_floating_point__! { f16 },
213            PType::F32 => __with_floating_point__! { f32 },
214            PType::F64 => __with_floating_point__! { f64 },
215        }
216    });
217    ($self:expr,
218     unsigned: | $_:tt $unsigned_enc:ident | { $($unsigned_body:tt)* }
219     signed: | $_1:tt $signed_enc:ident | { $($signed_body:tt)* }
220     floating: | $_2:tt $floating_point_enc:ident | { $($floating_point_body:tt)* }
221    ) => ({
222        macro_rules! __with_unsigned__ {( $_ $unsigned_enc:ident ) => ( { $($unsigned_body)* } )}
223        macro_rules! __with_signed__ {( $_ $signed_enc:ident ) => ( { $($signed_body)* } )}
224        macro_rules! __with_floating_point__ {( $_ $floating_point_enc:ident ) => ( { $($floating_point_body)* } )}
225        use $crate::PType;
226        use $crate::half::f16;
227        match $self {
228            PType::U8 => __with_unsigned__! { u8 },
229            PType::U16 => __with_unsigned__! { u16 },
230            PType::U32 => __with_unsigned__! { u32 },
231            PType::U64 => __with_unsigned__! { u64 },
232            PType::I8 => __with_signed__! { i8 },
233            PType::I16 => __with_signed__! { i16 },
234            PType::I32 => __with_signed__! { i32 },
235            PType::I64 => __with_signed__! { i64 },
236            PType::F16 => __with_floating_point__! { f16 },
237            PType::F32 => __with_floating_point__! { f32 },
238            PType::F64 => __with_floating_point__! { f64 },
239        }
240    })
241}
242
243/// Macro to match over each integer PType, binding the corresponding native type (from `NativePType`)
244#[macro_export]
245macro_rules! match_each_integer_ptype {
246    ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
247        macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
248        use $crate::PType;
249        match $self {
250            PType::I8 => __with__! { i8 },
251            PType::I16 => __with__! { i16 },
252            PType::I32 => __with__! { i32 },
253            PType::I64 => __with__! { i64 },
254            PType::U8 => __with__! { u8 },
255            PType::U16 => __with__! { u16 },
256            PType::U32 => __with__! { u32 },
257            PType::U64 => __with__! { u64 },
258            other => panic!("Unsupported ptype {other}")
259        }
260    })
261}
262
263/// Macro to match over each unsigned integer type, binding the corresponding native type (from `NativePType`)
264#[macro_export]
265macro_rules! match_each_unsigned_integer_ptype {
266    ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
267        macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
268        use $crate::PType;
269        match $self {
270            PType::U8 => __with__! { u8 },
271            PType::U16 => __with__! { u16 },
272            PType::U32 => __with__! { u32 },
273            PType::U64 => __with__! { u64 },
274            other => panic!("Unsupported ptype {other}"),
275        }
276    })
277}
278
279/// Macro to match over each signed integer type, binding the corresponding native type (from `NativePType`)
280#[macro_export]
281macro_rules! match_each_signed_integer_ptype {
282    ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
283        macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
284        use $crate::PType;
285        match $self {
286            PType::I8 => __with__! { i8 },
287            PType::I16 => __with__! { i16 },
288            PType::I32 => __with__! { i32 },
289            PType::I64 => __with__! { i64 },
290            other => panic!("Unsupported ptype {other}"),
291        }
292    })
293}
294
295/// Macro to match over each floating point type, binding the corresponding native type (from `NativePType`)
296#[macro_export]
297macro_rules! match_each_float_ptype {
298    ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
299        macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
300        use $crate::PType;
301        use vortex_dtype::half::f16;
302        match $self {
303            PType::F16 => __with__! { f16 },
304            PType::F32 => __with__! { f32 },
305            PType::F64 => __with__! { f64 },
306            other => panic!("Unsupported ptype {other}"),
307        }
308    })
309}
310
311/// Macro to match over each SIMD capable `PType`, binding the corresponding native type (from `NativePType`)
312///
313/// Note: The match will panic in case of `PType::F16`.
314#[macro_export]
315macro_rules! match_each_native_simd_ptype {
316    ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
317        macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
318        use $crate::PType;
319        match $self {
320            PType::I8 => __with__! { i8 },
321            PType::I16 => __with__! { i16 },
322            PType::I32 => __with__! { i32 },
323            PType::I64 => __with__! { i64 },
324            PType::U8 => __with__! { u8 },
325            PType::U16 => __with__! { u16 },
326            PType::U32 => __with__! { u32 },
327            PType::U64 => __with__! { u64 },
328            PType::F16 => panic!("f16 does not implement simd::SimdElement"),
329            PType::F32 => __with__! { f32 },
330            PType::F64 => __with__! { f64 },
331        }
332    })
333}
334
335impl PType {
336    /// Returns `true` iff this PType is an unsigned integer type
337    pub const fn is_unsigned_int(self) -> bool {
338        matches!(self, Self::U8 | Self::U16 | Self::U32 | Self::U64)
339    }
340
341    /// Returns `true` iff this PType is a signed integer type
342    pub const fn is_signed_int(self) -> bool {
343        matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64)
344    }
345
346    /// Returns `true` iff this PType is an integer type
347    /// Equivalent to `self.is_unsigned_int() || self.is_signed_int()`
348    pub const fn is_int(self) -> bool {
349        self.is_unsigned_int() || self.is_signed_int()
350    }
351
352    /// Returns `true` iff this PType is a floating point type
353    pub const fn is_float(self) -> bool {
354        matches!(self, Self::F16 | Self::F32 | Self::F64)
355    }
356
357    /// Returns the number of bytes in this PType
358    pub const fn byte_width(&self) -> usize {
359        match_each_native_ptype!(self, |$T| std::mem::size_of::<$T>())
360    }
361
362    /// Returns the number of bits in this PType
363    pub const fn bit_width(&self) -> usize {
364        self.byte_width() * 8
365    }
366
367    /// Returns the maximum value of this PType if it is an integer type
368    /// Returns `u64::MAX` if the value is too large to fit in a `u64`
369    pub fn max_value_as_u64(&self) -> u64 {
370        match_each_native_ptype!(self, |$T| <$T as UpperBounded>::max_value().to_u64().unwrap_or(u64::MAX))
371    }
372
373    /// Returns the PType that corresponds to the signed version of this PType
374    pub const fn to_signed(self) -> Self {
375        match self {
376            Self::U8 => Self::I8,
377            Self::U16 => Self::I16,
378            Self::U32 => Self::I32,
379            Self::U64 => Self::I64,
380            _ => self,
381        }
382    }
383
384    /// Returns the PType that corresponds to the unsigned version of this PType
385    /// For floating point types, this will simply return `self`
386    pub const fn to_unsigned(self) -> Self {
387        match self {
388            Self::I8 => Self::U8,
389            Self::I16 => Self::U16,
390            Self::I32 => Self::U32,
391            Self::I64 => Self::U64,
392            _ => self,
393        }
394    }
395}
396
397impl Display for PType {
398    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
399        match self {
400            Self::U8 => write!(f, "u8"),
401            Self::U16 => write!(f, "u16"),
402            Self::U32 => write!(f, "u32"),
403            Self::U64 => write!(f, "u64"),
404            Self::I8 => write!(f, "i8"),
405            Self::I16 => write!(f, "i16"),
406            Self::I32 => write!(f, "i32"),
407            Self::I64 => write!(f, "i64"),
408            Self::F16 => write!(f, "f16"),
409            Self::F32 => write!(f, "f32"),
410            Self::F64 => write!(f, "f64"),
411        }
412    }
413}
414
415impl TryFrom<&DType> for PType {
416    type Error = VortexError;
417
418    #[inline]
419    fn try_from(value: &DType) -> VortexResult<Self> {
420        match value {
421            Primitive(p, _) => Ok(*p),
422            _ => Err(vortex_err!("Cannot convert DType {} into PType", value)),
423        }
424    }
425}
426
427impl From<PType> for &DType {
428    fn from(item: PType) -> Self {
429        // We expand this match statement so that we can return a static reference.
430        match item {
431            PType::I8 => &Primitive(PType::I8, NonNullable),
432            PType::I16 => &Primitive(PType::I16, NonNullable),
433            PType::I32 => &Primitive(PType::I32, NonNullable),
434            PType::I64 => &Primitive(PType::I64, NonNullable),
435            PType::U8 => &Primitive(PType::U8, NonNullable),
436            PType::U16 => &Primitive(PType::U16, NonNullable),
437            PType::U32 => &Primitive(PType::U32, NonNullable),
438            PType::U64 => &Primitive(PType::U64, NonNullable),
439            PType::F16 => &Primitive(PType::F16, NonNullable),
440            PType::F32 => &Primitive(PType::F32, NonNullable),
441            PType::F64 => &Primitive(PType::F64, NonNullable),
442        }
443    }
444}
445
446impl From<PType> for DType {
447    fn from(item: PType) -> Self {
448        Primitive(item, NonNullable)
449    }
450}
451
452/// A trait for types that can be converted to a little-endian byte slice
453pub trait ToBytes: Sized {
454    /// Returns a slice of this type's bytes in little-endian order
455    fn to_le_bytes(&self) -> &[u8];
456}
457
458/// A trait for types that can be converted from a little-endian byte slice
459pub trait TryFromBytes: Sized {
460    /// Attempts to convert a slice of bytes in little-endian order to this type
461    fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self>;
462}
463
464macro_rules! try_from_bytes {
465    ($T:ty) => {
466        impl ToBytes for $T {
467            #[inline]
468            #[allow(clippy::size_of_in_element_count)]
469            fn to_le_bytes(&self) -> &[u8] {
470                // NOTE(ngates): this assumes the platform is little-endian. Currently enforced
471                //  with a flag cfg(target_endian = "little")
472                let raw_ptr = self as *const $T as *const u8;
473                unsafe { std::slice::from_raw_parts(raw_ptr, std::mem::size_of::<$T>()) }
474            }
475        }
476
477        impl TryFromBytes for $T {
478            fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self> {
479                Ok(<$T>::from_le_bytes(bytes.try_into().map_err(|_| {
480                    vortex_err!("Failed to convert bytes into {}", stringify!($T))
481                })?))
482            }
483        }
484    };
485}
486
487try_from_bytes!(u8);
488try_from_bytes!(u16);
489try_from_bytes!(u32);
490try_from_bytes!(u64);
491try_from_bytes!(i8);
492try_from_bytes!(i16);
493try_from_bytes!(i32);
494try_from_bytes!(i64);
495try_from_bytes!(f16);
496try_from_bytes!(f32);
497try_from_bytes!(f64);
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502
503    #[test]
504    fn try_from_bytes() {
505        assert_eq!(u8::try_from_le_bytes(&[0x01]).unwrap(), 0x01);
506        assert_eq!(u16::try_from_le_bytes(&[0x01, 0x02]).unwrap(), 0x0201);
507        assert_eq!(
508            u32::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04]).unwrap(),
509            0x04030201
510        );
511        assert_eq!(
512            u64::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]).unwrap(),
513            0x0807060504030201
514        );
515    }
516
517    #[test]
518    fn to_bytes_rt() {
519        assert_eq!(&0x01u8.to_le_bytes(), &[0x01]);
520        assert_eq!(&0x0201u16.to_le_bytes(), &[0x01, 0x02]);
521
522        assert_eq!(u8::try_from_le_bytes(&42_u8.to_le_bytes()).unwrap(), 42);
523        assert_eq!(u16::try_from_le_bytes(&42_u16.to_le_bytes()).unwrap(), 42);
524        assert_eq!(u32::try_from_le_bytes(&42_u32.to_le_bytes()).unwrap(), 42);
525        assert_eq!(u64::try_from_le_bytes(&42_u64.to_le_bytes()).unwrap(), 42);
526        assert_eq!(i8::try_from_le_bytes(&42_i8.to_le_bytes()).unwrap(), 42);
527        assert_eq!(i16::try_from_le_bytes(&42_i16.to_le_bytes()).unwrap(), 42);
528        assert_eq!(i32::try_from_le_bytes(&42_i32.to_le_bytes()).unwrap(), 42);
529        assert_eq!(i64::try_from_le_bytes(&42_i64.to_le_bytes()).unwrap(), 42);
530        assert_eq!(
531            f16::try_from_le_bytes(&f16::from_f32(42.0).to_le_bytes()).unwrap(),
532            f16::from_f32(42.0)
533        );
534        assert_eq!(
535            f32::try_from_le_bytes(&42.0_f32.to_le_bytes()).unwrap(),
536            42.0
537        );
538        assert_eq!(
539            f64::try_from_le_bytes(&42.0_f64.to_le_bytes()).unwrap(),
540            42.0
541        );
542    }
543
544    #[test]
545    fn max_value_u64() {
546        assert_eq!(PType::U8.max_value_as_u64(), u8::MAX as u64);
547        assert_eq!(PType::U16.max_value_as_u64(), u16::MAX as u64);
548        assert_eq!(PType::U32.max_value_as_u64(), u32::MAX as u64);
549        assert_eq!(PType::U64.max_value_as_u64(), u64::MAX);
550        assert_eq!(PType::I8.max_value_as_u64(), i8::MAX as u64);
551        assert_eq!(PType::I16.max_value_as_u64(), i16::MAX as u64);
552        assert_eq!(PType::I32.max_value_as_u64(), i32::MAX as u64);
553        assert_eq!(PType::I64.max_value_as_u64(), i64::MAX as u64);
554        assert_eq!(PType::F16.max_value_as_u64(), 65504); // f16 is a weird type...
555        assert_eq!(PType::F32.max_value_as_u64(), u64::MAX);
556        assert_eq!(PType::F64.max_value_as_u64(), u64::MAX);
557    }
558
559    #[test]
560    fn widths() {
561        assert_eq!(PType::U8.byte_width(), 1);
562        assert_eq!(PType::U16.byte_width(), 2);
563        assert_eq!(PType::U32.byte_width(), 4);
564        assert_eq!(PType::U64.byte_width(), 8);
565        assert_eq!(PType::I8.byte_width(), 1);
566        assert_eq!(PType::I16.byte_width(), 2);
567        assert_eq!(PType::I32.byte_width(), 4);
568        assert_eq!(PType::I64.byte_width(), 8);
569        assert_eq!(PType::F16.byte_width(), 2);
570        assert_eq!(PType::F32.byte_width(), 4);
571        assert_eq!(PType::F64.byte_width(), 8);
572
573        assert_eq!(PType::U8.bit_width(), 8);
574        assert_eq!(PType::U16.bit_width(), 16);
575        assert_eq!(PType::U32.bit_width(), 32);
576        assert_eq!(PType::U64.bit_width(), 64);
577        assert_eq!(PType::I8.bit_width(), 8);
578        assert_eq!(PType::I16.bit_width(), 16);
579        assert_eq!(PType::I32.bit_width(), 32);
580        assert_eq!(PType::I64.bit_width(), 64);
581        assert_eq!(PType::F16.bit_width(), 16);
582        assert_eq!(PType::F32.bit_width(), 32);
583        assert_eq!(PType::F64.bit_width(), 64);
584    }
585
586    #[test]
587    fn native_ptype_nan_handling() {
588        let a = f32::NAN;
589        let b = f32::NAN;
590        assert_ne!(a, b);
591        assert!(<f32 as NativePType>::is_nan(a));
592        assert!(<f32 as NativePType>::is_nan(b));
593        assert!(<f32 as NativePType>::is_eq(a, b));
594        assert!(<f32 as NativePType>::total_compare(a, b) == Ordering::Equal);
595    }
596
597    #[test]
598    fn to_signed() {
599        assert_eq!(PType::U8.to_signed(), PType::I8);
600        assert_eq!(PType::U16.to_signed(), PType::I16);
601        assert_eq!(PType::U32.to_signed(), PType::I32);
602        assert_eq!(PType::U64.to_signed(), PType::I64);
603        assert_eq!(PType::I8.to_signed(), PType::I8);
604        assert_eq!(PType::I16.to_signed(), PType::I16);
605        assert_eq!(PType::I32.to_signed(), PType::I32);
606        assert_eq!(PType::I64.to_signed(), PType::I64);
607        assert_eq!(PType::F16.to_signed(), PType::F16);
608        assert_eq!(PType::F32.to_signed(), PType::F32);
609        assert_eq!(PType::F64.to_signed(), PType::F64);
610    }
611
612    #[test]
613    fn to_unsigned() {
614        assert_eq!(PType::U8.to_unsigned(), PType::U8);
615        assert_eq!(PType::U16.to_unsigned(), PType::U16);
616        assert_eq!(PType::U32.to_unsigned(), PType::U32);
617        assert_eq!(PType::U64.to_unsigned(), PType::U64);
618        assert_eq!(PType::I8.to_unsigned(), PType::U8);
619        assert_eq!(PType::I16.to_unsigned(), PType::U16);
620        assert_eq!(PType::I32.to_unsigned(), PType::U32);
621        assert_eq!(PType::I64.to_unsigned(), PType::U64);
622        assert_eq!(PType::F16.to_unsigned(), PType::F16);
623        assert_eq!(PType::F32.to_unsigned(), PType::F32);
624        assert_eq!(PType::F64.to_unsigned(), PType::F64);
625    }
626
627    #[test]
628    fn to_dtype() {
629        assert_eq!(DType::from(PType::U8), Primitive(PType::U8, NonNullable));
630        assert_eq!(DType::from(PType::U16), Primitive(PType::U16, NonNullable));
631        assert_eq!(DType::from(PType::U32), Primitive(PType::U32, NonNullable));
632        assert_eq!(DType::from(PType::U64), Primitive(PType::U64, NonNullable));
633        assert_eq!(DType::from(PType::I8), Primitive(PType::I8, NonNullable));
634        assert_eq!(DType::from(PType::I16), Primitive(PType::I16, NonNullable));
635        assert_eq!(DType::from(PType::I32), Primitive(PType::I32, NonNullable));
636        assert_eq!(DType::from(PType::I64), Primitive(PType::I64, NonNullable));
637        assert_eq!(DType::from(PType::F16), Primitive(PType::F16, NonNullable));
638        assert_eq!(DType::from(PType::F32), Primitive(PType::F32, NonNullable));
639        assert_eq!(DType::from(PType::F64), Primitive(PType::F64, NonNullable));
640    }
641}