vortex_dtype/
ptype.rs

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