license import { GPGPUProgram } from './gpgpu_math' ; export class FFTProgram implements GPGPUProgram { variableNames = [ 'real' , 'imag' ] ; outputShape : number [ ] ; userCode : string ; constructor ( component , inputShape , inverse ) { const innerDim = inputShape [ 1 ] ; this . outputShape = inputShape ; const exponentMultiplierSnippet = inverse ? `2.0 * ${ Math . PI }` : `-2.0 * ${ Math . PI }` ; const resultDenominator = inverse ? `${ innerDim }.0` : '1.0' ; let opString ; if ( component === 'real' ) { opString = 'return real * expR - imag * expI;' ; } else if ( component === 'imag' ) { opString = 'return real * expI + imag * expR;' ; } else { throw new Error ( `FFT component must be either "real" or "imag", got ${ component }.` ) ; } this . userCode = `
      const float exponentMultiplier = ${ exponentMultiplierSnippet };

      float unaryOpComplex(float real, float expR, float imag, float expI) {
        ${ opString }
      }

      float mulMatDFT(int batch, int index) {
        float indexRatio = float(index) / float(${ innerDim });
        float exponentMultiplierTimesIndexRatio =
            exponentMultiplier * indexRatio;

        float result = 0.0;

        for (int i = 0; i < ${ innerDim }; i++) {
          // x = (-2|2 * PI / N) * index * i;
          float x = exponentMultiplierTimesIndexRatio * float(i);
          float expR = cos(x);
          float expI = sin(x);
          float real = getReal(batch, i);
          float imag = getImag(batch, i);

          result +=
              unaryOpComplex(real, expR, imag, expI) / ${ resultDenominator };
        }

        return result;
      }

      void main() {
        ivec2 coords = getOutputCoords();
        setOutput(mulMatDFT(coords[0], coords[1]));
      }
    ` ; } }