diff --git a/launchers/build.sbt b/launchers/build.sbt index 65892bcba0..528b94432a 100644 --- a/launchers/build.sbt +++ b/launchers/build.sbt @@ -48,7 +48,8 @@ lazy val macosx = (project in file("macosx")) lazy val root = (project in file(".")) .aggregate(common, browserbundle, macosx) -scalacOptions in Compile := Seq("-deprecated") +javacOptions ++= Seq("-source", "1.7", "-target", "1.7") +scalacOptions in Compile := Seq("-deprecated","-target:jvm-1.7") fork := true diff --git a/launchers/macosx/build.sbt b/launchers/macosx/build.sbt index bedbe667d0..5ac371620c 100644 --- a/launchers/macosx/build.sbt +++ b/launchers/macosx/build.sbt @@ -15,18 +15,14 @@ lazy val warsForCopy = new File(i2pBuildDir, "webapps").list.filter { f => f.end lazy val jarsForCopy = new File(i2pBuildDir, "lib").list.filter { f => f.endsWith(".jar") } -// Pointing the resources directory to the "installer" directory -resourceDirectory in Compile := baseDirectory.value / ".." / ".." / "installer" / "resources" - -// Unmanaged base will be included in a fat jar -unmanagedBase in Compile := baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" - - // Unmanaged classpath will be available at compile time unmanagedClasspath in Compile ++= Seq( - baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" / "*.jar" + baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" / "router.jar", + baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" / "i2p.jar" ) +unmanagedBase in Compile := baseDirectory.value / ".." / ".." / "pkg-temp" / "lib" + assemblyOption in assembly := (assemblyOption in assembly).value.copy(includeScala = false, includeDependency = false) assemblyExcludedJars in assembly := { @@ -34,76 +30,6 @@ assemblyExcludedJars in assembly := { cp filter { c => jarsForCopy.toList.contains(c.data.getName) } } -/* +javacOptions ++= Seq("-source", "1.7", "-target", "1.7") +scalacOptions in Compile := Seq("-deprecated","-target:jvm-1.7") -assemblyJarName in assembly := s"package.jar" - -// TODO: MEEH: Add assemblyExcludedJars and load the router from own jar files, to handle upgrades better. -// In fact, most likely the bundle never would need an update except for the router jars/wars. - -convertToICNSTask := { - println("TODO") -} - -cleanAllTask := { - clean.value - IO.delete(bundleBuildPath) -} - -buildDeployZipTask := { - println(s"Starting the zip file build process. This might take a while..") - if (!bundleBuildPath.exists()) bundleBuildPath.mkdir() - val sourceDir = i2pBuildDir - def recursiveListFiles(f: File): Array[File] = { - val these = f.listFiles - these ++ these.filter { f => f.isDirectory }.flatMap(recursiveListFiles).filter(!_.isDirectory) - } - def zip(out: String, files: Iterable[String]) = { - import java.io.{ BufferedInputStream, FileInputStream, FileOutputStream } - import java.util.zip.{ ZipEntry, ZipOutputStream } - - val zip = new ZipOutputStream(new FileOutputStream(out)) - - files.foreach { name => - val fname = sourceDir.toURI.relativize(new File(name).toURI).toString - //println(s"Zipping ${fname}") - if (!new File(name).isDirectory) { - zip.putNextEntry(new ZipEntry(fname)) - val in = new BufferedInputStream(new FileInputStream(name)) - var b = in.read() - while (b > -1) { - zip.write(b) - b = in.read() - } - in.close() - zip.closeEntry() - } - } - zip.close() - } - val fileList = recursiveListFiles(sourceDir.getCanonicalFile).toList - val zipFileName = new File(bundleBuildPath, "i2pbase.zip").getCanonicalPath - zip(zipFileName, fileList.map { f => f.toString }.toIterable) - zipFileName.toString -} - -buildAppBundleTask := { - println(s"Building Mac OS X bundle for I2P version ${i2pVersion}.") - if (!bundleBuildPath.exists()) bundleBuildPath.mkdir() - val paths = Map[String,File]( - "execBundlePath" -> new File(bundleBuildPath, "I2P.app/Contents/MacOS"), - "resBundlePath" -> new File(bundleBuildPath, "I2P.app/Contents/Resources") - ) - paths.map { case (s,p) => p.mkdirs() } - - val launcherBinary = Some(assembly.value) - launcherBinary.map { l => IO.copyFile( new File(l.toString), new File(paths.get("execBundlePath").get, "I2P") ) } - - val zipFilePath = Some(buildDeployZipTask.value) - - val zipFileOrigin = new File(zipFilePath.get) - IO.copyFile(zipFileOrigin, new File(paths.get("resBundlePath").get, "i2pbase.zip")) - println(s"Zip placed into bundle :)") - -} -*/ diff --git a/launchers/macosx/obj-cpp/PidWatcher.h b/launchers/macosx/obj-cpp/PidWatcher.h new file mode 100644 index 0000000000..acb79673cc --- /dev/null +++ b/launchers/macosx/obj-cpp/PidWatcher.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include + +#include "neither/either.hpp" +#include "AppDelegate.h" + +using callbackType = void (CFFileDescriptorRef, CFOptionFlags, void *); +using HandleFunction = std::function; +/* +class CallbackWrapper +{ + CallbackWrapper(HandleFunction func) : mCallback(func); + void operator(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info) { + struct kevent kev; + int fd = CFFileDescriptorGetNativeDescriptor(fdref); + kevent(fd, NULL, 0, &kev, 1, NULL); + // take action on death of process here + NSLog(@"process with pid '%u' died\n", (unsigned int)kev.ident); + + mCallback(0); + CFFileDescriptorInvalidate(fdref); + CFRelease(fdref); + } + +private: + HandleFunction mCallback; +}; +*/ + +static void noteProcDeath(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info) { + struct kevent kev; + int fd = CFFileDescriptorGetNativeDescriptor(fdref); + kevent(fd, NULL, 0, &kev, 1, NULL); + // take action on death of process here + NSLog(@"process with pid '%u' died\n", (unsigned int)kev.ident); + sendUserNotification(APP_IDSTR, @"The I2P router has stopped."); + CFFileDescriptorInvalidate(fdref); + CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example +} +// one argument, an integer pid to watch, required +int watchPid(int pid, callbackType callback = noteProcDeath) { + int fd = kqueue(); + struct kevent kev; + EV_SET(&kev, pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL); + kevent(fd, &kev, 1, NULL, 0, NULL); + CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, callback, NULL); + CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); + CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0); + CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode); + CFRelease(source); + /* + seconds + The length of time to run the run loop. If 0, only one pass is made through the run loop before returning; + if multiple sources or timers are ready to fire immediately, only one (possibly two if one is a version + 0 source) will be fired, regardless of the value of returnAfterSourceHandled. + */ + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); + return 0; +} diff --git a/launchers/macosx/obj-cpp/build.ninja b/launchers/macosx/obj-cpp/build.ninja index ef74c5d440..88df88db3a 100644 --- a/launchers/macosx/obj-cpp/build.ninja +++ b/launchers/macosx/obj-cpp/build.ninja @@ -21,12 +21,13 @@ rule ar description = AR $out rule cleanup - command = rm -fr *.o clauncher I2PLauncher.app + command = rm -fr *.o clauncher I2PLauncher.app base.zip # TODO: There must exists a cleaner way to solve this. rule bundledir command = mkdir -p I2PLauncher.app/Contents/{MacOS,Resources,Frameworks} $ && cp Info.plist I2PLauncher.app/Contents/Info.plist $ + && cd ../../../pkg-temp && zip -r7 ../base.zip * && cd - && mv ../../../base.zip base.zip $ && cp base.zip I2PLauncher.app/Contents/Resources/base.zip $ && cp ../target/scala-2.11/routerLauncher-assembly-0.1.0-SNAPSHOT.jar I2PLauncher.app/Contents/Resources/launcher.jar diff --git a/launchers/macosx/obj-cpp/fullBuild.sh b/launchers/macosx/obj-cpp/fullBuild.sh new file mode 100755 index 0000000000..f6c9a84e47 --- /dev/null +++ b/launchers/macosx/obj-cpp/fullBuild.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd ../../.. +ant preppkg-osx-only +cd pkg-temp +zip -r7 $DIR/base.zip * +echo "[+] Done building base.zip from ant's pkg-temp." +ninja appbundle + diff --git a/launchers/macosx/obj-cpp/include/any.hpp b/launchers/macosx/obj-cpp/include/any.hpp new file mode 100644 index 0000000000..c588a0ec47 --- /dev/null +++ b/launchers/macosx/obj-cpp/include/any.hpp @@ -0,0 +1,461 @@ +// +// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. +// +// See also: +// + http://en.cppreference.com/w/cpp/any +// + http://en.cppreference.com/w/cpp/experimental/any +// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any +// + https://cplusplus.github.io/LWG/lwg-active.html#2509 +// +// +// Copyright (c) 2016 Denilson das Mercês Amorim +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef LINB_ANY_HPP +#define LINB_ANY_HPP +#pragma once +#include +#include +#include + +namespace linb +{ + +class bad_any_cast : public std::bad_cast +{ +public: + const char* what() const noexcept override + { + return "bad any cast"; + } +}; + +class any final +{ +public: + /// Constructs an object of type any with an empty state. + any() : + vtable(nullptr) + { + } + + /// Constructs an object of type any with an equivalent state as other. + any(const any& rhs) : + vtable(rhs.vtable) + { + if(!rhs.empty()) + { + rhs.vtable->copy(rhs.storage, this->storage); + } + } + + /// Constructs an object of type any with a state equivalent to the original state of other. + /// rhs is left in a valid but otherwise unspecified state. + any(any&& rhs) noexcept : + vtable(rhs.vtable) + { + if(!rhs.empty()) + { + rhs.vtable->move(rhs.storage, this->storage); + rhs.vtable = nullptr; + } + } + + /// Same effect as this->clear(). + ~any() + { + this->clear(); + } + + /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward(value). + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + template::type, any>::value>::type> + any(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, + "T shall satisfy the CopyConstructible requirements."); + this->construct(std::forward(value)); + } + + /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown. + any& operator=(const any& rhs) + { + any(rhs).swap(*this); + return *this; + } + + /// Has the same effect as any(std::move(rhs)).swap(*this). + /// + /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid + /// but otherwise unspecified state. + any& operator=(any&& rhs) noexcept + { + any(std::move(rhs)).swap(*this); + return *this; + } + + /// Has the same effect as any(std::forward(value)).swap(*this). No effect if a exception is thrown. + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + template::type, any>::value>::type> + any& operator=(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, + "T shall satisfy the CopyConstructible requirements."); + any(std::forward(value)).swap(*this); + return *this; + } + + /// If not empty, destroys the contained object. + void clear() noexcept + { + if(!empty()) + { + this->vtable->destroy(storage); + this->vtable = nullptr; + } + } + + /// Returns true if *this has no contained object, otherwise false. + bool empty() const noexcept + { + return this->vtable == nullptr; + } + + /// If *this has a contained object of type T, typeid(T); otherwise typeid(void). + const std::type_info& type() const noexcept + { + return empty()? typeid(void) : this->vtable->type(); + } + + /// Exchange the states of *this and rhs. + void swap(any& rhs) noexcept + { + if(this->vtable != rhs.vtable) + { + any tmp(std::move(rhs)); + + // move from *this to rhs. + rhs.vtable = this->vtable; + if(this->vtable != nullptr) + { + this->vtable->move(this->storage, rhs.storage); + //this->vtable = nullptr; -- uneeded, see below + } + + // move from tmp (previously rhs) to *this. + this->vtable = tmp.vtable; + if(tmp.vtable != nullptr) + { + tmp.vtable->move(tmp.storage, this->storage); + tmp.vtable = nullptr; + } + } + else // same types + { + if(this->vtable != nullptr) + this->vtable->swap(this->storage, rhs.storage); + } + } + +private: // Storage and Virtual Method Table + + union storage_union + { + using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of::value>::type; + + void* dynamic; + stack_storage_t stack; // 2 words for e.g. shared_ptr + }; + + /// Base VTable specification. + struct vtable_type + { + // Note: The caller is responssible for doing .vtable = nullptr after destructful operations + // such as destroy() and/or move(). + + /// The type of the object this vtable is for. + const std::type_info& (*type)() noexcept; + + /// Destroys the object in the union. + /// The state of the union after this call is unspecified, caller must ensure not to use src anymore. + void(*destroy)(storage_union&) noexcept; + + /// Copies the **inner** content of the src union into the yet unitialized dest union. + /// As such, both inner objects will have the same state, but on separate memory locations. + void(*copy)(const storage_union& src, storage_union& dest); + + /// Moves the storage from src to the yet unitialized dest union. + /// The state of src after this call is unspecified, caller must ensure not to use src anymore. + void(*move)(storage_union& src, storage_union& dest) noexcept; + + /// Exchanges the storage between lhs and rhs. + void(*swap)(storage_union& lhs, storage_union& rhs) noexcept; + }; + + /// VTable for dynamically allocated storage. + template + struct vtable_dynamic + { + static const std::type_info& type() noexcept + { + return typeid(T); + } + + static void destroy(storage_union& storage) noexcept + { + //assert(reinterpret_cast(storage.dynamic)); + delete reinterpret_cast(storage.dynamic); + } + + static void copy(const storage_union& src, storage_union& dest) + { + dest.dynamic = new T(*reinterpret_cast(src.dynamic)); + } + + static void move(storage_union& src, storage_union& dest) noexcept + { + dest.dynamic = src.dynamic; + src.dynamic = nullptr; + } + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + // just exchage the storage pointers. + std::swap(lhs.dynamic, rhs.dynamic); + } + }; + + /// VTable for stack allocated storage. + template + struct vtable_stack + { + static const std::type_info& type() noexcept + { + return typeid(T); + } + + static void destroy(storage_union& storage) noexcept + { + reinterpret_cast(&storage.stack)->~T(); + } + + static void copy(const storage_union& src, storage_union& dest) + { + new (&dest.stack) T(reinterpret_cast(src.stack)); + } + + static void move(storage_union& src, storage_union& dest) noexcept + { + // one of the conditions for using vtable_stack is a nothrow move constructor, + // so this move constructor will never throw a exception. + new (&dest.stack) T(std::move(reinterpret_cast(src.stack))); + destroy(src); + } + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + storage_union tmp_storage; + move(rhs, tmp_storage); + move(lhs, rhs); + move(tmp_storage, lhs); + } + }; + + /// Whether the type T must be dynamically allocated or can be stored on the stack. + template + struct requires_allocation : + std::integral_constant::value // N4562 §6.3/3 [any.class] + && sizeof(T) <= sizeof(storage_union::stack) + && std::alignment_of::value <= std::alignment_of::value)> + {}; + + /// Returns the pointer to the vtable of the type T. + template + static vtable_type* vtable_for_type() + { + using VTableType = typename std::conditional::value, vtable_dynamic, vtable_stack>::type; + static vtable_type table = { + VTableType::type, VTableType::destroy, + VTableType::copy, VTableType::move, + VTableType::swap, + }; + return &table; + } + +protected: + template + friend const T* any_cast(const any* operand) noexcept; + template + friend T* any_cast(any* operand) noexcept; + + /// Same effect as is_same(this->type(), t); + bool is_typed(const std::type_info& t) const + { + return is_same(this->type(), t); + } + + /// Checks if two type infos are the same. + /// + /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the + /// type infos, otherwise does an actual comparision. Checking addresses is + /// only a valid approach when there's no interaction with outside sources + /// (other shared libraries and such). + static bool is_same(const std::type_info& a, const std::type_info& b) + { +#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE + return &a == &b; +#else + return a == b; +#endif + } + + /// Casts (with no type_info checks) the storage pointer as const T*. + template + const T* cast() const noexcept + { + return requires_allocation::type>::value? + reinterpret_cast(storage.dynamic) : + reinterpret_cast(&storage.stack); + } + + /// Casts (with no type_info checks) the storage pointer as T*. + template + T* cast() noexcept + { + return requires_allocation::type>::value? + reinterpret_cast(storage.dynamic) : + reinterpret_cast(&storage.stack); + } + +private: + storage_union storage; // on offset(0) so no padding for align + vtable_type* vtable; + + template + typename std::enable_if::value>::type + do_construct(ValueType&& value) + { + storage.dynamic = new T(std::forward(value)); + } + + template + typename std::enable_if::value>::type + do_construct(ValueType&& value) + { + new (&storage.stack) T(std::forward(value)); + } + + /// Chooses between stack and dynamic allocation for the type decay_t, + /// assigns the correct vtable, and constructs the object on our storage. + template + void construct(ValueType&& value) + { + using T = typename std::decay::type; + + this->vtable = vtable_for_type(); + + do_construct(std::forward(value)); + } +}; + + + +namespace detail +{ + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::true_type) + { + return std::move(*p); + } + + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::false_type) + { + return *p; + } +} + +/// Performs *any_cast>>(&operand), or throws bad_any_cast on failure. +template +inline ValueType any_cast(const any& operand) +{ + auto p = any_cast::type>::type>(&operand); + if(p == nullptr) throw bad_any_cast(); + return *p; +} + +/// Performs *any_cast>(&operand), or throws bad_any_cast on failure. +template +inline ValueType any_cast(any& operand) +{ + auto p = any_cast::type>(&operand); + if(p == nullptr) throw bad_any_cast(); + return *p; +} + +/// +/// If ANY_IMPL_ANYCAST_MOVEABLE is not defined, does as N4562 specifies: +/// Performs *any_cast>(&operand), or throws bad_any_cast on failure. +/// +/// If ANY_IMPL_ANYCAST_MOVEABLE is defined, does as LWG Defect 2509 specifies: +/// If ValueType is MoveConstructible and isn't a lvalue reference, performs +/// std::move(*any_cast>(&operand)), otherwise +/// *any_cast>(&operand). Throws bad_any_cast on failure. +/// +template +inline ValueType any_cast(any&& operand) +{ +#ifdef ANY_IMPL_ANY_CAST_MOVEABLE + // https://cplusplus.github.io/LWG/lwg-active.html#2509 + using can_move = std::integral_constant::value + && !std::is_lvalue_reference::value>; +#else + using can_move = std::false_type; +#endif + + auto p = any_cast::type>(&operand); + if(p == nullptr) throw bad_any_cast(); + return detail::any_cast_move_if_true(p, can_move()); +} + +/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object +/// contained by operand, otherwise nullptr. +template +inline const T* any_cast(const any* operand) noexcept +{ + if(operand == nullptr || !operand->is_typed(typeid(T))) + return nullptr; + else + return operand->cast(); +} + +/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object +/// contained by operand, otherwise nullptr. +template +inline T* any_cast(any* operand) noexcept +{ + if(operand == nullptr || !operand->is_typed(typeid(T))) + return nullptr; + else + return operand->cast(); +} + +} + +namespace std +{ + inline void swap(linb::any& lhs, linb::any& rhs) noexcept + { + lhs.swap(rhs); + } +} + +#endif diff --git a/launchers/macosx/obj-cpp/include/fn.h b/launchers/macosx/obj-cpp/include/fn.h new file mode 100644 index 0000000000..873007b996 --- /dev/null +++ b/launchers/macosx/obj-cpp/include/fn.h @@ -0,0 +1,168 @@ +#ifndef FN_H +#define FN_H + +#include +#include + +/* + * higher-order functions + * + * Read + * http://blog.madhukaraphatak.com/functional-programming-in-c++/ + * + * + * Lamda fingerprint: +namespace { + struct f { + void operator()(int) { + // do something + } + }; +} + * + * + * +template +void for_each(Collection col, unop op){ + std::for_each(col.begin(),col.end(),op); +} + +Usage: + +auto lambda_echo = [](int i ) { std::cout << i << std::endl; }; + std::vector col{20,24,37,42,23,45,37}; + for_each(col,lambda_echo); + +*/ +template +void for_each(Collection col, unop op){ + std::for_each(col.begin(),col.end(),op); +} + +/** + * map + * + * Usage example: + auto addOne = [](int i) { return i+1;}; + auto returnCol = map(col,addOne); + for_each(returnCol,lambda_echo); + * + * + * + */ +template +Collection map(Collection col,unop op) { + std::transform(col.begin(),col.end(),col.begin(),op); + return col; +} + + + +/* +Filter usage: + +auto filteredCol = filter(col,[](int value){ return value > 30;}); + for_each(filteredCol,lambda_echo); +*/ + +template +Collection filterNot(Collection col,Predicate predicate ) { + auto returnIterator = std::remove_if(col.begin(),col.end(),predicate); + col.erase(returnIterator,std::end(col)); + return col; +} + +template +Collection filter(Collection col,Predicate predicate) { + //capture the predicate in order to be used inside function + auto fnCol = filterNot(col,[predicate](typename Collection::value_type i) { return !predicate(i);}); + return fnCol; +} + +/** + * + * Alternative map implementations + * + **/ +template()(std::declval()))> +std::vector fmap(F f, const std::vector& vec) +{ + std::vector result; + std::transform(vec.begin(), vec.end(), std::back_inserter(result), f); + return result; +} + +template()(std::declval()))> +std::shared_ptr fmap(F f, const std::shared_ptr& p) +{ + if (p == nullptr) return nullptr; + else return std::shared_ptr(new U(f(*p))); +} + + +/** + * Experimental code - should not be in production + */ + +namespace Experimental { + template + T min3(const T& a, const T& b, const T& c) + { + return std::min(std::min(a, b), c); + } + + class LevenshteinDistance + { + mutable std::vector > matrix_; + + public: + explicit LevenshteinDistance(size_t initial_size = 8) + : matrix_(initial_size, std::vector(initial_size)) + { + } + + unsigned int operator()(const std::string& s, const std::string& t) const + { + const size_t m = s.size(); + const size_t n = t.size(); + // The distance between a string and the empty string is the string's length + if (m == 0) { + return n; + } + if (n == 0) { + return m; + } + // Size the matrix as necessary + if (matrix_.size() < m + 1) { + matrix_.resize(m + 1, matrix_[0]); + } + if (matrix_[0].size() < n + 1) { + for (auto& mat : matrix_) { + mat.resize(n + 1); + } + } + // The top row and left column are prefixes that can be reached by + // insertions and deletions alone + unsigned int i, j; + for (i = 1; i <= m; ++i) { + matrix_[i][0] = i; + } + for (j = 1; j <= n; ++j) { + matrix_[0][j] = j; + } + // Fill in the rest of the matrix + for (j = 1; j <= n; ++j) { + for (i = 1; i <= m; ++i) { + unsigned int substitution_cost = s[i - 1] == t[j - 1] ? 0 : 1; + matrix_[i][j] = + min3(matrix_[i - 1][j] + 1, // Deletion + matrix_[i][j - 1] + 1, // Insertion + matrix_[i - 1][j - 1] + substitution_cost); // Substitution + } + } + return matrix_[m][n]; + } + }; +} + +#endif // FN_H