# Toroidally magnify-blur a matrix

Given a matrix of numbers $$\M\$$ with $$\r\$$ rows and $$\c\$$ columns, and the magnification factor $$\n\$$, build the matrix with $$\rn\$$ rows and $$\cn\$$ columns where the original elements are spaced $$\n\$$ units apart and the gaps are filled by piecewise linear interpolation:

$$\begin{bmatrix} a_{11} & a_{12} & \cdots \\ a_{21} & a_{22} & \cdots \\ \vdots & \vdots & \ddots \\ \end{bmatrix} \Rightarrow \begin{bmatrix} a_{11} & \frac{(n-1)a_{11} + a_{12}}{n} & \cdots & a_{12} & \cdots \\ \frac{(n-1)a_{11} + a_{21}}{n} & \frac{(n-1) \frac{(n-1)a_{11} + a_{21}}{n} + \frac{(n-1)a_{12} + a_{22}}{n}}{n} & \cdots & \frac{(n-1)a_{12} + a_{22}}{n} & \cdots \\ \vdots & \vdots & \ddots & \vdots \\ a_{21} & \frac{(n-1)a_{21} + a_{22}}{n} & \cdots & a_{22} & \cdots \\ \vdots & \vdots & & \vdots & \ddots \\ \end{bmatrix}$$

Because the operation is toroidal, the "gaps" between the $$\r\$$-th row and the 1st row (resp. the $$\c\$$-th column and the 1st column) must also be filled, which is placed below the original elements of the $$\r\$$-th row (resp. on the right side of the $$\c\$$-th column).

You can take $$\M\$$ and $$\n\$$ (and optionally $$\r\$$ and $$\c\$$) as input and output the resulting matrix in any suitable format. $$\n\$$ is guaranteed to be a positive integer. The input matrix and the result may have non-integers.

Standard rules apply. The shortest code in bytes wins.

## Test cases

# one-element matrix
M = [[1]], n = 3
[[1, 1, 1],
[1, 1, 1],
[1, 1, 1]]

# one-element matrix, large n
M = [[1]], n = 100
(100-by-100 matrix of ones)

# one-row matrix
M = [[0, 6, 3, 6]], n = 3
[[0, 2, 4, 6, 5, 4, 3, 4, 5, 6, 4, 2],
[0, 2, 4, 6, 5, 4, 3, 4, 5, 6, 4, 2],
[0, 2, 4, 6, 5, 4, 3, 4, 5, 6, 4, 2]]

# one-column matrix
M = [[0], [6], [3], [6]], n = 3
(transpose of the above)

# n = 1
M = [[1, 9, 8, 3],
[5, 4, 2, 7],
[3, 8, 5, 1]], n = 1
(same as M)

# 2-by-2 matrix; here the result is rounded to 2 decimal places for convenience.
# An answer doesn't need to round them, though one may choose to do so.
M = [[0, 9],
[3, 6]], n = 3
[[0, 3,    6,    9, 6,    3],
[1, 3.33, 5.67, 8, 5.67, 3.33],
[2, 3.67, 5.33, 7, 5.33, 3.67],
[3, 4,    5,    6, 5,    4],
[2, 3.67, 5.33, 7, 5.33, 3.67],
[1, 3.33, 5.67, 8, 5.67, 3.33]]

# a larger test case
M = [[0, 25, 0],
[25, 0, 0],
[0, 0, 25]], n = 5
[[0, 5, 10, 15, 20, 25, 20, 15, 10, 5, 0, 0, 0, 0, 0],
[5, 8, 11, 14, 17, 20, 16, 12, 8, 4, 0, 1, 2, 3, 4],
[10, 11, 12, 13, 14, 15, 12, 9, 6, 3, 0, 2, 4, 6, 8],
[15, 14, 13, 12, 11, 10, 8, 6, 4, 2, 0, 3, 6, 9, 12],
[20, 17, 14, 11, 8, 5, 4, 3, 2, 1, 0, 4, 8, 12, 16],
[25, 20, 15, 10, 5, 0, 0, 0, 0, 0, 0, 5, 10, 15, 20],
[20, 16, 12, 8, 4, 0, 1, 2, 3, 4, 5, 8, 11, 14, 17],
[15, 12, 9, 6, 3, 0, 2, 4, 6, 8, 10, 11, 12, 13, 14],
[10, 8, 6, 4, 2, 0, 3, 6, 9, 12, 15, 14, 13, 12, 11],
[5, 4, 3, 2, 1, 0, 4, 8, 12, 16, 20, 17, 14, 11, 8],
[0, 0, 0, 0, 0, 0, 5, 10, 15, 20, 25, 20, 15, 10, 5],
[0, 1, 2, 3, 4, 5, 8, 11, 14, 17, 20, 16, 12, 8, 4],
[0, 2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 12, 9, 6, 3],
[0, 3, 6, 9, 12, 15, 14, 13, 12, 11, 10, 8, 6, 4, 2],
[0, 4, 8, 12, 16, 20, 17, 14, 11, 8, 5, 4, 3, 2, 1]]

• "an answer shouldn't round the values" - shouldn't or doesn't need to? Perhaps specify a minimum acceptable decimal precision? Jun 15, 2020 at 10:09
• @Shaggy Doesn't need to, my mistake. Of course any programming language will try to round to some places before printing it. Jun 15, 2020 at 10:12

# APL (Dyalog Extended), 27 26 bytes

-1 thanks to Bubbler.

Anonymous infix lambda. Takes $$\n\$$ as left argument and $$\M\$$ as right argument.

{⊃+⌿,(⍳⍺)⍉⍤⌽\⍣2⊂⍺/⍺⌿⍵÷⍺*2}


Try it online! (the $$\n=100\$$ case run out of memory on TIO gives by default, but works offline)

{} "dfn"; ⍺ is $$\n\$$ and ⍵ is $$\M\$$

⍺*2$$\n^2\$$

⍵÷$$\M\over n^2\$$

⍺⌿ replicate vertically so each row becomes $$\n\$$ copies

⍺/ replicate horizontally so each column becomes $$\n\$$ copies

⊂ enclose to work on entire matrix

(⍳⍺)⍣2 do the following twice, each time with the $$\0,1…n-1\$$ as left argument:

\ outer "product" using the following tacit function instead of multiplication:

⌽ cyclically rotate the rows by the indices

⍤ then:

⍉ transpose

, flatten

+⌿ sum

⊃ disclose (since the summation reduced rank from 1 to 0 by enclosing)

• I guess ⍉⍤⌽ should save a byte? Jun 15, 2020 at 10:46
• @Bubbler Yes, thanks.
Jun 15, 2020 at 10:48
• "with the 0, 2, ..., n-1 as left argument:" has a typo
– RGS
Jun 15, 2020 at 11:02
• @RGS Yes, thanks.
Jun 15, 2020 at 11:02

# J, 22 bytes

(1#.&|:<@[1&|.[#%~)^:2


Try it online!

The "blur" is separable so operate in two passes where each pass operates on the rows and transposes its results.

## Mathematica, 63?

I can't remember the rules regarding non-ASCII characters but it looks like they are in play.

ListCorrelate[##/n^2&[n-Abs[n-Range[2n-1]]],Upsample[m,n,n],1]


 is short notation for TensorProduct.

# R, 979291 86 bytes

function(m,n,[=apply)m[1,h][1,h<-function(i)approxfun(c(i,i))(0:(n*sum(1|i)-1)/n+1)]


Try it online!

Thanks to Giuseppe for -5 bytes.

• I've never seen approx before. Right tool for the job. You can get down to 92 bytes. Jun 15, 2020 at 18:04
• Thanks, I was pretty sure there must be something for this in base R, but I actually hoped it would support 2D interpolation at once :) Jun 15, 2020 at 18:09

# Charcoal, 36 bytes

ＩＥ×ηＬθＥ×ηＬ§θ⁰∕ΣＥη∕ΣＥη§§θ÷⁺ινη÷⁺λπηηη


Try it online! Link is to verbose version of code. Explanation:

Ｉ


Cast the final array to string for implicit print (each row's cells print vertically and rows are double-spaced).

Ｅ×ηＬθ


Loop over each row of the final array.

Ｅ×ηＬ§θ⁰


Loop over each column of the final array.

∕ΣＥη∕ΣＥη§§θ÷⁺ινη÷⁺λπηηη


Extract an n-by-n square from a virtual array created by a simple inflation of the original array, where the top left of the square is at the final row and column. Cyclic indexing ensures that the square wraps around toroidally. The average of the elements is then taken.

# Python 2, 109 bytes

M,n=input()
exec"M=[[i%n*((r*2)[i/n+1]-r[i/n])/n+r[i/n]for i in range(len(r)*n)]for r in zip(*M)];"*2
print M


Try it online!

# JavaScript (ES10), 170 bytes

Takes input as (m)(n).

m=>n=>(T=m=>m.map((r,y)=>r.map((v,x)=>(M[x]=M[x]||[])[y]=v),M=[])&&M)((g=m=>m.map((r,y)=>r.flatMap((v,x)=>[...Array(n)].map((_,i)=>v+(r[-~x%r.length]-v)*i/n))))(T(g(m))))


Try it online! (with formatted outputs for readability)