Skip to main content

tenferro_tensor/
default_scalars.rs

1//! The crate's default scalar set as a dtype-erased host value.
2//!
3//! [`DefaultScalars`] is tenferro's own [`ScalarSet`]: one value holding a
4//! host tensor of any preset dtype. Each member is the canonical
5//! `TypedTensor<T, DynRank, Host>` (views: `TypedTensorView<'a, T, DynRank,
6//! Host>`), so the set adds dtype erasure without a second owner or view
7//! family. `tenferro-tensor-core` keeps the scalar tags and promotion facts.
8
9use num_complex::{Complex32, Complex64};
10use tenferro_tensor_core::{promote_in_set, DType, DynRank};
11
12use crate::{Host, ScalarSet, StridedSliceSpec, TensorScalar, TypedTensor, TypedTensorView};
13
14fn dtype_mismatch(op: &'static str, expected: DType, actual: DType) -> crate::Error {
15    crate::Error::validation(
16        op,
17        tenferro_tensor_core::ValidationError::DTypeMismatch { expected, actual },
18    )
19}
20
21/// Dynamic host tensor over the crate's preset scalar set.
22///
23/// The payload is a private inline value: a host tensor is one move and a copy
24/// allocates nothing, and the preset variants are not part of the public
25/// surface. Construct through [`DefaultScalars::from_vec_col_major`] and read
26/// through [`DefaultScalars::as_slice`], [`DefaultScalars::as_mut_slice`], or
27/// [`DefaultScalars::into_vec_col_major`].
28///
29/// # Examples
30///
31/// ```rust
32/// use tenferro_tensor::{DefaultScalars, DType};
33/// use tenferro_tensor::ScalarSet;
34///
35/// let value = DefaultScalars::from_vec_col_major(vec![1], vec![7_i32])?;
36/// assert_eq!(value.tag(), DType::I32);
37/// assert_eq!(value.as_slice::<i32>()?, &[7]);
38/// # Ok::<(), tenferro_tensor::Error>(())
39/// ```
40#[derive(Clone, Debug)]
41pub struct DefaultScalars {
42    value: DefaultScalarsValue,
43}
44
45/// Private payload of [`DefaultScalars`].
46///
47/// The variants are not part of the public surface: construct through
48/// [`DefaultScalars::from_vec_col_major`] and read through
49/// [`DefaultScalars::as_slice`].
50#[derive(Clone, Debug)]
51pub(crate) enum DefaultScalarsValue {
52    F32(TypedTensor<f32, DynRank, Host>),
53    F64(TypedTensor<f64, DynRank, Host>),
54    I32(TypedTensor<i32, DynRank, Host>),
55    I64(TypedTensor<i64, DynRank, Host>),
56    Bool(TypedTensor<bool, DynRank, Host>),
57    C32(TypedTensor<Complex32, DynRank, Host>),
58    C64(TypedTensor<Complex64, DynRank, Host>),
59}
60
61impl ScalarSet for DefaultScalarsValue {
62    type Tag = DType;
63
64    const TAGS: &'static [Self::Tag] = DType::TAGS;
65
66    fn tag(&self) -> Self::Tag {
67        match self {
68            Self::F32(_) => DType::F32,
69            Self::F64(_) => DType::F64,
70            Self::I32(_) => DType::I32,
71            Self::I64(_) => DType::I64,
72            Self::Bool(_) => DType::Bool,
73            Self::C32(_) => DType::C32,
74            Self::C64(_) => DType::C64,
75        }
76    }
77
78    fn promote(lhs: Self::Tag, rhs: Self::Tag) -> Self::Tag {
79        // An externally defined member promotes to itself, because tenferro
80        // declares no facts that relate it to one of its own members.
81        if matches!(lhs, DType::External(_)) {
82            return lhs;
83        }
84        if matches!(rhs, DType::External(_)) {
85            return rhs;
86        }
87        promote_in_set(DType::TAGS, DType::SPECS, lhs, rhs)
88    }
89}
90
91impl DefaultScalarsValue {
92    /// Return the tensor dtype tag.
93    ///
94    /// # Examples
95    ///
96    /// ```rust
97    /// use tenferro_tensor::{DefaultScalars, DType};
98    ///
99    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![false])?;
100    /// assert_eq!(tensor.dtype(), DType::Bool);
101    /// # Ok::<(), tenferro_tensor::Error>(())
102    /// ```
103    pub fn dtype(&self) -> DType {
104        match self {
105            Self::F32(_) => DType::F32,
106            Self::F64(_) => DType::F64,
107            Self::I32(_) => DType::I32,
108            Self::I64(_) => DType::I64,
109            Self::Bool(_) => DType::Bool,
110            Self::C32(_) => DType::C32,
111            Self::C64(_) => DType::C64,
112        }
113    }
114
115    /// Borrow the tensor shape.
116    ///
117    /// # Examples
118    ///
119    /// ```rust
120    /// use tenferro_tensor::DefaultScalars;
121    ///
122    /// let tensor = DefaultScalars::from_vec_col_major(vec![2], vec![1_i32, 2])?;
123    /// assert_eq!(tensor.shape(), &[2]);
124    /// # Ok::<(), tenferro_tensor::Error>(())
125    /// ```
126    pub fn shape(&self) -> &[usize] {
127        match self {
128            Self::F32(t) => t.shape(),
129            Self::F64(t) => t.shape(),
130            Self::I32(t) => t.shape(),
131            Self::I64(t) => t.shape(),
132            Self::Bool(t) => t.shape(),
133            Self::C32(t) => t.shape(),
134            Self::C64(t) => t.shape(),
135        }
136    }
137
138    /// Return the tensor rank.
139    ///
140    /// # Examples
141    ///
142    /// ```rust
143    /// use tenferro_tensor::DefaultScalars;
144    ///
145    /// let tensor = DefaultScalars::from_vec_col_major(vec![1, 1], vec![1_i64])?;
146    /// assert_eq!(tensor.rank(), 2);
147    /// # Ok::<(), tenferro_tensor::Error>(())
148    /// ```
149    pub fn rank(&self) -> usize {
150        self.shape().len()
151    }
152
153    /// Return whether the tensor has zero elements.
154    ///
155    /// # Examples
156    ///
157    /// ```rust
158    /// use tenferro_tensor::DefaultScalars;
159    ///
160    /// let tensor = DefaultScalars::from_vec_col_major(vec![0], Vec::<f64>::new())?;
161    /// assert!(tensor.is_empty());
162    /// # Ok::<(), tenferro_tensor::Error>(())
163    /// ```
164    pub fn is_empty(&self) -> bool {
165        match self {
166            Self::F32(t) => t.shape().contains(&0),
167            Self::F64(t) => t.shape().contains(&0),
168            Self::I32(t) => t.shape().contains(&0),
169            Self::I64(t) => t.shape().contains(&0),
170            Self::Bool(t) => t.shape().contains(&0),
171            Self::C32(t) => t.shape().contains(&0),
172            Self::C64(t) => t.shape().contains(&0),
173        }
174    }
175
176    /// Borrow this tensor as a dynamic zero-offset view.
177    ///
178    /// # Examples
179    ///
180    /// ```rust
181    /// use tenferro_tensor::{DefaultScalars, DType};
182    ///
183    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1_i64])?;
184    /// assert_eq!(tensor.as_view().dtype(), DType::I64);
185    /// # Ok::<(), tenferro_tensor::Error>(())
186    /// ```
187    pub fn as_view(&self) -> DefaultScalarsView<'_> {
188        match self {
189            Self::F32(t) => DefaultScalarsView::F32(t.as_view()),
190            Self::F64(t) => DefaultScalarsView::F64(t.as_view()),
191            Self::I32(t) => DefaultScalarsView::I32(t.as_view()),
192            Self::I64(t) => DefaultScalarsView::I64(t.as_view()),
193            Self::Bool(t) => DefaultScalarsView::Bool(t.as_view()),
194            Self::C32(t) => DefaultScalarsView::C32(t.as_view()),
195            Self::C64(t) => DefaultScalarsView::C64(t.as_view()),
196        }
197    }
198}
199
200impl DefaultScalars {
201    /// Create a dynamic tensor from a column-major host buffer.
202    ///
203    /// # Examples
204    ///
205    /// ```rust
206    /// use tenferro_tensor::{DefaultScalars, DType};
207    ///
208    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![2.0_f32])?;
209    /// assert_eq!(tensor.dtype(), DType::F32);
210    /// # Ok::<(), tenferro_tensor::Error>(())
211    /// ```
212    ///
213    /// # Errors
214    ///
215    /// Returns a validation error carrying
216    /// [`tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch`] when the shape product
217    /// differs from `data.len()`, or [`tenferro_tensor_core::ValidationError::IntegerOverflow`]
218    /// when validating the shape overflows.
219    pub fn from_vec_col_major<T: TensorScalar>(
220        shape: impl Into<Vec<usize>>,
221        data: Vec<T>,
222    ) -> crate::Result<Self> {
223        T::into_default_scalars(shape.into(), data)
224    }
225
226    /// Return the tensor dtype tag.
227    ///
228    /// # Examples
229    ///
230    /// ```rust
231    /// use tenferro_tensor::{DefaultScalars, DType};
232    ///
233    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![false])?;
234    /// assert_eq!(tensor.dtype(), DType::Bool);
235    /// # Ok::<(), tenferro_tensor::Error>(())
236    /// ```
237    pub fn dtype(&self) -> DType {
238        self.value.dtype()
239    }
240
241    /// Borrow the tensor shape.
242    ///
243    /// # Examples
244    ///
245    /// ```rust
246    /// use tenferro_tensor::DefaultScalars;
247    ///
248    /// let tensor = DefaultScalars::from_vec_col_major(vec![2], vec![1_i32, 2])?;
249    /// assert_eq!(tensor.shape(), &[2]);
250    /// # Ok::<(), tenferro_tensor::Error>(())
251    /// ```
252    pub fn shape(&self) -> &[usize] {
253        self.value.shape()
254    }
255
256    /// Return the tensor rank.
257    ///
258    /// # Examples
259    ///
260    /// ```rust
261    /// use tenferro_tensor::DefaultScalars;
262    ///
263    /// let tensor = DefaultScalars::from_vec_col_major(vec![1, 1], vec![1_i64])?;
264    /// assert_eq!(tensor.rank(), 2);
265    /// # Ok::<(), tenferro_tensor::Error>(())
266    /// ```
267    pub fn rank(&self) -> usize {
268        self.value.rank()
269    }
270
271    /// Return whether the tensor has zero elements.
272    ///
273    /// # Examples
274    ///
275    /// ```rust
276    /// use tenferro_tensor::DefaultScalars;
277    ///
278    /// let tensor = DefaultScalars::from_vec_col_major(vec![0], Vec::<f64>::new())?;
279    /// assert!(tensor.is_empty());
280    /// # Ok::<(), tenferro_tensor::Error>(())
281    /// ```
282    pub fn is_empty(&self) -> bool {
283        self.value.is_empty()
284    }
285
286    /// Borrow the typed host slice when the dtype matches.
287    ///
288    /// # Examples
289    ///
290    /// ```rust
291    /// use tenferro_tensor::DefaultScalars;
292    ///
293    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![3.0_f64])?;
294    /// assert_eq!(tensor.as_slice::<f64>()?, &[3.0]);
295    /// assert!(tensor.as_slice::<f32>().is_err());
296    /// # Ok::<(), tenferro_tensor::Error>(())
297    /// ```
298    ///
299    /// # Errors
300    ///
301    /// Returns [`crate::Error::Validation`] with a dtype mismatch when `T`
302    /// does not match the tensor's runtime dtype.
303    pub fn as_slice<T: TensorScalar>(&self) -> crate::Result<&[T]> {
304        let actual = self.dtype();
305        T::default_scalars_slice(self)
306            .ok_or_else(|| dtype_mismatch("DefaultScalars::as_slice", T::dtype(), actual))
307    }
308
309    /// Mutably borrow the typed host slice when the dtype matches.
310    ///
311    /// # Examples
312    ///
313    /// ```rust
314    /// use tenferro_tensor::DefaultScalars;
315    ///
316    /// let mut tensor = DefaultScalars::from_vec_col_major(vec![1], vec![3.0_f64])?;
317    /// tensor.as_mut_slice::<f64>()?[0] = 4.0;
318    /// assert_eq!(tensor.as_slice::<f64>()?, &[4.0]);
319    /// # Ok::<(), tenferro_tensor::Error>(())
320    /// ```
321    ///
322    /// # Errors
323    ///
324    /// Returns [`crate::Error::Validation`] with a dtype mismatch when `T`
325    /// does not match the tensor's runtime dtype.
326    pub fn as_mut_slice<T: TensorScalar>(&mut self) -> crate::Result<&mut [T]> {
327        let actual = self.dtype();
328        T::default_scalars_slice_mut(self)
329            .ok_or_else(|| dtype_mismatch("DefaultScalars::as_mut_slice", T::dtype(), actual))
330    }
331
332    /// Borrow this tensor as a dynamic zero-offset view.
333    ///
334    /// # Examples
335    ///
336    /// ```rust
337    /// use tenferro_tensor::{DefaultScalars, DType};
338    ///
339    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1.0_f32])?;
340    /// assert_eq!(tensor.as_view().dtype(), DType::F32);
341    /// # Ok::<(), tenferro_tensor::Error>(())
342    /// ```
343    pub fn as_view(&self) -> DefaultScalarsView<'_> {
344        self.value.as_view()
345    }
346
347    /// Consume this tensor and return typed column-major data when the dtype matches.
348    ///
349    /// # Examples
350    ///
351    /// ```rust
352    /// use tenferro_tensor::DefaultScalars;
353    ///
354    /// let tensor = DefaultScalars::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
355    /// let (shape, data) = tensor.into_vec_col_major::<f64>()?;
356    /// assert_eq!(shape, vec![2]);
357    /// assert_eq!(data, vec![1.0, 2.0]);
358    /// # Ok::<(), tenferro_tensor::Error>(())
359    /// ```
360    ///
361    /// # Errors
362    ///
363    /// Returns [`crate::Error::Validation`] with a dtype mismatch when `T`
364    /// does not match the tensor's runtime dtype.
365    pub fn into_vec_col_major<T: TensorScalar>(self) -> crate::Result<(Vec<usize>, Vec<T>)> {
366        let actual = self.dtype();
367        T::from_default_scalars(self)
368            .map(TypedTensor::<T, DynRank, Host>::into_vec_col_major)
369            .ok_or_else(|| dtype_mismatch("DefaultScalars::into_vec_col_major", T::dtype(), actual))
370    }
371
372    pub(crate) fn from_payload(value: DefaultScalarsValue) -> Self {
373        Self { value }
374    }
375
376    pub(crate) fn payload(&self) -> &DefaultScalarsValue {
377        &self.value
378    }
379
380    pub(crate) fn payload_mut(&mut self) -> &mut DefaultScalarsValue {
381        &mut self.value
382    }
383
384    pub(crate) fn into_payload(self) -> DefaultScalarsValue {
385        self.value
386    }
387}
388
389impl ScalarSet for DefaultScalars {
390    type Tag = DType;
391
392    const TAGS: &'static [Self::Tag] = <DefaultScalarsValue as ScalarSet>::TAGS;
393
394    fn tag(&self) -> Self::Tag {
395        self.value.tag()
396    }
397
398    fn promote(lhs: Self::Tag, rhs: Self::Tag) -> Self::Tag {
399        <DefaultScalarsValue as ScalarSet>::promote(lhs, rhs)
400    }
401}
402
403/// Dynamic borrowed host tensor view.
404///
405/// # Examples
406///
407/// ```rust
408/// use tenferro_tensor::{DefaultScalars, DType};
409///
410/// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![true])?;
411/// let view = tensor.as_view();
412/// assert_eq!(view.dtype(), DType::Bool);
413/// # Ok::<(), tenferro_tensor::Error>(())
414/// ```
415///
416/// ```compile_fail
417/// # use tenferro_tensor::DefaultScalars;
418/// # let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap();
419/// let a = tensor.as_view();
420/// let b = tensor.as_view();
421/// let _ = a == b;
422/// ```
423#[derive(Clone, Debug)]
424pub enum DefaultScalarsView<'a> {
425    F32(TypedTensorView<'a, f32, DynRank, Host>),
426    F64(TypedTensorView<'a, f64, DynRank, Host>),
427    I32(TypedTensorView<'a, i32, DynRank, Host>),
428    I64(TypedTensorView<'a, i64, DynRank, Host>),
429    Bool(TypedTensorView<'a, bool, DynRank, Host>),
430    C32(TypedTensorView<'a, Complex32, DynRank, Host>),
431    C64(TypedTensorView<'a, Complex64, DynRank, Host>),
432}
433
434macro_rules! impl_dynamic_view {
435    ($self:ident, $method:ident($($arg:ident),*) => $inner:ident) => {
436        match $self {
437            DefaultScalarsView::F32(view) => DefaultScalarsView::F32(view.$method($($arg),*)?),
438            DefaultScalarsView::F64(view) => DefaultScalarsView::F64(view.$method($($arg),*)?),
439            DefaultScalarsView::I32(view) => DefaultScalarsView::I32(view.$method($($arg),*)?),
440            DefaultScalarsView::I64(view) => DefaultScalarsView::I64(view.$method($($arg),*)?),
441            DefaultScalarsView::Bool(view) => DefaultScalarsView::Bool(view.$method($($arg),*)?),
442            DefaultScalarsView::C32(view) => DefaultScalarsView::C32(view.$method($($arg),*)?),
443            DefaultScalarsView::C64(view) => DefaultScalarsView::C64(view.$method($($arg),*)?),
444        }
445    };
446}
447
448impl<'a> DefaultScalarsView<'a> {
449    /// Return this view's dtype.
450    ///
451    /// # Examples
452    ///
453    /// ```rust
454    /// use tenferro_tensor::{DefaultScalars, DType};
455    ///
456    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1.0_f32])?;
457    /// assert_eq!(tensor.as_view().dtype(), DType::F32);
458    /// # Ok::<(), tenferro_tensor::Error>(())
459    /// ```
460    pub fn dtype(&self) -> DType {
461        match self {
462            Self::F32(_) => DType::F32,
463            Self::F64(_) => DType::F64,
464            Self::I32(_) => DType::I32,
465            Self::I64(_) => DType::I64,
466            Self::Bool(_) => DType::Bool,
467            Self::C32(_) => DType::C32,
468            Self::C64(_) => DType::C64,
469        }
470    }
471
472    /// Borrow this view's shape.
473    ///
474    /// # Examples
475    ///
476    /// ```rust
477    /// use tenferro_tensor::DefaultScalars;
478    ///
479    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1.0_f64])?;
480    /// assert_eq!(tensor.as_view().shape(), &[1]);
481    /// # Ok::<(), tenferro_tensor::Error>(())
482    /// ```
483    pub fn shape(&self) -> &[usize] {
484        match self {
485            Self::F32(view) => view.shape(),
486            Self::F64(view) => view.shape(),
487            Self::I32(view) => view.shape(),
488            Self::I64(view) => view.shape(),
489            Self::Bool(view) => view.shape(),
490            Self::C32(view) => view.shape(),
491            Self::C64(view) => view.shape(),
492        }
493    }
494
495    /// Return the view rank.
496    ///
497    /// # Examples
498    ///
499    /// ```rust
500    /// use tenferro_tensor::DefaultScalars;
501    ///
502    /// let tensor = DefaultScalars::from_vec_col_major(vec![1, 1], vec![1_i64])?;
503    /// assert_eq!(tensor.as_view().rank(), 2);
504    /// # Ok::<(), tenferro_tensor::Error>(())
505    /// ```
506    pub fn rank(&self) -> usize {
507        self.shape().len()
508    }
509
510    /// Return whether this view has zero logical elements.
511    ///
512    /// # Examples
513    ///
514    /// ```rust
515    /// use tenferro_tensor::DefaultScalars;
516    ///
517    /// let tensor = DefaultScalars::from_vec_col_major(vec![0], Vec::<f64>::new())?;
518    /// assert!(tensor.as_view().is_empty());
519    /// # Ok::<(), tenferro_tensor::Error>(())
520    /// ```
521    pub fn is_empty(&self) -> bool {
522        match self {
523            Self::F32(view) => view.shape().contains(&0),
524            Self::F64(view) => view.shape().contains(&0),
525            Self::I32(view) => view.shape().contains(&0),
526            Self::I64(view) => view.shape().contains(&0),
527            Self::Bool(view) => view.shape().contains(&0),
528            Self::C32(view) => view.shape().contains(&0),
529            Self::C64(view) => view.shape().contains(&0),
530        }
531    }
532
533    /// Return a metadata-only reshape of this dynamic view.
534    ///
535    /// # Examples
536    ///
537    /// ```rust
538    /// use tenferro_tensor::DefaultScalars;
539    ///
540    /// let tensor = DefaultScalars::from_vec_col_major(vec![4], vec![1_i32, 2, 3, 4])?;
541    /// assert_eq!(tensor.as_view().reshape_view(vec![2, 2])?.shape(), &[2, 2]);
542    /// # Ok::<(), tenferro_tensor::Error>(())
543    /// ```
544    ///
545    /// # Errors
546    ///
547    /// Returns a validation error carrying [`tenferro_tensor_core::ValidationError::ShapeMismatch`]
548    /// when the element counts differ,
549    /// [`tenferro_tensor_core::ValidationError::NonContiguousViewAsSlice`] when the view's strides
550    /// cannot express the new shape, or [`tenferro_tensor_core::ValidationError::IntegerOverflow`]
551    /// when shape arithmetic overflows.
552    pub fn reshape_view(&self, shape: impl AsRef<[usize]>) -> crate::Result<Self> {
553        let shape = shape.as_ref();
554        Ok(impl_dynamic_view!(self, reshape_view(shape) => view))
555    }
556
557    /// Return a metadata-only transposed dynamic view with axes in the requested order.
558    ///
559    /// # Examples
560    ///
561    /// ```rust
562    /// use tenferro_tensor::DefaultScalars;
563    ///
564    /// let tensor = DefaultScalars::from_vec_col_major(vec![1, 2], vec![1_i64, 2])?;
565    /// assert_eq!(tensor.as_view().transpose_view(&[1, 0])?.shape(), &[2, 1]);
566    /// # Ok::<(), tenferro_tensor::Error>(())
567    /// ```
568    ///
569    /// # Errors
570    ///
571    /// Returns a validation error carrying
572    /// [`tenferro_tensor_core::ValidationError::InvalidPermutationLength`],
573    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`] or
574    /// [`tenferro_tensor_core::ValidationError::DuplicateAxis`] when `axes` is not a permutation of
575    /// the view rank.
576    pub fn transpose_view(&self, axes: &[usize]) -> crate::Result<Self> {
577        Ok(impl_dynamic_view!(self, transpose_view(axes) => view))
578    }
579
580    /// Return a metadata-only positive-step slice of this dynamic view.
581    ///
582    /// # Examples
583    ///
584    /// ```rust
585    /// use tenferro_tensor::{DefaultScalars, StridedSliceSpec};
586    ///
587    /// let tensor = DefaultScalars::from_vec_col_major(vec![3], vec![1_i64, 2, 3])?;
588    /// assert_eq!(
589    ///     tensor.as_view().slice_view(&[StridedSliceSpec::new(1, Some(3), 1)])?.shape(),
590    ///     &[2],
591    /// );
592    /// # Ok::<(), tenferro_tensor::Error>(())
593    /// ```
594    ///
595    /// # Errors
596    ///
597    /// Returns [`crate::Error::Validation`] for a rank mismatch or invalid
598    /// slice bounds or step.
599    pub fn slice_view(&self, spec: &[StridedSliceSpec]) -> crate::Result<Self> {
600        Ok(impl_dynamic_view!(self, slice_view(spec) => view))
601    }
602}
603
604/// Core-neutral tensor input reference.
605///
606/// # Examples
607///
608/// ```rust
609/// use tenferro_tensor::{DefaultScalars, DefaultScalarsRef};
610///
611/// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1.0_f32])?;
612/// let reference = DefaultScalarsRef::Tensor(&tensor);
613/// assert_eq!(reference.shape(), &[1]);
614/// # Ok::<(), tenferro_tensor::Error>(())
615/// ```
616// The view variant holds a canonical `TypedTensorView` inline so a borrowed
617// input costs no allocation; boxing it would allocate on every reference.
618#[allow(clippy::large_enum_variant)]
619#[derive(Clone, Debug)]
620pub enum DefaultScalarsRef<'a> {
621    Tensor(&'a DefaultScalars),
622    View(DefaultScalarsView<'a>),
623}
624
625impl<'a> DefaultScalarsRef<'a> {
626    /// Return the referenced dtype.
627    ///
628    /// # Examples
629    ///
630    /// ```rust
631    /// use tenferro_tensor::{DefaultScalars, DefaultScalarsRef, DType};
632    ///
633    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1_i64])?;
634    /// assert_eq!(DefaultScalarsRef::Tensor(&tensor).dtype(), DType::I64);
635    /// # Ok::<(), tenferro_tensor::Error>(())
636    /// ```
637    pub fn dtype(&self) -> DType {
638        match self {
639            Self::Tensor(tensor) => tensor.dtype(),
640            Self::View(view) => view.dtype(),
641        }
642    }
643
644    /// Borrow the referenced shape.
645    ///
646    /// # Examples
647    ///
648    /// ```rust
649    /// use tenferro_tensor::{DefaultScalars, DefaultScalarsRef};
650    ///
651    /// let tensor = DefaultScalars::from_vec_col_major(vec![1], vec![1_i64])?;
652    /// assert_eq!(DefaultScalarsRef::Tensor(&tensor).shape(), &[1]);
653    /// # Ok::<(), tenferro_tensor::Error>(())
654    /// ```
655    pub fn shape(&self) -> &[usize] {
656        match self {
657            Self::Tensor(tensor) => tensor.shape(),
658            Self::View(view) => view.shape(),
659        }
660    }
661
662    /// Return the referenced rank.
663    ///
664    /// # Examples
665    ///
666    /// ```rust
667    /// use tenferro_tensor::{DefaultScalars, DefaultScalarsRef};
668    ///
669    /// let tensor = DefaultScalars::from_vec_col_major(vec![1, 1], vec![1_i64])?;
670    /// assert_eq!(DefaultScalarsRef::Tensor(&tensor).rank(), 2);
671    /// # Ok::<(), tenferro_tensor::Error>(())
672    /// ```
673    pub fn rank(&self) -> usize {
674        self.shape().len()
675    }
676
677    /// Return whether the referenced tensor/view is empty.
678    ///
679    /// # Examples
680    ///
681    /// ```rust
682    /// use tenferro_tensor::{DefaultScalars, DefaultScalarsRef};
683    ///
684    /// let tensor = DefaultScalars::from_vec_col_major(vec![0], Vec::<f64>::new())?;
685    /// assert!(DefaultScalarsRef::Tensor(&tensor).is_empty());
686    /// # Ok::<(), tenferro_tensor::Error>(())
687    /// ```
688    pub fn is_empty(&self) -> bool {
689        match self {
690            Self::Tensor(tensor) => tensor.is_empty(),
691            Self::View(view) => view.shape().contains(&0),
692        }
693    }
694}