Skip to main content

tenferro_tensor/
types.rs

1use num_complex::{Complex, Complex32, Complex64};
2use num_traits::{One, Zero};
3use std::any::Any;
4use std::fmt::Debug;
5use std::ops::Deref;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8
9use crate::config::SliceConfig;
10pub use tenferro_tensor_core::{DynRank, Rank, TensorLayout, TensorRank};
11use tenferro_tensor_core::{ShapeVec, StrideVec};
12use tenferro_tensor_core::{SliceSpec as CoreSliceSpec, ValidationError};
13
14mod accessors;
15mod shape_packing;
16mod strided_view;
17#[cfg(test)]
18mod tests;
19
20pub use strided_view::StridedSliceSpec;
21
22fn shape_vec(shape: &[usize]) -> ShapeVec {
23    shape.iter().copied().collect()
24}
25
26fn stride_vec(strides: &[isize]) -> StrideVec {
27    strides.iter().copied().collect()
28}
29
30/// Memory location for tensor storage.
31///
32/// # Examples
33///
34/// ```rust
35/// use tenferro_tensor::MemoryKind;
36///
37/// let kind = MemoryKind::UnpinnedHost;
38/// ```
39#[derive(Clone, Debug, PartialEq, Eq, Hash)]
40pub enum MemoryKind {
41    Device,
42    PinnedHost,
43    UnpinnedHost,
44    Managed,
45    Other(String),
46}
47
48/// Compute device family.
49///
50/// # Examples
51///
52/// ```rust
53/// use tenferro_tensor::DeviceKind;
54///
55/// let kind = DeviceKind::Cpu;
56/// ```
57#[derive(Clone, Debug, PartialEq, Eq, Hash)]
58pub enum DeviceKind {
59    Cpu,
60    Gpu(GpuBackendKind),
61    Other(String),
62}
63
64/// GPU backend family used by placement metadata.
65///
66/// # Examples
67///
68/// ```rust
69/// use tenferro_tensor::GpuBackendKind;
70///
71/// let kind = GpuBackendKind::Cuda;
72/// let webgpu = GpuBackendKind::WebGpu;
73/// assert_ne!(kind, webgpu);
74/// ```
75#[derive(Clone, Debug, PartialEq, Eq, Hash)]
76pub enum GpuBackendKind {
77    Cuda,
78    WebGpu,
79    Rocm,
80    Other(String),
81}
82
83/// Concrete compute device identifier.
84///
85/// # Examples
86///
87/// ```rust
88/// use tenferro_tensor::{DeviceId, DeviceKind, GpuBackendKind};
89///
90/// let device = DeviceId {
91///     kind: DeviceKind::Gpu(GpuBackendKind::Cuda),
92///     ordinal: 0,
93/// };
94/// ```
95#[derive(Clone, Debug, PartialEq, Eq, Hash)]
96pub struct DeviceId {
97    pub kind: DeviceKind,
98    pub ordinal: usize,
99}
100
101/// Caller-stable identity for a CPU execution domain.
102///
103/// Domain IDs are metadata supplied by the caller or execution coordinator;
104/// creating an ID does not allocate a process-global identity.
105///
106/// # Examples
107///
108/// ```rust
109/// use tenferro_tensor::CpuDomainId;
110///
111/// let domain = CpuDomainId::new(17);
112/// assert_eq!(domain.as_u64(), 17);
113/// ```
114#[repr(transparent)]
115#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
116pub struct CpuDomainId(u64);
117
118impl CpuDomainId {
119    /// Create a caller-stable CPU domain identity.
120    ///
121    /// # Examples
122    ///
123    /// ```rust
124    /// use tenferro_tensor::CpuDomainId;
125    ///
126    /// assert_eq!(CpuDomainId::new(3), CpuDomainId::new(3));
127    /// ```
128    pub const fn new(id: u64) -> Self {
129        Self(id)
130    }
131
132    /// Return the caller-supplied integer identity.
133    ///
134    /// # Examples
135    ///
136    /// ```rust
137    /// use tenferro_tensor::CpuDomainId;
138    ///
139    /// assert_eq!(CpuDomainId::new(9).as_u64(), 9);
140    /// ```
141    pub const fn as_u64(self) -> u64 {
142        self.0
143    }
144}
145
146/// Placement metadata for a tensor buffer.
147///
148/// # Examples
149///
150/// ```rust
151/// use tenferro_tensor::{DeviceId, DeviceKind, GpuBackendKind, MemoryKind, Placement};
152///
153/// let placement = Placement {
154///     memory_kind: MemoryKind::Device,
155///     device: Some(DeviceId {
156///         kind: DeviceKind::Gpu(GpuBackendKind::Cuda),
157///         ordinal: 0,
158///     }),
159///     cpu_affinity: None,
160/// };
161/// assert!(placement.cpu_affinity.is_none());
162/// ```
163#[derive(Clone, Debug, PartialEq, Eq, Hash)]
164pub struct Placement {
165    /// Storage memory class, independent of execution routing metadata.
166    pub memory_kind: MemoryKind,
167    /// Device that owns or addresses the storage, when applicable.
168    pub device: Option<DeviceId>,
169    /// Preferred or producing CPU execution domain for routing and locality.
170    ///
171    /// This tag is not proof of allocation ownership, page residency, NUMA
172    /// pinning, or worker-affinity enforcement. Backend allocation-domain
173    /// metadata remains attached to the buffer independently.
174    pub cpu_affinity: Option<CpuDomainId>,
175}
176
177impl Default for Placement {
178    fn default() -> Self {
179        default_placement()
180    }
181}
182
183/// Backend-owned buffer handle.
184///
185/// `BufferHandle::new` creates an empty opaque handle. Use
186/// [`BufferHandle::new_with_len`] when test or adapter code needs to model a
187/// non-empty backend allocation.
188///
189/// # Examples
190///
191/// ```rust
192/// use tenferro_tensor::BufferHandle;
193///
194/// let handle = BufferHandle::<f64>::new(7);
195/// ```
196#[derive(Clone)]
197pub struct BufferHandle<T> {
198    id: u64,
199    len: usize,
200    _phantom: std::marker::PhantomData<T>,
201}
202
203/// Identity of a backend-owned allocation domain.
204///
205/// Domains let cooperating backends accept shared allocations without treating
206/// another context's physically similar buffer as compatible.
207///
208/// # Examples
209///
210/// ```rust
211/// use tenferro_tensor::AllocationDomainId;
212///
213/// assert_ne!(AllocationDomainId::fresh(), AllocationDomainId::fresh());
214/// ```
215#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
216pub struct AllocationDomainId(u64);
217
218impl AllocationDomainId {
219    /// Create a process-unique allocation-domain identity.
220    ///
221    /// # Examples
222    ///
223    /// ```rust
224    /// use tenferro_tensor::AllocationDomainId;
225    ///
226    /// let domain = AllocationDomainId::fresh();
227    /// assert_eq!(domain, domain);
228    /// ```
229    pub fn fresh() -> Self {
230        static NEXT_DOMAIN_ID: AtomicU64 = AtomicU64::new(1);
231        Self(NEXT_DOMAIN_ID.fetch_add(1, Ordering::Relaxed))
232    }
233}
234
235/// Stable physical identity of one backend allocation.
236///
237/// # Examples
238///
239/// ```rust
240/// use tenferro_tensor::AllocationId;
241///
242/// assert_eq!(AllocationId::from_backend_id(7), AllocationId::from_backend_id(7));
243/// ```
244#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
245pub struct AllocationId(u64);
246
247impl AllocationId {
248    /// Wrap an allocation identity supplied by the owning backend.
249    ///
250    /// # Examples
251    ///
252    /// ```rust
253    /// use tenferro_tensor::AllocationId;
254    ///
255    /// let id = AllocationId::from_backend_id(3);
256    /// assert_eq!(id, AllocationId::from_backend_id(3));
257    /// ```
258    pub const fn from_backend_id(id: u64) -> Self {
259        Self(id)
260    }
261}
262
263/// Typed failure returned by guarded backend host access.
264///
265/// # Examples
266///
267/// ```rust
268/// use tenferro_tensor::HostAccessError;
269///
270/// let error = HostAccessError::Unsupported { backend: "opaque" };
271/// assert!(error.to_string().contains("opaque"));
272/// ```
273#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
274#[non_exhaustive]
275pub enum HostAccessError {
276    /// The backend does not expose guarded host access for this allocation.
277    #[error("backend `{backend}` does not support guarded host access")]
278    Unsupported { backend: &'static str },
279    /// The allocation belongs to another shared-allocation domain.
280    #[error("allocation belongs to domain {actual:?}, expected {expected:?}")]
281    ForeignDomain {
282        expected: AllocationDomainId,
283        actual: AllocationDomainId,
284    },
285    /// Another host mapping overlaps this allocation.
286    #[error("the allocation already has an active host mapping")]
287    OverlappingHostMapping,
288    /// GPU work currently owns or has reserved the allocation.
289    #[error("GPU access is in progress for the allocation")]
290    GpuAccessInProgress,
291    /// Host mapping is active while GPU access was requested.
292    #[error("the allocation is mapped for host access")]
293    MappedForHost,
294    /// The backend failed to complete the map operation.
295    #[error("backend host mapping failed: {message}")]
296    BackendFailure { message: String },
297    /// The source did not cover the full write-only mapping.
298    #[error("host write length mismatch: expected {expected}, got {actual}")]
299    LengthMismatch { expected: usize, actual: usize },
300}
301
302trait ReadGuardAccess<T> {
303    fn as_slice(&self) -> &[T];
304}
305
306impl<T, G> ReadGuardAccess<T> for G
307where
308    G: Deref,
309    G::Target: AsRef<[T]>,
310{
311    fn as_slice(&self) -> &[T] {
312        self.deref().as_ref()
313    }
314}
315
316/// Closure-scoped read mapping of a backend allocation.
317///
318/// # Examples
319///
320/// ```rust
321/// use tenferro_tensor::HostReadGuard;
322///
323/// let guard = HostReadGuard::new(vec![1_u32, 2]);
324/// assert_eq!(&*guard, &[1, 2]);
325/// ```
326pub struct HostReadGuard<'a, T> {
327    access: Box<dyn ReadGuardAccess<T> + 'a>,
328}
329
330impl<T> Debug for HostReadGuard<'_, T> {
331    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332        formatter
333            .debug_struct("HostReadGuard")
334            .field("len", &self.len())
335            .finish_non_exhaustive()
336    }
337}
338
339impl<'a, T> HostReadGuard<'a, T> {
340    /// Wrap a backend-native read guard without exposing its concrete type.
341    ///
342    /// # Examples
343    ///
344    /// ```rust
345    /// use tenferro_tensor::HostReadGuard;
346    ///
347    /// let guard = HostReadGuard::new(vec![3_i32]);
348    /// assert_eq!(guard[0], 3);
349    /// ```
350    pub fn new<G>(guard: G) -> Self
351    where
352        G: Deref + 'a,
353        G::Target: AsRef<[T]>,
354        T: 'a,
355    {
356        Self {
357            access: Box::new(guard),
358        }
359    }
360}
361
362impl<T> Deref for HostReadGuard<'_, T> {
363    type Target = [T];
364
365    fn deref(&self) -> &Self::Target {
366        self.access.as_slice()
367    }
368}
369
370/// Backend-neutral owner of one shared tensor allocation domain.
371///
372/// CPU operation crates use this object-safe boundary to allocate results in
373/// the same managed domain without depending on a GPU provider crate.
374///
375/// # Examples
376///
377/// ```rust
378/// use std::sync::Arc;
379/// use tenferro_tensor::SharedTensorAllocationDomain;
380///
381/// let _domain: Option<Arc<dyn SharedTensorAllocationDomain>> = None;
382/// ```
383pub trait SharedTensorAllocationDomain: Debug + Send + Sync + 'static {
384    /// Return the stable identity shared by every allocation from this owner.
385    fn id(&self) -> AllocationDomainId;
386
387    /// Allocate an uninitialized compact column-major tensor in this domain.
388    ///
389    /// # Errors
390    ///
391    /// Returns a typed validation, unsupported-dtype, or backend allocation error.
392    fn allocate(&self, dtype: DType, shape: &[usize]) -> crate::Result<Tensor>;
393}
394
395type HostWriteCopy<'a, T> = dyn FnMut(&[T]) -> Result<(), HostAccessError> + 'a;
396
397/// Closure-scoped write-only mapping of a backend allocation.
398///
399/// # Examples
400///
401/// ```rust
402/// use tenferro_tensor::{HostAccessError, HostWriteGuard};
403///
404/// let mut written = Vec::new();
405/// {
406///     let mut guard = HostWriteGuard::new(2, |source: &[u32]| {
407///         written.extend_from_slice(source);
408///         Ok::<(), HostAccessError>(())
409///     });
410///     guard.copy_from_slice(&[4, 5]).unwrap();
411/// }
412/// assert_eq!(written, [4, 5]);
413/// ```
414pub struct HostWriteGuard<'a, T> {
415    len: usize,
416    copy: Box<HostWriteCopy<'a, T>>,
417}
418
419impl<T> Debug for HostWriteGuard<'_, T> {
420    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
421        formatter
422            .debug_struct("HostWriteGuard")
423            .field("len", &self.len)
424            .finish_non_exhaustive()
425    }
426}
427
428impl<'a, T> HostWriteGuard<'a, T> {
429    /// Wrap a backend-native write guard without exposing its concrete type.
430    ///
431    /// # Examples
432    ///
433    /// ```rust
434    /// use tenferro_tensor::{HostAccessError, HostWriteGuard};
435    ///
436    /// let guard = HostWriteGuard::new(0, |_source: &[f32]| Ok::<(), HostAccessError>(()));
437    /// assert!(guard.is_empty());
438    /// ```
439    ///
440    /// # Errors
441    ///
442    /// Construction is infallible. A callback failure such as
443    /// [`HostAccessError::BackendFailure`] is returned later by
444    /// [`Self::copy_from_slice`].
445    pub fn new<F>(len: usize, copy: F) -> Self
446    where
447        F: FnMut(&[T]) -> Result<(), HostAccessError> + 'a,
448        T: 'a,
449    {
450        Self {
451            len,
452            copy: Box::new(copy),
453        }
454    }
455
456    /// Number of elements covered by this write-only mapping.
457    ///
458    /// # Examples
459    ///
460    /// ```rust
461    /// use tenferro_tensor::{HostAccessError, HostWriteGuard};
462    ///
463    /// let guard = HostWriteGuard::new(2, |_source: &[f32]| Ok::<(), HostAccessError>(()));
464    /// assert_eq!(guard.len(), 2);
465    /// ```
466    pub fn len(&self) -> usize {
467        self.len
468    }
469
470    /// Returns `true` when this mapping covers no elements.
471    ///
472    /// # Examples
473    ///
474    /// ```rust
475    /// use tenferro_tensor::{HostAccessError, HostWriteGuard};
476    ///
477    /// let guard = HostWriteGuard::new(0, |_source: &[f32]| Ok::<(), HostAccessError>(()));
478    /// assert!(guard.is_empty());
479    /// ```
480    pub fn is_empty(&self) -> bool {
481        self.len == 0
482    }
483
484    /// Replace the full mapped allocation contents.
485    ///
486    /// # Examples
487    ///
488    /// ```rust
489    /// use tenferro_tensor::{HostAccessError, HostWriteGuard};
490    ///
491    /// let mut guard = HostWriteGuard::new(1, |_source: &[f32]| Ok::<(), HostAccessError>(()));
492    /// guard.copy_from_slice(&[1.0]).unwrap();
493    /// ```
494    ///
495    /// # Errors
496    ///
497    /// Returns [`HostAccessError::LengthMismatch`] when `source` does not cover
498    /// the complete mapping, or the typed backend error returned by the owning
499    /// write guard.
500    pub fn copy_from_slice(&mut self, source: &[T]) -> Result<(), HostAccessError> {
501        if source.len() != self.len {
502            return Err(HostAccessError::LengthMismatch {
503                expected: self.len,
504                actual: source.len(),
505            });
506        }
507        (self.copy)(source)
508    }
509}
510
511impl<T> Debug for BufferHandle<T> {
512    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513        f.debug_struct("BufferHandle")
514            .field("id", &self.id)
515            .finish()
516    }
517}
518
519impl<T> BufferHandle<T> {
520    /// Create a new backend buffer handle.
521    ///
522    /// # Examples
523    ///
524    /// ```rust
525    /// use tenferro_tensor::BufferHandle;
526    ///
527    /// let handle = BufferHandle::<f64>::new(1);
528    /// assert_eq!(tenferro_tensor::BackendBuffer::len(&handle), 0);
529    /// ```
530    pub fn new(id: u64) -> Self {
531        Self::new_with_len(id, 0)
532    }
533
534    /// Create a new backend buffer handle with a logical element count.
535    ///
536    /// # Examples
537    ///
538    /// ```rust
539    /// use tenferro_tensor::{BackendBuffer, BufferHandle};
540    ///
541    /// let handle = BufferHandle::<f64>::new_with_len(1, 4);
542    /// assert_eq!(BackendBuffer::len(&handle), 4);
543    /// ```
544    pub fn new_with_len(id: u64, len: usize) -> Self {
545        Self {
546            id,
547            len,
548            _phantom: std::marker::PhantomData,
549        }
550    }
551}
552
553/// Opaque backend-owned tensor buffer.
554///
555/// Tensor core never inspects backend-native allocations directly. Backend
556/// crates store their own concrete handle types behind this trait and
557/// downcast inside the owning backend only.
558///
559/// # Examples
560///
561/// ```rust
562/// use std::sync::Arc;
563/// use tenferro_tensor::{BackendBuffer, BufferHandle};
564///
565/// let buffer: Arc<dyn BackendBuffer<f64>> = Arc::new(BufferHandle::<f64>::new_with_len(7, 2));
566/// assert_eq!(buffer.backend_family(), "opaque");
567/// assert_eq!(buffer.len(), 2);
568/// ```
569pub trait BackendBuffer<T>: Debug + Send + Sync + 'static {
570    /// Stable backend family identifier.
571    fn backend_family(&self) -> &'static str;
572
573    /// Number of logical elements in the backend allocation.
574    fn len(&self) -> usize;
575
576    /// Returns `true` when the backend allocation is empty.
577    fn is_empty(&self) -> bool {
578        self.len() == 0
579    }
580
581    /// Return the shared-allocation domain, when this buffer belongs to one.
582    fn allocation_domain(&self) -> Option<AllocationDomainId> {
583        None
584    }
585
586    /// Return the stable physical allocation identity, when available.
587    fn allocation_id(&self) -> Option<AllocationId> {
588        None
589    }
590
591    /// Map the allocation for closure-scoped host reads.
592    ///
593    /// # Errors
594    ///
595    /// The default returns [`HostAccessError::Unsupported`]. Host-visible
596    /// backends return typed overlap, pending-GPU, or backend mapping failures.
597    fn map_read(&self) -> Result<HostReadGuard<'_, T>, HostAccessError> {
598        Err(HostAccessError::Unsupported {
599            backend: self.backend_family(),
600        })
601    }
602
603    /// Map the allocation for closure-scoped host writes.
604    ///
605    /// # Errors
606    ///
607    /// The default returns [`HostAccessError::Unsupported`]. Host-visible
608    /// backends return typed overlap, pending-GPU, or backend mapping failures.
609    fn map_write(&self) -> Result<HostWriteGuard<'_, T>, HostAccessError> {
610        Err(HostAccessError::Unsupported {
611            backend: self.backend_family(),
612        })
613    }
614
615    /// Type-erased access for the backend crate that owns the concrete handle.
616    fn as_any(&self) -> &dyn Any;
617}
618
619impl<T: Send + Sync + 'static> BackendBuffer<T> for BufferHandle<T> {
620    fn backend_family(&self) -> &'static str {
621        "opaque"
622    }
623
624    fn len(&self) -> usize {
625        self.len
626    }
627
628    fn as_any(&self) -> &dyn Any {
629        self
630    }
631}
632
633/// Tensor storage.
634///
635/// # Examples
636///
637/// ```rust
638/// use tenferro_tensor::Buffer;
639///
640/// let host = Buffer::Host(vec![1.0_f64, 2.0]);
641/// ```
642#[derive(Clone, Debug)]
643pub enum Buffer<T> {
644    Host(Vec<T>),
645    Backend(Arc<dyn BackendBuffer<T>>),
646}
647
648impl<T: 'static> Buffer<T> {
649    /// Return the physical element count in this buffer.
650    ///
651    /// # Examples
652    ///
653    /// ```rust
654    /// use tenferro_tensor::Buffer;
655    ///
656    /// assert_eq!(Buffer::Host(vec![1_i32, 2]).len(), 2);
657    /// ```
658    pub fn len(&self) -> usize {
659        match self {
660            Self::Host(data) => data.len(),
661            Self::Backend(buffer) => buffer.len(),
662        }
663    }
664
665    /// Return whether this buffer has no physical elements.
666    ///
667    /// # Examples
668    ///
669    /// ```rust
670    /// use tenferro_tensor::Buffer;
671    ///
672    /// assert!(Buffer::<i32>::Host(Vec::new()).is_empty());
673    /// ```
674    pub fn is_empty(&self) -> bool {
675        self.len() == 0
676    }
677
678    /// Return whether the storage is backend-owned rather than host-owned.
679    ///
680    /// # Examples
681    ///
682    /// ```rust
683    /// use tenferro_tensor::Buffer;
684    ///
685    /// assert!(!Buffer::Host(vec![1_i32]).is_backend());
686    /// ```
687    pub fn is_backend(&self) -> bool {
688        matches!(self, Self::Backend(_))
689    }
690}
691
692/// Runtime typed tensor storage with compile-time scalar type and rank metadata.
693///
694/// Owned tensors are compact column-major. Arbitrary strides and metadata-only
695/// layout changes are represented by [`TypedTensorView`] and
696/// [`TypedTensorViewMut`]. The buffer may be host-backed or backend-backed;
697/// host-inspection methods do not download backend buffers implicitly.
698///
699/// # Examples
700///
701/// ```
702/// use tenferro_tensor::{Rank, Tensor, TypedTensor};
703///
704/// let t = TypedTensor::<f64>::from_vec_col_major(vec![2, 2], vec![1.0, 2.0, 3.0, 4.0]).unwrap();
705/// assert_eq!(t.shape(), &[2, 2]);
706///
707/// let static_rank = TypedTensor::<f64, Rank<2>>::from_vec_col_major([2, 2], vec![1.0; 4]).unwrap();
708/// assert_eq!(static_rank.rank(), 2);
709///
710/// let dynamic = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64; 4]).unwrap();
711/// assert_eq!(dynamic.shape(), &[2, 2]);
712/// ```
713///
714/// The `R` parameter stores rank metadata. It defaults to dynamic rank
715/// (`DynRank`); use [`Rank<N>`](Rank) for compile-time rank validation.
716/// The dtype-erased [`Tensor`] enum remains dynamic-rank.
717#[derive(Clone, Debug)]
718pub struct TypedTensor<T, R: TensorRank = DynRank> {
719    buffer: Buffer<T>,
720    layout: TensorLayout<R>,
721    placement: Placement,
722}
723
724/// Borrowed tensor buffer reference used by read-only typed views.
725///
726/// # Examples
727///
728/// ```rust
729/// use tenferro_tensor::TensorBufferRef;
730///
731/// let data = [1_i32, 2];
732/// let buffer = TensorBufferRef::Host(&data);
733/// assert_eq!(buffer.len(), 2);
734/// ```
735#[derive(Debug)]
736pub enum TensorBufferRef<'a, T> {
737    Host(&'a [T]),
738    Backend(Arc<dyn BackendBuffer<T>>),
739}
740
741impl<T> Clone for TensorBufferRef<'_, T> {
742    fn clone(&self) -> Self {
743        match self {
744            Self::Host(data) => Self::Host(data),
745            Self::Backend(buffer) => Self::Backend(Arc::clone(buffer)),
746        }
747    }
748}
749
750impl<T: 'static> TensorBufferRef<'_, T> {
751    /// Return the logical length of the backing allocation.
752    ///
753    /// # Examples
754    ///
755    /// ```rust
756    /// use tenferro_tensor::TensorBufferRef;
757    ///
758    /// let data = [1_i32, 2, 3];
759    /// assert_eq!(TensorBufferRef::Host(&data).len(), 3);
760    /// ```
761    pub fn len(&self) -> usize {
762        match self {
763            Self::Host(data) => data.len(),
764            Self::Backend(buffer) => buffer.len(),
765        }
766    }
767
768    /// Return whether the backing allocation is empty.
769    ///
770    /// # Examples
771    ///
772    /// ```rust
773    /// use tenferro_tensor::TensorBufferRef;
774    ///
775    /// let data: [f64; 0] = [];
776    /// assert!(TensorBufferRef::Host(&data).is_empty());
777    /// ```
778    pub fn is_empty(&self) -> bool {
779        self.len() == 0
780    }
781}
782
783/// Borrowed tensor buffer reference used by mutable typed views.
784///
785/// Backend buffers can be represented for residency metadata, but this crate
786/// does not expose host mutation for backend-native allocations.
787///
788/// # Examples
789///
790/// ```rust
791/// use tenferro_tensor::TensorBufferRefMut;
792///
793/// let mut data = [1_i32, 2];
794/// let buffer = TensorBufferRefMut::Host(&mut data);
795/// assert_eq!(buffer.len(), 2);
796/// ```
797#[derive(Debug)]
798pub enum TensorBufferRefMut<'a, T> {
799    Host(&'a mut [T]),
800    Backend(Arc<dyn BackendBuffer<T>>),
801}
802
803impl<T: 'static> TensorBufferRefMut<'_, T> {
804    /// Return the logical length of the backing allocation.
805    ///
806    /// # Examples
807    ///
808    /// ```rust
809    /// use tenferro_tensor::TensorBufferRefMut;
810    ///
811    /// let mut data = [1_i32, 2, 3];
812    /// assert_eq!(TensorBufferRefMut::Host(&mut data).len(), 3);
813    /// ```
814    pub fn len(&self) -> usize {
815        match self {
816            Self::Host(data) => data.len(),
817            Self::Backend(buffer) => buffer.len(),
818        }
819    }
820
821    /// Return whether the backing allocation is empty.
822    ///
823    /// # Examples
824    ///
825    /// ```rust
826    /// use tenferro_tensor::TensorBufferRefMut;
827    ///
828    /// let mut data: [f64; 0] = [];
829    /// assert!(TensorBufferRefMut::Host(&mut data).is_empty());
830    /// ```
831    pub fn is_empty(&self) -> bool {
832        self.len() == 0
833    }
834}
835
836/// Read-only borrowed view of typed tensor storage with arbitrary strides.
837///
838/// `TypedTensorView` is the typed representation for layout-only tensor
839/// transformations. It borrows an existing host or backend allocation and
840/// carries a logical shape, strides, and an offset. Slicing, reshaping when
841/// stride-compatible, and [`transpose_view`](TypedTensorView::transpose_view)
842/// update only metadata and do not copy storage.
843///
844/// Materialize through [`TensorStructural::to_contiguous_read`](crate::TensorStructural::to_contiguous_read)
845/// on the active backend session when a compact owned [`TypedTensor`] is
846/// required. Use [`TypedTensorView::as_slice`] only when the current view is
847/// contiguous in the requested layout.
848///
849/// # Examples
850///
851/// ```rust
852/// use tenferro_tensor::{Rank, TypedTensorView};
853///
854/// let data = [1_i32, 2, 3, 4];
855/// let view = TypedTensorView::<_, Rank<2>>::from_slice_ranked([2, 2], [1, 2], 0, &data)?;
856/// assert_eq!(view.get(&[1, 1]), Some(&4));
857/// # Ok::<(), tenferro_tensor::Error>(())
858/// ```
859#[derive(Clone, Debug)]
860pub struct TypedTensorView<'a, T, R: TensorRank = DynRank> {
861    buffer: TensorBufferRef<'a, T>,
862    layout: TensorLayout<R>,
863    placement: Placement,
864}
865
866impl<'a, T: 'static> TypedTensorView<'a, T, DynRank> {
867    /// Create a borrowed dynamic-rank view over compact column-major host data.
868    ///
869    /// # Examples
870    ///
871    /// ```rust
872    /// use tenferro_tensor::TypedTensorView;
873    ///
874    /// let data = [1_i32, 2, 3, 4];
875    /// let view = TypedTensorView::from_col_major(&[2, 2], &data)?;
876    /// assert_eq!(view.strides(), &[1, 2]);
877    /// # Ok::<(), tenferro_tensor::Error>(())
878    /// ```
879    ///
880    /// # Errors
881    ///
882    /// Returns [`crate::Error::Validation`] with
883    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when compact
884    /// strides or reachable bounds overflow, or
885    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
886    /// requested shape reaches beyond `data`.
887    pub fn from_col_major(shape: &[usize], data: &'a [T]) -> crate::Result<Self> {
888        let layout = TensorLayout::<DynRank>::compact(shape_vec(shape))
889            .map_err(|err| tensor_layout_error("TypedTensorView::from_col_major", err))?;
890        Self::from_buffer_ref(
891            shape_vec(layout.shape()),
892            stride_vec(layout.strides()),
893            layout.offset(),
894            TensorBufferRef::Host(data),
895            default_placement(),
896            "TypedTensorView::from_col_major",
897        )
898    }
899
900    /// Create a borrowed host view from explicit layout metadata.
901    ///
902    /// # Examples
903    ///
904    /// ```rust
905    /// use tenferro_tensor::TypedTensorView;
906    ///
907    /// let data = [1_i32, 2, 3];
908    /// let view = TypedTensorView::from_slice(vec![3], vec![-1], 2, &data)?;
909    /// assert_eq!(view.get(&[2]), Some(&1));
910    /// # Ok::<(), tenferro_tensor::Error>(())
911    /// ```
912    ///
913    /// # Errors
914    ///
915    /// Returns [`crate::Error::Validation`] with
916    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `shape` and
917    /// `strides` have different ranks,
918    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
919    /// reachable layout exceeds `data`, or
920    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when layout
921    /// arithmetic overflows.
922    pub fn from_slice(
923        shape: impl AsRef<[usize]>,
924        strides: impl AsRef<[isize]>,
925        offset: isize,
926        data: &'a [T],
927    ) -> crate::Result<Self> {
928        Self::from_buffer_ref(
929            shape_vec(shape.as_ref()),
930            stride_vec(strides.as_ref()),
931            offset,
932            TensorBufferRef::Host(data),
933            default_placement(),
934            "TypedTensorView::from_slice",
935        )
936    }
937}
938
939impl<'a, T: 'static, R: TensorRank> TypedTensorView<'a, T, R> {
940    /// Create a rank-generic borrowed host view from explicit layout metadata.
941    ///
942    /// # Examples
943    ///
944    /// ```rust
945    /// use tenferro_tensor::{Rank, TypedTensorView};
946    ///
947    /// let data = [1_i32, 2, 3, 4];
948    /// let view = TypedTensorView::<_, Rank<2>>::from_slice_ranked([2, 2], [1, 2], 0, &data)?;
949    /// assert_eq!(view.get(&[1, 1]), Some(&4));
950    /// # Ok::<(), tenferro_tensor::Error>(())
951    /// ```
952    ///
953    /// # Errors
954    ///
955    /// Returns [`crate::Error::Validation`] with
956    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when the typed
957    /// rank does not match `shape` or `strides`,
958    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
959    /// reachable layout exceeds `data`, or
960    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when layout
961    /// arithmetic overflows.
962    pub fn from_slice_ranked(
963        shape: impl Into<R::Shape>,
964        strides: impl Into<R::Strides>,
965        offset: isize,
966        data: &'a [T],
967    ) -> crate::Result<Self> {
968        Self::from_buffer_ref(
969            shape,
970            strides,
971            offset,
972            TensorBufferRef::Host(data),
973            default_placement(),
974            "TypedTensorView::from_slice_ranked",
975        )
976    }
977
978    fn from_buffer_ref(
979        shape: impl Into<R::Shape>,
980        strides: impl Into<R::Strides>,
981        offset: isize,
982        buffer: TensorBufferRef<'a, T>,
983        placement: Placement,
984        op: &'static str,
985    ) -> crate::Result<Self> {
986        let layout = TensorLayout::from_parts(shape.into(), strides.into(), offset, buffer.len())
987            .map_err(|err| tensor_layout_error(op, err))?;
988        Ok(Self {
989            buffer,
990            layout,
991            placement,
992        })
993    }
994
995    /// Return the logical shape.
996    ///
997    /// # Examples
998    ///
999    /// ```rust
1000    /// use tenferro_tensor::TypedTensorView;
1001    ///
1002    /// let data = [0_i32; 2];
1003    /// let view = TypedTensorView::from_slice(vec![2], vec![1], 0, &data)?;
1004    /// assert_eq!(view.shape(), &[2]);
1005    /// # Ok::<(), tenferro_tensor::Error>(())
1006    /// ```
1007    pub fn shape(&self) -> &[usize] {
1008        self.layout.shape()
1009    }
1010
1011    /// Return strides in element units.
1012    ///
1013    /// # Examples
1014    ///
1015    /// ```rust
1016    /// use tenferro_tensor::TypedTensorView;
1017    ///
1018    /// let data = [0_i32; 2];
1019    /// let view = TypedTensorView::from_slice(vec![2], vec![-1], 1, &data)?;
1020    /// assert_eq!(view.strides(), &[-1]);
1021    /// # Ok::<(), tenferro_tensor::Error>(())
1022    /// ```
1023    pub fn strides(&self) -> &[isize] {
1024        self.layout.strides()
1025    }
1026
1027    /// Return the physical element offset.
1028    ///
1029    /// # Examples
1030    ///
1031    /// ```rust
1032    /// use tenferro_tensor::TypedTensorView;
1033    ///
1034    /// let data = [1_i32, 2];
1035    /// let view = TypedTensorView::from_slice(vec![1], vec![1], 1, &data)?;
1036    /// assert_eq!(view.offset(), 1);
1037    /// # Ok::<(), tenferro_tensor::Error>(())
1038    /// ```
1039    pub fn offset(&self) -> isize {
1040        self.layout.offset()
1041    }
1042
1043    /// Return the borrowed host storage backing this view.
1044    ///
1045    /// This exposes the entire backing host allocation, not just the logical
1046    /// slice covered by this view. Use [`TypedTensorView::as_slice`] when the
1047    /// caller needs the contiguous logical region instead.
1048    ///
1049    /// # Examples
1050    ///
1051    /// ```rust
1052    /// use tenferro_tensor::TypedTensorView;
1053    ///
1054    /// let data = [1_i32, 2];
1055    /// let view = TypedTensorView::from_slice(vec![2], vec![1], 0, &data)?;
1056    /// assert_eq!(view.host_storage()?, &[1, 2]);
1057    /// # Ok::<(), tenferro_tensor::Error>(())
1058    /// ```
1059    ///
1060    /// # Errors
1061    ///
1062    /// Returns [`crate::Error::RuntimeState`] when this view wraps a backend
1063    /// buffer; backend storage must be downloaded before host inspection.
1064    pub fn host_storage(&self) -> crate::Result<&'a [T]> {
1065        match &self.buffer {
1066            TensorBufferRef::Host(data) => Ok(data),
1067            TensorBufferRef::Backend(_) => Err(crate::Error::runtime_state(
1068                "TypedTensorView::host_storage",
1069                "backend buffers cannot expose host storage; download explicitly first",
1070            )),
1071        }
1072    }
1073
1074    /// Return the number of logical elements in this view.
1075    ///
1076    /// # Examples
1077    ///
1078    /// ```rust
1079    /// use tenferro_tensor::TypedTensorView;
1080    ///
1081    /// let data = [0_i32; 6];
1082    /// let view = TypedTensorView::from_slice(vec![2, 3], vec![1, 2], 0, &data)?;
1083    /// assert_eq!(view.n_elements(), 6);
1084    /// # Ok::<(), tenferro_tensor::Error>(())
1085    /// ```
1086    pub fn n_elements(&self) -> usize {
1087        // Invariant: public view constructors validate logical element count.
1088        match checked_view_element_count(self.shape(), "TypedTensorView::n_elements") {
1089            Ok(n) => n,
1090            Err(err) => {
1091                unreachable!("TypedTensorView layout shape is validated at construction: {err}")
1092            }
1093        }
1094    }
1095
1096    /// Return layout metadata for this view.
1097    ///
1098    /// # Examples
1099    ///
1100    /// ```rust
1101    /// use tenferro_tensor::TypedTensorView;
1102    ///
1103    /// let data = [1_i32, 2];
1104    /// let view = TypedTensorView::from_slice(vec![2], vec![1], 0, &data)?;
1105    /// assert!(view.layout().is_compact_col_major().unwrap());
1106    /// # Ok::<(), tenferro_tensor::Error>(())
1107    /// ```
1108    pub fn layout(&self) -> &TensorLayout<R> {
1109        &self.layout
1110    }
1111
1112    /// Return placement metadata for this view.
1113    ///
1114    /// # Examples
1115    ///
1116    /// ```rust
1117    /// use tenferro_tensor::{MemoryKind, TypedTensorView};
1118    ///
1119    /// let data = [1_i32];
1120    /// let view = TypedTensorView::from_slice(vec![1], vec![1], 0, &data)?;
1121    /// assert_eq!(view.placement().memory_kind, MemoryKind::UnpinnedHost);
1122    /// # Ok::<(), tenferro_tensor::Error>(())
1123    /// ```
1124    pub fn placement(&self) -> &Placement {
1125        &self.placement
1126    }
1127
1128    /// Return the backend allocation for backend integrations.
1129    #[doc(hidden)]
1130    pub fn backend_buffer(&self) -> Option<&Arc<dyn BackendBuffer<T>>> {
1131        match &self.buffer {
1132            TensorBufferRef::Host(_) => None,
1133            TensorBufferRef::Backend(buffer) => Some(buffer),
1134        }
1135    }
1136
1137    /// Compute the physical element offset for a logical index.
1138    ///
1139    /// # Examples
1140    ///
1141    /// ```rust
1142    /// use tenferro_tensor::TypedTensorView;
1143    ///
1144    /// let data = [1_i32, 2, 3];
1145    /// let view = TypedTensorView::from_slice(vec![3], vec![-1], 2, &data)?;
1146    /// assert_eq!(view.linear_offset(&[2]), Some(0));
1147    /// # Ok::<(), tenferro_tensor::Error>(())
1148    /// ```
1149    pub fn linear_offset(&self, indices: &[usize]) -> Option<usize> {
1150        checked_view_offset(self.shape(), self.strides(), self.offset(), indices)
1151    }
1152
1153    /// Compute the physical element offset for a logical index, returning a typed error.
1154    ///
1155    /// # Examples
1156    ///
1157    /// ```rust
1158    /// use tenferro_tensor::TypedTensorView;
1159    ///
1160    /// let data = [1_i32, 2, 3];
1161    /// let view = TypedTensorView::from_slice([3], [-1], 2, &data)?;
1162    /// assert_eq!(view.layout_linear_offset(&[2])?, 0);
1163    /// # Ok::<(), tenferro_tensor::Error>(())
1164    /// ```
1165    ///
1166    /// # Errors
1167    ///
1168    /// Returns [`crate::Error::Validation`] with
1169    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
1170    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
1171    /// when an index is outside its axis extent, or
1172    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
1173    /// arithmetic overflows.
1174    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
1175        checked_view_offset_result(
1176            self.shape(),
1177            self.strides(),
1178            self.offset(),
1179            indices,
1180            "TypedTensorView::layout_linear_offset",
1181        )
1182    }
1183
1184    /// Return whether this view is compact column-major.
1185    ///
1186    /// # Examples
1187    ///
1188    /// ```rust
1189    /// use tenferro_tensor::TypedTensorView;
1190    ///
1191    /// let data = [1_i32, 2];
1192    /// let view = TypedTensorView::from_slice([2], [1], 0, &data)?;
1193    /// assert!(view.is_col_major_contiguous()?);
1194    /// # Ok::<(), tenferro_tensor::Error>(())
1195    /// ```
1196    ///
1197    /// # Errors
1198    ///
1199    /// Returns [`crate::Error::Validation`] with
1200    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] if compactness
1201    /// arithmetic overflows.
1202    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
1203        self.layout
1204            .is_compact_col_major()
1205            .map_err(|err| tensor_layout_error("TypedTensorView::is_col_major_contiguous", err))
1206    }
1207
1208    /// Return a compact string summary of this view's layout metadata.
1209    ///
1210    /// # Examples
1211    ///
1212    /// ```rust
1213    /// use tenferro_tensor::TypedTensorView;
1214    ///
1215    /// let data = [1_i32, 2];
1216    /// let view = TypedTensorView::from_slice([2], [1], 0, &data)?;
1217    /// assert!(view.layout_summary().contains("shape=[2]"));
1218    /// # Ok::<(), tenferro_tensor::Error>(())
1219    /// ```
1220    pub fn layout_summary(&self) -> String {
1221        layout_summary(self.shape(), self.strides(), self.offset())
1222    }
1223
1224    /// Assert this view is compact column-major.
1225    ///
1226    /// # Examples
1227    ///
1228    /// ```rust
1229    /// use tenferro_tensor::TypedTensorView;
1230    ///
1231    /// let data = [1_i32, 2];
1232    /// let view = TypedTensorView::from_slice([2], [1], 0, &data)?;
1233    /// view.assert_col_major_contiguous()?;
1234    /// # Ok::<(), tenferro_tensor::Error>(())
1235    /// ```
1236    ///
1237    /// # Errors
1238    ///
1239    /// Returns [`crate::Error::Validation`] with
1240    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
1241    /// compactness arithmetic overflows, or
1242    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
1243    /// view is not compact column-major.
1244    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
1245        assert_layout_col_major_contiguous(
1246            self.is_col_major_contiguous()?,
1247            self.shape(),
1248            self.strides(),
1249            self.offset(),
1250            "TypedTensorView::assert_col_major_contiguous",
1251        )
1252    }
1253
1254    /// Borrow one host element by logical index.
1255    ///
1256    /// Returns `None` for out-of-bounds indices and backend buffers.
1257    ///
1258    /// # Examples
1259    ///
1260    /// ```rust
1261    /// use tenferro_tensor::TypedTensorView;
1262    ///
1263    /// let data = [1_i32, 2];
1264    /// let view = TypedTensorView::from_slice(vec![2], vec![1], 0, &data)?;
1265    /// assert_eq!(view.get(&[1]), Some(&2));
1266    /// # Ok::<(), tenferro_tensor::Error>(())
1267    /// ```
1268    pub fn get(&self, indices: &[usize]) -> Option<&T> {
1269        let offset = self.linear_offset(indices)?;
1270        match &self.buffer {
1271            TensorBufferRef::Host(data) => data.get(offset),
1272            TensorBufferRef::Backend(_) => None,
1273        }
1274    }
1275
1276    /// Borrow the contiguous host slice covered by this view.
1277    ///
1278    /// Returns an explicit error for backend buffers and for non-contiguous
1279    /// layouts. This method never downloads or materializes backend data.
1280    ///
1281    /// # Examples
1282    ///
1283    /// ```rust
1284    /// use tenferro_tensor::TypedTensorView;
1285    ///
1286    /// let data = [1_i32, 2, 3];
1287    /// let view = TypedTensorView::from_slice(vec![2], vec![1], 1, &data)?;
1288    /// assert_eq!(view.as_slice()?, &[2, 3]);
1289    /// # Ok::<(), tenferro_tensor::Error>(())
1290    /// ```
1291    ///
1292    /// # Errors
1293    ///
1294    /// Returns [`crate::Error::RuntimeState`] when this view wraps a backend
1295    /// buffer, [`tenferro_tensor_core::ValidationError::InvalidArgument`] when
1296    /// the layout is not slice-contiguous or has a negative offset, or
1297    /// [`crate::Error::Validation`] with
1298    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] or
1299    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when the
1300    /// requested host range is invalid.
1301    pub fn as_slice(&self) -> crate::Result<&'a [T]> {
1302        let data =
1303            match &self.buffer {
1304                TensorBufferRef::Host(data) => data,
1305                TensorBufferRef::Backend(_) => return Err(crate::Error::runtime_state(
1306                    "TypedTensorView::as_slice",
1307                    "backend buffers cannot be inspected as host slices; download explicitly first",
1308                )),
1309            };
1310        contiguous_layout_slice(self.layout(), data, "TypedTensorView::as_slice")
1311    }
1312
1313    /// Return a metadata-only axis permutation.
1314    ///
1315    /// # Examples
1316    ///
1317    /// ```rust
1318    /// use tenferro_tensor::{Rank, TypedTensorView};
1319    ///
1320    /// let data = [1_i32, 2, 3, 4, 5, 6];
1321    /// let view = TypedTensorView::<_, Rank<2>>::from_slice_ranked([2, 3], [1, 2], 0, &data)?;
1322    /// let transposed = view.transpose_view([1, 0])?;
1323    /// assert_eq!(transposed.shape(), &[3, 2]);
1324    /// # Ok::<(), tenferro_tensor::Error>(())
1325    /// ```
1326    /// # Errors
1327    ///
1328    /// Returns [`crate::Error::Validation`] with
1329    /// [`tenferro_tensor_core::ValidationError::InvalidPermutationLength`],
1330    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`], or
1331    /// [`tenferro_tensor_core::ValidationError::DuplicateAxis`] when `axes` is
1332    /// not a valid permutation of the view rank.
1333    pub fn transpose_view(&self, axes: impl AsRef<[usize]>) -> crate::Result<Self> {
1334        let layout = self
1335            .layout
1336            .transpose_view(axes)
1337            .map_err(|err| tensor_layout_error("TypedTensorView::transpose_view", err))?;
1338        Ok(Self {
1339            buffer: self.buffer.clone(),
1340            layout,
1341            placement: self.placement.clone(),
1342        })
1343    }
1344
1345    /// Return a metadata-only slice using one [`StridedSliceSpec`] per axis.
1346    ///
1347    /// # Examples
1348    ///
1349    /// ```rust
1350    /// use tenferro_tensor::{StridedSliceSpec, TypedTensorView};
1351    ///
1352    /// let data = [1_i32, 2, 3];
1353    /// let view = TypedTensorView::from_slice(vec![3], vec![1], 0, &data)?;
1354    /// let reversed = view.try_slice(&[StridedSliceSpec::reverse()])?;
1355    /// assert_eq!(reversed.get(&[0]), Some(&3));
1356    /// # Ok::<(), tenferro_tensor::Error>(())
1357    /// ```
1358    /// # Errors
1359    ///
1360    /// Returns [`crate::Error::Validation`] with
1361    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when the slice
1362    /// count differs from the view rank,
1363    /// [`tenferro_tensor_core::ValidationError::InvalidSliceStep`] or
1364    /// [`tenferro_tensor_core::ValidationError::InvalidSliceBounds`] for an
1365    /// invalid slice, [`tenferro_tensor_core::ValidationError::IntegerOverflow`]
1366    /// for slice arithmetic overflow, or
1367    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
1368    /// resulting layout exceeds the backing buffer.
1369    pub fn try_slice(&self, slices: &[StridedSliceSpec]) -> crate::Result<Self> {
1370        let specs = core_slice_specs(slices, self.shape(), "TypedTensorView::try_slice")?;
1371        let layout = self
1372            .layout
1373            .slice_view(specs, self.buffer.len())
1374            .map_err(|err| tensor_layout_error("TypedTensorView::try_slice", err))?;
1375        Ok(Self {
1376            buffer: self.buffer.clone(),
1377            layout,
1378            placement: self.placement.clone(),
1379        })
1380    }
1381
1382    /// Return a metadata-only slice along one axis.
1383    ///
1384    /// # Examples
1385    ///
1386    /// ```rust
1387    /// use tenferro_tensor::{StridedSliceSpec, TypedTensorView};
1388    ///
1389    /// let data = [1_i32, 2, 3, 4];
1390    /// let view = TypedTensorView::from_slice(vec![2, 2], vec![1, 2], 0, &data)?;
1391    /// assert_eq!(view.try_slice_axis(1, StridedSliceSpec::reverse())?.get(&[0, 0]), Some(&3));
1392    /// # Ok::<(), tenferro_tensor::Error>(())
1393    /// ```
1394    /// # Errors
1395    ///
1396    /// Returns [`crate::Error::Validation`] with
1397    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`] when `axis`
1398    /// is outside the view rank, [`tenferro_tensor_core::ValidationError::InvalidSliceStep`]
1399    /// or [`tenferro_tensor_core::ValidationError::InvalidSliceBounds`] for an
1400    /// invalid slice, [`tenferro_tensor_core::ValidationError::IntegerOverflow`]
1401    /// for slice arithmetic overflow, or
1402    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
1403    /// resulting layout exceeds the backing buffer.
1404    pub fn try_slice_axis(&self, axis: usize, slice: StridedSliceSpec) -> crate::Result<Self> {
1405        let slices = slice_axis_specs(
1406            self.shape().len(),
1407            axis,
1408            slice,
1409            "TypedTensorView::try_slice_axis",
1410        )?;
1411        self.try_slice(&slices)
1412    }
1413
1414    /// Return a metadata-only dynamic-rank reshape for contiguous column-major views.
1415    ///
1416    /// # Examples
1417    ///
1418    /// ```rust
1419    /// use tenferro_tensor::TypedTensorView;
1420    ///
1421    /// let data = [1_i32, 2, 3, 4];
1422    /// let view = TypedTensorView::from_slice(vec![2, 2], vec![1, 2], 0, &data)?;
1423    /// assert_eq!(view.try_reshape(&[4])?.shape(), &[4]);
1424    /// # Ok::<(), tenferro_tensor::Error>(())
1425    /// ```
1426    /// # Errors
1427    ///
1428    /// Returns [`crate::Error::Validation`] with
1429    /// [`tenferro_tensor_core::ValidationError::NonContiguousViewAsSlice`] when
1430    /// the source is not compact column-major,
1431    /// [`tenferro_tensor_core::ValidationError::ShapeMismatch`] (whose
1432    /// [`tenferro_tensor_core::ShapeMismatch::ReshapeElementCount`] source
1433    /// records the counts) when element counts differ,
1434    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for shape
1435    /// arithmetic overflow, or
1436    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
1437    /// reshaped view exceeds the backing buffer.
1438    pub fn try_reshape(&self, shape: &[usize]) -> crate::Result<TypedTensorView<'a, T, DynRank>> {
1439        let layout = reshape_layout_dyn(
1440            &self.layout,
1441            shape,
1442            self.buffer.len(),
1443            "TypedTensorView::try_reshape",
1444        )?;
1445        Ok(TypedTensorView {
1446            buffer: self.buffer.clone(),
1447            layout,
1448            placement: self.placement.clone(),
1449        })
1450    }
1451}
1452
1453/// Mutable borrowed view of typed tensor storage with arbitrary strides.
1454///
1455/// # Examples
1456///
1457/// ```rust
1458/// use tenferro_tensor::TypedTensorViewMut;
1459///
1460/// let mut data = [1_i32, 2, 3];
1461/// let mut view = TypedTensorViewMut::from_slice(vec![3], vec![-1], 2, &mut data)?;
1462/// *view.get_mut(&[2]).unwrap() = 10;
1463/// assert_eq!(view.as_read_only().get(&[2]), Some(&10));
1464/// # Ok::<(), tenferro_tensor::Error>(())
1465/// ```
1466#[derive(Debug)]
1467pub struct TypedTensorViewMut<'a, T, R: TensorRank = DynRank> {
1468    buffer: TensorBufferRefMut<'a, T>,
1469    layout: TensorLayout<R>,
1470    placement: Placement,
1471}
1472
1473/// Pair of mutable tensor views returned by disjoint multi-slice operations.
1474///
1475/// # Examples
1476///
1477/// ```rust
1478/// use tenferro_tensor::{StridedSliceSpec, TypedTensorViewMut, TypedTensorViewMutPair};
1479///
1480/// let mut data = [1_i32, 2, 3, 4];
1481/// let mut view = TypedTensorViewMut::from_slice(vec![4], vec![1], 0, &mut data)?;
1482/// let pair: TypedTensorViewMutPair<'_, i32> = view
1483///     .try_multi_slice_mut(
1484///         &[StridedSliceSpec::new(0, Some(2), 1)],
1485///         &[StridedSliceSpec::new(2, Some(4), 1)],
1486///     )
1487///     ?
1488///     .unwrap();
1489/// assert_eq!(pair.0.shape(), &[2]);
1490/// assert_eq!(pair.1.shape(), &[2]);
1491/// # Ok::<(), tenferro_tensor::Error>(())
1492/// ```
1493pub type TypedTensorViewMutPair<'a, T, R = DynRank> =
1494    (TypedTensorViewMut<'a, T, R>, TypedTensorViewMut<'a, T, R>);
1495
1496impl<'a, T: 'static> TypedTensorViewMut<'a, T, DynRank> {
1497    /// Create a mutable dynamic-rank view over compact column-major host data.
1498    ///
1499    /// # Examples
1500    ///
1501    /// ```rust
1502    /// use tenferro_tensor::TypedTensorViewMut;
1503    ///
1504    /// let mut data = [1_i32, 2, 3, 4];
1505    /// let view = TypedTensorViewMut::from_col_major(&[2, 2], &mut data)?;
1506    /// assert_eq!(view.strides(), &[1, 2]);
1507    /// # Ok::<(), tenferro_tensor::Error>(())
1508    /// ```
1509    /// # Errors
1510    ///
1511    /// Returns [`crate::Error::Validation`] with
1512    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
1513    /// shape or offset arithmetic overflow, or
1514    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
1515    /// compact shape reaches beyond `data`.
1516    pub fn from_col_major(shape: &[usize], data: &'a mut [T]) -> crate::Result<Self> {
1517        let layout = TensorLayout::<DynRank>::compact(shape_vec(shape))
1518            .map_err(|err| tensor_layout_error("TypedTensorViewMut::from_col_major", err))?;
1519        Self::from_buffer_ref_mut(
1520            shape_vec(layout.shape()),
1521            stride_vec(layout.strides()),
1522            layout.offset(),
1523            TensorBufferRefMut::Host(data),
1524            default_placement(),
1525            "TypedTensorViewMut::from_col_major",
1526        )
1527    }
1528
1529    /// Create a mutable host view from explicit layout metadata.
1530    ///
1531    /// Layouts where distinct logical elements can alias the same physical
1532    /// element are rejected.
1533    ///
1534    /// # Examples
1535    ///
1536    /// ```rust
1537    /// use tenferro_tensor::TypedTensorViewMut;
1538    ///
1539    /// let mut data = [1_i32, 2];
1540    /// assert!(TypedTensorViewMut::from_slice(vec![2], vec![0], 0, &mut data).is_err());
1541    /// ```
1542    /// # Errors
1543    ///
1544    /// Returns [`crate::Error::Validation`] with
1545    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `shape` and
1546    /// `strides` have different ranks,
1547    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
1548    /// layout reaches beyond `data`,
1549    /// [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`] when
1550    /// logical elements alias, or
1551    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
1552    /// arithmetic overflow.
1553    pub fn from_slice(
1554        shape: impl AsRef<[usize]>,
1555        strides: impl AsRef<[isize]>,
1556        offset: isize,
1557        data: &'a mut [T],
1558    ) -> crate::Result<Self> {
1559        Self::from_buffer_ref_mut(
1560            shape_vec(shape.as_ref()),
1561            stride_vec(strides.as_ref()),
1562            offset,
1563            TensorBufferRefMut::Host(data),
1564            default_placement(),
1565            "TypedTensorViewMut::from_slice",
1566        )
1567    }
1568}
1569
1570impl<'a, T: 'static, R: TensorRank> TypedTensorViewMut<'a, T, R> {
1571    /// Create a rank-generic mutable host view from explicit layout metadata.
1572    ///
1573    /// # Examples
1574    ///
1575    /// ```rust
1576    /// use tenferro_tensor::{Rank, TypedTensorViewMut};
1577    ///
1578    /// let mut data = [1_i32, 2, 3, 4];
1579    /// let view = TypedTensorViewMut::<_, Rank<2>>::from_slice_ranked([2, 2], [1, 2], 0, &mut data)?;
1580    /// assert_eq!(view.shape(), &[2, 2]);
1581    /// # Ok::<(), tenferro_tensor::Error>(())
1582    /// ```
1583    /// # Errors
1584    ///
1585    /// Returns [`crate::Error::Validation`] with
1586    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when the typed
1587    /// rank does not match `shape` or `strides`,
1588    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
1589    /// layout reaches beyond `data`,
1590    /// [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`] when
1591    /// logical elements alias, or
1592    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
1593    /// arithmetic overflow.
1594    pub fn from_slice_ranked(
1595        shape: impl Into<R::Shape>,
1596        strides: impl Into<R::Strides>,
1597        offset: isize,
1598        data: &'a mut [T],
1599    ) -> crate::Result<Self> {
1600        Self::from_buffer_ref_mut(
1601            shape,
1602            strides,
1603            offset,
1604            TensorBufferRefMut::Host(data),
1605            default_placement(),
1606            "TypedTensorViewMut::from_slice_ranked",
1607        )
1608    }
1609
1610    fn from_buffer_ref_mut(
1611        shape: impl Into<R::Shape>,
1612        strides: impl Into<R::Strides>,
1613        offset: isize,
1614        buffer: TensorBufferRefMut<'a, T>,
1615        placement: Placement,
1616        op: &'static str,
1617    ) -> crate::Result<Self> {
1618        let layout = TensorLayout::from_parts(shape.into(), strides.into(), offset, buffer.len())
1619            .map_err(|err| tensor_layout_error(op, err))?;
1620        layout
1621            .validate_mutable_no_overlap()
1622            .map_err(|err| tensor_layout_error(op, err))?;
1623        Ok(Self {
1624            buffer,
1625            layout,
1626            placement,
1627        })
1628    }
1629
1630    /// Return the logical shape.
1631    ///
1632    /// # Examples
1633    ///
1634    /// ```rust
1635    /// use tenferro_tensor::TypedTensorViewMut;
1636    ///
1637    /// let mut data = [0_i32; 2];
1638    /// let view = TypedTensorViewMut::from_slice(vec![2], vec![1], 0, &mut data)?;
1639    /// assert_eq!(view.shape(), &[2]);
1640    /// # Ok::<(), tenferro_tensor::Error>(())
1641    /// ```
1642    pub fn shape(&self) -> &[usize] {
1643        self.layout.shape()
1644    }
1645
1646    /// Return strides in element units.
1647    ///
1648    /// # Examples
1649    ///
1650    /// ```rust
1651    /// use tenferro_tensor::TypedTensorViewMut;
1652    ///
1653    /// let mut data = [0_i32; 2];
1654    /// let view = TypedTensorViewMut::from_slice(vec![2], vec![-1], 1, &mut data)?;
1655    /// assert_eq!(view.strides(), &[-1]);
1656    /// # Ok::<(), tenferro_tensor::Error>(())
1657    /// ```
1658    pub fn strides(&self) -> &[isize] {
1659        self.layout.strides()
1660    }
1661
1662    /// Return the physical element offset.
1663    ///
1664    /// # Examples
1665    ///
1666    /// ```rust
1667    /// use tenferro_tensor::TypedTensorViewMut;
1668    ///
1669    /// let mut data = [1_i32, 2];
1670    /// let view = TypedTensorViewMut::from_slice(vec![1], vec![1], 1, &mut data)?;
1671    /// assert_eq!(view.offset(), 1);
1672    /// # Ok::<(), tenferro_tensor::Error>(())
1673    /// ```
1674    pub fn offset(&self) -> isize {
1675        self.layout.offset()
1676    }
1677
1678    /// Return the borrowed host storage backing this view.
1679    ///
1680    /// This exposes the entire backing host allocation, not just the logical
1681    /// slice covered by this view. Use [`TypedTensorViewMut::as_read_only`]
1682    /// with [`TypedTensorView::as_slice`] when the caller needs the contiguous
1683    /// logical region instead.
1684    ///
1685    /// # Examples
1686    ///
1687    /// ```rust
1688    /// use tenferro_tensor::TypedTensorViewMut;
1689    ///
1690    /// let mut data = [1_i32, 2];
1691    /// let view = TypedTensorViewMut::from_slice(vec![2], vec![1], 0, &mut data)?;
1692    /// assert_eq!(view.host_storage()?, &[1, 2]);
1693    /// # Ok::<(), tenferro_tensor::Error>(())
1694    /// ```
1695    /// # Errors
1696    ///
1697    /// Returns [`crate::Error::RuntimeState`] when this view wraps a backend
1698    /// buffer; backend storage must be downloaded before host inspection.
1699    pub fn host_storage(&self) -> crate::Result<&[T]> {
1700        match &self.buffer {
1701            TensorBufferRefMut::Host(data) => Ok(data),
1702            TensorBufferRefMut::Backend(_) => Err(crate::Error::runtime_state(
1703                "TypedTensorViewMut::host_storage",
1704                "backend buffers cannot expose host storage; download explicitly first",
1705            )),
1706        }
1707    }
1708
1709    /// Mutably borrow the host storage backing this view.
1710    ///
1711    /// This exposes the entire backing host allocation, not just the logical
1712    /// slice covered by this view. Prefer scalar element accessors when mutating
1713    /// a logical region; tensor-sized copies belong to an active backend.
1714    ///
1715    /// # Examples
1716    ///
1717    /// ```rust
1718    /// use tenferro_tensor::TypedTensorViewMut;
1719    ///
1720    /// let mut data = [1_i32, 2];
1721    /// let mut view = TypedTensorViewMut::from_slice(vec![2], vec![1], 0, &mut data)?;
1722    /// view.host_storage_mut()?[0] = 3;
1723    /// assert_eq!(view.get(&[0]), Some(&3));
1724    /// # Ok::<(), tenferro_tensor::Error>(())
1725    /// ```
1726    /// # Errors
1727    ///
1728    /// Returns [`crate::Error::RuntimeState`] when this view wraps a backend
1729    /// buffer; backend storage must be downloaded before host inspection.
1730    pub fn host_storage_mut(&mut self) -> crate::Result<&mut [T]> {
1731        match &mut self.buffer {
1732            TensorBufferRefMut::Host(data) => Ok(data),
1733            TensorBufferRefMut::Backend(_) => Err(crate::Error::runtime_state(
1734                "TypedTensorViewMut::host_storage_mut",
1735                "backend buffers cannot expose mutable host storage; download explicitly first",
1736            )),
1737        }
1738    }
1739
1740    /// Return the number of logical elements in this view.
1741    ///
1742    /// # Examples
1743    ///
1744    /// ```rust
1745    /// use tenferro_tensor::TypedTensorViewMut;
1746    ///
1747    /// let mut data = [0_i32; 6];
1748    /// let view = TypedTensorViewMut::from_slice(vec![2, 3], vec![1, 2], 0, &mut data)?;
1749    /// assert_eq!(view.n_elements(), 6);
1750    /// # Ok::<(), tenferro_tensor::Error>(())
1751    /// ```
1752    pub fn n_elements(&self) -> usize {
1753        // Invariant: public mutable view constructors validate logical element count.
1754        match checked_view_element_count(self.shape(), "TypedTensorViewMut::n_elements") {
1755            Ok(n) => n,
1756            Err(err) => {
1757                unreachable!("TypedTensorViewMut layout shape is validated at construction: {err}")
1758            }
1759        }
1760    }
1761
1762    /// Return layout metadata for this view.
1763    ///
1764    /// # Examples
1765    ///
1766    /// ```rust
1767    /// use tenferro_tensor::TypedTensorViewMut;
1768    ///
1769    /// let mut data = [1_i32, 2];
1770    /// let view = TypedTensorViewMut::from_slice(vec![2], vec![1], 0, &mut data)?;
1771    /// assert!(view.layout().is_compact_col_major().unwrap());
1772    /// # Ok::<(), tenferro_tensor::Error>(())
1773    /// ```
1774    pub fn layout(&self) -> &TensorLayout<R> {
1775        &self.layout
1776    }
1777
1778    /// Return placement metadata for this view.
1779    ///
1780    /// # Examples
1781    ///
1782    /// ```rust
1783    /// use tenferro_tensor::{MemoryKind, TypedTensorViewMut};
1784    ///
1785    /// let mut data = [1_i32];
1786    /// let view = TypedTensorViewMut::from_slice(vec![1], vec![1], 0, &mut data)?;
1787    /// assert_eq!(view.placement().memory_kind, MemoryKind::UnpinnedHost);
1788    /// # Ok::<(), tenferro_tensor::Error>(())
1789    /// ```
1790    pub fn placement(&self) -> &Placement {
1791        &self.placement
1792    }
1793
1794    /// Return the backend allocation for backend integrations.
1795    #[doc(hidden)]
1796    pub fn backend_buffer(&self) -> Option<&Arc<dyn BackendBuffer<T>>> {
1797        match &self.buffer {
1798            TensorBufferRefMut::Host(_) => None,
1799            TensorBufferRefMut::Backend(buffer) => Some(buffer),
1800        }
1801    }
1802
1803    /// Compute the physical element offset for a logical index.
1804    ///
1805    /// # Examples
1806    ///
1807    /// ```rust
1808    /// use tenferro_tensor::TypedTensorViewMut;
1809    ///
1810    /// let mut data = [1_i32, 2, 3];
1811    /// let view = TypedTensorViewMut::from_slice(vec![3], vec![-1], 2, &mut data)?;
1812    /// assert_eq!(view.linear_offset(&[2]), Some(0));
1813    /// # Ok::<(), tenferro_tensor::Error>(())
1814    /// ```
1815    pub fn linear_offset(&self, indices: &[usize]) -> Option<usize> {
1816        checked_view_offset(self.shape(), self.strides(), self.offset(), indices)
1817    }
1818
1819    /// Compute the physical element offset for a logical index, returning a typed error.
1820    ///
1821    /// # Examples
1822    ///
1823    /// ```rust
1824    /// use tenferro_tensor::TypedTensorViewMut;
1825    ///
1826    /// let mut data = [1_i32, 2, 3];
1827    /// let view = TypedTensorViewMut::from_slice([3], [-1], 2, &mut data)?;
1828    /// assert_eq!(view.layout_linear_offset(&[2])?, 0);
1829    /// # Ok::<(), tenferro_tensor::Error>(())
1830    /// ```
1831    /// # Errors
1832    ///
1833    /// Returns [`crate::Error::Validation`] with
1834    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
1835    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
1836    /// when an index is outside its axis extent, or
1837    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
1838    /// arithmetic overflows.
1839    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
1840        checked_view_offset_result(
1841            self.shape(),
1842            self.strides(),
1843            self.offset(),
1844            indices,
1845            "TypedTensorViewMut::layout_linear_offset",
1846        )
1847    }
1848
1849    /// Return whether this mutable view is compact column-major.
1850    ///
1851    /// # Examples
1852    ///
1853    /// ```rust
1854    /// use tenferro_tensor::TypedTensorViewMut;
1855    ///
1856    /// let mut data = [1_i32, 2];
1857    /// let view = TypedTensorViewMut::from_slice([2], [1], 0, &mut data)?;
1858    /// assert!(view.is_col_major_contiguous()?);
1859    /// # Ok::<(), tenferro_tensor::Error>(())
1860    /// ```
1861    /// # Errors
1862    ///
1863    /// Returns [`crate::Error::Validation`] with
1864    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
1865    /// compactness arithmetic overflows.
1866    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
1867        self.layout
1868            .is_compact_col_major()
1869            .map_err(|err| tensor_layout_error("TypedTensorViewMut::is_col_major_contiguous", err))
1870    }
1871
1872    /// Return a compact string summary of this mutable view's layout metadata.
1873    ///
1874    /// # Examples
1875    ///
1876    /// ```rust
1877    /// use tenferro_tensor::TypedTensorViewMut;
1878    ///
1879    /// let mut data = [1_i32, 2];
1880    /// let view = TypedTensorViewMut::from_slice([2], [1], 0, &mut data)?;
1881    /// assert!(view.layout_summary().contains("shape=[2]"));
1882    /// # Ok::<(), tenferro_tensor::Error>(())
1883    /// ```
1884    pub fn layout_summary(&self) -> String {
1885        layout_summary(self.shape(), self.strides(), self.offset())
1886    }
1887
1888    /// Assert this mutable view is compact column-major.
1889    ///
1890    /// # Examples
1891    ///
1892    /// ```rust
1893    /// use tenferro_tensor::TypedTensorViewMut;
1894    ///
1895    /// let mut data = [1_i32, 2];
1896    /// let view = TypedTensorViewMut::from_slice([2], [1], 0, &mut data)?;
1897    /// view.assert_col_major_contiguous()?;
1898    /// # Ok::<(), tenferro_tensor::Error>(())
1899    /// ```
1900    /// # Errors
1901    ///
1902    /// Returns [`crate::Error::Validation`] with
1903    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
1904    /// compactness arithmetic overflows, or
1905    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
1906    /// view is not compact column-major.
1907    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
1908        assert_layout_col_major_contiguous(
1909            self.is_col_major_contiguous()?,
1910            self.shape(),
1911            self.strides(),
1912            self.offset(),
1913            "TypedTensorViewMut::assert_col_major_contiguous",
1914        )
1915    }
1916
1917    /// Borrow one host element by logical index.
1918    ///
1919    /// # Examples
1920    ///
1921    /// ```rust
1922    /// use tenferro_tensor::TypedTensorViewMut;
1923    ///
1924    /// let mut data = [1_i32, 2];
1925    /// let view = TypedTensorViewMut::from_slice(vec![2], vec![1], 0, &mut data)?;
1926    /// assert_eq!(view.get(&[1]), Some(&2));
1927    /// # Ok::<(), tenferro_tensor::Error>(())
1928    /// ```
1929    pub fn get(&self, indices: &[usize]) -> Option<&T> {
1930        let offset = self.linear_offset(indices)?;
1931        match &self.buffer {
1932            TensorBufferRefMut::Host(data) => data.get(offset),
1933            TensorBufferRefMut::Backend(_) => None,
1934        }
1935    }
1936
1937    /// Mutably borrow one host element by logical index.
1938    ///
1939    /// # Examples
1940    ///
1941    /// ```rust
1942    /// use tenferro_tensor::TypedTensorViewMut;
1943    ///
1944    /// let mut data = [1_i32, 2];
1945    /// let mut view = TypedTensorViewMut::from_slice(vec![2], vec![1], 0, &mut data)?;
1946    /// *view.get_mut(&[1]).unwrap() = 20;
1947    /// assert_eq!(view.get(&[1]), Some(&20));
1948    /// # Ok::<(), tenferro_tensor::Error>(())
1949    /// ```
1950    pub fn get_mut(&mut self, indices: &[usize]) -> Option<&mut T> {
1951        let offset = self.linear_offset(indices)?;
1952        match &mut self.buffer {
1953            TensorBufferRefMut::Host(data) => data.get_mut(offset),
1954            TensorBufferRefMut::Backend(_) => None,
1955        }
1956    }
1957
1958    /// Borrow this mutable view as a read-only view.
1959    ///
1960    /// # Examples
1961    ///
1962    /// ```rust
1963    /// use tenferro_tensor::TypedTensorViewMut;
1964    ///
1965    /// let mut data = [1_i32];
1966    /// let view = TypedTensorViewMut::from_slice(vec![1], vec![1], 0, &mut data)?;
1967    /// assert_eq!(view.as_read_only().get(&[0]), Some(&1));
1968    /// # Ok::<(), tenferro_tensor::Error>(())
1969    /// ```
1970    pub fn as_read_only(&self) -> TypedTensorView<'_, T, R> {
1971        let buffer = match &self.buffer {
1972            TensorBufferRefMut::Host(data) => TensorBufferRef::Host(data),
1973            TensorBufferRefMut::Backend(buffer) => TensorBufferRef::Backend(Arc::clone(buffer)),
1974        };
1975        TypedTensorView {
1976            buffer,
1977            layout: self.layout.clone(),
1978            placement: self.placement.clone(),
1979        }
1980    }
1981
1982    /// Convert this mutable view into a read-only view.
1983    ///
1984    /// # Examples
1985    ///
1986    /// ```rust
1987    /// use tenferro_tensor::TypedTensorViewMut;
1988    ///
1989    /// let mut data = [1_i32];
1990    /// let view = TypedTensorViewMut::from_slice(vec![1], vec![1], 0, &mut data)?;
1991    /// assert_eq!(view.into_read_only().get(&[0]), Some(&1));
1992    /// # Ok::<(), tenferro_tensor::Error>(())
1993    /// ```
1994    pub fn into_read_only(self) -> TypedTensorView<'a, T, R> {
1995        let buffer = match self.buffer {
1996            TensorBufferRefMut::Host(data) => TensorBufferRef::Host(data),
1997            TensorBufferRefMut::Backend(buffer) => TensorBufferRef::Backend(buffer),
1998        };
1999        TypedTensorView {
2000            buffer,
2001            layout: self.layout,
2002            placement: self.placement,
2003        }
2004    }
2005
2006    /// Consume this mutable view and return a metadata-only axis permutation.
2007    ///
2008    /// # Examples
2009    ///
2010    /// ```rust
2011    /// use tenferro_tensor::{Rank, TypedTensorViewMut};
2012    ///
2013    /// let mut data = [1_i32, 2, 3, 4];
2014    /// let view = TypedTensorViewMut::<_, Rank<2>>::from_slice_ranked([2, 2], [1, 2], 0, &mut data)?;
2015    /// let transposed = view.transpose_view([1, 0])?;
2016    /// assert_eq!(transposed.strides(), &[2, 1]);
2017    /// # Ok::<(), tenferro_tensor::Error>(())
2018    /// ```
2019    /// # Errors
2020    ///
2021    /// Returns [`crate::Error::Validation`] with
2022    /// [`tenferro_tensor_core::ValidationError::InvalidPermutationLength`],
2023    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`], or
2024    /// [`tenferro_tensor_core::ValidationError::DuplicateAxis`] when `axes` is
2025    /// not a valid permutation, [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`]
2026    /// when the permutation creates aliases, or
2027    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
2028    /// arithmetic overflow.
2029    pub fn transpose_view(
2030        self,
2031        axes: impl AsRef<[usize]>,
2032    ) -> crate::Result<TypedTensorViewMut<'a, T, R>> {
2033        let Self {
2034            buffer,
2035            layout,
2036            placement,
2037        } = self;
2038        let layout = layout
2039            .transpose_view(axes)
2040            .map_err(|err| tensor_layout_error("TypedTensorViewMut::transpose_view", err))?;
2041        layout
2042            .validate_mutable_no_overlap()
2043            .map_err(|err| tensor_layout_error("TypedTensorViewMut::transpose_view", err))?;
2044        match buffer {
2045            TensorBufferRefMut::Host(data) => Ok(TypedTensorViewMut {
2046                buffer: TensorBufferRefMut::Host(data),
2047                layout,
2048                placement,
2049            }),
2050            TensorBufferRefMut::Backend(buffer) => Ok(TypedTensorViewMut {
2051                buffer: TensorBufferRefMut::Backend(buffer),
2052                layout,
2053                placement,
2054            }),
2055        }
2056    }
2057
2058    /// Return a mutable metadata-only slice using one [`StridedSliceSpec`] per axis.
2059    ///
2060    /// # Examples
2061    ///
2062    /// ```rust
2063    /// use tenferro_tensor::{StridedSliceSpec, TypedTensorViewMut};
2064    ///
2065    /// let mut data = [1_i32, 2, 3];
2066    /// let mut view = TypedTensorViewMut::from_slice(vec![3], vec![1], 0, &mut data)?;
2067    /// *view.try_slice(&[StridedSliceSpec::reverse()])?.get_mut(&[0]).unwrap() = 30;
2068    /// assert_eq!(view.get(&[2]), Some(&30));
2069    /// # Ok::<(), tenferro_tensor::Error>(())
2070    /// ```
2071    /// # Errors
2072    ///
2073    /// Returns [`crate::Error::Validation`] with
2074    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when the slice
2075    /// count differs from the view rank,
2076    /// [`tenferro_tensor_core::ValidationError::InvalidSliceStep`] or
2077    /// [`tenferro_tensor_core::ValidationError::InvalidSliceBounds`] for an
2078    /// invalid slice, [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`]
2079    /// when the result exceeds the backing buffer,
2080    /// [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`] when
2081    /// logical elements alias, or
2082    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
2083    /// arithmetic overflow.
2084    pub fn try_slice(
2085        &mut self,
2086        slices: &[StridedSliceSpec],
2087    ) -> crate::Result<TypedTensorViewMut<'_, T, R>> {
2088        let specs = core_slice_specs(slices, self.shape(), "TypedTensorViewMut::try_slice")?;
2089        let layout = self
2090            .layout
2091            .slice_view(specs, self.buffer.len())
2092            .map_err(|err| tensor_layout_error("TypedTensorViewMut::try_slice", err))?;
2093        layout
2094            .validate_mutable_no_overlap()
2095            .map_err(|err| tensor_layout_error("TypedTensorViewMut::try_slice", err))?;
2096        let placement = self.placement.clone();
2097        match &mut self.buffer {
2098            TensorBufferRefMut::Host(data) => Ok(TypedTensorViewMut {
2099                buffer: TensorBufferRefMut::Host(data),
2100                layout,
2101                placement,
2102            }),
2103            TensorBufferRefMut::Backend(buffer) => Ok(TypedTensorViewMut {
2104                buffer: TensorBufferRefMut::Backend(Arc::clone(buffer)),
2105                layout,
2106                placement,
2107            }),
2108        }
2109    }
2110
2111    /// Return a mutable metadata-only slice along one axis.
2112    ///
2113    /// # Examples
2114    ///
2115    /// ```rust
2116    /// use tenferro_tensor::{StridedSliceSpec, TypedTensorViewMut};
2117    ///
2118    /// let mut data = [1_i32, 2, 3, 4];
2119    /// let mut view = TypedTensorViewMut::from_slice(vec![2, 2], vec![1, 2], 0, &mut data)?;
2120    /// assert_eq!(view.try_slice_axis(1, StridedSliceSpec::reverse())?.get(&[0, 0]), Some(&3));
2121    /// # Ok::<(), tenferro_tensor::Error>(())
2122    /// ```
2123    /// # Errors
2124    ///
2125    /// Returns [`crate::Error::Validation`] with
2126    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`] when `axis`
2127    /// is outside the view rank, [`tenferro_tensor_core::ValidationError::InvalidSliceStep`]
2128    /// or [`tenferro_tensor_core::ValidationError::InvalidSliceBounds`] for an
2129    /// invalid slice, [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`]
2130    /// when the result exceeds the backing buffer,
2131    /// [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`] when
2132    /// logical elements alias, or
2133    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
2134    /// arithmetic overflow.
2135    pub fn try_slice_axis(
2136        &mut self,
2137        axis: usize,
2138        slice: StridedSliceSpec,
2139    ) -> crate::Result<TypedTensorViewMut<'_, T, R>> {
2140        let slices = slice_axis_specs(
2141            self.shape().len(),
2142            axis,
2143            slice,
2144            "TypedTensorViewMut::try_slice_axis",
2145        )?;
2146        self.try_slice(&slices)
2147    }
2148
2149    /// Return two mutable metadata-only slices when their physical ranges are disjoint.
2150    ///
2151    /// # Examples
2152    ///
2153    /// ```rust
2154    /// use tenferro_tensor::{StridedSliceSpec, TypedTensorViewMut};
2155    ///
2156    /// let mut data = [1_i32, 2, 3, 4];
2157    /// let mut view = TypedTensorViewMut::from_slice(vec![4], vec![1], 0, &mut data)?;
2158    /// let (left, right) = view
2159    ///     .try_multi_slice_mut(
2160    ///         &[StridedSliceSpec::new(0, Some(2), 1)],
2161    ///         &[StridedSliceSpec::new(2, Some(4), 1)],
2162    ///     )
2163    ///     ?
2164    ///     .unwrap();
2165    /// assert_eq!(left.shape(), &[2]);
2166    /// assert_eq!(right.shape(), &[2]);
2167    /// # Ok::<(), tenferro_tensor::Error>(())
2168    /// ```
2169    /// # Errors
2170    ///
2171    /// Returns [`crate::Error::Validation`] with
2172    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] for either
2173    /// slice count, [`tenferro_tensor_core::ValidationError::InvalidSliceStep`]
2174    /// or [`tenferro_tensor_core::ValidationError::InvalidSliceBounds`] for
2175    /// invalid parameters, [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`]
2176    /// when a result exceeds the backing buffer,
2177    /// [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`] when
2178    /// a result aliases, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
2179    /// for a negative reachable offset, or
2180    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
2181    /// arithmetic overflow. The method returns `Ok(None)` when the two ranges
2182    /// overlap or the view uses backend storage.
2183    pub fn try_multi_slice_mut(
2184        &mut self,
2185        first: &[StridedSliceSpec],
2186        second: &[StridedSliceSpec],
2187    ) -> crate::Result<Option<TypedTensorViewMutPair<'_, T, R>>> {
2188        let op = "TypedTensorViewMut::try_multi_slice_mut";
2189        let first_specs = core_slice_specs(first, self.shape(), op)?;
2190        let second_specs = core_slice_specs(second, self.shape(), op)?;
2191        let buffer_len = self.buffer.len();
2192        let first_layout = self
2193            .layout
2194            .slice_view(first_specs, buffer_len)
2195            .map_err(|err| tensor_layout_error(op, err))?;
2196        let second_layout = self
2197            .layout
2198            .slice_view(second_specs, buffer_len)
2199            .map_err(|err| tensor_layout_error(op, err))?;
2200        first_layout
2201            .validate_mutable_no_overlap()
2202            .map_err(|err| tensor_layout_error(op, err))?;
2203        second_layout
2204            .validate_mutable_no_overlap()
2205            .map_err(|err| tensor_layout_error(op, err))?;
2206
2207        match (
2208            reachable_layout_span(
2209                first_layout.shape(),
2210                first_layout.strides(),
2211                first_layout.offset(),
2212            )?,
2213            reachable_layout_span(
2214                second_layout.shape(),
2215                second_layout.strides(),
2216                second_layout.offset(),
2217            )?,
2218        ) {
2219            (Some(first_span), Some(second_span)) => {
2220                let first_offset = adjusted_view_offset(first_layout.offset(), first_span.0)?;
2221                let second_offset = adjusted_view_offset(second_layout.offset(), second_span.0)?;
2222                let (first_data, second_data) = match &mut self.buffer {
2223                    TensorBufferRefMut::Host(data) => {
2224                        match split_two_mut_ranges(data, first_span, second_span) {
2225                            Some(ranges) => ranges,
2226                            None => return Ok(None),
2227                        }
2228                    }
2229                    TensorBufferRefMut::Backend(_) => return Ok(None),
2230                };
2231                let first_view = view_mut_from_layout_and_slice(
2232                    &first_layout,
2233                    first_offset,
2234                    first_data,
2235                    self.placement.clone(),
2236                )?;
2237                let second_view = view_mut_from_layout_and_slice(
2238                    &second_layout,
2239                    second_offset,
2240                    second_data,
2241                    self.placement.clone(),
2242                )?;
2243                Ok(Some((first_view, second_view)))
2244            }
2245            (None, Some(second_span)) => {
2246                let second_offset = adjusted_view_offset(second_layout.offset(), second_span.0)?;
2247                let (_, after_start) = match &mut self.buffer {
2248                    TensorBufferRefMut::Host(data) => data.split_at_mut(second_span.0),
2249                    TensorBufferRefMut::Backend(_) => return Ok(None),
2250                };
2251                let (second_data, _) = after_start.split_at_mut(second_span.1 - second_span.0 + 1);
2252                let first_view = view_mut_from_layout_and_slice(
2253                    &first_layout,
2254                    0,
2255                    &mut [],
2256                    self.placement.clone(),
2257                )?;
2258                let second_view = view_mut_from_layout_and_slice(
2259                    &second_layout,
2260                    second_offset,
2261                    second_data,
2262                    self.placement.clone(),
2263                )?;
2264                Ok(Some((first_view, second_view)))
2265            }
2266            (Some(first_span), None) => {
2267                let first_offset = adjusted_view_offset(first_layout.offset(), first_span.0)?;
2268                let (_, after_start) = match &mut self.buffer {
2269                    TensorBufferRefMut::Host(data) => data.split_at_mut(first_span.0),
2270                    TensorBufferRefMut::Backend(_) => return Ok(None),
2271                };
2272                let (first_data, _) = after_start.split_at_mut(first_span.1 - first_span.0 + 1);
2273                let first_view = view_mut_from_layout_and_slice(
2274                    &first_layout,
2275                    first_offset,
2276                    first_data,
2277                    self.placement.clone(),
2278                )?;
2279                let second_view = view_mut_from_layout_and_slice(
2280                    &second_layout,
2281                    0,
2282                    &mut [],
2283                    self.placement.clone(),
2284                )?;
2285                Ok(Some((first_view, second_view)))
2286            }
2287            (None, None) => {
2288                let first_view = view_mut_from_layout_and_slice(
2289                    &first_layout,
2290                    0,
2291                    &mut [],
2292                    self.placement.clone(),
2293                )?;
2294                let second_view = view_mut_from_layout_and_slice(
2295                    &second_layout,
2296                    0,
2297                    &mut [],
2298                    self.placement.clone(),
2299                )?;
2300                Ok(Some((first_view, second_view)))
2301            }
2302        }
2303    }
2304
2305    /// Return a mutable metadata-only dynamic-rank reshape for contiguous views.
2306    ///
2307    /// # Examples
2308    ///
2309    /// ```rust
2310    /// use tenferro_tensor::TypedTensorViewMut;
2311    ///
2312    /// let mut data = [1_i32, 2, 3, 4];
2313    /// let mut view = TypedTensorViewMut::from_slice(vec![2, 2], vec![1, 2], 0, &mut data)?;
2314    /// assert_eq!(view.try_reshape(&[4])?.shape(), &[4]);
2315    /// # Ok::<(), tenferro_tensor::Error>(())
2316    /// ```
2317    /// # Errors
2318    ///
2319    /// Returns [`crate::Error::Validation`] with
2320    /// [`tenferro_tensor_core::ValidationError::NonContiguousViewAsSlice`] when
2321    /// the source is not compact column-major,
2322    /// [`tenferro_tensor_core::ValidationError::ShapeMismatch`] (whose
2323    /// [`tenferro_tensor_core::ShapeMismatch::ReshapeElementCount`] source
2324    /// records the counts) when element counts differ,
2325    /// [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`] when
2326    /// the reshaped layout aliases, [`tenferro_tensor_core::ValidationError::IntegerOverflow`]
2327    /// for shape or layout arithmetic overflow, or
2328    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
2329    /// reshaped view exceeds the backing buffer.
2330    pub fn try_reshape(
2331        &mut self,
2332        shape: &[usize],
2333    ) -> crate::Result<TypedTensorViewMut<'_, T, DynRank>> {
2334        let layout = reshape_layout_dyn(
2335            &self.layout,
2336            shape,
2337            self.buffer.len(),
2338            "TypedTensorViewMut::try_reshape",
2339        )?;
2340        layout
2341            .validate_mutable_no_overlap()
2342            .map_err(|err| tensor_layout_error("TypedTensorViewMut::try_reshape", err))?;
2343        let placement = self.placement.clone();
2344        match &mut self.buffer {
2345            TensorBufferRefMut::Host(data) => Ok(TypedTensorViewMut {
2346                buffer: TensorBufferRefMut::Host(data),
2347                layout,
2348                placement,
2349            }),
2350            TensorBufferRefMut::Backend(buffer) => Ok(TypedTensorViewMut {
2351                buffer: TensorBufferRefMut::Backend(Arc::clone(buffer)),
2352                layout,
2353                placement,
2354            }),
2355        }
2356    }
2357}
2358
2359/// Runtime scalar dtype tag.
2360///
2361/// # Examples
2362///
2363/// ```rust
2364/// use tenferro_tensor::DType;
2365///
2366/// assert_eq!(DType::F64 as u8, DType::F64 as u8);
2367/// ```
2368#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2369pub enum DType {
2370    F32,
2371    F64,
2372    I32,
2373    I64,
2374    Bool,
2375    C32,
2376    C64,
2377}
2378
2379/// Sealed trait for scalar types that can be stored in a [`Tensor`].
2380///
2381/// This trait is implemented for `f64`, `f32`, `i32`, `i64`, `bool`,
2382/// [`Complex64`], and [`Complex32`].
2383///
2384/// # Examples
2385///
2386/// ```
2387/// use tenferro_tensor::TensorScalar;
2388///
2389/// let tensor = <f64 as TensorScalar>::into_tensor(vec![2], vec![1.0, 2.0])?;
2390/// assert_eq!(tensor.as_slice::<f64>()?, [1.0, 2.0].as_slice());
2391/// # Ok::<(), tenferro_tensor::Error>(())
2392/// ```
2393pub trait TensorScalar: Copy + Clone + Send + Sync + 'static + private::Sealed {
2394    /// Real-valued counterpart of this scalar type.
2395    type Real: TensorScalar;
2396
2397    /// The [`DType`] tag corresponding to this scalar type.
2398    ///
2399    /// # Examples
2400    ///
2401    /// ```
2402    /// use tenferro_tensor::{DType, TensorScalar};
2403    ///
2404    /// assert_eq!(f64::dtype(), DType::F64);
2405    /// assert_eq!(f32::dtype(), DType::F32);
2406    /// ```
2407    fn dtype() -> DType;
2408
2409    /// Wrap typed column-major data into a [`Tensor`] enum variant.
2410    /// # Errors
2411    ///
2412    /// Returns [`crate::Error::Validation`] with
2413    /// [`tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch`] when
2414    /// the shape product differs from `data.len()`, or
2415    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when shape
2416    /// arithmetic overflows.
2417    fn into_tensor(shape: Vec<usize>, data: Vec<Self>) -> crate::Result<Tensor>;
2418
2419    /// Wrap a typed tensor into its dynamic [`Tensor`] enum variant.
2420    ///
2421    /// # Examples
2422    ///
2423    /// ```
2424    /// use tenferro_tensor::{Tensor, TensorScalar, TypedTensor};
2425    ///
2426    /// let typed = TypedTensor::<f64>::from_vec_col_major(vec![1], vec![3.0])?;
2427    /// let tensor = <f64 as TensorScalar>::typed_tensor_into_tensor(typed);
2428    /// assert!(matches!(tensor, Tensor::F64(_)));
2429    /// # Ok::<(), tenferro_tensor::Error>(())
2430    /// ```
2431    fn typed_tensor_into_tensor(tensor: TypedTensor<Self>) -> Tensor;
2432
2433    /// Borrow a typed tensor as a dtype-erased [`TensorRead`] view.
2434    ///
2435    /// This keeps the typed tensor borrowed instead of copying host data into
2436    /// a new dynamic tensor.
2437    ///
2438    /// # Examples
2439    ///
2440    /// ```
2441    /// use tenferro_tensor::{DType, TensorScalar, TypedTensor};
2442    ///
2443    /// let tensor = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
2444    /// let read = f64::tensor_read(&tensor);
2445    /// assert_eq!(read.dtype(), DType::F64);
2446    /// assert_eq!(read.shape(), &[2]);
2447    /// ```
2448    fn tensor_read(tensor: &TypedTensor<Self>) -> TensorRead<'_>;
2449
2450    /// Wrap a typed borrowed view as a dtype-erased [`TensorView`].
2451    ///
2452    /// # Examples
2453    ///
2454    /// ```
2455    /// use tenferro_tensor::{DType, TensorScalar, TypedTensorView};
2456    ///
2457    /// let data = [1.0_f64];
2458    /// let view = TypedTensorView::from_col_major(&[1], &data)?;
2459    /// assert_eq!(f64::tensor_view(view).dtype(), DType::F64);
2460    /// # Ok::<(), tenferro_tensor::Error>(())
2461    /// ```
2462    fn tensor_view<'a>(view: TypedTensorView<'a, Self>) -> TensorView<'a>;
2463
2464    /// Wrap a typed mutable borrowed view as a dtype-erased [`TensorViewMut`].
2465    ///
2466    /// # Examples
2467    ///
2468    /// ```
2469    /// use tenferro_tensor::{DType, TensorScalar, TypedTensorViewMut};
2470    ///
2471    /// let mut data = [1.0_f64, 2.0];
2472    /// let view = TypedTensorViewMut::from_col_major(&[2], &mut data)?;
2473    /// let erased = f64::tensor_view_mut(view);
2474    /// assert_eq!(erased.dtype(), DType::F64);
2475    /// assert_eq!(erased.shape(), &[2]);
2476    /// # Ok::<(), tenferro_tensor::Error>(())
2477    /// ```
2478    fn tensor_view_mut<'a>(view: TypedTensorViewMut<'a, Self>) -> TensorViewMut<'a>;
2479
2480    /// Mutably borrow a typed tensor as a dtype-erased [`TensorWrite`] view.
2481    ///
2482    /// This keeps the typed output borrowed instead of wrapping it in a
2483    /// temporary dynamic tensor.
2484    ///
2485    /// # Examples
2486    ///
2487    /// ```
2488    /// use tenferro_tensor::{DType, TensorScalar, TypedTensor};
2489    ///
2490    /// let mut tensor = TypedTensor::<f64>::from_vec_col_major(vec![1], vec![0.0]).unwrap();
2491    /// let write = f64::tensor_write(&mut tensor);
2492    /// assert_eq!(write.dtype(), DType::F64);
2493    /// ```
2494    fn tensor_write(tensor: &mut TypedTensor<Self>) -> TensorWrite<'_>;
2495
2496    /// Borrow the host data from a [`Tensor`].
2497    /// # Errors
2498    ///
2499    /// Returns [`crate::Error::Validation`] with
2500    /// [`tenferro_tensor_core::ValidationError::DTypeMismatch`] when `tensor`
2501    /// is not the scalar type represented by this implementation, or
2502    /// [`crate::Error::RuntimeState`] when the matching tensor uses backend
2503    /// storage that has not been downloaded.
2504    fn as_slice(tensor: &Tensor) -> crate::Result<&[Self]>;
2505
2506    /// Mutably borrow the host data from a [`Tensor`].
2507    ///
2508    /// # Examples
2509    ///
2510    /// ```
2511    /// use tenferro_tensor::{Tensor, TensorScalar};
2512    ///
2513    /// let mut tensor = Tensor::from_vec_col_major(vec![1], vec![2.0_f64])?;
2514    /// <f64 as TensorScalar>::as_slice_mut(&mut tensor)?[0] = 3.0;
2515    ///
2516    /// assert_eq!(tensor.as_slice::<f64>()?, &[3.0]);
2517    /// # Ok::<(), tenferro_tensor::Error>(())
2518    /// ```
2519    /// # Errors
2520    ///
2521    /// Returns [`crate::Error::Validation`] with
2522    /// [`tenferro_tensor_core::ValidationError::DTypeMismatch`] when `tensor`
2523    /// is not the scalar type represented by this implementation, or
2524    /// [`crate::Error::RuntimeState`] when the matching tensor uses backend
2525    /// storage that has not been downloaded.
2526    fn as_slice_mut(tensor: &mut Tensor) -> crate::Result<&mut [Self]>;
2527
2528    /// Extract a [`TypedTensor<Self>`] from a dynamic [`Tensor`].
2529    ///
2530    /// # Examples
2531    ///
2532    /// ```
2533    /// use tenferro_tensor::{Tensor, TensorScalar};
2534    ///
2535    /// let tensor = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
2536    /// let typed = <f64 as TensorScalar>::into_typed(tensor)?;
2537    ///
2538    /// assert_eq!(typed.as_slice()?, &[1.0, 2.0]);
2539    /// # Ok::<(), tenferro_tensor::Error>(())
2540    /// ```
2541    /// # Errors
2542    ///
2543    /// Returns [`crate::Error::Validation`] with
2544    /// [`tenferro_tensor_core::ValidationError::DTypeMismatch`] when `tensor`
2545    /// is not the scalar type represented by this implementation.
2546    fn into_typed(tensor: Tensor) -> crate::Result<TypedTensor<Self>>;
2547}
2548
2549mod private {
2550    pub trait Sealed {}
2551
2552    impl Sealed for f64 {}
2553    impl Sealed for f32 {}
2554    impl Sealed for i32 {}
2555    impl Sealed for i64 {}
2556    impl Sealed for bool {}
2557    impl Sealed for num_complex::Complex64 {}
2558    impl Sealed for num_complex::Complex32 {}
2559}
2560
2561macro_rules! impl_tensor_scalar {
2562    ($ty:ty, $real:ty, $dtype:ident, $variant:ident) => {
2563        impl TensorScalar for $ty {
2564            type Real = $real;
2565
2566            fn dtype() -> DType {
2567                DType::$dtype
2568            }
2569
2570            fn into_tensor(shape: Vec<usize>, data: Vec<Self>) -> crate::Result<Tensor> {
2571                TypedTensor::from_vec_col_major(shape, data).map(Tensor::$variant)
2572            }
2573
2574            fn typed_tensor_into_tensor(tensor: TypedTensor<Self>) -> Tensor {
2575                Tensor::$variant(tensor)
2576            }
2577
2578            fn tensor_read(tensor: &TypedTensor<Self>) -> TensorRead<'_> {
2579                TensorRead::from_view(TensorView::$variant(tensor.as_view()))
2580            }
2581
2582            fn tensor_view<'a>(view: TypedTensorView<'a, Self>) -> TensorView<'a> {
2583                TensorView::$variant(view)
2584            }
2585
2586            fn tensor_view_mut<'a>(view: TypedTensorViewMut<'a, Self>) -> TensorViewMut<'a> {
2587                TensorViewMut::$variant(view)
2588            }
2589
2590            fn tensor_write(tensor: &mut TypedTensor<Self>) -> TensorWrite<'_> {
2591                TensorWrite::from_view(TensorViewMut::$variant(tensor.as_view_mut()))
2592            }
2593
2594            fn as_slice(tensor: &Tensor) -> crate::Result<&[Self]> {
2595                let actual = tensor.dtype();
2596                match tensor {
2597                    Tensor::$variant(t) => t.host_data(),
2598                    _ => Err(crate::Error::validation(
2599                        "Tensor::as_slice",
2600                        ValidationError::DTypeMismatch {
2601                            expected: crate::core_dtype(Self::dtype()),
2602                            actual: crate::core_dtype(actual),
2603                        },
2604                    )),
2605                }
2606            }
2607
2608            fn as_slice_mut(tensor: &mut Tensor) -> crate::Result<&mut [Self]> {
2609                let actual = tensor.dtype();
2610                match tensor {
2611                    Tensor::$variant(t) => t.host_data_mut(),
2612                    _ => Err(crate::Error::validation(
2613                        "Tensor::as_slice_mut",
2614                        ValidationError::DTypeMismatch {
2615                            expected: crate::core_dtype(Self::dtype()),
2616                            actual: crate::core_dtype(actual),
2617                        },
2618                    )),
2619                }
2620            }
2621
2622            fn into_typed(tensor: Tensor) -> crate::Result<TypedTensor<Self>> {
2623                let actual = tensor.dtype();
2624                match tensor {
2625                    Tensor::$variant(inner) => Ok(inner),
2626                    _ => Err(crate::Error::validation(
2627                        "TensorScalar::into_typed",
2628                        ValidationError::DTypeMismatch {
2629                            expected: crate::core_dtype(Self::dtype()),
2630                            actual: crate::core_dtype(actual),
2631                        },
2632                    )),
2633                }
2634            }
2635        }
2636    };
2637}
2638
2639impl_tensor_scalar!(f64, f64, F64, F64);
2640impl_tensor_scalar!(f32, f32, F32, F32);
2641impl_tensor_scalar!(i64, i64, I64, I64);
2642impl_tensor_scalar!(i32, i32, I32, I32);
2643impl_tensor_scalar!(bool, bool, Bool, Bool);
2644impl_tensor_scalar!(Complex64, f64, C64, C64);
2645impl_tensor_scalar!(Complex32, f32, C32, C32);
2646
2647/// Dynamic tensor enum over the supported scalar types.
2648///
2649/// The enum keeps dtype dynamic and rank dynamic. Use
2650/// [`TypedTensor<T, R>`](TypedTensor) directly when the scalar type or rank
2651/// should be represented in Rust's type system.
2652///
2653/// # Examples
2654///
2655/// ```rust
2656/// use tenferro_tensor::{Tensor, TypedTensor};
2657///
2658/// let t = Tensor::F64(TypedTensor::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap());
2659/// assert_eq!(t.shape(), &[2]);
2660///
2661/// let erased = Tensor::from_vec_col_major(vec![1, 2], vec![1.0_f64, 2.0]).unwrap();
2662/// assert_eq!(erased.shape().len(), 2);
2663/// ```
2664#[derive(Clone, Debug)]
2665pub enum Tensor {
2666    F32(TypedTensor<f32>),
2667    F64(TypedTensor<f64>),
2668    I32(TypedTensor<i32>),
2669    I64(TypedTensor<i64>),
2670    Bool(TypedTensor<bool>),
2671    C32(TypedTensor<Complex<f32>>),
2672    C64(TypedTensor<Complex<f64>>),
2673}
2674
2675/// Dynamic read-only borrowed tensor view.
2676///
2677/// `TensorView` keeps dtype erased while borrowing typed view metadata and
2678/// storage. Use [`TypedTensorView`] directly when the scalar type is statically
2679/// known.
2680///
2681/// # Examples
2682///
2683/// ```
2684/// use tenferro_tensor::{DType, TensorView, TypedTensorView};
2685///
2686/// let data = [1_i32, 2, 3, 4];
2687/// let typed = TypedTensorView::from_slice([2, 2], [1, 2], 0, &data)?;
2688/// let view = TensorView::I32(typed);
2689///
2690/// assert_eq!(view.dtype(), DType::I32);
2691/// assert_eq!(view.shape(), &[2, 2]);
2692/// # Ok::<(), tenferro_tensor::Error>(())
2693/// ```
2694#[derive(Clone, Debug)]
2695pub enum TensorView<'a> {
2696    F32(TypedTensorView<'a, f32>),
2697    F64(TypedTensorView<'a, f64>),
2698    I32(TypedTensorView<'a, i32>),
2699    I64(TypedTensorView<'a, i64>),
2700    Bool(TypedTensorView<'a, bool>),
2701    C32(TypedTensorView<'a, Complex<f32>>),
2702    C64(TypedTensorView<'a, Complex<f64>>),
2703}
2704
2705/// Dynamic mutable borrowed tensor view.
2706///
2707/// `TensorViewMut` is the mutable counterpart to [`TensorView`]. It keeps the
2708/// dtype erased while preserving the typed mutable view's shape, strides, and
2709/// offset metadata.
2710///
2711/// # Examples
2712///
2713/// ```
2714/// use tenferro_tensor::{DType, TensorViewMut, TypedTensorViewMut};
2715///
2716/// let mut data = [1.0_f64, 2.0];
2717/// let view = TensorViewMut::F64(TypedTensorViewMut::from_slice([2], [1], 0, &mut data)?);
2718/// assert_eq!(view.dtype(), DType::F64);
2719/// # Ok::<(), tenferro_tensor::Error>(())
2720/// ```
2721#[allow(clippy::large_enum_variant)]
2722#[derive(Debug)]
2723pub enum TensorViewMut<'a> {
2724    F32(TypedTensorViewMut<'a, f32>),
2725    F64(TypedTensorViewMut<'a, f64>),
2726    I32(TypedTensorViewMut<'a, i32>),
2727    I64(TypedTensorViewMut<'a, i64>),
2728    Bool(TypedTensorViewMut<'a, bool>),
2729    C32(TypedTensorViewMut<'a, Complex<f32>>),
2730    C64(TypedTensorViewMut<'a, Complex<f64>>),
2731}
2732
2733/// Read-only tensor input accepted by synchronous eager kernels.
2734///
2735/// `TensorRead` lets kernels accept either an owned tensor reference or a
2736/// borrowed [`TensorView`] without forcing callers to materialize first.
2737/// The `View` variant preserves arbitrary strides and offsets, so kernels that
2738/// support strided reads can consume transposes, slices, and broadcasts directly.
2739///
2740/// `TensorRead` is intentionally borrowed. It is an input-dispatch type, not an
2741/// owned lazy tensor value. APIs that need to store a lazy layout result should
2742/// keep an owned base tensor plus layout metadata, then expose a `TensorRead`
2743/// only for the duration of kernel dispatch.
2744///
2745/// # Examples
2746///
2747/// ```
2748/// use tenferro_tensor::{DType, Tensor, TensorRead};
2749///
2750/// let tensor = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
2751/// let read = TensorRead::from_tensor(&tensor);
2752///
2753/// assert_eq!(read.dtype(), DType::F64);
2754/// assert_eq!(read.shape(), &[2]);
2755/// ```
2756// Keep borrowed views inline to avoid allocation on read-only tensor dispatch paths.
2757#[allow(clippy::large_enum_variant)]
2758#[derive(Clone, Debug)]
2759pub enum TensorRead<'a> {
2760    Tensor(&'a Tensor),
2761    View(TensorView<'a>),
2762}
2763
2764/// Mutable typed tensor output accepted by synchronous eager kernels.
2765///
2766/// `TypedTensorWrite` is the typed counterpart to [`TensorWrite`]. It accepts
2767/// either an owned compact [`TypedTensor`] or an arbitrary-strided mutable
2768/// [`TypedTensorViewMut`] without erasing the scalar type at the public API
2769/// boundary.
2770///
2771/// # Examples
2772///
2773/// ```
2774/// use tenferro_tensor::{TypedTensorViewMut, TypedTensorWrite};
2775///
2776/// let mut data = [0.0_f64, 1.0, 0.0, 2.0];
2777/// let view = TypedTensorViewMut::from_slice([2], [2], 1, &mut data)?;
2778/// let write = TypedTensorWrite::from_view(view).into_tensor_write();
2779/// assert_eq!(write.shape(), &[2]);
2780/// assert_eq!(write.strides()?, [2]);
2781/// # Ok::<(), tenferro_tensor::Error>(())
2782/// ```
2783#[allow(clippy::large_enum_variant)]
2784#[derive(Debug)]
2785pub enum TypedTensorWrite<'a, T> {
2786    /// An owned compact typed tensor borrowed mutably for the write.
2787    Tensor(&'a mut TypedTensor<T>),
2788    /// An arbitrary-strided mutable typed tensor view.
2789    View(TypedTensorViewMut<'a, T>),
2790}
2791
2792impl<'a, T> TypedTensorWrite<'a, T> {
2793    /// Create a writable target from an owned typed tensor.
2794    ///
2795    /// # Examples
2796    ///
2797    /// ```
2798    /// use tenferro_tensor::{TypedTensor, TypedTensorWrite};
2799    ///
2800    /// let mut tensor = TypedTensor::<f64>::from_vec_col_major(vec![1], vec![0.0])?;
2801    /// let write = TypedTensorWrite::from_tensor(&mut tensor);
2802    /// assert!(matches!(write, TypedTensorWrite::Tensor(_)));
2803    /// # Ok::<(), tenferro_tensor::Error>(())
2804    /// ```
2805    pub fn from_tensor(tensor: &'a mut TypedTensor<T>) -> Self {
2806        Self::Tensor(tensor)
2807    }
2808
2809    /// Create a writable target from a mutable typed tensor view.
2810    ///
2811    /// # Examples
2812    ///
2813    /// ```
2814    /// use tenferro_tensor::{TypedTensorViewMut, TypedTensorWrite};
2815    ///
2816    /// let mut data = [0.0_f64, 1.0];
2817    /// let view = TypedTensorViewMut::from_col_major(&[2], &mut data)?;
2818    /// let write = TypedTensorWrite::from_view(view);
2819    /// assert!(matches!(write, TypedTensorWrite::View(_)));
2820    /// # Ok::<(), tenferro_tensor::Error>(())
2821    /// ```
2822    pub fn from_view(view: TypedTensorViewMut<'a, T>) -> Self {
2823        Self::View(view)
2824    }
2825}
2826
2827impl<'a, T: TensorScalar> TypedTensorWrite<'a, T> {
2828    /// Erase the scalar type while preserving the output layout.
2829    ///
2830    /// # Examples
2831    ///
2832    /// ```
2833    /// use tenferro_tensor::{DType, TypedTensor, TypedTensorWrite};
2834    ///
2835    /// let mut tensor = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![0.0; 2])?;
2836    /// let write = TypedTensorWrite::from_tensor(&mut tensor).into_tensor_write();
2837    /// assert_eq!(write.dtype(), DType::F64);
2838    /// assert_eq!(write.shape(), &[2]);
2839    /// # Ok::<(), tenferro_tensor::Error>(())
2840    /// ```
2841    pub fn into_tensor_write(self) -> TensorWrite<'a> {
2842        match self {
2843            Self::Tensor(tensor) => T::tensor_write(tensor),
2844            Self::View(view) => TensorWrite::from_view(T::tensor_view_mut(view)),
2845        }
2846    }
2847}
2848
2849impl<'a, T> From<&'a mut TypedTensor<T>> for TypedTensorWrite<'a, T> {
2850    fn from(tensor: &'a mut TypedTensor<T>) -> Self {
2851        Self::from_tensor(tensor)
2852    }
2853}
2854
2855impl<'a, T> From<TypedTensorViewMut<'a, T>> for TypedTensorWrite<'a, T> {
2856    fn from(view: TypedTensorViewMut<'a, T>) -> Self {
2857        Self::from_view(view)
2858    }
2859}
2860
2861/// Mutable tensor output accepted by synchronous eager kernels.
2862///
2863/// `TensorWrite` mirrors [`TensorRead`] for output dispatch: it can target an
2864/// owned compact [`Tensor`] or a borrowed mutable [`TensorViewMut`]. The target
2865/// is never resized.
2866///
2867/// # Examples
2868///
2869/// ```
2870/// use tenferro_tensor::{Tensor, TensorWrite};
2871///
2872/// let mut tensor = Tensor::from_vec_col_major(vec![1], vec![0.0_f64])?;
2873/// let write = TensorWrite::from_tensor(&mut tensor);
2874/// assert_eq!(write.shape(), &[1]);
2875/// # Ok::<(), tenferro_tensor::Error>(())
2876/// ```
2877#[allow(clippy::large_enum_variant)]
2878#[derive(Debug)]
2879pub enum TensorWrite<'a> {
2880    Tensor(&'a mut Tensor),
2881    View(TensorViewMut<'a>),
2882}
2883
2884/// Owned lazy tensor view over a shared base tensor.
2885///
2886/// This stores only ownership of the base allocation plus logical layout
2887/// metadata. Borrow it as [`TensorRead`] and materialize it through an active
2888/// backend session when compact storage is required.
2889#[derive(Clone, Debug)]
2890pub struct TensorOwnedView {
2891    base: Arc<Tensor>,
2892    layout: TensorLayout<DynRank>,
2893}
2894
2895/// Owned tensor value that can be compact or a lazy view.
2896///
2897/// `TensorValue` is the owned counterpart to [`TensorRead`]. It is suitable for
2898/// storing eager results that should remain lazy until an operation actually
2899/// requires compact materialized storage.
2900#[derive(Clone, Debug)]
2901pub enum TensorValue {
2902    Tensor(Arc<Tensor>),
2903    View(TensorOwnedView),
2904}
2905
2906impl TensorOwnedView {
2907    /// Create an owned view preserving the base tensor's current layout.
2908    pub fn from_tensor(base: Arc<Tensor>) -> Self {
2909        let layout = tensor_layout(base.as_ref());
2910        Self { base, layout }
2911    }
2912
2913    /// Create an owned view with explicit layout metadata.
2914    /// # Errors
2915    ///
2916    /// Returns [`crate::Error::Validation`] with
2917    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `shape` and
2918    /// `strides` have different ranks,
2919    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
2920    /// layout exceeds the base tensor buffer, or
2921    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
2922    /// arithmetic overflow.
2923    pub fn from_parts(
2924        base: Arc<Tensor>,
2925        shape: Vec<usize>,
2926        strides: Vec<isize>,
2927        offset: isize,
2928    ) -> crate::Result<Self> {
2929        let layout = TensorLayout::from_parts(
2930            shape.into(),
2931            strides.into(),
2932            offset,
2933            tensor_buffer_len(&base),
2934        )
2935        .map_err(|err| tensor_layout_error("TensorOwnedView::from_parts", err))?;
2936        Ok(Self { base, layout })
2937    }
2938
2939    pub fn dtype(&self) -> DType {
2940        self.base.dtype()
2941    }
2942
2943    pub fn shape(&self) -> &[usize] {
2944        self.layout.shape()
2945    }
2946
2947    pub fn strides(&self) -> &[isize] {
2948        self.layout.strides()
2949    }
2950
2951    pub fn offset(&self) -> isize {
2952        self.layout.offset()
2953    }
2954
2955    pub fn tensor_view(&self) -> TensorView<'_> {
2956        tensor_view_with_layout(self.base.as_ref(), self.layout.clone())
2957    }
2958
2959    pub fn tensor_read(&self) -> TensorRead<'_> {
2960        TensorRead::from_view(self.tensor_view())
2961    }
2962
2963    /// # Errors
2964    ///
2965    /// Returns [`crate::Error::Validation`] with
2966    /// [`tenferro_tensor_core::ValidationError::InvalidPermutationLength`],
2967    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`], or
2968    /// [`tenferro_tensor_core::ValidationError::DuplicateAxis`] when `axes` is
2969    /// not a valid permutation of the view rank.
2970    pub fn transpose_view(&self, axes: impl AsRef<[usize]>) -> crate::Result<Self> {
2971        let layout = self
2972            .layout
2973            .transpose_view(axes)
2974            .map_err(|err| tensor_layout_error("TensorOwnedView::transpose_view", err))?;
2975        Ok(Self {
2976            base: Arc::clone(&self.base),
2977            layout,
2978        })
2979    }
2980
2981    /// # Errors
2982    ///
2983    /// Returns [`crate::Error::Validation`] with
2984    /// [`tenferro_tensor_core::ValidationError::NonContiguousViewAsSlice`] when
2985    /// the source is not compact column-major,
2986    /// [`tenferro_tensor_core::ValidationError::ShapeMismatch`] (whose
2987    /// [`tenferro_tensor_core::ShapeMismatch::ReshapeElementCount`] source
2988    /// records the counts) when element counts differ,
2989    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for shape
2990    /// arithmetic overflow, or
2991    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
2992    /// reshaped view exceeds the base buffer.
2993    pub fn reshape_view(
2994        &self,
2995        shape: impl tenferro_tensor_core::IntoShapeVec,
2996    ) -> crate::Result<Self> {
2997        let shape = shape.into_shape_vec();
2998        let layout = reshape_layout_dyn(
2999            &self.layout,
3000            &shape,
3001            tensor_buffer_len(&self.base),
3002            "TensorOwnedView::reshape_view",
3003        )?;
3004        Ok(Self {
3005            base: Arc::clone(&self.base),
3006            layout,
3007        })
3008    }
3009
3010    /// # Errors
3011    ///
3012    /// Returns [`crate::Error::Validation`] with
3013    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when a slice
3014    /// vector does not match the view rank,
3015    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when a bound
3016    /// or stride cannot be represented or is invalid,
3017    /// [`tenferro_tensor_core::ValidationError::InvalidSliceStep`] or
3018    /// [`tenferro_tensor_core::ValidationError::InvalidSliceBounds`] for slice
3019    /// parameters, [`tenferro_tensor_core::ValidationError::IntegerOverflow`]
3020    /// for slice arithmetic overflow, or
3021    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3022    /// result exceeds the base buffer.
3023    pub fn slice_view(&self, config: &SliceConfig) -> crate::Result<Self> {
3024        let op = "TensorOwnedView::slice_view";
3025        if config.starts.len() != self.shape().len() {
3026            return Err(crate::Error::validation(
3027                op,
3028                ValidationError::RankMismatch {
3029                    expected: self.shape().len(),
3030                    actual: config.starts.len(),
3031                },
3032            ));
3033        }
3034        if config.limits.len() != self.shape().len() {
3035            return Err(crate::Error::validation(
3036                op,
3037                ValidationError::RankMismatch {
3038                    expected: self.shape().len(),
3039                    actual: config.limits.len(),
3040                },
3041            ));
3042        }
3043        if config.strides.len() != self.shape().len() {
3044            return Err(crate::Error::validation(
3045                op,
3046                ValidationError::RankMismatch {
3047                    expected: self.shape().len(),
3048                    actual: config.strides.len(),
3049                },
3050            ));
3051        }
3052
3053        let mut slices = Vec::with_capacity(self.shape().len());
3054        for ((&start, &limit), &stride) in config
3055            .starts
3056            .iter()
3057            .zip(config.limits.iter())
3058            .zip(config.strides.iter())
3059        {
3060            let start = isize::try_from(start).map_err(|_| {
3061                crate::Error::invalid_argument(
3062                    op,
3063                    "slice start",
3064                    format!("slice start {start} does not fit in isize"),
3065                )
3066            })?;
3067            let limit = isize::try_from(limit).map_err(|_| {
3068                crate::Error::invalid_argument(
3069                    op,
3070                    "slice limit",
3071                    format!("slice limit {limit} does not fit in isize"),
3072                )
3073            })?;
3074            let stride = isize::try_from(stride).map_err(|_| {
3075                crate::Error::invalid_argument(
3076                    op,
3077                    "slice stride",
3078                    format!("slice stride {stride} does not fit in isize"),
3079                )
3080            })?;
3081            slices.push(StridedSliceSpec::new(start, Some(limit), stride));
3082        }
3083
3084        let specs = core_slice_specs(&slices, self.shape(), op)?;
3085        let layout = self
3086            .layout
3087            .slice_view(&specs, tensor_buffer_len(&self.base))
3088            .map_err(|err| tensor_layout_error(op, err))?;
3089        Ok(Self {
3090            base: Arc::clone(&self.base),
3091            layout,
3092        })
3093    }
3094
3095    /// # Errors
3096    ///
3097    /// Returns [`crate::Error::Validation`] with
3098    /// [`tenferro_tensor_core::ValidationError::RankMismatch`],
3099    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`], or
3100    /// [`tenferro_tensor_core::ValidationError::DuplicateAxis`] for invalid
3101    /// dimension mappings,
3102    /// [`tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch`] for
3103    /// incompatible extents,
3104    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3105    /// result exceeds the base buffer, or
3106    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
3107    /// arithmetic overflow.
3108    pub fn broadcast_in_dim_view(
3109        &self,
3110        shape: impl tenferro_tensor_core::IntoShapeVec,
3111        dims: impl AsRef<[usize]>,
3112    ) -> crate::Result<Self> {
3113        let shape = shape.into_shape_vec();
3114        let layout = self
3115            .layout
3116            .broadcast_in_dim_view::<DynRank>(shape, dims, tensor_buffer_len(&self.base))
3117            .map_err(|err| tensor_layout_error("TensorOwnedView::broadcast_in_dim_view", err))?;
3118        Ok(Self {
3119            base: Arc::clone(&self.base),
3120            layout,
3121        })
3122    }
3123}
3124
3125impl TensorValue {
3126    pub fn from_tensor(tensor: Tensor) -> Self {
3127        Self::Tensor(Arc::new(tensor))
3128    }
3129
3130    pub fn from_tensor_arc(tensor: Arc<Tensor>) -> Self {
3131        Self::Tensor(tensor)
3132    }
3133
3134    pub fn as_tensor_arc(&self) -> Option<&Arc<Tensor>> {
3135        match self {
3136            Self::Tensor(tensor) => Some(tensor),
3137            Self::View(_) => None,
3138        }
3139    }
3140
3141    pub fn dtype(&self) -> DType {
3142        match self {
3143            Self::Tensor(tensor) => tensor.dtype(),
3144            Self::View(view) => view.dtype(),
3145        }
3146    }
3147
3148    pub fn shape(&self) -> &[usize] {
3149        match self {
3150            Self::Tensor(tensor) => tensor.shape(),
3151            Self::View(view) => view.shape(),
3152        }
3153    }
3154
3155    pub fn tensor_read(&self) -> TensorRead<'_> {
3156        match self {
3157            Self::Tensor(tensor) => TensorRead::from_tensor(tensor.as_ref()),
3158            Self::View(view) => view.tensor_read(),
3159        }
3160    }
3161
3162    /// # Errors
3163    ///
3164    /// Returns [`crate::Error::Validation`] with
3165    /// [`tenferro_tensor_core::ValidationError::InvalidPermutationLength`],
3166    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`], or
3167    /// [`tenferro_tensor_core::ValidationError::DuplicateAxis`] when `axes` is
3168    /// not a valid permutation of the value rank.
3169    pub fn transpose_view(&self, axes: impl AsRef<[usize]>) -> crate::Result<Self> {
3170        match self {
3171            Self::Tensor(tensor) => TensorOwnedView::from_tensor(Arc::clone(tensor))
3172                .transpose_view(axes)
3173                .map(Self::View),
3174            Self::View(view) => view.transpose_view(axes).map(Self::View),
3175        }
3176    }
3177
3178    /// # Errors
3179    ///
3180    /// Returns [`crate::Error::Validation`] with
3181    /// [`tenferro_tensor_core::ValidationError::NonContiguousViewAsSlice`] when
3182    /// the source is not compact column-major,
3183    /// [`tenferro_tensor_core::ValidationError::ShapeMismatch`] (whose
3184    /// [`tenferro_tensor_core::ShapeMismatch::ReshapeElementCount`] source
3185    /// records the counts) when element counts differ,
3186    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for shape
3187    /// arithmetic overflow, or
3188    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3189    /// reshaped view exceeds the backing buffer.
3190    pub fn reshape_view(
3191        &self,
3192        shape: impl tenferro_tensor_core::IntoShapeVec,
3193    ) -> crate::Result<Self> {
3194        let shape = shape.into_shape_vec();
3195        match self {
3196            Self::Tensor(tensor) => TensorOwnedView::from_tensor(Arc::clone(tensor))
3197                .reshape_view(shape.clone())
3198                .map(Self::View),
3199            Self::View(view) => view.reshape_view(shape).map(Self::View),
3200        }
3201    }
3202
3203    /// # Errors
3204    ///
3205    /// Returns [`crate::Error::Validation`] with
3206    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when a slice
3207    /// vector does not match the value rank,
3208    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when a bound
3209    /// or stride cannot be represented or is invalid,
3210    /// [`tenferro_tensor_core::ValidationError::InvalidSliceStep`] or
3211    /// [`tenferro_tensor_core::ValidationError::InvalidSliceBounds`] for slice
3212    /// parameters, [`tenferro_tensor_core::ValidationError::IntegerOverflow`]
3213    /// for slice arithmetic overflow, or
3214    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3215    /// result exceeds the backing buffer.
3216    pub fn slice_view(&self, config: &SliceConfig) -> crate::Result<Self> {
3217        match self {
3218            Self::Tensor(tensor) => TensorOwnedView::from_tensor(Arc::clone(tensor))
3219                .slice_view(config)
3220                .map(Self::View),
3221            Self::View(view) => view.slice_view(config).map(Self::View),
3222        }
3223    }
3224
3225    /// # Errors
3226    ///
3227    /// Returns [`crate::Error::Validation`] with
3228    /// [`tenferro_tensor_core::ValidationError::RankMismatch`],
3229    /// [`tenferro_tensor_core::ValidationError::AxisOutOfBounds`], or
3230    /// [`tenferro_tensor_core::ValidationError::DuplicateAxis`] for invalid
3231    /// dimension mappings,
3232    /// [`tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch`] for
3233    /// incompatible extents,
3234    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3235    /// result exceeds the backing buffer, or
3236    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
3237    /// arithmetic overflow.
3238    pub fn broadcast_in_dim_view(
3239        &self,
3240        shape: impl tenferro_tensor_core::IntoShapeVec,
3241        dims: impl AsRef<[usize]>,
3242    ) -> crate::Result<Self> {
3243        let shape = shape.into_shape_vec();
3244        let dims = dims.as_ref();
3245        match self {
3246            Self::Tensor(tensor) => TensorOwnedView::from_tensor(Arc::clone(tensor))
3247                .broadcast_in_dim_view(shape.clone(), dims)
3248                .map(Self::View),
3249            Self::View(view) => view.broadcast_in_dim_view(shape, dims).map(Self::View),
3250        }
3251    }
3252}
3253
3254fn tensor_layout(tensor: &Tensor) -> TensorLayout<DynRank> {
3255    match tensor {
3256        Tensor::F32(tensor) => tensor.layout.clone(),
3257        Tensor::F64(tensor) => tensor.layout.clone(),
3258        Tensor::I32(tensor) => tensor.layout.clone(),
3259        Tensor::I64(tensor) => tensor.layout.clone(),
3260        Tensor::Bool(tensor) => tensor.layout.clone(),
3261        Tensor::C32(tensor) => tensor.layout.clone(),
3262        Tensor::C64(tensor) => tensor.layout.clone(),
3263    }
3264}
3265
3266fn tensor_buffer_len(tensor: &Tensor) -> usize {
3267    match tensor {
3268        Tensor::F32(tensor) => buffer_len(&tensor.buffer),
3269        Tensor::F64(tensor) => buffer_len(&tensor.buffer),
3270        Tensor::I32(tensor) => buffer_len(&tensor.buffer),
3271        Tensor::I64(tensor) => buffer_len(&tensor.buffer),
3272        Tensor::Bool(tensor) => buffer_len(&tensor.buffer),
3273        Tensor::C32(tensor) => buffer_len(&tensor.buffer),
3274        Tensor::C64(tensor) => buffer_len(&tensor.buffer),
3275    }
3276}
3277
3278fn buffer_len<T: 'static>(buffer: &Buffer<T>) -> usize {
3279    match buffer {
3280        Buffer::Host(data) => data.len(),
3281        Buffer::Backend(buffer) => buffer.len(),
3282    }
3283}
3284
3285fn tensor_view_with_layout(tensor: &Tensor, layout: TensorLayout<DynRank>) -> TensorView<'_> {
3286    match tensor {
3287        Tensor::F32(tensor) => TensorView::F32(typed_view_with_layout(tensor, layout)),
3288        Tensor::F64(tensor) => TensorView::F64(typed_view_with_layout(tensor, layout)),
3289        Tensor::I32(tensor) => TensorView::I32(typed_view_with_layout(tensor, layout)),
3290        Tensor::I64(tensor) => TensorView::I64(typed_view_with_layout(tensor, layout)),
3291        Tensor::Bool(tensor) => TensorView::Bool(typed_view_with_layout(tensor, layout)),
3292        Tensor::C32(tensor) => TensorView::C32(typed_view_with_layout(tensor, layout)),
3293        Tensor::C64(tensor) => TensorView::C64(typed_view_with_layout(tensor, layout)),
3294    }
3295}
3296
3297fn typed_view_with_layout<T: 'static>(
3298    tensor: &TypedTensor<T>,
3299    layout: TensorLayout<DynRank>,
3300) -> TypedTensorView<'_, T> {
3301    let buffer = match &tensor.buffer {
3302        Buffer::Host(data) => TensorBufferRef::Host(data),
3303        Buffer::Backend(buffer) => TensorBufferRef::Backend(Arc::clone(buffer)),
3304    };
3305    TypedTensorView {
3306        buffer,
3307        layout,
3308        placement: tensor.placement.clone(),
3309    }
3310}
3311
3312/// Wrap an `f64` [`TypedTensor`] into the corresponding [`Tensor`] variant.
3313///
3314/// # Examples
3315///
3316/// ```
3317/// use tenferro_tensor::{Tensor, TypedTensor};
3318///
3319/// let typed = TypedTensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
3320/// let tensor: Tensor = typed.into();
3321/// assert_eq!(tensor.shape(), &[2]);
3322/// ```
3323impl From<TypedTensor<f64>> for Tensor {
3324    fn from(t: TypedTensor<f64>) -> Self {
3325        Tensor::F64(t)
3326    }
3327}
3328
3329/// Wrap an `f32` [`TypedTensor`] into the corresponding [`Tensor`] variant.
3330///
3331/// # Examples
3332///
3333/// ```
3334/// use tenferro_tensor::{Tensor, TypedTensor};
3335///
3336/// let typed = TypedTensor::from_vec_col_major(vec![2], vec![1.0_f32, 2.0]).unwrap();
3337/// let tensor: Tensor = typed.into();
3338/// assert_eq!(tensor.shape(), &[2]);
3339/// ```
3340impl From<TypedTensor<f32>> for Tensor {
3341    fn from(t: TypedTensor<f32>) -> Self {
3342        Tensor::F32(t)
3343    }
3344}
3345
3346/// Wrap an `i64` [`TypedTensor`] into the corresponding [`Tensor`] variant.
3347///
3348/// # Examples
3349///
3350/// ```
3351/// use tenferro_tensor::{DType, Tensor, TypedTensor};
3352///
3353/// let typed = TypedTensor::from_vec_col_major(vec![2], vec![1_i64, 2]).unwrap();
3354/// let tensor: Tensor = typed.into();
3355/// assert_eq!(tensor.dtype(), DType::I64);
3356/// assert_eq!(tensor.shape(), &[2]);
3357/// ```
3358impl From<TypedTensor<i64>> for Tensor {
3359    fn from(t: TypedTensor<i64>) -> Self {
3360        Tensor::I64(t)
3361    }
3362}
3363
3364/// Wrap an `i32` [`TypedTensor`] into the corresponding [`Tensor`] variant.
3365///
3366/// # Examples
3367///
3368/// ```
3369/// use tenferro_tensor::{DType, Tensor, TypedTensor};
3370///
3371/// let typed = TypedTensor::from_vec_col_major(vec![2], vec![1_i32, 2]).unwrap();
3372/// let tensor: Tensor = typed.into();
3373/// assert_eq!(tensor.dtype(), DType::I32);
3374/// assert_eq!(tensor.shape(), &[2]);
3375/// ```
3376impl From<TypedTensor<i32>> for Tensor {
3377    fn from(t: TypedTensor<i32>) -> Self {
3378        Tensor::I32(t)
3379    }
3380}
3381
3382/// Wrap a `bool` [`TypedTensor`] into the corresponding [`Tensor`] variant.
3383///
3384/// # Examples
3385///
3386/// ```
3387/// use tenferro_tensor::{DType, Tensor, TypedTensor};
3388///
3389/// let typed = TypedTensor::from_vec_col_major(vec![2], vec![true, false]).unwrap();
3390/// let tensor: Tensor = typed.into();
3391/// assert_eq!(tensor.dtype(), DType::Bool);
3392/// assert_eq!(tensor.shape(), &[2]);
3393/// ```
3394impl From<TypedTensor<bool>> for Tensor {
3395    fn from(t: TypedTensor<bool>) -> Self {
3396        Tensor::Bool(t)
3397    }
3398}
3399
3400/// Wrap a [`Complex64`] [`TypedTensor`] into the corresponding [`Tensor`]
3401/// variant.
3402///
3403/// # Examples
3404///
3405/// ```
3406/// use num_complex::Complex64;
3407/// use tenferro_tensor::{Tensor, TypedTensor};
3408///
3409/// let typed = TypedTensor::from_vec_col_major(
3410///     vec![1],
3411///     vec![Complex64::new(1.0, 2.0)],
3412/// ).unwrap();
3413/// let tensor: Tensor = typed.into();
3414/// assert_eq!(tensor.shape(), &[1]);
3415/// ```
3416impl From<TypedTensor<Complex<f64>>> for Tensor {
3417    fn from(t: TypedTensor<Complex<f64>>) -> Self {
3418        Tensor::C64(t)
3419    }
3420}
3421
3422/// Wrap a [`Complex32`] [`TypedTensor`] into the corresponding [`Tensor`]
3423/// variant.
3424///
3425/// # Examples
3426///
3427/// ```
3428/// use num_complex::Complex32;
3429/// use tenferro_tensor::{Tensor, TypedTensor};
3430///
3431/// let typed = TypedTensor::from_vec_col_major(
3432///     vec![1],
3433///     vec![Complex32::new(1.0, 2.0)],
3434/// ).unwrap();
3435/// let tensor: Tensor = typed.into();
3436/// assert_eq!(tensor.shape(), &[1]);
3437/// ```
3438impl From<TypedTensor<Complex<f32>>> for Tensor {
3439    fn from(t: TypedTensor<Complex<f32>>) -> Self {
3440        Tensor::C32(t)
3441    }
3442}
3443
3444impl<'a> TensorView<'a> {
3445    /// Create a dynamic `f32` view over compact column-major host data.
3446    ///
3447    /// # Examples
3448    ///
3449    /// ```
3450    /// use tenferro_tensor::{DType, TensorView};
3451    ///
3452    /// let data = [1.0_f32, 2.0];
3453    /// let view = TensorView::f32(&[2], &data)?;
3454    /// assert_eq!(view.dtype(), DType::F32);
3455    /// # Ok::<(), tenferro_tensor::Error>(())
3456    /// ```
3457    /// # Errors
3458    ///
3459    /// Returns [`crate::Error::Validation`] with
3460    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
3461    /// shape or offset arithmetic overflow, or
3462    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3463    /// compact shape reaches beyond `data`.
3464    pub fn f32(shape: &'a [usize], data: &'a [f32]) -> crate::Result<Self> {
3465        Ok(Self::F32(TypedTensorView::from_col_major(shape, data)?))
3466    }
3467
3468    /// Create a dynamic `f64` view over compact column-major host data.
3469    ///
3470    /// # Examples
3471    ///
3472    /// ```
3473    /// use tenferro_tensor::{DType, TensorView};
3474    ///
3475    /// let data = [1.0_f64, 2.0];
3476    /// let view = TensorView::f64(&[2], &data)?;
3477    /// assert_eq!(view.dtype(), DType::F64);
3478    /// # Ok::<(), tenferro_tensor::Error>(())
3479    /// ```
3480    /// # Errors
3481    ///
3482    /// Returns [`crate::Error::Validation`] with
3483    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
3484    /// shape or offset arithmetic overflow, or
3485    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3486    /// compact shape reaches beyond `data`.
3487    pub fn f64(shape: &'a [usize], data: &'a [f64]) -> crate::Result<Self> {
3488        Ok(Self::F64(TypedTensorView::from_col_major(shape, data)?))
3489    }
3490
3491    /// Create a dynamic `i64` view over compact column-major host data.
3492    ///
3493    /// # Examples
3494    ///
3495    /// ```
3496    /// use tenferro_tensor::{DType, TensorView};
3497    ///
3498    /// let data = [1_i64, 2];
3499    /// let view = TensorView::i64(&[2], &data)?;
3500    /// assert_eq!(view.dtype(), DType::I64);
3501    /// # Ok::<(), tenferro_tensor::Error>(())
3502    /// ```
3503    /// # Errors
3504    ///
3505    /// Returns [`crate::Error::Validation`] with
3506    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
3507    /// shape or offset arithmetic overflow, or
3508    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3509    /// compact shape reaches beyond `data`.
3510    pub fn i64(shape: &'a [usize], data: &'a [i64]) -> crate::Result<Self> {
3511        Ok(Self::I64(TypedTensorView::from_col_major(shape, data)?))
3512    }
3513
3514    /// Create a dynamic `i32` view over compact column-major host data.
3515    ///
3516    /// # Examples
3517    ///
3518    /// ```
3519    /// use tenferro_tensor::{DType, TensorView};
3520    ///
3521    /// let data = [1_i32, 2];
3522    /// let view = TensorView::i32(&[2], &data)?;
3523    /// assert_eq!(view.dtype(), DType::I32);
3524    /// # Ok::<(), tenferro_tensor::Error>(())
3525    /// ```
3526    /// # Errors
3527    ///
3528    /// Returns [`crate::Error::Validation`] with
3529    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
3530    /// shape or offset arithmetic overflow, or
3531    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3532    /// compact shape reaches beyond `data`.
3533    pub fn i32(shape: &'a [usize], data: &'a [i32]) -> crate::Result<Self> {
3534        Ok(Self::I32(TypedTensorView::from_col_major(shape, data)?))
3535    }
3536
3537    /// Create a dynamic `bool` view over compact column-major host data.
3538    ///
3539    /// # Examples
3540    ///
3541    /// ```
3542    /// use tenferro_tensor::{DType, TensorView};
3543    ///
3544    /// let data = [true, false];
3545    /// let view = TensorView::bool(&[2], &data)?;
3546    /// assert_eq!(view.dtype(), DType::Bool);
3547    /// # Ok::<(), tenferro_tensor::Error>(())
3548    /// ```
3549    /// # Errors
3550    ///
3551    /// Returns [`crate::Error::Validation`] with
3552    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
3553    /// shape or offset arithmetic overflow, or
3554    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3555    /// compact shape reaches beyond `data`.
3556    pub fn bool(shape: &'a [usize], data: &'a [bool]) -> crate::Result<Self> {
3557        Ok(Self::Bool(TypedTensorView::from_col_major(shape, data)?))
3558    }
3559
3560    /// Create a dynamic `Complex32` view over compact column-major host data.
3561    ///
3562    /// # Examples
3563    ///
3564    /// ```
3565    /// use num_complex::Complex32;
3566    /// use tenferro_tensor::{DType, TensorView};
3567    ///
3568    /// let data = [Complex32::new(1.0, 2.0)];
3569    /// let view = TensorView::c32(&[1], &data)?;
3570    /// assert_eq!(view.dtype(), DType::C32);
3571    /// # Ok::<(), tenferro_tensor::Error>(())
3572    /// ```
3573    /// # Errors
3574    ///
3575    /// Returns [`crate::Error::Validation`] with
3576    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
3577    /// shape or offset arithmetic overflow, or
3578    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3579    /// compact shape reaches beyond `data`.
3580    pub fn c32(shape: &'a [usize], data: &'a [Complex32]) -> crate::Result<Self> {
3581        Ok(Self::C32(TypedTensorView::from_col_major(shape, data)?))
3582    }
3583
3584    /// Create a dynamic `Complex64` view over compact column-major host data.
3585    ///
3586    /// # Examples
3587    ///
3588    /// ```
3589    /// use num_complex::Complex64;
3590    /// use tenferro_tensor::{DType, TensorView};
3591    ///
3592    /// let data = [Complex64::new(1.0, 2.0)];
3593    /// let view = TensorView::c64(&[1], &data)?;
3594    /// assert_eq!(view.dtype(), DType::C64);
3595    /// # Ok::<(), tenferro_tensor::Error>(())
3596    /// ```
3597    /// # Errors
3598    ///
3599    /// Returns [`crate::Error::Validation`] with
3600    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for compact
3601    /// shape or offset arithmetic overflow, or
3602    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
3603    /// compact shape reaches beyond `data`.
3604    pub fn c64(shape: &'a [usize], data: &'a [Complex64]) -> crate::Result<Self> {
3605        Ok(Self::C64(TypedTensorView::from_col_major(shape, data)?))
3606    }
3607
3608    pub fn dtype(&self) -> DType {
3609        match self {
3610            Self::F32(_) => DType::F32,
3611            Self::F64(_) => DType::F64,
3612            Self::I32(_) => DType::I32,
3613            Self::I64(_) => DType::I64,
3614            Self::Bool(_) => DType::Bool,
3615            Self::C32(_) => DType::C32,
3616            Self::C64(_) => DType::C64,
3617        }
3618    }
3619
3620    pub fn shape(&self) -> &[usize] {
3621        match self {
3622            Self::F32(t) => t.shape(),
3623            Self::F64(t) => t.shape(),
3624            Self::I32(t) => t.shape(),
3625            Self::I64(t) => t.shape(),
3626            Self::Bool(t) => t.shape(),
3627            Self::C32(t) => t.shape(),
3628            Self::C64(t) => t.shape(),
3629        }
3630    }
3631
3632    /// Return the placement metadata carried by this borrowed view.
3633    ///
3634    /// # Examples
3635    ///
3636    /// ```
3637    /// use tenferro_tensor::{MemoryKind, TensorView};
3638    ///
3639    /// let view = TensorView::f64(&[1], &[1.0])?;
3640    /// assert_eq!(view.placement().memory_kind, MemoryKind::UnpinnedHost);
3641    /// # Ok::<(), tenferro_tensor::Error>(())
3642    /// ```
3643    pub fn placement(&self) -> &Placement {
3644        match self {
3645            Self::F32(t) => t.placement(),
3646            Self::F64(t) => t.placement(),
3647            Self::I32(t) => t.placement(),
3648            Self::I64(t) => t.placement(),
3649            Self::Bool(t) => t.placement(),
3650            Self::C32(t) => t.placement(),
3651            Self::C64(t) => t.placement(),
3652        }
3653    }
3654
3655    /// Return the physical backend family, when this view is backend-owned.
3656    ///
3657    /// # Examples
3658    ///
3659    /// ```
3660    /// use tenferro_tensor::TensorView;
3661    ///
3662    /// let view = TensorView::f64(&[1], &[1.0])?;
3663    /// assert_eq!(view.backend_family(), None);
3664    /// # Ok::<(), tenferro_tensor::Error>(())
3665    /// ```
3666    pub fn backend_family(&self) -> Option<&'static str> {
3667        match self {
3668            Self::F32(t) => t.backend_buffer().map(|buffer| buffer.backend_family()),
3669            Self::F64(t) => t.backend_buffer().map(|buffer| buffer.backend_family()),
3670            Self::I32(t) => t.backend_buffer().map(|buffer| buffer.backend_family()),
3671            Self::I64(t) => t.backend_buffer().map(|buffer| buffer.backend_family()),
3672            Self::Bool(t) => t.backend_buffer().map(|buffer| buffer.backend_family()),
3673            Self::C32(t) => t.backend_buffer().map(|buffer| buffer.backend_family()),
3674            Self::C64(t) => t.backend_buffer().map(|buffer| buffer.backend_family()),
3675        }
3676    }
3677
3678    /// Return the shared allocation domain, when this view has one.
3679    ///
3680    /// # Examples
3681    ///
3682    /// ```
3683    /// use tenferro_tensor::TensorView;
3684    ///
3685    /// let view = TensorView::f64(&[1], &[1.0])?;
3686    /// assert_eq!(view.allocation_domain(), None);
3687    /// # Ok::<(), tenferro_tensor::Error>(())
3688    /// ```
3689    pub fn allocation_domain(&self) -> Option<AllocationDomainId> {
3690        match self {
3691            Self::F32(t) => t
3692                .backend_buffer()
3693                .and_then(|buffer| buffer.allocation_domain()),
3694            Self::F64(t) => t
3695                .backend_buffer()
3696                .and_then(|buffer| buffer.allocation_domain()),
3697            Self::I32(t) => t
3698                .backend_buffer()
3699                .and_then(|buffer| buffer.allocation_domain()),
3700            Self::I64(t) => t
3701                .backend_buffer()
3702                .and_then(|buffer| buffer.allocation_domain()),
3703            Self::Bool(t) => t
3704                .backend_buffer()
3705                .and_then(|buffer| buffer.allocation_domain()),
3706            Self::C32(t) => t
3707                .backend_buffer()
3708                .and_then(|buffer| buffer.allocation_domain()),
3709            Self::C64(t) => t
3710                .backend_buffer()
3711                .and_then(|buffer| buffer.allocation_domain()),
3712        }
3713    }
3714
3715    /// Return strides in element units.
3716    pub fn strides(&self) -> &[isize] {
3717        match self {
3718            Self::F32(t) => t.strides(),
3719            Self::F64(t) => t.strides(),
3720            Self::I32(t) => t.strides(),
3721            Self::I64(t) => t.strides(),
3722            Self::Bool(t) => t.strides(),
3723            Self::C32(t) => t.strides(),
3724            Self::C64(t) => t.strides(),
3725        }
3726    }
3727
3728    /// Return the physical element offset.
3729    pub fn offset(&self) -> isize {
3730        match self {
3731            Self::F32(t) => t.offset(),
3732            Self::F64(t) => t.offset(),
3733            Self::I32(t) => t.offset(),
3734            Self::I64(t) => t.offset(),
3735            Self::Bool(t) => t.offset(),
3736            Self::C32(t) => t.offset(),
3737            Self::C64(t) => t.offset(),
3738        }
3739    }
3740
3741    /// Compute the physical element offset for a logical index.
3742    /// # Errors
3743    ///
3744    /// Returns [`crate::Error::Validation`] with
3745    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
3746    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
3747    /// when an index is outside its axis extent, or
3748    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
3749    /// arithmetic overflows.
3750    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
3751        match self {
3752            Self::F32(t) => t.layout_linear_offset(indices),
3753            Self::F64(t) => t.layout_linear_offset(indices),
3754            Self::I32(t) => t.layout_linear_offset(indices),
3755            Self::I64(t) => t.layout_linear_offset(indices),
3756            Self::Bool(t) => t.layout_linear_offset(indices),
3757            Self::C32(t) => t.layout_linear_offset(indices),
3758            Self::C64(t) => t.layout_linear_offset(indices),
3759        }
3760    }
3761
3762    /// Return whether this view is compact column-major.
3763    /// # Errors
3764    ///
3765    /// Returns [`crate::Error::Validation`] with
3766    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
3767    /// compactness arithmetic overflows.
3768    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
3769        match self {
3770            Self::F32(t) => t.is_col_major_contiguous(),
3771            Self::F64(t) => t.is_col_major_contiguous(),
3772            Self::I32(t) => t.is_col_major_contiguous(),
3773            Self::I64(t) => t.is_col_major_contiguous(),
3774            Self::Bool(t) => t.is_col_major_contiguous(),
3775            Self::C32(t) => t.is_col_major_contiguous(),
3776            Self::C64(t) => t.is_col_major_contiguous(),
3777        }
3778    }
3779
3780    /// Return a compact string summary of this view's layout metadata.
3781    pub fn layout_summary(&self) -> String {
3782        layout_summary(self.shape(), self.strides(), self.offset())
3783    }
3784
3785    /// Assert this view is compact column-major.
3786    /// # Errors
3787    ///
3788    /// Returns [`crate::Error::Validation`] with
3789    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
3790    /// compactness arithmetic overflows, or
3791    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
3792    /// view is not compact column-major.
3793    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
3794        assert_layout_col_major_contiguous(
3795            self.is_col_major_contiguous()?,
3796            self.shape(),
3797            self.strides(),
3798            self.offset(),
3799            "TensorView::assert_col_major_contiguous",
3800        )
3801    }
3802}
3803
3804macro_rules! tensor_view_mut_constructor {
3805    ($name:ident, $variant:ident, $scalar:ty) => {
3806        /// Create a dynamic mutable view over compact column-major host data.
3807        ///
3808        /// # Errors
3809        ///
3810        /// Returns [`crate::Error::Validation`] with
3811        /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
3812        /// compact layout arithmetic overflows, or
3813        /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
3814        /// requested shape exceeds `data`.
3815        pub fn $name(shape: &'a [usize], data: &'a mut [$scalar]) -> crate::Result<Self> {
3816            Ok(Self::$variant(TypedTensorViewMut::from_col_major(
3817                shape, data,
3818            )?))
3819        }
3820    };
3821}
3822
3823impl<'a> TensorViewMut<'a> {
3824    tensor_view_mut_constructor!(f32, F32, f32);
3825    tensor_view_mut_constructor!(f64, F64, f64);
3826    tensor_view_mut_constructor!(i32, I32, i32);
3827    tensor_view_mut_constructor!(i64, I64, i64);
3828    tensor_view_mut_constructor!(bool, Bool, bool);
3829    tensor_view_mut_constructor!(c32, C32, Complex32);
3830    tensor_view_mut_constructor!(c64, C64, Complex64);
3831
3832    pub fn dtype(&self) -> DType {
3833        match self {
3834            Self::F32(_) => DType::F32,
3835            Self::F64(_) => DType::F64,
3836            Self::I32(_) => DType::I32,
3837            Self::I64(_) => DType::I64,
3838            Self::Bool(_) => DType::Bool,
3839            Self::C32(_) => DType::C32,
3840            Self::C64(_) => DType::C64,
3841        }
3842    }
3843
3844    pub fn shape(&self) -> &[usize] {
3845        match self {
3846            Self::F32(t) => t.shape(),
3847            Self::F64(t) => t.shape(),
3848            Self::I32(t) => t.shape(),
3849            Self::I64(t) => t.shape(),
3850            Self::Bool(t) => t.shape(),
3851            Self::C32(t) => t.shape(),
3852            Self::C64(t) => t.shape(),
3853        }
3854    }
3855
3856    pub fn strides(&self) -> &[isize] {
3857        match self {
3858            Self::F32(t) => t.strides(),
3859            Self::F64(t) => t.strides(),
3860            Self::I32(t) => t.strides(),
3861            Self::I64(t) => t.strides(),
3862            Self::Bool(t) => t.strides(),
3863            Self::C32(t) => t.strides(),
3864            Self::C64(t) => t.strides(),
3865        }
3866    }
3867
3868    pub fn offset(&self) -> isize {
3869        match self {
3870            Self::F32(t) => t.offset(),
3871            Self::F64(t) => t.offset(),
3872            Self::I32(t) => t.offset(),
3873            Self::I64(t) => t.offset(),
3874            Self::Bool(t) => t.offset(),
3875            Self::C32(t) => t.offset(),
3876            Self::C64(t) => t.offset(),
3877        }
3878    }
3879
3880    /// Compute the physical element offset for a logical index.
3881    ///
3882    /// # Errors
3883    ///
3884    /// Returns [`crate::Error::Validation`] with
3885    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
3886    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
3887    /// when an index is outside its axis extent, or
3888    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
3889    /// arithmetic overflows.
3890    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
3891        match self {
3892            Self::F32(t) => t.layout_linear_offset(indices),
3893            Self::F64(t) => t.layout_linear_offset(indices),
3894            Self::I32(t) => t.layout_linear_offset(indices),
3895            Self::I64(t) => t.layout_linear_offset(indices),
3896            Self::Bool(t) => t.layout_linear_offset(indices),
3897            Self::C32(t) => t.layout_linear_offset(indices),
3898            Self::C64(t) => t.layout_linear_offset(indices),
3899        }
3900    }
3901
3902    /// Return whether this view is compact column-major.
3903    ///
3904    /// # Errors
3905    ///
3906    /// Returns [`crate::Error::Validation`] with
3907    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
3908    /// compactness arithmetic overflows.
3909    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
3910        match self {
3911            Self::F32(t) => t.is_col_major_contiguous(),
3912            Self::F64(t) => t.is_col_major_contiguous(),
3913            Self::I32(t) => t.is_col_major_contiguous(),
3914            Self::I64(t) => t.is_col_major_contiguous(),
3915            Self::Bool(t) => t.is_col_major_contiguous(),
3916            Self::C32(t) => t.is_col_major_contiguous(),
3917            Self::C64(t) => t.is_col_major_contiguous(),
3918        }
3919    }
3920
3921    pub fn layout_summary(&self) -> String {
3922        layout_summary(self.shape(), self.strides(), self.offset())
3923    }
3924
3925    /// Assert this view is compact column-major.
3926    ///
3927    /// # Errors
3928    ///
3929    /// Returns [`crate::Error::Validation`] with
3930    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
3931    /// compactness arithmetic overflows, or
3932    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
3933    /// view is not compact column-major.
3934    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
3935        assert_layout_col_major_contiguous(
3936            self.is_col_major_contiguous()?,
3937            self.shape(),
3938            self.strides(),
3939            self.offset(),
3940            "TensorViewMut::assert_col_major_contiguous",
3941        )
3942    }
3943
3944    pub fn as_read_only(&self) -> TensorView<'_> {
3945        match self {
3946            Self::F32(t) => TensorView::F32(t.as_read_only()),
3947            Self::F64(t) => TensorView::F64(t.as_read_only()),
3948            Self::I32(t) => TensorView::I32(t.as_read_only()),
3949            Self::I64(t) => TensorView::I64(t.as_read_only()),
3950            Self::Bool(t) => TensorView::Bool(t.as_read_only()),
3951            Self::C32(t) => TensorView::C32(t.as_read_only()),
3952            Self::C64(t) => TensorView::C64(t.as_read_only()),
3953        }
3954    }
3955}
3956
3957impl<'a> TensorRead<'a> {
3958    pub fn from_tensor(tensor: &'a Tensor) -> Self {
3959        Self::Tensor(tensor)
3960    }
3961
3962    pub fn from_view(view: TensorView<'a>) -> Self {
3963        Self::View(view)
3964    }
3965
3966    /// Convert this read target into a dtype-erased tensor view.
3967    ///
3968    /// Owned tensors are borrowed without copying their storage. Existing
3969    /// views preserve their layout and placement metadata.
3970    pub fn tensor_view(self) -> TensorView<'a> {
3971        match self {
3972            Self::Tensor(tensor) => tensor_view_with_layout(tensor, tensor_layout(tensor)),
3973            Self::View(view) => view,
3974        }
3975    }
3976
3977    pub fn dtype(&self) -> DType {
3978        match self {
3979            Self::Tensor(tensor) => tensor.dtype(),
3980            Self::View(view) => view.dtype(),
3981        }
3982    }
3983
3984    pub fn shape(&self) -> &[usize] {
3985        match self {
3986            Self::Tensor(tensor) => tensor.shape(),
3987            Self::View(view) => view.shape(),
3988        }
3989    }
3990
3991    /// Return the placement metadata carried by this read target.
3992    ///
3993    /// # Examples
3994    ///
3995    /// ```
3996    /// use tenferro_tensor::{MemoryKind, Tensor, TensorRead};
3997    ///
3998    /// let tensor = Tensor::from_vec_col_major(vec![1], vec![1.0_f64])?;
3999    /// let read = TensorRead::from_tensor(&tensor);
4000    /// assert_eq!(read.placement().memory_kind, MemoryKind::UnpinnedHost);
4001    /// # Ok::<(), tenferro_tensor::Error>(())
4002    /// ```
4003    pub fn placement(&self) -> &Placement {
4004        match self {
4005            Self::Tensor(tensor) => tensor.placement(),
4006            Self::View(view) => view.placement(),
4007        }
4008    }
4009
4010    /// Return the physical backend family of this read target, when backend-owned.
4011    ///
4012    /// # Examples
4013    ///
4014    /// ```
4015    /// use tenferro_tensor::{Tensor, TensorRead};
4016    ///
4017    /// let tensor = Tensor::from_vec_col_major(vec![1], vec![1.0_f64])?;
4018    /// assert_eq!(TensorRead::from_tensor(&tensor).backend_family(), None);
4019    /// # Ok::<(), tenferro_tensor::Error>(())
4020    /// ```
4021    pub fn backend_family(&self) -> Option<&'static str> {
4022        match self {
4023            Self::Tensor(tensor) => match tensor {
4024                Tensor::F32(t) => buffer_backend_family(t.buffer()),
4025                Tensor::F64(t) => buffer_backend_family(t.buffer()),
4026                Tensor::I32(t) => buffer_backend_family(t.buffer()),
4027                Tensor::I64(t) => buffer_backend_family(t.buffer()),
4028                Tensor::Bool(t) => buffer_backend_family(t.buffer()),
4029                Tensor::C32(t) => buffer_backend_family(t.buffer()),
4030                Tensor::C64(t) => buffer_backend_family(t.buffer()),
4031            },
4032            Self::View(view) => view.backend_family(),
4033        }
4034    }
4035
4036    /// Return the shared allocation domain of this read target, when present.
4037    ///
4038    /// # Examples
4039    ///
4040    /// ```
4041    /// use tenferro_tensor::{Tensor, TensorRead};
4042    ///
4043    /// let tensor = Tensor::from_vec_col_major(vec![1], vec![1.0_f64])?;
4044    /// assert_eq!(TensorRead::from_tensor(&tensor).allocation_domain(), None);
4045    /// # Ok::<(), tenferro_tensor::Error>(())
4046    /// ```
4047    pub fn allocation_domain(&self) -> Option<AllocationDomainId> {
4048        match self {
4049            Self::Tensor(tensor) => match tensor {
4050                Tensor::F32(t) => t.allocation_domain(),
4051                Tensor::F64(t) => t.allocation_domain(),
4052                Tensor::I32(t) => t.allocation_domain(),
4053                Tensor::I64(t) => t.allocation_domain(),
4054                Tensor::Bool(t) => t.allocation_domain(),
4055                Tensor::C32(t) => t.allocation_domain(),
4056                Tensor::C64(t) => t.allocation_domain(),
4057            },
4058            Self::View(view) => view.allocation_domain(),
4059        }
4060    }
4061
4062    /// # Errors
4063    ///
4064    /// Returns [`crate::Error::Validation`] with
4065    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
4066    /// column-major stride arithmetic overflows.
4067    pub fn strides(&self) -> crate::Result<Vec<isize>> {
4068        match self {
4069            Self::Tensor(tensor) => col_major_strides(tensor.shape()),
4070            Self::View(view) => Ok(view.strides().to_vec()),
4071        }
4072    }
4073
4074    pub fn offset(&self) -> isize {
4075        match self {
4076            Self::Tensor(_) => 0,
4077            Self::View(view) => view.offset(),
4078        }
4079    }
4080
4081    /// # Errors
4082    ///
4083    /// Returns [`crate::Error::Validation`] with
4084    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
4085    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
4086    /// when an index is outside its axis extent, or
4087    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
4088    /// arithmetic overflows.
4089    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
4090        match self {
4091            Self::Tensor(tensor) => tensor.layout_linear_offset(indices),
4092            Self::View(view) => view.layout_linear_offset(indices),
4093        }
4094    }
4095
4096    /// # Errors
4097    ///
4098    /// Returns [`crate::Error::Validation`] with
4099    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
4100    /// compactness arithmetic overflows.
4101    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
4102        match self {
4103            Self::Tensor(tensor) => tensor.is_col_major_contiguous(),
4104            Self::View(view) => view.is_col_major_contiguous(),
4105        }
4106    }
4107
4108    pub fn layout_summary(&self) -> String {
4109        let strides = match self.strides() {
4110            Ok(strides) => strides,
4111            Err(err) => return format!("layout unavailable: {err}"),
4112        };
4113        layout_summary(self.shape(), &strides, self.offset())
4114    }
4115
4116    /// # Errors
4117    ///
4118    /// Returns [`crate::Error::Validation`] with
4119    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
4120    /// compactness arithmetic overflows, or
4121    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
4122    /// view is not compact column-major.
4123    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
4124        let strides = self.strides()?;
4125        assert_layout_col_major_contiguous(
4126            self.is_col_major_contiguous()?,
4127            self.shape(),
4128            &strides,
4129            self.offset(),
4130            "TensorRead::assert_col_major_contiguous",
4131        )
4132    }
4133
4134    pub fn as_tensor(&self) -> Option<&'a Tensor> {
4135        match self {
4136            Self::Tensor(tensor) => Some(*tensor),
4137            Self::View(_) => None,
4138        }
4139    }
4140}
4141
4142fn buffer_backend_family<T: 'static>(buffer: &Buffer<T>) -> Option<&'static str> {
4143    match buffer {
4144        Buffer::Host(_) => None,
4145        Buffer::Backend(buffer) => Some(buffer.backend_family()),
4146    }
4147}
4148
4149impl<'a> TensorWrite<'a> {
4150    pub fn from_tensor(tensor: &'a mut Tensor) -> Self {
4151        Self::Tensor(tensor)
4152    }
4153
4154    pub fn from_view(view: TensorViewMut<'a>) -> Self {
4155        Self::View(view)
4156    }
4157
4158    /// Borrow this writable target as a read-only tensor input.
4159    ///
4160    /// This is useful for explicit read-modify-write kernels such as
4161    /// accumulation updates. The returned view borrows through `&self`, so it
4162    /// cannot outlive the current read-only borrow of the writable target.
4163    ///
4164    /// # Examples
4165    ///
4166    /// ```rust
4167    /// use tenferro_tensor::{DType, Tensor, TensorWrite};
4168    ///
4169    /// let mut tensor = Tensor::from_vec_col_major(vec![1], vec![2.0_f64])?;
4170    /// let write = TensorWrite::from_tensor(&mut tensor);
4171    /// let read = write.as_read();
4172    /// assert_eq!(read.dtype(), DType::F64);
4173    /// # Ok::<(), tenferro_tensor::Error>(())
4174    /// ```
4175    pub fn as_read(&self) -> TensorRead<'_> {
4176        match self {
4177            Self::Tensor(tensor) => TensorRead::from_tensor(tensor),
4178            Self::View(view) => TensorRead::from_view(view.as_read_only()),
4179        }
4180    }
4181
4182    pub fn dtype(&self) -> DType {
4183        match self {
4184            Self::Tensor(tensor) => tensor.dtype(),
4185            Self::View(view) => view.dtype(),
4186        }
4187    }
4188
4189    pub fn shape(&self) -> &[usize] {
4190        match self {
4191            Self::Tensor(tensor) => tensor.shape(),
4192            Self::View(view) => view.shape(),
4193        }
4194    }
4195
4196    /// # Errors
4197    ///
4198    /// Returns [`crate::Error::Validation`] with
4199    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
4200    /// column-major stride arithmetic overflows.
4201    pub fn strides(&self) -> crate::Result<Vec<isize>> {
4202        match self {
4203            Self::Tensor(tensor) => col_major_strides(tensor.shape()),
4204            Self::View(view) => Ok(view.strides().to_vec()),
4205        }
4206    }
4207
4208    pub fn offset(&self) -> isize {
4209        match self {
4210            Self::Tensor(_) => 0,
4211            Self::View(view) => view.offset(),
4212        }
4213    }
4214
4215    /// # Errors
4216    ///
4217    /// Returns [`crate::Error::Validation`] with
4218    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
4219    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
4220    /// when an index is outside its axis extent, or
4221    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
4222    /// arithmetic overflows.
4223    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
4224        match self {
4225            Self::Tensor(tensor) => tensor.layout_linear_offset(indices),
4226            Self::View(view) => view.layout_linear_offset(indices),
4227        }
4228    }
4229
4230    /// # Errors
4231    ///
4232    /// Returns [`crate::Error::Validation`] with
4233    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
4234    /// compactness arithmetic overflows.
4235    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
4236        match self {
4237            Self::Tensor(tensor) => tensor.is_col_major_contiguous(),
4238            Self::View(view) => view.is_col_major_contiguous(),
4239        }
4240    }
4241
4242    pub fn layout_summary(&self) -> String {
4243        let strides = match self.strides() {
4244            Ok(strides) => strides,
4245            Err(err) => return format!("layout unavailable: {err}"),
4246        };
4247        layout_summary(self.shape(), &strides, self.offset())
4248    }
4249
4250    /// # Errors
4251    ///
4252    /// Returns [`crate::Error::Validation`] with
4253    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
4254    /// compactness arithmetic overflows, or
4255    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
4256    /// view is not compact column-major.
4257    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
4258        let strides = self.strides()?;
4259        assert_layout_col_major_contiguous(
4260            self.is_col_major_contiguous()?,
4261            self.shape(),
4262            &strides,
4263            self.offset(),
4264            "TensorWrite::assert_col_major_contiguous",
4265        )
4266    }
4267}
4268
4269/// Column-major strides derived from a shape.
4270///
4271/// # Examples
4272///
4273/// ```rust
4274/// use tenferro_tensor::col_major_strides;
4275///
4276/// assert_eq!(col_major_strides(&[2, 3])?, vec![1, 2]);
4277/// # Ok::<(), tenferro_tensor::Error>(())
4278/// ```
4279/// # Errors
4280///
4281/// Returns [`crate::Error::Validation`] with
4282/// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when a
4283/// column-major stride product overflows.
4284pub fn col_major_strides(shape: &[usize]) -> crate::Result<Vec<isize>> {
4285    let mut strides = Vec::with_capacity(shape.len());
4286    let mut stride = 1isize;
4287    for &extent in shape {
4288        strides.push(stride);
4289        let extent = isize::try_from(extent).map_err(|_| {
4290            crate::Error::validation("col_major_strides", ValidationError::IntegerOverflow)
4291        })?;
4292        stride = stride.checked_mul(extent).ok_or_else(|| {
4293            crate::Error::validation("col_major_strides", ValidationError::IntegerOverflow)
4294        })?;
4295    }
4296    Ok(strides)
4297}
4298
4299fn try_linear_offset_for_shape(
4300    shape: &[usize],
4301    indices: &[usize],
4302    op: &'static str,
4303) -> crate::Result<usize> {
4304    if indices.len() != shape.len() {
4305        return Err(crate::Error::validation(
4306            op,
4307            ValidationError::RankMismatch {
4308                expected: shape.len(),
4309                actual: indices.len(),
4310            },
4311        ));
4312    }
4313    let mut offset = 0usize;
4314    let mut stride = 1usize;
4315    for (axis, (&idx, &extent)) in indices.iter().zip(shape).enumerate() {
4316        if idx >= extent {
4317            return Err(crate::Error::invalid_argument(
4318                op,
4319                "index",
4320                format!("index {idx} out of bounds for axis {axis} extent {extent}"),
4321            ));
4322        }
4323        offset =
4324            offset
4325                .checked_add(idx.checked_mul(stride).ok_or_else(|| {
4326                    crate::Error::validation(op, ValidationError::IntegerOverflow)
4327                })?)
4328                .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))?;
4329        stride = stride
4330            .checked_mul(extent)
4331            .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))?;
4332    }
4333    Ok(offset)
4334}
4335
4336fn checked_view_offset_result(
4337    shape: &[usize],
4338    strides: &[isize],
4339    base_offset: isize,
4340    indices: &[usize],
4341    op: &'static str,
4342) -> crate::Result<usize> {
4343    if indices.len() != shape.len() {
4344        return Err(crate::Error::validation(
4345            op,
4346            ValidationError::RankMismatch {
4347                expected: shape.len(),
4348                actual: indices.len(),
4349            },
4350        ));
4351    }
4352    for (axis, (&index, &extent)) in indices.iter().zip(shape).enumerate() {
4353        if index >= extent {
4354            return Err(crate::Error::invalid_argument(
4355                op,
4356                "index",
4357                format!("index {index} out of bounds for axis {axis} extent {extent}"),
4358            ));
4359        }
4360    }
4361    checked_view_offset(shape, strides, base_offset, indices)
4362        .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))
4363}
4364
4365fn layout_summary(shape: &[usize], strides: &[isize], offset: isize) -> String {
4366    format!("shape={shape:?} strides={strides:?} offset={offset}")
4367}
4368
4369fn assert_layout_col_major_contiguous(
4370    is_contiguous: bool,
4371    shape: &[usize],
4372    strides: &[isize],
4373    offset: isize,
4374    op: &'static str,
4375) -> crate::Result<()> {
4376    if is_contiguous {
4377        Ok(())
4378    } else {
4379        Err(crate::Error::invalid_argument(
4380            op,
4381            "layout",
4382            format!(
4383                "expected compact column-major layout, got {}",
4384                layout_summary(shape, strides, offset)
4385            ),
4386        ))
4387    }
4388}
4389
4390fn try_shape_product(shape: &[usize], op: &'static str) -> crate::Result<usize> {
4391    shape.iter().try_fold(1usize, |acc, &dim| {
4392        acc.checked_mul(dim)
4393            .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))
4394    })
4395}
4396
4397fn try_checked_shape_len(shape: &[usize], data_len: usize, op: &'static str) -> crate::Result<()> {
4398    let n = try_shape_product(shape, op)?;
4399    if data_len != n {
4400        return Err(crate::Error::validation(
4401            op,
4402            ValidationError::ShapeDataLengthMismatch {
4403                expected: n,
4404                actual: data_len,
4405            },
4406        ));
4407    }
4408    Ok(())
4409}
4410
4411fn try_compact_layout<R: TensorRank>(
4412    shape: impl Into<R::Shape>,
4413    op: &'static str,
4414) -> crate::Result<TensorLayout<R>> {
4415    TensorLayout::compact(shape.into()).map_err(|err| tensor_layout_error(op, err))
4416}
4417
4418fn tensor_layout_error(
4419    op: &'static str,
4420    err: tenferro_tensor_core::ValidationError,
4421) -> crate::Error {
4422    crate::Error::validation(op, err)
4423}
4424
4425fn checked_view_element_count(shape: &[usize], op: &'static str) -> crate::Result<usize> {
4426    if shape.contains(&0) {
4427        return Ok(0);
4428    }
4429    shape.iter().try_fold(1usize, |product, &dim| {
4430        product
4431            .checked_mul(dim)
4432            .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))
4433    })
4434}
4435
4436fn checked_view_offset(
4437    shape: &[usize],
4438    strides: &[isize],
4439    base_offset: isize,
4440    indices: &[usize],
4441) -> Option<usize> {
4442    if indices.len() != shape.len() {
4443        return None;
4444    }
4445
4446    let mut offset = base_offset;
4447    for ((&index, &extent), &stride) in indices.iter().zip(shape).zip(strides) {
4448        if index >= extent {
4449            return None;
4450        }
4451        let index = isize::try_from(index).ok()?;
4452        let delta = index.checked_mul(stride)?;
4453        offset = offset.checked_add(delta)?;
4454    }
4455
4456    usize::try_from(offset).ok()
4457}
4458
4459fn reachable_layout_span(
4460    shape: &[usize],
4461    strides: &[isize],
4462    offset: isize,
4463) -> crate::Result<Option<(usize, usize)>> {
4464    if shape.contains(&0) {
4465        return Ok(None);
4466    }
4467
4468    let mut min_offset = offset;
4469    let mut max_offset = offset;
4470    for (&extent, &stride) in shape.iter().zip(strides) {
4471        let steps = isize::try_from(extent.saturating_sub(1)).map_err(|_| {
4472            crate::Error::validation(
4473                "TypedTensorViewMut::try_multi_slice_mut",
4474                ValidationError::IntegerOverflow,
4475            )
4476        })?;
4477        let end = stride.checked_mul(steps).ok_or_else(|| {
4478            crate::Error::validation(
4479                "TypedTensorViewMut::try_multi_slice_mut",
4480                ValidationError::IntegerOverflow,
4481            )
4482        })?;
4483        let (axis_min, axis_max) = if end < 0 { (end, 0) } else { (0, end) };
4484        min_offset = min_offset.checked_add(axis_min).ok_or_else(|| {
4485            crate::Error::validation(
4486                "TypedTensorViewMut::try_multi_slice_mut",
4487                ValidationError::IntegerOverflow,
4488            )
4489        })?;
4490        max_offset = max_offset.checked_add(axis_max).ok_or_else(|| {
4491            crate::Error::validation(
4492                "TypedTensorViewMut::try_multi_slice_mut",
4493                ValidationError::IntegerOverflow,
4494            )
4495        })?;
4496    }
4497
4498    let min_offset = usize::try_from(min_offset).map_err(|_| {
4499        crate::Error::invalid_argument(
4500            "TypedTensorViewMut::try_multi_slice_mut",
4501            "layout",
4502            "minimum reachable offset is negative",
4503        )
4504    })?;
4505    let max_offset = usize::try_from(max_offset).map_err(|_| {
4506        crate::Error::invalid_argument(
4507            "TypedTensorViewMut::try_multi_slice_mut",
4508            "layout",
4509            "maximum reachable offset is negative",
4510        )
4511    })?;
4512    Ok(Some((min_offset, max_offset)))
4513}
4514
4515fn split_two_mut_ranges<T>(
4516    data: &mut [T],
4517    first: (usize, usize),
4518    second: (usize, usize),
4519) -> Option<(&mut [T], &mut [T])> {
4520    if first.1 < second.0 {
4521        let (_, after_first_start) = data.split_at_mut(first.0);
4522        let (first_slice, after_first) = after_first_start.split_at_mut(first.1 - first.0 + 1);
4523        let (_, after_gap) = after_first.split_at_mut(second.0 - first.1 - 1);
4524        let (second_slice, _) = after_gap.split_at_mut(second.1 - second.0 + 1);
4525        Some((first_slice, second_slice))
4526    } else if second.1 < first.0 {
4527        let (_, after_second_start) = data.split_at_mut(second.0);
4528        let (second_slice, after_second) = after_second_start.split_at_mut(second.1 - second.0 + 1);
4529        let (_, after_gap) = after_second.split_at_mut(first.0 - second.1 - 1);
4530        let (first_slice, _) = after_gap.split_at_mut(first.1 - first.0 + 1);
4531        Some((first_slice, second_slice))
4532    } else {
4533        None
4534    }
4535}
4536
4537fn adjusted_view_offset(offset: isize, span_start: usize) -> crate::Result<isize> {
4538    let span_start = isize::try_from(span_start).map_err(|_| {
4539        crate::Error::validation(
4540            "TypedTensorViewMut::try_multi_slice_mut",
4541            ValidationError::IntegerOverflow,
4542        )
4543    })?;
4544    offset.checked_sub(span_start).ok_or_else(|| {
4545        crate::Error::validation(
4546            "TypedTensorViewMut::try_multi_slice_mut",
4547            ValidationError::IntegerOverflow,
4548        )
4549    })
4550}
4551
4552fn view_mut_from_layout_and_slice<'a, T: 'static, R: TensorRank>(
4553    layout: &TensorLayout<R>,
4554    offset: isize,
4555    data: &'a mut [T],
4556    placement: Placement,
4557) -> crate::Result<TypedTensorViewMut<'a, T, R>> {
4558    let shape = R::shape_from_vec(shape_vec(layout.shape()))
4559        .map_err(|err| tensor_layout_error("TypedTensorViewMut::try_multi_slice_mut", err))?;
4560    let strides = R::strides_from_vec(stride_vec(layout.strides()))
4561        .map_err(|err| tensor_layout_error("TypedTensorViewMut::try_multi_slice_mut", err))?;
4562    TypedTensorViewMut::from_buffer_ref_mut(
4563        shape,
4564        strides,
4565        offset,
4566        TensorBufferRefMut::Host(data),
4567        placement,
4568        "TypedTensorViewMut::try_multi_slice_mut",
4569    )
4570}
4571
4572fn contiguous_layout_slice<'a, T, R: TensorRank>(
4573    layout: &TensorLayout<R>,
4574    data: &'a [T],
4575    op: &'static str,
4576) -> crate::Result<&'a [T]> {
4577    if !layout
4578        .is_compact_col_major()
4579        .map_err(|err| tensor_layout_error(op, err))?
4580    {
4581        return Err(crate::Error::invalid_argument(
4582            op,
4583            "layout",
4584            "view is not contiguous column-major",
4585        ));
4586    }
4587    let len = checked_view_element_count(layout.shape(), op)?;
4588    let start = usize::try_from(layout.offset())
4589        .map_err(|_| crate::Error::invalid_argument(op, "layout", "view offset is negative"))?;
4590    let end = start
4591        .checked_add(len)
4592        .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))?;
4593    data.get(start..end)
4594        .ok_or_else(|| crate::Error::validation(op, ValidationError::ViewOutOfBounds))
4595}
4596
4597fn relaxed_col_major_contiguous(
4598    shape: &[usize],
4599    strides: &[isize],
4600    op: &'static str,
4601) -> crate::Result<bool> {
4602    if shape.contains(&0) {
4603        return Ok(true);
4604    }
4605
4606    let mut expected = 1isize;
4607    for (&extent, &stride) in shape.iter().zip(strides) {
4608        if extent <= 1 {
4609            continue;
4610        }
4611        if stride != expected {
4612            return Ok(false);
4613        }
4614        let extent = isize::try_from(extent)
4615            .map_err(|_| crate::Error::validation(op, ValidationError::IntegerOverflow))?;
4616        expected = expected
4617            .checked_mul(extent)
4618            .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))?;
4619    }
4620    Ok(true)
4621}
4622
4623fn reshape_layout_dyn<R: TensorRank>(
4624    layout: &TensorLayout<R>,
4625    shape: &[usize],
4626    buffer_len: usize,
4627    op: &'static str,
4628) -> crate::Result<TensorLayout<DynRank>> {
4629    match layout.reshape_view_as::<DynRank>(shape_vec(shape), buffer_len) {
4630        Ok(layout) => Ok(layout),
4631        Err(err) => {
4632            if !relaxed_col_major_contiguous(layout.shape(), layout.strides(), op)? {
4633                return Err(tensor_layout_error(op, err));
4634            }
4635            let from = checked_view_element_count(layout.shape(), op)?;
4636            let to = checked_view_element_count(shape, op)?;
4637            if from != to {
4638                return Err(tensor_layout_error(
4639                    op,
4640                    tenferro_tensor_core::ShapeMismatch::ReshapeElementCount { from, to }.into(),
4641                ));
4642            }
4643            TensorLayout::<DynRank>::compact(shape_vec(shape))
4644                .and_then(|compact| {
4645                    TensorLayout::from_parts(
4646                        shape_vec(compact.shape()),
4647                        stride_vec(compact.strides()),
4648                        layout.offset(),
4649                        buffer_len,
4650                    )
4651                })
4652                .map_err(|err| tensor_layout_error(op, err))
4653        }
4654    }
4655}
4656
4657fn core_slice_specs(
4658    slices: &[StridedSliceSpec],
4659    shape: &[usize],
4660    op: &'static str,
4661) -> crate::Result<Vec<CoreSliceSpec>> {
4662    if slices.len() != shape.len() {
4663        return Err(crate::Error::validation(
4664            op,
4665            ValidationError::RankMismatch {
4666                expected: shape.len(),
4667                actual: slices.len(),
4668            },
4669        ));
4670    }
4671
4672    let mut specs = Vec::with_capacity(slices.len());
4673    for (slice, &axis_len) in slices.iter().zip(shape) {
4674        specs.push(core_slice_spec(*slice, axis_len, op)?);
4675    }
4676    Ok(specs)
4677}
4678
4679fn core_slice_spec(
4680    slice: StridedSliceSpec,
4681    axis_len: usize,
4682    op: &'static str,
4683) -> crate::Result<CoreSliceSpec> {
4684    if slice.step() == 0 {
4685        return Err(crate::Error::validation(
4686            op,
4687            ValidationError::InvalidSliceStep { step: slice.step() },
4688        ));
4689    }
4690
4691    let start = normalize_strided_bound(slice.start(), axis_len, op, "slice start")?;
4692    let end = match slice.end() {
4693        Some(end) => normalize_strided_bound(end, axis_len, op, "slice end")?,
4694        None => isize::try_from(axis_len)
4695            .map_err(|_| crate::Error::validation(op, ValidationError::IntegerOverflow))?,
4696    };
4697
4698    if slice.step() > 0 {
4699        return Ok(CoreSliceSpec {
4700            start,
4701            end,
4702            step: slice.step(),
4703        });
4704    }
4705
4706    if start >= end {
4707        return Ok(CoreSliceSpec {
4708            start,
4709            end: start,
4710            step: slice.step(),
4711        });
4712    }
4713
4714    Ok(CoreSliceSpec {
4715        start: end
4716            .checked_sub(1)
4717            .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))?,
4718        end: start
4719            .checked_sub(1)
4720            .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))?,
4721        step: slice.step(),
4722    })
4723}
4724
4725fn normalize_strided_bound(
4726    bound: isize,
4727    axis_len: usize,
4728    op: &'static str,
4729    role: &'static str,
4730) -> crate::Result<isize> {
4731    let original_axis_len = axis_len;
4732    let axis_len = isize::try_from(axis_len)
4733        .map_err(|_| crate::Error::validation(op, ValidationError::IntegerOverflow))?;
4734    let bound = if bound < 0 {
4735        axis_len
4736            .checked_add(bound)
4737            .ok_or_else(|| crate::Error::validation(op, ValidationError::IntegerOverflow))?
4738    } else {
4739        bound
4740    };
4741    if !(0..=axis_len).contains(&bound) {
4742        let (start, end) = if role == "slice start" {
4743            (bound, bound)
4744        } else {
4745            (0, bound)
4746        };
4747        return Err(crate::Error::validation(
4748            op,
4749            ValidationError::InvalidSliceBounds {
4750                start,
4751                end,
4752                axis_len: original_axis_len,
4753            },
4754        ));
4755    }
4756    Ok(bound)
4757}
4758
4759fn slice_axis_specs(
4760    rank: usize,
4761    axis: usize,
4762    slice: StridedSliceSpec,
4763    op: &'static str,
4764) -> crate::Result<Vec<StridedSliceSpec>> {
4765    if axis >= rank {
4766        return Err(crate::Error::validation(
4767            op,
4768            ValidationError::AxisOutOfBounds { axis, rank },
4769        ));
4770    }
4771
4772    let mut slices = vec![StridedSliceSpec::all(); rank];
4773    slices[axis] = slice;
4774    Ok(slices)
4775}
4776
4777pub(crate) fn default_placement() -> Placement {
4778    Placement {
4779        memory_kind: MemoryKind::UnpinnedHost,
4780        device: None,
4781        cpu_affinity: None,
4782    }
4783}
4784
4785fn typed_tensor_from_vec_col_major<T, R: TensorRank>(
4786    shape: impl Into<R::Shape>,
4787    data: Vec<T>,
4788    op: &'static str,
4789) -> crate::Result<TypedTensor<T, R>> {
4790    try_typed_tensor_from_vec_col_major(shape, data, op)
4791}
4792
4793fn try_typed_tensor_from_vec_col_major<T, R: TensorRank>(
4794    shape: impl Into<R::Shape>,
4795    data: Vec<T>,
4796    op: &'static str,
4797) -> crate::Result<TypedTensor<T, R>> {
4798    let layout = try_compact_layout(shape, op)?;
4799    try_checked_shape_len(layout.shape(), data.len(), op)?;
4800    Ok(TypedTensor {
4801        buffer: Buffer::Host(data),
4802        layout,
4803        placement: default_placement(),
4804    })
4805}
4806
4807fn typed_tensor_zeros<T: Clone + Zero, R: TensorRank>(
4808    shape: impl Into<R::Shape>,
4809) -> crate::Result<TypedTensor<T, R>> {
4810    try_typed_tensor_zeros(shape)
4811}
4812
4813fn try_typed_tensor_zeros<T: Clone + Zero, R: TensorRank>(
4814    shape: impl Into<R::Shape>,
4815) -> crate::Result<TypedTensor<T, R>> {
4816    let layout = try_compact_layout(shape, "zeros")?;
4817    let n = try_shape_product(layout.shape(), "zeros")?;
4818    Ok(TypedTensor {
4819        buffer: Buffer::Host(vec![T::zero(); n]),
4820        layout,
4821        placement: default_placement(),
4822    })
4823}
4824
4825fn typed_tensor_ones<T: Clone + One + Zero, R: TensorRank>(
4826    shape: impl Into<R::Shape>,
4827) -> crate::Result<TypedTensor<T, R>> {
4828    try_typed_tensor_ones(shape)
4829}
4830
4831fn try_typed_tensor_ones<T: Clone + One + Zero, R: TensorRank>(
4832    shape: impl Into<R::Shape>,
4833) -> crate::Result<TypedTensor<T, R>> {
4834    let layout = try_compact_layout(shape, "ones")?;
4835    let n = try_shape_product(layout.shape(), "ones")?;
4836    Ok(TypedTensor {
4837        buffer: Buffer::Host(vec![T::one(); n]),
4838        layout,
4839        placement: default_placement(),
4840    })
4841}
4842
4843fn typed_tensor_from_buffer_col_major<T: 'static, R: TensorRank>(
4844    shape: impl Into<R::Shape>,
4845    buffer: Buffer<T>,
4846    placement: Placement,
4847) -> crate::Result<TypedTensor<T, R>> {
4848    try_typed_tensor_from_buffer_col_major(shape, buffer, placement)
4849}
4850
4851fn try_typed_tensor_from_buffer_col_major<T: 'static, R: TensorRank>(
4852    shape: impl Into<R::Shape>,
4853    buffer: Buffer<T>,
4854    placement: Placement,
4855) -> crate::Result<TypedTensor<T, R>> {
4856    let layout = try_compact_layout(shape, "from_buffer_col_major")?;
4857    let len = buffer.len();
4858    try_checked_shape_len(layout.shape(), len, "from_buffer_col_major")?;
4859    Ok(TypedTensor {
4860        buffer,
4861        layout,
4862        placement,
4863    })
4864}
4865
4866impl<T: Clone + Zero, R: TensorRank> TypedTensor<T, R> {
4867    /// Allocate a zero-filled tensor.
4868    ///
4869    /// # Examples
4870    ///
4871    /// ```rust
4872    /// use tenferro_tensor::TypedTensor;
4873    ///
4874    /// let t = TypedTensor::<f64>::zeros(vec![2, 3]).unwrap();
4875    /// assert_eq!(t.n_elements(), 6);
4876    /// ```
4877    /// # Errors
4878    ///
4879    /// Returns [`crate::Error::Validation`] with
4880    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when shape
4881    /// product or compact-stride arithmetic overflows.
4882    pub fn zeros(shape: impl Into<R::Shape>) -> crate::Result<Self> {
4883        typed_tensor_zeros(shape)
4884    }
4885}
4886
4887impl<T: Clone + One + Zero, R: TensorRank> TypedTensor<T, R> {
4888    /// Allocate a one-filled tensor.
4889    ///
4890    /// # Examples
4891    ///
4892    /// ```rust
4893    /// use tenferro_tensor::TypedTensor;
4894    ///
4895    /// let t = TypedTensor::<f64>::ones(vec![2]).unwrap();
4896    /// assert_eq!(t.host_data().unwrap(), &[1.0, 1.0]);
4897    /// ```
4898    /// # Errors
4899    ///
4900    /// Returns [`crate::Error::Validation`] with
4901    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when shape
4902    /// product or compact-stride arithmetic overflows.
4903    pub fn ones(shape: impl Into<R::Shape>) -> crate::Result<Self> {
4904        typed_tensor_ones(shape)
4905    }
4906}
4907
4908impl<T, R: TensorRank> TypedTensor<T, R> {
4909    /// Create a tensor from an existing buffer and compact column-major layout.
4910    ///
4911    /// This preserves the owned tensor invariant that layout metadata is
4912    /// compact column-major, including for backend-owned buffers.
4913    ///
4914    /// # Examples
4915    ///
4916    /// ```
4917    /// use tenferro_tensor::{Buffer, Placement, TypedTensor};
4918    ///
4919    /// let tensor = TypedTensor::<f64>::from_buffer_col_major(
4920    ///     vec![2],
4921    ///     Buffer::Host(vec![1.0, 2.0]),
4922    ///     Placement {
4923    ///         memory_kind: tenferro_tensor::MemoryKind::UnpinnedHost,
4924    ///         device: None,
4925    ///         cpu_affinity: None,
4926    ///     },
4927    /// )
4928    /// .unwrap();
4929    /// assert_eq!(tensor.shape(), &[2]);
4930    /// ```
4931    /// # Errors
4932    ///
4933    /// Returns [`crate::Error::Validation`] with
4934    /// [`tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch`] when
4935    /// the shape product differs from the buffer length,
4936    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when shape or
4937    /// stride arithmetic overflows, or
4938    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when a supplied
4939    /// rank-specific shape cannot be represented.
4940    pub fn from_buffer_col_major(
4941        shape: impl Into<R::Shape>,
4942        buffer: Buffer<T>,
4943        placement: Placement,
4944    ) -> crate::Result<Self>
4945    where
4946        T: 'static,
4947    {
4948        typed_tensor_from_buffer_col_major(shape, buffer, placement)
4949    }
4950
4951    /// Convert this tensor into static rank metadata after validating its rank.
4952    ///
4953    /// The buffer and placement are preserved. This method changes only the
4954    /// compile-time rank marker on the owned compact column-major tensor.
4955    ///
4956    /// # Examples
4957    ///
4958    /// ```rust
4959    /// use tenferro_tensor::{Rank, TypedTensor};
4960    ///
4961    /// let tensor = TypedTensor::<f64>::from_vec_col_major(vec![2, 3], vec![1.0; 6]).unwrap();
4962    /// let ranked: TypedTensor<f64, Rank<2>> = tensor.try_into_rank::<2>()?;
4963    /// assert_eq!(ranked.shape(), &[2, 3]);
4964    /// # Ok::<(), tenferro_tensor::Error>(())
4965    /// ```
4966    /// # Errors
4967    ///
4968    /// Returns [`crate::Error::Validation`] with
4969    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when the typed
4970    /// rank does not match the existing shape,
4971    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when compact
4972    /// strides cannot be computed, or
4973    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
4974    /// preserved buffer cannot hold the rank-converted layout.
4975    pub fn try_into_rank<const N: usize>(self) -> crate::Result<TypedTensor<T, Rank<N>>> {
4976        let op = "TypedTensor::try_into_rank";
4977        let actual = self.shape().len();
4978        let shape: [usize; N] = self.shape().try_into().map_err(|_| {
4979            tensor_layout_error(
4980                op,
4981                ValidationError::RankMismatch {
4982                    expected: N,
4983                    actual,
4984                },
4985            )
4986        })?;
4987        let layout =
4988            TensorLayout::<Rank<N>>::compact(shape).map_err(|err| tensor_layout_error(op, err))?;
4989        Ok(TypedTensor {
4990            buffer: self.buffer,
4991            layout,
4992            placement: self.placement,
4993        })
4994    }
4995
4996    /// Number of elements in the tensor.
4997    ///
4998    /// # Examples
4999    ///
5000    /// ```rust
5001    /// use tenferro_tensor::TypedTensor;
5002    ///
5003    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2, 3], vec![0.0; 6]).unwrap();
5004    /// assert_eq!(t.n_elements(), 6);
5005    /// ```
5006    pub fn n_elements(&self) -> usize {
5007        // Invariant: owned tensor constructors validate compact shape length against buffer length.
5008        match try_shape_product(self.shape(), "TypedTensor::n_elements") {
5009            Ok(n) => n,
5010            Err(err) => {
5011                unreachable!("TypedTensor compact shape is validated at construction: {err}")
5012            }
5013        }
5014    }
5015
5016    /// Tensor shape.
5017    ///
5018    /// # Examples
5019    ///
5020    /// ```
5021    /// use tenferro_tensor::TypedTensor;
5022    ///
5023    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5024    /// assert_eq!(t.shape(), &[2]);
5025    /// ```
5026    pub fn shape(&self) -> &[usize] {
5027        self.layout.shape()
5028    }
5029
5030    /// Tensor rank.
5031    ///
5032    /// # Examples
5033    ///
5034    /// ```
5035    /// use tenferro_tensor::TypedTensor;
5036    ///
5037    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2, 3], vec![0.0; 6]).unwrap();
5038    /// assert_eq!(t.rank(), 2);
5039    /// ```
5040    pub fn rank(&self) -> usize {
5041        self.shape().len()
5042    }
5043
5044    /// Tensor layout metadata.
5045    ///
5046    /// Owned typed tensors are always compact column-major layouts.
5047    ///
5048    /// # Examples
5049    ///
5050    /// ```
5051    /// use tenferro_tensor::TypedTensor;
5052    ///
5053    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2, 3], vec![0.0; 6]).unwrap();
5054    /// assert_eq!(t.layout().strides(), &[1, 2]);
5055    /// ```
5056    pub fn layout(&self) -> &TensorLayout<R> {
5057        &self.layout
5058    }
5059
5060    /// Return the storage backing this tensor.
5061    ///
5062    /// This is an explicit storage-inspection API for backend glue and tests.
5063    /// Host value inspection should prefer [`TypedTensor::host_data`] when the
5064    /// caller requires host storage.
5065    ///
5066    /// # Examples
5067    ///
5068    /// ```
5069    /// use tenferro_tensor::{Buffer, TypedTensor};
5070    ///
5071    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5072    /// assert!(matches!(t.buffer(), Buffer::Host(_)));
5073    /// ```
5074    pub fn buffer(&self) -> &Buffer<T> {
5075        &self.buffer
5076    }
5077
5078    /// Return the shared-allocation domain carried by the backend buffer.
5079    ///
5080    /// # Examples
5081    ///
5082    /// ```rust
5083    /// use tenferro_tensor::TypedTensor;
5084    ///
5085    /// let tensor = TypedTensor::<f32>::from_vec_col_major(vec![1], vec![1.0])?;
5086    /// assert_eq!(tensor.allocation_domain(), None);
5087    /// # Ok::<(), tenferro_tensor::Error>(())
5088    /// ```
5089    pub fn allocation_domain(&self) -> Option<AllocationDomainId>
5090    where
5091        T: 'static,
5092    {
5093        match &self.buffer {
5094            Buffer::Host(_) => None,
5095            Buffer::Backend(buffer) => buffer.allocation_domain(),
5096        }
5097    }
5098
5099    /// Return the stable physical backend allocation identity.
5100    ///
5101    /// # Examples
5102    ///
5103    /// ```rust
5104    /// use tenferro_tensor::TypedTensor;
5105    ///
5106    /// let tensor = TypedTensor::<f32>::from_vec_col_major(vec![1], vec![1.0])?;
5107    /// assert_eq!(tensor.allocation_id(), None);
5108    /// # Ok::<(), tenferro_tensor::Error>(())
5109    /// ```
5110    pub fn allocation_id(&self) -> Option<AllocationId>
5111    where
5112        T: 'static,
5113    {
5114        match &self.buffer {
5115            Buffer::Host(_) => None,
5116            Buffer::Backend(buffer) => buffer.allocation_id(),
5117        }
5118    }
5119
5120    /// Return placement metadata for this tensor.
5121    ///
5122    /// # Examples
5123    ///
5124    /// ```
5125    /// use tenferro_tensor::{MemoryKind, TypedTensor};
5126    ///
5127    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![1], vec![1.0]).unwrap();
5128    /// assert_eq!(t.placement().memory_kind, MemoryKind::UnpinnedHost);
5129    /// ```
5130    pub fn placement(&self) -> &Placement {
5131        &self.placement
5132    }
5133
5134    /// Replace placement metadata without changing the storage buffer.
5135    ///
5136    /// # Examples
5137    ///
5138    /// ```
5139    /// use tenferro_tensor::{MemoryKind, Placement, TypedTensor};
5140    ///
5141    /// let mut t = TypedTensor::<f64>::from_vec_col_major(vec![1], vec![1.0]).unwrap();
5142    /// t.set_placement(Placement {
5143    ///     memory_kind: MemoryKind::PinnedHost,
5144    ///     device: None,
5145    ///     cpu_affinity: None,
5146    /// });
5147    /// assert_eq!(t.placement().memory_kind, MemoryKind::PinnedHost);
5148    /// ```
5149    pub fn set_placement(&mut self, placement: Placement) {
5150        self.placement = placement;
5151    }
5152
5153    /// Replace only CPU routing/locality metadata without changing storage.
5154    ///
5155    /// Device, memory kind, backend allocation domain, and allocation identity
5156    /// remain unchanged.
5157    ///
5158    /// # Examples
5159    ///
5160    /// ```rust
5161    /// use tenferro_tensor::{CpuDomainId, TypedTensor};
5162    ///
5163    /// let mut tensor = TypedTensor::<f64>::from_vec_col_major(vec![1], vec![1.0])?;
5164    /// tensor.set_cpu_affinity(Some(CpuDomainId::new(4)));
5165    /// assert_eq!(tensor.placement().cpu_affinity, Some(CpuDomainId::new(4)));
5166    /// # Ok::<(), tenferro_tensor::Error>(())
5167    /// ```
5168    pub fn set_cpu_affinity(&mut self, cpu_affinity: Option<CpuDomainId>) {
5169        self.placement.cpu_affinity = cpu_affinity;
5170    }
5171
5172    /// Borrow this tensor as a typed view preserving rank and layout metadata.
5173    ///
5174    /// # Examples
5175    ///
5176    /// ```rust
5177    /// use tenferro_tensor::{Rank, TypedTensor};
5178    ///
5179    /// let tensor = TypedTensor::<f64, Rank<2>>::from_vec_col_major([2, 2], vec![1.0; 4]).unwrap();
5180    /// let view = tensor.as_view();
5181    /// assert_eq!(view.strides(), &[1, 2]);
5182    /// ```
5183    pub fn as_view(&self) -> TypedTensorView<'_, T, R>
5184    where
5185        T: 'static,
5186    {
5187        let buffer = match &self.buffer {
5188            Buffer::Host(data) => TensorBufferRef::Host(data),
5189            Buffer::Backend(buffer) => TensorBufferRef::Backend(Arc::clone(buffer)),
5190        };
5191        TypedTensorView {
5192            buffer,
5193            layout: self.layout.clone(),
5194            placement: self.placement.clone(),
5195        }
5196    }
5197
5198    /// Mutably borrow this tensor as a typed view preserving rank and layout metadata.
5199    ///
5200    /// # Examples
5201    ///
5202    /// ```rust
5203    /// use tenferro_tensor::TypedTensor;
5204    ///
5205    /// let mut tensor = TypedTensor::<i32>::from_vec_col_major(vec![1], vec![1]).unwrap();
5206    /// *tensor.as_view_mut().get_mut(&[0]).unwrap() = 2;
5207    /// assert_eq!(tensor.as_slice().unwrap(), &[2]);
5208    /// ```
5209    pub fn as_view_mut(&mut self) -> TypedTensorViewMut<'_, T, R>
5210    where
5211        T: 'static,
5212    {
5213        let layout = self.layout.clone();
5214        let placement = self.placement.clone();
5215        let buffer = match &mut self.buffer {
5216            Buffer::Host(data) => TensorBufferRefMut::Host(data),
5217            Buffer::Backend(buffer) => TensorBufferRefMut::Backend(Arc::clone(buffer)),
5218        };
5219        TypedTensorViewMut {
5220            buffer,
5221            layout,
5222            placement,
5223        }
5224    }
5225
5226    /// Borrow a read-only strided region view over this tensor's backend
5227    /// (device) buffer from explicit layout metadata.
5228    ///
5229    /// This is a metadata-only view: no data is copied or transferred. The
5230    /// layout's reachable element span is validated against the backend
5231    /// buffer's physical length. Host-backed tensors are rejected with an
5232    /// explicit backend error; host regions are expressed with
5233    /// [`TypedTensorView::from_slice`] over host storage instead.
5234    ///
5235    /// # Examples
5236    ///
5237    /// ```rust
5238    /// use tenferro_tensor::TypedTensor;
5239    ///
5240    /// // Host tensors are rejected: this constructor is for backend buffers.
5241    /// let host = TypedTensor::<f64>::from_vec_col_major(vec![4], vec![0.0; 4]).unwrap();
5242    /// let err = host.backend_region_view(vec![2, 2], vec![1, 2], 0).unwrap_err();
5243    /// assert!(err.to_string().contains("backend"));
5244    /// ```
5245    /// # Errors
5246    ///
5247    /// Returns [`crate::Error::RuntimeState`] when this tensor is host-backed;
5248    /// backend region views require a backend buffer. It returns
5249    /// [`crate::Error::Validation`] with
5250    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] for incompatible
5251    /// shape/stride ranks, [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`]
5252    /// when the region exceeds the backend buffer, or
5253    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
5254    /// arithmetic overflow.
5255    pub fn backend_region_view(
5256        &self,
5257        shape: Vec<usize>,
5258        strides: Vec<isize>,
5259        offset: isize,
5260    ) -> crate::Result<TypedTensorView<'_, T, DynRank>>
5261    where
5262        T: 'static,
5263    {
5264        let op = "TypedTensor::backend_region_view";
5265        let Buffer::Backend(buffer) = &self.buffer else {
5266            return Err(crate::Error::runtime_state(
5267                op,
5268                "expected a backend (device) buffer; host tensors use \
5269                 TypedTensorView::from_slice over host storage",
5270            ));
5271        };
5272        let layout = TensorLayout::from_parts(shape.into(), strides.into(), offset, buffer.len())
5273            .map_err(|err| tensor_layout_error(op, err))?;
5274        Ok(TypedTensorView {
5275            buffer: TensorBufferRef::Backend(Arc::clone(buffer)),
5276            layout,
5277            placement: self.placement.clone(),
5278        })
5279    }
5280
5281    /// Borrow a mutable strided region view over this tensor's backend
5282    /// (device) buffer from explicit layout metadata.
5283    ///
5284    /// This is the mutable counterpart of
5285    /// [`TypedTensor::backend_region_view`]. The layout's reachable element
5286    /// span is validated against the backend buffer's physical length, and
5287    /// layouts whose logical elements alias the same physical element are
5288    /// rejected. Host-backed tensors are rejected with an explicit backend
5289    /// error; mutable host regions must go through
5290    /// [`TypedTensorViewMut::try_multi_slice_mut`] or host constructors.
5291    ///
5292    /// Backend buffers are shared handles, so distinct region views over one
5293    /// buffer can coexist; disjointness between regions used concurrently by
5294    /// backend operations is the caller's contract (as with BLAS-style
5295    /// in-place update APIs).
5296    ///
5297    /// # Examples
5298    ///
5299    /// ```rust
5300    /// use tenferro_tensor::TypedTensor;
5301    ///
5302    /// // Host tensors are rejected: this constructor is for backend buffers.
5303    /// let mut host = TypedTensor::<f64>::from_vec_col_major(vec![4], vec![0.0; 4]).unwrap();
5304    /// let err = host.backend_region_view_mut(vec![2, 2], vec![1, 2], 0).unwrap_err();
5305    /// assert!(err.to_string().contains("backend"));
5306    /// ```
5307    /// # Errors
5308    ///
5309    /// Returns [`crate::Error::RuntimeState`] when this tensor is host-backed;
5310    /// mutable backend region views require a backend buffer. It returns
5311    /// [`crate::Error::Validation`] with
5312    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] for incompatible
5313    /// shape/stride ranks, [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`]
5314    /// when the region exceeds the backend buffer,
5315    /// [`tenferro_tensor_core::ValidationError::OverlappingMutableLayout`] when
5316    /// logical elements alias, or
5317    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] for layout
5318    /// arithmetic overflow.
5319    pub fn backend_region_view_mut(
5320        &mut self,
5321        shape: Vec<usize>,
5322        strides: Vec<isize>,
5323        offset: isize,
5324    ) -> crate::Result<TypedTensorViewMut<'_, T, DynRank>>
5325    where
5326        T: 'static,
5327    {
5328        let op = "TypedTensor::backend_region_view_mut";
5329        let Buffer::Backend(buffer) = &self.buffer else {
5330            return Err(crate::Error::runtime_state(
5331                op,
5332                "expected a backend (device) buffer; mutable host regions use \
5333                 TypedTensorViewMut host constructors or try_multi_slice_mut",
5334            ));
5335        };
5336        let layout = TensorLayout::from_parts(shape.into(), strides.into(), offset, buffer.len())
5337            .map_err(|err| tensor_layout_error(op, err))?;
5338        layout
5339            .validate_mutable_no_overlap()
5340            .map_err(|err| tensor_layout_error(op, err))?;
5341        Ok(TypedTensorViewMut {
5342            buffer: TensorBufferRefMut::Backend(Arc::clone(buffer)),
5343            layout,
5344            placement: self.placement.clone(),
5345        })
5346    }
5347
5348    /// Consume this tensor and return its layout metadata.
5349    ///
5350    /// # Examples
5351    ///
5352    /// ```
5353    /// use tenferro_tensor::TypedTensor;
5354    ///
5355    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5356    /// assert!(t.into_layout().is_compact_col_major().unwrap());
5357    /// ```
5358    pub fn into_layout(self) -> TensorLayout<R> {
5359        self.layout
5360    }
5361
5362    /// Consume this tensor and return its storage, layout, and placement.
5363    ///
5364    /// # Examples
5365    ///
5366    /// ```
5367    /// use tenferro_tensor::{Buffer, TypedTensor};
5368    ///
5369    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5370    /// let (buffer, layout, placement) = t.into_parts();
5371    /// assert!(matches!(buffer, Buffer::Host(_)));
5372    /// assert_eq!(layout.shape(), &[2]);
5373    /// assert!(placement.device.is_none());
5374    /// ```
5375    pub fn into_parts(self) -> (Buffer<T>, TensorLayout<R>, Placement) {
5376        (self.buffer, self.layout, self.placement)
5377    }
5378}
5379
5380impl<T: Clone, R: TensorRank> TypedTensor<T, R> {
5381    /// Create a tensor from a column-major buffer.
5382    ///
5383    /// # Examples
5384    ///
5385    /// ```
5386    /// use tenferro_tensor::TypedTensor;
5387    ///
5388    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2, 2], vec![1.0, 2.0, 3.0, 4.0]).unwrap();
5389    /// assert_eq!(t.get(&[1, 0])?, &2.0);
5390    /// # Ok::<(), tenferro_tensor::Error>(())
5391    /// ```
5392    /// # Errors
5393    ///
5394    /// Returns [`crate::Error::Validation`] with
5395    /// [`tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch`] when
5396    /// the shape product differs from `data.len()`, or
5397    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when shape
5398    /// arithmetic overflows.
5399    pub fn from_vec_col_major(shape: impl Into<R::Shape>, data: Vec<T>) -> crate::Result<Self> {
5400        typed_tensor_from_vec_col_major(shape, data, "from_vec_col_major")
5401    }
5402
5403    /// Consume this tensor and return its owned column-major host buffer.
5404    ///
5405    /// # Examples
5406    ///
5407    /// ```
5408    /// use tenferro_tensor::TypedTensor;
5409    ///
5410    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5411    /// let (shape, data) = t.into_vec_col_major().unwrap();
5412    /// assert_eq!(shape, vec![2]);
5413    /// assert_eq!(data, vec![1.0, 2.0]);
5414    /// ```
5415    /// # Errors
5416    ///
5417    /// Returns [`crate::Error::RuntimeState`] when this tensor uses backend
5418    /// storage; download it before exporting a host `Vec`.
5419    pub fn into_vec_col_major(self) -> crate::Result<(Vec<usize>, Vec<T>)> {
5420        let shape = self.shape().to_vec();
5421        match self.buffer {
5422            Buffer::Host(data) => Ok((shape, data)),
5423            Buffer::Backend(_) => Err(crate::Error::runtime_state(
5424                "into_vec_col_major",
5425                "backend buffers cannot be exported as host Vec",
5426            )),
5427        }
5428    }
5429
5430    /// Borrow the host buffer.
5431    ///
5432    /// # Examples
5433    ///
5434    /// ```rust
5435    /// use tenferro_tensor::TypedTensor;
5436    ///
5437    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5438    /// assert_eq!(t.host_data()?, &[1.0, 2.0]);
5439    /// # Ok::<(), tenferro_tensor::Error>(())
5440    /// ```
5441    /// # Errors
5442    ///
5443    /// Returns [`crate::Error::RuntimeState`] when this tensor uses backend
5444    /// storage; download it before borrowing host data.
5445    pub fn host_data(&self) -> crate::Result<&[T]> {
5446        match &self.buffer {
5447            Buffer::Host(v) => Ok(v),
5448            Buffer::Backend(_) => Err(crate::Error::runtime_state(
5449                "TypedTensor::host_data",
5450                "backend buffers cannot be inspected as host slices; download explicitly first",
5451            )),
5452        }
5453    }
5454
5455    /// View the tensor data as a flat slice.
5456    ///
5457    /// This is an alias for `host_data()` for API consistency with
5458    /// `Tensor::as_slice`.
5459    ///
5460    /// # Examples
5461    ///
5462    /// ```
5463    /// use tenferro_tensor::TypedTensor;
5464    ///
5465    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5466    /// assert_eq!(t.as_slice()?, &[1.0, 2.0]);
5467    /// # Ok::<(), tenferro_tensor::Error>(())
5468    /// ```
5469    /// # Errors
5470    ///
5471    /// Returns [`crate::Error::RuntimeState`] when this tensor uses backend
5472    /// storage; download it before borrowing it as a host slice.
5473    pub fn as_slice(&self) -> crate::Result<&[T]> {
5474        self.host_data()
5475    }
5476
5477    /// Mutably borrow the host buffer.
5478    ///
5479    /// # Examples
5480    ///
5481    /// ```rust
5482    /// use tenferro_tensor::TypedTensor;
5483    ///
5484    /// let mut t = TypedTensor::<f64>::zeros(vec![2]).unwrap();
5485    /// t.host_data_mut()?[0] = 3.0;
5486    /// assert_eq!(t.host_data()?, &[3.0, 0.0]);
5487    /// # Ok::<(), tenferro_tensor::Error>(())
5488    /// ```
5489    /// # Errors
5490    ///
5491    /// Returns [`crate::Error::RuntimeState`] when this tensor uses backend
5492    /// storage; download it before mutably borrowing host data.
5493    pub fn host_data_mut(&mut self) -> crate::Result<&mut [T]> {
5494        match &mut self.buffer {
5495            Buffer::Host(v) => Ok(v),
5496            Buffer::Backend(_) => Err(crate::Error::runtime_state(
5497                "TypedTensor::host_data_mut",
5498                "backend buffers cannot be mutated as host slices; download explicitly first",
5499            )),
5500        }
5501    }
5502
5503    /// Compute the linear physical-buffer offset for a logical index.
5504    ///
5505    /// # Examples
5506    ///
5507    /// ```rust
5508    /// use tenferro_tensor::TypedTensor;
5509    ///
5510    /// let t = TypedTensor::<f64>::zeros(vec![2, 3]).unwrap();
5511    /// assert_eq!(t.linear_offset(&[1, 2])?, 5);
5512    /// # Ok::<(), tenferro_tensor::Error>(())
5513    /// ```
5514    /// # Errors
5515    ///
5516    /// Returns [`crate::Error::Validation`] with
5517    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
5518    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
5519    /// when an index is outside its axis extent, or
5520    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
5521    /// arithmetic overflows.
5522    pub fn linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
5523        try_linear_offset_for_shape(self.shape(), indices, "TypedTensor::linear_offset")
5524    }
5525
5526    /// Compute the physical element offset for a logical index.
5527    ///
5528    /// # Examples
5529    ///
5530    /// ```rust
5531    /// use tenferro_tensor::TypedTensor;
5532    ///
5533    /// let t = TypedTensor::<f64>::zeros(vec![2, 3]).unwrap();
5534    /// assert_eq!(t.layout_linear_offset(&[1, 2])?, 5);
5535    /// # Ok::<(), tenferro_tensor::Error>(())
5536    /// ```
5537    /// # Errors
5538    ///
5539    /// Returns [`crate::Error::Validation`] with
5540    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
5541    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
5542    /// when an index is outside its axis extent, or
5543    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
5544    /// arithmetic overflows.
5545    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
5546        try_linear_offset_for_shape(self.shape(), indices, "TypedTensor::layout_linear_offset")
5547    }
5548
5549    /// Return whether this owned tensor is compact column-major.
5550    ///
5551    /// # Examples
5552    ///
5553    /// ```rust
5554    /// use tenferro_tensor::TypedTensor;
5555    ///
5556    /// let t = TypedTensor::<f64>::zeros(vec![2]).unwrap();
5557    /// assert!(t.is_col_major_contiguous()?);
5558    /// # Ok::<(), tenferro_tensor::Error>(())
5559    /// ```
5560    /// # Errors
5561    ///
5562    /// Returns [`crate::Error::Validation`] with
5563    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
5564    /// compactness arithmetic overflows.
5565    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
5566        self.layout
5567            .is_compact_col_major()
5568            .map_err(|err| tensor_layout_error("TypedTensor::is_col_major_contiguous", err))
5569    }
5570
5571    /// Return a compact string summary of this tensor's layout metadata.
5572    ///
5573    /// # Examples
5574    ///
5575    /// ```rust
5576    /// use tenferro_tensor::TypedTensor;
5577    ///
5578    /// let t = TypedTensor::<f64>::zeros(vec![2]).unwrap();
5579    /// assert!(t.layout_summary().contains("shape=[2]"));
5580    /// # Ok::<(), tenferro_tensor::Error>(())
5581    /// ```
5582    pub fn layout_summary(&self) -> String {
5583        layout_summary(self.shape(), self.layout.strides(), self.layout.offset())
5584    }
5585
5586    /// Assert this tensor is compact column-major.
5587    ///
5588    /// # Examples
5589    ///
5590    /// ```rust
5591    /// use tenferro_tensor::TypedTensor;
5592    ///
5593    /// let t = TypedTensor::<f64>::zeros(vec![2]).unwrap();
5594    /// t.assert_col_major_contiguous()?;
5595    /// # Ok::<(), tenferro_tensor::Error>(())
5596    /// ```
5597    /// # Errors
5598    ///
5599    /// Returns [`crate::Error::Validation`] with
5600    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
5601    /// compactness arithmetic overflows, or
5602    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
5603    /// tensor is not compact column-major.
5604    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
5605        assert_layout_col_major_contiguous(
5606            self.is_col_major_contiguous()?,
5607            self.shape(),
5608            self.layout.strides(),
5609            self.layout.offset(),
5610            "TypedTensor::assert_col_major_contiguous",
5611        )
5612    }
5613
5614    /// Borrow a single element by multi-index.
5615    ///
5616    /// # Examples
5617    ///
5618    /// ```rust
5619    /// use tenferro_tensor::TypedTensor;
5620    ///
5621    /// let t = TypedTensor::<f64>::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap();
5622    /// assert_eq!(t.get(&[1])?, &2.0);
5623    /// # Ok::<(), tenferro_tensor::Error>(())
5624    /// ```
5625    /// # Errors
5626    ///
5627    /// Returns [`crate::Error::Validation`] with
5628    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
5629    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
5630    /// when an index is outside its axis extent, or
5631    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
5632    /// computed offset is outside the host buffer. It returns
5633    /// [`crate::Error::RuntimeState`] when the tensor uses backend storage.
5634    pub fn get(&self, indices: &[usize]) -> crate::Result<&T> {
5635        let off = self.linear_offset(indices)?;
5636        self.host_data()?.get(off).ok_or_else(|| {
5637            crate::Error::validation("TypedTensor::get", ValidationError::ViewOutOfBounds)
5638        })
5639    }
5640
5641    /// Mutably borrow a single element by multi-index.
5642    ///
5643    /// # Examples
5644    ///
5645    /// ```rust
5646    /// use tenferro_tensor::TypedTensor;
5647    ///
5648    /// let mut t = TypedTensor::<f64>::zeros(vec![1]).unwrap();
5649    /// *t.get_mut(&[0])? = 7.0;
5650    /// assert_eq!(t.host_data()?, &[7.0]);
5651    /// # Ok::<(), tenferro_tensor::Error>(())
5652    /// ```
5653    /// # Errors
5654    ///
5655    /// Returns [`crate::Error::Validation`] with
5656    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
5657    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
5658    /// when an index is outside its axis extent, or
5659    /// [`tenferro_tensor_core::ValidationError::ViewOutOfBounds`] when the
5660    /// computed offset is outside the host buffer. It returns
5661    /// [`crate::Error::RuntimeState`] when the tensor uses backend storage.
5662    pub fn get_mut(&mut self, indices: &[usize]) -> crate::Result<&mut T> {
5663        let off = self.linear_offset(indices)?;
5664        self.host_data_mut()?.get_mut(off).ok_or_else(|| {
5665            crate::Error::validation("TypedTensor::get_mut", ValidationError::ViewOutOfBounds)
5666        })
5667    }
5668}
5669
5670impl Tensor {
5671    /// Create a tensor from a shape and column-major flat data.
5672    ///
5673    /// This is the `Tensor`-level equivalent of
5674    /// `TypedTensor::<T>::from_vec_col_major`.
5675    ///
5676    /// # Examples
5677    ///
5678    /// ```
5679    /// use tenferro_tensor::Tensor;
5680    ///
5681    /// let t = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 3.0, 2.0, 4.0]).unwrap();
5682    /// assert_eq!(t.shape(), &[2, 2]);
5683    /// assert_eq!(t.as_slice::<f64>().unwrap(), &[1.0, 3.0, 2.0, 4.0]);
5684    /// ```
5685    /// # Errors
5686    ///
5687    /// Returns [`crate::Error::Validation`] with
5688    /// [`tenferro_tensor_core::ValidationError::ShapeDataLengthMismatch`] when
5689    /// the shape product differs from `data.len()`, or
5690    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when shape
5691    /// arithmetic overflows.
5692    pub fn from_vec_col_major<T: TensorScalar>(
5693        shape: impl tenferro_tensor_core::IntoShapeVec,
5694        data: Vec<T>,
5695    ) -> crate::Result<Self> {
5696        T::into_tensor(shape.into_shape_vec().to_vec(), data)
5697    }
5698
5699    /// Tensor shape.
5700    ///
5701    /// # Examples
5702    ///
5703    /// ```rust
5704    /// use tenferro_tensor::{Tensor, TypedTensor};
5705    ///
5706    /// let t = Tensor::F64(TypedTensor::from_vec_col_major(vec![2], vec![1.0, 2.0]).unwrap());
5707    /// assert_eq!(t.shape(), &[2]);
5708    /// ```
5709    pub fn shape(&self) -> &[usize] {
5710        match self {
5711            Tensor::F32(t) => t.shape(),
5712            Tensor::F64(t) => t.shape(),
5713            Tensor::I32(t) => t.shape(),
5714            Tensor::I64(t) => t.shape(),
5715            Tensor::Bool(t) => t.shape(),
5716            Tensor::C32(t) => t.shape(),
5717            Tensor::C64(t) => t.shape(),
5718        }
5719    }
5720
5721    /// Tensor dtype tag.
5722    ///
5723    /// # Examples
5724    ///
5725    /// ```rust
5726    /// use tenferro_tensor::{DType, Tensor, TypedTensor};
5727    ///
5728    /// let t = Tensor::F64(TypedTensor::from_vec_col_major(vec![], vec![1.0]).unwrap());
5729    /// assert_eq!(t.dtype(), DType::F64);
5730    /// ```
5731    pub fn dtype(&self) -> DType {
5732        match self {
5733            Tensor::F32(_) => DType::F32,
5734            Tensor::F64(_) => DType::F64,
5735            Tensor::I32(_) => DType::I32,
5736            Tensor::I64(_) => DType::I64,
5737            Tensor::Bool(_) => DType::Bool,
5738            Tensor::C32(_) => DType::C32,
5739            Tensor::C64(_) => DType::C64,
5740        }
5741    }
5742
5743    /// Return placement metadata for this dtype-erased tensor.
5744    ///
5745    /// # Examples
5746    ///
5747    /// ```rust
5748    /// use tenferro_tensor::{MemoryKind, Tensor};
5749    ///
5750    /// let t = Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap();
5751    /// assert_eq!(t.placement().memory_kind, MemoryKind::UnpinnedHost);
5752    /// ```
5753    pub fn placement(&self) -> &Placement {
5754        match self {
5755            Tensor::F32(t) => t.placement(),
5756            Tensor::F64(t) => t.placement(),
5757            Tensor::I32(t) => t.placement(),
5758            Tensor::I64(t) => t.placement(),
5759            Tensor::Bool(t) => t.placement(),
5760            Tensor::C32(t) => t.placement(),
5761            Tensor::C64(t) => t.placement(),
5762        }
5763    }
5764
5765    /// Return whether this tensor is backed by backend-native storage.
5766    ///
5767    /// # Examples
5768    ///
5769    /// ```rust
5770    /// use tenferro_tensor::Tensor;
5771    ///
5772    /// let t = Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap();
5773    /// assert!(!t.is_backend_buffer());
5774    /// ```
5775    pub fn is_backend_buffer(&self) -> bool {
5776        match self {
5777            Tensor::F32(t) => t.buffer().is_backend(),
5778            Tensor::F64(t) => t.buffer().is_backend(),
5779            Tensor::I32(t) => t.buffer().is_backend(),
5780            Tensor::I64(t) => t.buffer().is_backend(),
5781            Tensor::Bool(t) => t.buffer().is_backend(),
5782            Tensor::C32(t) => t.buffer().is_backend(),
5783            Tensor::C64(t) => t.buffer().is_backend(),
5784        }
5785    }
5786
5787    /// Compute the physical element offset for a logical index.
5788    ///
5789    /// # Examples
5790    ///
5791    /// ```rust
5792    /// use tenferro_tensor::Tensor;
5793    ///
5794    /// let t = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
5795    /// assert_eq!(t.layout_linear_offset(&[1])?, 1);
5796    /// # Ok::<(), tenferro_tensor::Error>(())
5797    /// ```
5798    /// # Errors
5799    ///
5800    /// Returns [`crate::Error::Validation`] with
5801    /// [`tenferro_tensor_core::ValidationError::RankMismatch`] when `indices`
5802    /// has the wrong rank, [`tenferro_tensor_core::ValidationError::InvalidArgument`]
5803    /// when an index is outside its axis extent, or
5804    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when offset
5805    /// arithmetic overflows.
5806    pub fn layout_linear_offset(&self, indices: &[usize]) -> crate::Result<usize> {
5807        match self {
5808            Tensor::F32(t) => t.layout_linear_offset(indices),
5809            Tensor::F64(t) => t.layout_linear_offset(indices),
5810            Tensor::I32(t) => t.layout_linear_offset(indices),
5811            Tensor::I64(t) => t.layout_linear_offset(indices),
5812            Tensor::Bool(t) => t.layout_linear_offset(indices),
5813            Tensor::C32(t) => t.layout_linear_offset(indices),
5814            Tensor::C64(t) => t.layout_linear_offset(indices),
5815        }
5816    }
5817
5818    /// Return whether this tensor is compact column-major.
5819    ///
5820    /// # Examples
5821    ///
5822    /// ```rust
5823    /// use tenferro_tensor::Tensor;
5824    ///
5825    /// let t = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
5826    /// assert!(t.is_col_major_contiguous()?);
5827    /// # Ok::<(), tenferro_tensor::Error>(())
5828    /// ```
5829    /// # Errors
5830    ///
5831    /// Returns [`crate::Error::Validation`] with
5832    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
5833    /// compactness arithmetic overflows.
5834    pub fn is_col_major_contiguous(&self) -> crate::Result<bool> {
5835        match self {
5836            Tensor::F32(t) => t.is_col_major_contiguous(),
5837            Tensor::F64(t) => t.is_col_major_contiguous(),
5838            Tensor::I32(t) => t.is_col_major_contiguous(),
5839            Tensor::I64(t) => t.is_col_major_contiguous(),
5840            Tensor::Bool(t) => t.is_col_major_contiguous(),
5841            Tensor::C32(t) => t.is_col_major_contiguous(),
5842            Tensor::C64(t) => t.is_col_major_contiguous(),
5843        }
5844    }
5845
5846    /// Return a compact string summary of this tensor's layout metadata.
5847    ///
5848    /// # Examples
5849    ///
5850    /// ```rust
5851    /// use tenferro_tensor::Tensor;
5852    ///
5853    /// let t = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
5854    /// assert!(t.layout_summary().contains("shape=[2]"));
5855    /// # Ok::<(), tenferro_tensor::Error>(())
5856    /// ```
5857    pub fn layout_summary(&self) -> String {
5858        let layout = tensor_layout(self);
5859        layout_summary(layout.shape(), layout.strides(), layout.offset())
5860    }
5861
5862    /// Assert this tensor is compact column-major.
5863    ///
5864    /// # Examples
5865    ///
5866    /// ```rust
5867    /// use tenferro_tensor::Tensor;
5868    ///
5869    /// let t = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
5870    /// t.assert_col_major_contiguous()?;
5871    /// # Ok::<(), tenferro_tensor::Error>(())
5872    /// ```
5873    /// # Errors
5874    ///
5875    /// Returns [`crate::Error::Validation`] with
5876    /// [`tenferro_tensor_core::ValidationError::IntegerOverflow`] when
5877    /// compactness arithmetic overflows, or
5878    /// [`tenferro_tensor_core::ValidationError::InvalidArgument`] when the
5879    /// tensor is not compact column-major.
5880    pub fn assert_col_major_contiguous(&self) -> crate::Result<()> {
5881        let layout = tensor_layout(self);
5882        assert_layout_col_major_contiguous(
5883            self.is_col_major_contiguous()?,
5884            layout.shape(),
5885            layout.strides(),
5886            layout.offset(),
5887            "Tensor::assert_col_major_contiguous",
5888        )
5889    }
5890
5891    /// Try to borrow the host data as a typed slice.
5892    ///
5893    /// Returns an error if the tensor dtype does not match `T`.
5894    ///
5895    /// # Examples
5896    ///
5897    /// ```
5898    /// use tenferro_tensor::{Tensor, TypedTensor};
5899    ///
5900    /// let t = Tensor::F64(TypedTensor::from_vec_col_major(vec![3], vec![1.0, 2.0, 3.0]).unwrap());
5901    /// assert_eq!(t.as_slice::<f64>().unwrap(), [1.0, 2.0, 3.0].as_slice());
5902    /// assert!(t.as_slice::<f32>().is_err());
5903    /// ```
5904    /// # Errors
5905    ///
5906    /// Returns [`crate::Error::Validation`] with
5907    /// [`tenferro_tensor_core::ValidationError::DTypeMismatch`] when `T` does
5908    /// not match the tensor dtype, or [`crate::Error::RuntimeState`] when the
5909    /// matching tensor uses backend storage that has not been downloaded.
5910    pub fn as_slice<T: TensorScalar>(&self) -> crate::Result<&[T]> {
5911        T::as_slice(self)
5912    }
5913
5914    /// Consume this tensor and return its owned column-major buffer when the
5915    /// dtype matches.
5916    ///
5917    /// # Examples
5918    ///
5919    /// ```
5920    /// use tenferro_tensor::Tensor;
5921    ///
5922    /// let t = Tensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap();
5923    /// assert_eq!(t.into_vec_col_major::<f64>().unwrap().1, vec![2.0]);
5924    /// ```
5925    /// # Errors
5926    ///
5927    /// Returns [`crate::Error::Validation`] with
5928    /// [`tenferro_tensor_core::ValidationError::DTypeMismatch`] when `T` does
5929    /// not match the tensor dtype, or [`crate::Error::RuntimeState`] when the
5930    /// matching tensor uses backend storage that has not been downloaded.
5931    pub fn into_vec_col_major<T: TensorScalar>(self) -> crate::Result<(Vec<usize>, Vec<T>)> {
5932        let typed = T::into_typed(self)?;
5933        typed.into_vec_col_major()
5934    }
5935}
5936
5937// Kept for crate-local layout tests while tensor indexing helpers remain split
5938// across tensor and CPU crates.
5939#[allow(dead_code)]
5940pub(crate) fn flat_to_multi(mut flat: usize, shape: &[usize], out: &mut [usize]) {
5941    for i in 0..shape.len() {
5942        if shape[i] == 0 {
5943            out[i] = 0;
5944        } else {
5945            out[i] = flat % shape[i];
5946            flat /= shape[i];
5947        }
5948    }
5949}