edit-distance/myers-1986

View on GitHub
README.md

Summary

Maintainability
Test Coverage
:abacus: [@edit-distance/myers-1986](https://edit-distance.github.io/myers-1986)
==

Reasonably efficient implementation of Eugene W. Myers's exact longest common
subsequence and minimum edit-distance algorithm for JavaScript.
See [docs](https://edit-distance.github.io/myers-1986/index.html).

> :building_construction: Caveat emptor! This is work in progress. Code may be
> working. Documentation may be present. Coherence may be. Maybe.

> :warning: Only signed 32-bit integers are supported for `MAX`, `xi`, `xj`,
> `yi`, `yj`. This is also true for the values `xj - xi` and `yj - yi` but is
> not enforced. You should be fine if the elements of each pair have the same
> sign. This means you cannot diff things with more than `2,147,483,647`
> parts (array elements, string characters, text words or lines).

```js
// Example 1: compute Edit Distance.

import {makeScan, twoWay} from '@edit-distance/myers-1986';

const scan = makeScan(twoWay);

const ed = (x, y) => {
    const eq = (xi, yi) => x[xi] === y[yi];
    const xi = 0;
    const xj = x.length;
    const yi = 0;
    const yj = y.length;
    const MAX = xj - xi + (yj - yi);
    return scan(MAX, eq, xi, xj, yi, yj);
};

ed('BANANA', 'ATANA'); // 3

// Example 2: compute LCS.

import {diff} from '@edit-distance/myers-1986';

const rectangles = (x, y) => {
    const eq = (xi, yi) => x[xi] === y[yi];
    const xi = 0;
    const xj = x.length;
    const yi = 0;
    const yj = y.length;
    const MAX = xj - xi + (yj - yi);
    return diff(MAX, eq, xi, xj, yi, yj);
};

const lcs = (x, y) => {
    let result = x.slice(0, 0);
    let xp = 0;
    for (const [x0, x1] of rectangles(x, y)) {
        result = result.concat(x.slice(xp, x0));
        xp = x1;
    }

    return result.concat(x.slice(xp, x.length));
};

lcs('BANANA', 'ATANA'); // AANA
```

[![License](https://img.shields.io/github/license/edit-distance/myers-1986.svg)](https://raw.githubusercontent.com/edit-distance/myers-1986/main/LICENSE)
[![Version](https://img.shields.io/npm/v/@edit-distance/myers-1986.svg)](https://www.npmjs.org/package/@edit-distance/myers-1986)
[![Tests](https://img.shields.io/github/actions/workflow/status/edit-distance/myers-1986/ci.yml?branch=main&event=push&label=tests)](https://github.com/edit-distance/myers-1986/actions/workflows/ci.yml?query=branch:main)
[![Dependencies](https://img.shields.io/librariesio/github/edit-distance/myers-1986.svg)](https://github.com/edit-distance/myers-1986/network/dependencies)
[![GitHub issues](https://img.shields.io/github/issues/edit-distance/myers-1986.svg)](https://github.com/edit-distance/myers-1986/issues)
[![Downloads](https://img.shields.io/npm/dm/@edit-distance/myers-1986.svg)](https://www.npmjs.org/package/@edit-distance/myers-1986)

[![Code issues](https://img.shields.io/codeclimate/issues/edit-distance/myers-1986.svg)](https://codeclimate.com/github/edit-distance/myers-1986/issues)
[![Code maintainability](https://img.shields.io/codeclimate/maintainability/edit-distance/myers-1986.svg)](https://codeclimate.com/github/edit-distance/myers-1986/trends/churn)
[![Code coverage (cov)](https://img.shields.io/codecov/c/gh/edit-distance/myers-1986/main.svg)](https://codecov.io/gh/edit-distance/myers-1986)
[![Code technical debt](https://img.shields.io/codeclimate/tech-debt/edit-distance/myers-1986.svg)](https://codeclimate.com/github/edit-distance/myers-1986/trends/technical_debt)
[![Documentation](https://edit-distance.github.io/myers-1986/badge.svg)](https://edit-distance.github.io/myers-1986/source.html)
[![Package size](https://img.shields.io/bundlephobia/minzip/@edit-distance/myers-1986)](https://bundlephobia.com/result?p=@edit-distance/myers-1986)

## :bicyclist: Benchmark

| input                                               | fast-myers-diff v3.0.1 |  modern.js v0.0.1 |  module.js v0.0.1 |         cjs v0.0.1 |
| --------------------------------------------------- | ---------------------: | ----------------: | ----------------: | -----------------: |
| N+M=220 N=100 M=120 LCS=100 DEL=0 INS=20            |      77,170    ops/sec | 99,952    ops/sec | 97,806    ops/sec | 100,333    ops/sec |
| N+M=220 N=120 M=100 LCS=100 DEL=20 INS=0            |      68,575    ops/sec | 90,582    ops/sec | 89,178    ops/sec |  90,030    ops/sec |
| N+M=220 N=110 M=110 LCS=100 DEL=10 INS=10           |      63,494    ops/sec | 83,439    ops/sec | 81,201    ops/sec |  82,661    ops/sec |
| N+M=224 N=14 M=210 LCS=10 DEL=4 INS=200             |       7,112    ops/sec | 10,346    ops/sec | 10,406    ops/sec |  10,898    ops/sec |
| N+M=20020 N=10000 M=10020 LCS=10000 DEL=0 INS=20    |       3,282    ops/sec |  4,313    ops/sec |  4,174    ops/sec |   4,415    ops/sec |
| N+M=20020 N=10020 M=10000 LCS=10000 DEL=20 INS=0    |       3,317    ops/sec |  4,040    ops/sec |  4,035    ops/sec |   4,947    ops/sec |
| N+M=20020 N=10010 M=10010 LCS=10000 DEL=10 INS=10   |       2,414    ops/sec |  2,939    ops/sec |  2,843    ops/sec |   3,009    ops/sec |
| N+M=220 N=110 M=110 LCS=10 DEL=100 INS=100          |       1,607    ops/sec |  2,737    ops/sec |  2,720    ops/sec |   2,887    ops/sec |
| N+M=20200 N=10000 M=10200 LCS=10000 DEL=0 INS=200   |         871    ops/sec |  1,258    ops/sec |  1,254    ops/sec |   1,293    ops/sec |
| N+M=20200 N=10200 M=10000 LCS=10000 DEL=200 INS=0   |         844    ops/sec |  1,240    ops/sec |  1,258    ops/sec |   1,309    ops/sec |
| N+M=20200 N=10100 M=10100 LCS=10000 DEL=100 INS=100 |         703    ops/sec |    978    ops/sec |    976    ops/sec |     978    ops/sec |
| N+M=2020 N=1010 M=1010 LCS=10 DEL=1000 INS=1000     |          19.10 ops/sec |     36.35 ops/sec |     36.45 ops/sec |      38.16 ops/sec |