1use std::cmp::Ordering;
2use std::fmt;
3use std::ops::{Add, Div, Mul, Neg, Sub};
4
5use anyhow::{anyhow, ensure, Result};
6use num_complex::{Complex32, Complex64};
7use num_traits::{One, Zero};
8use tenferro::{DType, Tensor as NativeTensor, TensorScalar};
9
10use crate::storage::{Storage, SumFromStorage};
11use crate::tensor_element::TensorElement;
12
13#[derive(Clone, Copy, Debug, PartialEq)]
14enum ScalarValue {
15 F32(f32),
16 F64(f64),
17 I32(i32),
18 I64(i64),
19 Bool(bool),
20 C32(Complex32),
21 C64(Complex64),
22}
23
24impl ScalarValue {
25 fn real(self) -> f64 {
26 match self {
27 Self::F32(value) => value as f64,
28 Self::F64(value) => value,
29 Self::I32(value) => value as f64,
30 Self::I64(value) => value as f64,
31 Self::Bool(value) => {
32 if value {
33 1.0
34 } else {
35 0.0
36 }
37 }
38 Self::C32(value) => value.re as f64,
39 Self::C64(value) => value.re,
40 }
41 }
42
43 fn imag(self) -> f64 {
44 match self {
45 Self::F32(_) | Self::F64(_) | Self::I32(_) | Self::I64(_) | Self::Bool(_) => 0.0,
46 Self::C32(value) => value.im as f64,
47 Self::C64(value) => value.im,
48 }
49 }
50
51 fn abs(self) -> f64 {
52 match self {
53 Self::F32(value) => value.abs() as f64,
54 Self::F64(value) => value.abs(),
55 Self::I32(value) => value.abs() as f64,
56 Self::I64(value) => value.abs() as f64,
57 Self::Bool(value) => {
58 if value {
59 1.0
60 } else {
61 0.0
62 }
63 }
64 Self::C32(value) => {
65 if value.re.is_nan() || value.im.is_nan() {
66 f64::NAN
67 } else {
68 value.norm() as f64
69 }
70 }
71 Self::C64(value) => {
72 if value.re.is_nan() || value.im.is_nan() {
73 f64::NAN
74 } else {
75 value.norm()
76 }
77 }
78 }
79 }
80
81 fn is_complex(self) -> bool {
82 matches!(self, Self::C32(_) | Self::C64(_))
83 }
84
85 fn is_zero(self) -> bool {
86 match self {
87 Self::F32(value) => value == 0.0,
88 Self::F64(value) => value == 0.0,
89 Self::I32(value) => value == 0,
90 Self::I64(value) => value == 0,
91 Self::Bool(value) => !value,
92 Self::C32(value) => value == Complex32::new(0.0, 0.0),
93 Self::C64(value) => value == Complex64::new(0.0, 0.0),
94 }
95 }
96
97 fn into_complex(self) -> Complex64 {
98 match self {
99 Self::F32(value) => Complex64::new(value as f64, 0.0),
100 Self::F64(value) => Complex64::new(value, 0.0),
101 Self::I32(value) => Complex64::new(value as f64, 0.0),
102 Self::I64(value) => Complex64::new(value as f64, 0.0),
103 Self::Bool(value) => Complex64::new(if value { 1.0 } else { 0.0 }, 0.0),
104 Self::C32(value) => Complex64::new(value.re as f64, value.im as f64),
105 Self::C64(value) => value,
106 }
107 }
108}
109
110fn scalar_value_from_storage(storage: &Storage) -> ScalarValue {
111 if storage.is_f64() {
112 ScalarValue::F64(f64::sum_from_storage(storage))
113 } else {
114 ScalarValue::C64(Complex64::sum_from_storage(storage))
115 }
116}
117
118fn scalar_value_from_native(native: &NativeTensor) -> Result<ScalarValue> {
119 ensure!(
120 native.shape().is_empty(),
121 "expected rank-0 scalar tensor, got shape {:?}",
122 native.shape()
123 );
124
125 match native.dtype() {
126 DType::F32 => native
127 .as_slice::<f32>()
128 .map_err(anyhow::Error::new)?
129 .first()
130 .copied()
131 .map(ScalarValue::F32)
132 .ok_or_else(|| anyhow!("failed to read f32 scalar tensor value")),
133 DType::F64 => native
134 .as_slice::<f64>()
135 .map_err(anyhow::Error::new)?
136 .first()
137 .copied()
138 .map(ScalarValue::F64)
139 .ok_or_else(|| anyhow!("failed to read f64 scalar tensor value")),
140 DType::I32 => native
141 .as_slice::<i32>()
142 .map_err(anyhow::Error::new)?
143 .first()
144 .copied()
145 .map(ScalarValue::I32)
146 .ok_or_else(|| anyhow!("failed to read i32 scalar tensor value")),
147 DType::I64 => native
148 .as_slice::<i64>()
149 .map_err(anyhow::Error::new)?
150 .first()
151 .copied()
152 .map(ScalarValue::I64)
153 .ok_or_else(|| anyhow!("failed to read i64 scalar tensor value")),
154 DType::Bool => native
155 .as_slice::<bool>()
156 .map_err(anyhow::Error::new)?
157 .first()
158 .copied()
159 .map(ScalarValue::Bool)
160 .ok_or_else(|| anyhow!("failed to read bool scalar tensor value")),
161 DType::C32 => native
162 .as_slice::<Complex32>()
163 .map_err(anyhow::Error::new)?
164 .first()
165 .copied()
166 .map(ScalarValue::C32)
167 .ok_or_else(|| anyhow!("failed to read c32 scalar tensor value")),
168 DType::C64 => native
169 .as_slice::<Complex64>()
170 .map_err(anyhow::Error::new)?
171 .first()
172 .copied()
173 .map(ScalarValue::C64)
174 .ok_or_else(|| anyhow!("failed to read c64 scalar tensor value")),
175 }
176}
177
178trait ScalarTensorElement: TensorElement {
179 fn scalar_value(value: Self) -> ScalarValue;
180}
181
182fn scalar_native<T: TensorScalar>(value: T) -> NativeTensor {
183 crate::require_invariant(
184 NativeTensor::from_vec_col_major(vec![], vec![value]),
185 "rank-0 scalar construction failed",
186 )
187}
188
189impl ScalarTensorElement for f32 {
190 fn scalar_value(value: Self) -> ScalarValue {
191 ScalarValue::F32(value)
192 }
193}
194
195impl ScalarTensorElement for f64 {
196 fn scalar_value(value: Self) -> ScalarValue {
197 ScalarValue::F64(value)
198 }
199}
200
201impl ScalarTensorElement for Complex32 {
202 fn scalar_value(value: Self) -> ScalarValue {
203 ScalarValue::C32(value)
204 }
205}
206
207impl ScalarTensorElement for Complex64 {
208 fn scalar_value(value: Self) -> ScalarValue {
209 ScalarValue::C64(value)
210 }
211}
212
213pub(crate) fn promote_scalar_native(native: &NativeTensor, target: DType) -> Result<NativeTensor> {
214 let promoted = match (scalar_value_from_native(native)?, target) {
215 (ScalarValue::F32(value), DType::F32) => BackendScalar::from_value(value),
216 (ScalarValue::F32(value), DType::F64) => BackendScalar::from_value(value as f64),
217 (ScalarValue::F32(value), DType::C32) => {
218 BackendScalar::from_value(Complex32::new(value, 0.0))
219 }
220 (ScalarValue::F32(value), DType::C64) => {
221 BackendScalar::from_value(Complex64::new(value as f64, 0.0))
222 }
223 (ScalarValue::F32(_), DType::I64) => {
224 return Err(anyhow!(
225 "cannot promote f32 scalar to i64 without truncation"
226 ));
227 }
228 (ScalarValue::F32(_), DType::I32 | DType::Bool) => {
229 return Err(anyhow!(
230 "cannot promote f32 scalar to integer/bool without truncation"
231 ));
232 }
233 (ScalarValue::F64(value), DType::F32) => BackendScalar::from_value(value as f32),
234 (ScalarValue::F64(value), DType::F64) => BackendScalar::from_value(value),
235 (ScalarValue::F64(_), DType::I64) => {
236 return Err(anyhow!(
237 "cannot promote f64 scalar to i64 without truncation"
238 ));
239 }
240 (ScalarValue::F64(_), DType::I32 | DType::Bool) => {
241 return Err(anyhow!(
242 "cannot promote f64 scalar to integer/bool without truncation"
243 ));
244 }
245 (ScalarValue::F64(value), DType::C32) => {
246 BackendScalar::from_value(Complex32::new(value as f32, 0.0))
247 }
248 (ScalarValue::F64(value), DType::C64) => {
249 BackendScalar::from_value(Complex64::new(value, 0.0))
250 }
251 (ScalarValue::I32(value), DType::F32) => BackendScalar::from_value(value as f32),
252 (ScalarValue::I32(value), DType::F64) => BackendScalar::from_value(value as f64),
253 (ScalarValue::I32(value), DType::I32) => return Ok(scalar_native(value)),
254 (ScalarValue::I32(value), DType::I64) => BackendScalar::from_i64(value as i64),
255 (ScalarValue::I32(value), DType::C32) => {
256 BackendScalar::from_value(Complex32::new(value as f32, 0.0))
257 }
258 (ScalarValue::I32(value), DType::C64) => {
259 BackendScalar::from_value(Complex64::new(value as f64, 0.0))
260 }
261 (ScalarValue::I32(_), DType::Bool) => {
262 return Err(anyhow!("cannot promote i32 scalar to bool"));
263 }
264 (ScalarValue::I64(value), DType::F32) => BackendScalar::from_value(value as f32),
265 (ScalarValue::I64(value), DType::F64) => BackendScalar::from_value(value as f64),
266 (ScalarValue::I64(_), DType::I32 | DType::Bool) => {
267 return Err(anyhow!("cannot promote i64 scalar to i32/bool"));
268 }
269 (ScalarValue::I64(value), DType::I64) => BackendScalar::from_i64(value),
270 (ScalarValue::I64(value), DType::C32) => {
271 BackendScalar::from_value(Complex32::new(value as f32, 0.0))
272 }
273 (ScalarValue::I64(value), DType::C64) => {
274 BackendScalar::from_value(Complex64::new(value as f64, 0.0))
275 }
276 (ScalarValue::Bool(value), DType::F32) => {
277 BackendScalar::from_value(if value { 1.0_f32 } else { 0.0_f32 })
278 }
279 (ScalarValue::Bool(value), DType::F64) => {
280 BackendScalar::from_value(if value { 1.0 } else { 0.0 })
281 }
282 (ScalarValue::Bool(value), DType::I32) => {
283 return Ok(scalar_native(if value { 1 } else { 0 }));
284 }
285 (ScalarValue::Bool(value), DType::I64) => {
286 BackendScalar::from_i64(if value { 1 } else { 0 })
287 }
288 (ScalarValue::Bool(value), DType::Bool) => return Ok(scalar_native(value)),
289 (ScalarValue::Bool(value), DType::C32) => {
290 BackendScalar::from_value(Complex32::new(if value { 1.0 } else { 0.0 }, 0.0))
291 }
292 (ScalarValue::Bool(value), DType::C64) => {
293 BackendScalar::from_value(Complex64::new(if value { 1.0 } else { 0.0 }, 0.0))
294 }
295 (ScalarValue::C32(value), DType::F32) => BackendScalar::from_value(value.re),
296 (ScalarValue::C32(value), DType::F64) => BackendScalar::from_value(value.re as f64),
297 (ScalarValue::C32(_), DType::I32 | DType::I64 | DType::Bool) => {
298 return Err(anyhow!("cannot promote c32 scalar to integer/bool"));
299 }
300 (ScalarValue::C32(value), DType::C32) => BackendScalar::from_value(value),
301 (ScalarValue::C32(value), DType::C64) => {
302 BackendScalar::from_value(Complex64::new(value.re as f64, value.im as f64))
303 }
304 (ScalarValue::C64(value), DType::F32) => BackendScalar::from_value(value.re as f32),
305 (ScalarValue::C64(value), DType::F64) => BackendScalar::from_value(value.re),
306 (ScalarValue::C64(_), DType::I32 | DType::I64 | DType::Bool) => {
307 return Err(anyhow!("cannot promote c64 scalar to integer/bool"));
308 }
309 (ScalarValue::C64(value), DType::C32) => {
310 BackendScalar::from_value(Complex32::new(value.re as f32, value.im as f32))
311 }
312 (ScalarValue::C64(value), DType::C64) => BackendScalar::from_value(value),
313 };
314 Ok(promoted.native)
315}
316
317pub struct BackendScalar {
345 native: NativeTensor,
346 value: ScalarValue,
347}
348
349impl BackendScalar {
350 fn wrap_native(native: NativeTensor) -> Result<Self> {
351 if native.shape().is_empty() {
352 let value = scalar_value_from_native(&native)?;
353 Ok(Self { native, value })
354 } else {
355 Err(anyhow!(
356 "BackendScalar requires a rank-0 tensor, got shape {:?}",
357 native.shape()
358 ))
359 }
360 }
361
362 fn value(&self) -> ScalarValue {
363 self.value
364 }
365
366 fn from_i64(value: i64) -> Self {
367 Self {
368 native: scalar_native(value),
369 value: ScalarValue::I64(value),
370 }
371 }
372
373 fn from_i32(value: i32) -> Self {
374 Self {
375 native: scalar_native(value),
376 value: ScalarValue::I32(value),
377 }
378 }
379
380 fn from_bool(value: bool) -> Self {
381 Self {
382 native: scalar_native(value),
383 value: ScalarValue::Bool(value),
384 }
385 }
386
387 pub(crate) fn from_native(value: NativeTensor) -> Result<Self> {
388 Self::wrap_native(value)
389 }
390
391 pub(crate) fn as_native(&self) -> &NativeTensor {
392 &self.native
393 }
394
395 #[allow(private_bounds)]
412 pub fn from_value<T: ScalarTensorElement>(value: T) -> Self {
413 Self {
414 native: scalar_native(value),
415 value: T::scalar_value(value),
416 }
417 }
418
419 pub fn from_real(x: f64) -> Self {
431 Self::from_value(x)
432 }
433
434 pub fn from_complex(re: f64, im: f64) -> Self {
447 Self::from_value(Complex64::new(re, im))
448 }
449
450 pub fn new_real(x: f64) -> Self {
461 Self::from_real(x)
462 }
463
464 pub fn new_complex(re: f64, im: f64) -> Self {
475 Self::from_complex(re, im)
476 }
477
478 pub fn primal(&self) -> Self {
490 self.clone()
491 }
492
493 pub fn real(&self) -> f64 {
504 self.value().real()
505 }
506
507 pub fn imag(&self) -> f64 {
523 self.value().imag()
524 }
525
526 pub fn abs(&self) -> f64 {
542 self.value().abs()
543 }
544
545 pub fn is_complex(&self) -> bool {
556 self.value().is_complex()
557 }
558
559 pub fn is_real(&self) -> bool {
570 !self.is_complex()
571 }
572
573 pub fn is_zero(&self) -> bool {
584 self.value().is_zero()
585 }
586
587 pub fn as_f64(&self) -> Option<f64> {
603 match self.value() {
604 ScalarValue::F32(value) => Some(value as f64),
605 ScalarValue::F64(value) => Some(value),
606 ScalarValue::I32(value) => Some(value as f64),
607 ScalarValue::I64(value) => Some(value as f64),
608 ScalarValue::Bool(value) => Some(if value { 1.0 } else { 0.0 }),
609 ScalarValue::C32(_) | ScalarValue::C64(_) => None,
610 }
611 }
612
613 pub fn as_c64(&self) -> Option<Complex64> {
630 match self.value() {
631 ScalarValue::F32(_)
632 | ScalarValue::F64(_)
633 | ScalarValue::I32(_)
634 | ScalarValue::I64(_)
635 | ScalarValue::Bool(_) => None,
636 ScalarValue::C32(value) => Some(Complex64::new(value.re as f64, value.im as f64)),
637 ScalarValue::C64(value) => Some(value),
638 }
639 }
640
641 pub fn conj(&self) -> Self {
659 match self.value() {
660 ScalarValue::F32(value) => Self::from_value(value),
661 ScalarValue::F64(value) => Self::from_value(value),
662 ScalarValue::I32(value) => Self::from_i32(value),
663 ScalarValue::I64(value) => Self::from_i64(value),
664 ScalarValue::Bool(value) => Self::from_bool(value),
665 ScalarValue::C32(value) => Self::from_value(value.conj()),
666 ScalarValue::C64(value) => Self::from_value(value.conj()),
667 }
668 }
669
670 pub fn real_part(&self) -> Self {
685 Self::from_real(self.real())
686 }
687
688 pub fn imag_part(&self) -> Self {
704 Self::from_real(self.imag())
705 }
706
707 pub fn compose_complex(real: Self, imag: Self) -> Result<Self> {
727 if !real.is_real() || !imag.is_real() {
728 return Err(anyhow!(
729 "compose_complex requires real-valued inputs, got real={:?}, imag={:?}",
730 real.native.dtype(),
731 imag.native.dtype()
732 ));
733 }
734 Ok(Self::from_complex(real.real(), imag.real()))
735 }
736
737 pub fn sqrt(&self) -> Self {
750 if self.is_complex() || self.real() < 0.0 {
751 let value = self.value().into_complex().sqrt();
752 if value.im == 0.0 {
753 Self::from_real(value.re)
754 } else {
755 Self::from_value(value)
756 }
757 } else {
758 Self::from_real(self.real().sqrt())
759 }
760 }
761
762 pub fn powf(&self, exponent: f64) -> Self {
776 let needs_complex_promotion =
777 self.is_complex() || (self.real() < 0.0 && exponent.fract() != 0.0);
778 if needs_complex_promotion {
779 let value = self.value().into_complex().powf(exponent);
780 if value.im == 0.0 {
781 Self::from_real(value.re)
782 } else {
783 Self::from_value(value)
784 }
785 } else {
786 Self::from_real(self.real().powf(exponent))
787 }
788 }
789
790 pub fn powi(&self, exponent: i32) -> Self {
801 self.powf(exponent as f64)
802 }
803}
804
805impl SumFromStorage for BackendScalar {
806 fn sum_from_storage(storage: &Storage) -> Self {
807 match scalar_value_from_storage(storage) {
808 ScalarValue::F32(value) => Self::from_value(value),
809 ScalarValue::F64(value) => Self::from_value(value),
810 ScalarValue::I32(value) => Self::from_i32(value),
811 ScalarValue::I64(value) => Self::from_i64(value),
812 ScalarValue::Bool(value) => Self::from_bool(value),
813 ScalarValue::C32(value) => Self::from_value(value),
814 ScalarValue::C64(value) => Self::from_value(value),
815 }
816 }
817}
818
819impl From<f32> for BackendScalar {
820 fn from(value: f32) -> Self {
821 Self::from_value(value)
822 }
823}
824
825impl From<f64> for BackendScalar {
826 fn from(value: f64) -> Self {
827 Self::from_value(value)
828 }
829}
830
831impl From<Complex32> for BackendScalar {
832 fn from(value: Complex32) -> Self {
833 Self::from_value(value)
834 }
835}
836
837impl From<Complex64> for BackendScalar {
838 fn from(value: Complex64) -> Self {
839 Self::from_value(value)
840 }
841}
842
843impl TryFrom<BackendScalar> for f64 {
844 type Error = &'static str;
845
846 fn try_from(value: BackendScalar) -> std::result::Result<Self, Self::Error> {
847 match value.value() {
848 ScalarValue::F32(real) => Ok(real as f64),
849 ScalarValue::F64(real) => Ok(real),
850 ScalarValue::I32(real) => Ok(real as f64),
851 ScalarValue::I64(real) => Ok(real as f64),
852 ScalarValue::Bool(real) => Ok(if real { 1.0 } else { 0.0 }),
853 ScalarValue::C32(_) | ScalarValue::C64(_) => {
854 Err("cannot convert complex scalar to f64")
855 }
856 }
857 }
858}
859
860impl From<BackendScalar> for Complex64 {
861 fn from(value: BackendScalar) -> Self {
862 value.value().into_complex()
863 }
864}
865
866impl Add for BackendScalar {
867 type Output = Self;
868
869 fn add(self, rhs: Self) -> Self::Output {
870 match (self.value(), rhs.value()) {
871 (ScalarValue::F32(lhs), ScalarValue::F32(rhs)) => Self::from_value(lhs + rhs),
872 (lhs, rhs) if lhs.is_complex() || rhs.is_complex() => {
873 Self::from_value(lhs.into_complex() + rhs.into_complex())
874 }
875 (lhs, rhs) => Self::from_real(lhs.real() + rhs.real()),
876 }
877 }
878}
879
880impl Sub for BackendScalar {
881 type Output = Self;
882
883 fn sub(self, rhs: Self) -> Self::Output {
884 self + (-rhs)
885 }
886}
887
888impl Mul for BackendScalar {
889 type Output = Self;
890
891 fn mul(self, rhs: Self) -> Self::Output {
892 match (self.value(), rhs.value()) {
893 (ScalarValue::F32(lhs), ScalarValue::F32(rhs)) => Self::from_value(lhs * rhs),
894 (lhs, rhs) if lhs.is_complex() || rhs.is_complex() => {
895 Self::from_value(lhs.into_complex() * rhs.into_complex())
896 }
897 (lhs, rhs) => Self::from_real(lhs.real() * rhs.real()),
898 }
899 }
900}
901
902impl Div for BackendScalar {
903 type Output = Self;
904
905 fn div(self, rhs: Self) -> Self::Output {
906 match (self.value(), rhs.value()) {
907 (ScalarValue::F32(lhs), ScalarValue::F32(rhs)) => Self::from_value(lhs / rhs),
908 (lhs, rhs) if lhs.is_complex() || rhs.is_complex() => {
909 Self::from_value(lhs.into_complex() / rhs.into_complex())
910 }
911 (lhs, rhs) => Self::from_real(lhs.real() / rhs.real()),
912 }
913 }
914}
915
916impl Neg for BackendScalar {
917 type Output = Self;
918
919 fn neg(self) -> Self::Output {
920 match self.value() {
921 ScalarValue::F32(value) => Self::from_value(-value),
922 ScalarValue::F64(value) => Self::from_value(-value),
923 ScalarValue::I32(value) => Self::from_i32(-value),
924 ScalarValue::I64(value) => Self::from_i64(-value),
925 ScalarValue::Bool(value) => Self::from_real(if value { -1.0 } else { 0.0 }),
926 ScalarValue::C32(value) => Self::from_value(-value),
927 ScalarValue::C64(value) => Self::from_value(-value),
928 }
929 }
930}
931
932impl Mul<BackendScalar> for f64 {
933 type Output = BackendScalar;
934
935 fn mul(self, rhs: BackendScalar) -> Self::Output {
936 BackendScalar::from_real(self) * rhs
937 }
938}
939
940impl Mul<BackendScalar> for Complex64 {
941 type Output = BackendScalar;
942
943 fn mul(self, rhs: BackendScalar) -> Self::Output {
944 BackendScalar::from(self) * rhs
945 }
946}
947
948impl Div<BackendScalar> for Complex64 {
949 type Output = BackendScalar;
950
951 fn div(self, rhs: BackendScalar) -> Self::Output {
952 BackendScalar::from(self) / rhs
953 }
954}
955
956impl Default for BackendScalar {
957 fn default() -> Self {
958 Self::zero()
959 }
960}
961
962impl Zero for BackendScalar {
963 fn zero() -> Self {
964 Self::from_real(0.0)
965 }
966
967 fn is_zero(&self) -> bool {
968 BackendScalar::is_zero(self)
969 }
970}
971
972impl One for BackendScalar {
973 fn one() -> Self {
974 Self::from_real(1.0)
975 }
976}
977
978impl PartialEq for BackendScalar {
979 fn eq(&self, other: &Self) -> bool {
980 self.native.dtype() == other.native.dtype() && self.value() == other.value()
981 }
982}
983
984impl PartialOrd for BackendScalar {
985 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
986 match (self.value(), other.value()) {
987 (ScalarValue::F32(lhs), ScalarValue::F32(rhs)) => lhs.partial_cmp(&rhs),
988 (ScalarValue::F32(lhs), ScalarValue::F64(rhs)) => (lhs as f64).partial_cmp(&rhs),
989 (ScalarValue::F32(lhs), ScalarValue::I32(rhs)) => {
990 (lhs as f64).partial_cmp(&(rhs as f64))
991 }
992 (ScalarValue::F32(lhs), ScalarValue::I64(rhs)) => {
993 (lhs as f64).partial_cmp(&(rhs as f64))
994 }
995 (ScalarValue::F32(lhs), ScalarValue::Bool(rhs)) => {
996 (lhs as f64).partial_cmp(&(if rhs { 1.0 } else { 0.0 }))
997 }
998 (ScalarValue::F64(lhs), ScalarValue::F32(rhs)) => lhs.partial_cmp(&(rhs as f64)),
999 (ScalarValue::F64(lhs), ScalarValue::F64(rhs)) => lhs.partial_cmp(&rhs),
1000 (ScalarValue::F64(lhs), ScalarValue::I32(rhs)) => lhs.partial_cmp(&(rhs as f64)),
1001 (ScalarValue::F64(lhs), ScalarValue::I64(rhs)) => lhs.partial_cmp(&(rhs as f64)),
1002 (ScalarValue::F64(lhs), ScalarValue::Bool(rhs)) => {
1003 lhs.partial_cmp(&(if rhs { 1.0 } else { 0.0 }))
1004 }
1005 (ScalarValue::I32(lhs), ScalarValue::F32(rhs)) => {
1006 (lhs as f64).partial_cmp(&(rhs as f64))
1007 }
1008 (ScalarValue::I32(lhs), ScalarValue::F64(rhs)) => (lhs as f64).partial_cmp(&rhs),
1009 (ScalarValue::I32(lhs), ScalarValue::I32(rhs)) => lhs.partial_cmp(&rhs),
1010 (ScalarValue::I32(lhs), ScalarValue::I64(rhs)) => {
1011 (lhs as f64).partial_cmp(&(rhs as f64))
1012 }
1013 (ScalarValue::I32(lhs), ScalarValue::Bool(rhs)) => {
1014 (lhs as f64).partial_cmp(&(if rhs { 1.0 } else { 0.0 }))
1015 }
1016 (ScalarValue::I64(lhs), ScalarValue::F32(rhs)) => {
1017 (lhs as f64).partial_cmp(&(rhs as f64))
1018 }
1019 (ScalarValue::I64(lhs), ScalarValue::F64(rhs)) => (lhs as f64).partial_cmp(&rhs),
1020 (ScalarValue::I64(lhs), ScalarValue::I32(rhs)) => {
1021 (lhs as f64).partial_cmp(&(rhs as f64))
1022 }
1023 (ScalarValue::I64(lhs), ScalarValue::I64(rhs)) => lhs.partial_cmp(&rhs),
1024 (ScalarValue::I64(lhs), ScalarValue::Bool(rhs)) => {
1025 (lhs as f64).partial_cmp(&(if rhs { 1.0 } else { 0.0 }))
1026 }
1027 (ScalarValue::Bool(lhs), ScalarValue::F32(rhs)) => {
1028 (if lhs { 1.0 } else { 0.0 }).partial_cmp(&(rhs as f64))
1029 }
1030 (ScalarValue::Bool(lhs), ScalarValue::F64(rhs)) => {
1031 (if lhs { 1.0 } else { 0.0 }).partial_cmp(&rhs)
1032 }
1033 (ScalarValue::Bool(lhs), ScalarValue::I32(rhs)) => {
1034 (if lhs { 1.0 } else { 0.0 }).partial_cmp(&(rhs as f64))
1035 }
1036 (ScalarValue::Bool(lhs), ScalarValue::I64(rhs)) => {
1037 (if lhs { 1.0 } else { 0.0 }).partial_cmp(&(rhs as f64))
1038 }
1039 (ScalarValue::Bool(lhs), ScalarValue::Bool(rhs)) => lhs.partial_cmp(&rhs),
1040 _ => None,
1041 }
1042 }
1043}
1044
1045impl fmt::Display for BackendScalar {
1046 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1047 match self.value() {
1048 ScalarValue::F32(value) => value.fmt(f),
1049 ScalarValue::F64(value) => value.fmt(f),
1050 ScalarValue::I32(value) => value.fmt(f),
1051 ScalarValue::I64(value) => value.fmt(f),
1052 ScalarValue::Bool(value) => value.fmt(f),
1053 ScalarValue::C32(value) => value.fmt(f),
1054 ScalarValue::C64(value) => value.fmt(f),
1055 }
1056 }
1057}
1058
1059impl fmt::Debug for BackendScalar {
1060 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1061 f.debug_struct("BackendScalar")
1062 .field("dtype", &self.native.dtype())
1063 .field("value", &self.value())
1064 .finish()
1065 }
1066}
1067
1068impl Clone for BackendScalar {
1069 fn clone(&self) -> Self {
1070 match self.value {
1071 ScalarValue::F32(value) => Self::from_value(value),
1072 ScalarValue::F64(value) => Self::from_value(value),
1073 ScalarValue::I32(value) => Self::from_i32(value),
1074 ScalarValue::I64(value) => Self::from_i64(value),
1075 ScalarValue::Bool(value) => Self::from_bool(value),
1076 ScalarValue::C32(value) => Self::from_value(value),
1077 ScalarValue::C64(value) => Self::from_value(value),
1078 }
1079 }
1080}
1081
1082#[cfg(test)]
1083mod tests;