Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions lib/ronin/support/text/distance/levenshtein.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true
#
# Copyright (c) 2006-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-support is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-support is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-support. If not, see <https://www.gnu.org/licenses/>.
#

module Ronin
module Support
module Text
module Distance
#
# Methods for calculating Levenshtein Distance
#
module Levenshtein
#
# Calculates Levenshtein Distance between two strings.
#
# @param [String] string1
# First string to compare.
#
# @param [String] string2
# Second string to compare.
#
# @return [Integer]
# The calculated distance.
#
def levenshtein_distance(string1,string2)
m = string1.size
n = string2.size

previous_row = (0..n).to_a

1.upto(m) do |i|
current_value = i

1.upto(n) do |j|
diagonal = previous_row[j - 1]
previous_row[j - 1] = current_value

current_value = if string1[i - 1] == string2[j - 1]
diagonal
else
1 + [current_value, previous_row[j], diagonal].min
end
end

previous_row[n] = current_value
end

previous_row[n]
end
end
end
end
end
end
77 changes: 77 additions & 0 deletions spec/text/distance/levenshtein_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'spec_helper'
require 'ronin/support/text/distance/levenshtein'

describe Ronin::Support::Text::Distance::Levenshtein do
class TestTextLevenshtein
include Ronin::Support::Text::Distance::Levenshtein
end

let(:test_class) { TestTextLevenshtein }
subject { test_class.new }

it "must provide a #levenshtein_distance method" do
expect(subject).to respond_to(:levenshtein_distance)
end

describe "#levenshtein_distance" do
context "when given two somewhat similar strings" do
let(:string1) { "kitten" }
let(:string2) { "sitting" }
let(:expected_distance) { 3 }

it "must calculate levenshtein distance between them" do
expect(subject.levenshtein_distance(string1, string2)).to eq(expected_distance)
end
end

context "when given two empty strings" do
let(:string1) { "" }
let(:string2) { "" }
let(:expected_distance) { 0 }

it "must returns 0" do
expect(subject.levenshtein_distance(string1, string2)).to eq(expected_distance)
end
end

context "when given one empty string" do
let(:string1) { "test" }
let(:string2) { "" }
let(:expected_distance) { string1.size }

it "must return length of given string" do
expect(subject.levenshtein_distance(string1, string2)).to eq(expected_distance)
end
end

context "when given two identical strings" do
let(:string1) { "test" }
let(:string2) { "test" }
let(:expected_distance) { 0 }

it "must return 0" do
expect(subject.levenshtein_distance(string1, string2)).to eq(expected_distance)
end
end

context "when given strings differing by one character" do
let(:string1) { "cat" }
let(:string2) { "bat" }
let(:expected_distance) { 1 }

it "must returns 1" do
expect(subject.levenshtein_distance(string1, string2)).to eq(expected_distance)
end
end

context "when given two completely different strings" do
let(:string1) { "long_foo" }
let(:string2) { "bar" }
let(:expected_distance) { string1.size }

it "must return lenght of the longest one" do
expect(subject.levenshtein_distance(string1, string2)).to eq(expected_distance)
end
end
end
end