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