embiggened integers
This commit is contained in:
parent
6748444c7f
commit
a1d39eaf58
32 changed files with 2443 additions and 997 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
@ -162,11 +162,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.9"
|
version = "0.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -177,9 +177,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.168"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
|
@ -232,6 +232,31 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-complex"
|
name = "num-complex"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
@ -250,12 +275,24 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-rational"
|
name = "num-rational"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
@ -434,9 +471,7 @@ name = "talc-lang"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num-complex",
|
"num",
|
||||||
"num-rational",
|
|
||||||
"num-traits",
|
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -453,6 +488,7 @@ name = "talc-std"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"num-bigint",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"talc-lang",
|
"talc-lang",
|
||||||
|
|
|
@ -4,7 +4,9 @@ use talc_lang::{
|
||||||
compiler::compile,
|
compiler::compile,
|
||||||
lstring::LString,
|
lstring::LString,
|
||||||
optimize::optimize,
|
optimize::optimize,
|
||||||
parser, serial,
|
parser,
|
||||||
|
prelude::*,
|
||||||
|
serial,
|
||||||
symbol::Symbol,
|
symbol::Symbol,
|
||||||
value::{function::disasm_recursive, Value},
|
value::{function::disasm_recursive, Value},
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
|
@ -112,7 +114,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
|
||||||
ExitCode::FAILURE
|
ExitCode::FAILURE
|
||||||
}
|
}
|
||||||
Ok(Value::Bool(false)) => ExitCode::FAILURE,
|
Ok(Value::Bool(false)) => ExitCode::FAILURE,
|
||||||
Ok(Value::Int(n)) => ExitCode::from(n as u8),
|
Ok(Value::Int(n)) => ExitCode::from(n.to_u64().unwrap_or(1) as u8),
|
||||||
_ => ExitCode::SUCCESS,
|
_ => ExitCode::SUCCESS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-complex = "0.4"
|
num = { version = "0.4", features = [] }
|
||||||
num-rational = { version = "0.4", default-features = false, features = [] }
|
|
||||||
num-traits = "0.2"
|
|
||||||
lazy_static = "1.5"
|
lazy_static = "1.5"
|
||||||
unicode-ident = "1.0"
|
unicode-ident = "1.0"
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::rc::Rc;
|
||||||
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
|
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
|
||||||
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
||||||
use crate::parser::Pos;
|
use crate::parser::Pos;
|
||||||
|
use crate::prelude::*;
|
||||||
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
|
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
|
||||||
use crate::throw;
|
use crate::throw;
|
||||||
use crate::value::function::{FuncAttrs, Function};
|
use crate::value::function::{FuncAttrs, Function};
|
||||||
|
@ -766,22 +767,29 @@ impl<'a> Compiler<'a> {
|
||||||
match val {
|
match val {
|
||||||
Value::Nil => {
|
Value::Nil => {
|
||||||
self.emit(I::Nil);
|
self.emit(I::Nil);
|
||||||
|
return
|
||||||
}
|
}
|
||||||
Value::Bool(b) => {
|
Value::Bool(b) => {
|
||||||
self.emit(I::Bool(*b));
|
self.emit(I::Bool(*b));
|
||||||
}
|
return
|
||||||
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
|
|
||||||
self.emit(I::Int(Arg24::from_i64(*i)));
|
|
||||||
}
|
}
|
||||||
Value::Symbol(s) => {
|
Value::Symbol(s) => {
|
||||||
self.emit(I::Symbol(Arg24::from_symbol(*s)));
|
self.emit(I::Symbol(Arg24::from_symbol(*s)));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Value::Int(i) => {
|
||||||
|
if let Some(v) = i.to_i32() {
|
||||||
|
if (-0x80_0000..=0x7f_ffff).contains(&v) {
|
||||||
|
self.emit(I::Int(Arg24::from_i32(v)));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
let n = self.add_const(val.clone());
|
let n = self.add_const(val.clone());
|
||||||
self.emit(I::Const(Arg24::from_usize(n)));
|
self.emit(I::Const(Arg24::from_usize(n)));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> {
|
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> {
|
||||||
match (&lv.kind, o) {
|
match (&lv.kind, o) {
|
||||||
|
|
|
@ -8,9 +8,19 @@ pub mod chunk;
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
pub mod exception;
|
pub mod exception;
|
||||||
pub mod lstring;
|
pub mod lstring;
|
||||||
|
pub mod number;
|
||||||
pub mod optimize;
|
pub mod optimize;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
pub mod symbol;
|
pub mod symbol;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use crate::number::RatioExt;
|
||||||
|
pub use num::complex::ComplexFloat;
|
||||||
|
pub use num::integer::{Integer, Roots};
|
||||||
|
pub use num::traits::{
|
||||||
|
ConstOne, ConstZero, Euclid, FromPrimitive, Num, One, Signed, ToPrimitive, Zero,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -546,13 +546,6 @@ impl io::Write for LString {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//impl fmt::Write for LString {
|
|
||||||
// fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
// self.extend(s.as_bytes());
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// methods
|
// methods
|
||||||
//
|
//
|
||||||
|
|
986
talc-lang/src/number/int.rs
Normal file
986
talc-lang/src/number/int.rs
Normal file
|
@ -0,0 +1,986 @@
|
||||||
|
use core::{f64, panic};
|
||||||
|
use std::{borrow::Cow, cmp::Ordering, fmt, hash::Hash, mem::ManuallyDrop, ops, rc::Rc};
|
||||||
|
|
||||||
|
use num::{
|
||||||
|
bigint::{ParseBigIntError, Sign},
|
||||||
|
integer::Roots,
|
||||||
|
traits::CheckedEuclid,
|
||||||
|
BigInt, Integer, Num,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub enum IntType<'a> {
|
||||||
|
Int(i64),
|
||||||
|
Big(&'a BigInt),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const fn is_int_small(n: i64) -> bool {
|
||||||
|
let b = n >> 62;
|
||||||
|
b == 0 || b == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
union IntInner {
|
||||||
|
int: i64,
|
||||||
|
big: ManuallyDrop<Rc<BigInt>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntInner {
|
||||||
|
#[inline]
|
||||||
|
fn from_int(n: i64) -> Self {
|
||||||
|
if is_int_small(n) {
|
||||||
|
unsafe { Self::from_int_unchecked(n) }
|
||||||
|
} else {
|
||||||
|
Self::from_big_direct(BigInt::from(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const unsafe fn from_int_unchecked(n: i64) -> Self {
|
||||||
|
Self { int: (n << 1) | 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_big(n: BigInt) -> Self {
|
||||||
|
if let Some(n) = n.to_i64() {
|
||||||
|
if is_int_small(n) {
|
||||||
|
return unsafe { Self::from_int_unchecked(n) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::from_big_direct(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_big_ref(n: &BigInt) -> Self {
|
||||||
|
if let Some(n) = n.to_i64() {
|
||||||
|
if is_int_small(n) {
|
||||||
|
return unsafe { Self::from_int_unchecked(n) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::from_big_direct(n.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_big_direct(n: BigInt) -> Self {
|
||||||
|
Self {
|
||||||
|
big: ManuallyDrop::new(Rc::new(n)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_big_rc(n: Rc<BigInt>) -> Self {
|
||||||
|
Self {
|
||||||
|
big: ManuallyDrop::new(n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
const fn is_big(&self) -> bool {
|
||||||
|
unsafe { self.int & 1 == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn expand(&self) -> IntType {
|
||||||
|
if self.is_big() {
|
||||||
|
IntType::Big(unsafe { self.big.as_ref() })
|
||||||
|
} else {
|
||||||
|
IntType::Int(unsafe { self.int >> 1 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_int(&self) -> Option<i64> {
|
||||||
|
if let IntType::Int(n) = self.expand() {
|
||||||
|
Some(n)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_big(&self) -> Cow<BigInt> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Big(n) => Cow::Borrowed(n),
|
||||||
|
IntType::Int(n) => Cow::Owned(BigInt::from(n)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for IntInner {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
if let Some(n) = self.as_int() {
|
||||||
|
unsafe { Self::from_int_unchecked(n) }
|
||||||
|
} else {
|
||||||
|
Self::from_big_rc(ManuallyDrop::into_inner(unsafe { self.big.clone() }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IntInner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.is_big() {
|
||||||
|
unsafe { ManuallyDrop::drop(&mut self.big) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Int implementation
|
||||||
|
//
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Int(IntInner);
|
||||||
|
|
||||||
|
impl Int {
|
||||||
|
/// panics if n is not small enough
|
||||||
|
pub const fn from_small(n: i64) -> Self {
|
||||||
|
assert!(is_int_small(n), "argument to from_small was too large");
|
||||||
|
Self(unsafe { IntInner::from_int_unchecked(n) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Int {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(unsafe { IntInner::from_int_unchecked(0) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i8> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: i8) -> Self {
|
||||||
|
Self::from_small(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
Self::from_small(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i16> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: i16) -> Self {
|
||||||
|
Self::from_small(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
Self::from_small(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
Self::from_small(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
Self::from_small(value as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
Self(IntInner::from_int(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: u64) -> Self {
|
||||||
|
if let Ok(v) = value.try_into() {
|
||||||
|
Self(IntInner::from_int(v))
|
||||||
|
} else {
|
||||||
|
Self(IntInner::from_big_direct(BigInt::from(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i128> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: i128) -> Self {
|
||||||
|
if let Ok(v) = value.try_into() {
|
||||||
|
Self(IntInner::from_int(v))
|
||||||
|
} else {
|
||||||
|
Self(IntInner::from_big_direct(BigInt::from(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u128> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: u128) -> Self {
|
||||||
|
if let Ok(v) = value.try_into() {
|
||||||
|
Self(IntInner::from_int(v))
|
||||||
|
} else {
|
||||||
|
Self(IntInner::from_big_direct(BigInt::from(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<isize> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: isize) -> Self {
|
||||||
|
if let Ok(v) = value.try_into() {
|
||||||
|
Self(IntInner::from_int(v))
|
||||||
|
} else {
|
||||||
|
Self(IntInner::from_big_direct(BigInt::from(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for Int {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: usize) -> Self {
|
||||||
|
if let Ok(v) = value.try_into() {
|
||||||
|
Self(IntInner::from_int(v))
|
||||||
|
} else {
|
||||||
|
Self(IntInner::from_big_direct(BigInt::from(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BigInt> for Int {
|
||||||
|
fn from(value: BigInt) -> Self {
|
||||||
|
Self(IntInner::from_big(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&BigInt> for Int {
|
||||||
|
fn from(value: &BigInt) -> Self {
|
||||||
|
Self(IntInner::from_big_ref(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_binop {
|
||||||
|
($($trait:ident)::+,$($atrait:ident)::+,$fname:ident,$afname:ident,$op:tt,$checked:ident) => {
|
||||||
|
impl $($trait)::+ for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn $fname(self, rhs: Self) -> Self::Output {
|
||||||
|
match (self.expand(), rhs.expand()) {
|
||||||
|
(IntType::Int(a), IntType::Int(b)) => match a.$checked(b) {
|
||||||
|
Some(n) => Int::from(n),
|
||||||
|
None => Int::from(BigInt::from(a) $op b),
|
||||||
|
}
|
||||||
|
(IntType::Int(a), IntType::Big(b)) => Int::from(a $op b),
|
||||||
|
(IntType::Big(a), IntType::Int(b)) => Int::from(a $op b),
|
||||||
|
(IntType::Big(a), IntType::Big(b)) => Int::from(a $op b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($trait)::+ for Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn $fname(self, rhs: Int) -> Self::Output {
|
||||||
|
&self $op &rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($trait)::+<i64> for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn $fname(self, rhs: i64) -> Self::Output {
|
||||||
|
self $op &Int::from(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($atrait)::+ for Int {
|
||||||
|
fn $afname(&mut self, rhs: Int) {
|
||||||
|
*self = &*self $op &rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($atrait)::+<&Int> for Int {
|
||||||
|
fn $afname(&mut self, rhs: &Int) {
|
||||||
|
*self = &*self $op rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($atrait)::+<i64> for Int {
|
||||||
|
fn $afname(&mut self, rhs: i64) {
|
||||||
|
*self = &*self $op rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_binop!(ops::Add, ops::AddAssign, add, add_assign, +, checked_add);
|
||||||
|
impl_binop!(ops::Sub, ops::SubAssign, sub, sub_assign, -, checked_sub);
|
||||||
|
impl_binop!(ops::Mul, ops::MulAssign, mul, mul_assign, *, checked_mul);
|
||||||
|
impl_binop!(ops::Div, ops::DivAssign, div, div_assign, /, checked_div);
|
||||||
|
impl_binop!(ops::Rem, ops::RemAssign, rem, rem_assign, %, checked_rem);
|
||||||
|
|
||||||
|
macro_rules! impl_logicop {
|
||||||
|
($($trait:ident)::+,$($atrait:ident)::+,$fname:ident,$afname:ident,$op:tt) => {
|
||||||
|
impl $($trait)::+ for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn $fname(self, rhs: Self) -> Self::Output {
|
||||||
|
match (self.expand(), rhs.expand()) {
|
||||||
|
(IntType::Int(a), IntType::Int(b)) => Int::from(a $op b),
|
||||||
|
(IntType::Int(a), IntType::Big(b)) => Int::from(BigInt::from(a) $op b),
|
||||||
|
(IntType::Big(a), IntType::Int(b)) => Int::from(a $op BigInt::from(b)),
|
||||||
|
(IntType::Big(a), IntType::Big(b)) => Int::from(a $op b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($trait)::+ for Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn $fname(self, rhs: Int) -> Self::Output {
|
||||||
|
&self $op &rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($trait)::+<i64> for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn $fname(self, rhs: i64) -> Self::Output {
|
||||||
|
self $op &Int::from(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($atrait)::+<&Int> for Int {
|
||||||
|
fn $afname(&mut self, rhs: &Int) {
|
||||||
|
*self = &*self $op rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $($atrait)::+<i64> for Int {
|
||||||
|
fn $afname(&mut self, rhs: i64) {
|
||||||
|
*self = &*self $op rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_logicop!(ops::BitAnd, ops::BitAndAssign, bitand, bitand_assign, &);
|
||||||
|
impl_logicop!(ops::BitOr, ops::BitOrAssign, bitor, bitor_assign, |);
|
||||||
|
impl_logicop!(ops::BitXor, ops::BitXorAssign, bitxor, bitxor_assign, ^);
|
||||||
|
|
||||||
|
impl ops::Shl for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn shl(self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_negative() {
|
||||||
|
panic!("attempt to shift left with negative")
|
||||||
|
}
|
||||||
|
let Some(rhs) = rhs.to_u32() else {
|
||||||
|
panic!("shift left overflow")
|
||||||
|
};
|
||||||
|
Int::from(self.to_big().as_ref() << rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Shl for Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn shl(self, rhs: Int) -> Self::Output {
|
||||||
|
&self << &rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Shl<i64> for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn shl(self, rhs: i64) -> Self::Output {
|
||||||
|
self << &Int::from(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::ShlAssign<&Int> for Int {
|
||||||
|
fn shl_assign(&mut self, rhs: &Int) {
|
||||||
|
*self = &*self << rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::ShlAssign<i64> for Int {
|
||||||
|
fn shl_assign(&mut self, rhs: i64) {
|
||||||
|
*self = &*self << &Int::from(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Shr for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
fn shr(self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_negative() {
|
||||||
|
panic!("attempt to shift right with negative")
|
||||||
|
}
|
||||||
|
let Some(rhs) = rhs.to_u32() else {
|
||||||
|
return Int::ZERO
|
||||||
|
};
|
||||||
|
|
||||||
|
Int::from(self.to_big().as_ref() >> rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Shr for Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn shr(self, rhs: Int) -> Self::Output {
|
||||||
|
&self >> &rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Shr<i64> for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn shr(self, rhs: i64) -> Self::Output {
|
||||||
|
self >> &Int::from(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::ShrAssign<&Int> for Int {
|
||||||
|
fn shr_assign(&mut self, rhs: &Int) {
|
||||||
|
*self = &*self >> rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::ShrAssign<i64> for Int {
|
||||||
|
fn shr_assign(&mut self, rhs: i64) {
|
||||||
|
*self = &*self >> &Int::from(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Neg for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn neg(self) -> Int {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => Int::from(-n),
|
||||||
|
IntType::Big(n) => Int::from(-n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Neg for Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn neg(self) -> Int {
|
||||||
|
-&self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Not for &Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn not(self) -> Int {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => Int::from(!n),
|
||||||
|
IntType::Big(n) => Int::from(!n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Not for Int {
|
||||||
|
type Output = Int;
|
||||||
|
|
||||||
|
fn not(self) -> Int {
|
||||||
|
!&self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Int {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
|
||||||
|
a.eq(&b)
|
||||||
|
} else {
|
||||||
|
let a = self.0.to_big();
|
||||||
|
let b = other.0.to_big();
|
||||||
|
a.as_ref().eq(b.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Int {}
|
||||||
|
|
||||||
|
impl PartialOrd for Int {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Int {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
|
||||||
|
a.cmp(&b)
|
||||||
|
} else {
|
||||||
|
let a = self.0.to_big();
|
||||||
|
let b = other.0.to_big();
|
||||||
|
a.as_ref().cmp(b.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstZero for Int {
|
||||||
|
const ZERO: Self = Self::from_small(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstOne for Int {
|
||||||
|
const ONE: Self = Self::from_small(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zero for Int {
|
||||||
|
#[inline]
|
||||||
|
fn zero() -> Self {
|
||||||
|
Self::ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n == 0,
|
||||||
|
IntType::Big(n) => n.is_zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl One for Int {
|
||||||
|
#[inline]
|
||||||
|
fn one() -> Self {
|
||||||
|
Self::ONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Num for Int {
|
||||||
|
type FromStrRadixErr = ParseBigIntError;
|
||||||
|
|
||||||
|
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
|
||||||
|
match i64::from_str_radix(str, radix) {
|
||||||
|
Ok(v) => Ok(Self::from(v)),
|
||||||
|
Err(_) => Ok(Self::from(BigInt::from_str_radix(str, radix)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Integer for Int {
|
||||||
|
fn div_floor(&self, other: &Self) -> Self {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
|
||||||
|
return Integer::div_floor(&a, &b).into()
|
||||||
|
}
|
||||||
|
let (a, b) = (self.0.to_big(), other.0.to_big());
|
||||||
|
a.div_floor(&b).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mod_floor(&self, other: &Self) -> Self {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
|
||||||
|
return a.mod_floor(&b).into()
|
||||||
|
}
|
||||||
|
let (a, b) = (self.0.to_big(), other.0.to_big());
|
||||||
|
a.mod_floor(&b).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gcd(&self, other: &Self) -> Self {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
|
||||||
|
return a.gcd(&b).into()
|
||||||
|
}
|
||||||
|
let (a, b) = (self.0.to_big(), other.0.to_big());
|
||||||
|
a.gcd(&b).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lcm(&self, other: &Self) -> Self {
|
||||||
|
let (a, b) = (self.0.to_big(), other.0.to_big());
|
||||||
|
a.lcm(&b).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_multiple_of(&self, other: &Self) -> bool {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
|
||||||
|
return a.is_multiple_of(&b)
|
||||||
|
}
|
||||||
|
let (a, b) = (self.0.to_big(), other.0.to_big());
|
||||||
|
a.is_multiple_of(&b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_even(&self) -> bool {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.is_even(),
|
||||||
|
IntType::Big(n) => n.is_even(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_odd(&self) -> bool {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.is_odd(),
|
||||||
|
IntType::Big(n) => n.is_odd(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn div_rem(&self, other: &Self) -> (Self, Self) {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), other.0.as_int()) {
|
||||||
|
let (q, r) = a.div_rem(&b);
|
||||||
|
return (q.into(), r.into())
|
||||||
|
}
|
||||||
|
let (a, b) = (self.0.to_big(), other.0.to_big());
|
||||||
|
let (q, r) = a.div_rem(&b);
|
||||||
|
(q.into(), r.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signed for Int {
|
||||||
|
fn abs(&self) -> Self {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => Int::from(n.abs()),
|
||||||
|
IntType::Big(n) => Int::from(n.abs()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abs_sub(&self, other: &Self) -> Self {
|
||||||
|
(self - other).abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signum(&self) -> Self {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => Int::from(n.signum()),
|
||||||
|
IntType::Big(n) => Int::from(n.signum()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_positive(&self) -> bool {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n > 0,
|
||||||
|
IntType::Big(n) => n.is_positive(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_negative(&self) -> bool {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n < 0,
|
||||||
|
IntType::Big(n) => n.is_negative(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Roots for Int {
|
||||||
|
fn nth_root(&self, n: u32) -> Self {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(i) => Int::from(i.nth_root(n)),
|
||||||
|
IntType::Big(b) => Int::from(b.nth_root(n)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Euclid for Int {
|
||||||
|
fn div_euclid(&self, v: &Self) -> Self {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), v.0.as_int()) {
|
||||||
|
if let Some(v) = CheckedEuclid::checked_div_euclid(&a, &b) {
|
||||||
|
return Int::from(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (a, b) = (self.0.to_big(), v.0.to_big());
|
||||||
|
a.div_euclid(&b).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rem_euclid(&self, v: &Self) -> Self {
|
||||||
|
if let (Some(a), Some(b)) = (self.0.as_int(), v.0.as_int()) {
|
||||||
|
if let Some(v) = CheckedEuclid::checked_rem_euclid(&a, &b) {
|
||||||
|
return Int::from(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (a, b) = (self.0.to_big(), v.0.to_big());
|
||||||
|
a.rem_euclid(&b).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Int {
|
||||||
|
pub fn to_str_radix(&self, radix: u32) -> String {
|
||||||
|
self.to_big().to_str_radix(radix)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_str_radix_case(&self, radix: u32, upper: bool) -> String {
|
||||||
|
let mut s = self.to_big().to_str_radix(radix);
|
||||||
|
if upper {
|
||||||
|
s.make_ascii_uppercase();
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Int {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
fmt::Display::fmt(&self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Int {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(self.to_big().as_ref(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Binary for Int {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Binary::fmt(self.to_big().as_ref(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Octal for Int {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Octal::fmt(self.to_big().as_ref(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::LowerHex for Int {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::LowerHex::fmt(self.to_big().as_ref(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::UpperHex for Int {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::UpperHex::fmt(self.to_big().as_ref(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Int {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.hash(state),
|
||||||
|
IntType::Big(b) => match b.to_i64() {
|
||||||
|
Some(n) => n.hash(state),
|
||||||
|
None => b.hash(state),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromPrimitive for Int {
|
||||||
|
fn from_i64(n: i64) -> Option<Self> {
|
||||||
|
Some(Self::from(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u64(n: u64) -> Option<Self> {
|
||||||
|
Some(Self::from(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_isize(n: isize) -> Option<Self> {
|
||||||
|
Some(Self::from(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_i128(n: i128) -> Option<Self> {
|
||||||
|
Some(Self::from(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_usize(n: usize) -> Option<Self> {
|
||||||
|
Some(Self::from(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u128(n: u128) -> Option<Self> {
|
||||||
|
Some(Self::from(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_f32(n: f32) -> Option<Self> {
|
||||||
|
Some(Int::from(BigInt::from_f32(n)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_f64(n: f64) -> Option<Self> {
|
||||||
|
Some(Int::from(BigInt::from_f64(n)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToPrimitive for Int {
|
||||||
|
fn to_i64(&self) -> Option<i64> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => Some(n),
|
||||||
|
IntType::Big(b) => b.to_i64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_u64(&self) -> Option<u64> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.to_u64(),
|
||||||
|
IntType::Big(b) => b.to_u64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_isize(&self) -> Option<isize> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.to_isize(),
|
||||||
|
IntType::Big(b) => b.to_isize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_i128(&self) -> Option<i128> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => Some(n as i128),
|
||||||
|
IntType::Big(b) => b.to_i128(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_usize(&self) -> Option<usize> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.to_usize(),
|
||||||
|
IntType::Big(b) => b.to_usize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_u128(&self) -> Option<u128> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.to_u128(),
|
||||||
|
IntType::Big(b) => b.to_u128(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_f32(&self) -> Option<f32> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.to_f32(),
|
||||||
|
IntType::Big(b) => b.to_f32(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_f64(&self) -> Option<f64> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(n) => n.to_f64(),
|
||||||
|
IntType::Big(b) => b.to_f64(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Int {
|
||||||
|
#[inline]
|
||||||
|
pub fn to_f64_uc(&self) -> f64 {
|
||||||
|
self.to_f64().unwrap_or(f64::NAN)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn expand(&self) -> IntType {
|
||||||
|
self.0.expand()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// exp must be nonnegative, both cannot be zero.
|
||||||
|
pub fn ipow(&self, exp: &Int) -> Option<Int> {
|
||||||
|
if self.is_zero() && exp.is_zero() || exp.is_negative() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
if exp.is_zero() {
|
||||||
|
return Some(Int::ONE)
|
||||||
|
}
|
||||||
|
if exp.is_one() {
|
||||||
|
return Some(self.clone())
|
||||||
|
}
|
||||||
|
if self.is_zero() || self.is_one() {
|
||||||
|
return Some(self.clone())
|
||||||
|
}
|
||||||
|
if self == &Int::from_small(-1) {
|
||||||
|
return if exp.is_even() {
|
||||||
|
Some(Int::ONE)
|
||||||
|
} else {
|
||||||
|
Some(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(e) = exp.to_u32() {
|
||||||
|
if let Some(i) = self.0.as_int() {
|
||||||
|
if let Some(v) = i.checked_pow(e) {
|
||||||
|
return Some(Int::from(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Self::from(self.0.to_big().pow(e)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modpow(&self, exp: &Int, modulus: &Int) -> Int {
|
||||||
|
self.to_big()
|
||||||
|
.modpow(&exp.to_big(), &modulus.to_big())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modinv(&self, modulus: &Int) -> Option<Int> {
|
||||||
|
self.to_big().modinv(&modulus.to_big()).map(|v| v.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isignum(&self) -> i64 {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(i) => i.signum(),
|
||||||
|
IntType::Big(b) => match b.sign() {
|
||||||
|
Sign::Minus => -1,
|
||||||
|
Sign::NoSign => 0,
|
||||||
|
Sign::Plus => 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_big(&self) -> Cow<BigInt> {
|
||||||
|
self.0.to_big()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trailing_zeros(&self) -> Option<u64> {
|
||||||
|
match self.expand() {
|
||||||
|
IntType::Int(0) => None,
|
||||||
|
IntType::Int(n) => Some(n.trailing_zeros() as u64),
|
||||||
|
IntType::Big(n) => n.trailing_zeros(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn eq_i64(&self, n: i64) -> bool {
|
||||||
|
self.to_i64().is_some_and(|v| v == n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn ne_i64(&self, n: i64) -> bool {
|
||||||
|
!self.eq_i64(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cmp_i64(&self, n: i64) -> Ordering {
|
||||||
|
match self.to_i64() {
|
||||||
|
Some(v) => v.cmp(&n),
|
||||||
|
None => {
|
||||||
|
if self.is_negative() {
|
||||||
|
Ordering::Less
|
||||||
|
} else {
|
||||||
|
Ordering::Greater
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn lt_i64(&self, n: i64) -> bool {
|
||||||
|
self.cmp_i64(n).is_lt()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn gt_i64(&self, n: i64) -> bool {
|
||||||
|
self.cmp_i64(n).is_gt()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn le_i64(&self, n: i64) -> bool {
|
||||||
|
self.cmp_i64(n).is_le()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn ge_i64(&self, n: i64) -> bool {
|
||||||
|
self.cmp_i64(n).is_ge()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Int> for BigInt {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: Int) -> Self {
|
||||||
|
value.0.to_big().into_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Int> for BigInt {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: &Int) -> Self {
|
||||||
|
value.0.to_big().into_owned()
|
||||||
|
}
|
||||||
|
}
|
10
talc-lang/src/number/mod.rs
Normal file
10
talc-lang/src/number/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
mod int;
|
||||||
|
pub use int::{Int, IntType};
|
||||||
|
|
||||||
|
mod range;
|
||||||
|
pub use range::Range;
|
||||||
|
|
||||||
|
mod ratio;
|
||||||
|
pub use ratio::{Ratio, RatioExt};
|
||||||
|
|
||||||
|
pub use num::complex::Complex64 as Complex;
|
217
talc-lang/src/number/range.rs
Normal file
217
talc-lang/src/number/range.rs
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use num::BigInt;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use super::Int;
|
||||||
|
|
||||||
|
fn to_slice_index(n: &BigInt, len: usize) -> usize {
|
||||||
|
if n.is_negative() {
|
||||||
|
let n = n + len;
|
||||||
|
if n.is_negative() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
n.to_usize().unwrap_or(usize::MAX).min(len)
|
||||||
|
} else {
|
||||||
|
n.to_usize().unwrap_or(usize::MAX).min(len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Range {
|
||||||
|
pub start: Option<BigInt>,
|
||||||
|
pub end: Option<BigInt>,
|
||||||
|
pub inclusive: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Range {
|
||||||
|
#[expect(clippy::self_named_constructors)]
|
||||||
|
pub fn range<I, J>(start: I, end: J) -> Self
|
||||||
|
where
|
||||||
|
I: Into<BigInt>,
|
||||||
|
J: Into<BigInt>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
start: Some(start.into()),
|
||||||
|
end: Some(end.into()),
|
||||||
|
inclusive: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_incl<I, J>(start: I, end: J) -> Self
|
||||||
|
where
|
||||||
|
I: Into<BigInt>,
|
||||||
|
J: Into<BigInt>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
start: Some(start.into()),
|
||||||
|
end: Some(end.into()),
|
||||||
|
inclusive: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_from<I>(start: I) -> Self
|
||||||
|
where
|
||||||
|
I: Into<BigInt>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
start: Some(start.into()),
|
||||||
|
end: None,
|
||||||
|
inclusive: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_to<I>(end: I) -> Self
|
||||||
|
where
|
||||||
|
I: Into<BigInt>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
start: None,
|
||||||
|
end: Some(end.into()),
|
||||||
|
inclusive: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_to_incl<I>(end: I) -> Self
|
||||||
|
where
|
||||||
|
I: Into<BigInt>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
start: None,
|
||||||
|
end: Some(end.into()),
|
||||||
|
inclusive: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn range_all() -> Self {
|
||||||
|
Self {
|
||||||
|
start: None,
|
||||||
|
end: None,
|
||||||
|
inclusive: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> Option<Int> {
|
||||||
|
let (Some(s), Some(e)) = (&self.start, &self.end) else {
|
||||||
|
return None
|
||||||
|
};
|
||||||
|
if e < s {
|
||||||
|
return Some(Int::ZERO)
|
||||||
|
}
|
||||||
|
Some((e - s + if self.inclusive { 1i64 } else { 0i64 }).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
let (Some(s), Some(e)) = (&self.start, &self.end) else {
|
||||||
|
return false
|
||||||
|
};
|
||||||
|
if e < s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if e == s && !self.inclusive {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_iterate(&self) -> Option<impl Iterator<Item = Int>> {
|
||||||
|
let Some(s) = &self.start else { return None };
|
||||||
|
Some(RangeIterator {
|
||||||
|
cur: s.into(),
|
||||||
|
end: self.end.as_ref().map(|e| e.into()),
|
||||||
|
inclusive: self.inclusive,
|
||||||
|
done: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index_slice<'a, T>(&self, l: &'a [T]) -> &'a [T] {
|
||||||
|
let len = l.len();
|
||||||
|
let s = self
|
||||||
|
.start
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| to_slice_index(s, len))
|
||||||
|
.unwrap_or(0);
|
||||||
|
let e = self
|
||||||
|
.end
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| to_slice_index(e, len))
|
||||||
|
.unwrap_or(len);
|
||||||
|
let e = if self.inclusive {
|
||||||
|
e.saturating_add(1).min(len)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
};
|
||||||
|
if e <= s {
|
||||||
|
return &[]
|
||||||
|
}
|
||||||
|
&l[s..e]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace_range<T: Clone>(&self, l: &mut Vec<T>, rep: &[T]) {
|
||||||
|
let len = l.len();
|
||||||
|
let s = self
|
||||||
|
.start
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| to_slice_index(s, len))
|
||||||
|
.unwrap_or(0);
|
||||||
|
let e = self
|
||||||
|
.end
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| to_slice_index(e, len))
|
||||||
|
.unwrap_or(len);
|
||||||
|
let e = if self.inclusive {
|
||||||
|
e.saturating_add(1).min(len)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
};
|
||||||
|
|
||||||
|
if e < s {
|
||||||
|
l.splice(e..s, rep.iter().cloned());
|
||||||
|
} else {
|
||||||
|
l.splice(s..e, rep.iter().cloned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RangeIterator {
|
||||||
|
cur: Int,
|
||||||
|
end: Option<Int>,
|
||||||
|
inclusive: bool,
|
||||||
|
done: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for RangeIterator {
|
||||||
|
type Item = Int;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.done {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
if let Some(end) = &self.end {
|
||||||
|
if &self.cur > end || (&self.cur == end && !self.inclusive) {
|
||||||
|
self.done = true;
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut val = &self.cur + 1;
|
||||||
|
std::mem::swap(&mut val, &mut self.cur);
|
||||||
|
Some(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Range {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match &self.start {
|
||||||
|
Some(s) => write!(f, "{s}..")?,
|
||||||
|
None => write!(f, "*..")?,
|
||||||
|
}
|
||||||
|
if self.inclusive {
|
||||||
|
write!(f, "=")?;
|
||||||
|
}
|
||||||
|
match &self.end {
|
||||||
|
Some(e) => write!(f, "{e}"),
|
||||||
|
None => write!(f, "*"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
talc-lang/src/number/ratio.rs
Normal file
73
talc-lang/src/number/ratio.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use num::Rational64;
|
||||||
|
|
||||||
|
use super::Int;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub type Ratio = num::BigRational;
|
||||||
|
|
||||||
|
impl From<Int> for Ratio {
|
||||||
|
fn from(value: Int) -> Self {
|
||||||
|
Ratio::from_integer(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Int> for Ratio {
|
||||||
|
fn from(value: &Int) -> Self {
|
||||||
|
Ratio::from_integer(value.to_owned().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RatioExt: Sized {
|
||||||
|
fn div_euclid(&self, n: &Self) -> Self;
|
||||||
|
fn rem_euclid(&self, n: &Self) -> Self;
|
||||||
|
fn to_f64_uc(&self) -> f64;
|
||||||
|
fn approximate_float(x: f64) -> Option<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RatioExt for Ratio {
|
||||||
|
#[inline]
|
||||||
|
fn div_euclid(&self, n: &Self) -> Ratio {
|
||||||
|
let (n_abs, n_sgn) = (n.abs(), n.signum());
|
||||||
|
(self / n_abs).floor() * n_sgn
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn rem_euclid(&self, n: &Self) -> Ratio {
|
||||||
|
ToPrimitive::to_f64(self);
|
||||||
|
let n_abs = n.abs();
|
||||||
|
self - (self / &n_abs).floor() * &n_abs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_f64_uc(&self) -> f64 {
|
||||||
|
if let Some(f) = self.to_f64() {
|
||||||
|
f
|
||||||
|
} else if self.is_negative() {
|
||||||
|
f64::NEG_INFINITY
|
||||||
|
} else {
|
||||||
|
f64::INFINITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn approximate_float(x: f64) -> Option<Self> {
|
||||||
|
match Rational64::approximate_float(x) {
|
||||||
|
Some(r) => {
|
||||||
|
let r = Ratio::new((*r.numer()).into(), (*r.denom()).into());
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut y = x;
|
||||||
|
if y.abs() < 1.0 {
|
||||||
|
y = 1.0 / y;
|
||||||
|
}
|
||||||
|
let i = Int::from_f64(y)?;
|
||||||
|
let r = Ratio::from_integer(i.into());
|
||||||
|
if x.abs() < 1.0 {
|
||||||
|
Some(r.recip())
|
||||||
|
} else {
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,9 @@ pub enum UnaryOp {
|
||||||
Neg,
|
Neg,
|
||||||
Not,
|
Not,
|
||||||
BitNot,
|
BitNot,
|
||||||
RangeEndless,
|
RangeFrom,
|
||||||
|
RangeTo,
|
||||||
|
RangeToIncl,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -26,6 +26,9 @@ pub enum TokenKind {
|
||||||
AmperEqual,
|
AmperEqual,
|
||||||
Star,
|
Star,
|
||||||
StarEqual,
|
StarEqual,
|
||||||
|
StarDotDot,
|
||||||
|
StarDotDotStar,
|
||||||
|
StarDotDotEqual,
|
||||||
Plus,
|
Plus,
|
||||||
PlusPlus,
|
PlusPlus,
|
||||||
PlusPlusEqual,
|
PlusPlusEqual,
|
||||||
|
@ -115,6 +118,9 @@ impl TokenKind {
|
||||||
K::AmperEqual => "'&='",
|
K::AmperEqual => "'&='",
|
||||||
K::Star => "'*'",
|
K::Star => "'*'",
|
||||||
K::StarEqual => "'*='",
|
K::StarEqual => "'*='",
|
||||||
|
K::StarDotDot => "'*..'",
|
||||||
|
K::StarDotDotStar => "'*..*'",
|
||||||
|
K::StarDotDotEqual => "'*..='",
|
||||||
K::Plus => "'+'",
|
K::Plus => "'+'",
|
||||||
K::PlusPlus => "'++'",
|
K::PlusPlus => "'++'",
|
||||||
K::PlusPlusEqual => "'++='",
|
K::PlusPlusEqual => "'++='",
|
||||||
|
@ -489,6 +495,14 @@ impl<'s> Lexer<'s> {
|
||||||
},
|
},
|
||||||
'*' => match self.and_peek()? {
|
'*' => match self.and_peek()? {
|
||||||
'=' => self.and_emit(K::StarEqual),
|
'=' => self.and_emit(K::StarEqual),
|
||||||
|
'.' => match self.and_peek()? {
|
||||||
|
'.' => match self.and_peek()? {
|
||||||
|
'*' => self.and_emit(K::StarDotDotStar),
|
||||||
|
'=' => self.and_emit(K::StarDotDotEqual),
|
||||||
|
_ => self.emit(K::StarDotDot),
|
||||||
|
},
|
||||||
|
_ => self.unexpected(),
|
||||||
|
},
|
||||||
_ => self.emit(K::Star),
|
_ => self.emit(K::Star),
|
||||||
},
|
},
|
||||||
'/' => match self.and_peek()? {
|
'/' => match self.and_peek()? {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use super::{
|
||||||
parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span,
|
parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span,
|
||||||
SpanParserError, Token, TokenKind,
|
SpanParserError, Token, TokenKind,
|
||||||
};
|
};
|
||||||
use num_complex::Complex64;
|
use num::complex::Complex64;
|
||||||
use ExprKind as E;
|
use ExprKind as E;
|
||||||
use TokenKind as T;
|
use TokenKind as T;
|
||||||
|
|
||||||
|
@ -108,13 +108,15 @@ impl TokenKind {
|
||||||
T::Minus => Some(UnaryOp::Neg),
|
T::Minus => Some(UnaryOp::Neg),
|
||||||
T::Not => Some(UnaryOp::Not),
|
T::Not => Some(UnaryOp::Not),
|
||||||
T::Tilde => Some(UnaryOp::BitNot),
|
T::Tilde => Some(UnaryOp::BitNot),
|
||||||
|
T::StarDotDot => Some(UnaryOp::RangeTo),
|
||||||
|
T::StarDotDotEqual => Some(UnaryOp::RangeToIncl),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postfix_unary_op(self) -> Option<UnaryOp> {
|
fn postfix_unary_op(self) -> Option<UnaryOp> {
|
||||||
match self {
|
match self {
|
||||||
T::DotDotStar => Some(UnaryOp::RangeEndless),
|
T::DotDotStar => Some(UnaryOp::RangeFrom),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +126,7 @@ impl UnaryOp {
|
||||||
fn precedence(self) -> u8 {
|
fn precedence(self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
UnaryOp::Not => 0,
|
UnaryOp::Not => 0,
|
||||||
UnaryOp::RangeEndless => 40,
|
UnaryOp::RangeFrom | UnaryOp::RangeTo | UnaryOp::RangeToIncl => 40,
|
||||||
UnaryOp::Neg | UnaryOp::BitNot => 110,
|
UnaryOp::Neg | UnaryOp::BitNot => 110,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,25 +161,26 @@ fn b<T>(t: T) -> Box<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenKind {
|
impl TokenKind {
|
||||||
|
#[rustfmt::skip]
|
||||||
fn expr_first(self) -> bool {
|
fn expr_first(self) -> bool {
|
||||||
matches!(
|
matches!(self,
|
||||||
self,
|
| T::Return | T::Continue
|
||||||
T::Return
|
|
||||||
| T::Continue
|
|
||||||
| T::Break | T::Var
|
| T::Break | T::Var
|
||||||
| T::Global | T::Fn
|
| T::Global | T::Fn
|
||||||
| T::Not | T::Backslash
|
| T::Not | T::StarDotDot
|
||||||
| T::Amper | T::Minus
|
| T::StarDotDotStar
|
||||||
| T::Tilde | T::Identifier
|
| T::StarDotDotEqual
|
||||||
|
| T::Backslash | T::Amper
|
||||||
|
| T::Minus | T::Tilde
|
||||||
|
| T::Identifier
|
||||||
| T::LParen | T::LBrack
|
| T::LParen | T::LBrack
|
||||||
| T::LBrace | T::Dollar
|
| T::LBrace | T::Dollar
|
||||||
| T::Do | T::If
|
| T::Do | T::If | T::While
|
||||||
| T::While | T::For
|
| T::For | T::Try
|
||||||
| T::Try | T::Integer
|
| T::Integer | T::Float
|
||||||
| T::Float | T::Imaginary
|
| T::Imaginary | T::String
|
||||||
| T::String | T::Symbol
|
| T::Symbol | T::True
|
||||||
| T::True | T::False
|
| T::False | T::Nil
|
||||||
| T::Nil
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,6 +401,7 @@ impl<'s> Parser<'s> {
|
||||||
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
|
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
|
||||||
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
|
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
|
||||||
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
|
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
|
||||||
|
T::StarDotDotStar => Ok(E::Literal(Value::range_all()).span(tok.span)),
|
||||||
t => throw!(
|
t => throw!(
|
||||||
tok.span,
|
tok.span,
|
||||||
"unexpected token {}, expected expression",
|
"unexpected token {}, expected expression",
|
||||||
|
@ -503,7 +507,7 @@ impl<'s> Parser<'s> {
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let args = self.parse_ident_list()?;
|
let args = self.parse_ident_list()?;
|
||||||
expect!(self, T::Dot);
|
expect!(self, T::Arrow);
|
||||||
let body = self.parse_lambda()?;
|
let body = self.parse_lambda()?;
|
||||||
let body_span = body.span;
|
let body_span = body.span;
|
||||||
Ok(E::Lambda(args, b(body)).span(span + body_span))
|
Ok(E::Lambda(args, b(body)).span(span + body_span))
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
use num::{bigint::Sign, BigInt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chunk::{Chunk, Instruction, TryTable},
|
chunk::{Chunk, Instruction, TryTable},
|
||||||
lstring::LStr,
|
lstring::LStr,
|
||||||
|
number::Int,
|
||||||
|
prelude::*,
|
||||||
symbol::Symbol,
|
symbol::Symbol,
|
||||||
value::{function::Function, range::RangeType, Value},
|
value::{function::Function, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAGIC: [u8; 8] = *b"\x7fTALC\0\0\0";
|
const MAGIC: [u8; 8] = *b"\x7fTALC\0\0\0";
|
||||||
|
@ -109,12 +113,12 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
|
||||||
}
|
}
|
||||||
Value::Int(n) => {
|
Value::Int(n) => {
|
||||||
self.write_u8(3)?;
|
self.write_u8(3)?;
|
||||||
self.write_i64(*n)
|
self.write_int(n.clone())
|
||||||
}
|
}
|
||||||
Value::Ratio(r) => {
|
Value::Ratio(r) => {
|
||||||
self.write_u8(4)?;
|
self.write_u8(4)?;
|
||||||
self.write_i64(*r.numer())?;
|
self.write_int(Int::from(r.numer().clone()))?;
|
||||||
self.write_i64(*r.denom())
|
self.write_int(Int::from(r.denom().clone()))
|
||||||
}
|
}
|
||||||
Value::Float(f) => {
|
Value::Float(f) => {
|
||||||
self.write_u8(5)?;
|
self.write_u8(5)?;
|
||||||
|
@ -127,13 +131,19 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
|
||||||
}
|
}
|
||||||
Value::Range(r) => {
|
Value::Range(r) => {
|
||||||
self.write_u8(7)?;
|
self.write_u8(7)?;
|
||||||
match r.ty {
|
if let Some(s) = &r.start {
|
||||||
RangeType::Open => self.write_u8(0)?,
|
self.write_u8(1)?;
|
||||||
RangeType::Closed => self.write_u8(1)?,
|
self.write_int(s.into())?;
|
||||||
RangeType::Endless => self.write_u8(2)?,
|
} else {
|
||||||
|
self.write_u8(0)?;
|
||||||
}
|
}
|
||||||
self.write_i64(r.start)?;
|
if let Some(e) = &r.end {
|
||||||
self.write_i64(r.stop)
|
self.write_u8(1)?;
|
||||||
|
self.write_int(e.into())?;
|
||||||
|
} else {
|
||||||
|
self.write_u8(0)?;
|
||||||
|
}
|
||||||
|
self.write_bool(r.inclusive)
|
||||||
}
|
}
|
||||||
Value::String(s) => {
|
Value::String(s) => {
|
||||||
self.write_u8(8)?;
|
self.write_u8(8)?;
|
||||||
|
@ -194,11 +204,36 @@ impl<'w, W: Write> ProgramWriter<'w, W> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_int(&mut self, n: Int) -> Result<()> {
|
||||||
|
if let Some(i) = n.to_i64() {
|
||||||
|
self.write_u8(0x80)?;
|
||||||
|
self.write_i64(i)
|
||||||
|
} else {
|
||||||
|
let big = BigInt::from(n);
|
||||||
|
let (sign, digits) = big.to_u64_digits();
|
||||||
|
match sign {
|
||||||
|
Sign::Minus => self.write_u8(0xff)?,
|
||||||
|
Sign::NoSign => self.write_u8(0x00)?,
|
||||||
|
Sign::Plus => self.write_u8(0x01)?,
|
||||||
|
}
|
||||||
|
self.write_u32(digits.len() as u32)?;
|
||||||
|
for digit in digits {
|
||||||
|
self.write_u64(digit)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn write_i64(&mut self, n: i64) -> Result<()> {
|
fn write_i64(&mut self, n: i64) -> Result<()> {
|
||||||
self.w.write_all(&n.to_le_bytes())?;
|
self.w.write_all(&n.to_le_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_u64(&mut self, n: u64) -> Result<()> {
|
||||||
|
self.w.write_all(&n.to_le_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn write_u32(&mut self, n: u32) -> Result<()> {
|
fn write_u32(&mut self, n: u32) -> Result<()> {
|
||||||
self.w.write_all(&n.to_le_bytes())?;
|
self.w.write_all(&n.to_le_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
exception::{throw, Result},
|
exception::{throw, Result},
|
||||||
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR},
|
lstring::LStr,
|
||||||
|
number::Int,
|
||||||
|
prelude::*,
|
||||||
|
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
value::function::NativeFunc,
|
value::function::NativeFunc,
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
vmcalliter,
|
vmcalliter,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{range::RangeType, Value};
|
use super::Value;
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn index(&self, idx: Self) -> Result<Self> {
|
pub fn index(&self, idx: Self) -> Result<Self> {
|
||||||
|
@ -14,8 +17,15 @@ impl Value {
|
||||||
match (self, idx) {
|
match (self, idx) {
|
||||||
(V::List(l), V::Int(i)) => {
|
(V::List(l), V::Int(i)) => {
|
||||||
let l = l.borrow();
|
let l = l.borrow();
|
||||||
if i >= 0 && (i as usize) < l.len() {
|
let Some(i) = i.to_usize() else {
|
||||||
Ok(l[i as usize].clone())
|
throw!(
|
||||||
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {i} out of bounds for list of length {}",
|
||||||
|
l.len()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if i < l.len() {
|
||||||
|
Ok(l[i].clone())
|
||||||
} else {
|
} else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_INDEX_ERROR,
|
*SYM_INDEX_ERROR,
|
||||||
|
@ -25,14 +35,20 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(V::Range(r), V::Int(i)) => {
|
(V::Range(r), V::Int(i)) => {
|
||||||
if i >= 0
|
if !i.is_negative() {
|
||||||
&& (r.ty == RangeType::Endless
|
if let Some(s) = &r.start {
|
||||||
|| i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
|
let n = &Int::from(s) + &i;
|
||||||
{
|
if let Some(e) = &r.end {
|
||||||
Ok((r.start + i).into())
|
let e = Int::from(e);
|
||||||
} else {
|
if n < e || (n == e && r.inclusive) {
|
||||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
return Ok(n.into())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(n.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
||||||
}
|
}
|
||||||
(V::Table(t), i) if i.hashable() => {
|
(V::Table(t), i) if i.hashable() => {
|
||||||
let t = t.borrow();
|
let t = t.borrow();
|
||||||
|
@ -40,58 +56,13 @@ impl Value {
|
||||||
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
||||||
}
|
}
|
||||||
(V::String(s), V::Range(r)) => {
|
(V::String(s), V::Range(r)) => {
|
||||||
let slen = s.len();
|
let ns = r.index_slice(s.as_bytes());
|
||||||
match r.ty {
|
Ok(LStr::from_bytes(ns).into())
|
||||||
RangeType::Open => {
|
|
||||||
if r.start < 0 || r.start > slen as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for string of length {}",
|
|
||||||
r.stop,
|
|
||||||
slen
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if r.stop < 0 || r.stop > slen as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for string of length {}",
|
|
||||||
r.stop,
|
|
||||||
slen
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Ok(s[r.start as usize..r.stop as usize].into())
|
|
||||||
}
|
|
||||||
RangeType::Closed => {
|
|
||||||
if r.start < 0 || r.start > slen as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for string of length {}",
|
|
||||||
r.stop,
|
|
||||||
slen
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if r.stop < 0 || r.stop >= slen as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for string of length {}",
|
|
||||||
r.stop,
|
|
||||||
slen
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Ok(s[r.start as usize..=r.stop as usize].into())
|
|
||||||
}
|
|
||||||
RangeType::Endless => {
|
|
||||||
if r.start < 0 || r.start > slen as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for string of length {}",
|
|
||||||
r.stop,
|
|
||||||
slen
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Ok(s[r.start as usize..].into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
(V::List(l), V::Range(r)) => {
|
||||||
|
let l = l.borrow();
|
||||||
|
let nl = r.index_slice(l.as_slice());
|
||||||
|
Ok(nl.to_vec().into())
|
||||||
}
|
}
|
||||||
(col, idx) => {
|
(col, idx) => {
|
||||||
if let Ok(ii) = idx.clone().to_iter_function() {
|
if let Ok(ii) = idx.clone().to_iter_function() {
|
||||||
|
@ -113,8 +84,15 @@ impl Value {
|
||||||
match (self, idx) {
|
match (self, idx) {
|
||||||
(V::List(l), V::Int(i)) => {
|
(V::List(l), V::Int(i)) => {
|
||||||
let mut l = l.borrow_mut();
|
let mut l = l.borrow_mut();
|
||||||
if i >= 0 && (i as usize) < l.len() {
|
let Some(i) = i.to_usize() else {
|
||||||
l[i as usize] = val;
|
throw!(
|
||||||
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {i} out of bounds for list of length {}",
|
||||||
|
l.len()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if i < l.len() {
|
||||||
|
l[i] = val;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
throw!(
|
throw!(
|
||||||
|
@ -135,70 +113,28 @@ impl Value {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
(V::List(t), V::Range(r)) => {
|
(V::List(t), V::Range(r)) => {
|
||||||
|
if let Value::List(l) = val {
|
||||||
|
let mut tm = t.borrow_mut();
|
||||||
|
if let Ok(l) = l.try_borrow() {
|
||||||
|
r.replace_range(&mut tm, l.as_slice());
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"cannot replace part of a list with itself"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let iter = val.to_iter_function()?;
|
let iter = val.to_iter_function()?;
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||||
vals.push(v);
|
vals.push(v);
|
||||||
}
|
}
|
||||||
let mut tm = t.borrow_mut();
|
let mut tm = t.borrow_mut();
|
||||||
match r.ty {
|
r.replace_range(&mut tm, &vals);
|
||||||
RangeType::Open => {
|
|
||||||
if r.start < 0 || r.start > tm.len() as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for list of length {}",
|
|
||||||
r.stop,
|
|
||||||
tm.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if r.stop < 0 || r.stop > tm.len() as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for list of length {}",
|
|
||||||
r.stop,
|
|
||||||
tm.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let end = tm.split_off(r.stop as usize);
|
|
||||||
tm.truncate(r.start as usize);
|
|
||||||
tm.extend(vals.into_iter().chain(end));
|
|
||||||
}
|
|
||||||
RangeType::Closed => {
|
|
||||||
if r.start < 0 || r.start > tm.len() as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for list of length {}",
|
|
||||||
r.stop,
|
|
||||||
tm.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if r.stop < 0 || r.stop >= tm.len() as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for list of length {}",
|
|
||||||
r.stop,
|
|
||||||
tm.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let end = tm.split_off(r.stop as usize + 1);
|
|
||||||
tm.truncate(r.start as usize);
|
|
||||||
tm.extend(vals.into_iter().chain(end));
|
|
||||||
}
|
|
||||||
RangeType::Endless => {
|
|
||||||
if r.start < 0 || r.start > tm.len() as i64 {
|
|
||||||
throw!(
|
|
||||||
*SYM_INDEX_ERROR,
|
|
||||||
"index {} out of bounds for list of length {}",
|
|
||||||
r.stop,
|
|
||||||
tm.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
tm.truncate(r.start as usize);
|
|
||||||
tm.extend(vals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
(col, idx) => {
|
(col, idx) => {
|
||||||
if let Ok(ii) = idx.clone().to_iter_function() {
|
if let Ok(ii) = idx.clone().to_iter_function() {
|
||||||
let val = val.to_iter_function()?;
|
let val = val.to_iter_function()?;
|
||||||
|
|
|
@ -3,23 +3,19 @@ use std::borrow::Cow;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
|
||||||
|
|
||||||
pub use num_complex::Complex64;
|
use num::{complex::Complex64, BigInt};
|
||||||
use num_complex::ComplexFloat;
|
|
||||||
pub use num_rational::Rational64;
|
|
||||||
|
|
||||||
use crate::exception::{throw, Exception};
|
use crate::exception::{throw, Exception};
|
||||||
use crate::lstring::{LStr, LString};
|
use crate::lstring::{LStr, LString};
|
||||||
|
use crate::number::{Int, Range, Ratio};
|
||||||
|
use crate::prelude::*;
|
||||||
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
||||||
|
|
||||||
use self::{
|
use self::function::{Function, NativeFunc};
|
||||||
function::{Function, NativeFunc},
|
|
||||||
range::{Range, RangeType},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod range;
|
|
||||||
|
|
||||||
type RcList = Rc<RefCell<Vec<Value>>>;
|
type RcList = Rc<RefCell<Vec<Value>>>;
|
||||||
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
||||||
|
@ -31,11 +27,11 @@ pub enum Value {
|
||||||
Nil,
|
Nil,
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Symbol(Symbol),
|
Symbol(Symbol),
|
||||||
Range(Range),
|
Range(Rc<Range>),
|
||||||
|
|
||||||
Int(i64),
|
Int(Int),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Ratio(Rational64),
|
Ratio(Rc<Ratio>),
|
||||||
Complex(Complex64),
|
Complex(Complex64),
|
||||||
|
|
||||||
Cell(Rc<RefCell<Value>>),
|
Cell(Rc<RefCell<Value>>),
|
||||||
|
@ -113,11 +109,7 @@ impl Value {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Self::Range(r) => match r.ty {
|
Self::Range(r) => write!(w, "{r}"),
|
||||||
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
|
|
||||||
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
|
|
||||||
RangeType::Endless => write!(w, "{}..*", r.start),
|
|
||||||
},
|
|
||||||
Self::Int(i) => write!(w, "{i}"),
|
Self::Int(i) => write!(w, "{i}"),
|
||||||
Self::Float(x) => write!(w, "{x:?}"),
|
Self::Float(x) => write!(w, "{x:?}"),
|
||||||
Self::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()),
|
Self::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()),
|
||||||
|
@ -401,6 +393,23 @@ macro_rules! impl_from {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
($ty:ty, $var:ident, rchash) => {
|
||||||
|
impl From<$ty> for Value {
|
||||||
|
fn from(value: $ty) -> Self {
|
||||||
|
Self::$var(Rc::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Rc<$ty>> for Value {
|
||||||
|
fn from(value: Rc<$ty>) -> Self {
|
||||||
|
Self::$var(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<$ty> for HashValue {
|
||||||
|
fn from(value: $ty) -> Self {
|
||||||
|
Self(Value::$var(Rc::new(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
($ty:ty, $var:ident, into) => {
|
($ty:ty, $var:ident, into) => {
|
||||||
impl From<$ty> for Value {
|
impl From<$ty> for Value {
|
||||||
fn from(value: $ty) -> Self {
|
fn from(value: $ty) -> Self {
|
||||||
|
@ -412,10 +421,12 @@ macro_rules! impl_from {
|
||||||
|
|
||||||
impl_from!(bool, Bool, hash);
|
impl_from!(bool, Bool, hash);
|
||||||
impl_from!(Symbol, Symbol, hash);
|
impl_from!(Symbol, Symbol, hash);
|
||||||
impl_from!(Range, Range);
|
impl_from!(Range, Range, rc);
|
||||||
impl_from!(i64, Int, hash);
|
impl_from!(Int, Int, hash);
|
||||||
|
impl_from!(i64, Int, into);
|
||||||
|
impl_from!(BigInt, Int, into);
|
||||||
impl_from!(f64, Float);
|
impl_from!(f64, Float);
|
||||||
impl_from!(Rational64, Ratio, hash);
|
impl_from!(Ratio, Ratio, rchash);
|
||||||
impl_from!(Complex64, Complex);
|
impl_from!(Complex64, Complex);
|
||||||
impl_from!(HashMap<HashValue, Value>, Table, rcref);
|
impl_from!(HashMap<HashValue, Value>, Table, rcref);
|
||||||
impl_from!(Vec<Value>, List, rcref);
|
impl_from!(Vec<Value>, List, rcref);
|
||||||
|
@ -439,3 +450,15 @@ impl<T: NativeValue + 'static> From<T> for Value {
|
||||||
Self::Native(Rc::new(value))
|
Self::Native(Rc::new(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<String> for Value {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::String(LStr::from_str(&value).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Value {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Self::String(LStr::from_str(value).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,43 +5,31 @@ use std::{
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use num_complex::{Complex64, ComplexFloat};
|
use num::complex::Complex64;
|
||||||
use num_rational::Rational64;
|
|
||||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
exception::{throw, Result},
|
exception::{throw, Result},
|
||||||
lstring::LString,
|
lstring::LString,
|
||||||
|
number::{Int, Range, Ratio},
|
||||||
|
prelude::*,
|
||||||
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
value::range::RangeType,
|
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
function::{FuncAttrs, NativeFunc},
|
function::{FuncAttrs, NativeFunc},
|
||||||
range::Range,
|
|
||||||
HashValue, Value,
|
HashValue, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait RatioExt {
|
|
||||||
fn to_f64(&self) -> f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RatioExt for Rational64 {
|
|
||||||
fn to_f64(&self) -> f64 {
|
|
||||||
num_traits::ToPrimitive::to_f64(self).unwrap_or(f64::NAN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn truthy(&self) -> bool {
|
pub fn truthy(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Value::Nil => false,
|
Value::Nil => false,
|
||||||
Value::Bool(b) => *b,
|
Value::Bool(b) => *b,
|
||||||
Value::Range(r) => matches!(r.len(), None | Some(1..)),
|
Value::Range(r) => !r.is_empty(),
|
||||||
Value::Int(n) => *n != 0,
|
Value::Int(n) => !n.is_zero(),
|
||||||
Value::Float(x) => *x != 0.0 && !x.is_nan(),
|
Value::Float(x) => *x != 0.0 && !x.is_nan(),
|
||||||
Value::Ratio(r) => !(*r.numer() == 0 && *r.denom() != 0),
|
Value::Ratio(r) => !r.is_zero(),
|
||||||
Value::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()),
|
Value::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()),
|
||||||
Value::String(s) => !s.is_empty(),
|
Value::String(s) => !s.is_empty(),
|
||||||
Value::List(l) => l.borrow().len() > 0,
|
Value::List(l) => l.borrow().len() > 0,
|
||||||
|
@ -59,17 +47,17 @@ impl Value {
|
||||||
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) {
|
||||||
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
|
(V::Int(x), V::Ratio(..)) => (V::Ratio(Ratio::from(x).into()), b),
|
||||||
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
|
(V::Int(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
|
||||||
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
|
(V::Int(x), V::Complex(..)) => (V::Complex(x.to_f64_uc().into()), b),
|
||||||
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
|
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64_uc()), b),
|
||||||
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
|
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64_uc().into()), b),
|
||||||
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
|
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
|
||||||
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
|
(V::Ratio(..), V::Int(y)) => (a, V::Ratio(Ratio::from(y).into())),
|
||||||
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
|
(V::Float(..), V::Int(y)) => (a, V::Float(y.to_f64_uc())),
|
||||||
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
|
(V::Complex(..), V::Int(y)) => (a, V::Complex(y.to_f64_uc().into())),
|
||||||
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
|
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64_uc())),
|
||||||
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
|
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64_uc().into())),
|
||||||
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
|
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
|
||||||
_ => (a, b),
|
_ => (a, b),
|
||||||
}
|
}
|
||||||
|
@ -84,20 +72,8 @@ impl Neg for Value {
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match self {
|
match self {
|
||||||
V::Int(x) => {
|
V::Int(x) => Ok(V::Int(-x)),
|
||||||
if let Some(x) = x.checked_neg() {
|
V::Ratio(x) => Ok(V::Ratio((-x.as_ref()).into())),
|
||||||
Ok(V::Int(x))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
V::Ratio(x) => {
|
|
||||||
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
|
|
||||||
Ok(V::Ratio(x))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
V::Float(x) => Ok(V::Float(-x)),
|
V::Float(x) => Ok(V::Float(-x)),
|
||||||
V::Complex(x) => Ok(V::Complex(-x)),
|
V::Complex(x) => Ok(V::Complex(-x)),
|
||||||
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
||||||
|
@ -109,26 +85,8 @@ impl Value {
|
||||||
pub fn abs(self) -> Result<Self> {
|
pub fn abs(self) -> Result<Self> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match self {
|
match self {
|
||||||
V::Int(x) => {
|
V::Int(x) => Ok(V::Int(x.abs())),
|
||||||
if let Some(x) = x.checked_abs() {
|
V::Ratio(x) => Ok(V::Ratio(x.abs().into())),
|
||||||
Ok(V::Int(x))
|
|
||||||
} else {
|
|
||||||
throw!(
|
|
||||||
*SYM_VALUE_ERROR,
|
|
||||||
"overflow when finding absolute value of {self}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
V::Ratio(x) => {
|
|
||||||
if let Some((x, _)) = ratio_checked_absign(&x) {
|
|
||||||
Ok(V::Ratio(x))
|
|
||||||
} else {
|
|
||||||
throw!(
|
|
||||||
*SYM_VALUE_ERROR,
|
|
||||||
"overflow when finding absolute value of {self}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
V::Float(x) => Ok(V::Float(x.abs())),
|
V::Float(x) => Ok(V::Float(x.abs())),
|
||||||
V::Complex(x) => Ok(V::Float(x.norm())),
|
V::Complex(x) => Ok(V::Float(x.norm())),
|
||||||
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
||||||
|
@ -141,7 +99,7 @@ impl Value {
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
||||||
macro_rules! impl_value_arith {
|
macro_rules! impl_value_arith {
|
||||||
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
|
($trait:ident, $name:ident, $op:tt, $verb:literal) => {
|
||||||
|
|
||||||
impl $trait<Value> for Value {
|
impl $trait<Value> for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
|
@ -149,16 +107,8 @@ macro_rules! impl_value_arith {
|
||||||
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(y) {
|
(V::Int(x), V::Int(y)) => Ok(V::Int(x $op y)),
|
||||||
Ok(V::Int(v))
|
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() $op y.as_ref()).into())),
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
|
|
||||||
},
|
|
||||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.$checked(y) {
|
|
||||||
Ok(V::Ratio(v))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
|
|
||||||
},
|
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
|
||||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
|
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
|
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
|
||||||
|
@ -169,9 +119,9 @@ macro_rules! impl_value_arith {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_value_arith!(Add, add, checked_add, +, "add");
|
impl_value_arith!(Add, add, +, "add");
|
||||||
impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
|
impl_value_arith!(Sub, sub, -, "subtract");
|
||||||
impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
|
impl_value_arith!(Mul, mul, *, "multiply");
|
||||||
|
|
||||||
impl Div<Value> for Value {
|
impl Div<Value> for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
|
@ -179,18 +129,16 @@ impl Div<Value> for Value {
|
||||||
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(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
|
(V::Int(_), V::Int(y)) if y.is_zero() => {
|
||||||
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
|
throw!(*SYM_VALUE_ERROR, "integer division by 0")
|
||||||
|
}
|
||||||
|
(V::Int(x), V::Int(y)) => {
|
||||||
|
Ok(Value::from(Ratio::new(x.clone().into(), y.clone().into())))
|
||||||
|
}
|
||||||
(V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
|
(V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
|
||||||
throw!(*SYM_VALUE_ERROR, "rational division by 0")
|
throw!(*SYM_VALUE_ERROR, "rational division by 0")
|
||||||
}
|
}
|
||||||
(V::Ratio(x), V::Ratio(y)) => {
|
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio((x.as_ref() / y.as_ref()).into())),
|
||||||
if let Some(v) = x.checked_div(y) {
|
|
||||||
Ok(V::Ratio(v))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
|
||||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
|
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
|
||||||
|
@ -202,54 +150,20 @@ impl Div<Value> for Value {
|
||||||
// modulo and integer division //
|
// modulo and integer division //
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
|
|
||||||
let a = if r.is_negative() {
|
|
||||||
Rational64::ZERO.checked_sub(r)?
|
|
||||||
} else {
|
|
||||||
*r
|
|
||||||
};
|
|
||||||
Some((a, r.signum()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
|
||||||
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;
|
||||||
let (a, b) = promote(self, rhs);
|
let (a, b) = promote(self, rhs);
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
|
(V::Int(_), V::Int(y)) if y.is_zero() => {
|
||||||
(V::Int(x), V::Int(y)) => {
|
throw!(*SYM_VALUE_ERROR, "integer modulo by 0")
|
||||||
if let Some(v) = x.checked_rem_euclid(*y) {
|
|
||||||
Ok(V::Int(v))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
|
||||||
}
|
}
|
||||||
}
|
(V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))),
|
||||||
|
|
||||||
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
||||||
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
|
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
|
||||||
}
|
}
|
||||||
(V::Ratio(x), V::Ratio(y)) => {
|
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
|
||||||
if let Some(v) = ratio_checked_rem_euclid(x, y) {
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))),
|
||||||
Ok(V::Ratio(v))
|
|
||||||
} else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
|
|
||||||
(V::Complex(x), V::Complex(y)) => {
|
(V::Complex(x), V::Complex(y)) => {
|
||||||
let n = x / y;
|
let n = x / y;
|
||||||
let n = Complex64::new(n.re().floor(), n.im().floor());
|
let n = Complex64::new(n.re().floor(), n.im().floor());
|
||||||
|
@ -263,33 +177,15 @@ impl Value {
|
||||||
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(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
|
(V::Int(_), V::Int(y)) if y.is_zero() => {
|
||||||
(V::Int(x), V::Int(y)) => {
|
throw!(*SYM_VALUE_ERROR, "integer divsion by 0")
|
||||||
if let Some(v) = x.checked_div_euclid(*y) {
|
|
||||||
Ok(V::Int(v))
|
|
||||||
} else {
|
|
||||||
throw!(
|
|
||||||
*SYM_VALUE_ERROR,
|
|
||||||
"overflow when integer dividing {a} and {b}"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
(V::Int(x), V::Int(y)) => Ok(Value::from(x.div_euclid(y))),
|
||||||
|
|
||||||
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
||||||
throw!(*SYM_VALUE_ERROR, "integer division by 0")
|
throw!(*SYM_VALUE_ERROR, "integer division by 0")
|
||||||
}
|
}
|
||||||
(V::Ratio(x), V::Ratio(y)) => {
|
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x.rem_euclid(y).into())),
|
||||||
if let Some(v) = ratio_checked_div_euclid(x, y) {
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))),
|
||||||
Ok(V::Ratio(v))
|
|
||||||
} else {
|
|
||||||
throw!(
|
|
||||||
*SYM_VALUE_ERROR,
|
|
||||||
"overflow when integer dividing {a} and {b}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
|
|
||||||
(V::Complex(x), V::Complex(y)) => {
|
(V::Complex(x), V::Complex(y)) => {
|
||||||
let n = x / y;
|
let n = x / y;
|
||||||
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
|
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
|
||||||
|
@ -304,29 +200,11 @@ impl Value {
|
||||||
//////////////////////
|
//////////////////////
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ipow(n: i64, p: u64) -> Option<i64> {
|
fn rpow(n: &Int, d: &Int, p: &Int) -> Option<Ratio> {
|
||||||
match (n, p) {
|
let (sign, p) = (p.isignum(), p.abs());
|
||||||
(0, 0) => None,
|
match sign {
|
||||||
(0, _) => Some(0),
|
0.. => Some(Ratio::new(n.ipow(&p)?.into(), d.ipow(&p)?.into())),
|
||||||
(_, 0) => Some(1),
|
_ => Some(Ratio::new(d.ipow(&p)?.into(), n.ipow(&p)?.into())),
|
||||||
(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)?)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,10 +212,10 @@ 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) {
|
||||||
if x.is_zero() && *y == 0 {
|
if x.is_zero() && y.is_zero() {
|
||||||
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
|
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
|
||||||
}
|
}
|
||||||
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
|
let Some(v) = rpow(&x.numer().into(), &x.denom().into(), y) else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"overflow when raising {self} to the power {rhs}"
|
"overflow when raising {self} to the power {rhs}"
|
||||||
|
@ -347,11 +225,11 @@ impl Value {
|
||||||
}
|
}
|
||||||
let (a, b) = promote(self, rhs);
|
let (a, b) = promote(self, rhs);
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(0), V::Int(0)) => {
|
(V::Int(x), V::Int(y)) if x.is_zero() && y.is_zero() => {
|
||||||
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
|
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
|
||||||
}
|
}
|
||||||
(V::Int(x), V::Int(y @ 0..)) => {
|
(V::Int(x), V::Int(y)) if !y.is_negative() => {
|
||||||
if let Some(v) = ipow(*x, *y as u64) {
|
if let Some(v) = x.ipow(y) {
|
||||||
Ok(V::Int(v))
|
Ok(V::Int(v))
|
||||||
} else {
|
} else {
|
||||||
throw!(
|
throw!(
|
||||||
|
@ -361,7 +239,7 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(V::Int(x), V::Int(y)) => {
|
(V::Int(x), V::Int(y)) => {
|
||||||
if let Some(v) = rpow(*x, 1, *y) {
|
if let Some(v) = rpow(x, &Int::ONE, y) {
|
||||||
Ok(V::Ratio(v.into()))
|
Ok(V::Ratio(v.into()))
|
||||||
} else {
|
} else {
|
||||||
throw!(
|
throw!(
|
||||||
|
@ -370,7 +248,7 @@ impl Value {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
|
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64_uc().powf(y.to_f64_uc()))),
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
|
||||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
|
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
|
||||||
|
@ -389,7 +267,16 @@ impl Shl<Value> for Value {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(V::Int(a), V::Int(b)) => {
|
(V::Int(a), V::Int(b)) => {
|
||||||
Ok(Value::Int((a as u64).wrapping_shl(b as u32) as i64))
|
if b.is_negative() {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"cannot shift {a} left by negative amount {b}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if b.gt_i64(u32::MAX as i64) {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "cannot shift {a} left by {b}: too large")
|
||||||
|
}
|
||||||
|
Ok(Value::Int(a << b))
|
||||||
}
|
}
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}"),
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}"),
|
||||||
}
|
}
|
||||||
|
@ -403,7 +290,13 @@ impl Shr<Value> for Value {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(V::Int(a), V::Int(b)) => {
|
(V::Int(a), V::Int(b)) => {
|
||||||
Ok(Value::Int((a as u64).wrapping_shr(b as u32) as i64))
|
if b.is_negative() {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"cannot shift {a} right by negative amount {b}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(Value::Int(a >> b))
|
||||||
}
|
}
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
|
||||||
}
|
}
|
||||||
|
@ -464,7 +357,6 @@ impl Not for Value {
|
||||||
|
|
||||||
impl PartialEq for Value {
|
impl PartialEq for Value {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
use super::range::RangeType as Rty;
|
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(V::Nil, V::Nil) => true,
|
(V::Nil, V::Nil) => true,
|
||||||
|
@ -473,31 +365,23 @@ impl PartialEq for Value {
|
||||||
(V::Ratio(a), V::Ratio(b)) => *a == *b,
|
(V::Ratio(a), V::Ratio(b)) => *a == *b,
|
||||||
(V::Float(a), V::Float(b)) => *a == *b,
|
(V::Float(a), V::Float(b)) => *a == *b,
|
||||||
(V::Complex(a), V::Complex(b)) => *a == *b,
|
(V::Complex(a), V::Complex(b)) => *a == *b,
|
||||||
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
|
(V::Int(a), V::Ratio(b)) => b.is_integer() && a.to_big().as_ref() == b.numer(),
|
||||||
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
|
(V::Ratio(a), V::Int(b)) => a.is_integer() && a.numer() == b.to_big().as_ref(),
|
||||||
(V::Int(a), V::Float(b)) => *a as f64 == *b,
|
(V::Int(a), V::Float(b)) => a.to_f64_uc() == *b,
|
||||||
(V::Float(a), V::Int(b)) => *a == *b as f64,
|
(V::Float(a), V::Int(b)) => *a == b.to_f64_uc(),
|
||||||
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
|
(V::Int(a), V::Complex(b)) => Complex64::from(a.to_f64_uc()) == *b,
|
||||||
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
|
(V::Complex(a), V::Int(b)) => *a == Complex64::from(b.to_f64_uc()),
|
||||||
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
|
(V::Ratio(a), V::Float(b)) => a.to_f64_uc() == *b,
|
||||||
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
|
(V::Float(a), V::Ratio(b)) => *a == b.to_f64_uc(),
|
||||||
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
|
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64_uc()) == *b,
|
||||||
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()),
|
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64_uc()),
|
||||||
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
|
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
|
||||||
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
|
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
|
||||||
(V::String(a), V::String(b)) => *a == *b,
|
(V::String(a), V::String(b)) => *a == *b,
|
||||||
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
|
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
|
||||||
(V::Symbol(a), V::Symbol(b)) => a == b,
|
(V::Symbol(a), V::Symbol(b)) => a == b,
|
||||||
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
|
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
|
||||||
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
|
(V::Range(a), V::Range(b)) => a.as_ref() == b.as_ref(),
|
||||||
(Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
|
|
||||||
a.start == b.start && a.stop == b.stop
|
|
||||||
}
|
|
||||||
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
|
|
||||||
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
|
|
||||||
(Rty::Endless, Rty::Endless) => a.start == b.start,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
(V::Native(a), b) => a.partial_eq(b),
|
(V::Native(a), b) => a.partial_eq(b),
|
||||||
(a, V::Native(b)) => b.partial_eq(a),
|
(a, V::Native(b)) => b.partial_eq(a),
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -514,12 +398,12 @@ impl PartialOrd for Value {
|
||||||
(V::Int(a), V::Int(b)) => a.partial_cmp(b),
|
(V::Int(a), V::Int(b)) => a.partial_cmp(b),
|
||||||
(V::Ratio(a), V::Ratio(b)) => a.partial_cmp(b),
|
(V::Ratio(a), V::Ratio(b)) => a.partial_cmp(b),
|
||||||
(V::Float(a), V::Float(b)) => a.partial_cmp(b),
|
(V::Float(a), V::Float(b)) => a.partial_cmp(b),
|
||||||
(V::Int(a), V::Ratio(b)) => Rational64::from(*a).partial_cmp(b),
|
(V::Int(a), V::Ratio(b)) => Ratio::from(a).partial_cmp(b),
|
||||||
(V::Ratio(a), V::Int(b)) => a.partial_cmp(&Rational64::from(*b)),
|
(V::Ratio(a), V::Int(b)) => a.as_ref().partial_cmp(&Ratio::from(b)),
|
||||||
(V::Int(a), V::Float(b)) => (*a as f64).partial_cmp(b),
|
(V::Int(a), V::Float(b)) => a.to_f64_uc().partial_cmp(b),
|
||||||
(V::Float(a), V::Int(b)) => a.partial_cmp(&(*b as f64)),
|
(V::Float(a), V::Int(b)) => a.partial_cmp(&b.to_f64_uc()),
|
||||||
(V::Ratio(a), V::Float(b)) => a.to_f64().partial_cmp(b),
|
(V::Ratio(a), V::Float(b)) => a.to_f64_uc().partial_cmp(b),
|
||||||
(V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64()),
|
(V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64_uc()),
|
||||||
(V::String(a), V::String(b)) => a.partial_cmp(b),
|
(V::String(a), V::String(b)) => a.partial_cmp(b),
|
||||||
(V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()),
|
(V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()),
|
||||||
(V::Symbol(a), V::Symbol(b)) => a.partial_cmp(b),
|
(V::Symbol(a), V::Symbol(b)) => a.partial_cmp(b),
|
||||||
|
@ -574,19 +458,9 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
|
pub fn range(&self, other: &Self) -> Result<Self> {
|
||||||
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
|
if let (Value::Int(start), Value::Int(end)) = (self, other) {
|
||||||
let ty = if closed {
|
Ok(Range::range(start, end).into())
|
||||||
RangeType::Closed
|
|
||||||
} else {
|
|
||||||
RangeType::Open
|
|
||||||
};
|
|
||||||
Ok(Range {
|
|
||||||
start: *start,
|
|
||||||
stop: *stop,
|
|
||||||
ty,
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
} else {
|
} else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_TYPE_ERROR,
|
*SYM_TYPE_ERROR,
|
||||||
|
@ -595,19 +469,48 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range_endless(&self) -> Result<Self> {
|
pub fn range_incl(&self, other: &Self) -> Result<Self> {
|
||||||
if let Value::Int(start) = self {
|
if let (Value::Int(start), Value::Int(end)) = (self, other) {
|
||||||
Ok(Range {
|
Ok(Range::range_incl(start, end).into())
|
||||||
start: *start,
|
|
||||||
stop: 0,
|
|
||||||
ty: RangeType::Endless,
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"cannot create range between {self:#} and {other:#}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn range_from(&self) -> Result<Self> {
|
||||||
|
if let Value::Int(start) = self {
|
||||||
|
Ok(Range::range_from(start).into())
|
||||||
|
} else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "cannot create range from {self:#}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_to(&self) -> Result<Self> {
|
||||||
|
if let Value::Int(end) = self {
|
||||||
|
Ok(Range::range_to(end).into())
|
||||||
|
} else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "cannot create range to {self:#}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_to_incl(&self) -> Result<Self> {
|
||||||
|
if let Value::Int(end) = self {
|
||||||
|
Ok(Range::range_to_incl(end).into())
|
||||||
|
} else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "cannot create range to {self:#}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_all() -> Self {
|
||||||
|
thread_local! {
|
||||||
|
static RANGE_ALL: Rc<Range> = Rc::new(Range::range_all());
|
||||||
|
}
|
||||||
|
Self::Range(RANGE_ALL.with(|x| x.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_cell(self) -> Self {
|
pub fn to_cell(self) -> Self {
|
||||||
Value::Cell(Rc::new(RefCell::new(self)))
|
Value::Cell(Rc::new(RefCell::new(self)))
|
||||||
}
|
}
|
||||||
|
@ -630,7 +533,10 @@ impl Value {
|
||||||
match self {
|
match self {
|
||||||
Self::Function(_) | Self::NativeFunc(_) => Ok(self),
|
Self::Function(_) | Self::NativeFunc(_) => Ok(self),
|
||||||
Self::Range(range) => {
|
Self::Range(range) => {
|
||||||
let range_iter = RefCell::new(range.into_iter());
|
let Some(iter) = range.try_iterate() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "cannot iterate range {range}")
|
||||||
|
};
|
||||||
|
let range_iter = RefCell::new(iter);
|
||||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||||
Ok(Value::iter_pack(
|
Ok(Value::iter_pack(
|
||||||
range_iter.borrow_mut().next().map(Value::from),
|
range_iter.borrow_mut().next().map(Value::from),
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum RangeType {
|
|
||||||
Open,
|
|
||||||
Closed,
|
|
||||||
Endless,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct Range {
|
|
||||||
pub start: i64,
|
|
||||||
pub stop: i64,
|
|
||||||
pub ty: RangeType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Range {
|
|
||||||
pub fn len(&self) -> Option<usize> {
|
|
||||||
match self.ty {
|
|
||||||
RangeType::Open => Some((self.stop - self.start).max(0) as usize),
|
|
||||||
RangeType::Closed => Some((self.stop - self.start + 1).max(0) as usize),
|
|
||||||
RangeType::Endless => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
match self.ty {
|
|
||||||
RangeType::Open => self.stop - self.start <= 0,
|
|
||||||
RangeType::Closed => self.stop - self.start < 0,
|
|
||||||
RangeType::Endless => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for Range {
|
|
||||||
type Item = i64;
|
|
||||||
type IntoIter = Box<dyn Iterator<Item = i64>>;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
match self.ty {
|
|
||||||
RangeType::Open => Box::new(self.start..self.stop),
|
|
||||||
RangeType::Closed => Box::new(self.start..=self.stop),
|
|
||||||
RangeType::Endless => Box::new(self.start..),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -74,8 +74,8 @@ pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
|
||||||
BinaryOp::Ge => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Less)),
|
BinaryOp::Ge => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Less)),
|
||||||
BinaryOp::Lt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Less)),
|
BinaryOp::Lt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Less)),
|
||||||
BinaryOp::Le => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Greater)),
|
BinaryOp::Le => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Greater)),
|
||||||
BinaryOp::Range => a.range(&b, false),
|
BinaryOp::Range => a.range(&b),
|
||||||
BinaryOp::RangeIncl => a.range(&b, true),
|
BinaryOp::RangeIncl => a.range_incl(&b),
|
||||||
BinaryOp::Concat => a.concat(&b),
|
BinaryOp::Concat => a.concat(&b),
|
||||||
BinaryOp::Append => a.append(b),
|
BinaryOp::Append => a.append(b),
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,9 @@ pub fn unary_op(o: UnaryOp, a: Value) -> Result<Value> {
|
||||||
UnaryOp::Neg => -a,
|
UnaryOp::Neg => -a,
|
||||||
UnaryOp::Not => Ok(Value::Bool(!a.truthy())),
|
UnaryOp::Not => Ok(Value::Bool(!a.truthy())),
|
||||||
UnaryOp::BitNot => a.not(),
|
UnaryOp::BitNot => a.not(),
|
||||||
UnaryOp::RangeEndless => a.range_endless(),
|
UnaryOp::RangeFrom => a.range_from(),
|
||||||
|
UnaryOp::RangeTo => a.range_to(),
|
||||||
|
UnaryOp::RangeToIncl => a.range_to_incl(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +354,7 @@ impl Vm {
|
||||||
self.push(Value::Symbol(sym));
|
self.push(Value::Symbol(sym));
|
||||||
}
|
}
|
||||||
// [] -> [n]
|
// [] -> [n]
|
||||||
I::Int(n) => self.push(Value::Int(i64::from(n))),
|
I::Int(n) => self.push(Value::from(i64::from(n))),
|
||||||
// [x] -> [x,x]
|
// [x] -> [x,x]
|
||||||
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
|
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
|
||||||
// [x,y] -> [x,y,x,y]
|
// [x,y] -> [x,y,x,y]
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn scope() {
|
||||||
end
|
end
|
||||||
x + y
|
x + y
|
||||||
",
|
",
|
||||||
Value::Int(7 + 100),
|
Value::from(7 + 100),
|
||||||
);
|
);
|
||||||
assert_eval(
|
assert_eval(
|
||||||
"
|
"
|
||||||
|
@ -41,7 +41,7 @@ fn scope() {
|
||||||
if cond then var z = 5 else var z = 6 end
|
if cond then var z = 5 else var z = 6 end
|
||||||
z
|
z
|
||||||
",
|
",
|
||||||
Value::Int(2),
|
Value::from(2),
|
||||||
);
|
);
|
||||||
assert_eval(
|
assert_eval(
|
||||||
"
|
"
|
||||||
|
@ -52,7 +52,7 @@ fn scope() {
|
||||||
end
|
end
|
||||||
i + j
|
i + j
|
||||||
",
|
",
|
||||||
Value::Int(55 + 9),
|
Value::from(55 + 9),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ fn forloop() {
|
||||||
end
|
end
|
||||||
sum
|
sum
|
||||||
",
|
",
|
||||||
Value::Int(45 * 10),
|
Value::from(45 * 10),
|
||||||
);
|
);
|
||||||
assert_eval(
|
assert_eval(
|
||||||
"
|
"
|
||||||
|
@ -79,7 +79,7 @@ fn forloop() {
|
||||||
end
|
end
|
||||||
prod
|
prod
|
||||||
",
|
",
|
||||||
Value::Int(3 * 2 * 4 * 7),
|
Value::from(3 * 2 * 4 * 7),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,10 +88,10 @@ fn closures() {
|
||||||
assert_eval(
|
assert_eval(
|
||||||
"
|
"
|
||||||
var x = 2
|
var x = 2
|
||||||
next = \\. do x = x * 2 + 1 end
|
next = \\->do x = x * 2 + 1 end
|
||||||
next() + next() + next()
|
next() + next() + next()
|
||||||
",
|
",
|
||||||
Value::Int(5 + 11 + 23),
|
Value::from(5 + 11 + 23),
|
||||||
);
|
);
|
||||||
assert_eval(
|
assert_eval(
|
||||||
"
|
"
|
||||||
|
@ -108,7 +108,7 @@ fn closures() {
|
||||||
var g = outer(6)
|
var g = outer(6)
|
||||||
f() + f() + g() + g() + f()
|
f() + f() + g() + g() + f()
|
||||||
",
|
",
|
||||||
Value::Int(2 + 5 + 11 + 18 + 22),
|
Value::from(2 + 5 + 11 + 18 + 22),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +125,6 @@ fn tailcall() {
|
||||||
end
|
end
|
||||||
test(24, 0)
|
test(24, 0)
|
||||||
",
|
",
|
||||||
Value::Int(300),
|
Value::from(300),
|
||||||
) // = 24 + 23 + ... + 1
|
) // = 24 + 23 + ... + 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,11 @@ edition = "2021"
|
||||||
talc-lang = { path = "../talc-lang" }
|
talc-lang = { path = "../talc-lang" }
|
||||||
talc-macros = { path = "../talc-macros" }
|
talc-macros = { path = "../talc-macros" }
|
||||||
lazy_static = "1.5"
|
lazy_static = "1.5"
|
||||||
|
num-bigint = { version = "0.4", features = ["rand"], optional = true }
|
||||||
regex = { version = "1.11", optional = true }
|
regex = { version = "1.11", optional = true }
|
||||||
rand = { version = "0.8", optional = true }
|
rand = { version = "0.8", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rand", "regex"]
|
default = ["rand", "regex"]
|
||||||
rand = ["dep:rand"]
|
rand = ["dep:rand", "dep:num-bigint"]
|
||||||
regex = ["dep:regex"]
|
regex = ["dep:regex"]
|
||||||
|
|
|
@ -2,6 +2,8 @@ use std::cmp::Ordering;
|
||||||
|
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::{exception, Result},
|
exception::{exception, Result},
|
||||||
|
number::Int,
|
||||||
|
prelude::*,
|
||||||
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
value::{function::NativeFunc, Value},
|
value::{function::NativeFunc, Value},
|
||||||
|
@ -69,7 +71,7 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(col)
|
Ok(col)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
|
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<Int> {
|
||||||
let ord = vmcall!(vm; by, l, r)?;
|
let ord = vmcall!(vm; by, l, r)?;
|
||||||
let Value::Int(ord) = ord else {
|
let Value::Int(ord) = ord else {
|
||||||
throw!(
|
throw!(
|
||||||
|
@ -117,17 +119,17 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
|
||||||
|
|
||||||
while eq <= gt {
|
while eq <= gt {
|
||||||
let ord = call_comparison(vm, by.clone(), vals[eq].clone(), pivot.clone())?;
|
let ord = call_comparison(vm, by.clone(), vals[eq].clone(), pivot.clone())?;
|
||||||
match ord {
|
match ord.cmp(&Int::ZERO) {
|
||||||
..=-1 => {
|
Ordering::Less => {
|
||||||
vals.swap(eq, lt);
|
vals.swap(eq, lt);
|
||||||
lt += 1;
|
lt += 1;
|
||||||
eq += 1;
|
eq += 1;
|
||||||
}
|
}
|
||||||
1.. => {
|
Ordering::Greater => {
|
||||||
vals.swap(eq, gt);
|
vals.swap(eq, gt);
|
||||||
gt -= 1;
|
gt -= 1;
|
||||||
}
|
}
|
||||||
0 => {
|
Ordering::Equal => {
|
||||||
eq += 1;
|
eq += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +154,7 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
|
||||||
while j > 0 {
|
while j > 0 {
|
||||||
let ord =
|
let ord =
|
||||||
call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
|
call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
|
||||||
if ord <= 0 {
|
if !ord.is_positive() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
vals.swap(j, j - 1);
|
vals.swap(j, j - 1);
|
||||||
|
@ -212,9 +214,9 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let a = vmcall!(vm; key.clone(), a)?;
|
let a = vmcall!(vm; key.clone(), a)?;
|
||||||
let b = vmcall!(vm; key.clone(), b)?;
|
let b = vmcall!(vm; key.clone(), b)?;
|
||||||
match a.partial_cmp(&b) {
|
match a.partial_cmp(&b) {
|
||||||
Some(Ordering::Greater) => Ok(Value::Int(1)),
|
Some(Ordering::Greater) => Ok(Value::from(1)),
|
||||||
Some(Ordering::Equal) => Ok(Value::Int(0)),
|
Some(Ordering::Equal) => Ok(Value::from(0)),
|
||||||
Some(Ordering::Less) => Ok(Value::Int(-1)),
|
Some(Ordering::Less) => Ok(Value::from(-1)),
|
||||||
None => throw!(
|
None => throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"values returned from sort key were incomparable"
|
"values returned from sort key were incomparable"
|
||||||
|
|
|
@ -13,6 +13,7 @@ use lazy_static::lazy_static;
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::Result,
|
exception::Result,
|
||||||
lstring::LString,
|
lstring::LString,
|
||||||
|
prelude::*,
|
||||||
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
value::{function::NativeFunc, HashValue, NativeValue, Value},
|
value::{function::NativeFunc, HashValue, NativeValue, Value},
|
||||||
|
@ -294,7 +295,7 @@ pub fn read(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(nbytes) = nbytes else {
|
let Value::Int(nbytes) = nbytes else {
|
||||||
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
|
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
|
||||||
};
|
};
|
||||||
let Ok(nbytes) = usize::try_from(nbytes) else {
|
let Some(nbytes) = nbytes.to_usize() else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"number of bytes to read must be nonnegative"
|
"number of bytes to read must be nonnegative"
|
||||||
|
@ -464,11 +465,14 @@ pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
|
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
|
||||||
};
|
};
|
||||||
let timeout = match timeout {
|
let timeout = match timeout {
|
||||||
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
|
Value::Int(n) => match n.to_usize() {
|
||||||
|
Some(n) => Duration::from_secs(n as u64),
|
||||||
|
None => throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout",),
|
||||||
|
},
|
||||||
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
|
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
|
||||||
Duration::from_secs_f64(n)
|
Duration::from_secs_f64(n)
|
||||||
}
|
}
|
||||||
Value::Int(_) | Value::Float(_) => {
|
Value::Float(_) => {
|
||||||
throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout")
|
throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout")
|
||||||
}
|
}
|
||||||
_ => throw!(
|
_ => throw!(
|
||||||
|
@ -755,7 +759,7 @@ pub fn exit_code(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match proc.wait() {
|
match proc.wait() {
|
||||||
Ok(code) => Ok(code
|
Ok(code) => Ok(code
|
||||||
.code()
|
.code()
|
||||||
.map(|c| Value::Int(c as i64))
|
.map(|c| Value::from(c as i64))
|
||||||
.unwrap_or_default()),
|
.unwrap_or_default()),
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,12 @@ use std::io::Write;
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::{exception, Result},
|
exception::{exception, Result},
|
||||||
lstring::{LStr, LString},
|
lstring::{LStr, LString},
|
||||||
parser::{parse_int, to_lstring_radix},
|
number::{Complex, Int, Ratio},
|
||||||
|
parser::parse_int,
|
||||||
|
prelude::*,
|
||||||
symbol::SYM_TYPE_ERROR,
|
symbol::SYM_TYPE_ERROR,
|
||||||
throw,
|
throw,
|
||||||
value::{Complex64, Rational64, Value},
|
value::Value,
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
};
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
@ -235,16 +237,16 @@ fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
|
||||||
let Value::Int(v) = v else {
|
let Value::Int(v) = v else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_FORMAT_ERROR,
|
*SYM_FORMAT_ERROR,
|
||||||
"expected positive integer argument, found {v:#}"
|
"expected small positive integer argument, found {v:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if *v < 0 {
|
match v.to_usize() {
|
||||||
throw!(
|
Some(v) => Ok(v),
|
||||||
|
None => throw!(
|
||||||
*SYM_FORMAT_ERROR,
|
*SYM_FORMAT_ERROR,
|
||||||
"expected positive integer argument, found {v}"
|
"expected small positive integer argument, found {v}"
|
||||||
)
|
),
|
||||||
}
|
}
|
||||||
Ok(*v as usize)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_float(
|
fn format_float(
|
||||||
|
@ -270,7 +272,7 @@ fn format_float(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_complex(
|
fn format_complex(
|
||||||
cx: Complex64,
|
cx: Complex,
|
||||||
prec: Option<usize>,
|
prec: Option<usize>,
|
||||||
ty: FmtType,
|
ty: FmtType,
|
||||||
buf: &mut LString,
|
buf: &mut LString,
|
||||||
|
@ -286,7 +288,7 @@ fn format_complex(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_int(
|
fn format_int(
|
||||||
n: i64,
|
n: &Int,
|
||||||
prec: Option<usize>,
|
prec: Option<usize>,
|
||||||
ty: FmtType,
|
ty: FmtType,
|
||||||
buf: &mut LString,
|
buf: &mut LString,
|
||||||
|
@ -296,26 +298,25 @@ fn format_int(
|
||||||
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||||
}
|
}
|
||||||
let res = match ty {
|
let res = match ty {
|
||||||
FmtType::Str => write!(buf, "{}", Value::Int(n)),
|
FmtType::Str | FmtType::Repr => write!(buf, "{}", n),
|
||||||
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
|
FmtType::Hex(caps) => write!(buf, "{}", n.to_str_radix_case(16, caps)),
|
||||||
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
|
FmtType::Oct => write!(buf, "{}", n.to_str_radix(8)),
|
||||||
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
|
FmtType::Sex => write!(buf, "{}", n.to_str_radix(6)),
|
||||||
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
|
FmtType::Bin => write!(buf, "{}", n.to_str_radix(2)),
|
||||||
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
|
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
|
||||||
};
|
};
|
||||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_ratio(
|
fn format_ratio(
|
||||||
n: Rational64,
|
n: &Ratio,
|
||||||
prec: Option<usize>,
|
prec: Option<usize>,
|
||||||
ty: FmtType,
|
ty: FmtType,
|
||||||
buf: &mut LString,
|
buf: &mut LString,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
format_int(*n.numer(), prec, ty, buf, "ratio")?;
|
format_int(&Int::from(n.numer()), prec, ty, buf, "ratio")?;
|
||||||
buf.push_char('/');
|
buf.push_char('/');
|
||||||
format_int(*n.denom(), prec, ty, buf, "ratio")?;
|
format_int(&Int::from(n.denom()), prec, ty, buf, "ratio")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,8 +384,8 @@ fn format_arg(
|
||||||
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
|
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||||
|
|
||||||
match fmt_arg {
|
match fmt_arg {
|
||||||
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
Value::Int(n) => format_int(n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
||||||
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
|
Value::Ratio(n) => format_ratio(n, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
|
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
|
||||||
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
|
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
|
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
|
|
452
talc-std/src/ints.rs
Normal file
452
talc-std/src/ints.rs
Normal file
|
@ -0,0 +1,452 @@
|
||||||
|
use talc_lang::{
|
||||||
|
exception::Result,
|
||||||
|
number::Int,
|
||||||
|
prelude::*,
|
||||||
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::Value,
|
||||||
|
vm::Vm,
|
||||||
|
vmcalliter,
|
||||||
|
};
|
||||||
|
use talc_macros::native_func;
|
||||||
|
|
||||||
|
use crate::unpack_args;
|
||||||
|
|
||||||
|
fn miller_rabin_iter(n: &Int, s: u64, d: &Int, a: &Int) -> bool {
|
||||||
|
if a > n {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
let mut x = a.modpow(d, n);
|
||||||
|
for _ in 0..s {
|
||||||
|
let y = &(&x * &x) % n;
|
||||||
|
if y.eq_i64(1) && x.ne_i64(1) && x != (n - 1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
x = y;
|
||||||
|
}
|
||||||
|
x.eq_i64(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn miller_rabin(n: &Int) -> bool {
|
||||||
|
if n.lt_i64(2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n.is_even() {
|
||||||
|
return n.eq_i64(2)
|
||||||
|
}
|
||||||
|
for p in [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
|
||||||
|
if (n % p).is_zero() {
|
||||||
|
return n.eq_i64(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n.lt_i64(41) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let s = (n - 1).trailing_zeros().unwrap();
|
||||||
|
let d = n >> &Int::from(s);
|
||||||
|
for a in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
|
||||||
|
if !miller_rabin_iter(n, s, &d, &Int::from_small(a)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trial_div(n: u32) -> bool {
|
||||||
|
if n < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n % 2 == 0 {
|
||||||
|
return n == 2
|
||||||
|
}
|
||||||
|
for p in [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
|
||||||
|
if n % p == 0 {
|
||||||
|
return n == p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n < 41 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let lim = n.sqrt();
|
||||||
|
let mut i = 41;
|
||||||
|
while i <= lim + 1 {
|
||||||
|
if n % i == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n % (i + 2) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i += 6;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_prime_inner(n: &Int) -> bool {
|
||||||
|
if n.lt_i64(2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let Some(m) = n.to_u32() {
|
||||||
|
trial_div(m)
|
||||||
|
} else {
|
||||||
|
miller_rabin(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pollard_rho(n: &Int, a: Int, b: &Int) -> Option<Int> {
|
||||||
|
let mut x = a.clone();
|
||||||
|
let mut y = a.clone();
|
||||||
|
let mut d = Int::ONE;
|
||||||
|
while d.eq_i64(1) {
|
||||||
|
x = &(&(&x * &x) + b) % n;
|
||||||
|
y = &(&(&y * &y) + b) % n;
|
||||||
|
y = &(&(&y * &y) + b) % n;
|
||||||
|
d = (&x - &y).gcd(n);
|
||||||
|
}
|
||||||
|
if &d == n {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pollard_rho_checks(n: &Int) -> Int {
|
||||||
|
let nminus2 = n - 2;
|
||||||
|
let mut a = Int::from_small(2);
|
||||||
|
while &a < n {
|
||||||
|
let mut b = Int::ONE;
|
||||||
|
while b < nminus2 {
|
||||||
|
if let Some(f) = pollard_rho(n, a.clone(), &b) {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
b += 1;
|
||||||
|
}
|
||||||
|
a += 1;
|
||||||
|
}
|
||||||
|
// we should never get here, hopefully
|
||||||
|
n.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recurse_pollard_rho(n: &Int, factors: &mut Vec<Int>) {
|
||||||
|
if is_prime_inner(n) {
|
||||||
|
factors.push(n.clone());
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let factor = pollard_rho_checks(n);
|
||||||
|
recurse_pollard_rho(&factor, factors);
|
||||||
|
recurse_pollard_rho(&(n / &factor), factors);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn factorize(n: &Int) -> Vec<Int> {
|
||||||
|
let mut n = n.abs();
|
||||||
|
let mut factors = Vec::new();
|
||||||
|
if n.lt_i64(2) {
|
||||||
|
return factors
|
||||||
|
}
|
||||||
|
let tz = n.trailing_zeros().unwrap();
|
||||||
|
n = &n >> &Int::from(tz);
|
||||||
|
for _ in 0..tz {
|
||||||
|
factors.push(Int::from_small(2));
|
||||||
|
}
|
||||||
|
while (&n % 3).is_zero() {
|
||||||
|
n /= 3;
|
||||||
|
factors.push(Int::from_small(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
let nsqrt = n.sqrt();
|
||||||
|
let limit = nsqrt.min(Int::from_small(1 << 15));
|
||||||
|
|
||||||
|
let mut i = Int::from_small(5);
|
||||||
|
while i < limit && n.gt_i64(1) {
|
||||||
|
while (&n % &i).is_zero() {
|
||||||
|
n /= &i;
|
||||||
|
factors.push(i.clone());
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
while (&n % &i).is_zero() {
|
||||||
|
n /= &i;
|
||||||
|
factors.push(i.clone());
|
||||||
|
}
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
if &i * &i <= n {
|
||||||
|
let len = factors.len();
|
||||||
|
recurse_pollard_rho(&n, &mut factors);
|
||||||
|
factors[len..].sort_unstable();
|
||||||
|
} else if n.gt_i64(1) {
|
||||||
|
factors.push(n);
|
||||||
|
}
|
||||||
|
factors
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(vm: &mut Vm) {
|
||||||
|
vm.set_global_name("isprime", isprime().into());
|
||||||
|
vm.set_global_name("factors", factors().into());
|
||||||
|
vm.set_global_name("totient", totient().into());
|
||||||
|
|
||||||
|
vm.set_global_name("powmod", powmod().into());
|
||||||
|
vm.set_global_name("invmod", invmod().into());
|
||||||
|
|
||||||
|
vm.set_global_name("gcd", gcd().into());
|
||||||
|
vm.set_global_name("gcdn", gcdn().into());
|
||||||
|
vm.set_global_name("lcm", lcm().into());
|
||||||
|
vm.set_global_name("lcmn", lcmn().into());
|
||||||
|
vm.set_global_name("isqrt", isqrt().into());
|
||||||
|
vm.set_global_name("iroot", iroot().into());
|
||||||
|
|
||||||
|
vm.set_global_name("jacobi", jacobi().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, n] = unpack_args!(args);
|
||||||
|
let Value::Int(n) = n else {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"isprime expected integer argument, got {n:#}"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Ok(Value::from(is_prime_inner(&n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, n] = unpack_args!(args);
|
||||||
|
let Value::Int(n) = n else {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"factors expected integer argument, got {n:#}"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let factors = factorize(&n);
|
||||||
|
let factors: Vec<Value> = factors.into_iter().map(Value::from).collect();
|
||||||
|
Ok(factors.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, n] = unpack_args!(args);
|
||||||
|
let Value::Int(n) = n else {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"totient expected integer argument, got {n:#}"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if n.is_zero() {
|
||||||
|
return Ok(Value::from(n))
|
||||||
|
}
|
||||||
|
let mut res = Int::ONE;
|
||||||
|
let mut last_factor = Int::ONE;
|
||||||
|
for factor in factorize(&n) {
|
||||||
|
if last_factor != factor {
|
||||||
|
res *= &factor - 1;
|
||||||
|
last_factor = factor
|
||||||
|
} else {
|
||||||
|
res *= factor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(res.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(3)]
|
||||||
|
pub fn powmod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, n, e, m] = unpack_args!(args);
|
||||||
|
let (Value::Int(n), Value::Int(e), Value::Int(m)) = (n, e, m) else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "powmod expected 3 integer arguments")
|
||||||
|
};
|
||||||
|
if m.is_zero() {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "powmod: modulus was zero")
|
||||||
|
}
|
||||||
|
if e.is_negative() {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "powmod: exponent was negative")
|
||||||
|
}
|
||||||
|
Ok(n.modpow(&e, &m).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(2)]
|
||||||
|
pub fn invmod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, n, m] = unpack_args!(args);
|
||||||
|
let (Value::Int(n), Value::Int(m)) = (n, m) else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "invmod expected 2 integer arguments")
|
||||||
|
};
|
||||||
|
if m.is_zero() {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "invmod: modulus was zero")
|
||||||
|
}
|
||||||
|
match n.modinv(&m) {
|
||||||
|
Some(v) => Ok(v.into()),
|
||||||
|
None => Ok(Value::Int(0.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(2)]
|
||||||
|
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, x, y] = unpack_args!(args);
|
||||||
|
let Value::Int(x) = x else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
|
||||||
|
};
|
||||||
|
let Value::Int(y) = y else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
|
||||||
|
};
|
||||||
|
Ok(x.gcd(&y).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, args] = unpack_args!(args);
|
||||||
|
let args = args.to_iter_function()?;
|
||||||
|
|
||||||
|
let mut g = Int::ZERO;
|
||||||
|
|
||||||
|
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
||||||
|
let Value::Int(a) = a else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
|
||||||
|
};
|
||||||
|
g = g.gcd(&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(g.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(2)]
|
||||||
|
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, x, y] = unpack_args!(args);
|
||||||
|
let Value::Int(x) = x else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
|
||||||
|
};
|
||||||
|
let Value::Int(y) = y else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
|
||||||
|
};
|
||||||
|
Ok(x.lcm(&y).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, args] = unpack_args!(args);
|
||||||
|
let args = args.to_iter_function()?;
|
||||||
|
|
||||||
|
let mut l = Int::ONE;
|
||||||
|
|
||||||
|
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
||||||
|
let Value::Int(a) = a else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
|
||||||
|
};
|
||||||
|
l = l.lcm(&a);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::from(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, x] = unpack_args!(args);
|
||||||
|
let Value::Int(x) = x else {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"isqrt expected integer argument, got {x:#}"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if x.is_negative() {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "isqrt: radicand must be non-negative")
|
||||||
|
}
|
||||||
|
Ok(Value::Int(x.sqrt()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(2)]
|
||||||
|
pub fn iroot(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, n, x] = unpack_args!(args);
|
||||||
|
let (Value::Int(n), Value::Int(x)) = (&n, &x) else {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"iroot expected integer arguments, got {n:#} and {x:#}"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if x.is_negative() {
|
||||||
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"isqrt: radicand must be non-negative, got {x}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !n.is_positive() {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "isqrt: degree must be positive, got {n}")
|
||||||
|
}
|
||||||
|
let Some(n) = n.to_u32() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "isqrt: degree {n} is too large")
|
||||||
|
};
|
||||||
|
Ok(Value::Int(x.nth_root(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(2)]
|
||||||
|
pub fn jacobi(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, a, n] = unpack_args!(args);
|
||||||
|
let (mut a, mut n) = match (a, n) {
|
||||||
|
(Value::Int(a), Value::Int(n)) => {
|
||||||
|
(a.to_big().into_owned(), n.to_big().into_owned())
|
||||||
|
}
|
||||||
|
(a, n) => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"jacobi expected integer arguments, got {a:#} and {n:#}",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// (a|0) = 1 if |a| = 1, 0 otherwise
|
||||||
|
if n.is_zero() {
|
||||||
|
if a.to_i32().is_some_and(|v| v == -1 || v == 1) {
|
||||||
|
return Ok(Value::from(1))
|
||||||
|
} else {
|
||||||
|
return Ok(Value::from(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (k|l) = 0 if gcd(k,l) != 1
|
||||||
|
if a.is_even() && n.is_even() {
|
||||||
|
return Ok(Value::from(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = 1i64;
|
||||||
|
// (a|2n) = (a|2)(a|n),
|
||||||
|
// (a|2) = 0 if a is even, 1 if a = 1,7 mod 8, -1 if a = 3,5 mod 8
|
||||||
|
let am8 = ((&a % 8i32).to_i32().unwrap() + 8) % 8;
|
||||||
|
while n.is_even() {
|
||||||
|
n /= 2;
|
||||||
|
if am8 % 2 == 0 {
|
||||||
|
return Ok(Value::from(0))
|
||||||
|
}
|
||||||
|
if am8 == 3 || am8 == 5 {
|
||||||
|
res *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (a|-1) = -1 if a < 0, 1 if a >= 0
|
||||||
|
if n.is_negative() {
|
||||||
|
n *= -1;
|
||||||
|
if a.is_negative() {
|
||||||
|
res *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a = ((a % &n) + &n) % &n;
|
||||||
|
while !a.is_zero() {
|
||||||
|
// (2a|n) = (2|n)(a|n)
|
||||||
|
// (2|n) = 1 if a = 1,7 mod 8, -1 if a = 3,5 mod 8
|
||||||
|
while a.is_even() {
|
||||||
|
a /= 2;
|
||||||
|
let nm8 = ((&n % 8i32).to_i32().unwrap() + 8) % 8;
|
||||||
|
if nm8 == 3 || nm8 == 5 {
|
||||||
|
res *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// (m|n)(n|m) = (-1)^((m-1)/2 (n-1)/2)
|
||||||
|
std::mem::swap(&mut a, &mut n);
|
||||||
|
let am4 = ((&a % 4i32).to_i32().unwrap() + 4) % 4;
|
||||||
|
let nm4 = ((&n % 4i32).to_i32().unwrap() + 4) % 4;
|
||||||
|
if am4 == 3 && nm4 == 3 {
|
||||||
|
res *= -1;
|
||||||
|
}
|
||||||
|
// (a|n) = (a mod n|n)
|
||||||
|
a = ((a % &n) + &n) % &n;
|
||||||
|
}
|
||||||
|
if n.to_u32().is_some_and(|v| v == 1) {
|
||||||
|
return Ok(Value::from(res))
|
||||||
|
} else {
|
||||||
|
return Ok(Value::from(0))
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::{throw, Result},
|
exception::{throw, Result},
|
||||||
lstring::LString,
|
lstring::LString,
|
||||||
|
prelude::*,
|
||||||
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
value::Value,
|
value::Value,
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
|
@ -189,7 +190,14 @@ pub fn arg(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "arg expected integer, got {n:#}")
|
throw!(*SYM_TYPE_ERROR, "arg expected integer, got {n:#}")
|
||||||
};
|
};
|
||||||
let cmd_args = vm.args();
|
let cmd_args = vm.args();
|
||||||
if n < 0 || (n as usize) >= cmd_args.len() {
|
let Some(n) = n.to_usize() else {
|
||||||
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"arg number must be small positive integer, got {}",
|
||||||
|
n,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if n >= cmd_args.len() {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"arg number {} out of range for {} arguments",
|
"arg number {} out of range for {} arguments",
|
||||||
|
@ -197,7 +205,7 @@ pub fn arg(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
cmd_args.len()
|
cmd_args.len()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Ok(Value::from(cmd_args[n as usize].clone()))
|
Ok(Value::from(cmd_args[n].clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(0)]
|
#[native_func(0)]
|
||||||
|
|
|
@ -2,9 +2,10 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::Result,
|
exception::Result,
|
||||||
|
prelude::*,
|
||||||
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value},
|
value::{function::NativeFunc, HashValue, Value},
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
vmcall, vmcalliter,
|
vmcall, vmcalliter,
|
||||||
};
|
};
|
||||||
|
@ -186,7 +187,7 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(count) = count else {
|
let Value::Int(count) = count else {
|
||||||
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
|
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
|
||||||
};
|
};
|
||||||
let Ok(count) = count.try_into() else {
|
let Some(count) = count.to_usize() else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"take expected nonnegative integer, got {count:#}"
|
"take expected nonnegative integer, got {count:#}"
|
||||||
|
@ -215,10 +216,10 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(count) = count else {
|
let Value::Int(count) = count else {
|
||||||
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
|
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
|
||||||
};
|
};
|
||||||
let Ok(count) = count.try_into() else {
|
let Some(count) = count.to_usize() else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"count expected nonnegative integer, got {count:#}"
|
"count expected small nonnegative integer, got {count:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
@ -305,12 +306,12 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(by) = by else {
|
let Value::Int(by) = by else {
|
||||||
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
|
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
|
||||||
};
|
};
|
||||||
if by <= 0 {
|
let Some(by) = by.to_usize() else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"step expected positive integer, got {by:#}"
|
"step expected small positive integer, got {by:#}"
|
||||||
)
|
)
|
||||||
}
|
};
|
||||||
|
|
||||||
let state = RefCell::new(Step::First);
|
let state = RefCell::new(Step::First);
|
||||||
let f = move |vm: &mut Vm, _| match state.take() {
|
let f = move |vm: &mut Vm, _| match state.take() {
|
||||||
|
@ -322,7 +323,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(Value::iter_pack(res))
|
Ok(Value::iter_pack(res))
|
||||||
}
|
}
|
||||||
Step::Going => {
|
Step::Going => {
|
||||||
for _ in 0..(by - 1) {
|
for _ in 1..by {
|
||||||
if vmcall!(vm; iter.clone())? == Value::Nil {
|
if vmcall!(vm; iter.clone())? == Value::Nil {
|
||||||
return Ok(Value::iter_pack(None))
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
|
@ -648,8 +649,10 @@ pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Value::String(s) => return Ok((s.chars().count() as i64).into()),
|
Value::String(s) => return Ok((s.chars().count() as i64).into()),
|
||||||
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
|
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
|
||||||
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
|
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
|
||||||
Value::Range(r) if r.ty != RangeType::Endless => {
|
Value::Range(ref r) => {
|
||||||
return Ok((r.len().unwrap() as i64).into())
|
if let Some(len) = r.len() {
|
||||||
|
return Ok(len.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -692,7 +695,7 @@ pub fn sum(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let mut result = Value::Int(0);
|
let mut result = Value::from(0);
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
result = (result + value)?;
|
result = (result + value)?;
|
||||||
}
|
}
|
||||||
|
@ -704,7 +707,7 @@ pub fn prod(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let mut result = Value::Int(1);
|
let mut result = Value::from(1);
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
result = (result * value)?;
|
result = (result * value)?;
|
||||||
}
|
}
|
||||||
|
@ -757,6 +760,10 @@ pub fn nth(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "nth expected integer")
|
throw!(*SYM_TYPE_ERROR, "nth expected integer")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let Some(n) = n.to_usize() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "nth expected small positive integer")
|
||||||
|
};
|
||||||
|
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
if vmcalliter!(vm; iter.clone())?.is_none() {
|
if vmcalliter!(vm; iter.clone())?.is_none() {
|
||||||
return Ok(Value::Nil)
|
return Ok(Value::Nil)
|
||||||
|
@ -849,8 +856,8 @@ pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||||
let hv = v.try_into()?;
|
let hv = v.try_into()?;
|
||||||
map.entry(hv)
|
map.entry(hv)
|
||||||
.and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap())
|
.and_modify(|v: &mut Value| *v = (v.clone() + Value::from(1)).unwrap())
|
||||||
.or_insert(Value::Int(1));
|
.or_insert(Value::from(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(map.into())
|
Ok(map.into())
|
||||||
|
@ -862,7 +869,7 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let mut sum = Value::Float(0.0);
|
let mut sum = Value::Float(0.0);
|
||||||
let mut count = Value::Int(0);
|
let mut count = Value::from(0);
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
sum = (sum + value)?;
|
sum = (sum + value)?;
|
||||||
count = (count + Value::from(1))?;
|
count = (count + Value::from(1))?;
|
||||||
|
@ -876,11 +883,11 @@ fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
|
||||||
let mut k = 1;
|
let mut k = 1;
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
let old_m = m.clone();
|
let old_m = m.clone();
|
||||||
m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
|
m = (m.clone() + ((value.clone() - m.clone())? / Value::from(k))?)?;
|
||||||
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
|
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
|
||||||
k += 1;
|
k += 1;
|
||||||
}
|
}
|
||||||
s / Value::Int(k - if pop { 1 } else { 2 })
|
s / Value::from(k - if pop { 1 } else { 2 })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -898,9 +905,9 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
let v = variance_inner(vm, iter, false)?;
|
let v = variance_inner(vm, iter, false)?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
||||||
Value::Float(f) => Value::Float(f.sqrt()),
|
Value::Float(f) => Value::Float(f.sqrt()),
|
||||||
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
Value::Ratio(r) => Value::Float(r.to_f64_uc().sqrt()),
|
||||||
Value::Complex(c) => Value::Complex(c.sqrt()),
|
Value::Complex(c) => Value::Complex(c.sqrt()),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
||||||
})
|
})
|
||||||
|
@ -921,9 +928,9 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
let v = variance_inner(vm, iter, true)?;
|
let v = variance_inner(vm, iter, true)?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
Value::Int(n) => Value::Float(n.to_f64_uc().sqrt()),
|
||||||
Value::Float(f) => Value::Float(f.sqrt()),
|
Value::Float(f) => Value::Float(f.sqrt()),
|
||||||
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
Value::Ratio(r) => Value::Float(r.to_f64_uc().sqrt()),
|
||||||
Value::Complex(c) => Value::Complex(c.sqrt()),
|
Value::Complex(c) => Value::Complex(c.sqrt()),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
||||||
})
|
})
|
||||||
|
@ -957,10 +964,10 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let (_, _, _) = hi.select_nth_unstable(0);
|
let (_, _, _) = hi.select_nth_unstable(0);
|
||||||
let m2 = vals.swap_remove(count / 2);
|
let m2 = vals.swap_remove(count / 2);
|
||||||
let m1 = vals.swap_remove(count / 2 - 1);
|
let m1 = vals.swap_remove(count / 2 - 1);
|
||||||
(m1.0 + m2.0)? / Value::Int(2)
|
(m1.0 + m2.0)? / Value::from(2)
|
||||||
} else {
|
} else {
|
||||||
let (_, _, _) = vals.select_nth_unstable(count / 2);
|
let (_, _, _) = vals.select_nth_unstable(count / 2);
|
||||||
let m = vals.swap_remove(count / 2);
|
let m = vals.swap_remove(count / 2);
|
||||||
m.0 / Value::Int(1)
|
m.0 / Value::from(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,10 @@ pub mod collection;
|
||||||
pub mod exception;
|
pub mod exception;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
pub mod ints;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod num;
|
pub mod math;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
|
@ -27,7 +28,8 @@ pub fn load_all(vm: &mut Vm) {
|
||||||
format::load(vm);
|
format::load(vm);
|
||||||
io::load(vm);
|
io::load(vm);
|
||||||
iter::load(vm);
|
iter::load(vm);
|
||||||
num::load(vm);
|
math::load(vm);
|
||||||
|
ints::load(vm);
|
||||||
string::load(vm);
|
string::load(vm);
|
||||||
value::load(vm);
|
value::load(vm);
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::Result,
|
exception::Result,
|
||||||
parser::{parse_int, to_lstring_radix},
|
number::{Complex, Int, Ratio},
|
||||||
|
prelude::*,
|
||||||
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
value::{ops::RatioExt, Complex64, Value},
|
value::Value,
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
vmcalliter,
|
|
||||||
};
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
|
@ -25,8 +23,8 @@ lazy_static! {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_floaty(v: Value) -> Value {
|
fn to_floaty(v: Value) -> Value {
|
||||||
match v {
|
match v {
|
||||||
Value::Int(v) => Value::Float(v as f64),
|
Value::Int(v) => Value::Float(v.to_f64_uc()),
|
||||||
Value::Ratio(v) => Value::Float(v.to_f64()),
|
Value::Ratio(v) => Value::Float(v.to_f64_uc()),
|
||||||
Value::Float(v) => Value::Float(v),
|
Value::Float(v) => Value::Float(v),
|
||||||
Value::Complex(v) => Value::Complex(v),
|
Value::Complex(v) => Value::Complex(v),
|
||||||
v => v,
|
v => v,
|
||||||
|
@ -36,8 +34,8 @@ fn to_floaty(v: Value) -> Value {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_complex(v: Value) -> Value {
|
fn to_complex(v: Value) -> Value {
|
||||||
match v {
|
match v {
|
||||||
Value::Int(v) => Value::Complex((v as f64).into()),
|
Value::Int(v) => Value::Complex(v.to_f64_uc().into()),
|
||||||
Value::Ratio(v) => Value::Complex(v.to_f64().into()),
|
Value::Ratio(v) => Value::Complex(v.to_f64_uc().into()),
|
||||||
Value::Float(v) => Value::Complex(v.into()),
|
Value::Float(v) => Value::Complex(v.into()),
|
||||||
Value::Complex(v) => Value::Complex(v),
|
Value::Complex(v) => Value::Complex(v),
|
||||||
v => v,
|
v => v,
|
||||||
|
@ -57,8 +55,8 @@ pub fn load(vm: &mut Vm) {
|
||||||
|
|
||||||
vm.set_global_name("inf", (f64::INFINITY).into());
|
vm.set_global_name("inf", (f64::INFINITY).into());
|
||||||
vm.set_global_name("NaN", (f64::NAN).into());
|
vm.set_global_name("NaN", (f64::NAN).into());
|
||||||
vm.set_global_name("infi", Complex64::new(0.0, f64::INFINITY).into());
|
vm.set_global_name("infi", Complex::new(0.0, f64::INFINITY).into());
|
||||||
vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into());
|
vm.set_global_name("NaNi", Complex::new(0.0, f64::NAN).into());
|
||||||
|
|
||||||
vm.set_global_name("bin", bin().into());
|
vm.set_global_name("bin", bin().into());
|
||||||
vm.set_global_name("sex", sex().into());
|
vm.set_global_name("sex", sex().into());
|
||||||
|
@ -68,15 +66,6 @@ pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("to_radix_upper", to_radix_upper().into());
|
vm.set_global_name("to_radix_upper", to_radix_upper().into());
|
||||||
vm.set_global_name("from_radix", from_radix().into());
|
vm.set_global_name("from_radix", from_radix().into());
|
||||||
|
|
||||||
vm.set_global_name("gcd", gcd().into());
|
|
||||||
vm.set_global_name("gcdn", gcdn().into());
|
|
||||||
vm.set_global_name("lcm", lcm().into());
|
|
||||||
vm.set_global_name("lcmn", lcmn().into());
|
|
||||||
vm.set_global_name("isqrt", isqrt().into());
|
|
||||||
vm.set_global_name("isprime", isprime().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());
|
||||||
vm.set_global_name("floor", floor().into());
|
vm.set_global_name("floor", floor().into());
|
||||||
|
@ -137,7 +126,7 @@ pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_lstring_radix(x, 2, false).into())
|
Ok(Value::from(x.to_str_radix_case(2, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -146,7 +135,7 @@ pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_lstring_radix(x, 6, false).into())
|
Ok(Value::from(x.to_str_radix_case(6, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -155,7 +144,7 @@ pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_lstring_radix(x, 8, false).into())
|
Ok(Value::from(x.to_str_radix_case(8, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -164,7 +153,7 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_lstring_radix(x, 16, false).into())
|
Ok(Value::from(x.to_str_radix_case(16, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -176,10 +165,13 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
"to_radix expected integer arguments, got {x:#} and {radix:#}"
|
"to_radix expected integer arguments, got {x:#} and {radix:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if *radix < 2 || *radix > 36 {
|
let Some(radix) = radix.to_u32() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
|
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 2..=36")
|
||||||
|
};
|
||||||
|
if !(2..=36).contains(&radix) {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 2..=36")
|
||||||
}
|
}
|
||||||
Ok(to_lstring_radix(*x, *radix as u32, false).into())
|
Ok(Value::from(x.to_str_radix_case(radix, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -191,13 +183,19 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
|
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if *radix < 2 || *radix > 36 {
|
let Some(radix) = radix.to_u32() else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"to_radix_upper expected radix in range 0..=36"
|
"to_radix_upper expected radix in range 2..=36"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if !(2..=36).contains(&radix) {
|
||||||
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"to_radix_upper expected radix in range 2..=36"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Ok(to_lstring_radix(*x, *radix as u32, true).into())
|
Ok(Value::from(x.to_str_radix_case(radix, true)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -209,285 +207,32 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
|
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if *radix < 2 || *radix > 36 {
|
let Some(radix) = radix.to_u32() else {
|
||||||
throw!(
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"from_radix expected radix in range 0..=36"
|
"from_radix expected radix in range 2..=36"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if !(2..=36).contains(&radix) {
|
||||||
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"from_radix expected radix in range 2..=36"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
match parse_int(s.as_ref(), *radix as u32) {
|
let Ok(s) = s.to_str() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "from_radix: string has invalid UTF-8",)
|
||||||
|
};
|
||||||
|
match Int::from_str_radix(s, radix) {
|
||||||
Ok(v) => Ok(v.into()),
|
Ok(v) => Ok(v.into()),
|
||||||
Err(_) => throw!(
|
Err(_) => throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"string was not a valid integer in given radix"
|
"from_radix: string {:#} is not a valid integer in radix {}",
|
||||||
|
s,
|
||||||
|
radix
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// integer operations
|
|
||||||
//
|
|
||||||
|
|
||||||
fn isqrt_inner(mut n: i64) -> i64 {
|
|
||||||
assert!(n >= 0, "isqrt input should be nonnegative");
|
|
||||||
if n < 2 {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut c = 0;
|
|
||||||
let mut d = 1 << 62;
|
|
||||||
|
|
||||||
while d > n {
|
|
||||||
d >>= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
while d != 0 {
|
|
||||||
if n >= c + d {
|
|
||||||
n -= c + d;
|
|
||||||
c = (c >> 1) + d;
|
|
||||||
} else {
|
|
||||||
c >>= 1;
|
|
||||||
}
|
|
||||||
d >>= 2;
|
|
||||||
}
|
|
||||||
c
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gcd_inner(a: i64, b: i64) -> i64 {
|
|
||||||
let (mut a, mut b) = (a.abs(), b.abs());
|
|
||||||
if a == 0 {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
if b == 0 {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
let az = a.trailing_zeros();
|
|
||||||
a >>= az;
|
|
||||||
let bz = b.trailing_zeros();
|
|
||||||
b >>= bz;
|
|
||||||
let z = az.min(bz);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if a > b {
|
|
||||||
std::mem::swap(&mut a, &mut b);
|
|
||||||
}
|
|
||||||
b -= a;
|
|
||||||
if b == 0 {
|
|
||||||
return a << z
|
|
||||||
}
|
|
||||||
b >>= b.trailing_zeros();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[native_func(1)]
|
|
||||||
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, x] = unpack_args!(args);
|
|
||||||
let Value::Int(x) = x else {
|
|
||||||
throw!(
|
|
||||||
*SYM_TYPE_ERROR,
|
|
||||||
"isqrt expected integer argument, got {x:#}"
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if x < 0 {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
|
|
||||||
}
|
|
||||||
Ok(isqrt_inner(x).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[native_func(1)]
|
|
||||||
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, x] = unpack_args!(args);
|
|
||||||
let Value::Int(x) = x else {
|
|
||||||
throw!(
|
|
||||||
*SYM_TYPE_ERROR,
|
|
||||||
"isprime expected integer argument, got {x:#}"
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if x < 2 {
|
|
||||||
return Ok(false.into())
|
|
||||||
}
|
|
||||||
for p in [2, 3, 5, 7] {
|
|
||||||
if x % p == 0 {
|
|
||||||
return Ok((x == p).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if x < 11 {
|
|
||||||
return Ok(false.into())
|
|
||||||
}
|
|
||||||
let lim = isqrt_inner(x);
|
|
||||||
let mut i = 12;
|
|
||||||
while i <= lim + 1 {
|
|
||||||
if x % (i - 1) == 0 {
|
|
||||||
return Ok(false.into())
|
|
||||||
}
|
|
||||||
if x % (i + 1) == 0 {
|
|
||||||
return Ok(false.into())
|
|
||||||
}
|
|
||||||
i += 6;
|
|
||||||
}
|
|
||||||
Ok(true.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[native_func(2)]
|
|
||||||
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, x, y] = unpack_args!(args);
|
|
||||||
let Value::Int(x) = x else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
|
|
||||||
};
|
|
||||||
let Value::Int(y) = y else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
|
|
||||||
};
|
|
||||||
Ok(gcd_inner(x, y).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[native_func(1)]
|
|
||||||
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, args] = unpack_args!(args);
|
|
||||||
let args = args.to_iter_function()?;
|
|
||||||
|
|
||||||
let mut g = 0;
|
|
||||||
|
|
||||||
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
|
||||||
let Value::Int(a) = a else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
|
|
||||||
};
|
|
||||||
g = gcd_inner(g, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(g.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[native_func(2)]
|
|
||||||
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, x, y] = unpack_args!(args);
|
|
||||||
let Value::Int(x) = x else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
|
|
||||||
};
|
|
||||||
let Value::Int(y) = y else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
|
|
||||||
};
|
|
||||||
let g = gcd_inner(x, y);
|
|
||||||
if g == 0 {
|
|
||||||
Ok(Value::from(0))
|
|
||||||
} else {
|
|
||||||
(Value::from(x) / Value::from(g))? * Value::from(y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[native_func(1)]
|
|
||||||
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, args] = unpack_args!(args);
|
|
||||||
let args = args.to_iter_function()?;
|
|
||||||
|
|
||||||
let mut l = 1;
|
|
||||||
|
|
||||||
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
|
||||||
let Value::Int(a) = a else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
|
|
||||||
};
|
|
||||||
let g = gcd_inner(l, a);
|
|
||||||
if g == 0 {
|
|
||||||
return Ok(Value::from(0))
|
|
||||||
};
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[native_func(1)]
|
|
||||||
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, x] = unpack_args!(args);
|
|
||||||
let Value::Int(mut x) = x else {
|
|
||||||
throw!(
|
|
||||||
*SYM_TYPE_ERROR,
|
|
||||||
"factors expected integer argument, got {x:#}"
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let mut factors = Vec::new();
|
|
||||||
if x <= 1 {
|
|
||||||
return Ok(factors.into())
|
|
||||||
}
|
|
||||||
while x & 1 == 0 {
|
|
||||||
x >>= 1;
|
|
||||||
factors.push(Value::Int(2));
|
|
||||||
}
|
|
||||||
while x % 3 == 0 {
|
|
||||||
x /= 3;
|
|
||||||
factors.push(Value::Int(3));
|
|
||||||
}
|
|
||||||
let mut i = 5;
|
|
||||||
while x >= i * i {
|
|
||||||
while x % i == 0 {
|
|
||||||
x /= i;
|
|
||||||
factors.push(Value::Int(i));
|
|
||||||
}
|
|
||||||
i += 2;
|
|
||||||
while x % i == 0 {
|
|
||||||
x /= i;
|
|
||||||
factors.push(Value::Int(i));
|
|
||||||
}
|
|
||||||
i += 4;
|
|
||||||
}
|
|
||||||
if x > 1 {
|
|
||||||
factors.push(Value::Int(x));
|
|
||||||
}
|
|
||||||
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
|
||||||
//
|
//
|
||||||
|
@ -518,7 +263,7 @@ pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match x {
|
match x {
|
||||||
Value::Int(x) => Ok(Value::Int(x)),
|
Value::Int(x) => Ok(Value::Int(x)),
|
||||||
Value::Float(x) => Ok(Value::Float(x.floor())),
|
Value::Float(x) => Ok(Value::Float(x.floor())),
|
||||||
Value::Ratio(x) => Ok(Value::Ratio(x.floor())),
|
Value::Ratio(x) => Ok(Value::Ratio(x.floor().into())),
|
||||||
x => throw!(*SYM_TYPE_ERROR, "floor expected real argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "floor expected real argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -529,7 +274,7 @@ pub fn ceil(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match x {
|
match x {
|
||||||
Value::Int(x) => Ok(Value::Int(x)),
|
Value::Int(x) => Ok(Value::Int(x)),
|
||||||
Value::Float(x) => Ok(Value::Float(x.ceil())),
|
Value::Float(x) => Ok(Value::Float(x.ceil())),
|
||||||
Value::Ratio(x) => Ok(Value::Ratio(x.ceil())),
|
Value::Ratio(x) => Ok(Value::Ratio(x.ceil().into())),
|
||||||
x => throw!(*SYM_TYPE_ERROR, "ceil expected real argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "ceil expected real argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,7 +285,7 @@ pub fn round(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match x {
|
match x {
|
||||||
Value::Int(x) => Ok(Value::Int(x)),
|
Value::Int(x) => Ok(Value::Int(x)),
|
||||||
Value::Float(x) => Ok(Value::Float(x.round())),
|
Value::Float(x) => Ok(Value::Float(x.round())),
|
||||||
Value::Ratio(x) => Ok(Value::Ratio(x.round())),
|
Value::Ratio(x) => Ok(Value::Ratio(x.round().into())),
|
||||||
x => throw!(*SYM_TYPE_ERROR, "round expected real argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "round expected real argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,7 +296,7 @@ pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match x {
|
match x {
|
||||||
Value::Int(x) => Ok(Value::Int(x)),
|
Value::Int(x) => Ok(Value::Int(x)),
|
||||||
Value::Float(x) => Ok(Value::Float(x.trunc())),
|
Value::Float(x) => Ok(Value::Float(x.trunc())),
|
||||||
Value::Ratio(x) => Ok(Value::Ratio(x.trunc())),
|
Value::Ratio(x) => Ok(Value::Ratio(x.trunc().into())),
|
||||||
x => throw!(*SYM_TYPE_ERROR, "trunc expected real argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "trunc expected real argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -560,9 +305,9 @@ pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn fract(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn fract(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
match x {
|
match x {
|
||||||
Value::Int(_) => Ok(Value::Int(0)),
|
Value::Int(_) => Ok(Value::from(0)),
|
||||||
Value::Float(x) => Ok(Value::Float(x.fract())),
|
Value::Float(x) => Ok(Value::Float(x.fract())),
|
||||||
Value::Ratio(x) => Ok(Value::Ratio(x.fract())),
|
Value::Ratio(x) => Ok(Value::Ratio(x.fract().into())),
|
||||||
x => throw!(*SYM_TYPE_ERROR, "fract expected real argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "fract expected real argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -572,12 +317,8 @@ pub fn sign(_: &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.signum())),
|
Value::Int(x) => Ok(Value::Int(x.signum())),
|
||||||
Value::Float(x) => Ok(Value::Float(if x == 0.0 { 0.0 } else { x.signum() })),
|
Value::Float(x) => Ok(Value::Float(if x == 0.0 { x } else { x.signum() })),
|
||||||
Value::Ratio(x) => match x.cmp(&0.into()) {
|
Value::Ratio(x) => Ok(Value::Ratio(x.signum().into())),
|
||||||
Ordering::Greater => Ok(Value::Ratio(1.into())),
|
|
||||||
Ordering::Less => Ok(Value::Ratio((-1).into())),
|
|
||||||
Ordering::Equal => Ok(Value::Ratio(0.into())),
|
|
||||||
},
|
|
||||||
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,7 +407,7 @@ pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
"float_to_bits expected float argument, got {val:#}"
|
"float_to_bits expected float argument, got {val:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Ok(Value::Int(f.to_bits() as i64))
|
Ok(Value::Int(Int::from_u64(f.to_bits()).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -678,7 +419,13 @@ pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
"float_of_bits expected integer argument, got {val:#}"
|
"float_of_bits expected integer argument, got {val:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Ok(Value::Float(f64::from_bits(i as u64)))
|
let Some(v) = i.to_u64() else {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"float_of_bits expected integer in 0..2^64, got {i}"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Ok(Value::Float(f64::from_bits(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -690,7 +437,7 @@ pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, v] = unpack_args!(args);
|
let [_, v] = unpack_args!(args);
|
||||||
match v {
|
match v {
|
||||||
Value::Int(x) => Ok(Value::Int(x)),
|
Value::Int(x) => Ok(Value::Int(x)),
|
||||||
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
|
Value::Ratio(x) => Ok(Value::from(Int::from(x.numer()))),
|
||||||
v => throw!(
|
v => throw!(
|
||||||
*SYM_TYPE_ERROR,
|
*SYM_TYPE_ERROR,
|
||||||
"numer expected rational argument, got {v:#}"
|
"numer expected rational argument, got {v:#}"
|
||||||
|
@ -702,8 +449,8 @@ pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, v] = unpack_args!(args);
|
let [_, v] = unpack_args!(args);
|
||||||
match v {
|
match v {
|
||||||
Value::Int(_) => Ok(Value::Int(1)),
|
Value::Int(_) => Ok(Value::from(1)),
|
||||||
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
|
Value::Ratio(x) => Ok(Value::from(Int::from(x.denom()))),
|
||||||
v => throw!(
|
v => throw!(
|
||||||
*SYM_TYPE_ERROR,
|
*SYM_TYPE_ERROR,
|
||||||
"denom expected rational argument, got {v:#}"
|
"denom expected rational argument, got {v:#}"
|
||||||
|
@ -731,8 +478,8 @@ pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn im(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn im(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, v] = unpack_args!(args);
|
let [_, v] = unpack_args!(args);
|
||||||
match to_complex(v) {
|
match to_complex(v) {
|
||||||
Value::Int(_) => Ok(Value::Int(0)),
|
Value::Int(_) => Ok(Value::from(0)),
|
||||||
Value::Ratio(_) => Ok(Value::Ratio(0.into())),
|
Value::Ratio(_) => Ok(Value::Ratio(Ratio::zero().into())),
|
||||||
Value::Float(_) => Ok(Value::Float(0.0)),
|
Value::Float(_) => Ok(Value::Float(0.0)),
|
||||||
Value::Complex(z) => Ok(Value::Float(z.im)),
|
Value::Complex(z) => Ok(Value::Float(z.im)),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),
|
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),
|
|
@ -1,9 +1,11 @@
|
||||||
|
use num_bigint::RandBigInt;
|
||||||
use rand::{seq::SliceRandom, Rng};
|
use rand::{seq::SliceRandom, Rng};
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::Result,
|
exception::Result,
|
||||||
|
number::Int,
|
||||||
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
value::{range::RangeType, Value},
|
value::Value,
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
vmcalliter,
|
vmcalliter,
|
||||||
};
|
};
|
||||||
|
@ -46,14 +48,13 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if r.is_empty() {
|
if r.is_empty() {
|
||||||
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
|
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
|
||||||
}
|
}
|
||||||
match r.ty {
|
let (Some(s), Some(e)) = (&r.start, &r.end) else {
|
||||||
RangeType::Open => {
|
throw!(*SYM_VALUE_ERROR, "rand_in: infinite range")
|
||||||
Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop)))
|
};
|
||||||
}
|
if r.inclusive {
|
||||||
RangeType::Closed => {
|
Ok(Int::from(rand::thread_rng().gen_bigint_range(s, &(e + 1))).into())
|
||||||
Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop)))
|
} else {
|
||||||
}
|
Ok(Int::from(rand::thread_rng().gen_bigint_range(s, e)).into())
|
||||||
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
col => {
|
col => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::Result,
|
exception::Result,
|
||||||
lstring::LString,
|
lstring::LString,
|
||||||
|
prelude::*,
|
||||||
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
value::Value,
|
value::Value,
|
||||||
|
@ -40,7 +41,7 @@ pub fn ord(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if chars.next().is_some() {
|
if chars.next().is_some() {
|
||||||
throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
|
throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
|
||||||
};
|
};
|
||||||
Ok(Value::Int(c as u32 as i64))
|
Ok(Value::from(c as u32 as i64))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -49,7 +50,7 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(i) = i else {
|
let Value::Int(i) = i else {
|
||||||
throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}")
|
throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}")
|
||||||
};
|
};
|
||||||
let Ok(i) = u32::try_from(i) else {
|
let Some(i) = i.to_u32() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
|
throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
|
||||||
};
|
};
|
||||||
let Some(c) = char::from_u32(i) else {
|
let Some(c) = char::from_u32(i) else {
|
||||||
|
@ -67,7 +68,7 @@ pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
"len_bytes expected string argument, got {s:#}"
|
"len_bytes expected string argument, got {s:#}"
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
Ok(Value::Int(s.len() as i64))
|
Ok(Value::from(s.len() as i64))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -205,12 +206,20 @@ pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let bytes: Vec<u8> = b
|
let bytes: Vec<u8> = b
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| match v {
|
.map(|v| {
|
||||||
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
|
let Value::Int(i) = v else {
|
||||||
_ => throw!(
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"str_of_bytes expected list of integers in 0..=255"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let Some(n) = i.to_u8() else {
|
||||||
|
throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"str_of_bytes expected list of integers in 0..=255"
|
"str_of_bytes expected list of integers in 0..=255"
|
||||||
),
|
)
|
||||||
|
};
|
||||||
|
Ok(n)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<u8>>>()?;
|
.collect::<Result<Vec<u8>>>()?;
|
||||||
Ok(LString::from(bytes).into())
|
Ok(LString::from(bytes).into())
|
||||||
|
|
|
@ -3,10 +3,12 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
use talc_lang::{
|
use talc_lang::{
|
||||||
exception::{exception, Result},
|
exception::{exception, Result},
|
||||||
lformat,
|
lformat,
|
||||||
|
number::{Int, Ratio},
|
||||||
parser::{parse_float, parse_int},
|
parser::{parse_float, parse_int},
|
||||||
|
prelude::*,
|
||||||
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
throw,
|
throw,
|
||||||
value::{ops::RatioExt, HashValue, Rational64, Value},
|
value::{HashValue, Value},
|
||||||
vm::Vm,
|
vm::Vm,
|
||||||
};
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
@ -71,24 +73,22 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
(v, b"string") => Ok(Value::String(lformat!("{v}").into())),
|
(v, b"string") => Ok(Value::String(lformat!("{v}").into())),
|
||||||
(v, b"bool") => Ok(Value::Bool(v.truthy())),
|
(v, b"bool") => Ok(Value::Bool(v.truthy())),
|
||||||
|
|
||||||
(Value::Symbol(s), b"int") => Ok(Value::Int(s.id() as i64)),
|
(Value::Symbol(s), b"int") => Ok(Value::from(s.id() as i64)),
|
||||||
|
|
||||||
(Value::Int(x), b"ratio") => Ok(Value::Ratio(x.into())),
|
(Value::Int(x), b"ratio") => Ok(Value::from(Ratio::from_integer(x.into()))),
|
||||||
(Value::Int(x), b"float") => Ok(Value::Float(x as f64)),
|
(Value::Int(x), b"float") => Ok(Value::Float(x.to_f64_uc())),
|
||||||
(Value::Int(x), b"complex") => Ok(Value::Complex((x as f64).into())),
|
(Value::Int(x), b"complex") => Ok(Value::Complex(x.to_f64_uc().into())),
|
||||||
(Value::Ratio(x), b"int") => Ok(Value::Int(x.to_integer())),
|
(Value::Ratio(x), b"int") => Ok(Value::from(x.to_integer())),
|
||||||
(Value::Ratio(x), b"float") => Ok(Value::Float(x.to_f64())),
|
(Value::Ratio(x), b"float") => Ok(Value::Float(x.to_f64_uc())),
|
||||||
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
|
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64_uc().into())),
|
||||||
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
|
(Value::Float(x), b"int") => Ok(Value::from(Int::from_f64(x).unwrap_or_default())),
|
||||||
(Value::Float(x), b"ratio") => {
|
(Value::Float(x), b"ratio") => match Ratio::approximate_float(x) {
|
||||||
let r = Rational64::approximate_float(x).ok_or_else(|| {
|
Some(v) => Ok(Value::from(v)),
|
||||||
exception!(
|
None => throw!(
|
||||||
*SYM_VALUE_ERROR,
|
*SYM_VALUE_ERROR,
|
||||||
"float {x:?} could not be converted to ratio"
|
"float {x} could not be converted to ratio"
|
||||||
)
|
),
|
||||||
})?;
|
},
|
||||||
Ok(Value::Ratio(r))
|
|
||||||
}
|
|
||||||
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
|
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
|
||||||
|
|
||||||
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)
|
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)
|
||||||
|
|
Loading…
Reference in a new issue