text: /**
   * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  // Make sure to run node with --expose-gc option!
  
  // The times are reliable if about 1% relative mean error if you run it:
  
  // * immediately after restart
  // * with 100% battery charge
  // * not connected to network
  
  /* eslint import/no-extraneous-dependencies: "off" */
  
  const Benchmark = require('benchmark');
  const diffBaseline = require('diff').diffLines;
  const diffImproved = require('../build/index.js').default;
  
  const testBaseline = (a, b) => {
    const benchmark = new Benchmark({
      fn() {
        diffBaseline(a, b);
      },
      name: 'baseline',
      onCycle() {
        globalThis.gc(); // after run cycle
      },
      onStart() {
        globalThis.gc(); // when benchmark starts
      },
    });
  
    benchmark.run({async: false});
  
    return benchmark.stats;
  };
  
  const testImproved = function (a, b) {
    const benchmark = new Benchmark({
      fn() {
        // Split string arguments to make fair comparison with baseline.
        const aItems = a.split('\n');
        const bItems = b.split('\n');
  
        const isCommon = (aIndex, bIndex) => aItems[aIndex] === bItems[bIndex];
  
        // This callback obviously does less than baseline `diff` package,
        // but avoiding double work and memory churn is the goal.
        // For example, `jest-diff` has had to split strings that `diff` joins.
        const foundSubsequence = () => {};
  
        diffImproved(aItems.length, bItems.length, isCommon, foundSubsequence);
      },
      name: 'improved',
      onCycle() {
        globalThis.gc(); // after run cycle
      },
      onStart() {
        globalThis.gc(); // when benchmark starts
      },
    });
  
    benchmark.run({async: false});
  
    return benchmark.stats;
  };
  
  const writeHeading2 = () => {
    console.log('## Benchmark time for `diff-sequences` versus `diff`\n');
    console.log('A ratio less than 1.0 means `diff-sequences` is faster.');
  };
  
  const writeHeading3 = n => {
    console.log(`\n### n = ${n}\n`);
    console.log('| name | % | ratio | improved | rme | baseline | rme |');
    console.log('| :--- | ---: | :--- | :--- | ---: | :--- | ---: |');
  };
  
  const writeRow = (name, percent, statsImproved, statsBaseline) => {
    const {mean: meanImproved, rme: rmeImproved} = statsImproved;
    const {mean: meanBaseline, rme: rmeBaseline} = statsBaseline;
    const ratio = meanImproved / meanBaseline;
  
    console.log(
      `| ${name} | ${percent}% | ${ratio.toFixed(
        4,
      )} | ${meanImproved.toExponential(4)} | ${rmeImproved.toFixed(
        2,
      )}% | ${meanBaseline.toExponential(4)} | ${rmeBaseline.toFixed(2)}% |`,
    );
  };
  
  const testDeleteInsert = (tenths, more, less) => {
    // For improved `diff-sequences` package, delete is often slower than insert.
    const statsDeleteImproved = testImproved(more, less);
    const statsDeleteBaseline = testBaseline(more, less);
    writeRow('delete', tenths * 10, statsDeleteImproved, statsDeleteBaseline);
  
    // For baseline `diff` package, many insertions is serious perf problem.
    // However, the benchmark package cannot accurately measure for large n.
    const statsInsertBaseline = testBaseline(less, more);
    const statsInsertImproved = testImproved(less, more);
    writeRow('insert', tenths * 10, statsInsertImproved, statsInsertBaseline);
  };
  
  const testChange = (tenths, expected, received) => {
    const statsImproved = testImproved(expected, received);
    const statsBaseline = testBaseline(expected, received);
    writeRow('change', tenths * 10, statsImproved, statsBaseline);
  };
  
  const getItems = (n, callback) => {
    const items = [];
  
    for (let i = 0; i !== n; i += 1) {
      const item = callback(i);
      if (typeof item === 'string') {
        items.push(item);
      }
    }
  
    return items.join('\n');
  };
  
  // Simulate change of property name which is usually not same line.
  // Expected: 0 1 2 3 4 5 6 7 8 9 and so on
  // Received: 1 2 3 4 x0 5 6 7 8 9 and so on
  const change2 = i => {
    const j = i % 10;
    return j === 4 ? `x${i - 4}` : j < 4 ? `${i + 1}` : `${i}`;
  };
  
  const testLength = n => {
    const all = getItems(n, i => `${i}`);
  
    writeHeading3(n);
  
    [2, 4, 8].forEach(tenth => {
      testDeleteInsert(
        tenth,
        all,
        getItems(n, i => i % 10 >= tenth && `${i}`),
      );
    });
    testChange(
      1,
      all,
      getItems(n, i => (i % 10 === 0 ? `x${i}` : `${i}`)),
    );
    testChange(2, all, getItems(n, change2));
    testChange(
      5,
      all,
      getItems(n, i => (i % 2 === 0 ? `x${i}` : `${i}`)),
    );
    testChange(
      10,
      all,
      getItems(n, i => `x${i}`),
    ); // simulate TDD
  };
  
  writeHeading2();
  
  testLength(20);
  testLength(200);
  testLength(2000);
  