Skip to content

Commit

Permalink
stack roll
Browse files Browse the repository at this point in the history
  • Loading branch information
busstoptaktik committed Mar 14, 2024
1 parent 0b36c2e commit 5d65c5e
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 5 deletions.
28 changes: 26 additions & 2 deletions ruminations/002-rumination.md
Original file line number Diff line number Diff line change
Expand Up @@ -935,16 +935,17 @@ Note: Rust Geodesy does not support modifying the ellipsoid with an `R` paramete

### Operator `stack`

**Purpose:** Push/pop/swap coordinate dimensions onto the stack
**Purpose:** Push/pop/roll/swap coordinate dimensions onto the stack

**Description:**
Take a copy of one or more coordinate dimensions and push, pop or swap them onto the stack.
Take a copy of one or more coordinate dimensions and/or push, pop, roll or swap them onto the stack.


| Argument | Description |
|------------|--------------------------------------------|
| `push=...` | push a comma separated list of coordinate dimensions onto the stack |
| `pop=...` | pop a comma separated list of coordinate dimensions off the stack, into an operand |
| `roll=m,n` | On the sub-stack consisting of the m upper elements, roll n elements from the top, to the bottom of the sub-stack |
| `swap` | swap the top-of-stack and the next-to-top-of-stack |

The arguments to `push` and `pop` are handled from left to right, i.e. in latin reading order,
Expand All @@ -963,6 +964,29 @@ while the second will get that of the 2OS.

All in all, that amounts to a swapping of the first two coordinate elements of the operand.

#### `stack roll`

Essentially, `roll=m,n` is a [big swap](https://stackoverflow.com/a/15997537/618276), essentially
flipping the `n` upper elements with the `m - n` lower, as seen from these examples:

| Stack before | Instruction | Stack after |
|----------------|--------------------------------------------|
| 1,2,3,4 | roll=3,2 | 1,3,4,2 |
| 1,2,3,4 | roll=3,-2 | 1,3,4,2 |
| 1,3,4,2 | roll=3,1 | 1,2,3,4 |

Note that the last example shows that `roll=m,m-n` is the opposite of `roll=m,n`

#### Inverse operation

`stack` does not support the `inv` modifier. Instead use these substitutions:

| Forward | Inverse |
|---------|-----------|
| push | pop |
| pop | push |
| swap | swap |
| roll=m,n| roll=m,m-n|

**See also:** [`pop`](#operator-pop) (deprecated), [`push`](#operator-push) (deprecated)

Expand Down
102 changes: 99 additions & 3 deletions src/inner_op/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ pub fn new(parameters: &RawParameters, _ctx: &dyn Context) -> Result<Op, Error>
if roll_args.len() != 2
|| roll_args[0].fract() != 0.
|| roll_args[1].fract() != 0.
|| roll_args[0] < roll_args[1].abs()
|| roll_args[0] <= roll_args[1].abs()
{
return Err(Error::MissingParam(
"roll takes exactly two integer parameters".to_string(),
"roll takes exactly two integer parameters, ´(m,n): |n|<m´".to_string(),
));
}
params.text.insert("action", "roll".to_string());
Expand Down Expand Up @@ -124,6 +124,16 @@ pub(super) fn stack_fwd(
stack_pop(stack, operands, &args)
}

"roll" => {
let args: Vec<i64> = params
.series("roll")
.unwrap()
.iter()
.map(|i| *i as i64)
.collect();
stack_roll(stack, operands, &args)
}

"swap" => {
let n = stack.len();
if n > 1 {
Expand Down Expand Up @@ -179,6 +189,17 @@ pub(super) fn stack_inv(
return stack_push(stack, operands, &args);
}

"roll" => {
let mut args: Vec<i64> = params
.series("roll")
.unwrap()
.iter()
.map(|i| *i as i64)
.collect();
args[1] = -args[1];
stack_roll(stack, operands, &args)
}

"swap" => {
let n = stack.len();
if n > 1 {
Expand Down Expand Up @@ -222,6 +243,43 @@ fn stack_push(
number_of_operands
}

/// roll m,n: On the sub-stack consisting of the m upper elements,
/// roll n elements from the top, to the bottom of the sub-stack.
/// Hence, roll is a "large flip", essentially flipping the n upper
/// elements with the m - n lower
fn stack_roll(stack: &mut Vec<Vec<f64>>, operands: &mut dyn CoordinateSet, args: &[i64]) -> usize {
let m = args[0].abs();
let mut n = args[1];
let depth = stack.len();
//dbg!(&stack);
dbg!(&args);

// Negative n: count the number of rolled elements from the bottom,
// i.e. roll 3,-2 = roll 3,1
n = if n < 0 { m + n } else { n };

// The remaining becomes simpler if m, n and depth are all usize
let m = m as usize;
let n = n as usize;

if m > depth {
warn!("Roll too deep");
let nanny = Coor4D::nan();
for i in 0..operands.len() {
operands.set_coord(i, &nanny);
}
return 0;
}

for _ in 0..n {
let e = stack.pop().unwrap();
stack.insert(depth - m, e);
}
//dbg!(&stack);

operands.len()
}

/// Pop elements from the stack into elements of a CoordinateSet
fn stack_pop(stack: &mut Vec<Vec<f64>>, operands: &mut dyn CoordinateSet, args: &[usize]) -> usize {
let number_of_pops = args.len();
Expand Down Expand Up @@ -290,7 +348,7 @@ mod tests {
// use case, that would require code complication to disallow)
assert!(ctx.op("stack push=2,2,1,1 | stack pop=1,1,2").is_ok());

// ----- Three tests of the actual functionality -----
// ----- Four tests of the actual functionality -----

let mut data = master_data.clone();

Expand Down Expand Up @@ -342,6 +400,44 @@ mod tests {
assert_eq!(data[0], master_data[0]);
assert_eq!(data[1], master_data[1]);

// 4: Test the `roll` subcommand
let op = ctx.op("stack push=1,1,1,2,1,3,1,4 | stack roll=8,2 | stack pop=1,2")?;
ctx.apply(op, Fwd, &mut data)?;
assert_eq!(data[0][0], 13.);
assert_eq!(data[0][1], 11.);

// Then we do the inverse. We must, however, redo, since the push-pop asymmetry
// would otherwise wreak havoc:

// Just calling apply in the inverse direction leads to underflow:
assert_eq!(0, ctx.apply(op, Inv, &mut data)?);

// Instead, we must substitute (m,n) with (m,m-n)
let mut data = master_data.clone();
let op = ctx.op("stack push=1,2,3,4,1,2,3,4 | stack roll=8,6 | stack pop=1,2")?;
ctx.apply(op, Fwd, &mut data)?;
assert_eq!(data[0][0], 12.);
assert_eq!(data[0][1], 11.);

let mut data = master_data.clone();
let op = ctx.op("stack push=1,2,3,4,1,2,3,4 | stack roll=3,2 | stack pop=1,2")?;
ctx.apply(op, Fwd, &mut data)?;
assert_eq!(data[0][0], 12.);
assert_eq!(data[0][1], 14.);

let mut data = master_data.clone();
let op = ctx.op("stack push=1,2,3,4 | stack roll=3,-2 | stack pop=2,1")?;
ctx.apply(op, Fwd, &mut data)?;
assert_eq!(data[0][0], 12.);
assert_eq!(data[0][1], 13.);

// Roundrip roll
let mut data = master_data.clone();
let op = ctx.op("stack push=1,2,3,4 | stack roll=3,2 | stack roll=3,1 | stack pop=1,2")?;
ctx.apply(op, Fwd, &mut data)?;
assert_eq!(data[0][0], 14.);
assert_eq!(data[0][1], 13.);

Ok(())
}
}

0 comments on commit 5d65c5e

Please sign in to comment.