use std::any::Any;
use std::convert::TryFrom;
use std::fmt;
pub trait JitNumeric: Copy + Send + Sync + 'static + fmt::Debug {
fn to_f64(&self) -> f64;
fn from_f64(value: f64) -> Self;
fn zero() -> Self;
fn one() -> Self;
fn min_value() -> Self;
fn max_value() -> Self;
fn add(&self, other: &Self) -> Self;
fn sub(&self, other: &Self) -> Self;
fn mul(&self, other: &Self) -> Self;
fn div(&self, other: &Self) -> Self;
fn sqrt(&self) -> Self;
fn pow(&self, exp: i32) -> Self;
fn is_nan(&self) -> bool;
fn type_name() -> &'static str;
}
impl JitNumeric for f64 {
fn to_f64(&self) -> f64 {
*self
}
fn from_f64(value: f64) -> Self {
value
}
fn zero() -> Self {
0.0
}
fn one() -> Self {
1.0
}
fn min_value() -> Self {
f64::MIN
}
fn max_value() -> Self {
f64::MAX
}
fn add(&self, other: &Self) -> Self {
self + other
}
fn sub(&self, other: &Self) -> Self {
self - other
}
fn mul(&self, other: &Self) -> Self {
self * other
}
fn div(&self, other: &Self) -> Self {
self / other
}
fn sqrt(&self) -> Self {
f64::sqrt(*self)
}
fn pow(&self, exp: i32) -> Self {
self.powi(exp)
}
fn is_nan(&self) -> bool {
f64::is_nan(*self)
}
fn type_name() -> &'static str {
"f64"
}
}
impl JitNumeric for f32 {
fn to_f64(&self) -> f64 {
*self as f64
}
fn from_f64(value: f64) -> Self {
value as f32
}
fn zero() -> Self {
0.0
}
fn one() -> Self {
1.0
}
fn min_value() -> Self {
f32::MIN
}
fn max_value() -> Self {
f32::MAX
}
fn add(&self, other: &Self) -> Self {
self + other
}
fn sub(&self, other: &Self) -> Self {
self - other
}
fn mul(&self, other: &Self) -> Self {
self * other
}
fn div(&self, other: &Self) -> Self {
self / other
}
fn sqrt(&self) -> Self {
f32::sqrt(*self)
}
fn pow(&self, exp: i32) -> Self {
self.powi(exp)
}
fn is_nan(&self) -> bool {
f32::is_nan(*self)
}
fn type_name() -> &'static str {
"f32"
}
}
impl JitNumeric for i64 {
fn to_f64(&self) -> f64 {
*self as f64
}
fn from_f64(value: f64) -> Self {
value as i64
}
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
fn min_value() -> Self {
i64::MIN
}
fn max_value() -> Self {
i64::MAX
}
fn add(&self, other: &Self) -> Self {
self + other
}
fn sub(&self, other: &Self) -> Self {
self - other
}
fn mul(&self, other: &Self) -> Self {
self * other
}
fn div(&self, other: &Self) -> Self {
self / other
}
fn sqrt(&self) -> Self {
(*self as f64).sqrt() as i64
}
fn pow(&self, exp: i32) -> Self {
(*self as f64).powi(exp) as i64
}
fn is_nan(&self) -> bool {
false }
fn type_name() -> &'static str {
"i64"
}
}
impl JitNumeric for i32 {
fn to_f64(&self) -> f64 {
*self as f64
}
fn from_f64(value: f64) -> Self {
value as i32
}
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
fn min_value() -> Self {
i32::MIN
}
fn max_value() -> Self {
i32::MAX
}
fn add(&self, other: &Self) -> Self {
self + other
}
fn sub(&self, other: &Self) -> Self {
self - other
}
fn mul(&self, other: &Self) -> Self {
self * other
}
fn div(&self, other: &Self) -> Self {
self / other
}
fn sqrt(&self) -> Self {
(*self as f64).sqrt() as i32
}
fn pow(&self, exp: i32) -> Self {
(*self as f64).powi(exp) as i32
}
fn is_nan(&self) -> bool {
false }
fn type_name() -> &'static str {
"i32"
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum NumericValue {
F64(f64),
F32(f32),
I64(i64),
I32(i32),
}
impl NumericValue {
pub fn to_f64(&self) -> f64 {
match self {
NumericValue::F64(val) => *val,
NumericValue::F32(val) => *val as f64,
NumericValue::I64(val) => *val as f64,
NumericValue::I32(val) => *val as f64,
}
}
pub fn type_name(&self) -> &'static str {
match self {
NumericValue::F64(_) => "f64",
NumericValue::F32(_) => "f32",
NumericValue::I64(_) => "i64",
NumericValue::I32(_) => "i32",
}
}
}
impl From<f64> for NumericValue {
fn from(val: f64) -> Self {
NumericValue::F64(val)
}
}
impl From<f32> for NumericValue {
fn from(val: f32) -> Self {
NumericValue::F32(val)
}
}
impl From<i64> for NumericValue {
fn from(val: i64) -> Self {
NumericValue::I64(val)
}
}
impl From<i32> for NumericValue {
fn from(val: i32) -> Self {
NumericValue::I32(val)
}
}
pub trait JitType: Send + Sync + 'static {
type Output;
fn to_numeric_value(&self) -> Option<NumericValue>;
fn from_numeric_value(value: &NumericValue) -> Option<Self>
where
Self: Sized;
fn type_name() -> &'static str;
}
impl JitType for f64 {
type Output = f64;
fn to_numeric_value(&self) -> Option<NumericValue> {
Some(NumericValue::F64(*self))
}
fn from_numeric_value(value: &NumericValue) -> Option<Self> {
match value {
NumericValue::F64(val) => Some(*val),
NumericValue::F32(val) => Some(*val as f64),
NumericValue::I64(val) => Some(*val as f64),
NumericValue::I32(val) => Some(*val as f64),
}
}
fn type_name() -> &'static str {
"f64"
}
}
impl JitType for f32 {
type Output = f32;
fn to_numeric_value(&self) -> Option<NumericValue> {
Some(NumericValue::F32(*self))
}
fn from_numeric_value(value: &NumericValue) -> Option<Self> {
match value {
NumericValue::F64(val) => Some(*val as f32),
NumericValue::F32(val) => Some(*val),
NumericValue::I64(val) => Some(*val as f32),
NumericValue::I32(val) => Some(*val as f32),
}
}
fn type_name() -> &'static str {
"f32"
}
}
impl JitType for i64 {
type Output = i64;
fn to_numeric_value(&self) -> Option<NumericValue> {
Some(NumericValue::I64(*self))
}
fn from_numeric_value(value: &NumericValue) -> Option<Self> {
match value {
NumericValue::F64(val) => Some(*val as i64),
NumericValue::F32(val) => Some(*val as i64),
NumericValue::I64(val) => Some(*val),
NumericValue::I32(val) => Some(*val as i64),
}
}
fn type_name() -> &'static str {
"i64"
}
}
impl JitType for i32 {
type Output = i32;
fn to_numeric_value(&self) -> Option<NumericValue> {
Some(NumericValue::I32(*self))
}
fn from_numeric_value(value: &NumericValue) -> Option<Self> {
match value {
NumericValue::F64(val) => Some(*val as i32),
NumericValue::F32(val) => Some(*val as i32),
NumericValue::I64(val) => Some(*val as i32),
NumericValue::I32(val) => Some(*val),
}
}
fn type_name() -> &'static str {
"i32"
}
}
#[derive(Debug, Clone)]
pub enum TypedVector {
F64(Vec<f64>),
F32(Vec<f32>),
I64(Vec<i64>),
I32(Vec<i32>),
}
impl TypedVector {
pub fn new<T: JitType + Clone>(values: Vec<T>) -> Option<Self> {
if T::type_name() == "f64" {
if let Some(vals) = to_f64_vec(&values) {
return Some(TypedVector::F64(vals));
}
} else if T::type_name() == "f32" {
if let Some(vals) = to_f32_vec(&values) {
return Some(TypedVector::F32(vals));
}
} else if T::type_name() == "i64" {
if let Some(vals) = to_i64_vec(&values) {
return Some(TypedVector::I64(vals));
}
} else if T::type_name() == "i32" {
if let Some(vals) = to_i32_vec(&values) {
return Some(TypedVector::I32(vals));
}
}
None
}
pub fn len(&self) -> usize {
match self {
TypedVector::F64(vec) => vec.len(),
TypedVector::F32(vec) => vec.len(),
TypedVector::I64(vec) => vec.len(),
TypedVector::I32(vec) => vec.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn type_name(&self) -> &'static str {
match self {
TypedVector::F64(_) => "f64",
TypedVector::F32(_) => "f32",
TypedVector::I64(_) => "i64",
TypedVector::I32(_) => "i32",
}
}
pub fn to_f64_vec(&self) -> Vec<f64> {
match self {
TypedVector::F64(vec) => vec.clone(),
TypedVector::F32(vec) => vec.iter().map(|&x| x as f64).collect(),
TypedVector::I64(vec) => vec.iter().map(|&x| x as f64).collect(),
TypedVector::I32(vec) => vec.iter().map(|&x| x as f64).collect(),
}
}
}
fn to_f64_vec<T: Any>(values: &[T]) -> Option<Vec<f64>> {
if let Some(vals) = downcast_slice::<T, f64>(values) {
return Some(vals.to_vec());
}
None
}
fn to_f32_vec<T: Any>(values: &[T]) -> Option<Vec<f32>> {
if let Some(vals) = downcast_slice::<T, f32>(values) {
return Some(vals.to_vec());
}
None
}
fn to_i64_vec<T: Any>(values: &[T]) -> Option<Vec<i64>> {
if let Some(vals) = downcast_slice::<T, i64>(values) {
return Some(vals.to_vec());
}
None
}
fn to_i32_vec<T: Any>(values: &[T]) -> Option<Vec<i32>> {
if let Some(vals) = downcast_slice::<T, i32>(values) {
return Some(vals.to_vec());
}
None
}
fn downcast_slice<T: Any, U: 'static>(slice: &[T]) -> Option<&[U]> {
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<U>() {
let ptr = slice as *const [T] as *const [U];
unsafe { Some(&*ptr) }
} else {
None
}
}