use crate::core::area;
use crate::core::code::Code;
use crate::core::state::State;
use crate::number::number::Num;
use crate::util::error::Error;
use crate::util::io::ReadLine;
use crate::util::{io, util};
use std::io::Write;
use std::process;
pub fn push_stack_wrap<T>(
out: &mut impl Write,
err: &mut impl Write,
state: &mut T,
idx: usize,
num: Num,
) -> Result<(), Error>
where
T: State,
{
match idx {
1 => {
if num.is_pos() {
write!(out, "{}", util::num_to_unicode(&num)?)?;
} else {
write!(out, "{}", -&num)?;
}
}
2 => {
if num.is_pos() {
write!(err, "{}", util::num_to_unicode(&num)?)?;
} else {
write!(err, "{}", -&num)?;
}
}
_ => state.push_stack(idx, num),
}
Ok(())
}
pub fn pop_stack_wrap<T>(
ipt: &mut impl ReadLine,
out: &mut impl Write,
err: &mut impl Write,
state: &mut T,
idx: usize,
) -> Result<Num, Error>
where
T: State,
{
match idx {
0 => {
if state.get_stack(0).is_empty() {
let s = io::read_line_from(ipt)?;
for c in s.chars().rev() {
state.push_stack(0, Num::from_num(c as isize));
}
}
Ok(state.pop_stack(0))
}
1 => {
out.flush().unwrap();
err.flush().unwrap();
process::exit(0);
}
2 => {
out.flush().unwrap();
err.flush().unwrap();
process::exit(1);
}
_ => Ok(state.pop_stack(idx)),
}
}
pub fn execute_one<T>(
ipt: &mut impl ReadLine,
out: &mut impl Write,
err: &mut impl Write,
mut state: T,
cur_loc: usize,
) -> Result<(T, usize), Error>
where
T: State,
{
let code = (*state.get_code(cur_loc)).clone();
let mut cur_stack = state.current_stack();
match code.get_type() {
0 => {
push_stack_wrap(
out,
err,
&mut state,
cur_stack,
&Num::from_num(code.get_hangul_count() as isize)
* &Num::from_num(code.get_dot_count() as isize),
)?;
}
1 => {
let mut n = Num::zero();
for _ in 0..code.get_hangul_count() {
n += &pop_stack_wrap(ipt, out, err, &mut state, cur_stack)?;
}
push_stack_wrap(out, err, &mut state, code.get_dot_count(), n)?;
}
2 => {
let mut n = Num::one();
for _ in 0..code.get_hangul_count() {
n *= &pop_stack_wrap(ipt, out, err, &mut state, cur_stack)?;
}
push_stack_wrap(out, err, &mut state, code.get_dot_count(), n)?;
}
3 => {
let mut n = Num::zero();
let mut v = Vec::with_capacity(code.get_hangul_count());
for _ in 0..code.get_hangul_count() {
v.push(pop_stack_wrap(ipt, out, err, &mut state, cur_stack)?);
}
v.reverse();
for mut x in v {
x.minus();
n += &x;
push_stack_wrap(out, err, &mut state, cur_stack, x)?;
}
push_stack_wrap(out, err, &mut state, code.get_dot_count(), n)?;
}
4 => {
let mut n = Num::one();
let mut v = Vec::with_capacity(code.get_hangul_count());
for _ in 0..code.get_hangul_count() {
v.push(pop_stack_wrap(ipt, out, err, &mut state, cur_stack)?);
}
v.reverse();
for mut x in v {
x.flip();
n *= &x;
push_stack_wrap(out, err, &mut state, cur_stack, x)?;
}
push_stack_wrap(out, err, &mut state, code.get_dot_count(), n)?;
}
_ => {
let n = pop_stack_wrap(ipt, out, err, &mut state, cur_stack)?;
for _ in 0..code.get_hangul_count() {
push_stack_wrap(out, err, &mut state, code.get_dot_count(), n.clone())?;
}
push_stack_wrap(out, err, &mut state, cur_stack, n)?;
state.set_current_stack(code.get_dot_count());
}
}
cur_stack = state.current_stack();
let area_type = area::calc(code.get_area(), code.get_area_count(), || {
pop_stack_wrap(ipt, out, err, &mut state, cur_stack)
})?;
if area_type != 0 {
if area_type != 13 {
let id = ((code.get_area_count() as u128) << 4) + area_type as u128;
match state.get_point(id) {
Some(value) => {
if cur_loc != value {
state.set_latest_loc(cur_loc);
return Ok((state, value));
}
}
None => state.set_point(id, cur_loc),
}
} else {
if let Some(loc) = state.get_latest_loc() {
return Ok((state, loc));
}
}
}
Ok((state, cur_loc + 1))
}
pub fn execute<T>(
ipt: &mut impl ReadLine,
out: &mut impl Write,
err: &mut impl Write,
mut state: T,
code: &T::CodeType,
) -> Result<T, Error>
where
T: State,
{
let mut cur_loc = state.push_code((*code).clone());
let length = cur_loc + 1;
while cur_loc < length {
let (new_state, new_loc) = execute_one(ipt, out, err, state, cur_loc)?;
state = new_state;
cur_loc = new_loc;
}
Ok(state)
}