use std::io::{Write, BufRead};
use std::time::{SystemTime, Duration, Instant};
use std::thread as th;
use std::collections::HashMap;
use rug::{
Integer, integer::Order, Complete,
Float, float::{Round, Constant, Special},
ops::Pow, rand::RandState
};
use rand::{RngCore, rngs::OsRng};
use phf::phf_map;
use regex::{Regex, RegexBuilder};
macro_rules! nope {
() => {unsafe{::std::hint::unreachable_unchecked();}};
}
#[derive(Clone)]
pub enum Obj {
Num(Float),
Str(String)
}
use Obj::*;
impl Default for Obj {
#[inline(always)] fn default() -> Self {
Str(String::new())
}
}
#[derive(Clone, Default)]
pub struct RegObj {
pub o: Obj, pub a: Vec<Obj> }
#[derive(Default)]
pub struct Register {
pub v: Vec<RegObj>,
pub th: Option<th::JoinHandle<Vec<Obj>>>
}
const NIL: u8 = 0x00;
const NIL_R: u8 = 0x80;
const AX: u8 = 0x01;
const AX_R: u8 = 0x81;
const AN: u8 = 0x11;
const AN_R: u8 = 0x91;
const AS: u8 = 0x21;
const AS_R: u8 = 0xa1;
const AX_BX: u8 = 0x02;
const AX_BX_R: u8 = 0x82;
const AX_BN: u8 = 0x12;
const AX_BN_R: u8 = 0x92;
const AN_BN: u8 = 0x22;
const AS_BN: u8 = 0x32;
const AX_BX_CX: u8 = 0x03;
#[inline(always)] const fn plural(x: u8) -> &'static str {
if x & 0xf == 1 {""} else {"s"}
}
#[inline(always)] const fn correct(x: u8) -> &'static str {
match x & 0x7f { AN => "must be a number",
AS => "must be a string",
AX_BX => "must be two numbers or two strings",
AX_BN => "2nd must be a number",
AN_BN => "must be two numbers",
AS_BN => "1st must be a string, 2nd must be a number",
AX_BX_CX => "must be three numbers or three strings",
_ => nope!()
}
}
const CMD_SIGS: phf::Map<char, u8> = phf_map! {
'F' => NIL_R,
'l' => NIL_R,
'L' => NIL_R,
'b' => NIL_R,
'B' => NIL_R,
'Z' => NIL_R,
'+' => AX_BX,
'^' => AX_BX,
'<' => AX_BX_R,
'=' => AX_BX_R,
'>' => AX_BX_R,
'|' => AX_BX_CX,
'-' => AX_BN,
'*' => AX_BN,
'/' => AX_BN,
'%' => AX_BN,
'~' => AX_BN,
':' => AX_BN_R,
'm' => AS_R,
'&' => AS,
'$' => AS,
'\\' => AS,
'n' => AX,
'P' => AX,
'a' => AX,
'A' => AX,
'"' => AX,
'x' => AX,
'g' => AX,
's' => AX_R,
'S' => AX_R,
',' => AX,
'y' => AX_R,
'X' => AS_BN,
'v' => AN,
'T' => AN,
'N' => AN,
'C' => AN,
'D' => AN,
'R' => AN,
'k' => AN,
'i' => AN,
'o' => AN,
'w' => AN,
';' => AN_R,
'Q' => AN,
'M' => AN_R,
'V' => AN_BN,
'G' => AN_BN,
't' => AN_BN,
};
#[derive(Clone)]
#[repr(transparent)]
pub struct ParamStk(pub Vec<(Integer, Integer, Integer)>);
impl ParamStk {
#[inline(always)] fn create(&mut self) {
self.0.push((Integer::from(0_u8), Integer::from(10_u8), Integer::from(10_u8)));
}
#[inline(always)] fn destroy(&mut self) {
self.0.pop();
if self.0.is_empty() {self.create()}
}
#[inline(always)] fn set_k(&mut self, n: Integer) -> Result<(), &'static str> {
if n>=0 {
self.0.last_mut().unwrap().0 = n;
Ok(())
}
else {Err("Output precision must be at least 0")}
}
#[inline(always)] fn set_i(&mut self, n: Integer) -> Result<(), &'static str> {
if n>=2 {
self.0.last_mut().unwrap().1 = n;
Ok(())
}
else {Err("Input base must be at least 2")}
}
#[inline(always)] fn set_o(&mut self, n: Integer) -> Result<(), &'static str> {
if n>=2 {
self.0.last_mut().unwrap().2 = n;
Ok(())
}
else {Err("Output base must be at least 2")}
}
#[inline(always)] fn k(&self) -> Integer {self.0.last().unwrap().0.clone()}
#[inline(always)] fn i(&self) -> Integer {self.0.last().unwrap().1.clone()}
#[inline(always)] fn o(&self) -> Integer {self.0.last().unwrap().2.clone()}
}
impl Default for ParamStk {
#[inline(always)] fn default() -> Self {
let mut p = Self(Vec::new());
p.create();
p
}
}
pub struct State<'a> {
pub mstk: Vec<Obj>,
pub regs: HashMap<Integer, Register>,
pub ro_buf: RegObj,
pub rptr: Option<Integer>,
pub rng: RandState<'a>,
pub par: ParamStk,
pub w: u32
}
impl Default for State<'_> {
#[inline(always)] fn default() -> Self {
Self {
mstk: Vec::new(),
regs: HashMap::new(),
ro_buf: RegObj::default(),
rptr: None,
rng: {
let mut r = RandState::new();
let mut seed = [0_u8; 128];
OsRng.fill_bytes(&mut seed);
r.seed(&Integer::from_digits(&seed, Order::Msf));
r
},
par: ParamStk::default(),
w: 64
}
}
}
#[inline(always)] fn round(n: &Float) -> Integer {
if let Some((i, _)) = n.to_integer_round(Round::Zero) {i} else {Integer::ZERO}
}
#[inline(never)] fn parse_abnum(src: String, base: Integer, prec: u32) -> Result<Float, &'static str> {
let (mut mstr, estr) = match src.split(['@', 'e', 'E']).collect::<Vec<&str>>()[..] { [m] => (m, "0"),
[m, e] => (m, e),
_ => {return Err("more than one exponential part");} };
let mneg = if let Some(s) = mstr.strip_prefix(['-', '_']) { mstr = s;
true
}
else {false};
let exp = if let Ok(i) = Integer::parse(estr.replace('_', "-")) {
Float::with_val(prec, &base).pow(i.complete()) }
else {return Err("invalid exponential part");};
let mut man = Integer::from(0_u8); let mut scale = Integer::from(1_u8); let mut frac = false;
for mut dig in mstr.split_inclusive([' ', '.']) {
man *= &base; if frac {scale *= &base;}
if let Some(d) = dig.strip_suffix('.') { if frac {return Err("more than one '.'");}
frac = true;
dig = d;
}
let di = if let Ok(i) = Integer::parse(String::from('0')+dig) {i.complete()}
else {return Err("invalid character in digit");};
if di >= base {return Err("digit too high for input base");}
man += di; }
if mneg {man *= -1_i8;}
Ok(
Float::with_val(prec, man) / scale * exp
)
}
macro_rules! cval {
($val:expr) => {
|prec: u32| Float::with_val(prec, $val)
}
}
macro_rules! csci {
($man:literal, $exp:literal) => {
|prec: u32| Float::with_val(prec, $man) * Float::with_val(prec, $exp).exp10()
}
}
macro_rules! crec {
($base:literal, $numer:literal, $denom:literal) => {
|prec: u32| CONSTANTS.get($base).unwrap()(prec) * $numer / $denom
}
}
pub const CONSTANTS: phf::Map<&'static str, fn(u32) -> Float> = phf_map! {
"e" => |prec| Float::with_val(prec, 1_u8).exp(),
"pi" => cval!(Constant::Pi),
"gamma" => cval!(Constant::Euler),
"phi" => |prec| (Float::with_val(prec, 5_u8).sqrt()+1_u8)/2_u8,
"deg" => crec!("pi", 1_u8, 180_u8),
"°" => crec!("pi", 1_u8, 180_u8),
"gon" => crec!("pi", 1_u8, 200_u8),
"grad" => crec!("pi", 1_u8, 200_u8),
"c" => cval!(299_792_458_u32),
"hbar" => |prec| Float::with_val(prec, 662_607_015_u32) / Integer::from(10_u8).pow(42) / Float::with_val(prec, Constant::Pi) / 2_u8,
"G" => csci!(6674_u16, -3_i8),
"qe" => csci!(1_602_176_634_u32, -28_i8),
"NA" => csci!(602_214_076_u32, 31_u8),
"kB" => csci!(1_380_649_u32, -29_i8),
"u" => csci!(1_660_539_066_u32, -36_i8),
"lp" => csci!(16162_u16, -39_i8),
"tp" => csci!(5391_u16, -47_i8),
"mp" => csci!(21764_u16, -12_i8),
"Tp" => csci!(14167_u16, 28_u8),
"in" => csci!(254_u8, -4_i8),
"ft" => crec!("in", 12_u8, 1_u8),
"yd" => crec!("ft", 3_u8, 1_u8),
"m" => cval!(1_u8),
"fur" => crec!("ft", 660_u16, 1_u8),
"mi" => crec!("ft", 5280_u16, 1_u8),
"nmi" => cval!(1852_u16),
"AU" => cval!(149_597_870_700_u64),
"ly" => cval!(9_460_730_472_580_800_u64),
"pc" => |prec| Float::with_val(prec, 96_939_420_213_600_000_u64) / Float::with_val(prec, Constant::Pi),
"ac" => csci!(40_468_564_224_u64, -7_i8),
"acre" => csci!(40_468_564_224_u64, -7_i8),
"l" => csci!(1_u8, -3_i8),
"ifloz" => csci!(284_130_625_u32, -13_i8),
"ipt" => crec!("ifloz", 20_u8, 1_u8),
"iqt" => crec!("ifloz", 40_u8, 1_u8),
"igal" => crec!("ifloz", 160_u8, 1_u8),
"ibu" => crec!("ifloz", 1280_u16, 1_u8),
"ibsh" => crec!("ifloz", 1280_u16, 1_u8),
"ufldr" => csci!(36_966_911_953_125_u64, -19_i8),
"tsp" => crec!("ufldr", 4_u8, 3_u8),
"tbsp" => crec!("ufldr", 4_u8, 1_u8),
"ufloz" => crec!("ufldr", 8_u8, 1_u8),
"upt" => crec!("ufloz", 16_u8, 1_u8),
"uqt" => crec!("ufloz", 32_u8, 1_u8),
"ugal" => crec!("ufloz", 128_u8, 1_u8),
"bbl" => crec!("ugal", 42_u8, 1_u8),
"udpt" => csci!(5_506_104_713_575_u64, -16_i8),
"udqt" => crec!("udpt", 2_u8, 1_u8),
"udgal" => crec!("udpt", 8_u8, 1_u8),
"ubu" => crec!("udpt", 64_u8, 1_u8),
"ubsh" => crec!("udpt", 64_u8, 1_u8),
"dbbl" => csci!(115_627_123_584_i64, -12_i8),
"ct" => csci!(2_u8, -4_i8),
"oz" => csci!(28_349_523_125_u64, -12_i8),
"lb" => crec!("oz", 16_u8, 1_u8),
"kg" => cval!(1_u8),
"st" => crec!("lb", 14_u8, 1_u8),
"t" => crec!("lb", 2240_u16, 1_u8),
"s" => cval!(1_u8),
"min" => cval!(60_u8),
"h" => crec!("min", 60_u8, 1_u8),
"d" => crec!("h", 24_u8, 1_u8),
"w" => crec!("d", 7_u8, 1_u8),
"mo" => crec!("d", 30_u8, 1_u8),
"a" => crec!("d", 365_u16, 1_u8),
"aj" => crec!("d", 36525_u16, 100_u8),
"ag" => crec!("d", 3_652_425_u32, 10000_u16),
"b" => cval!(1_u8),
"Kib" => cval!(1_u16<<10_u8),
"Mib" => cval!(1_u32<<20_u8),
"Gib" => cval!(1_u32<<30_u8),
"Tib" => cval!(1_u64<<40_u8),
"Pib" => cval!(1_u64<<50_u8),
"Eib" => cval!(1_u64<<60_u8),
"Zib" => cval!(1_u128<<70_u8),
"Yib" => cval!(1_u128<<80_u8),
"Rib" => cval!(1_u128<<90_u8),
"Qib" => cval!(1_u128<<100_u8),
"B" => cval!(8_u8),
"KiB" => cval!(1_u16<<13_u8),
"MiB" => cval!(1_u32<<23_u8),
"GiB" => cval!(1_u64<<33_u8),
"TiB" => cval!(1_u64<<43_u8),
"PiB" => cval!(1_u64<<53_u8),
"EiB" => cval!(1_u64<<63_u8),
"ZiB" => cval!(1_u128<<73_u8),
"YiB" => cval!(1_u128<<83_u8),
"RiB" => cval!(1_u128<<93_u8),
"QiB" => cval!(1_u128<<103_u8),
"J" => cval!(1_u8),
"cal" => csci!(4184_u16, -3_i8),
"Pa" => cval!(1_u8),
"atm" => cval!(101_325_u32),
"psi" => csci!(6_894_757_293_168_u64, -9_i8),
"torr" => crec!("atm", 1_u8, 760_u16),
"inf" => cval!(Special::Infinity),
"ninf" => cval!(Special::NegInfinity),
"nan" => cval!(Special::Nan),
"time" => cval!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()),
"timens" => cval!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_nanos()),
"pid" => cval!(std::process::id()),
"author" => cval!(43615_u16),
};
#[inline(never)] pub fn get_constant(prec: u32, query: &str, safe: bool) -> Option<Float> {
let mut q = query.to_string();
let mut scale = String::new();
while q.starts_with(|c: char| c.is_ascii_digit()||c=='-') {
scale.push(q.remove(0)); }
if scale.is_empty()||scale.ends_with('-') {scale.push('0');}
let s = Float::with_val(prec, Integer::parse(scale).unwrap().complete()).exp10();
let mut power = String::new();
while q.ends_with(|c: char| c.is_ascii_digit()) {
power.insert(0, q.pop().unwrap()); }
if power.is_empty() {power.push('1');}
let p = Float::with_val(prec, Integer::parse(power).unwrap().complete());
if let Some(n) = CONSTANTS.get(q.as_str()).map(|c| c(prec)) {
Some((s*n).pow(p))
}
else if !safe {
match q.as_str() { "abort" => {std::process::abort();}
"crash" => {get_constant(prec, "crash", false)} "panic" => {std::panic::panic_any("Expected, [panic]\" was executed");}
_ => None
}
}
else {None}
}
#[inline(always)] fn trim_0s(v: &mut Vec<u8>) {
while v.last()==Some(&b'0') {v.pop();}
if v.last()==Some(&b'.') {v.pop();}
}
#[inline(never)] fn flt_to_str(mut num: Float, obase: Integer, oprec: Integer) -> String {
if !num.is_normal() {
let mut ret = String::new();
if num.is_sign_negative() {ret.push('_');}
if num.is_zero() {ret.push('0');}
else if num.is_infinite() {ret.push('∞');}
else {ret.push_str("NaN");}
return ret;
}
if obase>36_u8 { let mut outstr = String::from(if num.is_sign_negative() {"(-"} else {"("}); num.abs_mut();
let mut scale = 0; while !num.is_integer()&&(oprec==0_u8||scale<oprec) { let temp = num.clone() * &obase; if temp.is_infinite() { num /= &obase; break; }
num = temp; scale += 1;
}
num *= &obase; let mut int = num.to_integer().unwrap(); int /= &obase; let mut dig = Integer::from(1_u8); while dig<=int {
dig *= &obase; }
dig /= &obase; loop { let (quot, rem) = int.clone().div_rem_euc(dig.clone()); outstr.push_str(".to_string()); outstr.push(' ');
int = rem; if dig==1_u8 {break;} dig /= &obase; }
if scale>0 {
if let Some((idx, _)) = outstr.rmatch_indices(' ').nth(scale) { unsafe { outstr.as_bytes_mut()[idx] = b'.'; } }
else {
outstr.insert_str(if outstr.starts_with("(-") {2} else {1}, "0."); }
}
outstr.pop();
outstr.push(')');
outstr
}
else { let mut ret = num.to_string_radix(
obase.to_i32().unwrap(), oprec.to_usize() );
let bytes = unsafe {ret.as_mut_vec()};
let mneg = bytes[0]==b'-';
if let Some(elen) = bytes.iter().rev().take(12) .position(|c| *c==if obase<=10_u8 {b'e'} else {b'@'}) {
let mut epart = bytes.split_off(bytes.len()-elen); bytes.pop();
if epart[0]==b'-' { if elen==2 { let eint = unsafe {String::from_utf8_unchecked(epart)[1..].parse::<usize>().unwrap()}; let mut buf = vec![b'0', b'.']; buf.resize(eint + 1, b'0'); buf.push(bytes[mneg as usize]); buf.extend(bytes.split_off(2 + mneg as usize)); bytes.clear(); if mneg {bytes.push(b'_');} bytes.append(&mut buf);
trim_0s(bytes);
}
else { if mneg {bytes[0] = b'_';} trim_0s(bytes);
bytes.push(b'@'); epart[0]=b'_';
bytes.append(&mut epart); }
}
else { if mneg {bytes[0] = b'_';} trim_0s(bytes);
bytes.push(b'@'); bytes.append(&mut epart); }
}
else { if mneg {bytes[0] = b'_';} if bytes.iter().any(|x| *x==b'.') { trim_0s(bytes);
}
}
ret
}
}
#[derive(Clone, Default)]
struct Macro {
v: Vec<char>,
i: usize,
reps: Integer
}
impl Iterator for Macro {
type Item = char;
#[inline(always)] fn next(&mut self) -> Option<Self::Item> {
self.v.get(self.i).map(|c|{self.i+=1; *c})
}
}
impl<T: ToString> From<T> for Macro {
#[inline(always)] fn from(value: T) -> Self {
Self {
v: value.to_string().chars().collect(),
i: 0,
reps: Integer::ZERO
}
}
}
impl Macro {
#[inline(always)] fn at_end(&self) -> bool {
self.i >= self.v.len()
}
#[inline(always)] fn exhausted(&self) -> bool {
self.at_end() && self.reps.is_zero()
}
#[inline(always)] fn repeat(&mut self) {
self.i = 0;
self.reps -= 1_u8;
}
}
pub struct IOTriple (
pub Box<dyn BufRead>,
pub Box<dyn Write>,
pub Box<dyn Write>
);
#[macro_export] macro_rules! std_io {
() => {
$crate::IOTriple(
::std::boxed::Box::new(::std::io::BufReader::new(::std::io::stdin())),
::std::boxed::Box::new(::std::io::stdout()),
::std::boxed::Box::new(::std::io::stderr())
)
}
}
#[macro_export] macro_rules! no_io {
() => {
$crate::IOTriple(
::std::boxed::Box::new(::std::io::BufReader::new(::std::io::empty())),
::std::boxed::Box::new(::std::io::sink()),
::std::boxed::Box::new(::std::io::sink())
)
}
}
pub enum ExecDone {
Finished,
Quit(i32)
}
use ExecDone::*;
#[cold] #[inline(never)] pub fn exec(st: &mut State, io: &mut IOTriple, safe: bool, cmds: &str) -> std::io::Result<ExecDone> {
let (input, output, error) = (&mut *io.0, &mut *io.1, &mut *io.2);
let mut cmdstk: Vec<Macro> = vec!(cmds.into()); let mut inv = false; let mut re_cache: HashMap<String, Regex> = HashMap::new();
let mut reg_dummy = Register::default();
let sx_dummy = String::new();
let nx_dummy = Float::new(1);
while !cmdstk.is_empty() {
let mut cmd = cmdstk.last_mut().unwrap().next().unwrap_or_default(); let sig = CMD_SIGS.get(&cmd).copied().unwrap_or(NIL);
let (reg, rnum): (&mut Register, Integer) = if sig & 0x80 != 0 { let i = if let Some(i) = st.rptr.take() {i} else if let Some(c) = cmdstk.last_mut().unwrap().next() {Integer::from(c as u32)} else {
writeln!(error, "! Command '{cmd}' needs a register number")?;
continue;
};
(
if let Some(r) = st.regs.get_mut(&i) {r} else {
st.regs.insert(i.clone(), Register::default()); st.regs.get_mut(&i).unwrap()
},
i
)
}
else {(&mut reg_dummy, Integer::ZERO)};
let adi = sig & 0xf;
if st.mstk.len() < adi as usize { writeln!(error, "! Command '{cmd}' needs {} argument{}", adi, plural(sig))?;
continue;
}
let (c, b, a) = match adi { 1 => (Obj::default(), Obj::default(), st.mstk.pop().unwrap()),
2 => (Obj::default(), st.mstk.pop().unwrap(), st.mstk.pop().unwrap()),
3 => (st.mstk.pop().unwrap(), st.mstk.pop().unwrap(), st.mstk.pop().unwrap()),
_ => (Obj::default(), Obj::default(), Obj::default())
};
let [mut na, mut nb, mut nc] = [&nx_dummy; 3]; let [mut sa, mut sb, mut sc] = [&sx_dummy; 3]; let mut strv = false;
if match sig & 0x7f { NIL => false,
AX => match &a {
Num(x) => {
na = x;
false
},
Str(x) => {
sa = x;
strv = true;
false
}
},
AN => match &a {
Num(x) => {
na = x;
false
},
_ => true
},
AS => match &a {
Str(x) => {
sa = x;
false
},
_ => true
},
AX_BX => match (&a, &b) {
(Num(x), Num(y)) => {
(na, nb) = (x, y);
false
},
(Str(x), Str(y)) => {
(sa, sb) = (x, y);
strv = true;
false
},
_ => true
},
AX_BN => match (&a, &b) {
(Num(x), Num(y)) => {
(na, nb) = (x, y);
false
},
(Str(x), Num(y)) => {
(sa, nb) = (x, y);
strv = true;
false
},
_ => true
},
AN_BN => match (&a, &b) {
(Num(x), Num(y)) => {
(na, nb) = (x, y);
false
},
_ => true
},
AS_BN => match (&a, &b) {
(Str(x), Num(y)) => {
(sa, nb) = (x, y);
false
},
_ => true
},
AX_BX_CX => match (&a, &b, &c) {
(Num(x), Num(y), Num(z)) => {
(na, nb, nc) = (x, y, z);
false
},
(Str(x), Str(y), Str(z)) => {
(sa, sb, sc) = (x, y, z);
strv = true;
false
},
_ => true
},
_ => nope!()
}
{
writeln!(error, "! Wrong argument type{} for command '{cmd}': {}", plural(sig), correct(sig))?;
match adi { 1 => {st.mstk.push(a);},
2 => {st.mstk.push(a); st.mstk.push(b);},
3 => {st.mstk.push(a); st.mstk.push(b); st.mstk.push(c);},
_ => {}
}
continue;
}
if let Some(semerr) = match cmd {
'0'..='9'|'.'|'_'|'\''|'@' => {
if st.par.i()>36_u8 {
Some("\0Any-base number input must be used for I > 36".into())
}
else {
let mut frac = false; let mut neg = false; let alpha = if cmd == '\'' {
cmd = cmdstk.last_mut().unwrap().next().unwrap_or_default(); true
}
else {false};
let mut buf = String::new();
let mut first = true;
loop { if !first {cmd = cmdstk.last_mut().unwrap().next().unwrap_or_default();} first = false;
match cmd {
'0'..='9' => {buf.push(cmd);},
'.' => {
if frac {break;}
frac = true;
buf.push(cmd);
},
'_' => {
if neg {break;}
neg = true;
buf.push('-'); },
'@' => {
if buf.is_empty()||buf=="-" {
buf.push('1'); }
if buf=="."||buf=="-." {
buf.push('0'); }
neg = false; buf.push(cmd);
},
'a'..='z'|'A'..='Z' if alpha => { buf.push(cmd);
},
_ => {break;}
}
}
if cmd!='\0' {cmdstk.last_mut().unwrap().i -= 1;} if buf.ends_with(['.','-','@']) || buf.is_empty() { buf.push('0'); } match Float::parse_radix(buf.clone(), st.par.i().to_i32().unwrap()) {
Ok(res) => {
st.mstk.push(Num(Float::with_val(st.w, res)));
None
},
Err(err) => {
Some(format!("\0Unable to parse number \"{buf}\": {err}")) },
}
}
},
'(' => {
let mut to_parse = String::new();
cmd = cmdstk.last_mut().unwrap().next().unwrap_or(')'); while cmd != ')' { to_parse.push(cmd);
cmd = cmdstk.last_mut().unwrap().next().unwrap_or(')'); }
match parse_abnum(to_parse, st.par.i(), st.w) {
Ok(n) => {st.mstk.push(Num(n)); None}
Err(e) => {Some(format!("\0Unable to parse any-base number: {e}"))}
}
},
'[' => {
let mut res = String::new(); let mut nest: usize = 1; cmd = cmdstk.last_mut().unwrap().next().unwrap_or(']'); loop { res.push(cmd);
if cmd == '[' { nest+=1; }
if cmd == ']' { nest-=1; }
if nest==0 { res.pop(); st.mstk.push(Str(res));
break None;
}
cmd = cmdstk.last_mut().unwrap().next().unwrap_or(']'); }
},
'p' => {
match st.mstk.last() {
Some(Num(n)) => {writeln!(output, "{}", flt_to_str(n.clone(), st.par.o(), st.par.k()))?;},
Some(Str(s)) => {writeln!(output, "[{s}]")?;},
None => {}
}
None
},
'f' => {
if !st.mstk.is_empty() {
for i in st.mstk.iter().rev() {
match i {
Num(n) => {writeln!(output, "{}", flt_to_str(n.clone(), st.par.o(), st.par.k()))?;},
Str(s) => {writeln!(output, "[{s}]")?;},
}
}
}
None
},
'n' => {
if !strv {
write!(output, "{}", flt_to_str(na.clone(), st.par.o(), st.par.k()))?;
}
else {
write!(output, "{sa}")?;
}
output.flush().unwrap();
None
},
'P' => {
if !strv {writeln!(output, "{}", flt_to_str(na.clone(), st.par.o(), st.par.k()))?;}
else {writeln!(output, "{sa}")?;}
None
},
'F' => {
if !reg.v.is_empty(){
for i in (0..reg.v.len()).rev() {
match ®.v[i].o {
Num(n) => {writeln!(output, "{}", flt_to_str(n.clone(), st.par.o(), st.par.k()))?;},
Str(s) => {writeln!(output, "[{s}]")?;},
}
let width = (reg.v[i].a.len()-1).to_string().len(); for (ai, o) in reg.v[i].a.iter().enumerate() {
match o {
Num(n) => {writeln!(output, "\t{ai:>width$}: {}", flt_to_str(n.clone(), st.par.o(), st.par.k()))?;},
Str(s) => {writeln!(output, "\t{ai:>width$}: [{s}]")?;},
}
}
}
}
None
},
'+' => {
if !strv {st.mstk.push(Num(Float::with_val(st.w, na + nb)));}
else {st.mstk.push(Str(sa.clone() + sb));}
None
},
'-' => {
if !strv {st.mstk.push(Num(Float::with_val(st.w, na - nb))); None}
else {
let ib = round(nb);
if let Some(n) = ib.clone().abs().to_usize() {
st.mstk.push(Str(
if ib.is_negative() { sa.chars().skip(n).collect()
}
else { sa.chars().take(sa.chars().count().saturating_sub(n)).collect()
}
));
None
}
else {
Some(format!("Cannot possibly remove {ib} characters from a string"))
}
}
},
'*' => {
if !strv {st.mstk.push(Num(Float::with_val(st.w, na * nb))); None}
else {
let ib = round(nb);
if let Some(n) = ib.clone().abs().to_usize() {
st.mstk.push(Str(
if ib.is_negative() { sa.chars().rev().collect::<String>().repeat(n)
}
else { sa.repeat(n)
}
));
None
}
else {
Some(format!("Cannot possibly repeat a string {ib} times"))
}
}
},
'/' => {
if !strv {
if nb.is_zero() {
Some("Division by zero".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, na / nb)));
None
}
}
else {
let ib = round(nb);
if let Some(n) = ib.clone().abs().to_usize() {
st.mstk.push(Str(
if ib.is_negative() { sa.chars().skip(sa.chars().count().saturating_sub(n)).collect()
}
else { sa.chars().take(n).collect()
}
));
None
}
else {
Some(format!("Cannot possibly shorten a string to {ib} characters"))
}
}
},
'%' => {
if !strv {
let ia = round(na);
let ib = round(nb);
if ib==0 {
Some("Reduction mod 0".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, ia % ib)));
None
}
}
else {
let ib = round(nb);
if let Some(n) = ib.to_usize() {
if let Some(c) = sa.chars().nth(n) {
st.mstk.push(Str(c.into()));
None
}
else {
Some(format!("String is too short for index {n}"))
}
}
else {
Some(format!("Cannot possibly extract character at index {ib}"))
}
}
},
'~' => {
if !strv {
let ia = round(na);
let ib = round(nb);
if ib==0_u8 {
Some("Reduction mod 0".into())
}
else {
let (quot, rem)=ia.div_rem_euc(ib);
st.mstk.push(Num(Float::with_val(st.w, quot)));
st.mstk.push(Num(Float::with_val(st.w, rem)));
None
}
}
else {
let ib = round(nb);
if let Some(n) = ib.to_usize() {
st.mstk.push(Str(sa.chars().take(n).collect()));
st.mstk.push(Str(sa.chars().skip(n).collect()));
None
}
else {
Some(format!("Cannot possibly split a string at character {ib}"))
}
}
},
'^' => {
if !strv {
if *na<0_u8 && nb.clone().abs()<1_u8{
Some("Root of negative number".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, na.pow(nb))));
None
}
}
else if let Some(bidx) =
if inv { let mut re = re_cache.get(sb).cloned();
if re.is_none() {
if let Ok(new) = RegexBuilder::new(sb).size_limit(usize::MAX).build() {
re_cache.insert(sb.clone(), new.clone());
re = Some(new);
}
}
re.and_then(|re| re.find(sa).map(|m| m.start()))
}
else {
sa.find(sb) }
{
let cidx = sa.char_indices().position(|(cidx, _)| cidx==bidx).unwrap(); st.mstk.push(Num(Float::with_val(st.w, cidx)));
None
}
else {
st.mstk.push(Num(Float::with_val(st.w, -1_i8))); None
}
},
'|' => {
if !strv {
let ia = round(na);
let ib = round(nb);
let ic = round(nc);
if ic==0_u8 {
Some("Reduction mod 0".into())
}
else if let Ok(res) = ia.clone().pow_mod(&ib, &ic) {
st.mstk.push(Num(Float::with_val(st.w, res)));
None
}
else {
Some(format!("{ia} doesn't have an inverse mod {ic}"))
}
}
else if inv { let mut re = re_cache.get(sb).cloned();
if re.is_none() {
if let Ok(new) = RegexBuilder::new(sb).size_limit(usize::MAX).build() {
re_cache.insert(sb.clone(), new.clone());
re = Some(new);
}
}
st.mstk.push(Str(re.map(|re|
re.replace_all(sa, sc).into_owned() ).unwrap_or_else(|| sa.clone()) ));
None
}
else { st.mstk.push(Str(sa.replace(sb, sc)));
None
}
},
'v' => {
if *na<0_u8 {
Some("Root of negative number".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, na.clone().sqrt())));
None
}
},
'V' => {
if *na<0_u8 && nb.clone().abs()>1{
Some("Root of negative number".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, na.pow(nb.clone().recip()))));
None
}
},
'g' => {
if !strv {
if *na<=0_u8 {
Some("Logarithm of non-positive number".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, na.clone().ln())));
None
}
}
else {
st.mstk.push(Num(Float::with_val(st.w, sa.chars().count())));
None
}
},
'G' => {
if *na<=0_u8 {
Some("Logarithm of non-positive number".into())
}
else if *nb==1_u8||*nb<=0_u8{
Some("Logarithm with base ≤0 or =1".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, na.clone().ln()/nb.clone().ln())));
None
}
},
't' => {
let int = round(nb);
match match int.to_i8() {
Some(i) if (-6..=6).contains(&i) => {
match i {
0 => Ok(na * Float::with_val(st.w, Constant::Pi) / 180_u8),
1 => Ok(Float::with_val(st.w, na.sin_ref())),
2 => Ok(Float::with_val(st.w, na.cos_ref())),
3 => Ok(Float::with_val(st.w, na.tan_ref())),
4 => Ok(Float::with_val(st.w, na.sinh_ref())),
5 => Ok(Float::with_val(st.w, na.cosh_ref())),
6 => Ok(Float::with_val(st.w, na.tanh_ref())),
-1 => if na.clone().abs()<=1_u8 {
Ok(Float::with_val(st.w, na.asin_ref()))
}
else {
Err("Inverse sine of value outside [-1,1]")
},
-2 => if na.clone().abs()<=1_u8 {
Ok(Float::with_val(st.w, na.acos_ref()))
}
else {
Err("Inverse cosine of value outside [-1,1]")
},
-3 => Ok(Float::with_val(st.w, na.atan_ref())),
-4 => Ok(Float::with_val(st.w, na.asinh_ref())),
-5 => if na>=&1_u8 {
Ok(Float::with_val(st.w, na.acosh_ref()))
}
else {
Err("Inverse hyperbolic cosine of value below 1")
},
-6 => if na.clone().abs()<1_u8 {
Ok(Float::with_val(st.w, na.atanh_ref()))
}
else {
Err("Inverse hyperbolic tangent of value outside (-1,1)")
},
_ => nope!()
}
}.map_err(|e| e.into()),
_ => { Err(format!("Function # {int} doesn't exist"))
}
}
{
Ok(n) => {
st.mstk.push(Num(n));
None
}
Err(e) => {
Some(e)
}
}
},
'T' => {
let int = round(na);
if let Some(u) = int.to_u64() {
th::sleep(Duration::from_millis(u));
None
}
else {
Some(format!("Cannot possibly wait {int} milliseconds"))
}
},
'N' => {
let int = round(na);
if int<=0_u8 {
Some("Upper bound for random value must be above 0".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, int.random_below(&mut st.rng))));
None
}
},
'"' => {
if !strv {
st.mstk.push(Str(flt_to_str(na.clone(), st.par.o(), st.par.k())));
None
}
else {
match sa.matches(' ').count() {
0 => { if let Some(res) = get_constant(st.w, sa, safe) {
st.mstk.push(Num(res));
None
}
else {
Some("Constant/conversion factor not found".into())
}
},
1 => { let (sl, sr) = sa.split_once(' ').unwrap();
if let (Some(nl), Some(nr)) = (get_constant(st.w, sl, safe), get_constant(st.w, sr, safe)) {
st.mstk.push(Num(nl/nr));
None
}
else {
Some("Constant/conversion factor not found".into())
}
},
_ => {
Some("Too many spaces in constant/conversion query".into())
},
}
}
},
'c' => {
if inv { st.mstk.shrink_to_fit();
st.regs.retain(|_, r| !r.v.is_empty() || r.th.is_some()); st.regs.shrink_to_fit();
for (_, reg) in st.regs.iter_mut() {
reg.v.shrink_to_fit(); }
st.par.0.shrink_to_fit();
}
else { st.mstk.clear();
}
None
},
'C' => {
let int = round(na);
if let Some(num) = int.to_usize() {
st.mstk.truncate(st.mstk.len().saturating_sub(num));
None
}
else {
Some(format!("Cannot possibly remove {int} objects from the main stack"))
}
},
'd' => {
if let Some(o) = st.mstk.last() {
st.mstk.push(o.clone());
None
}
else {
Some("Nothing to duplicate".into())
}
},
'D' => {
let int = round(na);
if let Some(num) = int.to_usize() {
if num<=st.mstk.len() {
st.mstk.extend_from_within(st.mstk.len()-num..);
None
}
else {
Some("Not enough objects to duplicate".into())
}
}
else {
Some(format!("Cannot possibly duplicate {int} objects"))
}
},
'r' => {
let len = st.mstk.len();
if len>=2 {
st.mstk.swap(len-2, len-1);
None
}
else {
Some("Not enough objects to swap".into())
}
},
'R' => {
let mut int = round(na);
if int==0_u8 { int = Integer::from(1_u8); } if let Some(num) = int.clone().abs().to_usize() {
let len = st.mstk.len();
if num<=len {
if inv {
st.mstk[len-num..].reverse(); }
else if int<0_u8 {
st.mstk[len-num..].rotate_left(1); }
else {
st.mstk[len-num..].rotate_right(1); }
None
}
else {
Some("Not enough objects to rotate".into())
}
}
else {
Some(format!("Cannot possibly rotate {} objects", int.abs()))
}
},
'z' => {
st.mstk.push(Num(Float::with_val(st.w, st.mstk.len())));
None
},
'k' => {
if let Err(e) = st.par.set_k(round(na)) {
Some(e.into())
}
else {None}
},
'i' => {
if let Err(e) = st.par.set_i(round(na)) {
Some(e.into())
}
else {None}
},
'o' => {
if let Err(e) = st.par.set_o(round(na)) {
Some(e.into())
}
else {None}
},
'w' => {
let i = round(na);
if let (Some(u), false) = (i.to_u32(), i==0_u8) {
st.w = u;
None
}
else {
Some(format!("Working precision must be in range 1 ≤ W ≤ {}", u32::MAX))
}
},
'K' => {
st.mstk.push(Num(Float::with_val(st.w, st.par.k())));
None
},
'I' => {
st.mstk.push(Num(Float::with_val(st.w, st.par.i())));
None
},
'O' => {
st.mstk.push(Num(Float::with_val(st.w, st.par.o())));
None
},
'W' => {
st.mstk.push(Num(Float::with_val(st.w, st.w)));
None
},
'{' => {
st.par.create();
None
},
'}' => {
st.par.destroy();
None
},
's' => {
if !inv { if let Some(ro) = reg.v.last_mut() {
ro.o = a.clone();
} else {
reg.v.push(RegObj { o: a.clone(), a: Vec::new() });
}
}
else { reg.v = st.mstk.drain(..).chain(std::iter::once(a.clone()))
.map(|o| RegObj{o, a: Vec::new()}).collect();
}
None
},
'S' => {
if !inv { reg.v.push(RegObj { o: a.clone(), a: Vec::new() });
}
else { st.mstk.drain(..).chain(std::iter::once(a.clone()))
.map(|o| RegObj{o, a: Vec::new()})
.for_each(|ro| reg.v.push(ro));
}
None
},
'l' => {
if !inv{ if let Some(ro) = reg.v.last() {
st.mstk.push(ro.o.clone());
None
} else {
Some(format!("Register # {rnum} is empty"))
}
}
else { reg.v.iter().for_each(|ro| st.mstk.push(ro.o.clone()));
None
}
},
'L' => {
if !inv { if let Some(ro) = reg.v.pop() {
st.mstk.push(ro.o);
None
} else {
Some(format!("Register # {rnum} is empty"))
}
}
else { reg.v.drain(..).for_each(|ro| st.mstk.push(ro.o));
None
}
},
':' => {
if reg.v.is_empty() {
reg.v.push(RegObj::default());
}
let int = round(nb);
if let Some(rai) = int.to_usize() {
if inv { reg.v.last_mut().unwrap().a.resize(rai, a.clone());
}
else { if rai>=reg.v.last().unwrap().a.len() {
reg.v.last_mut().unwrap().a.resize(rai+1, Obj::default()); }
reg.v.last_mut().unwrap().a[rai] = a.clone();
}
None
}
else {
Some(format!("Cannot possibly save to array index {int}"))
}
},
';' => {
if reg.v.is_empty() {
reg.v.push(RegObj::default());
}
let int = round(na);
if let Some(rai) = int.to_usize() {
if inv { reg.v.last_mut().unwrap().a.truncate(rai);
reg.v.last_mut().unwrap().a.shrink_to_fit();
}
else { if rai>=reg.v.last().unwrap().a.len() {
reg.v.last_mut().unwrap().a.resize(rai+1, Obj::default()); }
st.mstk.push(reg.v.last().unwrap().a[rai].clone());
}
None
}
else {
Some(format!("Cannot possibly load from array index {int}"))
}
},
'b' => {
if let Some(mut ro) = reg.v.pop() {
if !inv { st.ro_buf = ro;
}
else { st.mstk.append(&mut ro.a);
}
None
}
else {
Some(format!("Register # {rnum} is empty"))
}
},
'B' => {
if !inv { reg.v.push(st.ro_buf.clone());
}
else { if reg.v.is_empty() {reg.v.push(RegObj::default());}
reg.v.last_mut().unwrap().a.append(&mut st.mstk);
}
None
},
'Z' => {
st.mstk.push(Num(Float::with_val(st.w,
if inv { reg.v.last().map(|ro| ro.a.len()).unwrap_or_default()
}
else { reg.v.len()
}
))); None
},
',' => {
st.rptr = Some(
if !strv {
round(na) }
else {
Integer::from_digits(sa.as_bytes(), Order::Msf) }
);
None
},
'a' => {
if !strv {
let ia = round(na).to_u32_wrapping();
if let Some(res) = char::from_u32(ia) {
st.mstk.push(Str(res.into()));
None
}
else {
Some(format!("Unable to convert number {ia} to character: not a valid Unicode value"))
}
}
else if sa.is_empty() {
Some("Cannot convert empty string to number".into())
}
else {
st.mstk.push(Num(Float::with_val(st.w, sa.chars().next().unwrap() as u32)));
None
}
},
'A' => {
if !strv {
if let Ok(res) = String::from_utf8(round(na).to_digits::<u8>(Order::Msf)) {
st.mstk.push(Str(res));
None
}
else {
Some(format!("Unable to convert number {} to string: not a valid UTF-8 sequence", round(na)))
}
}
else {
st.mstk.push(Num(Float::with_val(st.w, Integer::from_digits(sa.as_bytes(), Order::Msf))));
None
}
},
'x' => {
if strv {
if cmdstk.last().unwrap().exhausted() {
cmdstk.pop(); }
cmdstk.push(sa.into());
} else {
st.mstk.push(a.clone());
}
None
},
'<'|'='|'>' => {
match reg.v.last() {
Some(RegObj{o: Str(mac), ..}) => {
if inv != match (cmd, strv) {
('<', false) => { nb < na },
('=', false) => { nb == na },
('>', false) => { nb > na },
('<', true) => {
let lb = sb.chars().count();
let la = sa.chars().count();
lb < la || (lb == la && sb.chars().zip(sa.chars()).any(|(cb, ca)| cb < ca))
},
('=', true) => { sb == sa },
('>', true) => {
let lb = sb.chars().count();
let la = sa.chars().count();
lb > la || (lb == la && sb.chars().zip(sa.chars()).any(|(cb, ca)| cb > ca))
},
_ => nope!()
}
{
if cmdstk.last().unwrap().exhausted() {
cmdstk.pop(); }
cmdstk.push(mac.into());
}
None
},
Some(_) => {
Some(format!("Top of register # {rnum} is not a string"))
},
None => {
Some(format!("Register # {rnum} is empty"))
}
}
},
'y' => {
match reg.v.last() {
Some(RegObj{o: Str(mac), ..}) => {
if inv != strv {
if cmdstk.last().unwrap().exhausted() {
cmdstk.pop(); }
cmdstk.push(mac.into());
}
None
},
Some(_) => {
Some(format!("Top of register # {rnum} is not a string"))
},
None => {
Some(format!("Register # {rnum} is empty"))
}
}
},
'X' => {
let int = round(nb);
if int>=0_u8 {
if cmdstk.last().unwrap().exhausted() {
cmdstk.pop(); }
if int > 0_u8 {
cmdstk.push(Macro {
v: sa.chars().collect(),
i: 0,
reps: int-1_u8
});
}
else if cmdstk.is_empty() {
break; }
None
}
else {
Some(format!("Cannot possibly repeat a macro {int} times"))
}
},
'm' => {
if reg.th.is_some() {
Some(format!("Register # {rnum} is already occupied by a thread"))
}
else {
let mut snap = if inv {
State::default() }
else {
State { mstk: st.mstk.clone(),
ro_buf: st.ro_buf.clone(),
rptr: None, par: st.par.clone(),
w: st.w,
regs: { let mut m: HashMap<Integer, Register> = HashMap::new();
for (ri, r) in st.regs.iter() {
m.insert(ri.clone(), Register {
v: r.v.clone(),
th: None
});
}
m
},
.. Default::default() }
};
let cmds = sa.clone();
let handle = th::spawn(move || {
let mut no_io = no_io!();
let _ = exec(&mut snap, &mut no_io, true, &cmds);
snap.mstk
});
st.regs.get_mut(&rnum).unwrap().th = Some(handle);
None
}
},
'M' => {
if reg.th.is_none() {
Some(format!("Register # {rnum} is not occupied by a thread"))
}
else {
let handle = reg.th.take().unwrap();
let timeout = Instant::now() + Duration::from_millis(round(na).to_u64().unwrap_or(u64::MAX));
loop {
if handle.is_finished() {
for o in handle.join().unwrap() { reg.v.push(RegObj {
a: Vec::new(),
o
});
}
break;
}
else if Instant::now()>=timeout { reg.th = Some(handle); break;
}
th::sleep(Duration::from_millis(1));
}
None
}
},
'q' => {
return Ok(Quit(st.rptr.clone().unwrap_or_default().to_i32().unwrap_or_default())); },
'Q' => {
let int = round(na);
if let Some(num) = int.to_usize() {
cmdstk.truncate(cmdstk.len().saturating_sub(num));
if cmdstk.is_empty() {
break; }
None
}
else {
Some(format!("Cannot possibly quit {int} levels"))
}
},
'?' => {
let mut prompt_in = String::new();
input.read_line(&mut prompt_in)?;
prompt_in = prompt_in.trim_end_matches(['\n','\r']).into();
if inv {
st.mstk.push(Str(prompt_in));
}
else {
if cmdstk.last().unwrap().exhausted() {
cmdstk.pop(); }
cmdstk.push(prompt_in.into());
}
None
},
'&' => {
if !safe {
match std::fs::read_to_string(sa.clone()) {
Ok(script) => {
let mut script_nc = String::new(); for line in script.split('\n') {
script_nc.push_str(line.split_once('#').unwrap_or((line,"")).0); script_nc.push('\n');
}
if inv {
st.mstk.push(Str(script_nc));
}
else {
if cmdstk.last().unwrap().exhausted() {
cmdstk.pop(); }
cmdstk.push(script_nc.into());
}
None
},
Err(err) => {
Some(format!("Unable to read file \"{sa}\": {err}"))
},
}
}
else {
Some("Disabled by --safe flag".into())
}
},
'$' => {
if !safe {
match std::env::var(sa.clone()) {
Ok(val) => {
st.mstk.push(Str(val));
None
},
Err(err) => {
Some(format!("Unable to get value of ${sa}: {err}"))
},
}
}
else {
Some("Disabled by --safe flag".into())
}
},
'\\' => {
if !safe {
if let Some((var, val)) = sa.split_once('=') { std::env::set_var(var, val);
None
}
else { let mut args: Vec<&str> = sa.trim().split(' ').collect();
match std::process::Command::new(args.remove(0)).args(args).spawn() {
Ok(mut child) => {
let stat = child.wait().unwrap();
match stat.code() {
Some(code) if code!=0 => {Some(format!("OS command \"{sa}\" exited with code {code}"))},
_ => None
}
},
Err(err) => {
Some(format!("Unable to execute OS command \"{sa}\": {err}"))
},
}
}
}
else {
Some("Disabled by --safe flag".into())
}
},
'#' => {
cmdstk.last_mut().unwrap().v.clear();
None
},
_ => {
if cmd.is_whitespace() || cmd=='\0' || cmd=='!' {None} else {Some(format!("\0Invalid command: {cmd} (U+{:04X})", cmd as u32))}
},
}
{
if let Some(e) = semerr.strip_prefix('\0') {
writeln!(error, "! {e}")?; }
else {
writeln!(error, "! {cmd}: {semerr}")?; }
match adi { 1 => {st.mstk.push(a);},
2 => {st.mstk.push(a); st.mstk.push(b);},
3 => {st.mstk.push(a); st.mstk.push(b); st.mstk.push(c);},
_ => {}
}
}
inv = false; if cmd=='!' {inv = true;}
if cmdstk.last().unwrap().at_end() { if cmdstk.last().unwrap().exhausted() { cmdstk.pop(); }
else {
cmdstk.last_mut().unwrap().repeat(); }
inv = false; }
}
Ok(Finished)
}