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    (
178        $self:expr,integral: |
179        $integral_enc:ident |
180        $intbody:block,floating_point: |
181        $floating_point_enc:ident |
182        $floatbody:block
183    ) => {{
184        use $crate::PType;
185        use $crate::half::f16;
186        match $self {
187            PType::I8 => {
188                type $integral_enc = i8;
189                $intbody
190            }
191            PType::I16 => {
192                type $integral_enc = i16;
193                $intbody
194            }
195            PType::I32 => {
196                type $integral_enc = i32;
197                $intbody
198            }
199            PType::I64 => {
200                type $integral_enc = i64;
201                $intbody
202            }
203            PType::U8 => {
204                type $integral_enc = u8;
205                $intbody
206            }
207            PType::U16 => {
208                type $integral_enc = u16;
209                $intbody
210            }
211            PType::U32 => {
212                type $integral_enc = u32;
213                $intbody
214            }
215            PType::U64 => {
216                type $integral_enc = u64;
217                $intbody
218            }
219            PType::F16 => {
220                type $floating_point_enc = f16;
221                $floatbody
222            }
223            PType::F32 => {
224                type $floating_point_enc = f32;
225                $floatbody
226            }
227            PType::F64 => {
228                type $floating_point_enc = f64;
229                $floatbody
230            }
231        }
232    }};
233    (
234        $self:expr,unsigned: |
235        $unsigned_enc:ident |
236        $unsigned_body:block,signed: |
237        $signed_enc:ident |
238        $signed_body:block,floating: |
239        $floating_point_enc:ident |
240        $floating_point_body:block
241    ) => {{
242        use $crate::PType;
243        use $crate::half::f16;
244        match $self {
245            PType::U8 => {
246                type $unsigned_enc = u8;
247                $unsigned_body
248            }
249            PType::U16 => {
250                type $unsigned_enc = u16;
251                $unsigned_body
252            }
253            PType::U32 => {
254                type $unsigned_enc = u32;
255                $unsigned_body
256            }
257            PType::U64 => {
258                type $unsigned_enc = u64;
259                $unsigned_body
260            }
261            PType::I8 => {
262                type $signed_enc = i8;
263                $signed_body
264            }
265            PType::I16 => {
266                type $signed_enc = i16;
267                $signed_body
268            }
269            PType::I32 => {
270                type $signed_enc = i32;
271                $signed_body
272            }
273            PType::I64 => {
274                type $signed_enc = i64;
275                $signed_body
276            }
277            PType::F16 => {
278                type $floating_point_enc = f16;
279                $floating_point_body
280            }
281            PType::F32 => {
282                type $floating_point_enc = f32;
283                $floating_point_body
284            }
285            PType::F64 => {
286                type $floating_point_enc = f64;
287                $floating_point_body
288            }
289        }
290    }};
291    ($self:expr, | $tname:ident | $body:block) => {{
292        use $crate::PType;
293        use $crate::half::f16;
294        match $self {
295            PType::I8 => {
296                type $tname = i8;
297                $body
298            }
299            PType::I16 => {
300                type $tname = i16;
301                $body
302            }
303            PType::I32 => {
304                type $tname = i32;
305                $body
306            }
307            PType::I64 => {
308                type $tname = i64;
309                $body
310            }
311            PType::U8 => {
312                type $tname = u8;
313                $body
314            }
315            PType::U16 => {
316                type $tname = u16;
317                $body
318            }
319            PType::U32 => {
320                type $tname = u32;
321                $body
322            }
323            PType::U64 => {
324                type $tname = u64;
325                $body
326            }
327            PType::F16 => {
328                type $tname = f16;
329                $body
330            }
331            PType::F32 => {
332                type $tname = f32;
333                $body
334            }
335            PType::F64 => {
336                type $tname = f64;
337                $body
338            }
339        }
340    }};
341}
342
343/// Macro to match over each integer PType, binding the corresponding native type (from `NativePType`)
344#[macro_export]
345macro_rules! match_each_integer_ptype {
346    ($self:expr, | $enc:ident | $body:block) => {{
347        use $crate::PType;
348        match $self {
349            PType::I8 => {
350                type $enc = i8;
351                $body
352            }
353            PType::I16 => {
354                type $enc = i16;
355                $body
356            }
357            PType::I32 => {
358                type $enc = i32;
359                $body
360            }
361            PType::I64 => {
362                type $enc = i64;
363                $body
364            }
365            PType::U8 => {
366                type $enc = u8;
367                $body
368            }
369            PType::U16 => {
370                type $enc = u16;
371                $body
372            }
373            PType::U32 => {
374                type $enc = u32;
375                $body
376            }
377            PType::U64 => {
378                type $enc = u64;
379                $body
380            }
381            other => panic!("Unsupported ptype {other}"),
382        }
383    }};
384}
385
386/// Macro to match over each unsigned integer type, binding the corresponding native type (from `NativePType`)
387#[macro_export]
388macro_rules! match_each_unsigned_integer_ptype {
389    ($self:expr, | $enc:ident | $body:block) => {{
390        use $crate::PType;
391        match $self {
392            PType::U8 => {
393                type $enc = u8;
394                $body
395            }
396            PType::U16 => {
397                type $enc = u16;
398                $body
399            }
400            PType::U32 => {
401                type $enc = u32;
402                $body
403            }
404            PType::U64 => {
405                type $enc = u64;
406                $body
407            }
408            other => panic!("Unsupported ptype {other}"),
409        }
410    }};
411}
412
413/// Macro to match over each signed integer type, binding the corresponding native type (from `NativePType`)
414#[macro_export]
415macro_rules! match_each_signed_integer_ptype {
416    ($self:expr, | $enc:ident | $body:block) => {{
417        use $crate::PType;
418        match $self {
419            PType::I8 => {
420                type $enc = i8;
421                $body
422            }
423            PType::I16 => {
424                type $enc = i16;
425                $body
426            }
427            PType::I32 => {
428                type $enc = i32;
429                $body
430            }
431            PType::I64 => {
432                type $enc = i64;
433                $body
434            }
435            other => panic!("Unsupported ptype {other}"),
436        }
437    }};
438}
439
440/// Macro to match over each floating point type, binding the corresponding native type (from `NativePType`)
441#[macro_export]
442macro_rules! match_each_float_ptype {
443    ($self:expr, | $enc:ident | $body:block) => {{
444        use vortex_dtype::half::f16;
445        use $crate::PType;
446        match $self {
447            PType::F16 => {
448                type $enc = f16;
449                $body
450            }
451            PType::F32 => {
452                type $enc = f32;
453                $body
454            }
455            PType::F64 => {
456                type $enc = f64;
457                $body
458            }
459            other => panic!("Unsupported ptype {other}"),
460        }
461    }};
462}
463
464/// Macro to match over each SIMD capable `PType`, binding the corresponding native type (from `NativePType`)
465///
466/// Note: The match will panic in case of `PType::F16`.
467#[macro_export]
468macro_rules! match_each_native_simd_ptype {
469    ($self:expr, | $enc:ident | $body:block) => {{
470        use $crate::PType;
471        match $self {
472            PType::I8 => {
473                type $enc = i8;
474                $body
475            }
476            PType::I16 => {
477                type $enc = i16;
478                $body
479            }
480            PType::I32 => {
481                type $enc = i32;
482                $body
483            }
484            PType::I64 => {
485                type $enc = i64;
486                $body
487            }
488            PType::U8 => {
489                type $enc = u8;
490                $body
491            }
492            PType::U16 => {
493                type $enc = u16;
494                $body
495            }
496            PType::U32 => {
497                type $enc = u32;
498                $body
499            }
500            PType::U64 => {
501                type $enc = u64;
502                $body
503            }
504            PType::F16 => panic!("f16 does not implement simd::SimdElement"),
505            PType::F32 => {
506                type $enc = f32;
507                $body
508            }
509            PType::F64 => {
510                type $enc = f64;
511                $body
512            }
513        }
514    }};
515}
516
517impl PType {
518    /// Returns `true` iff this PType is an unsigned integer type
519    pub const fn is_unsigned_int(self) -> bool {
520        matches!(self, Self::U8 | Self::U16 | Self::U32 | Self::U64)
521    }
522
523    /// Returns `true` iff this PType is a signed integer type
524    pub const fn is_signed_int(self) -> bool {
525        matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64)
526    }
527
528    /// Returns `true` iff this PType is an integer type
529    /// Equivalent to `self.is_unsigned_int() || self.is_signed_int()`
530    pub const fn is_int(self) -> bool {
531        self.is_unsigned_int() || self.is_signed_int()
532    }
533
534    /// Returns `true` iff this PType is a floating point type
535    pub const fn is_float(self) -> bool {
536        matches!(self, Self::F16 | Self::F32 | Self::F64)
537    }
538
539    /// Returns the number of bytes in this PType
540    pub const fn byte_width(&self) -> usize {
541        match_each_native_ptype!(self, |T| { size_of::<T>() })
542    }
543
544    /// Returns the number of bits in this PType
545    pub const fn bit_width(&self) -> usize {
546        self.byte_width() * 8
547    }
548
549    /// Returns the maximum value of this PType if it is an integer type
550    /// Returns `u64::MAX` if the value is too large to fit in a `u64`
551    pub fn max_value_as_u64(&self) -> u64 {
552        match_each_native_ptype!(self, |T| {
553            <T as UpperBounded>::max_value()
554                .to_u64()
555                .unwrap_or(u64::MAX)
556        })
557    }
558
559    /// Returns the PType that corresponds to the signed version of this PType
560    pub const fn to_signed(self) -> Self {
561        match self {
562            Self::U8 => Self::I8,
563            Self::U16 => Self::I16,
564            Self::U32 => Self::I32,
565            Self::U64 => Self::I64,
566            _ => self,
567        }
568    }
569
570    /// Returns the PType that corresponds to the unsigned version of this PType
571    /// For floating point types, this will simply return `self`
572    pub const fn to_unsigned(self) -> Self {
573        match self {
574            Self::I8 => Self::U8,
575            Self::I16 => Self::U16,
576            Self::I32 => Self::U32,
577            Self::I64 => Self::U64,
578            _ => self,
579        }
580    }
581}
582
583impl Display for PType {
584    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
585        match self {
586            Self::U8 => write!(f, "u8"),
587            Self::U16 => write!(f, "u16"),
588            Self::U32 => write!(f, "u32"),
589            Self::U64 => write!(f, "u64"),
590            Self::I8 => write!(f, "i8"),
591            Self::I16 => write!(f, "i16"),
592            Self::I32 => write!(f, "i32"),
593            Self::I64 => write!(f, "i64"),
594            Self::F16 => write!(f, "f16"),
595            Self::F32 => write!(f, "f32"),
596            Self::F64 => write!(f, "f64"),
597        }
598    }
599}
600
601impl TryFrom<&DType> for PType {
602    type Error = VortexError;
603
604    #[inline]
605    fn try_from(value: &DType) -> VortexResult<Self> {
606        match value {
607            Primitive(p, _) => Ok(*p),
608            _ => Err(vortex_err!("Cannot convert DType {} into PType", value)),
609        }
610    }
611}
612
613impl From<PType> for &DType {
614    fn from(item: PType) -> Self {
615        // We expand this match statement so that we can return a static reference.
616        match item {
617            PType::I8 => &Primitive(PType::I8, NonNullable),
618            PType::I16 => &Primitive(PType::I16, NonNullable),
619            PType::I32 => &Primitive(PType::I32, NonNullable),
620            PType::I64 => &Primitive(PType::I64, NonNullable),
621            PType::U8 => &Primitive(PType::U8, NonNullable),
622            PType::U16 => &Primitive(PType::U16, NonNullable),
623            PType::U32 => &Primitive(PType::U32, NonNullable),
624            PType::U64 => &Primitive(PType::U64, NonNullable),
625            PType::F16 => &Primitive(PType::F16, NonNullable),
626            PType::F32 => &Primitive(PType::F32, NonNullable),
627            PType::F64 => &Primitive(PType::F64, NonNullable),
628        }
629    }
630}
631
632impl From<PType> for DType {
633    fn from(item: PType) -> Self {
634        Primitive(item, NonNullable)
635    }
636}
637
638/// A trait for types that can be converted to a little-endian byte slice
639pub trait ToBytes: Sized {
640    /// Returns a slice of this type's bytes in little-endian order
641    fn to_le_bytes(&self) -> &[u8];
642}
643
644/// A trait for types that can be converted from a little-endian byte slice
645pub trait TryFromBytes: Sized {
646    /// Attempts to convert a slice of bytes in little-endian order to this type
647    fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self>;
648}
649
650macro_rules! try_from_bytes {
651    ($T:ty) => {
652        impl ToBytes for $T {
653            #[inline]
654            #[allow(clippy::size_of_in_element_count)]
655            fn to_le_bytes(&self) -> &[u8] {
656                // NOTE(ngates): this assumes the platform is little-endian. Currently enforced
657                //  with a flag cfg(target_endian = "little")
658                let raw_ptr = self as *const $T as *const u8;
659                unsafe { std::slice::from_raw_parts(raw_ptr, std::mem::size_of::<$T>()) }
660            }
661        }
662
663        impl TryFromBytes for $T {
664            fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self> {
665                Ok(<$T>::from_le_bytes(bytes.try_into().map_err(|_| {
666                    vortex_err!("Failed to convert bytes into {}", stringify!($T))
667                })?))
668            }
669        }
670    };
671}
672
673try_from_bytes!(u8);
674try_from_bytes!(u16);
675try_from_bytes!(u32);
676try_from_bytes!(u64);
677try_from_bytes!(i8);
678try_from_bytes!(i16);
679try_from_bytes!(i32);
680try_from_bytes!(i64);
681try_from_bytes!(f16);
682try_from_bytes!(f32);
683try_from_bytes!(f64);
684
685#[cfg(test)]
686mod tests {
687    use super::*;
688
689    #[test]
690    fn try_from_bytes() {
691        assert_eq!(u8::try_from_le_bytes(&[0x01]).unwrap(), 0x01);
692        assert_eq!(u16::try_from_le_bytes(&[0x01, 0x02]).unwrap(), 0x0201);
693        assert_eq!(
694            u32::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04]).unwrap(),
695            0x04030201
696        );
697        assert_eq!(
698            u64::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]).unwrap(),
699            0x0807060504030201
700        );
701    }
702
703    #[test]
704    fn to_bytes_rt() {
705        assert_eq!(&0x01u8.to_le_bytes(), &[0x01]);
706        assert_eq!(&0x0201u16.to_le_bytes(), &[0x01, 0x02]);
707
708        assert_eq!(u8::try_from_le_bytes(&42_u8.to_le_bytes()).unwrap(), 42);
709        assert_eq!(u16::try_from_le_bytes(&42_u16.to_le_bytes()).unwrap(), 42);
710        assert_eq!(u32::try_from_le_bytes(&42_u32.to_le_bytes()).unwrap(), 42);
711        assert_eq!(u64::try_from_le_bytes(&42_u64.to_le_bytes()).unwrap(), 42);
712        assert_eq!(i8::try_from_le_bytes(&42_i8.to_le_bytes()).unwrap(), 42);
713        assert_eq!(i16::try_from_le_bytes(&42_i16.to_le_bytes()).unwrap(), 42);
714        assert_eq!(i32::try_from_le_bytes(&42_i32.to_le_bytes()).unwrap(), 42);
715        assert_eq!(i64::try_from_le_bytes(&42_i64.to_le_bytes()).unwrap(), 42);
716        assert_eq!(
717            f16::try_from_le_bytes(&f16::from_f32(42.0).to_le_bytes()).unwrap(),
718            f16::from_f32(42.0)
719        );
720        assert_eq!(
721            f32::try_from_le_bytes(&42.0_f32.to_le_bytes()).unwrap(),
722            42.0
723        );
724        assert_eq!(
725            f64::try_from_le_bytes(&42.0_f64.to_le_bytes()).unwrap(),
726            42.0
727        );
728    }
729
730    #[test]
731    fn max_value_u64() {
732        assert_eq!(PType::U8.max_value_as_u64(), u8::MAX as u64);
733        assert_eq!(PType::U16.max_value_as_u64(), u16::MAX as u64);
734        assert_eq!(PType::U32.max_value_as_u64(), u32::MAX as u64);
735        assert_eq!(PType::U64.max_value_as_u64(), u64::MAX);
736        assert_eq!(PType::I8.max_value_as_u64(), i8::MAX as u64);
737        assert_eq!(PType::I16.max_value_as_u64(), i16::MAX as u64);
738        assert_eq!(PType::I32.max_value_as_u64(), i32::MAX as u64);
739        assert_eq!(PType::I64.max_value_as_u64(), i64::MAX as u64);
740        assert_eq!(PType::F16.max_value_as_u64(), 65504); // f16 is a weird type...
741        assert_eq!(PType::F32.max_value_as_u64(), u64::MAX);
742        assert_eq!(PType::F64.max_value_as_u64(), u64::MAX);
743    }
744
745    #[test]
746    fn widths() {
747        assert_eq!(PType::U8.byte_width(), 1);
748        assert_eq!(PType::U16.byte_width(), 2);
749        assert_eq!(PType::U32.byte_width(), 4);
750        assert_eq!(PType::U64.byte_width(), 8);
751        assert_eq!(PType::I8.byte_width(), 1);
752        assert_eq!(PType::I16.byte_width(), 2);
753        assert_eq!(PType::I32.byte_width(), 4);
754        assert_eq!(PType::I64.byte_width(), 8);
755        assert_eq!(PType::F16.byte_width(), 2);
756        assert_eq!(PType::F32.byte_width(), 4);
757        assert_eq!(PType::F64.byte_width(), 8);
758
759        assert_eq!(PType::U8.bit_width(), 8);
760        assert_eq!(PType::U16.bit_width(), 16);
761        assert_eq!(PType::U32.bit_width(), 32);
762        assert_eq!(PType::U64.bit_width(), 64);
763        assert_eq!(PType::I8.bit_width(), 8);
764        assert_eq!(PType::I16.bit_width(), 16);
765        assert_eq!(PType::I32.bit_width(), 32);
766        assert_eq!(PType::I64.bit_width(), 64);
767        assert_eq!(PType::F16.bit_width(), 16);
768        assert_eq!(PType::F32.bit_width(), 32);
769        assert_eq!(PType::F64.bit_width(), 64);
770    }
771
772    #[test]
773    fn native_ptype_nan_handling() {
774        let a = f32::NAN;
775        let b = f32::NAN;
776        assert_ne!(a, b);
777        assert!(<f32 as NativePType>::is_nan(a));
778        assert!(<f32 as NativePType>::is_nan(b));
779        assert!(<f32 as NativePType>::is_eq(a, b));
780        assert!(<f32 as NativePType>::total_compare(a, b) == Ordering::Equal);
781    }
782
783    #[test]
784    fn to_signed() {
785        assert_eq!(PType::U8.to_signed(), PType::I8);
786        assert_eq!(PType::U16.to_signed(), PType::I16);
787        assert_eq!(PType::U32.to_signed(), PType::I32);
788        assert_eq!(PType::U64.to_signed(), PType::I64);
789        assert_eq!(PType::I8.to_signed(), PType::I8);
790        assert_eq!(PType::I16.to_signed(), PType::I16);
791        assert_eq!(PType::I32.to_signed(), PType::I32);
792        assert_eq!(PType::I64.to_signed(), PType::I64);
793        assert_eq!(PType::F16.to_signed(), PType::F16);
794        assert_eq!(PType::F32.to_signed(), PType::F32);
795        assert_eq!(PType::F64.to_signed(), PType::F64);
796    }
797
798    #[test]
799    fn to_unsigned() {
800        assert_eq!(PType::U8.to_unsigned(), PType::U8);
801        assert_eq!(PType::U16.to_unsigned(), PType::U16);
802        assert_eq!(PType::U32.to_unsigned(), PType::U32);
803        assert_eq!(PType::U64.to_unsigned(), PType::U64);
804        assert_eq!(PType::I8.to_unsigned(), PType::U8);
805        assert_eq!(PType::I16.to_unsigned(), PType::U16);
806        assert_eq!(PType::I32.to_unsigned(), PType::U32);
807        assert_eq!(PType::I64.to_unsigned(), PType::U64);
808        assert_eq!(PType::F16.to_unsigned(), PType::F16);
809        assert_eq!(PType::F32.to_unsigned(), PType::F32);
810        assert_eq!(PType::F64.to_unsigned(), PType::F64);
811    }
812
813    #[test]
814    fn to_dtype() {
815        assert_eq!(DType::from(PType::U8), Primitive(PType::U8, NonNullable));
816        assert_eq!(DType::from(PType::U16), Primitive(PType::U16, NonNullable));
817        assert_eq!(DType::from(PType::U32), Primitive(PType::U32, NonNullable));
818        assert_eq!(DType::from(PType::U64), Primitive(PType::U64, NonNullable));
819        assert_eq!(DType::from(PType::I8), Primitive(PType::I8, NonNullable));
820        assert_eq!(DType::from(PType::I16), Primitive(PType::I16, NonNullable));
821        assert_eq!(DType::from(PType::I32), Primitive(PType::I32, NonNullable));
822        assert_eq!(DType::from(PType::I64), Primitive(PType::I64, NonNullable));
823        assert_eq!(DType::from(PType::F16), Primitive(PType::F16, NonNullable));
824        assert_eq!(DType::from(PType::F32), Primitive(PType::F32, NonNullable));
825        assert_eq!(DType::from(PType::F64), Primitive(PType::F64, NonNullable));
826    }
827}