# Expand Sine and Cosine

Trigonometry has LOTS of identities. So many that you can expand most functions into sines and cosines of a few values. The task here is to do that in the fewest bytes possible.

# Identity list

Well, the ones we're using here.

sin(-x)=-sin(x)
sin(π)=0
cos(-x)=cos(x)
cos(π)=-1
sin(a+b)=sin(a)*cos(b)+sin(b)*cos(a)
cos(a+b)=cos(a)cos(b)-sin(a)sin(b)


For the sake of golfing, I've omitted the identities that can be derived from these. You are free to encode double-angles and such but it may cost you bytes.

# Input

• You should take in an expression as a string with an arbitrary number of terms consisting of a coefficient and some sine and cosine functions, each with an exponent and an arbitrary number of arguments.
• Coefficients will always be nonzero integers.
• Each argument will be a coefficient, followed by either a single latin letter or pi.
• You can decide whether to take in pi as pi or π. Either way remember that you're scored in bytes, not characters.

# Output

Output the same expression, but…

• All trig functions are either sin or cos.
• All arguments of trig functions are single variables, with no coefficients.
• All like terms are combined.
• Terms with a coefficient of 0 are removed.
• All factors in the same term that are the same function of the same variable are condensed into a single function with an exponent.

Note: Leading + signs are allowed, but not required. a+-b is allowed, and equivalent to a-b. If there are no terms with nonzero coefficients, then output either 0 or an empty string.

# Worked Example

We'll start with sin(-3x)+sin^3(x). The obvious first thing to do would be to deal with the sign using the parity identity, leaving -sin(3x). Next I can expand 3x into x+2x, and apply the sine additive identity recursively:

-(sin(x)cos(2x)+cos(x)sin(2x))+sin^3(x)
-(sin(x)cos(2x)+cos(x)(sin(x)cos(x)+sin(x)cos(x)))+sin^3(x)


Next, some distributive property and like terms:

-sin(x)cos(2x)-2cos^2(x)sin(x)+sin^3(x)


Now, I expand the cos(2x) and apply the same reduction:

-sin(x)(cos(x)cos(x)-sin(x)sin(x))-2cos^2(x)sin(x)+sin^3(x)
-sin(x)cos^2(x)+sin^3(x)-2cos^2(x)sin(x)+sin^3(x)
2sin^3(x)-3sin(x)cos^2(x)


And now, it's finished!

# Test Cases

In addition to the following, all of the individual identities (above) are test cases. Correct ordering is neither defined nor required.

cos(2x)+3sin^2(x) => cos^2(x)+2sin^2(x)
sin(-4x) => 4sin^3(x)cos(x)-4sin(x)cos^3(x)
cos(a+2b-c+3π) => 2sin(a)sin(b)cos(b)cos(c)-sin(a)sin(c)cos^2(b)+sin(a)sin(c)sin^2(b)-cos(a)cos^2(b)cos(c)+cos(a)sin^2(b)cos(c)-2cos(a)sin(b)cos(b)sin(c)
sin(x+674868986π)+sin(x+883658433π) => 0 (empty string works too)
sin(x+674868986π)+sin(x+883658434π) => 2sin(x)


…and may the shortest program in bytes win.

• May we output (for example) sin(x)^3 rather than sin^3(x)? Can we take pi as PI as well? Commented Apr 6, 2018 at 17:01
• This looks very close to a dup of codegolf.stackexchange.com/questions/38341/… Commented Apr 6, 2018 at 17:11
• @DigitalTrauma that seems to be about printing a specific answer set, not a generalized and specified input-processing challenge. Commented Apr 6, 2018 at 17:24
• cos(0) = cos(pi+(-pi)) = cos(pi)cos(-pi) - sin(pi)sin(-pi) = cos(pi)cos(pi) - 0 = (-1)^2 = 1. So, 1 = cos(0) = cos(x+(-x)) = cos(x)cos(-x) - sin(x)sin(-x) = cos(x)cos(x) + sin(x)sin(x) = cos^2(x) + sin^2(x). Commented Apr 6, 2018 at 17:51
• @NoOneIsHere yes, but I won't upvote it because it's zero-effort. Commented Apr 7, 2018 at 1:16

# Bracmat, 221 bytes

(P=F v a b.!arg:((sin|cos):?F.?v)&(!v:@&(!F.!v)|!F$!v)|!arg:%?a_%?b&(P$!a)_(P$!b)|!arg)&(f=i a b n u.1+(!arg:e^(?n*((i|-i):?i)*?u+?b)&(!i*(sin.!u)+(cos.!u))^!n*f$(e^!b)|!arg:%?a_%?b&(f$!a)_(f$!b)|!arg)+-1)&(Z=.f$(P$!arg))


Try it online!

• I have no idea what that's doing but it works... good answer on an otherwise-abandoned question! Commented Nov 8, 2019 at 20:25

# Python3, 841 bytes

Longer than I hoped, but the logic is straightforward.

E=enumerate
S,O='sin','cos'
def f(e):
e=[[(a,b,[([1,-1][j<0]*([1,j]['pi'==C]),C)for j,C in c for _ in range(1,abs(j)+1 if'pi'!=C else 2)],d)for a,b,c,d in i]for i in e]
P=1
while P:
P=0
for i,a in E(e):
F=0
for I,A in E(a):
if len(A[2])>1:q,*w=A[2];l=[[(A[0],S,[q],1),(1,O,w,1)],[(A[0],S,w,1),(1,O,[q],1)]]if S==A[1]else[[(A[0],O,[q],1),(1,O,w,1)],[(-1*A[0],S,[q],1),(1,S,w,1)]];l=[u+a[:I]+a[I+1:]for u in l];e=e[:i]+e[i+1:]+l;F=1;break
if F:P=1;break
r={}
for i in e:
R,T={},1
for a,b,[(C,V)],d in i:
if C==-1 and'sin'==b:a*=-1
if'pi'==V:
if(L:=[[1,-1][abs(C)%2],0][b==S])==0:R,T={},0;break
else:T*=L*a
else:R[(b,V)]=R.get((b,V),0)+d;T*=a
r[M]=r.get(M:=str(sorted(R.items())),0)+T
return' + '.join(str(b)+''.join(f'{j}{["^"+str(l),""][l==1]}({k})'for(j,k),l in eval(a))for a,b in r.items()if b)


Try it online!

• Without changing the algorithm, I get 814 chars. First half: E=enumerate S,O='sin','cos' def f(e): e=[[(a,b,[([1,-1][j<0]*[1,j]['pi'==C],C)for j,C in c for _ in range(1,1+abs(j)if'pi'!=C else 2)],d)for a,b,c,d in i]for i in e] P=1 while P: P=0 for i,a in E(e): for I,A in E(a): if len(A[2])>1:q,*w=A[2];l=[[(A[0],S,[q],1),(1,O,w,1)],[(A[0],S,w,1),(1,O,[q],1)]]if S==A[1]else[[(A[0],O,[q],1),(1,O,w,1)],[(-1*A[0],S,[q],1),(1,S,w,1)]];l=[u+a[:I]+a[I+1:]for u in l];e=e[:i]+e[i+1:]+l;P=1;break if P:break Commented Jan 26 at 0:50
• Second half: r={} for i in e: R,T={},1 for a,b,[(C,V)],d in i: if C==-1 and S==b:a*=-1 if'pi'==V: if(L:=(1-2*(abs(C)%2))*(b!=S))==0:R,T={},0;break else:T*=L*a else:R[(b,V)]=R.get((b,V),0)+d;T*=a r[M]=r.get(M:=str(sorted(R.items())),0)+T return' + '.join(str(b)+''.join(f'{j}{f"^{l}"*(l!=1)}({k})'for(j,k),l in eval(a))for a,b in r.items()if b) Commented Jan 26 at 0:51
• @movatica Thank you very much. I have added the essence of your golfs to the post, will complete it when time allows... Commented Jan 26 at 1:09
• Saving more bytes using exponent to replace conditionals: e=[[(a,b,[([1,-1][j<0]*j**('pi'==C),C)for j,C in c for _ in range(1,1+abs(j)**('pi'!=C))],d)for a,b,c,d in i]for i in e] Commented Jan 26 at 1:09
• ... and finally, -1 is a True-value in Python3, allowing to turn around the conditional: if(L:=(1-2*(abs(C)%2))*(b!=S)):T*=L*a else:R,T={},0;break Commented Jan 26 at 1:10