imagnum 0.2.21

A Rust library providing versatile numeric types supporting integers and floats designed for the Lucia programming language.
Documentation
use crate::foundation::{Float, FloatKind, Int, SmallFloat, SmallInt};
use bigdecimal::BigDecimal;
use num_bigint::BigInt;
use num_traits::FromPrimitive;
use std::str::FromStr;

pub fn int_to_string(i: &Int) -> String {
    match i {
        Int::Big(b) => b.to_string().trim_start_matches('-').to_string(),
        Int::Small(s) => {
            let s = match s {
                SmallInt::I8(v) => v.to_string(),
                SmallInt::U8(v) => v.to_string(),
                SmallInt::I16(v) => v.to_string(),
                SmallInt::U16(v) => v.to_string(),
                SmallInt::I32(v) => v.to_string(),
                SmallInt::U32(v) => v.to_string(),
                SmallInt::I64(v) => v.to_string(),
                SmallInt::U64(v) => v.to_string(),
                SmallInt::I128(v) => v.to_string(),
                SmallInt::U128(v) => v.to_string(),
                SmallInt::USize(v) => v.to_string(),
                SmallInt::ISize(v) => v.to_string(),
            };
            s.trim_start_matches('-').to_string()
        }
    }
}

pub fn float_to_parts(f: &Float) -> (String, i32, bool, FloatKind) {
    match f {
        Float::Big(bd) => from_bigdecimal(bd),
        Float::Irrational(bd) => {
            let (m, e, neg, _k) = from_bigdecimal(bd);
            (m, e, neg, FloatKind::Irrational)
        }
        Float::Recurring(bd) => {
            let (m, e, neg, _k) = from_bigdecimal(bd);
            (m, e, neg, FloatKind::Recurring)
        }
        Float::Small(s) => match s {
            SmallFloat::F32(v) => {
                let s = v.to_string();
                match BigDecimal::from_str(&s) {
                    Ok(bd) => from_bigdecimal(&bd),
                    Err(_) => (String::new(), 0, v.is_sign_negative(), FloatKind::Finite),
                }
            }
            SmallFloat::F64(v) => {
                let s = v.to_string();
                match BigDecimal::from_str(&s) {
                    Ok(bd) => from_bigdecimal(&bd),
                    Err(_) => (String::new(), 0, v.is_sign_negative(), FloatKind::Finite),
                }
            }
        },
        Float::NaN => (String::new(), 0, false, FloatKind::NaN),
        Float::Infinity => (String::new(), 0, false, FloatKind::Infinity),
        Float::NegInfinity => (String::new(), 0, true, FloatKind::NegInfinity),
        Float::Complex(_, _) => (String::new(), 0, false, FloatKind::Complex),
    }
}

fn from_bigdecimal(bd: &BigDecimal) -> (String, i32, bool, FloatKind) {
    let s = bd.normalized().to_string();
    let neg = s.starts_with('-');
    let s = s.trim_start_matches('-');

    if s == "0" || s.is_empty() {
        return ("0".to_string(), 0, false, FloatKind::Finite);
    }

    let parts: Vec<&str> = s.split('E').collect();
    let (base, exp_part) = if parts.len() == 2 {
        (parts[0], parts[1])
    } else {
        (s, "0")
    };

    let exp_from_e: i32 = exp_part.parse().unwrap_or(0);

    let (mant, exp) = if let Some(dot) = base.find('.') {
        let mantissa = base[..dot].to_string() + &base[dot + 1..];
        let exp_decimal = -((base.len() - dot - 1) as i32);
        (mantissa.trim_start_matches('0').to_string(), exp_decimal)
    } else {
        (base.trim_start_matches('0').to_string(), 0)
    };

    let final_exp = exp + exp_from_e;
    (mant, final_exp, neg, FloatKind::Finite)
}

pub fn float_is_zero(f: &Float) -> bool {
    match f {
        Float::Big(bd) | Float::Irrational(bd) | Float::Recurring(bd) => {
            let (m, _e, _neg, _k) = from_bigdecimal(bd);
            m == "0"
        }
        Float::Small(s) => match s {
            SmallFloat::F32(v) => *v == 0.0,
            SmallFloat::F64(v) => *v == 0.0,
        },
        _ => false,
    }
}

pub fn float_is_one(f: &Float) -> bool {
    match f {
        Float::Big(bd) | Float::Irrational(bd) | Float::Recurring(bd) => {
            bd.to_string() == "1" || bd.to_string() == "1.0"
        }
        Float::Small(s) => match s {
            SmallFloat::F32(v) => *v == 1.0,
            SmallFloat::F64(v) => *v == 1.0,
        },
        _ => false,
    }
}

pub fn float_is_neg_one(f: &Float) -> bool {
    match f {
        Float::Big(bd) | Float::Irrational(bd) | Float::Recurring(bd) => {
            bd.to_string() == "-1" || bd.to_string() == "-1.0"
        }
        Float::Small(s) => match s {
            SmallFloat::F32(v) => *v == -1.0,
            SmallFloat::F64(v) => *v == -1.0,
        },
        _ => false,
    }
}

pub fn float_to_bigdecimal(f: &Float) -> Option<BigDecimal> {
    match f {
        Float::Big(bd) | Float::Irrational(bd) | Float::Recurring(bd) => Some(bd.clone()),
        Float::Small(s) => match s {
            SmallFloat::F32(v) => BigDecimal::from_f32(*v),
            SmallFloat::F64(v) => BigDecimal::from_f64(*v),
        },
        _ => None,
    }
}

pub fn int_to_parts(i: &Int) -> (String, bool, FloatKind) {
    match i {
        Int::Big(bi) => {
            let s = bi.to_string();
            let neg = s.starts_with('-');
            let digits = s.trim_start_matches('-').to_string();
            (digits, neg, FloatKind::Finite)
        }
        Int::Small(sv) => {
            let s = match sv {
                SmallInt::I8(v) => v.to_string(),
                SmallInt::U8(v) => v.to_string(),
                SmallInt::I16(v) => v.to_string(),
                SmallInt::U16(v) => v.to_string(),
                SmallInt::I32(v) => v.to_string(),
                SmallInt::U32(v) => v.to_string(),
                SmallInt::I64(v) => v.to_string(),
                SmallInt::U64(v) => v.to_string(),
                SmallInt::I128(v) => v.to_string(),
                SmallInt::U128(v) => v.to_string(),
                SmallInt::USize(v) => v.to_string(),
                SmallInt::ISize(v) => v.to_string(),
            };
            let neg = s.starts_with('-');
            let digits = s.trim_start_matches('-').to_string();
            (digits, neg, FloatKind::Finite)
        }
    }
}

pub fn int_to_bigint(i: &Int) -> BigInt {
    match i {
        Int::Big(bi) => bi.clone(),
        Int::Small(sv) => {
            let s = match sv {
                SmallInt::I8(v) => v.to_string(),
                SmallInt::U8(v) => v.to_string(),
                SmallInt::I16(v) => v.to_string(),
                SmallInt::U16(v) => v.to_string(),
                SmallInt::I32(v) => v.to_string(),
                SmallInt::U32(v) => v.to_string(),
                SmallInt::I64(v) => v.to_string(),
                SmallInt::U64(v) => v.to_string(),
                SmallInt::I128(v) => v.to_string(),
                SmallInt::U128(v) => v.to_string(),
                SmallInt::USize(v) => v.to_string(),
                SmallInt::ISize(v) => v.to_string(),
            };
            match BigInt::from_str(&s) {
                Ok(bi) => bi,
                Err(_) => BigInt::from(0),
            }
        }
    }
}

pub fn make_int_from_parts(digits: String, negative: bool, _kind: FloatKind) -> Int {
    match BigDecimal::from_str(&digits) {
        Ok(_) => {
            match BigInt::from_str(&digits) {
                Ok(mut bi) => {
                    if negative {
                        bi = -bi
                    };
                    Int::Big(bi)
                }
                Err(_) => Int::new(),
            }
        }
        Err(_) => {
            match BigInt::from_str(&digits) {
                Ok(mut bi) => {
                    if negative {
                        bi = -bi
                    };
                    Int::Big(bi)
                }
                Err(_) => Int::new(),
            }
        }
    }
}

pub fn make_float_from_parts(
    mantissa: String,
    exponent: i32,
    negative: bool,
    kind: FloatKind,
) -> Float {
    match kind {
        FloatKind::NaN => Float::NaN,
        FloatKind::Infinity => {
            if negative {
                Float::NegInfinity
            } else {
                Float::Infinity
            }
        }
        FloatKind::NegInfinity => Float::NegInfinity,
        FloatKind::Irrational | FloatKind::Finite | FloatKind::Recurring => {
            let mut s = mantissa;
            if s.is_empty() {
                s = "0".to_string();
            }
            if negative && !s.starts_with('-') {
                s = format!("-{}", s);
            }
            if let Ok(bi) = BigInt::from_str(&s) {
                let scale = -(exponent as i64);
                let bd = BigDecimal::new(bi, scale);
                if kind == FloatKind::Irrational {
                    Float::Irrational(bd)
                } else if kind == FloatKind::Recurring {
                    Float::Recurring(bd)
                } else {
                    Float::Big(bd)
                }
            } else {
                let s2 = if exponent == 0 {
                    s.clone()
                } else {
                    format!("{}e{}", s, exponent)
                };
                match BigDecimal::from_str(&s2) {
                    Ok(bd) => {
                        if kind == FloatKind::Irrational {
                            Float::Irrational(bd)
                        } else if kind == FloatKind::Recurring {
                            Float::Recurring(bd)
                        } else {
                            Float::Big(bd)
                        }
                    }
                    Err(_) => Float::NaN,
                }
            }
        }
        FloatKind::Complex | FloatKind::Imaginary => {
            Float::NaN
        }
    }
}

pub fn int_is_nan(_i: &Int) -> bool {
    false
}
pub fn int_is_infinite(_i: &Int) -> bool {
    false
}
pub fn float_kind(f: &Float) -> FloatKind {
    float_to_parts(f).3
}
pub fn float_is_negative(f: &Float) -> bool {
    float_to_parts(f).2
}