4

Does it have one? The continuous variant does, $f'(t) \rightarrow j \omega F(\omega)$ - but $jkX[k]$ definitely isn't it for DFT.

To find it there must be a useful simplification of $\text{DFT}(x[n] - x[n-1])$, where $x[n] = \sum_{k=0}^{N-1} X[k] e^{j2\pi kn/N}$, while also accounting for the one dropped sample (x[0]) - and I cannot see such a simplification.


Note: $x[n] = ...$ above is for the definition of IDFT I use, rather than where the signal derives from. The signal should be assumed the most general possible; not periodic, can be complex or real, but must be finite.

Note2: see my answer for some important caveats to (and direct contradicting of) the accepted answer, and optionally the comments discussions below answers. Thanks to @CedronDawg for the detailed discussion.

Note3: "derivative" = finite difference (for sake of this question).

Edit 2023: hijacking the question for visibility, the exact question effectively asks for fft(x[1:]) - fft(x[:-1]); this requires "DFT trim property". We have that for x[:len(x)//2], and it can perhaps extend to x[:-1] via fractional resampling. Olli's answer works great for$^{1}$ fft(x[:-1]) ~= fft(x) (which has many use cases). For "derivative" as in differentiating via frequency domain, "trigonometric differentiation" may be of interest, summarized here.

1: as in predicting fft(diff(x)) from only fft(x); it's exact for fft(x[:-1])

OverLordGoldDragon
  • 8,912
  • 5
  • 23
  • 74
  • 3
    strictly speaking, there is no derivative operator applied to discrete sequences. there are no differential equations involving discrete sequences, but we *do* approximate (or emulate) differential equations with difference equations. that said, your "derivative" could be meaningfully replaced with a difference but there are issues involved in doing so. one of the issues is an apparent offset of $\frac12$ sample when the discrete samples are cast back into a continuous function using the reconstruction theorem. – robert bristow-johnson Sep 11 '20 at 02:44
  • (continuing from Cedron's 2nd answer's comments) "it shouldn't matter" i.e., whether or not $x[n]$ "comes from $X[k]$" or vice versa, both $x[n]$ and $X[k]$ in the analysis frame (from 0 to N-1) are the same. But in case x[-1] is relevant, then yes, $x[n]$ is the original, with samples outside the frame. – OverLordGoldDragon Sep 11 '20 at 16:24
  • (also con't from my second answer) Because Olli got it first and got it right. I took us off on a tangential tour due to my misinterpretation of the question. I'm pleased that was more educational for you. – Cedron Dawg Sep 11 '20 at 16:40
  • @CedronDawg Not just for me, but for any future reader; our discussion added much value to this entire thread. Alright, I'll note it in my question - after finishing a sort of discussion summary in my answer. – OverLordGoldDragon Sep 11 '20 at 16:43
  • // "it shouldn't matter" i.e., whether or not $x[n]$ "comes from $X[k]$" or vice versa, both $x[n]$ and $X[k]$ in the analysis frame (from $0$ to $N-1$) are the same.// -- not sure what this sentence means, or has anything to do with the fact of inherent periodic extension to the DFT. $$ $$ //But in case $x[-1]$ is relevant, then yes, $x[n]$ is the original, with samples outside the frame.// -- my point is that Ced is explicitly making use of "$x[-1]$" and Ced denies the inherent periodic extension of the DFT. If Ced puts it into his/her equations, that $x[-1]$ must have some value. – robert bristow-johnson Sep 11 '20 at 17:51
  • @robertbristow-johnson Neither of us deny the extension's periodicity, no - we simply say it's not required to get the DFT of the analysis frame, or to reconstruct the frame. We say that the extension cannot describe anything about the original signal. As for my sentence; I'm saying the DFT and its inverse will be exactly the same in both scenarios. -- P.S., pardon if I speak wrongly on Cedron's behalf. – OverLordGoldDragon Sep 11 '20 at 17:57
  • 1
    Ced denies the property inherent to the DFT that is periodic extension of the data passed to it. so i suspect you are misrepresenting his position. i will try to be careful to not represent it and will let Ced speak for him/herself. i have been trying to corner Ced into taking a position regarding the relationship that $x[N-1]$ has to $x[0]$ in the DFT. (it is clear that it is exactly the same relationship that $x[0]$ has to $x[1]$ and my understanding of Ced's position is that he/she denies this.) any shifting in the DFT, must be circular because of inherent periodic extension. – robert bristow-johnson Sep 11 '20 at 18:13
  • @CedronDawg It suddenly dawned on me that I might've hastily given up on $x'(n)$, as it was not based on x[n]-x[n-1], which is what I used to check it. It's still true that we can't roll with $(n)$ - but I'm out of arguments if we simply use $[n]$. Your expression handled a linear chirp fairly well in terms of shape, comparing with np.diff, and since latter indeed isn't a "true" derivative, it's not obvious which is "more correct". There's math work to be done to justify continuous-differentiating and then discretizing again, but maybe it can be done after all. – OverLordGoldDragon Sep 11 '20 at 20:49
  • 1
    @robertbristow-johnson Himself. OLGD has represented my position succinctly and correctly. I urge your to reread both my answers here, as well as the comments in the other thread. It is you that have consistently misrepresented what I've said and I'm tired of trying to correct you. Modular arithmetic does not require "an extension" to perform a rotation. Snipping the analysis frame out of the signal and wrapping it in a circle is a more accurate portrayal. I originally understood the meaning of $X[-1]$ to be the original signal, not the extension of the inverse DFT of the spectrum. – Cedron Dawg Sep 11 '20 at 21:55
  • The math was done in my answer. By allowing n to be real in the definition of of the inverse DFT, the definition of the "trigonometric interpolation function" is achieved. Clearly $x[n] = x(n)$ on the integers. It can be differentiated, and the derivative evaluated at each point. My oops was not selecting the minimum bandwidth interpolation function. Again $(x[n+1]-x[n-1])/2$ is a better approximation to $x'(n)$. I was confused by RB-J's first comment as I had already made the same points and he said that he had read my answer. – Cedron Dawg Sep 11 '20 at 21:56
  • The discussion (with RB-J) in the fluffy cloud link arose when he asserted the Nyquist bin "had" to be split on the high and low side. For a real valued signal it doesn't matter since the imaginary part of the bin is zero and $\cos(-x)=\cos(x)$. In my code below it is fully on the negative side. For complex valued signals it is best to split it, but nothing (bandwidth-wise) says you have to. A lesson you can draw from that is that odd N frame sizes with a zero centered signal frame and a zero centered spectrum is the "most natural" configuration. The same is true in my eigenvector studies. – Cedron Dawg Sep 11 '20 at 21:57
  • 1
    //I'm tired of trying to correct you.// --- it's because you're wrong and the mathematics say that you're wrong and your attempt to "correct" me doesn't take. ... // Modular arithmetic does not require "an extension" to perform a rotation..// --- Modular arithmetic *is* an extension to perform a shift of data that is periodic. So take whatever DFT theorem that implies shifting of the data in either domain, that shifting must require modulo-$N$ arithmetic. So if you define simply:

    $$ y[n] = x[ n \mod N ] \qquad \forall n \in \mathbb{Z} $$

    then how periodic is $y[n]$?

    – robert bristow-johnson Sep 11 '20 at 22:37
  • 1
    For the last time, you can extend the inverse DFT of the spectrum in the time domain to your heart's content, and the result will be periodic, but that is no reason to assume the originating signal is, or pretend it is. That's sort of like saying a ROL, ROR, RCL or RCR assembly language instruction makes the register shift into another register so it can rotate. It's nonsense. The inverse will match the original signal on the analysis frame, that is all you can say. The rotation doesn't occur until you take the inverse DFT. I'm done with you here too. Let others judge as they may. – Cedron Dawg Sep 11 '20 at 23:34
  • 2
    // but that is no reason to assume the originating signal is,// -- can you show me anywhere that i mentioned the term "originating signal"? (please don't misrepresent my position.) .... // or pretend it is.// -- no pretending. when you pass $N$ contiguous samples of $x[n]$ to the DFT (regardless of the origin), the DFT periodically extends the data in all of its math and in any theorem where it makes a difference (pretty much anything other than linearity and scaling), so that $$x[n+N]=x[n] \qquad \forall n \in \mathbb{Z}$$ That is simply a mathematical fact. – robert bristow-johnson Sep 12 '20 at 00:48
  • now Ced, when are you going to answer my question? – robert bristow-johnson Sep 12 '20 at 00:54
  • 1
    @robertbristow-johnson No. It is clear to me you are the one not following the discussion and any further attempts of clarification are futile. When the understanding of what we have been talking about hits you, feel free to come back. – Cedron Dawg Sep 12 '20 at 01:26
  • 1
    @CedronDawg, i am not accepting your false mathematic axioms. i am not accepting your self-assessed authority and i am calling you out on promoting false mathematical claims, on being inconsistent with your own mathematical claims and on bad engineering practices regarding units and dimensions. anyone who says that $2$ or $2 \pi$ or any other dimensionless number has units is simply mistaken and your understanding is pedantic. you should not be propagating this misunderstanding. you claim that the DFT does not inherently periodically extend the data passed to it, yet you make use of $x[-1]$. – robert bristow-johnson Sep 12 '20 at 02:19
  • even though you claim to be, you are not really a DFT expert. you have a pedantic understanding of it and you've made some either false or inconsistent claims. and you never once answered any of the questions or challenges put to you. – robert bristow-johnson Sep 12 '20 at 02:20
  • What does dropping x[0] mean? – Olli Niemitalo Sep 12 '20 at 03:59
  • 1
    @OlliNiemitalo well, I was already pretty sure, even without reading, that neither of the answers were trying to prove the unexisting derivative property of DFT, but then the only real problem to be solved in this question is to find the correct question to ask for what is being answered below... :-))) – Fat32 Sep 12 '20 at 10:59
  • 1
    @robertbristow-johnson This is not an argument about how the math works (I hope), but how it should be described. My second answer repeats your questions and then does a thorough answer. If that answer is unsatisfactory to you, well I can't control that, but to say I didn't answer is a prevarication. $$ $$ Starting with:

    $$ z[n] = x[ n \mod N ] \qquad \forall n \in \mathbb{Z} $$

    You can say:

    $$ z[n] = z[n+N] $$

    You can't say:

    $$ x[n] = x[n+N] $$

    It might be true, but likely not. You are saying one should assume it is true, I am denying that.

    – Cedron Dawg Sep 12 '20 at 11:15
  • Phenomenal fireworks to log on to. I'm inclined agree with Cedron that RBJ is, in fact, not following the discussion closely enough; much of accusations are resolved by comments made outside the immediate context, on this very Q&A. Example: RBJ: "Ced denies X and you misinterpret him" -- Ced: "No, I was interpreted correctly." -- RBJ: "No, you're wrong" ... ?? -- I'll see if I find time to make a guided Q&A on DFT's periodicity 'assumption'. – OverLordGoldDragon Sep 12 '20 at 11:16
  • 1
    @OlliNiemitalo Finite difference demands dropping a sample; it operates on two points, starting from x[0], ending with x[N-1], which can only produce N-1 points, unless we impute one somehow. I say drop x[0] since that's what Python's numpy.diff does. – OverLordGoldDragon Sep 12 '20 at 11:20
  • Ah, I see. So you wish to have an expression for a length $N-1$ DFT of backward difference of $x$ given a length $N$ DFT of $x$? – Olli Niemitalo Sep 12 '20 at 11:36
  • @OlliNiemitalo Not given a DFT; see Note1, and Ctrl + F "my sentence", that comment and one above it. Pretty sure your and Cedron's answer already did the job, I've just yet to hear Cedron agree that the slope term will always drop. – OverLordGoldDragon Sep 12 '20 at 11:38
  • If Olli does formulate a proper N point spectrum of a signal to a N-1 point spectrum of the backward finite difference of the signal transform then he should get the check for sure. When I realized that was the issue, I considered trying, but didn't, hence my hack "reframe the problem" comment in my j;first answer which requires taking the inverse DFT, taking the difference with shortening the frame, then taking the DFT. It seems to me that a simple formulation is not possible, but I haven't tried. – Cedron Dawg Sep 12 '20 at 12:02
  • My first answer is based on the input signal, not the inverse DFT of the spectrum, but it also works for the latter. In the latter case the slope term will always drop. – Cedron Dawg Sep 12 '20 at 12:05
  • @CedronDawg No, it must drop, always. If not, what do you say to my counterexample? What if x[-1] = 100^100? – OverLordGoldDragon Sep 12 '20 at 12:06
  • If the finite backward difference is based on the input signal, call it $y[n] =x[n]-x[n-1]$ prior to the selection of the analysis frame then $Y[k]$ cannot be determined from $X[k]$ without considering the value of $x[-1]$. Dropping the slope term is equivalent to saying $x[-1]=x[N-1]$. Naturally, a N periodic signal satisfies that and is a much stronger condition than required, so I am not sure what you mean by your counter example. If you do the "dropped sample approach" and want to find $Y_{N-1}[k]$ from $X_{N}[k]$ then $x[0]$, determinable from $X_{N}[k]$, plays the role of $x[-1]$ – Cedron Dawg Sep 12 '20 at 12:23
  • 2
    @CedronDawg I have to agree with Robert here. You don't need to introduce new fancy math just to show that the DFT implies circular first difference, instead of the linear one. And to get the effect of linear first difference, all you have to do is to append one zero to the end of x[n], and use N+1 length DFT. And btw, DFT = DFS. It's nothing new. And it's circular all the way. But the link between a linear sequence $x[n]$ and its circular (periodic) extension $\tilde{x}[n]$ should be made very clear at the beginning so as to allow readers correctly interpret what you are doing.. – Fat32 Sep 12 '20 at 12:53
  • @Fat32 I've prepended my answer with an explanatory note. Hopefully that is satisfactory to all. In the terminology of the discussion I would say "DFS = Extended Inverse DFT", but I have no strong interest in that issue. I don't think I introduced any "fancy math", I just addressed the problem from the definition rather than applying the circular convolution property of the DFT. Did you mean "append x[0] to the end"? This still wouldn't be correct to get the backward finite difference. – Cedron Dawg Sep 12 '20 at 13:25
  • @Fat32 How about this, for an unnormalized DFT?

    $$ Y_{linear}[k] = Y_{circular}[k] + ( x[N-1] - x[-1] ) $$

    Seeking the linear backward finite difference implies that x[-1] exists.

    – Cedron Dawg Sep 12 '20 at 14:06
  • @CedronDawg I'm saying let's assume you're right, and consider the implications. The implication is, a sample outside the frame which we're transforming (in this case, taking the "derivative" of), has a "say" on samples inside the frame. So we take zeros from 0 to N-1, and insert a bomb at x[-1]; now the "derivative", which should be zero over the frame, is far from it. – OverLordGoldDragon Sep 12 '20 at 14:36
  • 1
    @CedronDawg I said fancy, and not fake :-) so please don't resent back ;-). Things are all about definitions and not real math issues here. In the context of DFT, $x[-1]$ simply refers to $x[N-1]$ due to $x[n]$ being circular (periodic) sequence. If you want to make a reference to a linear operation on the supplied data of $x[n]$ treated as an aperiodic finite length sequence,you can do it in a number of ways,but the most elegant is to use the concept of circular vs linear adjustment on the DFT size N. And finally DFT is DFS and there is no need to re-define it I guess... ;-) – Fat32 Sep 12 '20 at 14:40
  • @CedronDawg The only difference between DFS and DFT is that while DFS uses the concept of infinite length periodic sequences $\tilde{x}[n]$ and $\tilde{X}[k]$, the DFT assumes that the data $x[n]$ is finite length but then uses the modulo-N arithmetic on every operation it defines to get the exact-same effect as if DFS was used on the data x[n], but avoids the term infinite length periodic sequence. Because the computer science does not like the terms continuous or infinite. That's it, you proces the base period of a periodic sequence to achieve DFS results and call it DFT. – Fat32 Sep 12 '20 at 14:48
  • @CedronDawg If you're saying we can shift the slope term to lie within 0 to N-1, that's yet to be shown. Now, I've tried various signals, and using x[0] and x[N-1] as slope indices. I found the mean absolute difference between the finite difference taken via np.diff, and inverted from the manipulated frequency domain, to be exactly equal in float64 both with and without the slope term - which I found strange. I couldn't force the slope term to be non-negligible compared to all other coefficients. – OverLordGoldDragon Sep 12 '20 at 14:49
  • OLGD, I think the thing giving you trouble is using the np.diff. Since it drops the leading sample, you are really getting the forward difference. My answer doesn't deal with the dropped sample case. Perhaps you should just generate a long signal x[n], calculate y[n] = x[n] - x[n-1], then pick a frame in the middle somewhere and take the respective DFTs, X and Y. I'm pretty sure those results will match my answer. – Cedron Dawg Sep 12 '20 at 15:14
  • @Fat32 I didn't mean to sound resentful, sorry about that. The DFS thing is of no concern to me, I probably shouldn't have said anything about that. The DFT has no inherent assumption. It assumes nothing about what is outside the frame. It might be useful for some purposes to assume the signal is a periodic repeat of the frame and I have no problem with that, but that should be stated not presumed. In my frequency formula articles, the assumption is that the signal is a portion of a single pure tone that extends as a pure tone outside the frame. Different purpose, different assumption. – Cedron Dawg Sep 12 '20 at 15:30
  • @CedronDawg Quick note, it's only a slope term at inversion, not in $Y[k]$, else it's $1/N^2$ at inversion. Think I have some good & bad news for the slope term; might add an answer - but x[-1] remains unredeemed. – OverLordGoldDragon Sep 12 '20 at 18:28
  • Answer unaccepted - everyone is wrong. See my new answer. -- Or maybe I should open a new Q&A at this point - the length of this one is getting out of hand. – OverLordGoldDragon Sep 12 '20 at 21:21

5 Answers5

8

To get a detailed answer along the lines of what you propose, we need to be careful about the normalization used in discrete Fourier transform (DFT) and inverse discrete Fourier transform (IDFT):

$$\text{DFT: }\quad X[k] = \sum_{n=0}^{N-1} x[n] e^{-j 2\pi kn/N}\tag{1}$$ $$\text{IDFT: }\quad x[n] = \frac{1}{N} \sum_{k=0}^{N-1} X[k] e^{j 2 \pi k n / N}\tag{2}$$

Those have a normalization that is directly compatible with fft and ifft from MATLAB, Octave, NumPy and SciPy. The indexes $k$ and $n$ run from $0$ to $N-1$. (MATLAB and Octave have a different indexing convention, $1$ to $N$.) Then:

$$\begin{array}{c}y[n] = x[n] - x[\operatorname{mod}(n-1, N)]\\ \begin{align}\\ Y &= \operatorname{DFT}\big(y\big)\\ &= \operatorname{DFT}(x*[1, -1, 0, 0, \ldots])\\ &= \operatorname{DFT}(x)\times\operatorname{DFT}([1, -1, 0, 0, \ldots])\\ &= X\times\operatorname{DFT}([1, -1, 0, 0, \ldots])\end{align}\end{array}\tag{3}$$ $$\Rightarrow Y[k]= X[k]\left(1 - e^{-j2\pi k/N}\right),\tag{4}$$

where $\operatorname{mod}$ gives the unsigned remainder, for example $\operatorname{mod}(-1, N) = N - 1$, the symbol $*$ denotes length-$N$ circular convolution and $\times$ denotes multiplication, and all sequences are of length $N$.

Circular convolution in the discrete time domain is exactly equivalent to multiplication in the discrete frequency domain, when DFT and IDFT are used to transform the sequences between the domains. See for example Circular Convolution - MIT OpenCourseWare. For "linear" convolution of discrete sequences, there is no such elegant pair of equivalent operations, which makes me think no expression proposed as an answer to the question will ever be fully satisfactory.

Considering input signals that extend to the left of the output range $0 \le n < N$ of IDFT, calculating the backward difference using modulo indexing is conditionally equal to calculating it without it:

$$x[n] - x[\operatorname{mod}(n-1, N)] = x[n] - x[n-1]\quad\text{conditionally}\tag{5}$$

under the condition that you only calculate it for some of the indexes:

$$0 < n < N,\tag{6}$$

or for $0 \le n < N$ under the condition that there's a hint of periodicity in the signal:

$$x[-1] = x[N-1].\tag{7}$$

A sufficient but not necessary condition that satisfies Eq. 7 is that $x$ is $N$-periodic, which is however prohibited by the signal defined as finite in the question. An example of another condition that satisfies Eq. 7 is $x[-1] = x[N-1] = 0$.

@CedronDawg's first answer provides a formula which corrects in the frequency domain the error in Eq. 5 if neither condition is satisfied: $Y[k] = X[k]\left( 1 - e^{-j2\pi k/N} \right) - x[-1] + x[N-1]$. As an alternative derivation, in length-$N$ time domain the correction is an impulse:

$$\begin{align}&\big[x[0] - x[−1] - \big(x[0] - x[N-1]\big),\, 0,\, 0,\, \ldots\big]\\ = &\big[x[N-1] - x[-1],\, 0,\, 0,\, \ldots\big],\end{align}\tag{8}$$

which in frequency domain is a constant (see DFT Pairs and Properties: pair row 2, property linearity):

$$x[N-1] - x[-1],\tag{9}$$

to be added to all elements of $Y$ calculated by Eq. 4.

For a general $x$, the condition of Eq. 6 for Eq. 5 enables to use a length $N+1$ DFT and IFT to calculate the backward difference, by shifting the input to the DFT one step to the right, and finally by shifting the output from IDFT one step to the left. With forward difference $x[n + 1] - x[n]$, the shift would not be necessary, and I think this matches your discarding of the 0th sample. For a circular convolution implementation of convolution by a finite sequence, using a longer transform is a common trick to avoid the circular effects in a sufficiently large part of the output of the IDFT. For then obtaining the DFT of a partial IDFT output, in particular a one shorter, I don't think there is any shortcut.

We could perhaps re-express the question as: What is the DFT of the length $N-1$ forward difference of $x$ of length $N$, given $x$ and $X_{N-1} = \operatorname{DFT}(x_{N-1})$, a length $N-1$ DFT of the partial sequence $x_{N-1} = \big[x[0], x[1], \ldots, x[N-2]\big]$? Analogously to Eq. 4 we have:

$$y_{N-1} = x_{N-1}*[-1, 0, 0, \ldots, 0, 0, 1]\tag{10.1}$$ $$\Leftrightarrow Y_{N-1} = X_{N-1}\times\operatorname{DFT}([-1, 0, 0, \ldots, 0, 0, 1])\tag{10.2}$$ $$\Rightarrow Y_{N-1}[k] = X_{N-1}[k]\big(e^{j2\pi k / (N - 1)} - 1\big),\tag{10.3}$$

where each sequence is of length $N-1$. The desired forward difference $f_{N-1}$ is:

$$f_{N-1} = \big[x[1] - x[0],\, x[2] - x[1],\, \ldots,\, x[N-1] - x[N-2]\big].\tag{11}$$

Eq. 10.1 can be expanded to:

$$y_{N-1} = \big[x[1] - x[0],\, x[2] - x[1],\, \ldots,\, x[0] - x[N-2]\big].\tag{12}$$

By comparing Eqs. 11 and 12, it can be seen that:

$$f_{N-1} = y_{N-1} + \big[\ldots,\, 0,\, 0,\, x[N-1] - x[0]\big],\tag{13}$$

where the sequence in brackets is of length $N-1$. Taking the DFT of both sides of Eq. 13 and applying Eq. 10.3 gives the answer:

$$\begin{align}F_{N-1}[k] &= Y_{N-1}[k] + (x[N-1] - x[0])e^{j2\pi k/(N-1)}\\ &=X_{N-1}[k]\big(e^{j2\pi k / (N - 1)} - 1\big) + (x[N-1] - x[0])e^{j2\pi k/(N-1)}.\end{align}\tag{14}$$

This is the length $N-1$ DFT of the length $N-1$ forward difference of $x$ of length $N$.

Alternatively, you might be interested in the derivative of the band-limited signal represented by the samples.

Olli Niemitalo
  • 13,491
  • 1
  • 33
  • 61
  • Seem to have nailed it - I'll run some checks to confirm this works in my context. – OverLordGoldDragon Sep 04 '20 at 12:39
  • A question as simple as DFT{x[n] - x[n-1]} should be answered as simple as $X[k] (1 - e^{-j\frac{2\pi}{N}k})$, and that arguments are modulo-N.;i.e., $x[n-1] = x[(n-1)_N]$ and that $x[-1] = x[N-1]$. Now, I understand that the OP wants to get a linear first difference instead of circular one, and then the answer should only be modified by adding that $M=N+1$ length cicular difference will be equal to the linear difference. Just as the case of liner vs circular convolution. Nothing else needs to be added. But we all tend to improve already perfect answers bu putting more work of us.;-) – Fat32 Sep 12 '20 at 12:42
  • Haven't studied circular in detail yet, can't comment there - but the end-result still fails properties 1 & 2: len(Y) == N, and Y[0] != sum(x[n] - x[n - 1]). Prop 2 must hold whether or not $x$ is periodic - but in your answer, Y[0] == 0. – OverLordGoldDragon Sep 13 '20 at 11:19
  • @OverLordGoldDragon what a long argumentation for something not so deserving it... Of course I have full sympathy and respect for whatever hobby you have, but Olli's answer is the one you will get from a dsp guy. I've read your argument on rejecting this answer as anti-derivative is not correct due full recovery of x[n]+C from its first difference (you call dropping a sample). Remember that by invoking a (N+1)-point DFT of x[n], you place one zero sample at the end which means you are setting C=0 so you can solve the DE from the first diference. If C is nonzero then x[n] has no DFT... – Fat32 Sep 13 '20 at 11:52
  • @Fat32 Au contraire, this is a learning exercise of the best kind. Olli's answer is consistent with "DFT Theory". And it is blind to my formula. It is also apparently blind to my frequency formulas (as practitioners would refuse to look at my math because "exact formulas are impossible"). In my attempts to confirm their novelty, I came to the conclusion that most EE profs are posers and don't really understand math. Sad to say. – Cedron Dawg Sep 13 '20 at 12:28
  • These were the first. I've found better once since. All in my articles. Click on the red author name to see the full list. https://www.dsprelated.com/showarticle/773.php – Cedron Dawg Sep 13 '20 at 12:43
  • @Fat32 That's fabricating information; we have no reason to assume the resulting derivative is what it is with a zero, a pi, or a TREE(3) inserted anywhere. Finite difference loses information - it must; there are infinitely many $x[n]$ with the same $x'[n]$, the transform is non-invertible. It becomes invertible if we preserve "C", but we don't - DFT(x[n] - x[n-1]) yields N - 1 coefficients, and they cannot encode this information. Even if I grant an exception to this, Y[0] == 0 remains a fallacy (unless padding both endpoints, which is even more fabrication). – OverLordGoldDragon Sep 13 '20 at 12:46
  • @OverLordGoldDragon Within your stated paradigm, the missing information is x[-1], which would be called initial value or analogous to "+C". "DFT theory" "assumes" x[-1] = x[N-1]. harris, in his seminal windows paper, uses "essentially considered as". You might think those are synonyms, but I see the dividing line of acceptability between them. – Cedron Dawg Sep 13 '20 at 12:52
  • @CedronDawg The +C is the dropped sample, not x[-1]. In fact, we can recover x[n] entirely from x'[n] by knowing any one sample from the original x[n], dropped or not. x[-1] is fundamentally problematic; we're describing frame A with information outside it. -- Reading your updated answer. – OverLordGoldDragon Sep 13 '20 at 12:55
  • Well Olli, I've cited them frequently in answers when pertinent to the solution. You asked which formulas, I gave you the link, so my primary motivation here was to satisfy your curiosity. – Cedron Dawg Sep 13 '20 at 12:56
  • @OlliNiemitalo This. I've taken this approach in StackOverflow - doesn't disappoint. Not only do you farm rep, but can start discussions you otherwise wouldn't on a blog. (And no, dropping a link in an answer isn't equivalent; the link itself must be the subject of the Q&A. What you actually include in the Q&A depends on whether you seek people to actually go to the site, or just to spread knowledge). – OverLordGoldDragon Sep 13 '20 at 12:58
  • @OverLordGoldDragon Sorry, like my answer, I'm not covering the dropped sample scenario. The dropped sample(s) in np.diff are done precisely to address this issue rather than assuming a rotation. The dropped sample is indeed x[0]-x[-1]. – Cedron Dawg Sep 13 '20 at 12:59
  • @CedronDawg This isn't even about np.diff any more, it's just a necessity of the transform that is the finite difference over the analysis frame 0 to N-1. If y[0] describes x[0] - x[-1], of course you're right, but if we ever needed to do this, we'd just expand the analysis frame a sample left - and back to square one. – OverLordGoldDragon Sep 13 '20 at 13:02
  • @OverLordGoldDragon Seems to me I said something vaguely similar to that in my first answer concerning the dropped sample scenario. The latest code sample confirms the equations numerically, and hopefully Fat32 has learned a new technique. There are a lot of "what this is about" being juggled in the air right now. – Cedron Dawg Sep 13 '20 at 13:22
  • @OverLordGoldDragon Nobody has to answer your nonsesence concerns here. DFT is about FINITE LENGTH sequences, (i.e., known to be ZERO outside of a domain of support). And indeed if a sequence is known to be finite length, then it can be fully recovered from it's first difference x[n]-x[n-1], there's nothing suprising, restricting, or violating about it? Ex: x[n] = [2,3,4] for n=[0,1,2] => x[n]-x[n-1] = [2,1,1]... now can you recover x[n]? YES! becuase it's FINITE LENGTH. If it were infinite length, then you would ask what x[-1] was? Or you would invent the dropping fun..?? – Fat32 Sep 13 '20 at 13:34
  • @OverLordGoldDragon DTFT (discrete-time Fourier transform), on the other hand deals with sequences of INFINITE LENGTH (as well as finite length), and in that case, you cannot recover a sequence x[n] from its first difference WITHOUT an additional piece of information. That additional piece of information simply becomes as x[n] = 0 for some n<n0, when x[n] is a FINITE length sequence. That's sufficient to close this ridiculus discussion here... – Fat32 Sep 13 '20 at 13:37
  • @Fat32 I expected better of the supposed DSP experts here, then again experts falling apart when it comes to the fundamentals of their domain isn't much news to me. See my latest reply under my second answer. – OverLordGoldDragon Sep 13 '20 at 13:39
  • @CedronDawg you developed a SYSTEMATIC HABIT of putting nonesense fancy math here... And you abuse here by covering your nonsense claims behind those fancy math that nobody understands... please stop doing that. – Fat32 Sep 13 '20 at 13:40
  • @OverLordGoldDragon the example should be x[n] = [2,3,4] and x[n]-x[n-1] =[2,1,1,-4] – Fat32 Sep 13 '20 at 13:43
  • @Fat32 I would say "rigorous" rather than "fancy". I've been pondering what you have meant by that since you first said it, still not sure. Well, a perception of nonsense can be due to not understanding or a nonsensical proposition. At least you didn't say "not understandable". harris, in the paragraphs before my last reference speaks of having two viewpoints and switching between them. It works. I would think you would welcome a new viewpoint and strive to understand it. – Cedron Dawg Sep 13 '20 at 13:56
  • @CedronDawg Rigorous? Are you serious? These are just fancy and useless, sorry to say... Not even re-inventing the wheel ;-) Your method is not something new too ;-) – Fat32 Sep 13 '20 at 14:00
  • @CedronDawg I really wonder how you mathematicians rigorously describe differentiation on an integer domain set, as you did below. By just freely assuming that n is a continuous variable? Is that the level of rigor? Sometimes it works but mostly it's awkward. – Fat32 Sep 13 '20 at 14:07
  • @CedronDawg enough for today. I have said all about this question... and don't want to hear more. – Fat32 Sep 13 '20 at 14:15
  • @Fat32 [I wrote this before your last comment so I am not going to throw it away, you can read it another day] Serious? Mostly. Resentful? Heck no. Concerning the formula in my first answer whether it is new, or new to you, nor neither, ain't no thing. If you happen to have a reference to an occurence that would be cool. Actually, "let's make in continuous" is quite proper here as the RHS of the first is already continuous, it is the LHS which introduces the discreteness. As rigorous as possible, not even close. Nobody I think, including me, wants to go full Real Analysis. – Cedron Dawg Sep 13 '20 at 14:18
  • @Fat32 I accept your proposition. Now, let's stand on a very tall cliff, and take one step off of it. Since our initial vertical position is 9000, that's also our vertical velocity (x'[0] = x[0] - 0, right?). Congrats, we faceplanted at 10x the terminal velocity. -- This is what your math implies, nothing less. x'[0] has nothing to do with the rate of change of x, but its initial value. If you find this description meaningful or useful, so be it - I seek something without magical consequences for the nature of reality or information. – OverLordGoldDragon Sep 13 '20 at 14:23
  • @CedronDawg You still side by x'(n)? That's due much "works only assuming X, Y, and Z", else you've invented an infinite compression algorithm and solved world's energy problems. – OverLordGoldDragon Sep 13 '20 at 14:26
  • @OverLordGoldDragon Not sure what you mean by that, and I've got some other stuff to do. The math is the math, it says what it says. The code is the code, it does what it does. That's pretty objective. How we describe it is a fount of endless squabbling. That's pretty subjective. .......... It is also the crucible of deeper understanding. – Cedron Dawg Sep 13 '20 at 14:36
  • @CedronDawg You've contributed enough, thanks for your effort. – OverLordGoldDragon Sep 13 '20 at 14:45
  • @CedronDawg Ok, I do also freely apply calculus operators (limit, diff etc.) on discrete-time sequences to get a working result in some circumstances; the value of a sinc(n) pulse at the origin is a nice example. I said it because of your rigour claim. DSP is not about formal analysis, though it benefits a lot from its results. DSP is about coming up with intuitive, creative, efficient, practical, reliable, accurate, and robust solutions to electrical (or some other) engineering problems implemented in discrete-time hardware & software. Because otherwise it won't make sense. – Fat32 Sep 13 '20 at 20:27
  • 1
    Err, a small typo in my code. I have a grand announcement... It WORKS! Excellent work, Olli. – OverLordGoldDragon Sep 15 '20 at 21:54
  • Though, two issues: (1) we were to have a relation from $X_N$, not $X_{N-1}$; (2) we shouldn't require the original signal as an input to compute $F_{N-1}$ (and we can't recover it from $X_{N-1}$). Since you've worked out an expression with 1 fewer coefficient ($X_{N-1}$), you in a way solved a harder problem, so a workaround to (1) and (2) seems possible. – OverLordGoldDragon Sep 15 '20 at 22:13
  • $X_{N-1} = \text{DFT}(\text{IDFT}(x)[\text{:-1}])$ is possible, but doesn't quite count as the idea is to directly relate the frequency spectrum of x with that of DIFF(x) (magnitude, phase, etc) - but for a long enough or padded x it's a close equivalent. – OverLordGoldDragon Sep 15 '20 at 23:22
  • 1
    I don't think there is a shortcut to "directly relate the frequency spectrum of x with that of DIFF(x)", as you express it. The main computation would amount to taking the IDFT and then a one-shorter DFT, of which I say the same thing in the answer. That computation would be equivalent to band-limited resampling, which is a quite thoroughly researched area of digital signal processing, and I doubt there is anything new to be found there, math or computation-wise, that would also be applicable to the present task. – Olli Niemitalo Sep 16 '20 at 05:55
  • I see. Still, something came of all this: with endpoints equal, "differentiating" shifts phase linearly, and modulates amplitude sinusoidally. If unequal, it also depends on their difference. – OverLordGoldDragon Sep 16 '20 at 19:24
3

[Edit: This answer is based on x[n] being the input signal (likely sampled from a continuous differentiable function), which was my misinterpretion of the OP's question, and that what was being sought was a transform from the spectrum of the input signal to the spectrum of the linear first difference. If one desires the spectrum of the circular first difference within the analysis frame there is no need for the slope term. Furthermore, it can clearly be seen from the formula that the DC bin of the circluar difference spectrum will be zero.]

This is a different approach than Olli's doing it straight from the definition and not assuming a N periodic signal.

$$ \begin{aligned} Y[k] &= \text{DFT}(x[n] - x[n-1]) \\ &= \text{DFT}(x[n]) - \text{DFT}(x[n-1]) \\ &= X[k] - \text{DFT}(x[n-1]) \\ &= X[k] - \sum_{n=0}^{N-1} x[n-1] e^{-i\frac{2\pi}{N}kn } \\ &= X[k] - \sum_{m=-1}^{N-2} x[m] e^{-i\frac{2\pi}{N}k(m+1)} \\ &= X[k] - e^{-i\frac{2\pi}{N}k} \sum_{m=-1}^{N-2} x[m] e^{-i\frac{2\pi}{N}km} \\ &= X[k] - e^{-i\frac{2\pi}{N}k}\left[ \sum_{m=0}^{N-1} x[m] e^{-i\frac{2\pi}{N}km} + x[-1]e^{i\frac{2\pi}{N}k} - x[N-1] e^{-i\frac{2\pi}{N}k(N-1)} \right] \\ &= X[k] - e^{-i\frac{2\pi}{N}k}\left[ X[k] + x[-1] e^{i\frac{2\pi}{N}k} - x[N-1] e^{-i\frac{2\pi}{N}k(N-1)} \right] \\ &= X[k]\left( 1 - e^{-i\frac{2\pi}{N}k} \right) - \left[ x[-1] - x[N-1] e^{-i\frac{2\pi}{N}k(N-1)}e^{-i\frac{2\pi}{N}k} \right] \\ &= X[k]\left( 1 - e^{-i\frac{2\pi}{N}k} \right) - x[-1] + x[N-1] \\ \end{aligned} $$

For a N periodic signal $x[-1] = x[N-1] $ so the result becomes:

$$ Y[k] = X[k]\left( 1 - e^{-i\frac{2\pi}{N}k} \right) $$

Considering the normalization is important. I used the conventional unnormalized forward DFT definition. Using the (more proper,IMO) 1/N normalization, as implied by the OP's definition of the inverse DFT, the expression becomes:

$$ Y[k] = X[k]\left( 1 - e^{-i\frac{2\pi}{N}k} \right) + \frac{x[N-1]-x[-1]}{N} $$

The last term now clearly becomes a slope calculation.


In response to OverLordGoldDragon's comment. Using the conventional normalization:

$$ x[n] = \frac{1}{N} \sum_{k=0}^{N-1} X[k] e^{i \frac{2\pi}{N} nk } $$

Let's make it continuous.

$$ x(n) = \frac{1}{N} \sum_{k=0}^{N-1} X[k] e^{i \frac{2\pi}{N} nk } $$

Take the derivative in respect to n.

$$ \begin{aligned} \frac{dx}{dn} (n) &= \frac{1}{N} \sum_{k=0}^{N-1} X[k] e^{i \frac{2\pi}{N} nk } \left(i \frac{2\pi}{N} k \right) \\ &= \frac{2\pi}{N^2} \sum_{k=0}^{N-1} i k X[k] e^{i \frac{2\pi}{N} nk } \\ \end{aligned} $$

So, your initial assertion isn't quite correct, it does apply to the DFT for the continuous derivative.

By the chain rule:

$$ \frac{dx}{dt} = \frac{dx}{dn} \cdot \frac{dn}{dt} $$

The differential you are using for an approximation for the derivative is not the best one as it has a half sample shift included. Generally you would prefer $(x[n+1]-x[n-1])/2$. This stays centered and doesn't "amplify noise" in the higher frequency range.

The comment is a little off too. $$ \begin{aligned} \left( 1 - e^{-i\frac{2\pi}{N}k} \right) &= e^{-i\frac{\pi}{N}k} \left( e^{i\frac{\pi}{N}k} - e^{-i\frac{\pi}{N}k} \right) \\ &= e^{-i\frac{\pi}{N} k} 2i \sin\left( \frac{\pi}{N}k \right) \\ &= e^{-i\frac{\pi}{N} k} 2 e^{i \frac{\pi}{2} } \sin\left( \frac{\pi}{N}k \right) \\ &= e^{-i\left( \frac{\pi}{N} k - \frac{\pi}{2} \right) } 2 \sin\left( \frac{\pi}{N}k \right) \\ \end{aligned} $$

so

$$ \left| \left( 1 - e^{-i\frac{2\pi}{N}k} \right) \right| = 2 \sin\left( \frac{\pi}{N}k \right) $$

and

$$ \arg \left( 1 - e^{-i\frac{2\pi}{N}k} \right) = -\frac{\pi}{N} k + \frac{\pi}{2} = - \frac{\pi}{2} \left( 1 - \frac{2k}{N} \right) $$

Switching gears, note that for $k=0$

$$ Y[0] = \frac{x[N-1]-x[-1]}{N} $$

So the DC component of your differential is the average slope over your frame, as expected.

I don't read text books, so I can't really address that. Interesting is not always synonymous with useful.

I'm self taught, then I do online research to confirm my findings. What is a wonder to me is that my frequency formulas, which are the first to achieve exactness, aren't in the curriculum yet either.


Puzzle solved. Duh.

Unless you center around zero, so you are using $k=-1$ instead of $k=N-1$, the derivative will be of the higher frequency interpolation (equivalent to the derivative of the DTFT at that point).

This is similar to the "Fluffy Cloud" case here: How to get Fourier coefficients to draw any shape using DFT?


I think part of my confusion is that you are using (upon rereading) the extended inverse DFT as x[n], thus x[-1] can be calculated and will match x[N-1], while in your code you generate a signal x[n] from scratch.

Define

$$ y[n] = x[n] - x[n-1] $$

and

$$ Y[k] = DFT(y[n]) $$

Your code "drops a sample", while Olli's and my answers employ x[-1]. Olli's answer assumes periodicity (go ahead accept it), mine doesn't (thinking you were working with a raw signal). I don't think it is appropriate to "drop a sample" as the np.diff call does. If you do, you should reframe the problem on the domain of 1 to N-1 as being 0 to M-1, where M = N-1, then you have the equivalent situation as not having dropped a sample.

When your "drop a sample" it changes the DFT definitions invalidating my first two lines.

Be a bit patient, and I'll clarify the puzzle remark.


Without a lot of explanation, here is the "puzzle solved". Note, that if x were to be interpolated using the upper k values as positive frequencies, there would be a whole lot of oscillations between the plotted points. Thus, so would Y.

Rescaling is muddled by the $ 2\pi $ factor on t and the $\frac{dn}{dt}$ factor, so I didn't bother cluttering the code as it isn't salient to the issue at hand.

import numpy as np import matplotlib.pyplot as plt

#========================================================= def main():

    N = 128

    t = np.linspace( 0, 1, N, False )
    x = np.cos( 2 * np.pi * t )

    X = np.fft.fft( x )

    plt.plot( x )
    plt.show()

    Y = np.zeros( N, dtype='complex' )

    for k in range( N ):
      Y[k] = X[k] * 1j * k

    y = np.fft.ifft( Y )

    plt.plot( y.real )
    plt.plot( y.imag )
    plt.show()


    Z = np.zeros( N, dtype='complex' )

    H = N &gt;&gt; 1

    for k in range( -H, H ):
      if k &gt;= 0:
         Z[k]   = X[k]   * 1j * k
      else:
         Z[k+N] = X[k+N] * 1j * k

    z = np.fft.ifft( Z )

    plt.plot( z.real )
    plt.plot( z.imag )
    plt.show()


#========================================================= main()

Here is the last plot:

enter image description here

Here is a test program for the formula:

import numpy as np

#============================================================================= def main():

    L  = 10000              # Length of Signals
    F  = 100                # Frame location 

    N  = 16                 # Frame Size = DFT Sample Count

    P = np.zeros( L )       # Position
    D = np.zeros( L )       # Difference

    for n in range( L ):
      P[n] = 1.2 + 0.3 * n + 0.045 * n * n

    for n in range( 1, L ):
      D[n] = P[n] - P[n-1]

    x = P[F:F+N]  
    y = D[F:F+N]

    X = np.fft.fft( x ) / N
    Y = np.fft.fft( y ) / N

    Z = np.zeros( N, dtype=complex )

    C = ( x[N-1] - P[F-1] ) / N

    for k in range( N ):
      Z[k] = X[k] * ( 1 - np.exp( -1j * ( 2.0 * np.pi / N ) * k ) ) + C

    for n in range( N ):
      print( "%2d %10.6f %10.6f   %10.6f %10.6f" % \
           ( n, Y[n].real, Y[n].imag, Z[n].real, Z[n].imag ) )


#============================================================================= main()

Here are the results:

 0   9.930000   0.000000     9.930000   0.000000
 1  -0.045000   0.226230    -0.045000   0.226230
 2  -0.045000   0.108640    -0.045000   0.108640
 3  -0.045000   0.067347    -0.045000   0.067347
 4  -0.045000   0.045000    -0.045000   0.045000
 5  -0.045000   0.030068    -0.045000   0.030068
 6  -0.045000   0.018640    -0.045000   0.018640
 7  -0.045000   0.008951    -0.045000   0.008951
 8  -0.045000   0.000000    -0.045000  -0.000000
 9  -0.045000  -0.008951    -0.045000  -0.008951
10  -0.045000  -0.018640    -0.045000  -0.018640
11  -0.045000  -0.030068    -0.045000  -0.030068
12  -0.045000  -0.045000    -0.045000  -0.045000
13  -0.045000  -0.067347    -0.045000  -0.067347
14  -0.045000  -0.108640    -0.045000  -0.108640
15  -0.045000  -0.226230    -0.045000  -0.226230
Cedron Dawg
  • 7,560
  • 2
  • 9
  • 24
  • "The response part of your edit should've been a comment". Nah, it was too long for a comment, I figured you'd see it anyway, or be notified of a change in an answer. – Cedron Dawg Sep 09 '20 at 15:11
  • Your continuous derivation appears invalid; tried it with a simple cosine - inverse yields negative of derivative in real part, but also an entire imaginary part (which should be zero), and normalization is also off. Problem seems to be in assuming $x[n] \rightarrow x(n)$; an inverse "inverts" the forward transformation, and a forward transform of a continuous signal would have $N=\infty$. – OverLordGoldDragon Sep 10 '20 at 13:03
  • How's it work with DTFT then? Not exactly sure, but I'd explain it as the encoding inserting as much information (i.e. $\omega$) as is decoded (inverted), and can thus be discretized arbitrarily. To contrary, the DFT of a signal demands as many coefficients as the number of points in the signal - whereas your inverse assumes a finite (or simply different) $N$, hence not decoding what was encoded. -- Lastly, it absurdly implies ability for infinite compression, as you're restoring an infinite $x(n)$ from $N$ finite coefficients. – OverLordGoldDragon Sep 10 '20 at 13:07
  • @OverLordGoldDragon This is a puzzler, I am looking at it. For the DTFT, you will have to center the spectrum on zero as -1 is not the same as N-1 in the interpolation. It helps that you used a degenerate case. One small point, I've added a ",False" parameter to your linspace call to not include the endpoint. Funny, since you mentioned that as an issue earlier. – Cedron Dawg Sep 10 '20 at 14:24
  • Good catch, and it is an issue; endpoints are akin to boundary conditions and shouldn't be treated lightly. The discrete derivative does accurately differentiate every case I've tried so far, but the slope addition seems to be a redundancy; DFT already assumes the input signal to be N-periodic, and where would we fetch $x[-1]$ anyway? The indices also should run from 1 to N - 1 instead of from 0, but I'm wondering how to 'justify' this restriction mathematically. – OverLordGoldDragon Sep 10 '20 at 14:34
  • @OverLordGoldDragon Sigh, there is so much wrong in this comment I don't know where to begin. The DFT does not assume the input signal to be N-periodic. See the comments on RB-Js long answer here to his "periodic deniers" claim: https://dsp.stackexchange.com/questions/70035 If the input signal were periodic then x[-1] = x[N-1]. The fact that this presumption is generally incorrect is why I answered differently than Olli, to include this. Usually, your analysis frame is a subset of the signal so the actual value is obtainable. More later. – Cedron Dawg Sep 10 '20 at 14:49
  • Read it - if anything, I entirely agree with you; I presumed what people mean by "assumes" is what you described there - but indeed the DFT couldn't care less about a signal's periodicity, so long as it's finite. What I meant is the extension (better term than "extrapolate" as latter implies approximating) is periodic in both domains, enabling various aliasing and symmetry analysis. The phrase could really use a reformulation. – OverLordGoldDragon Sep 10 '20 at 15:34
  • However, I'm unsure what you mean by "x[-1] = x[N - 1] is generally incorrect". If we agree the signal extends to left and right as repeated copies of itself (i.e. periodically) upon inversion, then x[-1] must be x[N-1]; have a counterexample? – OverLordGoldDragon Sep 10 '20 at 15:45
  • so Ced, i read through your entire answer, including the updated edits. what does a DFT periodicity denier do with $x[-1]$? – robert bristow-johnson Sep 11 '20 at 02:40
  • @OverLordGoldDragon says: //but indeed the DFT couldn't care less about a signal's periodicity, so long as it's finite.// $$ $$ that's bullshit. again, what if you multiply the DFT of $x[n]$ (which we call "$X[k]$") with $e^{j 2 \pi d k/N}$ (where $d$ is an integer)? you will find that the DFT *very much cares* about periodicity. – robert bristow-johnson Sep 11 '20 at 02:57
2

Complementing, and based on C. Dawg's answer, discarding the slope addition, the effect on magnitude and phase are

$$ \begin{aligned} |X[k]| & \rightarrow M|X[k]| \\ \angle{(X[k])} & \rightarrow \phi + \angle{(X[k])} \\ \end{aligned} $$

where

$$ \begin{aligned} M &= 2 \left| \sin{\left( \pi \frac{k}{N} \right)} \right| \\ \phi &= \frac{\pi}{N}(k\ \text{mod}\ N- N/2) \cdot \lceil k\ \text{mod}\ N \rceil \end{aligned} $$

The $\lceil k\ \text{mod}\ N \rceil$ sets $\phi$ to 0 when $k$ is a multiple of $N$, using the convention $\angle(0 + 0i)=0$. Graphically,

Note that the unwrapped $\phi$ is a straight line, so the time-domain effect is a time shift.


iDFT: indices should run from 1 to N - 1, as the finite difference drops a sample. Also, the slope addition is redundant; the inverse DFT extends the original signal N-periodicically, so it's zeroed.

Python implementation below; tested with random normal noise, which is a sort of "un-nicest" signal - mean absolute error is 1e-16, which is simply float error.

def d_idft(coef):
    N = len(coef)
    coef = coef * (1 - np.exp(-1j * 2 * np.pi / N * np.arange(N)))
    return np.fft.ifft(coef)[1:]

APPENDUM: below are my comments, trimmed from discussions below answers, summarizing important points/caveats, and contradicting the accepted answer. Didn't include other speakers as it'd get really long, but shown excerpts should hint at what's being responded to.


Slope term:

"discrete derivative meaningless" - this isn't about the discrete derivative, but about x[n] - x[n-1], whatever interpretation it may hold. Sometimes it's as good as a derivative, other times it's exact in the sense of undoing cumsum, yet other times virtually useless, but point is it's some time-domain transformation whose frequency-domain equivalent we seek

As to your discrete derivation; since there isn't an x[-1] to begin with, the only alternative is the inverse, which equals x[N-1], so again it drops.

It's an overcomplete representation. To encode an N-1 point derivative, it requires N+1 points of data (the original N coefficients plus x[-1]). The DFT is a complete encoding; we can invert it and differentiate in time domain without ever needing x[-1], so same must be possible working purely in the frequency domain.

Consider a counterexample; suppose we don't take x[-1] to come from inversion; then it must be of the original signal, which we framed, but where x[-1] exists. Suppose x[0] to x[N-1] are all zeros, and x[-1] = 100^100 ...


"DFT assumes the input is periodic"

I said the inversion, when extended, is periodic; the DTF describes only the portion of the original signal which was fed - not more, not less. We can extend in time domain to analyze aliasing etc., but can't make any description about the original signal outside of the frame.

The root of the debate is a lack of sufficient definitions, i.e. what "assumes" even means; you and I see it as non-fundamental to the transform itself, yet we also agree some contexts / operations demand periodicity.


Continuous derivation ($x(n)$)

An inverse "inverts" the forward transformation, and a forward transform of a continuous signal would have $N=\infty$. DTFT: the encoding inserts as much information (i.e. ω) as is decoded (inverted), and can thus be discretized arbitrarily. To contrary, the DFT of a signal demands as many coefficients as the number of points in the signal - whereas your inverse assumes a finite (or simply different) N, hence not decoding what was encoded.

It absurdly implies ability for infinite compression, as you're restoring an infinite x(n) from N finite coefficients.

But what if we simply discretize, $x'(n)\rightarrow x'[n]$? Then the compression argument falls. Your expression handled a linear chirp fairly well in terms of shape, comparing with np.diff, and since latter indeed isn't a "true" derivative, it's not obvious which is "more correct". There's math work to be done to justify continuous-differentiating and then discretizing again, but maybe it can be done after all.

Edit 1/10/2020: indeed, it can be done, and is interpreted as a discretized (not discrete) derivative of the trigonometric interpolation of $x[n]$ (not of $x(n)$), and the 'nicer' such interpolation fits $x[n]$, the more accurate the derivative. Stranger yet, discretized FT and DFT can work together sometimes.

However, Cedron's derivation still normalizes wrongly (but his code is correct); strangely, $x'[n]$ is found via discrete iFT (iDFT) of discretized FT derivative, so the extra $2\pi / N$ doesn't belong. ... or maybe it belongs under a different set of assumptions, but none I've yet come across.

OverLordGoldDragon
  • 8,912
  • 5
  • 23
  • 74
  • I'm afraid you are a bit incorrect here as well. First, it would be nicer if you used $Y[k]$ for your differential as Olli and I did to distinquish it from the original DFT. Second, you are still missing the proper factor in your sine argument. $\sin(k/2)$ would have a ton of oscillations across your range. $\sin\left( \frac{\pi}{N}k \right)$ has the argument going from 0 to $\pi$ as $k$ goes from 0 to $N$. Likewise the arg will range from $ -\frac{\pi}{2}$ to $ \frac{\pi}{2}$ without any wrapping. – Cedron Dawg Sep 09 '20 at 14:41
  • @CedronDawg Right, I had the k/2 fixed - didn't actually derive it, but tossed |1 - e^(-ix)| to WolframAlpha and forgot about everything else. As for $Y[k]$, I am symbolically showing the direct effect on the non-differentiated $X[k]$. Also good lead on the alternative derivative; I'm going by Python's Numpy's implementation, which may not necessarily be best - but for this Q&A we rather finish the started. – OverLordGoldDragon Sep 09 '20 at 14:45
  • @CedronDawg Also, $k$ goes from $0$ to $N-1$ in one period, and the $\angle (0 + 0i)$ case needs accounting for. – OverLordGoldDragon Sep 09 '20 at 14:46
  • When talking about the general behavior, whether you include N or not isn't a big deal. So, yeah, you should have one lobe of the sine function, starting at 0, almost reaching 0 again at the end at N-1. Not two lobes as you show. Similar with the angle case, indeed arg(0) is undefined, but I think you will find a limit argument will quell any discomfort. Just put an open circle in the zero position and you'll be good. – Cedron Dawg Sep 09 '20 at 15:16
  • I didn't notice your scale. The graph is correct and I did my first comment incorrectly. The arg in my equation goes from $ -\frac{\pi}{2}$ to $ -3 \frac{\pi}{2}$. You can add $\pi$ (instead of $2\pi$) and still be correct because of the absolute value on the magnitude part. So, it goes from $ \frac{\pi}{2}$ to $ - \frac{\pi}{2}$ as shown in your graph. – Cedron Dawg Sep 09 '20 at 15:31
  • We have gotten the chat prompt under my answer, so I am continuing here. I don't agree the signal extends itself on either side periodically or that that should be assumed. That only happens in special cases, so in general when placing a frame on a portion of a signal you can not assume that $ x[-1] = x[N-1] $, nor should you. You can count on that being true in the frequency domain $ X[k] = X[k+N] $ as well as on the extended inverse DFT of the spectrum, but you should not say, nor imply that, about the signal itself. – Cedron Dawg Sep 10 '20 at 19:37
  • @CedronDawg I said the inversion, when extended, is periodic; the DTF describes only the portion of the original signal which was fed - not more, not less. We can extend in time domain to analyze aliasing etc., but can't make any description about the original signal outside of the frame. -- and chat prompt is fine, just means a mod will collapse everything and we get more space, which I don't mind. – OverLordGoldDragon Sep 10 '20 at 19:47
  • @CedronDawg There's another problem with the slope; it's an overcomplete representation. To encode an N-1 point derivative, it requires N+1 points of data (the original N coefficients plus x[-1]). The DFT is a complete encoding; we can invert it and differentiate in time domain without ever needing x[-1], so same must be possible working purely in the frequency domain. Must and does; again, the DFT describes only the frame that was fed, that includes the derivative. The slope term won't tell us anything about what happens outside. – OverLordGoldDragon Sep 10 '20 at 19:54
  • I think your verbage was clunky. This is kind of the root cause of the confusion. The "signal" you get upon inversion, when extended, is not necessarily "the signal" you started with. They will match in the interval. I have no doubt that you (or RB-J) understand what the math does. So when I said "x[-1] = x[N - 1] is generally incorrect", I am talking about "the signal" not the extended inverse of the spectrum. – Cedron Dawg Sep 10 '20 at 19:54
  • @CedronDawg Right, it is what I'm saying, but RB-J asserts an assumption on the input itself, where I disagree. To finish previous comment; if you still disagree, it'd help to see an example where excluding the slope term yields different results than directly differentiating the framed signal. – OverLordGoldDragon Sep 10 '20 at 19:55
  • And that is where I disagree as well. The inclusion of the x[-1] in my answer was in response to your "dropped sample". I didn't realize that you meant the frame was shrunk (as per the np call). I think you can rework my answer with different limits on the summation and substitute in x[0] and (N-1) for x[-1] and N, respectively. – Cedron Dawg Sep 10 '20 at 20:01
  • @CedronDawg Alright, that's on me for ambiguity - if we're staying within 0 to N - 1 then we should be good to go; in fact as with the regular iDFT we don't need to pass the original signal, as we can use the already-inverted x[0] and x[N-1]. However, I don't see how you'd obtain x[0] and x[N-1] in the sum, and x[-1] seems appropriate as it cancels with x[N-1] in the extension - in fact including this slope worsens reconstruction error for some signals I tried, though very slightly. -- Also unsure which "puzzle" was solved, surely not $x'(n)$. – OverLordGoldDragon Sep 10 '20 at 21:25
  • We should be good to go. I think this has been hashed out sufficiently, see my latest followup. – Cedron Dawg Sep 10 '20 at 22:08
1

Response to RB-J's comments under my other answer:

"what does a DFT periodicity denier do with x[−1]?"

"what if you multiply the DFT of x[n] (which we call "X[k]") with ej2πdk/N (where d is an integer)? you will find that the DFT very much cares about periodicity. – "

And similar from Making the units of a analytically calculated PSD consistent with the units of an FFT

"//the DFT does not consider what the signal is outside the frame// ... never? ---okay @CedronDawg, what does the DFT "consider" when you multiply X[k] with ej2πdk/N where d∈Z? (all of the X[k] for 0≤k<N.) – robert bristow-johnson 4 hours ago "

Okay, let's start with a signal with lots and lots of samples. Suppose I want to analyze a section of it with N samples in it. To do so, I will establish an analysis frame. This means re-indexing the samples so that the first sample of my analysis frame is at index 0. Since I intend to take a DFT on this section, I can also call it a DFT frame.

This is not the same thing as "windowing". I could also define a rectangle window function having ones for all the samples in my section, and zeroes everywhere else. The window function is indexed on the underlying indexing, it does not establish a new indexing frame.

If I wanted to, I could also define a window function relative to my analysis frame. A rectangle window spanning my frame would be inert/moot in terms of taking the DFT. You can consider the DFT definition as having an implicit window like this, but it is not part of the definition. If it were the unnormalized definition would be:

$$ X[k] = \sum_{n=0}^{N-1} 1 \cdot x[n] \cdot e^{-i \frac{2\pi}{N} kn } $$

So, if the section is not at the start of the signal, $x[-1]$ would refer to the sample just prior to my analysis frame. If the section is at the start of the signal then the correct answer would be that $x[-1]$ is undefined. If one had to make an assumption about it, the most logical answer would be a value of zero.

Now, suppose we take the DFT of this section which yields $ X[k] $ values for $ 0 <= k < N $. Values outside this range can also be evaluated by the defintion and will result in a periodic spectrum with a period of N. This may not be the fundamental period, but that is irrelevant to this discussion.

We are now at the starting point of the OP's question (something I misunderstood at first).

Given the values of $X[k]$ we can apply the inverse DFT and reproduce the $x[n]$ values for $ 0 <= n < N $, the samples within the analysis frame. As with the spectrum, the formula also can be evaluated for $n$ values outside the analysis frame. Likewise, the resulting values will form a periodic sequence with period N. There is absolutely no reason to assume, assert, or define that these extended values should match the original signal values outside the analysis frame. If you wish to label that as denial, well ....

It is also impossible, given just the $X[k]$ values to determine what the original $x[-1]$ value is. The value obtained by extending the inverse DFT will be the same as $x[N-1]$. Again, there is no foundation to stipulate, assume, assert, maintain, or proclaim that this matches the original signal value.

Suppose now that I multiply/twist up/modulate the spectrum using a factor of $ e^{i \frac{2\pi}{N} dk } $. Okay, let's do the math.

$$ Z[k] = X[k] \cdot e^{i \frac{2\pi}{N} dk } $$

Take the inverse DFT:

$$ \begin{aligned} z[n] &= \frac{1}{N} \sum_{k=0}^{N-1} Z[k] \cdot e^{ i \frac{2\pi}{N} nk } \\ &= \frac{1}{N} \sum_{k=0}^{N-1} X[k] \cdot e^{i \frac{2\pi}{N} dk } \cdot e^{ i \frac{2\pi}{N} nk } \\ &= \frac{1}{N} \sum_{k=0}^{N-1} X[k] \cdot e^{ i \frac{2\pi}{N} (n+d)k } \\ \end{aligned} $$

Let $ m = n + d $ so $ n = m - d $.

$$ z[n] = \frac{1}{N} \sum_{k=0}^{N-1} X[k] \cdot e^{ i \frac{2\pi}{N} mk } = x[m \text{ mod } N] = x[ ( n + d ) \text{ mod } N] $$

The end result is that the samples in the analysis frame have been rotated by $d$ samples.

As above, $z[n]$ can be extended using the inverse DFT definition outside the analysis frame forming an N periodic sequence. There is even less rationale to assume that those values will match the original signal.

The OP's question was suppose that $ y[n] = x[n]-x[n-1] $, can you find $Y[k]$ from $X[k]$ without going through the taking the inverse DFT, taking the difference, and then taking the DFT.

The OP stated that $x[n]$ came from the inverse DFT of $X[k]$, whereas I mistakenly assumed the original $x[n]$ was known.

Then answer was given elegantly by Olli. And derived from definition in my answer. The condition that $x[-1]= x[N-1]$ is met for the extended reconstructed signal.

Thus spoke this periodicity denier.

Cedron Dawg
  • 7,560
  • 2
  • 9
  • 24
  • This is great - we're almost doing research here with this 'debate'. Maybe I should dedicate a Q&A to it, though, as we're going quite a bit off topic. – OverLordGoldDragon Sep 11 '20 at 10:41
  • @OverLordGoldDragon Actually I consider it a huge waste of my time. However, if it can drive a stake in the heart of the incredibly misleading assertion that "The DFT assumes the signal is periodic" for at least one person, it was worth it. $$ $$ I hope you can see that RB-J's "challenge" is pertinent to your question. The value of $X[k]\left( 1 - e^{-i\frac{2\pi}{N}k} \right)$ can be seen as the DFT of the original signal minus the original signal rotated by one sample. – Cedron Dawg Sep 11 '20 at 14:30
  • Can relate with your disposition, but mind that we're dealing with mathematics and there's no room for subjective disagreement; everything can be resolved exactly. The root of the debate is a lack of sufficient definitions, i.e. what "assumes" even means; you and I see it as non-fundamental to the transform itself, yet we also agree some contexts / operations demand periodicity. Explaining this, and explicitly clarifying when periodicity does and does not apply, may create a world-first complete primer on this topic (at least the freely-accessible one). – OverLordGoldDragon Sep 11 '20 at 14:37
  • Puzzle not solved; try x = np.random.randn(N). I'm unsure you followed my argument from information standpoint; your derivation implies absurdities. It's not a math typo, it's literally impossible. – OverLordGoldDragon Sep 11 '20 at 15:43
  • @OverLordGoldDragon That's mighty praise, but I doubt it deserves it. Two points to emphasize though: $$ $$
    1. Definitions are important

    $$ $$ 2. If you can't derive an assertion from definitions then you don't really understand it $$ $$ I'd also say there is room for subjectivity in Mathematics in the form of unproven conjectures and any results derived by assuming their truth (or falsehood). I would use the term "exhibit periodicity" rather than "demand periodicity".

    – Cedron Dawg Sep 11 '20 at 15:44
  • Well, okay, let's say unstated premises rather than a definitional issue. Taking a derivative on a discrete set of values is meaningless. Taking the derivative of a differentiable interpolation function is possible. The "trigonometric interpolation function" is differentiable. So the derivative value you get at the sample locations is merely the derivative of the interpolation function. Of course, there is a wide choice of differentiable interpolation functions, so which you choose is a subjective decision. – Cedron Dawg Sep 11 '20 at 15:54
  • "The OP stated that x[n] came from the inverse DFT of X[k], whereas I mistakenly assumed the original x[n] was known" - no, I didn't, but this shouldn't matter. -- As for all else; I'm not sure arguments along the lines of "manipulate frequency this and that way" are always meaningful, or relevant to what "assumes periodicity" is supposed to mean. Indeed, it can be useful to analyze effects of said modifications in these terms, e.g. circular convolution yielding time-domain aliasing due to overflow, but that's nothing to do with "input must be periodic for DFT to work". – OverLordGoldDragon Sep 11 '20 at 15:58
  • "discrete derivative meaningless" - this isn't about the discrete derivative, but about x[n] - x[n-1], whatever interpretation it may hold. Sometimes it's as good as a derivative, other times it's exact in the sense of undoing cumsum, yet other times virtually useless, but point is it's some time-domain transformation whose frequency-domain equivalent we seek - and we can't do $x(n)$ as that implies infinite compression. – OverLordGoldDragon Sep 11 '20 at 16:01
  • As to your discrete derivation; since there isn't an x[-1] to begin with, the closest (or rather the only) alternative is the inverse, which equals x[N-1], so again it drops. Again, the DFT "knows" nothing outside the analysis frame. Then there's the "overcompleteness" issue. Also, consider a counterexample; suppose we don't take x[-1] to come from inversion; then it must be of the original signal, which we framed, but where x[-1] exists. Suppose x[0] to x[N-1] are all zeros, and x[-1] = 100^100 ... 3.6 Roentgen. – OverLordGoldDragon Sep 11 '20 at 16:07
  • Damn chat prompt again. (No I don't like disturbing the mods). Perhaps for a discussion you can post something at https://www.dsprelated.com/forums. The "puzzle solved" pertained to the derivative question in your opening sentence. The differential question pertains to the $x[n] -x[n-1]$ aspect. Did you not say "where $x[n] = \sum_{k=0}^{N-1} X[k] e^{j2\pi kn/N}$" in the question? If so, and taken at face value, $x[n]$ is the periodic extension sequence, not the original signal from whence $X[k]$ was presumably obtained. – Cedron Dawg Sep 11 '20 at 16:15
  • They ought to give us the option. I usually avoid non-SE forums as they tend to house an inferior knowledgepool and responsiveness - so suppose eventually I'll get back to this topic and we'll have more space to straighten matters in a Q&A. Now, to your answer: I'll accept, but only if you remove or note the following: the slope term is necessarily zero; the continuous derivation is invalid. If you disagree, we need to make more space somehow ... perhaps below my question? – OverLordGoldDragon Sep 11 '20 at 16:20
  • I would say the knowledge pool at dsprelated is a stark exception to your rule. It is much slower moving and not as broad, but quite deep. Many of the best posters here frequent there as well (including RB-J). fred harris even pops by (though he hasn't commented on my windows article) dsprelated.com/showarticle/1365.php Go ahead give the accept to Olli. The clarifications you seek are amply available to any one who takes the effort to follow the comments. It has been my observation that upvotes and acceptances in this forum aren't very reflective of the quality of answers as I see them. – Cedron Dawg Sep 11 '20 at 16:28
  • Alright, noted @dsprelated. But why Olli's? You've given by far the most comprehensive answer and comments discussion in this Q&A; I'd have learned far less if not for our back & forths. Only you can bear the mark of acceptance. At the least add a note sentence about those two points, probably on top of the answer - else I can do it myself in the question. – OverLordGoldDragon Sep 11 '20 at 16:33
  • Off-topic; do you have or know of an article on a visual or otherwise intuitive explanation of "time-domain conv. <=> freq-domain mult."? Could open a Q&A on this - not looking for an equation salad, already ate it. – OverLordGoldDragon Sep 13 '20 at 21:14
  • @OverLordGoldDragon Off-topic???!!!! Honestly I am not the best person to answer that. I don't really think in those terms. Perhaps you should post that as a new question. Maybe I'll opine more in a while. Very abstractly, Olli painted the ceiling, and I built the scaffolding. To get a feel for it, I would recommend that you redo my derivation and generalize the "slope term" for any $ y[n] = \sum_{k} h[n-k] \cdot x[n] $ in terms of $h$. For the OP, h[0] = 1, h[1] = -1. Keep in mind, index order is what distinguishes a convolution from a sliding weighted average. – Cedron Dawg Sep 14 '20 at 01:23
  • Done, though I'll be less active the coming days - stuff to do. Briefly, I thought of your x'(n), and how you mentioned trigonometric interpolation; so, to make things valid, it must be stated that x(n) or x'(n) are interpolations, not exact, original signals, and that exactness is only at integer n from 0 to N - 1. That's completely fine - but otherwise you claim ability to restore an infinite signal from N coefficients, and the day you do that we'll become a Type 3 civilization. – OverLordGoldDragon Sep 14 '20 at 08:57
0

WINNER: Olli's solution:

$$ F_{N-1}[k] = X_{N-1}[k]\big(e^{j2\pi k / (N - 1)} - 1\big) + (x[N-1] - x[0])e^{j2\pi k/(N-1)} $$

Code + Demo:

def dft(x):
    return np.fft.fft(x[:-1])

def d_idft(coef, x): M = len(x) - 1 # N - 1 exp = np.exp(1j * (2 * np.pi / M) * np.arange(M)) coef = coef * (exp - 1) + (x[-1] - x[0]) * exp return np.fft.ifft(coef)

Though, problem not entirely solved. All properties 1 & 2 met, negligible reconstruction loss, and very small difference in coefficients computed directly from DFT(DIFF(x)) and this method. "Very small" as in 1e-12, which might be FFT's float error. -- Comparison code.

  1. Need a relation in terms of $X_N=\text{FFT}(x)$, not $X_{N−1}=\text{FFT}(x[\text{:-1}])$
  2. Cannot require the original signal as input to compute $F_{N−1}$ (irrecoverable from $X_{N−1}$).

Since Olli worked out an expression with 1 fewer coefficient ($X_{N−1}$), in a way a harder problem was solved, so a workaround to (1) and (2) seems possible.


This answer aims to further refine the problem, test proposed solutions, and explicate flaws in other answers to guide a resolution.

Slope term: it's needed, whether $x$ is periodic or not. Without it, $Y[0] \neq \sum_{n=1}^{N-1} (x[n] - x[n-1])$. It somewhat ties to the Fundamental Theorem of Calculus; if the finite difference is the derivative, then $x[n]$ is the antiderivative of the function that's the finite difference, so the sum for $Y[0]$ is quite simply $x[N-1]-x[0]$ -- [$\int_a^b f'(x)dx=f(b)-f(a)$].

More precisely, $x[n-1]-x[0]$ equals the net finite difference on the interval; try to force this value to be anything else without changing the endpoints - you won't.


Answer properties - what the resultant expression must satisfy:

  1. len(Y) == N - 1; the finite difference uses 1 fewer term than x
  2. $Y[0] = \sum_{n=1}^{N-1} (x[n] - x[n-1])$
  3. $Y$ is expressed in terms of $X$, $j$, $n$, $N$, and maybe its own index - no other parameter; $x$ is fine as long as within $[0, N-1]$, as that's recoverable from $X$.

Properties justification:

Olli's answer suggests a shift with a N+1-point DFT&IDFT; this is invalid, and so is any attempt at not dropping a sample. It may prove useful in some applications, but is fundamentally flawed and hence not general-purpose. Here's why.

Recall $+C$ when integrating? Same story: differentiation loses information. Let $x'[n] = x[n] - x[n-1]$. We cannot restore $x[n]$ from $x'[n]$, only its shape; the DC offset is missing. If we know any one original sample, $x[h]$, we can restore the whole signal from $x'[n]$, since we know $x[N-1]-x[0] = \sum x'[n]$, and we'll find $x[n-1]$ via cumulative sum starting at $x[h]$. It's analogous to an Initial Value Problem.


Cedron's result,

$$ Y[k] = X[k]\left( 1 - e^{-i\frac{2\pi}{N}k} \right) + (x[N-1]-x[-1]), $$

meets none of the properties, but it clearly steps in the right direction.


Cedron's result + mod, tested (mod = $x[0]$ instead of $x[-1]$):

The three substantially different magnitude & phase spectra all invert-transform to almost exactly the same signal. Any error seems attributable to float imprecision. Green here is the "ground truth". -- Code.

What if we modify d_idft per above rationale? i.e., drop coef[0], the DC term:

def d_idft(coef, x, slope=True):
    M = len(coef) - 1
    coef = coef[1:] * (1 - np.exp(-1j * 2 * np.pi / M * np.arange(M)))
    coef += (x[-1] - x[0])
    return np.fft.ifft(coef)

The error grows significant - no good. Also note I pass x as input for convenience only.


If what we have already works, why look further? (i.e. MAE within float64) - because it's simply wrong, and just because I haven't found a signal with significant MAE, doesn't mean it doesn't exist, or that there aren't graver implications in practice.


Test script: you know your solution works if it passes this script.

OverLordGoldDragon
  • 8,912
  • 5
  • 23
  • 74