fix imaginary, refactor
This commit is contained in:
parent
0560847753
commit
59f3e320e3
6 changed files with 214 additions and 113 deletions
|
@ -63,7 +63,8 @@ impl Highlighter for TalcHelper {
|
||||||
| TokenKind::True
|
| TokenKind::True
|
||||||
| TokenKind::False
|
| TokenKind::False
|
||||||
| TokenKind::Integer
|
| TokenKind::Integer
|
||||||
| TokenKind::Float => "\x1b[93m",
|
| TokenKind::Float
|
||||||
|
| TokenKind::Imaginary => "\x1b[93m",
|
||||||
TokenKind::String => "\x1b[92m",
|
TokenKind::String => "\x1b[92m",
|
||||||
TokenKind::Symbol => "\x1b[96m",
|
TokenKind::Symbol => "\x1b[96m",
|
||||||
_ => "",
|
_ => "",
|
||||||
|
|
|
@ -76,11 +76,11 @@ impl Display for Exception {
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! exception {
|
macro_rules! exception {
|
||||||
($exc_ty:expr, $fstr:literal, $($arg:expr),*) => {
|
($exc_ty:expr, $($t:tt)*) => {
|
||||||
$crate::exception::Exception::new_with_msg(
|
$crate::exception::Exception::new_with_msg(
|
||||||
$exc_ty,
|
$exc_ty,
|
||||||
$crate::lstring::LString::from(
|
$crate::lstring::LString::from(
|
||||||
format!($fstr, $($arg),*)
|
format!($($t)*)
|
||||||
).into()
|
).into()
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,6 +67,7 @@ pub enum TokenKind {
|
||||||
Identifier,
|
Identifier,
|
||||||
Integer,
|
Integer,
|
||||||
Float,
|
Float,
|
||||||
|
Imaginary,
|
||||||
String,
|
String,
|
||||||
Symbol,
|
Symbol,
|
||||||
And,
|
And,
|
||||||
|
@ -154,6 +155,7 @@ impl TokenKind {
|
||||||
K::Identifier => "identifier",
|
K::Identifier => "identifier",
|
||||||
K::Integer => "integer",
|
K::Integer => "integer",
|
||||||
K::Float => "float",
|
K::Float => "float",
|
||||||
|
K::Imaginary => "imaginary",
|
||||||
K::String => "string",
|
K::String => "string",
|
||||||
K::Symbol => "symbol",
|
K::Symbol => "symbol",
|
||||||
K::And => "'and'",
|
K::And => "'and'",
|
||||||
|
@ -324,6 +326,15 @@ impl<'s> Lexer<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_imag(&mut self) -> Result<Token<'s>> {
|
||||||
|
self.next()?;
|
||||||
|
if is_xid_start(self.peek()?) {
|
||||||
|
self.unexpected()
|
||||||
|
} else {
|
||||||
|
self.emit(K::Imaginary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn next_float(&mut self, mut has_e: bool) -> Result<Token<'s>> {
|
fn next_float(&mut self, mut has_e: bool) -> Result<Token<'s>> {
|
||||||
while !has_e {
|
while !has_e {
|
||||||
while matches!(self.peek()?, '_' | '0'..='9') {
|
while matches!(self.peek()?, '_' | '0'..='9') {
|
||||||
|
@ -331,6 +342,7 @@ impl<'s> Lexer<'s> {
|
||||||
}
|
}
|
||||||
match self.peek()? {
|
match self.peek()? {
|
||||||
'e' => { self.next()?; has_e = true }
|
'e' => { self.next()?; has_e = true }
|
||||||
|
'i' => return self.next_imag(),
|
||||||
c if is_xid_start(c) => return self.unexpected(),
|
c if is_xid_start(c) => return self.unexpected(),
|
||||||
_ => return self.emit(K::Float)
|
_ => return self.emit(K::Float)
|
||||||
}
|
}
|
||||||
|
@ -341,10 +353,10 @@ impl<'s> Lexer<'s> {
|
||||||
while matches!(self.peek()?, '_' | '0'..='9') {
|
while matches!(self.peek()?, '_' | '0'..='9') {
|
||||||
self.next()?;
|
self.next()?;
|
||||||
}
|
}
|
||||||
if is_xid_start(self.peek()?) {
|
match self.peek()? {
|
||||||
self.unexpected()
|
'i' => self.next_imag(),
|
||||||
} else {
|
c if is_xid_start(c) => self.unexpected(),
|
||||||
self.emit(K::Float)
|
_ => self.emit(K::Float)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,8 +368,8 @@ impl<'s> Lexer<'s> {
|
||||||
'o' => { self.next()?; return self.next_int_base(8) },
|
'o' => { self.next()?; return self.next_int_base(8) },
|
||||||
's' => { self.next()?; return self.next_int_base(6) },
|
's' => { self.next()?; return self.next_int_base(6) },
|
||||||
'b' => { self.next()?; return self.next_int_base(2) },
|
'b' => { self.next()?; return self.next_int_base(2) },
|
||||||
|
'0'..='9' | '.' | 'e' | 'i' => (),
|
||||||
c if is_xid_start(c) => return self.unexpected(),
|
c if is_xid_start(c) => return self.unexpected(),
|
||||||
'0'..='9' => (),
|
|
||||||
_ => return self.emit(K::Integer)
|
_ => return self.emit(K::Integer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +379,7 @@ impl<'s> Lexer<'s> {
|
||||||
match self.peek()? {
|
match self.peek()? {
|
||||||
'r' => todo!("arbitrary radix integer literals"),
|
'r' => todo!("arbitrary radix integer literals"),
|
||||||
'e' => { self.next()?; self.next_float(true) },
|
'e' => { self.next()?; self.next_float(true) },
|
||||||
|
'i' => self.next_imag(),
|
||||||
'.' => {
|
'.' => {
|
||||||
if self.peek_n(1) == Some('.') {
|
if self.peek_n(1) == Some('.') {
|
||||||
self.emit(K::Integer)
|
self.emit(K::Integer)
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::iter::Peekable;
|
||||||
use crate::{lstr, lstring::LStr, symbol::Symbol, value::Value};
|
use crate::{lstr, lstring::LStr, symbol::Symbol, value::Value};
|
||||||
|
|
||||||
use super::{ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp}, parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError, Token, TokenKind};
|
use super::{ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp}, parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError, Token, TokenKind};
|
||||||
|
use num_complex::Complex64;
|
||||||
use TokenKind as T;
|
use TokenKind as T;
|
||||||
use ExprKind as E;
|
use ExprKind as E;
|
||||||
|
|
||||||
|
@ -179,6 +180,7 @@ impl TokenKind {
|
||||||
| T::Try
|
| T::Try
|
||||||
| T::Integer
|
| T::Integer
|
||||||
| T::Float
|
| T::Float
|
||||||
|
| T::Imaginary
|
||||||
| T::String
|
| T::String
|
||||||
| T::Symbol
|
| T::Symbol
|
||||||
| T::True
|
| T::True
|
||||||
|
@ -371,6 +373,11 @@ impl<'s> Parser<'s> {
|
||||||
.span_err(tok.span)?;
|
.span_err(tok.span)?;
|
||||||
Ok(E::Literal(x.into()).span(tok.span))
|
Ok(E::Literal(x.into()).span(tok.span))
|
||||||
},
|
},
|
||||||
|
T::Imaginary => {
|
||||||
|
let x = parse_float(&tok.content[..tok.content.len()-1])
|
||||||
|
.span_err(tok.span)?;
|
||||||
|
Ok(E::Literal(Complex64::new(0.0, x).into()).span(tok.span))
|
||||||
|
},
|
||||||
T::String => {
|
T::String => {
|
||||||
let inner = &tok.content[1..tok.content.len()-1];
|
let inner = &tok.content[1..tok.content.len()-1];
|
||||||
let s = if &tok.content[..1] == "\"" {
|
let s = if &tok.content[..1] == "\"" {
|
||||||
|
@ -554,7 +561,6 @@ impl<'s> Parser<'s> {
|
||||||
|
|
||||||
fn parse_assign(&mut self) -> Result<Expr<'s>> {
|
fn parse_assign(&mut self) -> Result<Expr<'s>> {
|
||||||
if let Some(tok) = try_next!(self, T::Global | T::Var) {
|
if let Some(tok) = try_next!(self, T::Global | T::Var) {
|
||||||
self.next()?;
|
|
||||||
let name = expect!(self, T::Identifier);
|
let name = expect!(self, T::Identifier);
|
||||||
expect!(self, T::Equal);
|
expect!(self, T::Equal);
|
||||||
let val = self.parse_or()?;
|
let val = self.parse_or()?;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use num_complex::{Complex64, ComplexFloat};
|
||||||
use num_rational::Rational64;
|
use num_rational::Rational64;
|
||||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
||||||
|
|
||||||
use crate::{exception::{exception, throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::range::RangeType, Vm};
|
use crate::{exception::{throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::range::RangeType, Vm};
|
||||||
|
|
||||||
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
|
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::cast_precision_loss)]
|
|
||||||
pub fn promote(a: Value, b: Value) -> (Value, Value) {
|
pub fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
|
@ -61,6 +60,11 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// unary arithmetic //
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
|
||||||
impl Neg for Value {
|
impl Neg for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
|
@ -83,75 +87,66 @@ impl Neg for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<Value> for Value {
|
impl Value {
|
||||||
type Output = Result<Self>;
|
pub fn abs(self) -> Result<Self> {
|
||||||
fn add(self, rhs: Value) -> Self::Output {
|
|
||||||
use Value as V;
|
use Value as V;
|
||||||
let (a, b) = promote(self, rhs);
|
match self {
|
||||||
match (&a, &b) {
|
V::Int(x) => if let Some(x) = x.checked_abs() {
|
||||||
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_add(y) {
|
Ok(V::Int(x))
|
||||||
Ok(V::Int(v))
|
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when adding {a} and {b}")
|
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
|
||||||
},
|
},
|
||||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_add(y) {
|
V::Ratio(x) => if let Some((x, _)) = ratio_checked_absign(&x) {
|
||||||
Ok(V::Ratio(v))
|
Ok(V::Ratio(x))
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when adding {a} and {b}")
|
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
|
||||||
},
|
}
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x + y)),
|
V::Float(x) => Ok(V::Float(x.abs())),
|
||||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x + y)),
|
V::Complex(x) => Ok(V::Float(x.norm())),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot add {l:#} and {r:#}")
|
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub<Value> for Value {
|
|
||||||
|
/////////////////////////
|
||||||
|
// binary arithmetic //
|
||||||
|
/////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! impl_value_arith {
|
||||||
|
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
|
||||||
|
|
||||||
|
impl $trait<Value> for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
fn sub(self, rhs: Value) -> Self::Output {
|
fn $name(self, rhs: Value) -> Self::Output {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
let (a, b) = promote(self, rhs);
|
let (a, b) = promote(self, rhs);
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_sub(y) {
|
(V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) {
|
||||||
Ok(V::Int(v))
|
Ok(V::Int(v))
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when subtracting {a} and {b}")
|
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
|
||||||
},
|
},
|
||||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_sub(y) {
|
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.$checked(y) {
|
||||||
Ok(V::Ratio(v))
|
Ok(V::Ratio(v))
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when subtracting {a} and {b}")
|
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
|
||||||
},
|
},
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x - y)),
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
|
||||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x - y)),
|
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot subtract {l:#} and {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<Value> for Value {
|
};
|
||||||
type Output = Result<Self>;
|
|
||||||
fn mul(self, rhs: Value) -> Self::Output {
|
|
||||||
use Value as V;
|
|
||||||
let (a, b) = promote(self, rhs);
|
|
||||||
match (&a, &b) {
|
|
||||||
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_mul(y) {
|
|
||||||
Ok(V::Int(v))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when multiplying {a} and {b}")
|
|
||||||
},
|
|
||||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_mul(y) {
|
|
||||||
Ok(V::Ratio(v))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when multiplying {a} and {b}")
|
|
||||||
},
|
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x * y)),
|
|
||||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x * y)),
|
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot multiply {l:#} and {r:#}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_value_arith!(Add, add, checked_add, +, "add");
|
||||||
|
impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
|
||||||
|
impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
|
||||||
|
|
||||||
impl Div<Value> for Value {
|
impl Div<Value> for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
fn div(self, rhs: Value) -> Self::Output {
|
fn div(self, rhs: Value) -> Self::Output {
|
||||||
|
@ -174,42 +169,35 @@ impl Div<Value> for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
// modulo and integer division //
|
||||||
|
///////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ipow(n: i64, p: u64) -> Option<i64> {
|
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
|
||||||
match (n, p) {
|
let a = if r.is_negative() {
|
||||||
(0, 0) => None,
|
Rational64::ZERO.checked_sub(r)?
|
||||||
(0, _) => Some(0),
|
} else {
|
||||||
(_, 0) => Some(1),
|
*r
|
||||||
(1, _) => Some(1),
|
};
|
||||||
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
|
Some((a, r.signum()))
|
||||||
(_, p) if p > u32::MAX as u64 => None,
|
|
||||||
(n, p) => n.checked_pow(p as u32),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::cast_sign_loss)]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
|
|
||||||
match p {
|
|
||||||
i64::MIN => match (n, d) {
|
|
||||||
(0, _) => Some((0, 1)),
|
|
||||||
(1, 1) => Some((1, 1)),
|
|
||||||
(-1, 1) => Some((-1, 1)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
|
|
||||||
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
||||||
todo!()
|
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
|
||||||
|
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
||||||
|
let q = ratio_checked_div_euclid(r1, r2)?;
|
||||||
|
r1.checked_sub(&r2.checked_mul(&q)?)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn modulo(self, rhs: Value) -> Result<Self> {
|
pub fn modulo(self, rhs: Value) -> Result<Self> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
|
@ -267,7 +255,42 @@ impl Value {
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////
|
||||||
|
// exponentiation //
|
||||||
|
//////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn ipow(n: i64, p: u64) -> Option<i64> {
|
||||||
|
match (n, p) {
|
||||||
|
(0, 0) => None,
|
||||||
|
(0, _) => Some(0),
|
||||||
|
(_, 0) => Some(1),
|
||||||
|
(1, _) => Some(1),
|
||||||
|
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
|
||||||
|
(_, p) if p > u32::MAX as u64 => None,
|
||||||
|
(n, p) => n.checked_pow(p as u32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
|
||||||
|
match p {
|
||||||
|
i64::MIN => match (n, d) {
|
||||||
|
(0, _) => Some((0, 1)),
|
||||||
|
(1, 1) => Some((1, 1)),
|
||||||
|
(-1, 1) => Some((-1, 1)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
|
||||||
|
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
pub fn pow(self, rhs: Value) -> Result<Self> {
|
pub fn pow(self, rhs: Value) -> Result<Self> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
|
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
|
||||||
|
@ -304,6 +327,12 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Bitwise operations //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
|
||||||
impl Shl<Value> for Value {
|
impl Shl<Value> for Value {
|
||||||
type Output = Result<Value>;
|
type Output = Result<Value>;
|
||||||
|
|
||||||
|
@ -364,8 +393,13 @@ impl BitOr<Value> for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Equality and ordering //
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
impl PartialEq for Value {
|
impl PartialEq for Value {
|
||||||
#[expect(clippy::cast_precision_loss)]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
use super::range::RangeType as Rty;
|
use super::range::RangeType as Rty;
|
||||||
|
@ -408,7 +442,6 @@ impl PartialEq for Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Value {
|
impl PartialOrd for Value {
|
||||||
#[expect(clippy::cast_precision_loss)]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
@ -431,6 +464,12 @@ impl PartialOrd for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////
|
||||||
|
// misc //
|
||||||
|
////////////
|
||||||
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
||||||
match self.partial_cmp(other) {
|
match self.partial_cmp(other) {
|
||||||
|
|
|
@ -67,6 +67,7 @@ pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("isqrt", isqrt().into());
|
vm.set_global_name("isqrt", isqrt().into());
|
||||||
vm.set_global_name("isprime", isprime().into());
|
vm.set_global_name("isprime", isprime().into());
|
||||||
vm.set_global_name("factors", factors().into());
|
vm.set_global_name("factors", factors().into());
|
||||||
|
vm.set_global_name("totient", totient().into());
|
||||||
|
|
||||||
vm.set_global_name("min", min().into());
|
vm.set_global_name("min", min().into());
|
||||||
vm.set_global_name("max", max().into());
|
vm.set_global_name("max", max().into());
|
||||||
|
@ -364,7 +365,7 @@ pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if g == 0 {
|
if g == 0 {
|
||||||
Ok(Value::from(0))
|
Ok(Value::from(0))
|
||||||
} else {
|
} else {
|
||||||
Value::from(x/g) * Value::from(y)
|
(Value::from(x)/Value::from(g))? * Value::from(y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +382,11 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
};
|
};
|
||||||
let g = gcd_inner(l, a);
|
let g = gcd_inner(l, a);
|
||||||
if g == 0 { return Ok(Value::from(0)) };
|
if g == 0 { return Ok(Value::from(0)) };
|
||||||
l = (l/g).wrapping_mul(a);
|
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
|
||||||
|
let Value::Int(new_l) = new_l else {
|
||||||
|
unreachable!("int//int * int != int")
|
||||||
|
};
|
||||||
|
l = new_l;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::from(l))
|
Ok(Value::from(l))
|
||||||
|
@ -391,7 +396,7 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
let Value::Int(mut x) = x else {
|
let Value::Int(mut x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "factords expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "factors expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
let mut factors = Vec::new();
|
let mut factors = Vec::new();
|
||||||
if x <= 1 {
|
if x <= 1 {
|
||||||
|
@ -424,6 +429,49 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(factors.into())
|
Ok(factors.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn totient_prime(n: &mut u64, p: u64) -> u64 {
|
||||||
|
if *n % p != 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
*n /= p;
|
||||||
|
let mut v = p - 1;
|
||||||
|
while *n % p == 0 {
|
||||||
|
*n /= p;
|
||||||
|
v *= p;
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, x] = unpack_args!(args);
|
||||||
|
let Value::Int(x) = x else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "totient expected integer argument, got {x:#}")
|
||||||
|
};
|
||||||
|
if x <= 1 {
|
||||||
|
return Ok(1.into())
|
||||||
|
}
|
||||||
|
let mut x = x as u64;
|
||||||
|
let mut totient = 1;
|
||||||
|
if x & 1 == 0 { x >>= 1; }
|
||||||
|
while x & 1 == 0 {
|
||||||
|
x >>= 1;
|
||||||
|
totient <<= 1;
|
||||||
|
}
|
||||||
|
totient *= totient_prime(&mut x, 3);
|
||||||
|
let mut i = 5;
|
||||||
|
while x >= i*i {
|
||||||
|
totient *= totient_prime(&mut x, i);
|
||||||
|
i += 2;
|
||||||
|
totient *= totient_prime(&mut x, i);
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
if x > 1 {
|
||||||
|
totient *= x - 1;
|
||||||
|
}
|
||||||
|
Ok((totient as i64).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// numeric operations
|
// numeric operations
|
||||||
|
@ -669,22 +717,16 @@ pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
match x {
|
x.abs()
|
||||||
Value::Int(x) => Ok(Value::Int(x.abs())),
|
|
||||||
Value::Ratio(x) => Ok(Value::Ratio(if x < 0.into() { -x } else { x })),
|
|
||||||
Value::Float(x) => Ok(Value::Float(x.abs())),
|
|
||||||
Value::Complex(x) => Ok(Value::Float(x.norm())),
|
|
||||||
x => throw!(*SYM_TYPE_ERROR, "abs expected numeric argument, got {x:#}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
match x {
|
match x {
|
||||||
Value::Int(x) => Ok(Value::Int(x * x)),
|
Value::Int(_) => x.clone() * x,
|
||||||
Value::Ratio(x) => Ok(Value::Ratio(x * x)),
|
Value::Ratio(_) => x.clone() * x,
|
||||||
Value::Float(x) => Ok(Value::Float(x * x)),
|
Value::Float(_) => x.clone() * x,
|
||||||
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
|
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
|
||||||
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue