Python to Rust - Babylonian confusion

I found a function on Rosseta Code that interested me:

#!/usr/bin/env python                                                                            
from collections import deque

def ack_ix(m, n):
    stack = deque([])
    stack.extend([m, n])
    while len(stack) > 1:
        n, m = stack.pop(), stack.pop()
        if m == 0:
            stack.append(n + 1)
        elif m == 1:
            stack.append(n + 2)
        elif m == 2:
            stack.append(2 * n + 3)
        elif m == 3:
            stack.append(2 ** (n + 3) - 3)
        elif n == 0:
            stack.extend([m - 1, 1])
        else:
            stack.extend([m - 1, m, n - 1])
    return stack[0]

print(ack_ix(3, 5))

My attempts to port it to Rust were not successful:

fn rosza3(m: u32, n: u32) -> u32 {
    let mut stack: Vec<u32> = Vec::from([m, n]);
    while stack.len() > 1 {
        let (n, m) = (stack.pop().unwrap(), stack.pop().unwrap());
        match (m, n) {
            (0, _) => stack.push(n + 1),
            (1, _) => stack.push(n + 2),
            (2, _) => stack.push(2 * n + 3),
            (3, _) => stack.push((n + 3).pow(2) - 3),
            (_, 0) => [m - 1, 1].iter().for_each(|i| stack.push(*i)),
            _ => [m - 1, m, n - 1].iter().for_each(|i| stack.push(*i)),
        }
    }
    stack[0]
}
fn rosza2(m: u32, n: u32) -> u32 {
    let mut stack: Vec<u32> = Vec::from([m, n]);
    while stack.len() > 1 {
        let (n, m) = (stack.pop().unwrap(), stack.pop().unwrap());
        if m == 0 {
            stack.push(n + 1);
        } else if m == 1 {
            stack.push(n + 2);
        } else if m == 2 {
            stack.push(2 * n + 3);
        } else if m == 3 {
            stack.push((n + 3).pow(2) - 3);
        } else if n == 0 {
            [m - 1, 1].iter().for_each(|i| stack.push(*i));
        } else {
            [m - 1, m, n - 1].iter().for_each(|i| {
                stack.push(*i);
            });
        }
    }
    stack[0]
}

The result is wrong - it must be 253 for let (m, n) = (3, 5); and not 61. I don't see the error at the moment.

P.S.: This is the ubiquitous working recursive solution:

fn rosza(m: u32, n: u32) -> u32 {
    match (m, n) {
        (0, n) => n + 1,
        (m, 0) => rosza(m - 1, 1),
        (m, n) => rosza(m - 1, rosza(m, n - 1)),
    }
}

2^(n+3) != (n+3)^2

Or without unwraps:

2 Likes

Not related to why it doesn't work, but this can also be written as stack.extend([m - 1, 1]).

2 Likes

Ouch! Yes, thanks.

Yeah cool. I thought it wouldn't work and didn't try.