From dcdd88b56d69dd7cac9e872d5695fb4305f87b96 Mon Sep 17 00:00:00 2001 From: Ferdinand Neman Date: Thu, 11 Feb 2021 20:57:54 +0700 Subject: [PATCH] Added damerau levenshtein distance algorithm --- .gitignore | 618 +++++++++++++++++++++++++++++++++++++++++++++++++++ Beda.go | 142 ++++++++++++ Beda_test.go | 52 +++++ README.md | 54 +++++ coverage.out | 181 +++++++++------ 5 files changed, 978 insertions(+), 69 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c7e1ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,618 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/jetbrains+all,vim,windows,linux,macos,vscode,visualstudio,go,emacs +# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+all,vim,windows,linux,macos,vscode,visualstudio,go,emacs + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive +ltximg/** + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### Go Patch ### +/vendor/ +/Godeps/ + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### vscode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*[.json, .xml, .info] + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all,vim,windows,linux,macos,vscode,visualstudio,go,emacs \ No newline at end of file diff --git a/Beda.go b/Beda.go index 6c0d881..61cc7d6 100644 --- a/Beda.go +++ b/Beda.go @@ -1,5 +1,10 @@ package beda +import ( + "fmt" + "math" +) + // NewStringDiff will create a new instance of StringDiff func NewStringDiff(s1, s2 string) *StringDiff { return &StringDiff{ @@ -288,3 +293,140 @@ func (sd *StringDiff) JaroWinklerDistance(p float32) float32 { return dw } + +// DamerauLevenshteinDistance Algorithm is an extension to the Levenshtein +// Algorithm which solves the edit distance problem between a source string and +// a target string with the following operations: +// +// Read https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance +func DamerauLevenshteinDistance(s1, s2 string) int { + sd := NewStringDiff(s1, s2) + return sd.DamerauLevenshteinDistance(1,1,1,1) +} + + +// DamerauLevenshteinDistance Algorithm is an extension to the Levenshtein +// Algorithm which solves the edit distance problem between a source string and +// a target string with the following operations: +// +// - Character Insertion +// - Character Deletion +// - Character Replacement +// - Adjacent Character Swap +// +// Note that the adjacent character swap operation is an edit that may be +// applied when two adjacent characters in the source string match two adjacent +// characters in the target string, but in reverse order, rather than a general +// allowance for adjacent character swaps. +// +// This implementation allows the client to specify the costs of the various +// edit operations with the restriction that the cost of two swap operations +// must not be less than the cost of a delete operation followed by an insert +// operation. This restriction is required to preclude two swaps involving the +// same character being required for optimality which, in turn, enables a fast +// dynamic programming solution. +// +// The running time of the Damerau-Levenshtein algorithm is O(n*m) where n is +// the length of the source string and m is the length of the target string. +// This implementation consumes O(n*m) space. +// +// This code is an adaptation from https://github.com/KevinStern/software-and-algorithms/blob/master/src/main/java/blogspot/software_and_algorithms/stern_library/string/DamerauLevenshteinAlgorithm.java +func (sd *StringDiff) DamerauLevenshteinDistance(deleteCost, insertCost, + replaceCost, swapCost int) int { + if 2 * swapCost < insertCost + deleteCost { + panic(fmt.Sprintf("Unsupported cost assignment. Expression 2 * %d(swapCost) < %d(insertCost) + %d(deleteCost) is detected", swapCost, insertCost, deleteCost)) + } + + source := []byte(sd.S1) + target := []byte(sd.S2) + if len(source) == 0 { + return len(target) * insertCost + } + if len(target) == 0 { + return len(source) * deleteCost + } + table := make([][]int, len(source)) + for i := range table { + table[i] = make([]int, len(target)) + } + sourceIndexByCharacter := make(map[byte]int) + if source[0] != target[0] { + table[0][0] = minInt(replaceCost, deleteCost + insertCost) + } + sourceIndexByCharacter[source[0]] = 0 + for i := 1; i < len(source); i++ { + deleteDistance := table[i - 1][0] + deleteCost + insertDistance := (i + 1) * deleteCost + insertCost + ops := replaceCost + if source[i] == target[0] { + ops = 0 + } + matchDistance := i * deleteCost + ops + table[i][0] = minInt(minInt(deleteDistance, insertDistance), + matchDistance) + } + for j := 1; j < len(target); j++ { + deleteDistance := (j + 1) * insertCost + deleteCost; + insertDistance := table[0][j - 1] + insertCost + ops := replaceCost + if source[0] == target[j] { + ops = 0 + } + matchDistance := j * insertCost + ops + table[0][j] = minInt(minInt(deleteDistance, insertDistance), + matchDistance) + } + for i := 1; i < len(source); i++ { + maxSourceLetterMatchIndex := -1 + if source[i] == target[0] { + maxSourceLetterMatchIndex = 0 + } + for j := 1; j < len(target); j++ { + sourceIndexByCharacterNil := true + var candidateSwapIndex int + if v, ok := sourceIndexByCharacter[target[j]]; ok { + candidateSwapIndex = v + sourceIndexByCharacterNil = false + } + jSwap := maxSourceLetterMatchIndex + deleteDistance := table[i - 1][j] + deleteCost + insertDistance := table[i][j - 1] + insertCost + matchDistance := table[i - 1][j - 1] + if source[i] != target[j] { + matchDistance += replaceCost + } else { + maxSourceLetterMatchIndex = j + } + var swapDistance int + if sourceIndexByCharacterNil != true && jSwap != -1 { + iSwap := candidateSwapIndex + var preSwapCost int + if iSwap == 0 && jSwap == 0 { + preSwapCost = 0 + } else { + preSwapCost = table[maxInt(0, iSwap - 1)][maxInt(0, jSwap - 1)] + } + swapDistance = preSwapCost + (i - iSwap - 1) * deleteCost + (j - jSwap - 1) * insertCost + swapCost + } else { + swapDistance = math.MaxInt32 + } + table[i][j] = minInt(minInt(minInt(deleteDistance, insertDistance), matchDistance), swapDistance) + } + sourceIndexByCharacter[source[i]] = i + } + return table[len(source) - 1][len(target) - 1] +} + +func minInt(a,b int) int { + if a < b { + return a + } + return b +} + +func maxInt(a,b int) int { + if a > b { + return a + } + return b +} \ No newline at end of file diff --git a/Beda_test.go b/Beda_test.go index d560e20..e1242d5 100644 --- a/Beda_test.go +++ b/Beda_test.go @@ -120,3 +120,55 @@ func TestJaroWinklerDistance(t *testing.T) { } } } + +func TestDamerauLevenshteinDistance(t *testing.T) { + testData := make([]*TestLehvenstein, 0) + testData = append(testData, &TestLehvenstein{ + S1: "abc", + S2: "abd", + D: 1, + }, &TestLehvenstein{ + S1: "abc", + S2: "abc", + D: 0, + }, &TestLehvenstein{ + S1: "abc", + S2: "ade", + D: 2, + }, &TestLehvenstein{ + S1: "abc", + S2: "def", + D: 3, + }, &TestLehvenstein{ + S1: "abc", + S2: "abca", + D: 1, + }, &TestLehvenstein{ + S1: "abc", + S2: "abcabc", + D: 3, + }, &TestLehvenstein{ + S1: "abc", + S2: "ab", + D: 1, + }, &TestLehvenstein{ + S1: "abc", + S2: "", + D: 3, + }, &TestLehvenstein{ // test swap + S1: "abcde", + S2: "abced", + D: 1, + }, &TestLehvenstein{ // test swap + S1: "abcde", + S2: "ebcda", + D: 2, + }) + + for _, td := range testData { + sd := NewStringDiff(td.S1, td.S2) + if sd.DamerauLevenshteinDistance(1,1,1,1) != td.D { + t.Error("Distance between", td.S1, "and", td.S2, "expected to", td.D, "but", sd.DamerauLevenshteinDistance(1,1,1,1)) + } + } +} diff --git a/README.md b/README.md index cd73392..1c26773 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,60 @@ or fmt.Printf("Distance is %d \n", beda.LevenshteinDistance("abcd", "bc")) ``` + +### Damerau-Levenshtein Distance + +(From [Wikipedia](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance)) +Damerau-Levenshtein Distance is a string metric for measuring the edit distance between two +sequences. Informally, the Damerau–Levenshtein distance between two words is the minimum +number of operations (consisting of insertions, deletions or substitutions of a single +character, or transposition of two adjacent characters) required to change one word into the other. + +The Damerau–Levenshtein distance differs from the classical Levenshtein distance by +including transpositions among its allowable operations in addition to the three classical +single-character edit operations (insertions, deletions and substitutions). + +Reading : + +- [https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance) + +API : + +```go +func DamerauLevenshteinDistance(s1, s2 string) int +func (sd *StringDiff) DamerauLevenshteinDistance(deleteCost, insertCost, replaceCost, swapCost int) int +``` + +`func DamerauLevenshteinDistance` take 2 arguments,
+`s1` is the first string to compare
+`s2` is the second string to compare
+The closer return value to 0 means the more similar the two words. +This function uses the default value of 1 for all `deleteCost`, `insertCost`, `replaceCost` and `swapCost` + +`func (sd *StringDiff) DamerauLevenshteinDistance` takes 4 arguments,
+`deleteCost` is multiplier factor for delete operation
+`insertCost` is multiplier factor for insert operation
+`replaceCost` is multiplier factor for replace operation
+`swapCost` is multiplier factor for swap operation
+A multiplier value enable us to weight on how impactful each of the operation +contributing to the change distance. + + +Example : + +```go +sd := beda.NewStringDiff("abcd", "bc") +lDist := sd.DamerauLevenshteinDistance(1,1,1,1) +fmt.Printf("Distance is %d \n", lDist) // prints : Distance is 2 +``` + +or + +```go +fmt.Printf("Distance is %d \n", beda.DamerauLevenshteinDistance("abcd", "bc")) +``` + + ### TriGram Compare TrigramCompare is a case of n-gram, a contiguous sequence of n (three, in this case) items from a given sample. diff --git a/coverage.out b/coverage.out index e75cdac..5b2ef7d 100644 --- a/coverage.out +++ b/coverage.out @@ -1,72 +1,115 @@ mode: count -github.com/hyperjumptech/beda/Beda.go:4.47,9.2 1 14 -github.com/hyperjumptech/beda/Beda.go:26.45,29.2 2 0 -github.com/hyperjumptech/beda/Beda.go:38.49,47.19 6 8 -github.com/hyperjumptech/beda/Beda.go:52.2,52.26 1 8 +github.com/hyperjumptech/beda/Beda.go:9.47,14.2 1 24 +github.com/hyperjumptech/beda/Beda.go:31.45,34.2 2 0 +github.com/hyperjumptech/beda/Beda.go:43.49,52.19 6 8 github.com/hyperjumptech/beda/Beda.go:57.2,57.26 1 8 -github.com/hyperjumptech/beda/Beda.go:61.2,61.25 1 8 -github.com/hyperjumptech/beda/Beda.go:74.2,74.21 1 8 -github.com/hyperjumptech/beda/Beda.go:47.19,49.3 1 32 -github.com/hyperjumptech/beda/Beda.go:52.26,54.3 1 24 +github.com/hyperjumptech/beda/Beda.go:62.2,62.26 1 8 +github.com/hyperjumptech/beda/Beda.go:66.2,66.25 1 8 +github.com/hyperjumptech/beda/Beda.go:79.2,79.21 1 8 +github.com/hyperjumptech/beda/Beda.go:52.19,54.3 1 32 github.com/hyperjumptech/beda/Beda.go:57.26,59.3 1 24 -github.com/hyperjumptech/beda/Beda.go:61.25,62.26 1 24 -github.com/hyperjumptech/beda/Beda.go:62.26,64.20 2 72 -github.com/hyperjumptech/beda/Beda.go:69.4,71.36 1 72 -github.com/hyperjumptech/beda/Beda.go:64.20,66.5 1 18 -github.com/hyperjumptech/beda/Beda.go:66.10,68.5 1 54 -github.com/hyperjumptech/beda/Beda.go:84.46,85.28 1 40 -github.com/hyperjumptech/beda/Beda.go:90.2,90.30 1 22 -github.com/hyperjumptech/beda/Beda.go:85.28,86.18 1 202 -github.com/hyperjumptech/beda/Beda.go:86.18,88.4 1 18 -github.com/hyperjumptech/beda/Beda.go:93.46,94.35 1 402 -github.com/hyperjumptech/beda/Beda.go:97.2,97.27 1 402 -github.com/hyperjumptech/beda/Beda.go:102.2,102.13 1 36 -github.com/hyperjumptech/beda/Beda.go:94.35,96.3 1 0 -github.com/hyperjumptech/beda/Beda.go:97.27,98.24 1 496 -github.com/hyperjumptech/beda/Beda.go:98.24,100.4 1 366 -github.com/hyperjumptech/beda/Beda.go:105.40,107.17 2 4 -github.com/hyperjumptech/beda/Beda.go:110.2,115.33 5 4 -github.com/hyperjumptech/beda/Beda.go:119.2,119.12 1 4 -github.com/hyperjumptech/beda/Beda.go:107.17,109.3 1 0 -github.com/hyperjumptech/beda/Beda.go:115.33,118.3 2 40 -github.com/hyperjumptech/beda/Beda.go:126.44,129.2 2 0 -github.com/hyperjumptech/beda/Beda.go:139.48,146.25 7 2 -github.com/hyperjumptech/beda/Beda.go:154.2,155.25 2 2 -github.com/hyperjumptech/beda/Beda.go:158.2,158.25 1 2 -github.com/hyperjumptech/beda/Beda.go:161.2,163.35 2 2 -github.com/hyperjumptech/beda/Beda.go:146.25,147.26 1 20 -github.com/hyperjumptech/beda/Beda.go:147.26,148.19 1 200 -github.com/hyperjumptech/beda/Beda.go:148.19,151.5 1 18 -github.com/hyperjumptech/beda/Beda.go:155.25,157.3 1 20 -github.com/hyperjumptech/beda/Beda.go:158.25,160.3 1 20 -github.com/hyperjumptech/beda/Beda.go:166.31,168.25 2 72 -github.com/hyperjumptech/beda/Beda.go:173.2,173.12 1 72 -github.com/hyperjumptech/beda/Beda.go:168.25,169.24 1 216 -github.com/hyperjumptech/beda/Beda.go:169.24,171.4 1 123 -github.com/hyperjumptech/beda/Beda.go:176.35,179.21 3 4 -github.com/hyperjumptech/beda/Beda.go:186.2,187.23 2 4 -github.com/hyperjumptech/beda/Beda.go:192.2,192.12 1 4 -github.com/hyperjumptech/beda/Beda.go:179.21,182.3 2 0 -github.com/hyperjumptech/beda/Beda.go:182.8,185.3 2 4 -github.com/hyperjumptech/beda/Beda.go:187.23,188.17 1 24 -github.com/hyperjumptech/beda/Beda.go:188.17,190.4 1 4 -github.com/hyperjumptech/beda/Beda.go:195.32,197.21 2 4 -github.com/hyperjumptech/beda/Beda.go:204.2,205.23 2 4 -github.com/hyperjumptech/beda/Beda.go:213.2,213.12 1 4 -github.com/hyperjumptech/beda/Beda.go:197.21,200.3 2 0 -github.com/hyperjumptech/beda/Beda.go:200.8,203.3 2 4 -github.com/hyperjumptech/beda/Beda.go:205.23,206.24 1 24 -github.com/hyperjumptech/beda/Beda.go:206.24,207.16 1 68 -github.com/hyperjumptech/beda/Beda.go:207.16,209.10 2 24 -github.com/hyperjumptech/beda/Beda.go:219.42,222.2 2 0 -github.com/hyperjumptech/beda/Beda.go:227.46,238.2 8 4 -github.com/hyperjumptech/beda/Beda.go:247.60,250.2 2 0 -github.com/hyperjumptech/beda/Beda.go:263.62,269.21 6 2 -github.com/hyperjumptech/beda/Beda.go:276.2,276.22 1 2 -github.com/hyperjumptech/beda/Beda.go:287.2,289.11 2 2 -github.com/hyperjumptech/beda/Beda.go:269.21,272.3 2 0 -github.com/hyperjumptech/beda/Beda.go:272.8,275.3 2 2 -github.com/hyperjumptech/beda/Beda.go:276.22,277.16 1 9 -github.com/hyperjumptech/beda/Beda.go:277.16,279.15 2 8 -github.com/hyperjumptech/beda/Beda.go:279.15,280.10 1 1 -github.com/hyperjumptech/beda/Beda.go:282.9,283.9 1 1 +github.com/hyperjumptech/beda/Beda.go:62.26,64.3 1 24 +github.com/hyperjumptech/beda/Beda.go:66.25,67.26 1 24 +github.com/hyperjumptech/beda/Beda.go:67.26,69.20 2 72 +github.com/hyperjumptech/beda/Beda.go:74.4,76.36 1 72 +github.com/hyperjumptech/beda/Beda.go:69.20,71.5 1 18 +github.com/hyperjumptech/beda/Beda.go:71.10,73.5 1 54 +github.com/hyperjumptech/beda/Beda.go:89.46,90.28 1 40 +github.com/hyperjumptech/beda/Beda.go:95.2,95.30 1 22 +github.com/hyperjumptech/beda/Beda.go:90.28,91.18 1 202 +github.com/hyperjumptech/beda/Beda.go:91.18,93.4 1 18 +github.com/hyperjumptech/beda/Beda.go:98.46,99.35 1 402 +github.com/hyperjumptech/beda/Beda.go:102.2,102.27 1 402 +github.com/hyperjumptech/beda/Beda.go:107.2,107.13 1 36 +github.com/hyperjumptech/beda/Beda.go:99.35,101.3 1 0 +github.com/hyperjumptech/beda/Beda.go:102.27,103.24 1 496 +github.com/hyperjumptech/beda/Beda.go:103.24,105.4 1 366 +github.com/hyperjumptech/beda/Beda.go:110.40,112.17 2 4 +github.com/hyperjumptech/beda/Beda.go:115.2,120.33 5 4 +github.com/hyperjumptech/beda/Beda.go:124.2,124.12 1 4 +github.com/hyperjumptech/beda/Beda.go:112.17,114.3 1 0 +github.com/hyperjumptech/beda/Beda.go:120.33,123.3 2 40 +github.com/hyperjumptech/beda/Beda.go:131.44,134.2 2 0 +github.com/hyperjumptech/beda/Beda.go:144.48,151.25 7 2 +github.com/hyperjumptech/beda/Beda.go:159.2,160.25 2 2 +github.com/hyperjumptech/beda/Beda.go:163.2,163.25 1 2 +github.com/hyperjumptech/beda/Beda.go:166.2,168.35 2 2 +github.com/hyperjumptech/beda/Beda.go:151.25,152.26 1 20 +github.com/hyperjumptech/beda/Beda.go:152.26,153.19 1 200 +github.com/hyperjumptech/beda/Beda.go:153.19,156.5 1 18 +github.com/hyperjumptech/beda/Beda.go:160.25,162.3 1 20 +github.com/hyperjumptech/beda/Beda.go:163.25,165.3 1 20 +github.com/hyperjumptech/beda/Beda.go:171.31,173.25 2 72 +github.com/hyperjumptech/beda/Beda.go:178.2,178.12 1 72 +github.com/hyperjumptech/beda/Beda.go:173.25,174.24 1 216 +github.com/hyperjumptech/beda/Beda.go:174.24,176.4 1 123 +github.com/hyperjumptech/beda/Beda.go:181.35,184.21 3 4 +github.com/hyperjumptech/beda/Beda.go:191.2,192.23 2 4 +github.com/hyperjumptech/beda/Beda.go:197.2,197.12 1 4 +github.com/hyperjumptech/beda/Beda.go:184.21,187.3 2 0 +github.com/hyperjumptech/beda/Beda.go:187.8,190.3 2 4 +github.com/hyperjumptech/beda/Beda.go:192.23,193.17 1 24 +github.com/hyperjumptech/beda/Beda.go:193.17,195.4 1 4 +github.com/hyperjumptech/beda/Beda.go:200.32,202.21 2 4 +github.com/hyperjumptech/beda/Beda.go:209.2,210.23 2 4 +github.com/hyperjumptech/beda/Beda.go:218.2,218.12 1 4 +github.com/hyperjumptech/beda/Beda.go:202.21,205.3 2 0 +github.com/hyperjumptech/beda/Beda.go:205.8,208.3 2 4 +github.com/hyperjumptech/beda/Beda.go:210.23,211.24 1 24 +github.com/hyperjumptech/beda/Beda.go:211.24,212.16 1 68 +github.com/hyperjumptech/beda/Beda.go:212.16,214.10 2 24 +github.com/hyperjumptech/beda/Beda.go:224.42,227.2 2 0 +github.com/hyperjumptech/beda/Beda.go:232.46,243.2 8 4 +github.com/hyperjumptech/beda/Beda.go:252.60,255.2 2 0 +github.com/hyperjumptech/beda/Beda.go:268.62,274.21 6 2 +github.com/hyperjumptech/beda/Beda.go:281.2,281.22 1 2 +github.com/hyperjumptech/beda/Beda.go:292.2,294.11 2 2 +github.com/hyperjumptech/beda/Beda.go:274.21,277.3 2 0 +github.com/hyperjumptech/beda/Beda.go:277.8,280.3 2 2 +github.com/hyperjumptech/beda/Beda.go:281.22,282.16 1 9 +github.com/hyperjumptech/beda/Beda.go:282.16,284.15 2 8 +github.com/hyperjumptech/beda/Beda.go:284.15,285.10 1 1 +github.com/hyperjumptech/beda/Beda.go:287.9,288.9 1 1 +github.com/hyperjumptech/beda/Beda.go:302.52,305.2 2 0 +github.com/hyperjumptech/beda/Beda.go:335.33,336.44 1 10 +github.com/hyperjumptech/beda/Beda.go:340.2,342.22 3 10 +github.com/hyperjumptech/beda/Beda.go:345.2,345.22 1 10 +github.com/hyperjumptech/beda/Beda.go:348.2,349.23 2 9 +github.com/hyperjumptech/beda/Beda.go:352.2,353.28 2 9 +github.com/hyperjumptech/beda/Beda.go:356.2,357.35 2 9 +github.com/hyperjumptech/beda/Beda.go:368.2,368.35 1 9 +github.com/hyperjumptech/beda/Beda.go:379.2,379.35 1 9 +github.com/hyperjumptech/beda/Beda.go:417.2,417.48 1 9 +github.com/hyperjumptech/beda/Beda.go:336.44,337.161 1 0 +github.com/hyperjumptech/beda/Beda.go:342.22,344.3 1 0 +github.com/hyperjumptech/beda/Beda.go:345.22,347.3 1 1 +github.com/hyperjumptech/beda/Beda.go:349.23,351.3 1 31 +github.com/hyperjumptech/beda/Beda.go:353.28,355.3 1 2 +github.com/hyperjumptech/beda/Beda.go:357.35,361.29 4 22 +github.com/hyperjumptech/beda/Beda.go:364.3,366.18 2 22 +github.com/hyperjumptech/beda/Beda.go:361.29,363.4 1 1 +github.com/hyperjumptech/beda/Beda.go:368.35,372.29 4 25 +github.com/hyperjumptech/beda/Beda.go:375.3,377.18 2 25 +github.com/hyperjumptech/beda/Beda.go:372.29,374.4 1 3 +github.com/hyperjumptech/beda/Beda.go:379.35,381.29 2 22 +github.com/hyperjumptech/beda/Beda.go:384.3,384.36 1 22 +github.com/hyperjumptech/beda/Beda.go:415.3,415.40 1 22 +github.com/hyperjumptech/beda/Beda.go:381.29,383.4 1 1 +github.com/hyperjumptech/beda/Beda.go:384.36,387.54 3 66 +github.com/hyperjumptech/beda/Beda.go:391.4,395.30 5 66 +github.com/hyperjumptech/beda/Beda.go:400.4,401.56 2 66 +github.com/hyperjumptech/beda/Beda.go:413.4,413.101 1 66 +github.com/hyperjumptech/beda/Beda.go:387.54,390.5 2 26 +github.com/hyperjumptech/beda/Beda.go:395.30,397.5 1 49 +github.com/hyperjumptech/beda/Beda.go:397.10,399.5 1 17 +github.com/hyperjumptech/beda/Beda.go:401.56,404.33 3 13 +github.com/hyperjumptech/beda/Beda.go:409.5,409.104 1 13 +github.com/hyperjumptech/beda/Beda.go:404.33,406.6 1 1 +github.com/hyperjumptech/beda/Beda.go:406.11,408.6 1 12 +github.com/hyperjumptech/beda/Beda.go:410.10,412.5 1 53 +github.com/hyperjumptech/beda/Beda.go:420.26,421.11 1 294 +github.com/hyperjumptech/beda/Beda.go:424.2,424.10 1 120 +github.com/hyperjumptech/beda/Beda.go:421.11,423.3 1 174 +github.com/hyperjumptech/beda/Beda.go:427.26,428.11 1 24 +github.com/hyperjumptech/beda/Beda.go:431.2,431.10 1 14 +github.com/hyperjumptech/beda/Beda.go:428.11,430.3 1 10