# Taylor Series of a Function with Periodic Derivatives

Taylor series are a very useful tool in calculating values of analytic functions that cannot be expressed in terms of elementary functions, using only information about that function at a single point.

In this challenge, you won't be actually doing any math with them, but merely making string representations of taylor series of functions with periodic derivatives.

# Specs

You will takes a input three things:

1. A cycle of the derivatives of the function, including the original function as a list of strings, e.g. ["sin(x)", "cos(x)", "-sin(x)", "-cos(x)"].
2. A number a, the point around which to center the approximation.
3. A positive integer n, the number of terms of the series to output.

The general form for the nth term of a taylor series is:

f^(n)(a)/n!*(x-a)^n


where f^(n) is the nth derivative of f. Here is the taylor series of f in a better formatted image from wikipedia:

.

The above example with sin(x), and a of 1, and n of 6, would have an output of:

sin(1) + cos(1)*(x-1) - sin(1)/2*(x-1)^2 - cos(1)/6*(x-1)^3 + sin(1)/24*(x-1)^4 + cos(1)/120*(x-1)^5


You can do the whitespace want. However, you have to replace + - and - - with - and + respectively. Also, if the series is a maclaurin series, and a is 0, you cannot have (x-0). (x) and just x is fine, but not (x-0). Niether can you have ^0 nor ^1 nor /1 . Additionally, you must expand out the factorials in the denominators. Multiplication by juxtaposition is not allowed.

# Test cases

["e^x"], 0, 4  ->  e^0 + e^0*x + e^0*x^2/2 + e^0*x^3/6
["sin(x)", "cos(x)", "-sin(x)", "-cos(x)"], 1, 6 -> sin(1) + cos(1)*(x-1) - sin(1)/2*(x-1)^2 - cos(1)/6*(x-1)^3 + sin(1)/24*(x-1)^4 + cos(1)/120*(x-1)^5
["e^(i*x)", "i * e^(i*x)", "-e^(i*x)", "-i*e^(i*x)"], 0, 7 -> e^(i*0) + i*e^(i*0)*x - e^(i*0)*x^2/2 - i*e^(i*0)*x^3/6 + e^(i*0)*x^4/24 + i*e^(i*0)*x^5/120 - e^(i*0)*x^6/720


This is , so shortest code in bytes wins!

• Are built ins allowed?
– user45941
Commented Apr 25, 2016 at 3:41
• @Mego sure, if you can find one that doesn't evaluate the function and only does string manipulation. Commented Apr 25, 2016 at 4:16
• What about something like this?
– user45941
Commented Apr 25, 2016 at 4:50
• Additionally, can ** be used in place of ^?
– user45941
Commented Apr 25, 2016 at 5:25
• May we assume that the derivatives only start with nothing or a single minus sign if they're negative?
– orlp
Commented Apr 25, 2016 at 10:51

## JavaScript (ES6), 183 bytes

(c,a,n)=>[...Array(n)].map((_,i)=>(s=c[i%c.length].replace(/x/g,a),i?s=s.replace(/^-?/,c=>c? - : + )+(a>0?*(x-${a}):a<0?*(x+${-a}):*x):s,i>1?s+^${i}/${f*=i}:s),f=1).join


Lots of tedious string manipulation. Explanation:

(c,a,n)=>                               Parameters
[...Array(n)].map((_,i)=>(             Loop i from 0 to n-1
s=c[i%c.length]                       Get the right element from the cycle
.replace(/x/g,a),                    Substitute a in for x
i?                                    If i > 0
s=s.replace(/^-?/,c=>c? - : + )  Turn the sign into an operator
+(a>0?*(x-${a}) If a > 0 then * (x - a) :a<0?*(x+${-a})                  If a < 0 then * (x + -a)
:*x)                             If a == 0 then * x
:s,                                  No change if i < 1
i>1?                                  If i > 1
s+^${i}/${f*=i}                    Append ^ i / i!
:s),                                 No change if i < 2
f=1                                   0! == 1
).join                               Join all the results together


170 bytes if you remove all the whitespace:

(c,a,n)=>[...Array(n)].map((_,i)=>(s=c[i%c.length].replace(/x/g,a),i?s=(s[0]=='-'?'':'+')+s+(a>0?*(x-${a}):a<0?*(x+${-a}):*x):s,i>1?s+^${i}/${f*=i}:s),f=1).join


# Python 2, 181 bytes

D,a,N=input()
D=["+"*(d[0]!="-")+d.replace("x",a)for d in D]
x=a and"*(x-%s)"%a or"*x"
r=D[0][1:]+(D[1]+x)*(N>1)
n=f=2
while n<N:r+=D[n%len(D)]+"/"+f+x+"^"+n;n+=1;f*=n
print r

• Maybe a short explanation on how this works? Commented Apr 27, 2016 at 23:38
• @R.Kap There's nothing to explain, really. It's just a bunch of string concatenation. Each line reasonably speaks for itself.
– orlp
Commented Apr 27, 2016 at 23:39
• I mean, what's going on in x? What are the and and or doing there? Commented Apr 27, 2016 at 23:39
• @R.Kap x is simply "*(x-a)" or just "*x" if a is 0.
– orlp
Commented Apr 27, 2016 at 23:40
• I would presume that you would have to use ternary if statements for that, but this I did not know was even possible. Commented Apr 27, 2016 at 23:41

# Python 3.5, 308 299 bytes:

def g(f,a,n,L=len,S=str):import math,re;A=S(a);print('+'.join(t.replace('x',A)+re.sub('(\W$$x-\d+$$\^0|/1|\*1|\^1|\*0)(?!\d+)','',g)for t,g in zip(f*(n//L(f))+f[:n%L(f)],['*'+A+'/'+S(math.factorial(n))+'*'+'(x-'+A+')^'+S(n)for n in range(n)])).replace('+-','-').replace('--','+').replace('x-0','x'))


This always outputs in the order according to the equation f^(n)(a)/n!*(x-a)^n. For instance, if the expected output for one of the terms is e^0*x^2/2, it instead outputs e^0/2*x^2. However, they are both basically the same answer, so I hope this is okay. Confirmed by OP that this is okay. This works perfectly as I have had no issues with it. I will shorten this over time wherever I can.

Note: In the output, all (x-0) are replaced with (x).

Try it online! (Ideone) (Accepts input on 3 lines, where the first line is the input list, f, the second line is a, and the third line is n)

# Explanation

For the purposes of this explanation, assume the function is executed with the values "e^(i*x)", "i*e^(i*x)", "-e^(i*x)", "-i*e^(i*x)"],0,7. That being said, basically, what's going on in the main part, step-by-step, is:

1. '+'.join(t.replace('x',A)+re.sub('(\W$$x-\d+$$\^0|/1|\*1|\^1|\*0)(?!\d+)','',g)for t,g in zip(f*(n//L(f))+f[:n%L(f)],['*'+A+'/'+S(math.factorial(n))+'*'+'(x-'+A+')^'+S(n)for n in range(n)]))

A list is created from each item pair in a zip object containing the input list, f, with n number of items, and another list which contains each term in the sequence except the items in f. In this second list, *+a is added, followed by a /+ n! for each value, n, in the range n, and finally, a lone * followed by (x-a)^n. After all this is done, for each item pair in this zip object, t corresponding to each object in f and g corresponding to each value in the second list, each 'x' in t is replaced with a, and every g goes through a regular expression substitution, namely (\W$$x-\d+$$\^0|/1|\*1|\^1|\*0)(?!\d+), in which every (x-a)^0, /1,*1,^1, and *0 is matched and replaced with '' as long as each of those are not followed by any more integers. After all this substitution is done, each t and g are concatenated together, and each term created using this method is joined with +s and returned. For this particular case in which "e^(i*x)", "i*e^(i*x)", "-e^(i*x)", "-i*e^(i*x)"],0,7 is the input, this part would return:

e^(i*0)+i*e^(i*0)*(x-0)+-e^(i*0)/2*(x-0)^2+-i*e^(i*0)/6*(x-0)^3+e^(i*0)/24*(x-0)^4+i*e^(i*0)/120*(x-0)^5+-e^(i*0)/720*(x-0)^6


As you can see, those +-s, --s, and (x-0)s exist. However, these will all be replaced with -,+,and (x) respectively in the next part.

2. '+'.join(t.replace('x',A)+re.sub('(\W$$x-\d+$$\^0|/1|\*1|\^1|\*0)(?!\d+)','',g)for t,g in zip(f*(n//L(f))+f[:n%L(f)],['*'+A+'/'+S(math.factorial(n))+'*'+'(x-'+A+')^'+S(n)for n in range(n)])).replace('+-','-').replace('--','+').replace('x-0','x')

Here is where those substrings such as +-,++,and (x-0) in the now joined string are replaced with their correct substitutes using Python's built-in sub-string replacement function. After all the replacing has been done, this finally returns the finished product, which in this case would be:

e^(i*0)+i*e^(i*0)*(x)-e^(i*0)/2*(x)^2-i*e^(i*0)/6*(x)^3+e^(i*0)/24*(x)^4+i*e^(i*0)/120*(x)^5-e^(i*0)/720*(x)^6