Visual Long Multiplication

There is a nice way to perform long multiplication for two integers without having to do anything but counting, which occasional gets shared around the internet. You write the digits of each number as a bunch of slanted lines, with the two numbers at a 90 degree angle. Then you can simply count the intersections in the separate columns that arise. A diagram will probably clarify this. Here is an example for calculating 21 * 32:

If you google for "visual/graphical long multiplication" you'll find a lot more examples.

In this challenge, you are to generate these diagrams using ASCII art. For the same example, the output would look like this:

   \ /
X /
\ / X /
\ X / X
X X / \ /
/ X X   X /
/ X \ / X
/ \ X / \
X X
/ X \
/ \


It's probably easiest to figure out the construction rules for these from some examples (see below), but here some details:

• Intersecting segments are X, non-intersecting segments of the lines are / or \.
• There should be exactly one segment after the outermost intersections.
• There should be exactly one segment between intersections belonging to different digits. If there are zero-digits, these will result in consecutive / or \ segments.
• You have to support any positive input (at least up to some reasonable limit like 216 or 232), and any digits from 0 to 9. However, you may assume that there neither leading nor trailing 0s.
• You must not print extraneous leading whitespace or leading or trailing empty lines.
• You may print trailing whitespace but it must not exceed the diagram's axis-aligned bounding box.
• You may optionally print a single trailing newline.
• You may choose in which order you take the two input numbers. However, it you must support arbitrary numbers for either orientation, so you can't choose something like "The larger number is given first".
• If you're taking input as a string, you can use any non-digit separator between the two numbers.

You may write a program or function, taking input via STDIN (or closest alternative), command-line argument or function argument and outputting the result via STDOUT (or closest alternative), function return value or function (out) parameter.

This is code golf, the shortest answer (in bytes) wins.

Examples

1*1
\ /
X
/ \

2*61
\ /
\ X /
X X /
/ X X /
/ X X /
/ X X /
/ X X
/ X \ /
/ \ X
X \
/ \

45*1
\ /
\ X
\ X \
\ X \
\ X \
X \
\ / \
\ X
\ X \
\ X \
X \
/ \

21001*209
\ /
X /
/ X
/ / \
\ / /   \ /
X /     X /
\ / X     / X /
\ X / \   / / X /
X X   \ / / / X /
/ X \   X / / / X /
/ \ \ / X / / / X /
\ X / X / / / X /
X X / X / / / X /
/ X X / X / / / X
/ X X / X / / / \
/ X X / X / /
/ X X / X /
/ X X / X
/ X X / \
/ X X
/ X \
/ \

• A function with 2 string parameters or just one single string and I have to split it in my code? Commented Jun 12, 2015 at 12:30
• @edc65 Two strings or even two integer parameters are fine. Commented Jun 12, 2015 at 12:39

python, 303

def f(s):
a,b=s.split('*')
a,b=map(lambda l:reduce(lambda x,y:x+[1]*int(y)+[0],l,[0]),[reversed(a),b])
n=sum(map(len,[a,b]))
l=[[' ']*n for i in xrange(n)]
for i,x in enumerate(a):
for j,y in enumerate(b):
l[i+j][j-i+len(a)]=r' \/x'[x+2*y]
return '\n'.join(''.join(x[2:-1]) for x in l[1:-2])


Verification:

print '---'
print '\n'.join('"%s"'%x for x in f('21001*209').split('\n'))
print '---'
---
"       \ /            "
"        x /           "
"       / x            "
"      / / \           "
"   \ / /   \ /        "
"    x /     x /       "
" \ / x     / x /      "
"\ x / \   / / x /     "
" x x   \ / / / x /    "
"/ x \   x / / / x /   "
" / \ \ / x / / / x /  "
"    \ x / x / / / x / "
"     x x / x / / / x /"
"    / x x / x / / / x "
"     / x x / x / / / \"
"      / x x / x / /   "
"       / x x / x /    "
"        / x x / x     "
"         / x x / \    "
"          / x x       "
"           / x \      "
"            / \       "
---

• Just a few quick golfs: reversed is the same as [::-1], you can put the contents of the for loop into one line to save on indentation, len(a)+len(b) is shorter than sum(map(len,[a,b])), don't use xrange in golfing, the space in ) for can be removed, and since you are using python2, you can combine spaces and tabs in indentation. Commented Jun 7, 2015 at 17:44
• Thanks much. These gives 22 bytes. But I don't think it would be the shortest. I don't code pyth but I've seen 31-byte programs... Btw, 303 is the count when every 4-spaces is replaced by a tab actually. Commented Jun 7, 2015 at 18:05
• Here, I was able to get 276 from simple syntactic golfing: gist.github.com/Maltysen/e8231c0a9b585e2a4941 Commented Jun 7, 2015 at 18:09
• Also, do you mind if I translate your program to Pyth and post it as a separate answer? Commented Jun 7, 2015 at 18:10
• You can set e=enumerate at the start to golf 4 chars Commented Jun 8, 2017 at 10:47

Python 3, 205 bytes

L=a,b=[eval("+[0]+[1]*".join("0%s0"%x)[2:])for x in input().split()]
A,B=map(len,L)
for c in range(2,A+B-1):print((" "*abs(c-A)+" ".join(" \/X"[a[i-c]+2*b[i]]for i in range(max(0,c-A),min(c,B))))[1:A+B-2])


The expressions are quite long so I think there's a fair amount of room for improvement, but anyway...

Takes input space-separated via STDIN, e.g.

21 32
\ /
X /
\ / X /
\ X / X
X X / \ /
/ X X   X /
/ X \ / X
/ \ X / \
X X
/ X \
/ \


There's a possible trailing space on some lines, but the A+B-2 ensures that all of the trailing spaces are within the bounding box.

Pyth - 79 bytes

A translation of @AlexeyBurdin's answer. Can probably be golfed a lot more.

AzHmu++Gm1sH]Zd]Z,_zwK+lzlHJmm\ KK .e.eX@J+kY+-Yklz@" \/x"+byZHzjbmjk:d2_1:J1_2


Takes input as two numbers, newline separated. Explanation coming soon.

C#, 451 bytes

void d(string s){var S=s.Split('*');int X=S[1].Select(c=>c-47).Sum(),Y=S[0].Select(c=>c-47).Sum(),L=9*(X+Y),A=1,B=L/3,i,j;var a=Range(0,L).Select(_=>new int[L]).ToArray();foreach(var c in S[1]){for(i=48;i<c;++i){for(j=-1;j<Y;++j)a[B-j][A+j]=1;A++;B++;}A++;B++;}A=1;B=L/3;foreach(var c in S[0]){for(i=48;i<c;++i){for(j=-1;j<X;++j)a[B+j][A+j]|=2;A++;B--;}A++;B--;}Write(Join("\n",a.Select(r=>Concat(r.Select(n=>@" /\X"[n]))).Where(r=>r.Trim().Any())));}


Formatted for readability, the function in context:

using System.Linq;
using static System.Console;
using static System.Linq.Enumerable;
using static System.String;

class VisualMultiply
{
static void Main(string[] args)
{
new VisualMultiply().d("21001*209");

WriteLine();
}

void d(string s)
{
var S = s.Split('*');

int X = S[1].Select(c => c - 47).Sum(),
Y = S[0].Select(c => c - 47).Sum(),
L = 9 * (X + Y),
A = 1,
B = L / 3,
i,
j;

var a = Range(0, L).Select(_ => new int[L]).ToArray();

foreach (var c in S[1])
{
for (i = 48; i < c; ++i)
{
for (j = -1; j < Y; ++j)
a[B - j][A + j] = 1;
A++;
B++;
}
A++;
B++;
}

A = 1;
B = L / 3;
foreach (var c in S[0])
{
for (i = 48; i < c; ++i)
{
for (j = -1; j < X; ++j)
a[B + j][A + j] |= 2;
A++;
B--;
}
A++;
B--;
}

Write(Join("\n", a.Select(r => Concat(r.Select(n => @" /\X"[n]))).Where(r => r.Trim().Any())));
}
}


The bitwise OR was just for fun, but addition would work too.

JavaScript (ES6) 271

I'm sure that there is a solution that builds the output row by row, fiddling with math and x,y coordinates (x+y==k, x-y==k ...). But I still can't nail it.

So here is a solution that simply draws the lines one by one.

Run the snippet in Firefox to test.

F=(a,b)=>( // string parameters
t=u=0,[for(v of a)t-=~v],[for(v of b)u-=~v],
r=t+u,o=[...' '.repeat(r*r-r)],
L=(x,y,n,z,m,c)=>{
for(i=0;d=n[i++];)
for(j=0;++x,y+=z,j++<d;)
for(l=m+1,p=x+y*r-1-r;l--;p+=r-z,o[p-p%r-1]='\n')
o[p]=o[p]>' '&&o[p]!=c?'X':c
},
L(u,0,a,1,u,'/'),
L(0,u,b,-1,t,'\\'),
o.join('')
)

// TEST

function test()
{
O.innerHTML= F(A.value, B.value);
}

test();
<input id=A value=21001> * <input id=B value=209> <button onclick='test()'>-></button>
<pre id=O></pre>

VC++ (289)280

t(char*a){int i,j,k,r,c=1,e,A=0,B=0,*C,G[99],f,u;for(C=&A;c+48|(k=(c=(*(a+=c<1))---48)>0);G[2**(C=!(c+6)?&(B+=A):&(++*C))]=k**C);for(i=0;i<B*B;printf("\n%c"+!!j,32+15*((k=(c<(f=G[(c=i/B)+(j=i%B)+A+2]))*(j<f)*(f>A))+4*(r=(!!e*B>c+(e=G[A+j-c]))*(!!e*c>A-e-2)*(e<A)))+13*k*r),i++);


Usage

#include  <stdio.h>
#include  <conio.h>

int t(char*);

int main(void)
{   char a[]="123*45";
t((char*)a);
getch();
return 0;
}

int
t(char*a){int i,j,k,r,c=1,e,A=0,B=0,*C,G[99],f,u;memset(G,0,396);for(C=&A;c+48|(k=(c=(*(a+=c<1))---48)>0);G[2**(C=!(c+6)?&(B+=A):&(++*C))]=k**C);for(i=0;i<B*B;printf("\n%c"+!!j,32+15*((k=(c<(f=G[(c=i/B)+(j=i%B)+A+2]))*(j<f)*(f>A))+4*(r=(!!e*B>c+(e=G[A+j-c]))*(!!e*c>A-e-2)*(e<A)))+13*k*r),i++);

//---------------------------
return 0;}


Results

       \ /
\ x /
\ x x /
x x x /
\ / x x x
\ x / x x \ /
x x / x \ x /
\ / x x / \ x x /
x / x x   x x x /
/ x / x \ / x x x /
/ x / \ x / x x x
/ x   x x / x x \
/ \ / x x / x \
x / x x / \
/ x / x x
/ x / x \
/ x / \
/ x
/ \

• the function iterates one single loop , and use some geometricks and ascii trifling.
• What's the ---48 for? Commented Jun 14, 2015 at 12:56
• @LegionMammal978 sometimes i write things then i forget even why did i put it there :D anyway im busy doing something else, when i finish , ill remember for you ; (does this code go well in your compilater) ? Commented Jun 14, 2015 at 13:12
• @LegionMammal978 here ,array content at specific (actual) index is subtracted to 48 before it decrements , subtracting 48 in order to wait a coming nil character then decrement step by step forward in loop (or backwards as ascii pattern) Commented Jun 14, 2015 at 15:52
• 48 is the ascii representation of "0" Commented Jun 14, 2015 at 15:55
• I see now, it works like ...)-- - 48).... Commented Jun 20, 2015 at 17:23

Canvas, 41 bytes

（Ａ┬｛［１｝０｝┐）
⁷Ｌ├Ｘ⁷ｌＹø；｛³×？³ｙ³－╵ｘ＼╋
⁸⤢↔╶╶⁸ｎ


Try it here!

C (329 b)

int f(char*a){int i,j,r,k,c,h,o,e=15,m=99,A=0,B=0,*C,L[m][m],G[m],*g=G;for(C=&A;(c=*a-48)+48;C=!(c+6)?&B:&(*C+=(*g++=c+1)),a++);for(i=B-1,j=0;j<(r=A+B-1);i--,j++)for(k=0,o=4*!!((*(g-=!*g))---1);k<=*(C=(h=i<0)?&B:&A);k++)L[abs(i)+k][j+k-2*k*h]+=o/(3*h+1)*e;for(i=0;i<r*r;i++)printf("\n%c"+!!(i%r),((h=L[i/r][i%r])>e*4)?120:h+32);}


TRY IT

• It seems there are columns of spaces after every character, and the final non-intersecting segments at the bottom ends are missing. You're also using the digits in reverse order. Commented Jun 9, 2015 at 15:46
• @MartinBüttner imagine someone is doing this on the moon and u watching it by telescope , that s the way you should perceive the diagram (-joking-i ll adjust that later) Commented Jun 9, 2015 at 16:52

R, 294 bytes

d=do.call;r=outer;m=d(r,c(Map(function(y,w)d(c,c(lapply(y%/%10^rev(0:log10(y))%%10,function(z)c(0,rep(w,z))),0)),scan(),1:2),+))+1;p=matrix(" ",u<-sum(dim(m)),u);p[d(r,c(lapply(dim(m),seq),function(a,b)nrow(m)-a+b+u*(a+b-2)))]=c(" ","\\","/","X")[m];cat(apply(p,1,paste,collapse=""),sep="\n")


Try it online!

Jelly, 58 bytes

ŒDṙLN‘ƊṚị“\/X ”K€
L‘0xż1xⱮ$F DÇ€Ḥ2¦+þ/µZJUṖ;J’⁶xⱮżÑṖḊẎḊ$€Y


Try it online!

Explanation

A full program that takes the two numbers as a list of two integers and returns a string.

Helper link 1: rotate the matrix

ŒD                        | get the diagonals
ṙ                       | rotate left by
LN‘Ɗ                   | minus num columns +1
Ṛ                  | reverse order
ị“\/X ”           | index into \/X
K€         | join each with spaces


Helper link 2: generate the row and column templates

L‘0x                      | 0 copied (num digits + 1) times
ż                     | interleaved with
1xⱮ$| 1 copied as specified by the digits F | flattened  Main link D | convert to decimal digits Ç€ | call above link for each number Ḥ2¦ | double the second one +þ/ | outer product using + µ | start a new monadic chain ZJUṖ;J’ | reversed range counting down | the columns, followed by range counting up the rows (without duplicating 0 in the middle) ⁶xⱮ | that many spaces (to provide indents) ż | interleaved with Ñ | rotated matrix ṖḊẎḊ$€ Y | remove blank rows and columns and join with newlines