@@ -53,10 +53,10 @@ pub fn new(parameters: &RawParameters, _ctx: &dyn Context) -> Result<Op, Error>
53
53
if roll_args. len ( ) != 2
54
54
|| roll_args[ 0 ] . fract ( ) != 0.
55
55
|| roll_args[ 1 ] . fract ( ) != 0.
56
- || roll_args[ 0 ] < roll_args[ 1 ] . abs ( )
56
+ || roll_args[ 0 ] <= roll_args[ 1 ] . abs ( )
57
57
{
58
58
return Err ( Error :: MissingParam (
59
- "roll takes exactly two integer parameters" . to_string ( ) ,
59
+ "roll takes exactly two integer parameters, ´(m,n): |n|<m´ " . to_string ( ) ,
60
60
) ) ;
61
61
}
62
62
params. text . insert ( "action" , "roll" . to_string ( ) ) ;
@@ -124,6 +124,16 @@ pub(super) fn stack_fwd(
124
124
stack_pop ( stack, operands, & args)
125
125
}
126
126
127
+ "roll" => {
128
+ let args: Vec < i64 > = params
129
+ . series ( "roll" )
130
+ . unwrap ( )
131
+ . iter ( )
132
+ . map ( |i| * i as i64 )
133
+ . collect ( ) ;
134
+ stack_roll ( stack, operands, & args)
135
+ }
136
+
127
137
"swap" => {
128
138
let n = stack. len ( ) ;
129
139
if n > 1 {
@@ -179,6 +189,17 @@ pub(super) fn stack_inv(
179
189
return stack_push ( stack, operands, & args) ;
180
190
}
181
191
192
+ "roll" => {
193
+ let mut args: Vec < i64 > = params
194
+ . series ( "roll" )
195
+ . unwrap ( )
196
+ . iter ( )
197
+ . map ( |i| * i as i64 )
198
+ . collect ( ) ;
199
+ args[ 1 ] = -args[ 1 ] ;
200
+ stack_roll ( stack, operands, & args)
201
+ }
202
+
182
203
"swap" => {
183
204
let n = stack. len ( ) ;
184
205
if n > 1 {
@@ -222,6 +243,43 @@ fn stack_push(
222
243
number_of_operands
223
244
}
224
245
246
+ /// roll m,n: On the sub-stack consisting of the m upper elements,
247
+ /// roll n elements from the top, to the bottom of the sub-stack.
248
+ /// Hence, roll is a "large flip", essentially flipping the n upper
249
+ /// elements with the m - n lower
250
+ fn stack_roll ( stack : & mut Vec < Vec < f64 > > , operands : & mut dyn CoordinateSet , args : & [ i64 ] ) -> usize {
251
+ let m = args[ 0 ] . abs ( ) ;
252
+ let mut n = args[ 1 ] ;
253
+ let depth = stack. len ( ) ;
254
+ //dbg!(&stack);
255
+ dbg ! ( & args) ;
256
+
257
+ // Negative n: count the number of rolled elements from the bottom,
258
+ // i.e. roll 3,-2 = roll 3,1
259
+ n = if n < 0 { m + n } else { n } ;
260
+
261
+ // The remaining becomes simpler if m, n and depth are all usize
262
+ let m = m as usize ;
263
+ let n = n as usize ;
264
+
265
+ if m > depth {
266
+ warn ! ( "Roll too deep" ) ;
267
+ let nanny = Coor4D :: nan ( ) ;
268
+ for i in 0 ..operands. len ( ) {
269
+ operands. set_coord ( i, & nanny) ;
270
+ }
271
+ return 0 ;
272
+ }
273
+
274
+ for _ in 0 ..n {
275
+ let e = stack. pop ( ) . unwrap ( ) ;
276
+ stack. insert ( depth - m, e) ;
277
+ }
278
+ //dbg!(&stack);
279
+
280
+ operands. len ( )
281
+ }
282
+
225
283
/// Pop elements from the stack into elements of a CoordinateSet
226
284
fn stack_pop ( stack : & mut Vec < Vec < f64 > > , operands : & mut dyn CoordinateSet , args : & [ usize ] ) -> usize {
227
285
let number_of_pops = args. len ( ) ;
@@ -290,7 +348,7 @@ mod tests {
290
348
// use case, that would require code complication to disallow)
291
349
assert ! ( ctx. op( "stack push=2,2,1,1 | stack pop=1,1,2" ) . is_ok( ) ) ;
292
350
293
- // ----- Three tests of the actual functionality -----
351
+ // ----- Four tests of the actual functionality -----
294
352
295
353
let mut data = master_data. clone ( ) ;
296
354
@@ -342,6 +400,44 @@ mod tests {
342
400
assert_eq ! ( data[ 0 ] , master_data[ 0 ] ) ;
343
401
assert_eq ! ( data[ 1 ] , master_data[ 1 ] ) ;
344
402
403
+ // 4: Test the `roll` subcommand
404
+ let op = ctx. op ( "stack push=1,1,1,2,1,3,1,4 | stack roll=8,2 | stack pop=1,2" ) ?;
405
+ ctx. apply ( op, Fwd , & mut data) ?;
406
+ assert_eq ! ( data[ 0 ] [ 0 ] , 13. ) ;
407
+ assert_eq ! ( data[ 0 ] [ 1 ] , 11. ) ;
408
+
409
+ // Then we do the inverse. We must, however, redo, since the push-pop asymmetry
410
+ // would otherwise wreak havoc:
411
+
412
+ // Just calling apply in the inverse direction leads to underflow:
413
+ assert_eq ! ( 0 , ctx. apply( op, Inv , & mut data) ?) ;
414
+
415
+ // Instead, we must substitute (m,n) with (m,m-n)
416
+ let mut data = master_data. clone ( ) ;
417
+ let op = ctx. op ( "stack push=1,2,3,4,1,2,3,4 | stack roll=8,6 | stack pop=1,2" ) ?;
418
+ ctx. apply ( op, Fwd , & mut data) ?;
419
+ assert_eq ! ( data[ 0 ] [ 0 ] , 12. ) ;
420
+ assert_eq ! ( data[ 0 ] [ 1 ] , 11. ) ;
421
+
422
+ let mut data = master_data. clone ( ) ;
423
+ let op = ctx. op ( "stack push=1,2,3,4,1,2,3,4 | stack roll=3,2 | stack pop=1,2" ) ?;
424
+ ctx. apply ( op, Fwd , & mut data) ?;
425
+ assert_eq ! ( data[ 0 ] [ 0 ] , 12. ) ;
426
+ assert_eq ! ( data[ 0 ] [ 1 ] , 14. ) ;
427
+
428
+ let mut data = master_data. clone ( ) ;
429
+ let op = ctx. op ( "stack push=1,2,3,4 | stack roll=3,-2 | stack pop=2,1" ) ?;
430
+ ctx. apply ( op, Fwd , & mut data) ?;
431
+ assert_eq ! ( data[ 0 ] [ 0 ] , 12. ) ;
432
+ assert_eq ! ( data[ 0 ] [ 1 ] , 13. ) ;
433
+
434
+ // Roundrip roll
435
+ let mut data = master_data. clone ( ) ;
436
+ let op = ctx. op ( "stack push=1,2,3,4 | stack roll=3,2 | stack roll=3,1 | stack pop=1,2" ) ?;
437
+ ctx. apply ( op, Fwd , & mut data) ?;
438
+ assert_eq ! ( data[ 0 ] [ 0 ] , 14. ) ;
439
+ assert_eq ! ( data[ 0 ] [ 1 ] , 13. ) ;
440
+
345
441
Ok ( ( ) )
346
442
}
347
443
}
0 commit comments