Advertisement
Guest User

Fourier transform based polynomial arithmetic

a guest
May 2nd, 2020
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /******/ (function(modules) { // webpackBootstrap
  2. /******/    // The module cache
  3. /******/    var installedModules = {};
  4. /******/
  5. /******/    // The require function
  6. /******/    function __webpack_require__(moduleId) {
  7. /******/
  8. /******/        // Check if module is in cache
  9. /******/        if(installedModules[moduleId]) {
  10. /******/            return installedModules[moduleId].exports;
  11. /******/        }
  12. /******/        // Create a new module (and put it into the cache)
  13. /******/        var module = installedModules[moduleId] = {
  14. /******/            i: moduleId,
  15. /******/            l: false,
  16. /******/            exports: {}
  17. /******/        };
  18. /******/
  19. /******/        // Execute the module function
  20. /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  21. /******/
  22. /******/        // Flag the module as loaded
  23. /******/        module.l = true;
  24. /******/
  25. /******/        // Return the exports of the module
  26. /******/        return module.exports;
  27. /******/    }
  28. /******/
  29. /******/
  30. /******/    // expose the modules object (__webpack_modules__)
  31. /******/    __webpack_require__.m = modules;
  32. /******/
  33. /******/    // expose the module cache
  34. /******/    __webpack_require__.c = installedModules;
  35. /******/
  36. /******/    // identity function for calling harmony imports with the correct context
  37. /******/    __webpack_require__.i = function(value) { return value; };
  38. /******/
  39. /******/    // define getter function for harmony exports
  40. /******/    __webpack_require__.d = function(exports, name, getter) {
  41. /******/        if(!__webpack_require__.o(exports, name)) {
  42. /******/            Object.defineProperty(exports, name, {
  43. /******/                configurable: false,
  44. /******/                enumerable: true,
  45. /******/                get: getter
  46. /******/            });
  47. /******/        }
  48. /******/    };
  49. /******/
  50. /******/    // getDefaultExport function for compatibility with non-harmony modules
  51. /******/    __webpack_require__.n = function(module) {
  52. /******/        var getter = module && module.__esModule ?
  53. /******/            function getDefault() { return module['default']; } :
  54. /******/            function getModuleExports() { return module; };
  55. /******/        __webpack_require__.d(getter, 'a', getter);
  56. /******/        return getter;
  57. /******/    };
  58. /******/
  59. /******/    // Object.prototype.hasOwnProperty.call
  60. /******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  61. /******/
  62. /******/    // __webpack_public_path__
  63. /******/    __webpack_require__.p = "";
  64. /******/
  65. /******/    // Load entry module and return exports
  66. /******/    return __webpack_require__(__webpack_require__.s = 2);
  67. /******/ })
  68. /************************************************************************/
  69. /******/ ([
  70.  
  71. /* 0 */
  72. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  73.  
  74. "use strict";
  75. class ComplexArray {
  76.   constructor(other, arrayType = Float32Array) {
  77.     if (other instanceof ComplexArray) {
  78.       // Copy constuctor.
  79.       this.ArrayType = other.ArrayType;
  80.       this.real = new this.ArrayType(other.real);
  81.       this.imag = new this.ArrayType(other.imag);
  82.     } else {
  83.       this.ArrayType = arrayType;
  84.       // other can be either an array or a number.
  85.       this.real = new this.ArrayType(other);
  86.       this.imag = new this.ArrayType(this.real.length);
  87.     }
  88.  
  89.     this.length = this.real.length;
  90.   }
  91.  
  92.   toString() {
  93.     const components = [];
  94.  
  95.     this.forEach((value, i) => {
  96.       components.push(
  97.         `(${value.real.toFixed(2)}, ${value.imag.toFixed(2)})`
  98.       );
  99.     });
  100.  
  101.     return `[${components.join(', ')}]`;
  102.   }
  103.  
  104.   forEach(iterator) {
  105.     const n = this.length;
  106.     // For gc efficiency, re-use a single object in the iterator.
  107.     const value = Object.seal(Object.defineProperties({}, {
  108.       real: {writable: true}, imag: {writable: true},
  109.     }));
  110.  
  111.     for (let i = 0; i < n; i++) {
  112.       value.real = this.real[i];
  113.       value.imag = this.imag[i];
  114.       iterator(value, i, n);
  115.     }
  116.   }
  117.  
  118.   // In-place mapper.
  119.   map(mapper) {
  120.     this.forEach((value, i, n) => {
  121.       mapper(value, i, n);
  122.       this.real[i] = value.real;
  123.       this.imag[i] = value.imag;
  124.     });
  125.  
  126.     return this;
  127.   }
  128.  
  129.   conjugate() {
  130.     return new ComplexArray(this).map((value) => {
  131.       value.imag *= -1;
  132.     });
  133.   }
  134.  
  135.   magnitude() {
  136.     const mags = new this.ArrayType(this.length);
  137.  
  138.     this.forEach((value, i) => {
  139.       mags[i] = Math.sqrt(value.real*value.real + value.imag*value.imag);
  140.     })
  141.  
  142.     return mags;
  143.   }
  144. }
  145. /* harmony export (immutable) */ __webpack_exports__["a"] = ComplexArray;
  146.  
  147. /***/ }),
  148. /* 1 */
  149. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  150.  
  151. "use strict";
  152. /* unused harmony export FFT */
  153. /* unused harmony export InvFFT */
  154. /* unused harmony export frequencyMap */
  155. /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__complex_array__ = __webpack_require__(0);
  156.  
  157.  
  158. // Math constants and functions we need.
  159. const PI = Math.PI;
  160. const SQRT1_2 = Math.SQRT1_2;
  161.  
  162. function FFT(input) {
  163.   return ensureComplexArray(input).FFT();
  164. };
  165.  
  166. function InvFFT(input) {
  167.   return ensureComplexArray(input).InvFFT();
  168. };
  169.  
  170. function frequencyMap(input, filterer) {
  171.   return ensureComplexArray(input).frequencyMap(filterer);
  172. };
  173.  
  174. class ComplexArray extends __WEBPACK_IMPORTED_MODULE_0__complex_array__["a" /* default */] {
  175.   FFT() {
  176.     return fft(this, false);
  177.   }
  178.  
  179.   InvFFT() {
  180.     return fft(this, true);
  181.   }
  182.  
  183.   FFT_mult(that) {
  184.      // Complexity: O(n * log(n))
  185.     const n = this.length;
  186.     fft(this, false);
  187.     fft(that, false); // discarded
  188.     for (let i = 0; i < n; i++) {
  189.       // pointwise multiplication
  190.       let real = this.real[i] * that.real[i] - this.imag[i] * that.imag[i];
  191.       let imag = this.real[i] * that.imag[i] + this.imag[i] * that.real[i];
  192.       this.real[i] = real;
  193.       this.imag[i] = imag;
  194.     }
  195.     return fft(this, true);
  196.   }
  197.  
  198.   FFT_sq() {
  199.     // Complexity: O(n * log(n))
  200.     const n = this.length;
  201.     fft(this, false);
  202.     for (let i = 0; i < n; i++) {
  203.       // pointwise multiplication
  204.       let real = this.real[i] * this.real[i] - this.imag[i] * this.imag[i];
  205.       let imag = 2 * this.real[i] * this.imag[i];
  206.       this.real[i] = real;
  207.       this.imag[i] = imag;
  208.     }
  209.     return fft(this, true);
  210.   }
  211.  
  212.   FFT_recip() {
  213.     // y *= (2 - x * y)
  214.     // Complexity: O(n * log(n)^2)
  215.     const n = this.length;
  216.     const m = 2 * n;
  217.     let out = new ComplexArray(m);
  218.     let a = new ComplexArray(m);
  219.     let b = new ComplexArray(m);
  220.     let x = 1 / (this.real[0] ** 2 + this.imag[0] ** 2);
  221.     let prec = 1;
  222.     out.real[0] =  this.real[0] * x;
  223.     out.imag[0] = -this.imag[0] * x;
  224.     do
  225.     {
  226.       for (let i = 0; i < n; i++) {
  227.         a.real[i] = out.real[i];
  228.         a.imag[i] = out.imag[i];
  229.         b.real[i] = this.real[i];
  230.         b.imag[i] = this.imag[i];
  231.       }
  232.       a.FFT_mult(b);
  233.       a.real[0] = 2 - a.real[0];
  234.       a.imag[0] = a.imag[0];
  235.       for (let i = 1; i < n; i++) {
  236.         a.real[i] = -a.real[i];
  237.         a.imag[i] = -a.imag[i];
  238.       }
  239.       for (let i = n; i < m; i++) {
  240.         a.real[i] = 0;
  241.         a.imag[i] = 0;
  242.         b.real[i] = 0;
  243.         b.imag[i] = 0;
  244.       }
  245.       out.FFT_mult(a);
  246.       for (let i = n; i < m; i++) {
  247.         a.real[i] = 0;
  248.         a.imag[i] = 0;
  249.         out.real[i] = 0;
  250.         out.imag[i] = 0;
  251.       }
  252.       prec *= 2;
  253.     }
  254.     while (prec < n);
  255.     for (let i = 0; i < n; i++) {
  256.       this.real[i] = out.real[i];
  257.       this.imag[i] = out.imag[i];
  258.     }
  259.   }
  260.  
  261.   // Applies a frequency-space filter to input, and returns the real-space
  262.   // filtered input.
  263.   // filterer accepts freq, i, n and modifies freq.real and freq.imag.
  264.   frequencyMap(filterer) {
  265.     return this.FFT().map(filterer).InvFFT();
  266.   }
  267. }
  268. /* harmony export (immutable) */ __webpack_exports__["a"] = ComplexArray;
  269.  
  270.  
  271. function ensureComplexArray(input) {
  272.   return input instanceof ComplexArray && input || new ComplexArray(input);
  273. }
  274.  
  275. function fft(input, inverse) {
  276.   const n = input.length;
  277.  
  278.   if (n & (n - 1)) {
  279.     return FFT_Recursive(input, inverse);
  280.   } else {
  281.     return FFT_2_Iterative(input, inverse);
  282.   }
  283. }
  284.  
  285. function FFT_Recursive(input, inverse) {
  286.   const n = input.length;
  287.  
  288.   if (n === 1) {
  289.     return input;
  290.   }
  291.  
  292.   const output = new ComplexArray(n, input.ArrayType);
  293.  
  294.   // Use the lowest odd factor, so we are able to use FFT_2_Iterative in the
  295.   // recursive transforms optimally.
  296.   const p = LowestOddFactor(n);
  297.   const m = n / p;
  298.   const factor = inverse ? 1 / p : 1;
  299.   let recursive_result = new ComplexArray(m, input.ArrayType);
  300.  
  301.   // Loops go like O(n Σ p_i), where p_i are the prime factors of n.
  302.   // for a power of a prime, p, this reduces to O(n p log_p n)
  303.   for(let j = 0; j < p; j++) {
  304.     for(let i = 0; i < m; i++) {
  305.       recursive_result.real[i] = input.real[i * p + j];
  306.       recursive_result.imag[i] = input.imag[i * p + j];
  307.     }
  308.     // Don't go deeper unless necessary to save allocs.
  309.     if (m > 1) {
  310.       recursive_result = fft(recursive_result, inverse);
  311.     }
  312.  
  313.     const del_f_r = Math.cos(2*PI*j/n);
  314.     const del_f_i = (inverse ? -1 : 1) * Math.sin(2*PI*j/n);
  315.     let f_r = 1;
  316.     let f_i = 0;
  317.  
  318.     for(let i = 0; i < n; i++) {
  319.       const _real = recursive_result.real[i % m];
  320.       const _imag = recursive_result.imag[i % m];
  321.  
  322.       output.real[i] += f_r * _real - f_i * _imag;
  323.       output.imag[i] += f_r * _imag + f_i * _real;
  324.  
  325.       [f_r, f_i] = [
  326.         f_r * del_f_r - f_i * del_f_i,
  327.         f_i = f_r * del_f_i + f_i * del_f_r,
  328.       ];
  329.     }
  330.   }
  331.  
  332.   // Copy back to input to match FFT_2_Iterative in-placeness
  333.   // TODO: faster way of making this in-place?
  334.   for(let i = 0; i < n; i++) {
  335.     input.real[i] = factor * output.real[i];
  336.     input.imag[i] = factor * output.imag[i];
  337.   }
  338.  
  339.   return input;
  340. }
  341.  
  342. function FFT_2_Iterative(input, inverse) {
  343.   const n = input.length;
  344.  
  345.   const output = BitReverseComplexArray(input);
  346.   const output_r = output.real;
  347.   const output_i = output.imag;
  348.   // Loops go like O(n log n):
  349.   //   width ~ log n; i,j ~ n
  350.   let width = 1, factor = inverse ? 0.5 : 1;
  351.   while (width < n) {
  352.     const del_f_r = Math.cos(PI/width);
  353.     const del_f_i = (inverse ? -1 : 1) * Math.sin(PI/width);
  354.     for (let i = 0; i < n/(2*width); i++) {
  355.       let f_r = 1;
  356.       let f_i = 0;
  357.       for (let j = 0; j < width; j++) {
  358.         const l_index = 2*i*width + j;
  359.         const r_index = l_index + width;
  360.  
  361.         const left_r = output_r[l_index];
  362.         const left_i = output_i[l_index];
  363.         const right_r = f_r * output_r[r_index] - f_i * output_i[r_index];
  364.         const right_i = f_i * output_r[r_index] + f_r * output_i[r_index];
  365.  
  366.         output_r[l_index] = factor * (left_r + right_r);
  367.         output_i[l_index] = factor * (left_i + right_i);
  368.         output_r[r_index] = factor * (left_r - right_r);
  369.         output_i[r_index] = factor * (left_i - right_i);
  370.  
  371.         [f_r, f_i] = [
  372.           f_r * del_f_r - f_i * del_f_i,
  373.           f_r * del_f_i + f_i * del_f_r,
  374.         ];
  375.       }
  376.     }
  377.     width <<= 1;
  378.   }
  379.  
  380.   return output;
  381. }
  382.  
  383. function BitReverseIndex(index, n) {
  384.   let bitreversed_index = 0;
  385.  
  386.   while (n > 1) {
  387.     bitreversed_index <<= 1;
  388.     bitreversed_index += index & 1;
  389.     index >>= 1;
  390.     n >>= 1;
  391.   }
  392.   return bitreversed_index;
  393. }
  394.  
  395. function BitReverseComplexArray(array) {
  396.   const n = array.length;
  397.   const flips = new Set();
  398.  
  399.   for(let i = 0; i < n; i++) {
  400.     const r_i = BitReverseIndex(i, n);
  401.  
  402.     if (flips.has(i)) continue;
  403.  
  404.     [array.real[i], array.real[r_i]] = [array.real[r_i], array.real[i]];
  405.     [array.imag[i], array.imag[r_i]] = [array.imag[r_i], array.imag[i]];
  406.  
  407.     flips.add(r_i);
  408.   }
  409.  
  410.   return array;
  411. }
  412.  
  413. function LowestOddFactor(n) {
  414.   const sqrt_n = Math.sqrt(n);
  415.   let factor = 3;
  416.  
  417.   while(factor <= sqrt_n) {
  418.     if (n % factor === 0) return factor;
  419.     factor += 2;
  420.   }
  421.   return n;
  422. }
  423.  
  424. /***/ }),
  425. /* 2 */
  426. /***/ (function(module, exports, __webpack_require__) {
  427.  
  428. module.exports = __webpack_require__(1);
  429. window.FFT_polynomial = module.exports["a" /* ComplexArray */];
  430.  
  431. /***/ })
  432. /******/ ]);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement