Numerical rot

Quick followup on previous notes. It's possible to generate a rotation matrix in K without any list appending functions. No idea if it's faster, and don't particularly care, but this way just feels righter.

I rewrote rots to be a matrix of indices that can be projected across a real array, rather than a direct rotation of it. rots[x;y] generates y rows of rightward rotations of a x-sized array. I also made an inclusive rotsi function which leaves the first row unchanged. Maybe that one is easier to visualize.

 rotsi[5;6]

0 1 2 3 4
4 0 1 2 3
3 4 0 1 2
2 3 4 0 1
1 2 3 4 0
0 1 2 3 4

 rots[5;6]

4 0 1 2 3
3 4 0 1 2
2 3 4 0 1
1 2 3 4 0
0 1 2 3 4
4 0 1 2 3

rotsi is implemented in terms of rots, which is implemented with the enumeration ! operator.

Since the resulting matrix will be y×x items long, it makes sense to begin by enumerating that many.

 {!x*y}[5;6]

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

Then cut it into y rows.

 {y^!x*y}[5;6]

0 1 2 3 4     
5 6 7 8 9     
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
25 26 27 28 29

The shape is there. A modulo operation against x will give the indices of 0…x-1. K has a builtin mod function, but it gives me weird errors when currying, so here's my (stackoverflow's) version.

 md:{y-x*_y%x }

 {(md[x])'y^!x*y}[5;6]

0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4

Getting there, just gotta spin it. The order of each row is the same at the moment, but it wouldn't be if we added a different amount to each one before the modulo. There are six rows, so what about adding 0…5?

 {(md[x])'(!y)+y^!x*y}[5;6]

0 1 2 3 4
1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3
0 1 2 3 4

Almost. Would work if the matrix were a perfect square. Try it with a larger offset of 1…6.

 {(md[x])'(1+!y)+y^!x*y}[5;6]

1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3
0 1 2 3 4
1 2 3 4 0

Definitely spinning now, just not in the right order. Generalized, we need to add an iteration vector of the difference between x and y to apply the correct offset.

 {(md[x])'((x-y)+!y)+y^!x*y}[5;4]

1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3

Reversing this offset vector changes the rotation direction.

 rots:{(md[x])'(|(x-y)+!y)+y^!x*y};rots[5;6]

4 0 1 2 3
3 4 0 1 2
2 3 4 0 1
1 2 3 4 0
0 1 2 3 4
4 0 1 2 3

The inclusive rotation matrix is self-explanatory.

 rotsi:{(,!x),rots[x;y-1]};rotsi[5;6]

0 1 2 3 4
4 0 1 2 3
3 4 0 1 2
2 3 4 0 1
1 2 3 4 0
0 1 2 3 4

Update

  1. The order of arguments should be flipped to allow for better currying.
  2. Both left/right rotations can be implemented from a cross summation of arrays +/:\:
  3. rots and rotsi should be general functions where a negative x rotates left, and a positive right.
  4. There should be a direct array rotation function rot.

Observe.

 rotsli:{md[y]'(!x)+/:\:!y}
 rotsl:{1_rotsli[x+1;y]}
 rotsr:{md[y]'(|(y-x)+!x)+/:\:!y}
 rotsri:{(,!y),rotsr[x-1;y]}
 rots:{$[x<0;rotsl[abs x;y];rotsr[x;y]]}
 rotsi:{$[x<0;rotsli[abs x;y];rotsri[x;y]]}
 rot:{y[,/((abs x)-1)_(rots[x;#y])]}

 rotsi[-8;5]

0 1 2 3 4
1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3
0 1 2 3 4
1 2 3 4 0
2 3 4 0 1

 rotsi[8;5]

0 1 2 3 4
4 0 1 2 3
3 4 0 1 2
2 3 4 0 1
1 2 3 4 0
0 1 2 3 4
4 0 1 2 3
3 4 0 1 2

 xs:"hello"

 rot[-1;xs]

"elloh"

 rot[1;xs]

"ohell"

See my kool repo for these procedures, among others.