<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title>RalfZ's blog (English)</title><link href="https://blog.ralfz.com/en/atom.xml" rel="self"/><link href="https://blog.ralfz.com/en/"/><updated>2019-01-14T15:47:47.000Z</updated><id>https://blog.ralfz.com/en/</id><author><name>RalfZ</name></author><generator uri="https://gohugo.io/">Hugo</generator><entry><title>Starting from a Problem on Euler's Totient Function</title><link href="https://blog.ralfz.com/en/2019/01/14/euler-s-totient-function/"/><id>https://blog.ralfz.com/en/2019/01/14/euler-s-totient-function/</id><published>2019-01-14T15:47:47.000Z</published><updated>2019-01-14T15:47:47.000Z</updated><content type="html">&lt;p&gt;Today someone in a group chat shared &lt;a target="_blank" rel="noopener" href="https://projecteuler.net/problem=69"&gt;Problem 69&lt;/a&gt; from Project Euler, saying their algorithm ran for 40 minutes before it found the answer. That made me curious about what kind of problem it was. Here it is:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Euler&amp;rsquo;s Totient function, φ(n) [sometimes called the phi function], is used to determine the number of numbers less than n which are relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than nine and relatively prime to nine, φ(9)=6.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;n&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Relatively Prime&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;φ(n)&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;n/φ(n)&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,3&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,2,3,4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.25&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,5&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;7&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,2,3,4,5,6&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.1666&amp;hellip;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;8&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,3,5,7&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;9&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,2,4,5,7,8&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;6&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;10&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1,3,7,9&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2.5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;It can be seen that n=6 produces a maximum n/φ(n) for n ≤ 10.&lt;br&gt;&#10;Find the value of n ≤ 1,000,000 for which n/φ(n) is a maximum.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;The problem itself is easy to understand. But a pure brute-force solution would probably take those 40 minutes, so we need something more elegant.&lt;/p&gt;&#10;&lt;p&gt;After reading the problem, my intuition said the answer should be the product of the primes taken from the smallest up (&lt;code&gt;2 * 3 * 5…&lt;/code&gt;), stopping at the largest product below the 1,000,000 limit.&lt;/p&gt;&#10;&lt;p&gt;So I came up with this:&lt;/p&gt;&#10;&lt;div class="code-tabs"&gt;&#10;&lt;figure class="highlight js"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isPrime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;LIMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPrime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 510510&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;figure class="highlight python"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;isqrt&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;LIMIT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 510510&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&#10;&lt;p&gt;The answer is 510510. I submitted it, and it was correct!&lt;br&gt;&#10;It is simply the product of the primes from 2 to 17, also known as the primorial of 17 (written 17#).&lt;/p&gt;&#10;&lt;p&gt;Now let&amp;rsquo;s go back and look at the reasoning behind it.&lt;/p&gt;&#10;&lt;p&gt;The problem asks for the maximum of n/φ(n).&lt;br&gt;&#10;So we want φ(n) to be as small as possible while n is as large as possible.&lt;br&gt;&#10;That means n should share a common factor with as many of the numbers below it as possible.&lt;br&gt;&#10;So we might as well build n by multiplying primes smaller than it, from the smallest up (a common factor of 2 covers far more numbers than 7 does). That covers the most numbers without wasting any of n&amp;rsquo;s size (multiplying by 2 a second time doesn&amp;rsquo;t help at all).&lt;/p&gt;&#10;&lt;p&gt;OK, the intuition turns out to be right. So how do we prove it mathematically?&lt;/p&gt;&#10;&lt;p&gt;The &lt;a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Euler%27s_totient_function"&gt;Wikipedia article&lt;/a&gt; on Euler&amp;rsquo;s totient function has everything we need.&lt;br&gt;&#10;Following the derivation there, we have:&lt;/p&gt;&#10;&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML" display="block"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;φ&lt;/mi&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;munderover&gt;&lt;mo&gt;∏&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;/munderover&gt;&lt;msubsup&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;munder&gt;&lt;mo&gt;∏&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo&gt;∣&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;/munder&gt;&lt;msup&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;α&lt;/mi&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;munder&gt;&lt;mo&gt;∏&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo&gt;∣&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;/munder&gt;&lt;mrow&gt;&lt;mo fence="true"&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo fence="true"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;\varphi(n) = \prod_{i=1}^{r} p_i^{k_i-1}(p_i-1) = \prod_{p \mid n} p^{\alpha_p-1}(p-1) = n \prod_{p \mid n} \left(1-\frac{1}{p}\right)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;So what the problem ultimately asks us to maximize is:&lt;/p&gt;&#10;&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML" display="block"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;φ&lt;/mi&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mrow&gt;&lt;munder&gt;&lt;mo&gt;∏&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mo&gt;∣&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;/munder&gt;&lt;mrow&gt;&lt;mo fence="true"&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo fence="true"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;\frac{n}{\varphi(n)} = \frac{1}{\prod_{p \mid n} \left(1-\frac{1}{p}\right)}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;To make the right-hand side as large as possible, we make the denominator as small as possible.&lt;br&gt;&#10;Every factor in the denominator&amp;rsquo;s product is less than 1, so we want as many primes p as possible, and the smaller the better.&lt;br&gt;&#10;Hence n should be built by multiplying primes, starting from the smallest one (2).&lt;/p&gt;&#10;&lt;p&gt;So the answer is &lt;code&gt;2 * 3 * 5 * 7 * 11 * 13 * 17 = 510510&lt;/code&gt;.&lt;/p&gt;&#10;</content><summary>Today someone in a group chat shared Problem 69 from Project Euler, saying their algorithm ran for 40 minutes before it found the answer. …</summary><category term="Algorithm" scheme="https://blog.ralfz.com/en/tags/Algorithm/"/><category term="Math" scheme="https://blog.ralfz.com/en/tags/Math/"/></entry><entry><title>Getting Started with Travis CI</title><link href="https://blog.ralfz.com/en/2018/01/25/travis-ci-introduction/"/><id>https://blog.ralfz.com/en/2018/01/25/travis-ci-introduction/</id><published>2018-01-25T06:34:00.000Z</published><updated>2018-01-25T06:34:00.000Z</updated><content type="html">&lt;h1 id="The-Problem"&gt;&lt;a href="#The-Problem" class="headerlink" title="The Problem"&gt;&lt;/a&gt;The Problem&lt;/h1&gt;&lt;p&gt;Once we finish a feature, we usually need to build the project and publish it to a server. The &amp;ldquo;simplest&amp;rdquo; way is to build it locally and send it to the server by hand, with scp or similar. But that is tedious, since we have to go through the whole process manually every time. So we want a way to automate it.&lt;/p&gt;&#10;&lt;h1 id="The-Solution"&gt;&lt;a href="#The-Solution" class="headerlink" title="The Solution"&gt;&lt;/a&gt;The Solution&lt;/h1&gt;&lt;p&gt;Here we use Travis CI for &lt;a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Continuous_integration"&gt;continuous integration&lt;/a&gt;. It works with GitHub and provides an online command runner that reads the &lt;code&gt;.travis.yml&lt;/code&gt; config file in the project and runs the commands configured in it.&lt;br&gt;&#10;Taking &lt;a target="_blank" rel="noopener" href="https://github.com/RalfZhang/vdo"&gt;this project&lt;/a&gt; as an example, we watch the project&amp;rsquo;s master branch. Once development on the dev branch is done, we merge the changes into master. Travis then detects the change on master and runs the build command &lt;code&gt;yarn run build&lt;/code&gt;. When the build is done, it runs &lt;code&gt;scp -r dist name@8.8.8.8:/direction&lt;/code&gt; to deploy the dist folder to the server.&lt;br&gt;&#10;Besides the Travis config file, there is one more problem to solve: unless it is trusted, Travis has to enter a password every time it runs scp. The fix is simple. We send the public key of our local dev machine to the server, so the server trusts the dev machine. Then we give the dev machine&amp;rsquo;s private key to Travis, so Travis can pose as the dev machine and run scp without a password. (More: &lt;a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Public-key_cryptography"&gt;public-key cryptography&lt;/a&gt;)&lt;br&gt;&#10;But that raises another problem. If we upload the private key to GitHub, the key becomes public, and the server gets hacked in no time. So we encrypt the private key file with Travis&amp;rsquo;s encrypt-file, and Travis decrypts it when it runs. That keeps the key safe.&lt;/p&gt;&#10;&lt;h1 id="Steps"&gt;&lt;a href="#Steps" class="headerlink" title="Steps"&gt;&lt;/a&gt;Steps&lt;/h1&gt;&lt;p&gt;Create a &lt;code&gt;.travis.yml&lt;/code&gt; file on the master branch of the GitHub project.&lt;br&gt;&#10;Sign in to &lt;a target="_blank" rel="noopener" href="https://travis-ci.org/"&gt;https://travis-ci.org/&lt;/a&gt; with your GitHub account and enable the project you want to set up.&lt;/p&gt;&#10;&lt;p&gt;Generate a key pair on the local dev machine&lt;br&gt;&#10;&lt;code&gt;ssh-keygen -t rsa&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Copy the public key to the server (create the file on the server if it doesn&amp;rsquo;t exist)&lt;br&gt;&#10;&lt;code&gt;cat .ssh/id_rsa.pub | ssh user@8.8.8.8 &amp;quot;cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys&amp;quot;&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;The server now trusts the local dev machine. Try logging in with ssh: it won&amp;rsquo;t ask for a password.&lt;/p&gt;&#10;&lt;p&gt;The next step is to encrypt the private key.&lt;/p&gt;&#10;&lt;p&gt;First, install Ruby. Some pitfalls:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Version 2.5.0 currently pulls in dependencies that are too new, so installing Travis fails. Version 2.4.* is recommended. (This post was published in January 2018.)&lt;/li&gt;&#10;&lt;li&gt;Because of the notoriously bad network in mainland China, installing locally may fail. I recommend sending the private key to the server and encrypting it there with Travis.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Install Travis&lt;br&gt;&#10;&lt;code&gt;gem install travis&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Log in with your GitHub account&lt;br&gt;&#10;&lt;code&gt;travis login&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;In the project directory, encrypt the private key&lt;br&gt;&#10;&lt;code&gt;travis encrypt-file ~/.ssh/id_rsa --add&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;You&amp;rsquo;ll find that &lt;code&gt;.travis.yml&lt;/code&gt; now contains the following&lt;/p&gt;&#10;&#10;&lt;figure class="highlight yml"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;before_install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;openssl aes-256-cbc -K $encrypted_04c64bd82511_key -iv $encrypted_04c64bd82511_iv&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="l"&gt;in ~/.ssh/id_rsa.enc -out ~/.ssh/id_rsa -d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;If you encrypted the key on the server, now copy the encrypted private key file &lt;code&gt;id_rsa.enc&lt;/code&gt; into your local repository, and delete the unencrypted private key to stay safe.&lt;/p&gt;&#10;&lt;p&gt;Copy the decryption command above into your local &lt;code&gt;.travis.yml&lt;/code&gt;, changing &lt;code&gt;~/.ssh/id_rsa.enc&lt;/code&gt; to &lt;code&gt;./id_rsa.enc&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;That solves the main encryption problem. Next, two improvements:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Restrict the permissions of the id_rsa file, so that ssh will use the key file&lt;/li&gt;&#10;&lt;li&gt;Add the server to Travis&amp;rsquo;s list of trusted hosts&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;The config file now reads:&lt;/p&gt;&#10;&#10;&lt;figure class="highlight yml"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;before_install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;openssl aes-256-cbc -K $encrypted_04c64bd82511_key -iv $encrypted_04c64bd82511_iv&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="l"&gt;in id_rsa.enc -out ~/.ssh/id_rsa -d&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;chmod 600 ~/.ssh/id_rsa&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;echo -e &amp;#34;Host 8.8.8.8\n\tStrictHostKeyChecking no\n&amp;#34; &amp;gt;&amp;gt; ~/.ssh/config&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Finally, complete the rest of &lt;code&gt;.travis.yml&lt;/code&gt;.&lt;br&gt;&#10;The config file is simple, so I won&amp;rsquo;t explain it further here.&lt;/p&gt;&#10;&#10;&lt;figure class="highlight yml"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;node_js&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;node_js&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;node&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="s1"&gt;&amp;#39;8&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;before_install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;openssl aes-256-cbc -K $encrypted_04c64bd82511_key -iv $encrypted_04c64bd82511_iv&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="l"&gt;in id_rsa.enc -out ~/.ssh/id_rsa -d&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;chmod 600 ~/.ssh/id_rsa&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;echo -e &amp;#34;Host 8.8.8.8\n\tStrictHostKeyChecking no\n&amp;#34; &amp;gt;&amp;gt; ~/.ssh/config&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;yarn install&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;yarn run build&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;after_success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;scp -r dist name@8.8.8.8:/direction&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h1 id="References"&gt;&lt;a href="#References" class="headerlink" title="References"&gt;&lt;/a&gt;References&lt;/h1&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a target="_blank" rel="noopener" href="https://docs.travis-ci.com/"&gt;https://docs.travis-ci.com/&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a target="_blank" rel="noopener" href="https://cnodejs.org/topic/5885f19c171f3bc843f6017e"&gt;https://cnodejs.org/topic/5885f19c171f3bc843f6017e&lt;/a&gt; (in Chinese)&lt;/li&gt;&#10;&lt;li&gt;&lt;a target="_blank" rel="noopener" href="http://www.voidcn.com/article/p-aglpzloh-c.html"&gt;http://www.voidcn.com/article/p-aglpzloh-c.html&lt;/a&gt; (in Chinese)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content><summary>The Problem Once we finish a feature, we usually need to build the project and publish it to a server. The “simplest” way is to build it …</summary><category term="tool" scheme="https://blog.ralfz.com/en/tags/tool/"/><category term="CI" scheme="https://blog.ralfz.com/en/tags/CI/"/></entry><entry><title>Ngrok Setup Guide: Exposing a Local Machine to the Internet</title><link href="https://blog.ralfz.com/en/2017/12/28/ngrok-tutorial/"/><id>https://blog.ralfz.com/en/2017/12/28/ngrok-tutorial/</id><published>2017-12-27T16:59:51.000Z</published><updated>2017-12-27T16:59:51.000Z</updated><content type="html">&lt;h1 id="The-Problem"&gt;&lt;a href="#The-Problem" class="headerlink" title="The Problem"&gt;&lt;/a&gt;The Problem&lt;/h1&gt;&lt;p&gt;Lately I&amp;rsquo;ve been developing some APIs on my home computer. Locally, I can reach them at &lt;code&gt;http://localhost:8000&lt;/code&gt;. But once I leave home, there is no way to reach them directly over the internet.&lt;br&gt;&#10;If I committed and deployed to my VPS every time before going out, there would be two problems:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;It&amp;rsquo;s a real hassle.&lt;/li&gt;&#10;&lt;li&gt;Sometimes the code is still being developed and debugged, and isn&amp;rsquo;t ready to be deployed to the VPS.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;The best solution is to use a public domain and port as a relay that leads straight to a port on the dev machine at home. That is, I can visit &lt;code&gt;http://xxx.ralfz.com:8081&lt;/code&gt; to get the content of &lt;code&gt;http://localhost:8000&lt;/code&gt;.&lt;br&gt;&#10;Below, we&amp;rsquo;ll use ngrok to solve this problem.&lt;/p&gt;&#10;&lt;h1 id="What-You-Need"&gt;&lt;a href="#What-You-Need" class="headerlink" title="What You Need"&gt;&lt;/a&gt;What You Need&lt;/h1&gt;&lt;ol&gt;&#10;&lt;li&gt;A VPS on the public internet&lt;/li&gt;&#10;&lt;li&gt;A domain name pointing to that VPS&lt;/li&gt;&#10;&lt;li&gt;A dev machine, like my home computer&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h1 id="On-the-Server"&gt;&lt;a href="#On-the-Server" class="headerlink" title="On the Server"&gt;&lt;/a&gt;On the Server&lt;/h1&gt;&lt;p&gt;My VPS runs CentOS. Here are the steps.&lt;/p&gt;&#10;&lt;p&gt;Install the tools:&lt;/p&gt;&#10;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo yum install build-essential golang mercurial git&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Make sure they all install successfully.&lt;br&gt;&#10;(For me, &lt;code&gt;build-essential&lt;/code&gt; failed to install. Following &lt;a target="_blank" rel="noopener" href="https://unix.stackexchange.com/questions/16422/cant-install-build-essential-on-centos"&gt;this answer on StackExchange&lt;/a&gt;, I solved it with &lt;code&gt;sudo yum groupinstall 'Development Tools'&lt;/code&gt;.)&lt;/p&gt;&#10;&lt;p&gt;Download ngrok:&lt;/p&gt;&#10;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/inconshreveable/ngrok.git ngrok&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ngrok&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Generate the certificates (replace the domain with your own VPS&amp;rsquo;s domain):&lt;/p&gt;&#10;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;NGROK_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;yourdomain.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl genrsa -out base.key &lt;span class="m"&gt;2048&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -new -x509 -nodes -key base.key -days &lt;span class="m"&gt;10000&lt;/span&gt; -subj &lt;span class="s2"&gt;&amp;#34;/CN=&lt;/span&gt;&lt;span class="nv"&gt;$NGROK_DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -out base.pem&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl genrsa -out server.key &lt;span class="m"&gt;2048&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -new -key server.key -subj &lt;span class="s2"&gt;&amp;#34;/CN=&lt;/span&gt;&lt;span class="nv"&gt;$NGROK_DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -out server.csr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl x509 -req -in server.csr -CA base.pem -CAkey base.key -CAcreateserial -days &lt;span class="m"&gt;10000&lt;/span&gt; -out server.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp base.pem assets/client/tls/ngrokroot.crt&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Build ngrokd, the server:&lt;br&gt;&#10;&lt;code&gt;sudo make release-server&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Start it:&lt;br&gt;&#10;&lt;code&gt;sudo ./bin/ngrokd -tlsKey=server.key -tlsCrt=server.crt -domain=&amp;quot;yourdomain.com&amp;quot; -httpAddr=&amp;quot;:8081&amp;quot; -httpsAddr=&amp;quot;:8082&amp;quot;&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;Once it&amp;rsquo;s running, you&amp;rsquo;ll see:&lt;br&gt;&#10;&lt;img src="/2017/12/28/ngrok-tutorial/1.png" alt="ngrokd log output after starting"&gt;&lt;/p&gt;&#10;&lt;p&gt;Visiting &lt;code&gt;yourdomain.com:8081&lt;/code&gt; shows &lt;code&gt;Tunnel yourdomain.com:8081 not found&lt;/code&gt;, which means the server has started successfully.&lt;br&gt;&#10;&lt;img src="/2017/12/28/ngrok-tutorial/2.png" alt="Browser showing “Tunnel yourdomain.com:8081 not found”"&gt;&lt;/p&gt;&#10;&lt;p&gt;Next, we need to build the ngrok client for the client&amp;rsquo;s platform. My client (the dev machine) runs 64-bit Windows, so I run this on the VPS:&lt;/p&gt;&#10;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;windows &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amd64 make release-client&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;The settings for each platform:&lt;/p&gt;&#10;&lt;pre&gt;&lt;code&gt;Linux, 32-bit: GOOS=linux GOARCH=386&#10;Linux, 64-bit: GOOS=linux GOARCH=amd64&#10;Windows, 32-bit: GOOS=windows GOARCH=386&#10;Windows, 64-bit: GOOS=windows GOARCH=amd64&#10;Mac, 32-bit: GOOS=darwin GOARCH=386&#10;Mac, 64-bit: GOOS=darwin GOARCH=amd64&#10;ARM: GOOS=linux GOARCH=arm&#10;&lt;/code&gt;&lt;/pre&gt;&#10;&lt;p&gt;Build the ngrok client for your own client platform.&lt;br&gt;&#10;After a successful build, the client files are generated under &lt;code&gt;./bin&lt;/code&gt;, in a folder named after the client platform (mine is &lt;code&gt;./bin/windows_amd64&lt;/code&gt;).&lt;/p&gt;&#10;&lt;p&gt;Finally, copy the client folder to the client computer with scp or similar, for the next step.&lt;/p&gt;&#10;&lt;p&gt;(Note: keep the server running.)&lt;/p&gt;&#10;&lt;h1 id="On-the-Client"&gt;&lt;a href="#On-the-Client" class="headerlink" title="On the Client"&gt;&lt;/a&gt;On the Client&lt;/h1&gt;&lt;p&gt;Open the client folder you copied over, and you&amp;rsquo;ll see the &lt;code&gt;ngrok&lt;/code&gt; executable. In that folder, add a new file &lt;code&gt;ngrok.cfg&lt;/code&gt; with the following content:&lt;/p&gt;&#10;&#10;&lt;figure class="highlight plaintext"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;server_addr: yourdomain:4443&#10;trust_host_root_certs: false&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Finally, start it with the following command:&lt;/p&gt;&#10;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ngrok.exe -subdomain&lt;span class="o"&gt;=&lt;/span&gt;pub -proto&lt;span class="o"&gt;=&lt;/span&gt;http -config&lt;span class="o"&gt;=&lt;/span&gt;ngrok.cfg &lt;span class="m"&gt;8000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Here, 8000 is the local port on the client that you want to reach from the internet, and pub is the subdomain.&lt;/p&gt;&#10;&lt;p&gt;Now open &lt;code&gt;pub.yourdomain.com:8081&lt;/code&gt;, and you&amp;rsquo;ll see your local service.&lt;/p&gt;&#10;&lt;h1 id="Wrapping-Up"&gt;&lt;a href="#Wrapping-Up" class="headerlink" title="Wrapping Up"&gt;&lt;/a&gt;Wrapping Up&lt;/h1&gt;&lt;p&gt;Those are the basic steps for exposing a local machine with ngrok. The tool can do a lot more, which I&amp;rsquo;ll explore over time.&lt;/p&gt;&#10;</content><summary>The Problem Lately I’ve been developing some APIs on my home computer. Locally, I can reach them at http://localhost:8000. But once I leave …</summary><category term="tool" scheme="https://blog.ralfz.com/en/tags/tool/"/></entry></feed>