tag:blogger.com,1999:blog-85134383911577774652024-09-02T00:44:46.357-07:00Re: FactorFactor: the language, the theory, and the practice.mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]Blogger256125tag:blogger.com,1999:blog-8513438391157777465.post-79938771444062288812023-04-27T14:28:00.001-07:002023-04-27T14:28:27.323-07:00Migrating...<p>I am migrating this blog to a new site available on the domain <a href="https://re.factorcode.org">re.factorcode.org</a>.</p>
<p>While I do joke a lot about all of the <a href="https://killedbygoogle.com">Killed by Google</a> apps, products, and features... it seems inevitable that at some point Google will kill the Blogger product and I wanted more control of this website.</p>
<p>For anyone curious, the <a href="https://re.factorcode.org">new Re: Factor</a> is built currently on <a href="https://gohugo.io">Hugo</a> the "world’s fastest framework for building websites" and <a href="https://watercss.kognise.dev">water.css</a> semantic CSS framework. It is a lot simpler to use than the <a href="https://blogger.com">Blogger interface</a>, plus a bit faster since it's just a simple static website.</p>
<p>This blog will remain as a historical archive, but new posts will be on the new website...</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-54204523899744210202023-04-25T20:51:00.002-07:002023-04-26T13:29:55.721-07:00OpenAI<p>It’s been pretty hard to avoid all of the incredible stories about
<a href="https://en.wikipedia.org/wiki/Artificial_intelligence">artificial
intelligence</a> over
the past few months. There seem to be incredible applications to the area of
<a href="https://www.mckinsey.com/featured-insights/mckinsey-explainers/what-is-generative-ai">generative
AI</a>
occurring on a daily basis. Image generation with
<a href="http://midjourney.com">Midjourney</a> is pretty next-level. Code generation
using <a href="https://github.com/features/preview/copilot-x">GitHub Copilot</a> seems
pretty amazing. Interacting with large language models like
<a href="https://openai.com/product/gpt-4">GPT-4</a> or <a href="https://bard.google.com">Bard</a>
or <a href="https://www.bing.com/new">Bing Chat</a> or <a href="https://ai.facebook.com/blog/large-language-model-llama-meta-ai/">Facebook
LLaMA</a> or
<a href="https://stability.ai/blog/stability-ai-launches-the-first-of-its-stablelm-suite-of-language-models">StableLM</a>
and so many others seems like science fiction. Audio models like
<a href="https://openai.com/research/whisper">Whisper</a> used for audio transcription
even make popular audio assistants look pretty dated.</p>
<p>With all of the hype, it seemed inevitable that
<a href="https://factorcode.org">Factor</a> would gain some kind of AI functionality.</p>
<p>Despite the <a href="https://hackernoon.com/how-openai-transitioned-from-a-nonprofit-to-a-$29b-for-profit-company#">non-profit vs for-profit controversy of
OpenAI</a>,
they do seem to have a momentary lead in the race to make tools that others
can build upon. One of those tools is the <a href="https://platform.openai.com/docs/introduction">OpenAI
API</a>, which is made available
using <a href="https://restfulapi.net/introduction-to-json/">JSON</a> and
<a href="https://www.cloudflare.com/learning/ddos/glossary/hypertext-transfer-protocol-http">HTTP</a>.
Besides the <a href="https://platform.openai.com/docs/api-reference">OpenAI API
Reference</a>, there is an
<a href="https://github.com/openai/openai-cookbook/">OpenAI Cookbook</a> and popular
libraries such as <a href="https://github.com/openai/openai-python">OpenAI Python</a>
for building systems using it.</p>
<p>Recently, I contributed the <a href="https://github.com/factor/factor/blob/master/extra/openai/openai.factor">openai
vocabulary</a>
which allows using all the methods currently made available by
<a href="https://openai.com">OpenAI</a>. You will need an <a href="https://platform.openai.com/account/api-keys">OpenAI API
Key</a>.</p>
<p>Once you obtain that,
you can set it in <a href="https://docs.factorcode.org/content/article-listener.html">the
listener</a>.</p>
<blockquote><pre><span></span><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">USE:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">openai</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"sk-....................."</span><span style="color: #bbbbbb"> </span>openai-api-key<span style="color: #bbbbbb"> </span><span style="color: #008000">set-global</span></pre></blockquote>
<p>And now you have enough to try it out…</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"text-davinci-003"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"what is the factor programming language"</span>
<span style="color: #bbbbbb"> </span><completion><span style="color: #bbbbbb"> </span><span style="color: #666666">100 </span>>>max_tokens<span style="color: #bbbbbb"> </span>create-completion
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"choices"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span><span style="color: #008000">first</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"text"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span><span style="color: #008000">print</span>
Factor is a stack-oriented programming language, designed for creating
flexible, reusable software components. It combines elements from both
object-oriented and functional programming, and provides powerful features,
including static typing and static type checking, an interactive program
development environment, built-in automated testing, and a wide range of
built-in data types. The language is designed to be easy to use, yet provide
a high degree of flexibility.</pre></blockquote>
<p>Cool!</p>
<p>We even have a <a href="https://github.com/factor/factor/blob/master/extra/discord/chatgpt-bot/chatgpt-bot.factor">Discord bot using
OpenAI</a>
that answers on the <a href="https://discord.gg/QxJYZx3QDf">Factor Discord server</a>.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-84259691362707331222023-03-07T16:58:00.003-08:002023-04-01T14:52:20.543-07:00ASCII Table PDF<p><a href="https://jugad2.blogspot.com/p/about-vasudev-ram.html">Vasudev Ram</a> has a
blog with many different posts about various programming topics including
Python, Linux, SQL, and PDFs. On the topic of PDF generation, they have a
blog post about making an <a href="https://jugad2.blogspot.com/2015/03/ascii-table-to-pdf-with-xtopdf.html">ASCII Table to PDF with
xtopdf</a>.</p>
<blockquote><i><p>Recently, I had the need for an ASCII table lookup, which I searched for and
found, thanks to the folks here:</p>
<p><a href="https://www.ascii-code.com">www.ascii-code.com</a></p>
<p>That gave me the idea of writing a simple program to generate an ASCII table
in PDF. Here is the code for a part of that table - the first 32 (0 to 31)
ASCII characters, which are the control characters:</p></i></blockquote>
<p>It might not be widely known, but <a href="https://factorcode.org">Factor</a> has built-in
support for writing to <a href="https://docs.factorcode.org/content/vocab-pdf.streams.html">PDF
Streams</a> using the
<a href="https://docs.factorcode.org/content/article-io.styles.html">formatted output</a>
protocol. This supports <a href="https://docs.factorcode.org/content/article-styles.html">text
styles</a> including
changing font names, bold and italic styles, foreground and background colors,
etc.</p>
<p>We start by defining the symbols and descriptions of the first 32 ASCII
characters. These are all non-printable <a href="https://en.wikipedia.org/wiki/Control_character">control
character</a>, which is why we
use this array of strings to render them in a table.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">ASCII</span><span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"NUL Null char"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"SOH Start of Heading"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"STX Start of Text"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"ETX End of Text"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"EOT End of Transmission"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"ENQ Enquiry"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"ACK Acknowledgment"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"BEL Bell"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"BS Back Space"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"HT Horizontal Tab"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"LF Line Feed"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"VT Vertical Tab"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"FF Form Feed"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"CR Carriage Return"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"SO Shift Out / X-On"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"SI Shift In / X-Off"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"DLE Data Line Escape"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"DC1 Device Control 1 (oft. XON)"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"DC2 Device Control 2"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"DC3 Device Control 3 (oft. XOFF)"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"DC4 Device Control 4"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"NAK Negative Acknowledgement"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"SYN Synchronous Idle"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"ETB End of Transmit Block"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"CAN Cancel"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"EM End of Medium"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"SUB Substitute"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"ESC Escape"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"FS File Separator"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"GS Group Separator"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"RS Record Separator"</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"US Unit Separator"</span>
}</pre></blockquote>
<p>The core <em>printing</em> logic is a header, followed by rows for each character,
formatted into a table of decimal, octal, hexadecimal, and binary values
along with their symbol and description from the array above:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">ascii.</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"ASCII Control Characters - 0 to 31"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">print</span><span style="color: #bbbbbb"> </span><span style="color: #008000">nl</span>
<span style="color: #bbbbbb"> </span>ASCII<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>>dec<span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>>oct<span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span><span style="color: #BA2121">CHAR:</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">0</span><span style="color: #bbbbbb"> </span><span style="color: #008000">pad-head</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>>hex<span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #BA2121">CHAR:</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">0</span><span style="color: #bbbbbb"> </span><span style="color: #008000">pad-head</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>>bin<span style="color: #bbbbbb"> </span><span style="color: #666666">8 </span><span style="color: #BA2121">CHAR:</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">0</span><span style="color: #bbbbbb"> </span><span style="color: #008000">pad-head</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">cleave</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">dip</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">" "</span><span style="color: #bbbbbb"> </span>split1<span style="color: #bbbbbb"> </span><span style="color: #666666">6 </span>narray
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">map-index</span><span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"DEC"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"OCT"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"HEX"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"BIN"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Symbol"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Description"</span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">prefix</span><span style="color: #bbbbbb"> </span>format-table<span style="color: #bbbbbb"> </span><span style="color: #008000">unclip</span>
<span style="color: #bbbbbb"> </span>H{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>font-style<span style="color: #bbbbbb"> </span>bold<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>format<span style="color: #bbbbbb"> </span><span style="color: #008000">nl</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">print</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">each</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Since the <a href="https://docs.factorcode.org/content/article-ui-listener.html">UI
listener</a> supports formatted streams, you can see it from the listener:</p>
<img src="https://i.imgur.com/OTVFgNe.png" alt="" width="614" height="655" />
<p>Outputting this to a PDF file is now easy. We make sure to set the font to
<code>monospace</code> and then run <code>ascii.</code> with our <a href="https://docs.factorcode.org/content/word-with-pdf-writer,pdf.streams.html">PDF
writer</a>,
saving the generated PDF output into a file.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">ascii-pdf</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">path</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>H{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>font-name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"monospace"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>ascii.<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>with-style
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>with-pdf-writer<span style="color: #bbbbbb"> </span>pdf>string<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>utf8<span style="color: #bbbbbb"> </span>set-file-contents<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We also support writing to <a href="https://docs.factorcode.org/content/article-html.streams.html">HTML
streams</a> in a
similar manner, so it would be pretty easy to create an <code>ascii-html</code> word to
output an HTML file with the same printing logic above but instead using our
<a href="https://docs.factorcode.org/content/word-with-html-writer,html.streams.html">HTML
writer</a>.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-17041660389216110562023-03-03T15:14:00.002-08:002023-03-03T15:14:35.909-08:00Short UUID<p>The <a href="https://github.com/skorokithakis/shortuuid">shortuuid</a> project is a
“<em>simple python library that generates concise, unambiguous, URL-safe UUIDs</em>”.
I thought it would be a fun exercise to implement this in
<a href="https://factorcode.org">Factor</a>.</p>
<p>What is a “short UUID”?</p>
<p>You can read the <a href="https://web.archive.org/web/20130525172507/https://blog.stochastictechnologies.com/new-python-module-shortuuid">original
announcement</a>,
but basically it is a string representation of a number using a reduced
alphabet that can be used in places like URLs where conciseness is
desirable. The author mentions that it provides security by “<em>not divulging
information (such as how many rows there are in that particular table, the
time difference between one item and the next, etc.)</em>”. However, I think it
is more <a href="https://en.wikipedia.org/wiki/Security_through_obscurity">security through
obscurity</a> than
real security.</p>
<p>In any event, the alphabet used are these 57 characters:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">alphabet</span>
<span style="color: #BA2121">"23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"</span></pre></blockquote>
<p>We encode a numeric input by repeatedly “divmod”, indexing into an alphabet,
until exhausted.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">encode-uuid</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">uuid</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">shortuuid</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span><span style="color: #008000">></span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>alphabet<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span><span style="color: #008000">/mod</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #BA2121">""</span><span style="color: #bbbbbb"> </span><span style="color: #008000">produce-as</span><span style="color: #bbbbbb"> </span><span style="color: #008000">nip</span><span style="color: #bbbbbb"> </span><span style="color: #008000">reverse</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We decode using a reverse process, looking up the position of each character
in the alphabet, re-assembling the numeric input for each character in the
<em>shortuuid</em>.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">decode-uuid</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">shortuuid</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">uuid</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>[
<span style="color: #bbbbbb"> </span>alphabet<span style="color: #bbbbbb"> </span><span style="color: #008000">index</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>alphabet<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">dip</span><span style="color: #bbbbbb"> </span><span style="color: #008000">+</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">reduce</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/blob/master/shortuuid/shortuuid.factor">GitHub</a>,
including features to deal with <em>legacy</em> values generated before version
1.0.0 as well as supporting different alphabets being used.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-38840049046238354862023-03-01T09:43:00.004-08:002023-03-02T07:30:55.636-08:00Geo Timezones<p><a href="https://bradfitz.com">Brad Fitzpatrick</a> wrote a <a href="https://github.com/bradfitz/latlong">Go package called
latlong</a> which efficiently maps a
latitude/longitude to a timezone. The <a href="https://plus.google.com/u/0/+BradFitzpatrick/posts/XVyy1bAzkZd">original
post</a>
describing it was on <a href="https://plus.google.com">Google+</a> and is likely lost
forever — unless it made it into the <a href="https://wiki.archiveteam.org/index.php/Google%2B">Google+
archive</a> before Google+
joined the <a href="https://killedbygoogle.com">Google Graveyard</a>.</p>
<blockquote><i>
It tries to have a small binary size (~360 KB), low memory footprint
(~1 MB), and incredibly fast lookups (~0.5 microseconds). It does not
try to be perfectly accurate when very close to borders.</i></blockquote>
<p>It’s available in other languages, too!</p>
<p><a href="https://huonw.github.io">Huon Wilson</a> ported the library to the <a href="https://www.rust-lang.org">Rust
Programming Language</a>, making the code <a href="https://github.com/huonw/tz-search">available
on GitHub</a> and <a href="https://crates.io/crates/tz-search">installable via
Cargo</a>. There is even a wrapper made for
<a href="https://nodejs.org/">NodeJs</a> that is <a href="https://www.npmjs.com/package/node-latlong">installable via
NPM</a> that uses a <a href="https://github.com/Ecube-Labs/latlong-cli">command-line
executable written in Go</a>.</p>
<p>When it was announced in 2015, I had ported the library to
<a href="https://factorcode.org">Factor</a>, but missed the opportunity to blog about
it. Below we discuss some details about the implementation, starting with
its use of a <a href="http://efele.net/maps/tz/world/">shapefile of the TZ timezones of the
world</a> to divide the world into zones that
are assigned timezone values — looking something like this:</p>
<p>
<img src="http://i.imgur.com/Rt8bLSD.png" alt="" width="472" height="378" />
</p>
<p>The world is divided into 6 <em>zoom levels</em> of tiles (represented by a key and
an index value) that allow us to search from a very large area first, then
down to the more specific geographic area. Note: we represent the struct as
a <a href="https://en.wikipedia.org/wiki/Endianness">big endian</a> struct with
<a href="http://www.catb.org/esr/structure-packing/">structure packing</a> to minimize
wasted space in the files.</p>
<p>The <em>zoom levels</em> are then cached using <a href="https://docs.factorcode.org/content/article-literals.html">literal
syntax</a> into a
<code>zoom-levels</code> constant.</p>
<blockquote><pre>BE-PACKED-STRUCT:<span style="color: #bbbbbb"> </span>tile
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>key<span style="color: #bbbbbb"> </span>uint<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>idx<span style="color: #bbbbbb"> </span>ushort<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
SPECIALIZED-ARRAY:<span style="color: #bbbbbb"> </span>tile
<span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">zoom-levels</span><span style="color: #bbbbbb"> </span>$[
<span style="color: #bbbbbb"> </span><span style="color: #666666">6 </span><iota><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>number>string
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"vocab:geo-tz/zoom"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">".dat"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">surround</span>
<span style="color: #bbbbbb"> </span>binary<span style="color: #bbbbbb"> </span>file-contents<span style="color: #bbbbbb"> </span>tile<span style="color: #bbbbbb"> </span>cast-array
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">map</span>
]</pre></blockquote>
<p>Each of the <em>zoom levels</em> reference indexes into a <em>leaves</em> data structure
that contains 14,110 items — each represented by one of three data types:</p>
<ol>
<li>Type <code>S</code> is a <em>string</em>.</li>
<li>Type <code>2</code> is a <em>one bit tile</em>.</li>
<li>Type <code>P</code> is a <em>pixmap</em> thats 128 bytes long.</li>
</ol>
<p>These we load and cache into a <code>unique-leaves</code> constant.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">#leaves</span><span style="color: #bbbbbb"> </span><span style="color: #666666">14110</span>
BE-PACKED-STRUCT:<span style="color: #bbbbbb"> </span>one-bit-tile
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>idx0<span style="color: #bbbbbb"> </span>ushort<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>idx1<span style="color: #bbbbbb"> </span>ushort<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>bits<span style="color: #bbbbbb"> </span>ulonglong<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">unique-leaves</span><span style="color: #bbbbbb"> </span>$[
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"vocab:geo-tz/leaves.dat"</span><span style="color: #bbbbbb"> </span>binary<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>#leaves<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">read1</span><span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">CHAR:</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">S</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">read-until</span><span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>utf8<span style="color: #bbbbbb"> </span>decode<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">CHAR:</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">2</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>one-bit-tile<span style="color: #bbbbbb"> </span>read-struct<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">CHAR:</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">P</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">128 </span><span style="color: #008000">read</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">case</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">replicate</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>with-file-reader
]</pre></blockquote>
<p>The core logic involves looking up a leaf (which is one of three types,
loaded above), given an <code>(x, y)</code> coordinate. If it is a <code>string</code> type,
we are done. If it is a <code>one-bit-tile</code>, we defer to the appropriate leaf
specified by <code>idx0</code> or <code>idx1</code>. And if it is pixmap, we have a smidge
more logic to detect oceans or defer again to a different leaf.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">ocean-index</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0xffff</span>
GENERIC#:<span style="color: #bbbbbb"> </span>lookup-leaf<span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">leaf</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">x</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">y</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">zone/f</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #008000; font-weight: bold">M:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">string</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">lookup-leaf</span><span style="color: #bbbbbb"> </span><span style="color: #008000">2drop</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">M::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">one-bit-tile</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">lookup-leaf</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">leaf</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">x</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">y</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">zone/f</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>leaf<span style="color: #bbbbbb"> </span>bits>><span style="color: #bbbbbb"> </span>y<span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span>bits<span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span>bits<span style="color: #bbbbbb"> </span><span style="color: #008000">bitor</span><span style="color: #bbbbbb"> </span><span style="color: #008000">bit?</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>leaf<span style="color: #bbbbbb"> </span>idx1>><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>leaf<span style="color: #bbbbbb"> </span>idx0>><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if</span>
<span style="color: #bbbbbb"> </span>unique-leaves<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span>y<span style="color: #bbbbbb"> </span>lookup-leaf<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">M::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">byte-array</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">lookup-leaf</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">leaf</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">x</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">y</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">zone/f</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>y<span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span>bits<span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span>bits<span style="color: #bbbbbb"> </span><span style="color: #008000">bitor</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>i
<span style="color: #bbbbbb"> </span>i<span style="color: #bbbbbb"> </span>leaf<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span><span style="color: #666666">8 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>i<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>leaf<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span><span style="color: #008000">+</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>ocean-index<span style="color: #bbbbbb"> </span><span style="color: #008000">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>unique-leaves<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span>y<span style="color: #bbbbbb"> </span>lookup-leaf
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span</pre></blockquote>
<p>We’re almost done! Given a zoom level, a <code>tile-key</code> helps us find a
specific tile that we then can lookup the leaf for, hopefully finding the
<code>timezone</code> associated with the coordinate.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">lookup-zoom-level</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">zoom-level</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">x</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">y</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">tile-key</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">zone/f</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>zoom-level<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>key>><span style="color: #bbbbbb"> </span>tile-key<span style="color: #bbbbbb"> </span>>=<<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>search<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>key>><span style="color: #bbbbbb"> </span>tile-key<span style="color: #bbbbbb"> </span><span style="color: #008000">=</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>idx>><span style="color: #bbbbbb"> </span>unique-leaves<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span>y<span style="color: #bbbbbb"> </span>lookup-leaf
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Each coordinate is effectively a
<a href="https://en.wikipedia.org/wiki/Pixel">pixel</a> in the image, so our logic
searches from the outermost <em>zoom level</em> to the innermost, trying to lookup
a timezone in each one using the coordinate and level as a <code>tile-key</code>.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">tile-key</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">x</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">y</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">level</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">tile-key</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #008000">neg</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>n
<span style="color: #bbbbbb"> </span>y<span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>n<span style="color: #bbbbbb"> </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span><span style="color: #666666">14 </span>bits<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi@</span>
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">0 14 28 </span>}<span style="color: #bbbbbb"> </span>bitfield<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">lookup-pixel</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">x</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">y</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">zone</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">6 </span><iota><span style="color: #bbbbbb"> </span>[|<span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span>|
<span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span>zoom-levels<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span>
<span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span>y<span style="color: #bbbbbb"> </span><span style="color: #008000">2dup</span><span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span>tile-key
<span style="color: #bbbbbb"> </span>lookup-zoom-level
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">map-find-last</span><span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Finally, we have enough to implement our public API, converting a given
latitude/longitude coordinate to a <em>pixel</em> value, deferring to the word we
just defined above.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">deg-pixels</span><span style="color: #bbbbbb"> </span><span style="color: #666666">32</span>
<span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">lookup-zone</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">lat</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">lon</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">zone</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>lon<span style="color: #bbbbbb"> </span><span style="color: #666666">180 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>deg-pixels<span style="color: #bbbbbb"> </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0 360 </span>deg-pixels<span style="color: #bbbbbb"> </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span>clamp
<span style="color: #bbbbbb"> </span><span style="color: #666666">90 </span>lat<span style="color: #bbbbbb"> </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span>deg-pixels<span style="color: #bbbbbb"> </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0 180 </span>deg-pixels<span style="color: #bbbbbb"> </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span>clamp
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">>integer</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi@</span><span style="color: #bbbbbb"> </span>lookup-pixel<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>And then a couple of test cases to show it’s working:</p>
<blockquote><pre>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"America/Los_Angeles"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">37.7833 -122.4167 </span>lookup-zone<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Australia/Sydney"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">-33.8885 151.1908 </span>lookup-zone<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<p>Performance is pretty good, we can generate over 3 million lookups per
second, putting our cost per lookup around 0.33 microseconds. And all of
that in less than 70 lines of code.</p>
<p>This is available on my
<a href="https://github.com/mrjbq7/re-factor/tree/master/geo-tz">GitHub</a>.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-14566714327680011772023-02-28T07:28:00.002-08:002023-02-28T07:46:29.275-08:00Reference Server<p><a href="https://eatonphil.com">Phil Eaton</a> made a repository of <a href="https://github.com/eatonphil/referenceserver">Barebones UNIX socket servers</a> with this description:</p>
<blockquote><i>I find myself writing this server in some language every few months. Each time I have to scour the web for a good reference. Use this as a reference to write your own bare server in C or other languages with a UNIX API (Python, OCaml, etc).</i></blockquote>
<p>Many developers learning network programming will encounter <a href="https://beej.us/guide/bgnet/">Beej's Guide to Network Programming</a> which uses the sockets API, has been ported to many platforms, and explains the intricacies of making computers talk to each other in this manner.</p>
<h3>C</h3>
<p>We can take a look at his <a href="https://github.com/eatonphil/referenceserver/blob/master/c/server.c">C implementation</a> of a server that listens on port 15000, accepts client connections, reads up to 1024 bytes which are printed to the screen, then writes <code>hello world</code> back to the client and disconnects them:</p>
<blockquote><pre><span style="color: #9C6500">#include</span><span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"><netinet/in.h></span>
<span style="color: #9C6500">#include</span><span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"><stdio.h></span>
<span style="color: #9C6500">#include</span><span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"><stdlib.h></span>
<span style="color: #9C6500">#include</span><span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"><sys/socket.h></span>
<span style="color: #9C6500">#include</span><span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic"><unistd.h></span>
<span style="color: #B00040">int</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">main</span>()<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span><span style="color: #B00040">int</span><span style="color: #bbbbbb"> </span>server,<span style="color: #bbbbbb"> </span>client;
<span style="color: #bbbbbb"> </span><span style="color: #B00040">socklen_t</span><span style="color: #bbbbbb"> </span>addrlen;
<span style="color: #bbbbbb"> </span><span style="color: #B00040">int</span><span style="color: #bbbbbb"> </span>bufsize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1024</span>;
<span style="color: #bbbbbb"> </span><span style="color: #B00040">char</span><span style="color: #bbbbbb"> </span><span style="color: #666666">*</span>buffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>malloc(bufsize);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">struct</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">sockaddr_in</span><span style="color: #bbbbbb"> </span>address;
<span style="color: #bbbbbb"> </span>server<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>socket(AF_INET,<span style="color: #bbbbbb"> </span>SOCK_STREAM,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>);
<span style="color: #bbbbbb"> </span>address.sin_family<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>AF_INET;
<span style="color: #bbbbbb"> </span>address.sin_addr.s_addr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>INADDR_ANY;
<span style="color: #bbbbbb"> </span>address.sin_port<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>htons(<span style="color: #666666">15000</span>);
<span style="color: #bbbbbb"> </span>bind(server,<span style="color: #bbbbbb"> </span>(<span style="color: #008000; font-weight: bold">struct</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">sockaddr</span><span style="color: #bbbbbb"> </span><span style="color: #666666">*</span>)<span style="color: #bbbbbb"> </span><span style="color: #666666">&</span>address,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">sizeof</span>(address));
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">while</span><span style="color: #bbbbbb"> </span>(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>listen(server,<span style="color: #bbbbbb"> </span><span style="color: #666666">10</span>);
<span style="color: #bbbbbb"> </span>client<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>accept(server,<span style="color: #bbbbbb"> </span>(<span style="color: #008000; font-weight: bold">struct</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">sockaddr</span><span style="color: #bbbbbb"> </span><span style="color: #666666">*</span>)<span style="color: #bbbbbb"> </span><span style="color: #666666">&</span>address,<span style="color: #bbbbbb"> </span><span style="color: #666666">&</span>addrlen);
<span style="color: #bbbbbb"> </span>recv(client,<span style="color: #bbbbbb"> </span>buffer,<span style="color: #bbbbbb"> </span>bufsize,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>);
<span style="color: #bbbbbb"> </span>printf(<span style="color: #BA2121">"%s</span><span style="color: #AA5D1F; font-weight: bold">\n</span><span style="color: #BA2121">"</span>,<span style="color: #bbbbbb"> </span>buffer);
<span style="color: #bbbbbb"> </span>write(client,<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"hello world</span><span style="color: #AA5D1F; font-weight: bold">\n</span><span style="color: #BA2121">"</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">12</span>);
<span style="color: #bbbbbb"> </span>close(client);
<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>close(server);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">return</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;
}</pre></blockquote>
<h3>Factor</h3>
<p>A direct <a href="https://factorcode.org">Factor</a> translation — without any error checking, like in the original example — using the <a href="https://docs.factorcode.org/content/article-alien.html">C library interface</a> might look something like this:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">USING:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">accessors</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">alien.c-types</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">alien.data</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">byte-arrays</span>
<span style="color: #0000FF; font-weight: bold">classes.struct</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">io</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">io.encodings.string</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">io.encodings.utf8</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">kernel</span>
<span style="color: #0000FF; font-weight: bold">sequences</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">unix.ffi</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">unix.types</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">reference-server</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">1024 </span><byte-array><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>buffer
<span style="color: #bbbbbb"> </span>AF_INET<span style="color: #bbbbbb"> </span>SOCK_STREAM<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>socket<span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>server
<span style="color: #bbbbbb"> </span>sockaddr-in<span style="color: #bbbbbb"> </span>malloc-struct
<span style="color: #bbbbbb"> </span>AF_INET<span style="color: #bbbbbb"> </span>>>family
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>>>addr
<span style="color: #bbbbbb"> </span><span style="color: #666666">15000 </span>htons<span style="color: #bbbbbb"> </span>>>port<span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>address
<span style="color: #bbbbbb"> </span>server<span style="color: #bbbbbb"> </span>address<span style="color: #bbbbbb"> </span>sockaddr-in<span style="color: #bbbbbb"> </span>heap-size<span style="color: #bbbbbb"> </span>bind<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span>
<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>server<span style="color: #bbbbbb"> </span><span style="color: #666666">10 </span>listen<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span>
<span style="color: #bbbbbb"> </span>server<span style="color: #bbbbbb"> </span>address<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>socklen_t<span style="color: #bbbbbb"> </span><ref><span style="color: #bbbbbb"> </span>accept<span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>client
<span style="color: #bbbbbb"> </span>client<span style="color: #bbbbbb"> </span>buffer<span style="color: #bbbbbb"> </span><span style="color: #666666">1024 0 </span>recv
<span style="color: #bbbbbb"> </span>buffer<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span><span style="color: #008000">head-slice</span><span style="color: #bbbbbb"> </span>utf8<span style="color: #bbbbbb"> </span>decode<span style="color: #bbbbbb"> </span><span style="color: #008000">print</span><span style="color: #bbbbbb"> </span><span style="color: #008000">flush</span>
<span style="color: #bbbbbb"> </span>client<span style="color: #bbbbbb"> </span>$[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"hello world\n"</span><span style="color: #bbbbbb"> </span>>byte-array<span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span>unix.ffi:write<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span>
<span style="color: #bbbbbb"> </span>client<span style="color: #bbbbbb"> </span>close<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span>
<span style="color: #bbbbbb"> </span><span style="color: #880000">t</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">loop</span>
<span style="color: #bbbbbb"> </span>server<span style="color: #bbbbbb"> </span>close<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>I noticed that some of his examples are more idiomatic to the language, so we could rewrite this using <a href="https://docs.factorcode.org/content/article-io.servers.html">threaded servers</a> — gaining the benefit of working on Windows as well as error handling and logging — using a handler <a href="https://docs.factorcode.org/content/article-quotations.html">quotation</a> to implement the read/print/write/disconnect logic.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">USING:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">accessors</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">io</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">io.encodings.binary</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">io.encodings.string</span>
<span style="color: #0000FF; font-weight: bold">io.encodings.utf8</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">io.servers</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">kernel</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">namespaces</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">reference-server</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>binary<span style="color: #bbbbbb"> </span><threaded-server>
<span style="color: #bbbbbb"> </span><span style="color: #666666">15000 </span>>>insecure
<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #666666">1024 </span><span style="color: #008000">read-partial</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>utf8<span style="color: #bbbbbb"> </span>decode<span style="color: #bbbbbb"> </span><span style="color: #008000">print</span><span style="color: #bbbbbb"> </span><span style="color: #008000">flush</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">with-global</span>
<span style="color: #bbbbbb"> </span>$[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"hello world\n"</span><span style="color: #bbbbbb"> </span>>byte-array<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>io:write<span style="color: #bbbbbb"> </span><span style="color: #008000">flush</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">when*</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>>>handler
<span style="color: #bbbbbb"> </span>start-server<span style="color: #bbbbbb"> </span>wait-for-server<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>This is available on my <a href="https://github.com/mrjbq7/re-factor/blob/master/reference-server/reference-server.factor">GitHub</a>.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-90667134769397217662023-02-26T14:38:00.005-08:002023-02-26T17:41:56.843-08:00Weighted Random<p>Some time ago, I <a href="https://github.com/factor/factor/commit/5f4bf4513bad8bf6d6b71fa97bfaf29aae3a014d">implemented a way to generate weighted random values</a> from a discrete distribution in <a href="https://factorcode.org">Factor</a>. It ended up being a pretty satisfyingly simple word that builds a cumulative probability table, generates a random probability, then searches the table to find which value to return:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">weighted-random</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">histogram</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">obj</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">unzip</span><span style="color: #bbbbbb"> </span>cum-sum<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">last</span><span style="color: #bbbbbb"> </span><span style="color: #008000">>float</span><span style="color: #bbbbbb"> </span>random<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">keep</span><span style="color: #bbbbbb"> </span>bisect-left<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<h3>Is It Fast?</h3>
<p>We can define a simple discrete distribution of values:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">dist</span><span style="color: #bbbbbb"> </span>H{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"A"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span>}<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"B"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span>}<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"C"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span>}<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"D"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4 </span>}<span style="color: #bbbbbb"> </span>}</pre></blockquote>
<p>And it seems to work — we can make a few random values from it:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span>weighted-random<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"C"</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span>weighted-random<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"C"</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span>weighted-random<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"D"</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span>weighted-random<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"B"</span></pre></blockquote>
<p>After generating a lot of random values, we can see the <a href="https://docs.factorcode.org/content/word-histogram,math.statistics.html">histogram</a> matches our distribution:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #666666">10,000,000 </span>[<span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span>weighted-random<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">replicate</span><span style="color: #bbbbbb"> </span>histogram<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
H{
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"A"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">998403 </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"B"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2000400 </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"C"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3001528 </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"D"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3999669 </span>}
}</pre></blockquote>
<p>But, how fast is it?</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>[ <span style="color: #666666">10,000,000 </span>[<span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span>weighted-random<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">replicate</span> ]<span style="color: #bbbbbb"> </span>time<span style="color: #bbbbbb"> </span>
Running<span style="color: #bbbbbb"> </span>time:<span style="color: #bbbbbb"> </span><span style="color: #666666">3.02998325 </span>seconds</pre></blockquote>
<p>Okay, so it's not <i>that</i> fast... generating around <b>3.3 million</b> per second on one of my computers.</p>
<h3>Improvements</h3>
<p>We can make two quick improvements to this:</p>
<ol>
<li>First, we can factor out the initial step from the random number generation.</li>
<li>Second, we can take advantage of a <a href="https://github.com/factor/factor/commit/930bc25ad350e83806e482d130de6a589cee886a">recent improvement to the random vocabulary</a>, mainly to change the <code>random</code> word that was previously implemented for different types to instead get the current <a href="https://docs.factorcode.org/content/word-random-generator%2Crandom.html">random-generator</a> and then pass it to the <code>random*</code> implementation instead. This allows a few speedups where we can lookup the <a href="https://docs.factorcode.org/content/article-namespaces.html">dynamic variable</a> once and then use it many times.</li>
</ol>
<p>That results in this definition:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">weighted-randoms</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">length</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">histogram</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">unzip</span><span style="color: #bbbbbb"> </span>cum-sum<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">last</span><span style="color: #bbbbbb"> </span><span style="color: #008000">>float</span><span style="color: #bbbbbb"> </span>random-generator<span style="color: #bbbbbb"> </span><span style="color: #008000">get</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">keep</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">dip</span>
<span style="color: #bbbbbb"> </span>'[<span style="color: #bbbbbb"> </span>_<span style="color: #bbbbbb"> </span>_<span style="color: #bbbbbb"> </span>random*<span style="color: #bbbbbb"> </span>_<span style="color: #bbbbbb"> </span>bisect-left<span style="color: #bbbbbb"> </span>_<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">replicate</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>That gives us a nice speedup, just over <b>10 million</b> per second:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">10,000,000 </span>dist<span style="color: #bbbbbb"> </span>weighted-randoms<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>time<span style="color: #bbbbbb"> </span>histogram<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
Running<span style="color: #bbbbbb"> </span>time:<span style="color: #bbbbbb"> </span><span style="color: #666666">0.989039625 </span>seconds
H{
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"A"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1000088 </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"B"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1999445 </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"C"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3000688 </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"D"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3999779 </span>}
}</pre></blockquote>
<p>That's pretty nice, but it turns out that we can do better.</p>
<h3>Vose Alias Method</h3>
<p><a href="https://www.keithschwarz.com/me/">Keith Schwarz</a> wrote a fascinating blog post about some better algorithms for <a href="https://www.keithschwarz.com/darts-dice-coins/">sampling from a discrete distribution</a>. One of those algorithms is the <a href="https://en.wikipedia.org/wiki/Alias_method">Vose Alias Method</a> which creates a data structure of items, probabilities, and an alias table that is used to return an alternate choice:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">TUPLE:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">vose</span>
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #19177C">n</span><span style="color: #bbbbbb"> </span>integer<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #19177C">items</span><span style="color: #bbbbbb"> </span>array<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #19177C">probs</span><span style="color: #bbbbbb"> </span>array<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #19177C">alias</span><span style="color: #bbbbbb"> </span>array<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We construct a <code>vose</code> tuple by splitting the distribution into items and their probabilities, and then processing the probabilities into lists of <code>small</code> (less than 1) or <code>large</code> (greater than or equal to 1), iteratively aliasing the index of smaller items to larger items.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF"><vose></span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">dist</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">vose</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>V{<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">clone</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>small
<span style="color: #bbbbbb"> </span>V{<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">clone</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>large
<span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span><span style="color: #008000">assoc-size</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>n
<span style="color: #bbbbbb"> </span>n<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span><span style="color: #008000"><array></span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>alias
<span style="color: #bbbbbb"> </span>dist<span style="color: #bbbbbb"> </span><span style="color: #008000">unzip</span><span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">sum</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi</span><span style="color: #bbbbbb"> </span><span style="color: #008000">/</span><span style="color: #bbbbbb"> </span>v*n<span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">items</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">probs</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>probs<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000"><</span><span style="color: #bbbbbb"> </span>small<span style="color: #bbbbbb"> </span>large<span style="color: #bbbbbb"> </span><span style="color: #008000">?</span><span style="color: #bbbbbb"> </span><span style="color: #008000">push</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">each-index</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>small<span style="color: #bbbbbb"> </span><span style="color: #008000">empty?</span><span style="color: #bbbbbb"> </span><span style="color: #008000">not</span><span style="color: #bbbbbb"> </span>large<span style="color: #bbbbbb"> </span><span style="color: #008000">empty?</span><span style="color: #bbbbbb"> </span><span style="color: #008000">not</span><span style="color: #bbbbbb"> </span><span style="color: #008000">and</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>small<span style="color: #bbbbbb"> </span><span style="color: #008000">pop</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>s
<span style="color: #bbbbbb"> </span>large<span style="color: #bbbbbb"> </span><span style="color: #008000">pop</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>l
<span style="color: #bbbbbb"> </span>l<span style="color: #bbbbbb"> </span>s<span style="color: #bbbbbb"> </span>alias<span style="color: #bbbbbb"> </span><span style="color: #008000">set-nth</span>
<span style="color: #bbbbbb"> </span>l<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>probs<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>s<span style="color: #bbbbbb"> </span>probs<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">change-nth</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000"><</span><span style="color: #bbbbbb"> </span>small<span style="color: #bbbbbb"> </span>large<span style="color: #bbbbbb"> </span><span style="color: #008000">?</span><span style="color: #bbbbbb"> </span><span style="color: #008000">push</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">while</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span>large<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>probs<span style="color: #bbbbbb"> </span><span style="color: #008000">set-nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">with</span><span style="color: #bbbbbb"> </span><span style="color: #008000">each</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span>small<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>probs<span style="color: #bbbbbb"> </span><span style="color: #008000">set-nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">with</span><span style="color: #bbbbbb"> </span><span style="color: #008000">each</span>
<span style="color: #bbbbbb"> </span>n<span style="color: #bbbbbb"> </span>items<span style="color: #bbbbbb"> </span>probs<span style="color: #bbbbbb"> </span>alias<span style="color: #bbbbbb"> </span>vose<span style="color: #bbbbbb"> </span><span style="color: #008000">boa</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
</pre></blockquote>
<p>We can implement the <code>random*</code> generic to select a random item from the <code>vose</code> tuple — choosing a random item index, check it's probability against a random number between 0.0 and 1.0, and if it is over a threshold we return the aliased item instead:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">M::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">vose</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">random*</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">obj</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">rnd</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">elt</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>obj<span style="color: #bbbbbb"> </span>n>><span style="color: #bbbbbb"> </span>rnd<span style="color: #bbbbbb"> </span>random*
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>obj<span style="color: #bbbbbb"> </span>probs>><span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>rnd<span style="color: #bbbbbb"> </span>(random-unit)<span style="color: #bbbbbb"> </span><span style="color: #008000">>=</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>obj<span style="color: #bbbbbb"> </span>alias>><span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">unless</span>
<span style="color: #bbbbbb"> </span>obj<span style="color: #bbbbbb"> </span>items>><span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>It's much faster, over <b>14.4 million</b> per second:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">10,000,000 </span>dist<span style="color: #bbbbbb"> </span><vose><span style="color: #bbbbbb"> </span>randoms<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>time<span style="color: #bbbbbb"> </span>
Running<span style="color: #bbbbbb"> </span>time:<span style="color: #bbbbbb"> </span><span style="color: #666666">0.693588458 </span>seconds</pre></blockquote>
<p>This is available now in the <a href="https://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a> vocabulary in the current development version, along with a few tweaks that brings the performance over <b>21.7 million</b> per second...</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-78569972367770935372023-02-15T09:18:00.000-08:002023-02-15T09:18:04.592-08:00DuckDuckGo<p>The conversation around the current quality of web search engines, the doomsday prediction about various incumbents, and the <a href="https://dkb.blog/p/bing-ai-cant-be-trusted">equal parts inspiring and challenging rollout</a> of large language models to improve search has been fascinating to watch. There are many challengers in the search engine space including companies like <a href="https://kagi.com">Kagi</a> and <a href="https://neeva.com">Neeva</a> among many <a href="https://www.crunchbase.com/hub/search-engine-startups">search engine startups</a>. One privacy-focused startup that has been fun to follow for awhile has been <a href="https://duckduckgo.com">DuckDuckGo</a>.</p>
<p>You can see an <a href="http://api.duckduckgo.com/?q=DuckDuckGo&format=json&pretty=1">example of the DuckDuckGo API</a> that is available on <code>api.duckduckgo.com</code>. This does not provide access to their full search results, but instead provides access to their instant answers. Regardless, I thought it would be neat if we could use this from <a href="https://factorcode.org">Factor</a>.</p>
<p>We can take a search query and turn it into a <a href="https://docs.factorcode.org/content/article-urls.html">URL object</a>:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">duckduckgo-url</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">query</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">url</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">URL"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">http://api.duckduckgo.com"</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"q"</span><span style="color: #bbbbbb"> </span>set-query-param
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"json"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"format"</span><span style="color: #bbbbbb"> </span>set-query-param
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"1"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"pretty"</span><span style="color: #bbbbbb"> </span>set-query-param
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"1"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"no_redirect"</span><span style="color: #bbbbbb"> </span>set-query-param
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"1"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"no_html"</span><span style="color: #bbbbbb"> </span>set-query-param
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"1"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"skip_disambig"</span><span style="color: #bbbbbb"> </span>set-query-param<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Using the <a href="https://docs.factorcode.org/content/article-http.client.html">http.client</a> vocabulary and the <a href="https://docs.factorcode.org/content/article-json.html">json</a> vocabulary we can retrieve a result set:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">duckduckgo</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">query</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">results</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>duckduckgo-url<span style="color: #bbbbbb"> </span>http-get<span style="color: #bbbbbb"> </span><span style="color: #008000">nip</span><span style="color: #bbbbbb"> </span>utf8<span style="color: #bbbbbb"> </span>decode<span style="color: #bbbbbb"> </span>json><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can make a word that prints out the <i>abstract</i> response with clickable links:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">abstract.</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">results</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Heading"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"AbstractURL"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span>>url<span style="color: #bbbbbb"> </span>write-object<span style="color: #bbbbbb"> </span><span style="color: #008000">nl</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"AbstractText"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span><span style="color: #008000">print</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"AbstractSource"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"- "</span><span style="color: #bbbbbb"> </span><span style="color: #008000">write</span><span style="color: #bbbbbb"> </span><span style="color: #008000">print</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">cleave</span><span style="color: #bbbbbb"> </span><span style="color: #008000">nl</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if-empty</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>And then a word that prints out a <i>result</i> response, parsing the <a href="https://en.wikipedia.org/wiki/HTML">HTML</a> using the <a href="https://docs.factorcode.org/content/vocab-html.parser.html">html.parser</a> vocabulary and output as text using the <a href="https://docs.factorcode.org/content/vocab-html.parser.printer.html">html.parser.printer</a> vocabulary:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">result.</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">result</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Result"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"<a href=\""</span><span style="color: #bbbbbb"> </span>?head<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"\">"</span><span style="color: #bbbbbb"> </span>split1<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"</a>"</span><span style="color: #bbbbbb"> </span>split1
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>>url<span style="color: #bbbbbb"> </span>write-object<span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>parse-html<span style="color: #bbbbbb"> </span>html-text.<span style="color: #bbbbbb"> </span><span style="color: #008000">nl</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi*</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">when*</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>There are more aspects to the response from the API, but we can initially print out the abstract, the results, and the related topics:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">duckduckgo.</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">query</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>duckduckgo<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>abstract.<span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Results"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>result.<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">each</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"RelatedTopics"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">of</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>result.<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">each</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">cleave</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can try it out on a topic that this particular blog likes to discuss:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"factorcode"</span><span style="color: #bbbbbb"> </span>duckduckgo.
Factor (programming language)
Factor is a stack-oriented programming language created by Slava
Pestov. Factor is dynamically typed and has automatic memory
management, as well as powerful metaprogramming features. The
language has a single implementation featuring a self-hosted
optimizing compiler and an interactive development environment.
The Factor distribution includes a large standard library.
- Wikipedia
Official site - Factor (programming language)
Concatenative programming languages
Stack-oriented programming languages
Extensible syntax programming languages
Function-level languages
High-level programming languages
Programming languages
Software using the BSD license</pre></blockquote>
<p>This is available on my <a href="https://github.com/mrjbq7/re-factor/blob/master/duckduckgo/duckduckgo.factor">GitHub</a>.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-1266277485748230502023-02-12T16:42:00.011-08:002023-02-15T15:32:46.419-08:00Magic<p>Ever wonder what the type of a particular binary file is? Or wonder how a program knows that a particular binary file is in a compatible <a href="https://en.wikipedia.org/wiki/File_format">file format</a>? One way is to look at the <a href="https://en.wikipedia.org/wiki/Magic_number_(programming)">magic number</a> used by the file format in question. You can see some examples in a <a href="https://en.wikipedia.org/wiki/List_of_file_signatures">list of file signatures</a>.</p>
<p>The <a href="http://www.darwinsys.com/file/">libmagic</a> library commonly supports the <a href="http://linux.die.net/man/1/file">file</a> command on Unix systems, other than <a href="https://www.apple.com/macos/ventura/">Apple macOS</a> which has its own implementation, and uses magic numbers and other techniques to identify file types. You can see how it works through a few examples:</p>
<blockquote><pre>$ file vm/factor.hpp
vm/factor.hpp: C++ source text, ASCII text
$ file Factor.app/Contents/Info.plist
Factor.app/Contents/Info.plist: XML document text
$ file factor
factor: Mach-O 64-bit executable x86_64
$ file factor.image
factor.image: data</pre></blockquote>
<h3>Wrapping the C library</h3>
<p>I am going to show how to wrap a C library using the <a href="https://docs.factorcode.org/content/article-alien.html">alien vocabulary</a> which provides an <a href="https://en.wikipedia.org/wiki/Foreign_function_interface">FFI capability</a> in <a href="https://factorcode.org">Factor</a>. The <a href="https://manpages.ubuntu.com/manpages/trusty/man3/libmagic.3.html">man pages for libmagic</a> show us some of the functions available in <code>magic.h</code>.</p>
<p>The <code>libmagic</code> library needs to be made available to the Factor instance:</p>
<blockquote><pre><span style="color: #BA2121">"magic"</span><span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>os<span style="color: #bbbbbb"> </span>macosx?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"libmagic.dylib"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>os<span style="color: #bbbbbb"> </span>unix?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"libmagic.so"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
}<span style="color: #bbbbbb"> </span><span style="color: #008000">cond</span><span style="color: #bbbbbb"> </span>cdecl<span style="color: #bbbbbb"> </span>add-library<span style="color: #bbbbbb"> </span></pre></blockquote>
<p>We start by defining an opaque type for <code>magic_t</code>:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">TYPEDEF:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">void*</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">magic_t</span></pre></blockquote>
<p>Some functions are available for opening, loading, and then closing the <code>magic_t</code>:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">FUNCTION:</span><span style="color: #bbbbbb"> </span>magic_t<span style="color: #bbbbbb"> </span><span style="color: #0000FF">magic_open</span><span style="color: #bbbbbb"> </span>(<span style="color: #bbbbbb"> </span>int flags )
<span style="color: #008000; font-weight: bold">FUNCTION:</span><span style="color: #bbbbbb"> </span>int<span style="color: #bbbbbb"> </span><span style="color: #0000FF">magic_load</span><span style="color: #bbbbbb"> </span>(<span style="color: #bbbbbb"> </span>magic_t magic, c-string path )
<span style="color: #008000; font-weight: bold">FUNCTION:</span><span style="color: #bbbbbb"> </span>void<span style="color: #bbbbbb"> </span><span style="color: #0000FF">magic_close</span><span style="color: #bbbbbb"> </span>(<span style="color: #bbbbbb"> </span>magic_t magic )</pre></blockquote>
<p>It is convenient to wrap the close function as a <a href="https://docs.factorcode.org/content/word-DESTRUCTOR__colon__%2Calien.destructors.html">destructor</a> for use in a <a href="https://docs.factorcode.org/content/word-with-destructors,destructors.html">with-destructors</a> form.</p>
<blockquote><pre>DESTRUCTOR:<span style="color: #bbbbbb"> </span>magic_close</pre></blockquote>
<p>A function that <i>"returns a textual description of the contents of the filename argument"</i>, which gives us the <code>file</code> command ability above:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">FUNCTION:</span><span style="color: #bbbbbb"> </span>c-string<span style="color: #bbbbbb"> </span><span style="color: #0000FF">magic_file</span><span style="color: #bbbbbb"> </span>(<span style="color: #bbbbbb"> </span>magic_t magic, c-string path )</pre></blockquote>
<p>That should be everything we need to continue...</p>
<h3>Using the C library</h2>
<p>Now that we have the raw C library made available as Factor words, we can create a simpler interface by wrapping some of the words into a simple word that guesses the type of a file:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">guess-file</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">path</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">result</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>normalize-path
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>magic_open<span style="color: #bbbbbb"> </span>&magic_close
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>magic_load<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>magic_file<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>with-destructors<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>And we can then try it on a few files:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"vm/factor.hpp"</span><span style="color: #bbbbbb"> </span>guess-file<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"C++ source, ASCII text"</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Factor.app/Contents/Info.plist"</span><span style="color: #bbbbbb"> </span>guess-file<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"XML 1.0 document, Unicode text, UTF-8 text"</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"factor"</span><span style="color: #bbbbbb"> </span>guess-file<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"symbolic link to Factor.app/Contents/MacOS/factor"</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"factor.image"</span><span style="color: #bbbbbb"> </span>guess-file<span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #BA2121">"data"</span></pre></blockquote>
<p>This has been available for awhile in the <a href="https://docs.factorcode.org/content/vocab-magic.html">magic vocabulary</a> with improved error checking and some options to guess the <a href="https://en.wikipedia.org/wiki/Media_type">MIME type</a> of files.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-71235535587259742292023-02-08T08:38:00.001-08:002023-02-08T08:38:51.934-08:00Hipku<p>Once upon a time, there was a Javascript project called <a href="https://github.com/gabemart/hipku">Hipku</a>. The original post that described it was lost somewhere in the <a href="https://en.wikipedia.org/wiki/Series_of_tubes">series of tubes</a>, but thankfully the "full documentation and a working demo" was <a href="https://web.archive.org/web/20211204171141/https://gabrielbrady.com/projects/hipku/">saved by the Wayback Machine</a>. It is also still <a href="https://www.npmjs.com/package/hipku">available on npm</a> for installation.</p>
<blockquote><i>Hipku is a small javascript library to encode IP addresses as haiku. It can express any IPv4 or IPv6 address as a Western-style 5/7/5 syllable haiku.</i></blockquote>
<p>An implementation in <a href="https://python.org">Python</a> was created called <a href="https://github.com/lord63/pyhipku">PyHipku</a>. It is still <a href="https://pypi.org/project/pyhipku/">available on PyPi</a> for installation, but the website associated with it was also lost to history and not even the Great Wayback Machine seems able to recover it. I think of programming as aspiring to a kind of poetic result — and wonder what kind of language could run <a href="https://spot.colorado.edu/~sniderc/poetry/wakawaka.html">Waka Waka Bang Splat</a> — well, the <a href="https://en.wikipedia.org/wiki/Haiku">haiku</a> style caught my interest, so I ported the <i>hipku</i> algorithm to the <a href="https://factorcode.org">Factor programming language</a>.</p>
<p>At it's core, we encode an <a href="https://en.wikipedia.org/wiki/Internet_Protocol_version_4">IPv4 address</a> or <a href="https://en.wikipedia.org/wiki/IPv6">IPv6 address</a> into a series of numerical values and then make a poem by looking up each word from a word list. Some symbols are defined to help us know to start a sentence with an uppercase letter or end a sentence with a period:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">SYMBOLS:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">Octet</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">octet</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">octet.</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
</pre></blockquote>
<p>For example, an IPv4 key specifies the word lists to use for each octet and an IPv4 schema specify how the octets form into a <i>hipku</i> — an <code>f</code> indicates a newline:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">ipv4-key</span><span style="color: #bbbbbb"> </span>${
<span style="color: #bbbbbb"> </span>animal-adjectives<span style="color: #bbbbbb"> </span>animal-colors<span style="color: #bbbbbb"> </span>animal-nouns<span style="color: #bbbbbb"> </span>animal-verbs
<span style="color: #bbbbbb"> </span>nature-adjectives<span style="color: #bbbbbb"> </span>nature-nouns<span style="color: #bbbbbb"> </span>plant-nouns<span style="color: #bbbbbb"> </span>plant-verbs
}
<span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">ipv4-schema</span><span style="color: #bbbbbb"> </span>${
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"The"</span><span style="color: #bbbbbb"> </span>octet<span style="color: #bbbbbb"> </span>octet<span style="color: #bbbbbb"> </span>octet<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span>
<span style="color: #bbbbbb"> </span>octet<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"in the"</span><span style="color: #bbbbbb"> </span>octet<span style="color: #bbbbbb"> </span>octet.<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span>
<span style="color: #bbbbbb"> </span>Octet<span style="color: #bbbbbb"> </span>octet.
}</pre></blockquote>
<p>To create the <i>hipku</i>, we iterate across the key, choosing words numerically by looking up the octet value, and then composing them into the ordering specified by the schema.</p>
<p>You can see a couple examples below:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"127.0.0.1"</span><span style="color: #bbbbbb"> </span>>hipku<span style="color: #bbbbbb"> </span><span style="color: #008000">print</span>
The hungry white ape
aches in the ancient canyon.
Autumn colors crunch.
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"2001:db8:3333:4444:5555:6666:7777:8888"</span><span style="color: #bbbbbb"> </span>>hipku<span style="color: #bbbbbb"> </span><span style="color: #008000">print</span>
Chilled apes and blunt seas
clap dear firm firm grim grim gnomes.
Large holes grasp pained mares.</pre></blockquote>
<p>We support both encoding into a <i>hipku</i> as well as decoding back into an IPv4/IPv6 address. This is available as the <a href="https://github.com/factor/factor/blob/master/extra/hipku/hipku.factor">hipku vocabulary</a> in a recent nightly build.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-83806953937387045372023-02-07T09:56:00.002-08:002023-02-07T12:39:29.527-08:00Proquint<p>A few days ago, Ciprian Dorin Craciun wrote a <a href="https://notes.volution.ro/v1/2023/02/notes/0a9aaa3a/">binary to text encoding blog post</a> about the "state of the art and missed opportunities" in various encoding schemes. In that post, I was introduced to the <a href="https://arxiv.org/html/0901.4016">Proquint encoding</a> which stands for "<i>PRO-nouncable QUINT-uplets</i>".</p>
<p>In the <a href="https://factorcode.org">Factor programming language</a>, we have enjoyed implementing many encoding/decoding methods including: <a href="https://docs.factorcode.org/content/vocab-base16.html">base16</a>, <a href="https://docs.factorcode.org/content/vocab-base24.html">base24</a>, <a href="https://docs.factorcode.org/content/vocab-base32.html">base32</a>, <a href="https://docs.factorcode.org/content/vocab-base32hex.html">base32hex</a>, <a href="https://docs.factorcode.org/content/vocab-base32-crockford.html">base32-crockford</a>, <a href="https://docs.factorcode.org/content/vocab-base36.html">base36</a>, <a href="https://docs.factorcode.org/content/vocab-base58.html">base58</a>, <a href="https://docs.factorcode.org/content/vocab-base62.html">base62</a>, <a href="https://docs.factorcode.org/content/vocab-base64.html">base64</a>, <a href="https://docs.factorcode.org/content/vocab-base85.html">base85</a>, <a href="https://docs.factorcode.org/content/vocab-base91.html">base91</a>, <a href="https://docs.factorcode.org/content/vocab-uu.html">uu</a>, and many others. I thought it would be fun to add a quick implementation of Proquint.</p>
<p>Like other encodings, it makes use of an alphabet — grouped as consonants and vowels:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">consonant</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"bdfghjklmnprstvz"</span>
<span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">vowel</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"aiou"</span></pre></blockquote>
<p>Numbers are grouped into 5-character blocks representing a 16-bit number, with alternating consonants representing 4 bits and vowels representing 2 bits:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">>quint16</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">m</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">5 </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">even?</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">-4 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">4 </span>bits<span style="color: #bbbbbb"> </span>consonant<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">-2 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span>bits<span style="color: #bbbbbb"> </span>vowel<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #BA2121">""</span><span style="color: #bbbbbb"> </span>map-integers-as<span style="color: #bbbbbb"> </span><span style="color: #008000">reverse</span><span style="color: #bbbbbb"> </span><span style="color: #008000">nip</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Encoding a 32-bit number is made by joining two 16-bit blocks:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">>quint32</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">m</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">-16 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">keep</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">16 </span>bits<span style="color: #bbbbbb"> </span>>quint16<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi@</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"-"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">glue</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Decoding numbers looks up each consonant or vowel, skipping separators:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">quint></span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">m</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>$[<span style="color: #bbbbbb"> </span>consonant<span style="color: #bbbbbb"> </span>alphabet-inverse<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">nip</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">4 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi*</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>$[<span style="color: #bbbbbb"> </span>vowel<span style="color: #bbbbbb"> </span>alphabet-inverse<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #008000">nip</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #008000">shift</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi*</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">CHAR:</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">-</span><span style="color: #bbbbbb"> </span><span style="color: #008000">assert=</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if*</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if*</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">reduce</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can use this to make a random password that might be more memorable — but perhaps more secure if using more <a href="https://docs.factorcode.org/content/word-random-bits,random.html">random-bits</a>:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">quint-password</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">quint</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">32 </span>random-bits<span style="color: #bbbbbb"> </span>>quint32<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>And we could use our <a href="https://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser vocabulary</a> to make <a href="https://en.wikipedia.org/wiki/Internet_Protocol_version_4">IPv4 addresses</a> more memorable:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">ipv4>quint</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">ipv4</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>ipv4-aton<span style="color: #bbbbbb"> </span>>quint32<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">quint>ipv4</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">ipv4</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>quint><span style="color: #bbbbbb"> </span>ipv4-ntoa<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
</pre></blockquote>
<p>You can see how this might work by building a test suite to show roundtrips work:</p>
<blockquote><pre>{<span style="color: #bbbbbb"> </span><span style="color: #880000">t</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"127.0.0.1"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"lusab-babad"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"63.84.220.193"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"gutih-tugad"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"63.118.7.35"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"gutuk-bisog"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"140.98.193.141"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"mudof-sakat"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"64.255.6.200"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"haguz-biram"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"128.30.52.45"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"mabiv-gibot"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"147.67.119.2"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"natag-lisaf"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"212.58.253.68"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"tibup-zujah"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"216.35.68.215"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"tobog-higil"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"216.68.232.21"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"todah-vobij"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"198.81.129.136"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"sinid-makam"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"12.110.110.204"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"budov-kuras"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>quint>ipv4<span style="color: #bbbbbb"> </span><span style="color: #008000">=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>ipv4>quint<span style="color: #bbbbbb"> </span><span style="color: #008000">=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">2bi</span><span style="color: #bbbbbb"> </span><span style="color: #008000">and</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">assoc-all?</span>
]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<p>This is now available as the <a href="https://github.com/factor/factor/blob/master/extra/proquint/proquint.factor">proquint vocabulary</a> in a recent nightly build.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-91970239088084163392023-01-24T19:29:00.004-08:002023-01-24T19:35:36.570-08:00Semantic Versioning<p><a href="https://semver.org">Semantic Versioning</a> (or "semver" for short) is a specification for handling version numbers, and providing a way to sort and specify compatibility using a <code>MAJOR.MINOR.PATCH</code> structure with optional "pre-release" and "build" information.</p>
<p>Some examples of semantic version numbers:</p>
<ul>
<li>1.0.0-alpha</li>
<li>1.0.0-beta+win32</li>
<li>1.0.0-rc.1</li>
<li>1.0.0</li>
</ul>
<p>For a long time, I thought it might be funny to follow the upcoming release of <a href="https://factorcode.org">Factor</a> version <code>0.99</code> with version <code>0.100</code>. Well, if we wanted to be consistent with "semver", it might instead have to be something like <code>0.100.0-joke+haha</code>.</p>
<p>There is now a <a href="https://docs.factorcode.org/content/vocab-semver.html">semver vocabulary</a> that provides some words for sorting and working with semantic version numbers. Here's an example using it:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">USE:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">semver</span>
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"0.99.0"</span><span style="color: #bbbbbb"> </span>>semver<span style="color: #bbbbbb"> </span>bump-alpha<span style="color: #bbbbbb"> </span>semver.
0.99.1-alpha.0
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"0.99.0"</span><span style="color: #bbbbbb"> </span>>semver<span style="color: #bbbbbb"> </span>bump-preminor<span style="color: #bbbbbb"> </span>bump-rc<span style="color: #bbbbbb"> </span>semver.
0.100.0-rc.0
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"0.99.0"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"0.100.0"</span><span style="color: #bbbbbb"> </span>semver<=><span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
+lt+
<span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"0.100.0-joke+haha"</span><span style="color: #bbbbbb"> </span>>semver<span style="color: #bbbbbb"> </span>bump-major<span style="color: #bbbbbb"> </span>semver.
1.0.0</pre></blockquote>
<p>Reading the <a href="https://semver.org/spec/v2.0.0.html">Semantic Versioning 2.0.0</a> specification, it suggests using the version numbers to represent compatibility with previous versions. And many languages have package managers that use these compatibility guarantees with "semver ranges" to manage project dependencies.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-11080210162804558012023-01-21T11:51:00.024-08:002023-01-22T10:36:11.438-08:00Five Questions<p>Many years ago, there was a blog post containing <a href="https://blog.svpino.com/2015/05/07/five-programming-problems-every-software-engineer-should-be-able-to-solve-in-less-than-1-hour">five programming problems every software engineer should be able to solve in less than 1 hour</a>. I had bookmarked it at the time and didn't notice <a href="https://www.reddit.com/r/programming/comments/358tnp/five_programming_problems_every_software_engineer/">the controversy it created on Reddit</a>. The original link seems to be down — you can <a href="https://web.archive.org/web/20160816193027/http://www.shiftedup.com/2015/05/07/five-programming-problems-every-software-engineer-should-be-able-to-solve-in-less-than-1-hour">view a copy of it on the Wayback Machine</a> — but there are various solutions posted online, including a <a href="https://grison.me/2015/05/09/five-programming-problems-every-software-engineer-should-be-able-to-solve-in-less-than-1-hour/">solution in Python</a>.</p>
<p>I finally got around to looking at it and writing up some solutions to the
problems listed. Apparently, instead of solving this in 1 hour in <a href="https://factorcode.org">Factor</a>, it took me almost 8 years:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2015 05 09 </span><date><span style="color: #bbbbbb"> </span>days-since<span style="color: #bbbbbb"> </span><span style="color: #008000">>integer</span><span style="color: #bbbbbb"> </span><span style="color: #666666">.</span>
<span style="color: #666666">2814</span></pre></blockquote>
<h2>Problem 1</h2>
<p><i>Write three functions that compute the sum of the numbers in a given list using a for-loop, a while-loop, and recursion.</i></p>
<p>In idiomatic Factor, this is just <a href="https://docs.factorcode.org/content/word-sum,sequences.html">sum</a>, and we would typically use <a href="https://docs.factorcode.org/content/article-sequences-combinators.html">sequence combinators</a>, but instead here are a few solutions using <a href="https://docs.factorcode.org/content/article-locals.html">lexical variables</a>.</p>
<p>Using a for-loop, iterating forwards:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-1</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">n</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>seq<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>seq<span style="color: #bbbbbb"> </span>nth-unsafe<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">each-integer</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Using a while-loop, iterating forwards:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-1</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">n</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 0 </span>seq<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span>:><span style="color: #bbbbbb"> </span>n
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span>n<span style="color: #bbbbbb"> </span><span style="color: #008000"><</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>seq<span style="color: #bbbbbb"> </span>nth-unsafe<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">while</span><span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Using recursion, iterating backwards:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(solve-1)</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">accum</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">i</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">accum'</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>accum<span style="color: #bbbbbb"> </span>i<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span>seq<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>nth-unsafe<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">2keep</span><span style="color: #bbbbbb"> </span>(solve-1)
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">unless-zero</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-1</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">n</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">keep</span><span style="color: #bbbbbb"> </span>(solve-1)<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Some test cases to confirm behavior:</p>
<blockquote><pre>{<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>solve-1<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span>}<span style="color: #bbbbbb"> </span>solve-1<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span><span style="color: #666666">6 </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">1 2 3 </span>}<span style="color: #bbbbbb"> </span>solve-1<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<h2>Problem 2</h2>
<p><i>Write a function that combines two lists by alternatively taking elements. For example: given the two lists <code>[a, b, c]</code> and <code>[1, 2, 3]</code>, the function should return [a, 1, b, 2, c, 3].</i></p>
<p>We can alternately choose items from each list:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-2</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq1</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq2</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">newseq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">min-length</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">2keep</span><span style="color: #bbbbbb"> </span>'[
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">2/</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">even?</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi</span><span style="color: #bbbbbb"> </span>_<span style="color: #bbbbbb"> </span>_<span style="color: #bbbbbb"> </span><span style="color: #008000">?</span><span style="color: #bbbbbb"> </span>nth-unsafe
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>map-integers-as<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Some test cases to confirm behavior:</p>
<blockquote><pre>{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"a"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #BA2121">"b"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #BA2121">"c"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"a"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"b"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"c"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">1 2 3 </span>}<span style="color: #bbbbbb"> </span>solve-2
]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"a"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #BA2121">"b"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #BA2121">"c"</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"a"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"b"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"c"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"d"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">1 2 3 </span>}<span style="color: #bbbbbb"> </span>solve-2
]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<h2>Problem 3</h2>
<p><i>Write a function that computes the list of the first 100 Fibonacci numbers. By definition, the first two numbers in the Fibonacci sequence are 0 and 1, and each subsequent number is the sum of the previous two.</i></p>
<p>There are many approaches, including using <a href="https://docs.factorcode.org/content/article-memoize.html">memoization</a>, but instead we'll just iterate from the starting values and use <a href="https://docs.factorcode.org/content/word-replicate,sequences.html">replicate</a> to build up an output array.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-3</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">n</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0 1 </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">dip</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #008000">rot</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">keep</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">replicate</span><span style="color: #bbbbbb"> </span><span style="color: #008000">2nip</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Some test cases to confirm behavior:</p>
<blockquote><pre>{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>solve-3<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span>solve-3<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">0 1 </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span>solve-3<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">0 1 1 2 3 5 8 13 21 34 </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">10 </span>solve-3<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span><span style="color: #666666">573147844013817084100 </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">100 </span>solve-3<span style="color: #bbbbbb"> </span><span style="color: #008000">sum</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<h2>Problem 4</h2>
<p><i>Write a function that given a list of non negative integers, arranges them such that they form the largest possible number. For example, given <code>[50, 2, 1, 9]</code>, the largest formed number is <code>95021</code>.</i></p>
<p>We can try <a href="https://docs.factorcode.org/content/word-each-permutation,math.combinatorics.html">each-permutation</a> of the input numbers, looking for their largest numerical value when the digits are concatenated.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-4</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">n</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>number>string<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">map</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">concat</span><span style="color: #bbbbbb"> </span>string>number<span style="color: #bbbbbb"> </span>max<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>each-permutation<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Some test cases to confirm behavior:</p>
<blockquote><pre>{<span style="color: #bbbbbb"> </span><span style="color: #666666">95021 </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">50 2 1 9 </span>}<span style="color: #bbbbbb"> </span>solve-4<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test
{<span style="color: #bbbbbb"> </span><span style="color: #666666">5523 </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">52 5 3 </span>}<span style="color: #bbbbbb"> </span>solve-4<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<h2>Problem 5</h2>
<p><i>Write a program that outputs all possibilities to put <code>+</code> or <code>-</code> or nothing between the numbers 1, 2, ..., 9 (in this order) such that the result is always 100. For example: 1 + 2 + 34 – 5 + 67 – 8 + 9 = 100.</i></p>
<p>This one is more complicated than the previous ones, but we can build it up piece by piece, using a test-case on each step to show how it works.</p>
<p>First, we want a word to interleave numbers amongst operators using <code>solve-2</code>.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">insert-numbers</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">operators</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span>[1..b]<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>solve-2<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">length</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #008000">suffix</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">tri</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4 </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>insert-numbers<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<p>Next, we want a word that will join adjacent digits — separated by <code>f</code>:
</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">GENERIC:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">digits,</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">prev</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">intermediate</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">next</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #008000; font-weight: bold">M:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">number</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">digits,</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">10 </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">bi*</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">M:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">object</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">digits,</span><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0 </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">dip</span><span style="color: #bbbbbb"> </span>,<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">when*</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">join-digits</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>digits,<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">map-reduce</span><span style="color: #bbbbbb"> </span>,<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>make<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">12 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #666666">34 </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3 </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4 </span>}<span style="color: #bbbbbb"> </span>join-digits<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<p>Since Factor is a kind of <a href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">Reverse Polish
notation</a>, we'll want to swap from infix to postfix:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">swap-operators</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">seq</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #008000">rest-slice</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2 </span><groups><span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0 1 </span><span style="color: #008000">rot</span><span style="color: #bbbbbb"> </span><span style="color: #008000">exchange</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">each</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
{<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">12 34 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #666666">12 </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #666666">34 </span>}<span style="color: #bbbbbb"> </span>swap-operators<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>unit-test</pre></blockquote>
<p>The solution, then, is to use
<a href="https://docs.factorcode.org/content/word-all-selections,math.combinatorics.html">all-selections</a>
of addition, subtraction, and adjacency — interleaving the numbers, joining
adjacent digits, swapping operators, and then calling each sequence as a
quotation, filtering for the ones that return <code>100</code>:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-5</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">solutions</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000">+</span><span style="color: #bbbbbb"> </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #666666">8 </span>all-selections
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>insert-numbers<span style="color: #bbbbbb"> </span>join-digits<span style="color: #bbbbbb"> </span>swap-operators<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">map</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>>quotation<span style="color: #bbbbbb"> </span>call(<span style="color: #bbbbbb"> </span>--<span style="color: #bbbbbb"> </span>x<span style="color: #bbbbbb"> </span>)<span style="color: #bbbbbb"> </span><span style="color: #666666">100 </span><span style="color: #008000">=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">filter</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can print the formula out by swapping the operators back to infix and printing them out:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">print-formula</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">solutions</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>present<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">map</span><span style="color: #bbbbbb"> </span>swap-operators<span style="color: #bbbbbb"> </span><span style="color: #BA2121">" "</span><span style="color: #bbbbbb"> </span><span style="color: #008000">join</span><span style="color: #bbbbbb"> </span><span style="color: #008000">print</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">solve-5.</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>solve-5<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>print-formula<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">each</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Spoilers! The printed solutions:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span>solve-5.
1 + 2 + 3 - 4 + 5 + 6 + 78 + 9
1 + 2 + 34 - 5 + 67 - 8 + 9
1 + 23 - 4 + 5 + 6 + 78 - 9
1 + 23 - 4 + 56 + 7 + 8 + 9
12 + 3 + 4 + 5 - 6 - 7 + 89
12 + 3 - 4 + 5 + 67 + 8 + 9
12 - 3 - 4 + 5 - 6 + 7 + 89
123 + 4 - 5 + 67 - 89
123 + 45 - 67 + 8 - 9
123 - 4 - 5 - 6 - 7 + 8 - 9
123 - 45 - 67 + 89</pre></blockquote>
mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-25470434366534797182023-01-19T11:21:00.002-08:002023-01-19T12:00:32.276-08:00GetPercentageRounds<p>There was a <a href="https://twitter.com/jeroenfrijters/status/1615204074588180481">funny post on Twitter</a> a couple of days ago about a recent event where the <i>"Dutch government was forced to release the source code of their DigiD digital authentication iOS app"</i> with this piece of C# code:</p>
<blockquote><img src="https://i.imgur.com/u9XlVjU.png" width="425" height="504" /></blockquote>
<p>Some very funny discussions continued, with comments about how good or bad this code is, and how one might rewrite it in various ways. I thought it would be a fun opportunity to show a few variations of this simple function in <a href="https://factorcode.org">Factor</a>.</p>
<h2>Implementations</h2>
<p>A direct translation of this code, might use <a href="https://docs.factorcode.org/content/word-cond,combinators.html">cond</a> which is basically a sequence of <code>if</code> statements:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">get-percentage-rounds</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">percentage</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.0 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.0 0.1 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.1 0.2 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.2 0.3 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.3 0.4 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.4 0.5 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.5 0.6 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.6 0.7 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.7 0.8 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.8 0.9 </span>between?<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵"</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">cond</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>But since this is a series of <code>if</code> statements checked sequentially, you can just check the upper bounds. And since we only care about the argument for the comparison, we can use <a href="https://docs.factorcode.org/content/word-cond-case,combinators.extras.html">cond-case</a>:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">get-percentage-rounds</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">percentage</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.1 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.2 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.3 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.4 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.5 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.6 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.7 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.8 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">0.9 </span><span style="color: #008000"><=</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪"</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵"</span><span style="color: #bbbbbb"> </span>]
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>cond-case<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>One suggestion was to generate a substring based on the input — with the somewhat negative aspect that it allocates memory for the returned string when called:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">get-percentage-rounds</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">percentage</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">10 </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">10 </span><span style="color: #008000">swap</span><span style="color: #bbbbbb"> </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span><span style="color: #008000">>integer</span><span style="color: #bbbbbb"> </span><span style="color: #008000">dup</span><span style="color: #bbbbbb"> </span><span style="color: #666666">10 </span><span style="color: #008000">+</span>
<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"</span><span style="color: #bbbbbb"> </span><span style="color: #008000">subseq</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>But another way would be to just index into the possible results, using <a href="https://docs.factorcode.org/content/article-qw.html">quoted words</a> to reduce the amount of tokens involved — resulting in this fairly aesthetic result:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">get-percentage-rounds</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">(</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">percentage</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">--</span><span style="color: #bbbbbb"> </span><span style="color: #19177C">str</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">)</span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">10 </span><span style="color: #008000">*</span><span style="color: #bbbbbb"> </span>ceiling<span style="color: #bbbbbb"> </span><span style="color: #008000">>integer</span><span style="color: #bbbbbb"> </span>qw{
<span style="color: #bbbbbb"> </span>⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪
<span style="color: #bbbbbb"> </span>🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span><span style="color: #008000">nth</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>It's always fun to see different ways to solve problems. In the <a href="https://twitter.com/jeroenfrijters/status/1615204074588180481">Twitter thread</a>, that includes using binary search, building the output character-by-character, generating solutions using ChatGPT, one-liners in Python, pattern matching, unit testing, and discussions of edge cases and naming conventions.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-39746339896720188922023-01-16T10:38:00.003-08:002023-01-18T20:59:27.268-08:00Project Gemini<p><a href="https://gemini.circumlunar.space">Project Gemini</a> is a neat modern take on the <a href="https://en.wikipedia.org/wiki/Gopher_(protocol)">Gopher protocol</a>. You can read the <a href="https://gemini.circumlunar.space/docs/faq.gmi">Gemini FAQ</a> or the <a href="https://gemini.circumlunar.space/docs/specification.gmi">Gemini specification</a> to learn more details, but the home page has a nice summary:</p>
<blockquote><i><p>Gemini is a new internet protocol which</p>
<ul>
<li>Is heavier than gopher</li>
<li>Is lighter than the web</li>
<li>Will not replace either</li>
<li>Strives for maximum power to weight ratio</li>
<li>Takes user privacy very seriously</li>
</ul></i></blockquote>
<p>There are some nice <a href="https://gemini.circumlunar.space/clients.html">Gemini clients</a> implemented in various languages, for both the command-line and with nice user interfaces. I happen to enjoy using <a href="https://tildegit.org/solderpunk/AV-98">AV-98</a> and <a href="https://gmi.skyjake.fi/lagrange/">Lagrange</a>, but many others are also great.</p>
<p>In a similar manner to my <a href="https://re-factor.blogspot.com/2014/12/gopher.html">Gopher implementation</a> in <a href="https://factorcode.org">Factor</a>, I recently implemented the <a href="https://github.com/factor/factor/blob/master/extra/gemini/gemini.factor">Gemini protocol</a> as well as a <a href="https://github.com/factor/factor/blob/master/extra/gemini/server/server.factor">Gemini server</a> and a <a href="https://github.com/factor/factor/blob/master/extra/gemini/ui/ui.factor">Gemini user interface</a>:
<blockquote><img src="https://i.imgur.com/zIa46Qb.png" width="619" height="666" /></blockquote>
<p>Instead of going into how the protocol or the user interface is implemented, I wanted to go over the <a href="https://github.com/factor/factor/blob/master/extra/gemini/cli/cli.factor">Gemini command-line interface</a>. In the spirit of Python's <a href="https://docs.python.org/3/library/cmd.html">cmd module</a>, I contributed the <a href="https://docs.factorcode.org/content/vocab-command-loop.html">command-loop vocabulary</a> to support generic line-oriented command interpreters.</p>
<p>We start by making a sequence of commands that our Gemini interpreter will support:</p>
<blockquote><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">CONSTANT:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">COMMANDS</span><span style="color: #bbbbbb"> </span>{
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"back"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-back<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Go back to the previous Gemini URL."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"b"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"forward"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-forward<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Go forward to the next Gemini URL."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"f"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"history"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-history<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Display recently viewed Gemini URLs."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"h"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"hist"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"less"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-less<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"View the most recent Gemini URL in a pager."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"l"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"ls"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>gemini-ls<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"List the currently available links."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"go"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>gemini-go<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Go to a Gemini URL"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"g"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"gus"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"gemini://gus.guru/search"</span><span style="color: #bbbbbb"> </span>gemini-go<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Submit a query to the GUS search engine."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"up"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-up<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Go up one directory from the recent Gemini URL."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"u"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"url"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-url<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Print the most recent Gemini URL."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"reload"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-reload<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Reload the most recent Gemini URL."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"r"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"root"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-root<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Navigate to the most recent Gemini URL's root."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"shell"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span>gemini-shell<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"'cat' the most recent Gemini URL through a shell."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"!"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>T{<span style="color: #bbbbbb"> </span>command
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>name<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"quit"</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>quot<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #008000">drop</span><span style="color: #bbbbbb"> </span>gemini-quit<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>help<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"Quit the program."</span><span style="color: #bbbbbb"> </span>}
<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>abbrevs<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #BA2121">"q"</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"exit"</span><span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>}
}</pre></blockquote>
<p>And then we define a custom <a href="https://docs.factorcode.org/content/word-command-loop,command-loop.html">command-loop</a> that will allow us to number the links on a Gemini page, and then by typing a number we can navigate to one of the links by detecting a "missing command":</p>
<blockquote><pre><span></span><span style="color: #008000; font-weight: bold">TUPLE:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">gemini-command-loop</span><span style="color: #bbbbbb"> </span><<span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">command-loop</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">M:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">gemini-command-loop</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF">missing-command</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">over</span><span style="color: #bbbbbb"> </span>string>number<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #666666">1 </span><span style="color: #008000">-</span><span style="color: #bbbbbb"> </span>LINKS<span style="color: #bbbbbb"> </span><span style="color: #008000">?nth</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[<span style="color: #bbbbbb"> </span><span style="color: #880000">f</span><span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if*</span><span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>gemini-go<span style="color: #bbbbbb"> </span><span style="color: #008000">3drop</span>
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span>[
<span style="color: #bbbbbb"> </span>call-next-method
<span style="color: #bbbbbb"> </span>]<span style="color: #bbbbbb"> </span><span style="color: #008000">if*</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">;</span>
</pre></blockquote>
<p>You can see it in action:</p>
<blockquote><pre>$ ./factor -run=gemini.cli
Welcome to Project Gemini!
GEMINI> go gemini.circumlunar.space/news/
Official Project Gemini news feed
[1] Atom feed
2023 News
[2] 2023-01-14 - Tidying up gemini.circumlunar.space user capsules
[3] 2023-01-08 - Changing DNS server
2022 News
[4] 2022-06-20 - Three years of Gemini!
[5] 2022-01-30 - Minor specification update (0.16.1)
[6] 2022-01-22 - Mailing list archives, Atom feed for official news
[7] 2022-01-16 - Mailing list downtime, official news feed
</pre></blockquote>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-3291249608988981902023-01-15T19:28:00.002-08:002023-01-15T20:00:56.355-08:00Guided Tour of Factor<p>Many years ago, <a href="https://github.com/andreaferretti">Andrea Ferretti</a> created a <a href="https://andreaferretti.github.io/factor-tutorial/">Factor tutorial</a> which I had <a href="https://re-factor.blogspot.com/2014/11/factor-tutorial.html">written about at the time</a>.</p>
<p>One of our new core developers, <a href="https://github.com/razetime">Raghu Ranganathan</a>, got permission to include the tutorial in the main <a href="https://factorcode.org">Factor</a> repository, and has reformatted it and updated it for the not-yet-released version 0.99 as the <a href="https://docs.factorcode.org/content/article-tour.html">Guided tour of Factor</a>.</p>
<p>You can access it in a recent nightly build by doing:</p>
<blockquote><pre><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">IN:</span><span style="color: #bbbbbb"> </span><span style="color: #0000FF; font-weight: bold">scratchpad</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">"tour"</span><span style="color: #bbbbbb"> </span>help</pre></blockquote>
<p>Check it out!</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-85795547833125651862022-02-04T08:10:00.002-08:002022-02-04T15:21:18.874-08:00Speedrun Feedback<p>Recently, <a href="http://t-a-w.blogspot.com">Tomasz Wegrzanowski</a> chose <a href="https://factorcode.org">Factor</a> for his <a href="https://dev.to/taw/100-languages-speedrun-episode-71-factor-3bb2">100 Languages Speedrun: Episode 71: Factor</a> and it encouraged us to make some improvements that I wanted to describe.</p>
<p>Many of our users use the Factor environment through the <a href="https://docs.factorcode.org/content/article-ui-tools.html">UI developer tools</a> or on the command-line with <a href="https://docs.factorcode.org/content/article-listener.html">the listener</a>. Another important use case is being able to <code>eval</code> and run scripts -- and this is where much of Tomasz' criticism was focused.</p>
<p>We now do command-line eval and run scripts with <a href="https://docs.factorcode.org/content/word-auto-use__que__%2Cparser.html">auto-use?</a> enabled. This will be available in the nightly builds and as part of an upcoming 0.99 release.</p>
<p>So this works now:</p>
<blockquote><pre>$ ./factor -e="1 2 + ."
3
$ cat foo.factor
USE: io
"Hello World" print
12
$ ./factor foo.factor
Hello World
--- Data stack:
12</pre></blockquote>
<p>Previously, the first example would error with a "No word named “+” found in current vocabulary search path" and the second example would complain that the "Quotation's stack effect does not match call site" because the script did not have a <code>( -- )</code> stack effect.</p>
<p>I appreciate that some users approach <a href="https://factorcode.org">Factor</a> differently than I do, and we love getting feedback. I wish we could solve the name conflict with <a href="https://man7.org/linux/man-pages/man1/factor.1.html">factor(1)</a>, but that is more challenging.</p>
<p>We may adjust this slightly as it just landed last night, and if anyone has further suggestions, please keep them coming!</p>
mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-86801128675933219022018-07-31T10:09:00.002-07:002018-08-02T19:48:51.510-07:00Factor 0.98 now available<i>“Even though you're growing up, you should never stop having fun.” - Nina Dobrev</i>
<p>I'm very pleased to announce the release of <a href="http://factorcode.org">Factor</a> 0.98!</p>
<style>
a.release {
color:#222;
border-bottom:1px solid #aaa;
text-decoration:none;
}
a.release:hover {
border-bottom:1px solid #ccc;
}
th.bg {
font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
padding: 6px 6px 6px 6px;
background: #CAE8EA url(bg_header.jpg) no-repeat;
}
th.nobg {
border-top: 0;
border-left: 0;
border-right: 1px solid #C1DAD7;
background: none;
}
td.alt {
background: #F5FAFA;
color: #797268;
}
td.doesnotexist {
background: #E5EAEA;
}
td.unsupported {
background: #ffaaaa;
}
td.supported {
background: #aaffaa;
}
td.supported :hover { background-color: #88ff88; }
div.bigdiv {
width: 100px;
text-align: center;
color: #050;
}
th.spec {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #fff;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
}
th.specalt {
border-left: 1px solid #C1DAD7;
border-top: 0;
background: #f5fafa;
font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #797268;
}
th.allbg {
border-top: 0;
border-left: 0;
border-right: 0;
background: none;
}
</style>
<blockquote>
<table class="downloads" cellspacing="0">
<tr><th class="nobg bg">OS/CPU</th><th class="bg" align="center" scope="col">Windows</th><th class="bg" align="center" scope="col">Mac OS X</th><th class="bg" align="center" scope="col">Linux</th></tr>
<tr><th class="bg" align="center" scope="row">x86</th><td class="supported"><div class="bigdiv"><a class="release" href="http://builds.factorcode.org/release?os=windows&cpu=x86.32">0.98</a></div></td><td class="supported"><div class="bigdiv"><a class="release" href="http://builds.factorcode.org/release?os=macosx&cpu=x86.32">0.98</a></div></td><td class="supported"><div class="bigdiv"><a class="release" href="http://builds.factorcode.org/release?os=linux&cpu=x86.32">0.98</a></div></td></tr><tr><th class="bg" align="center" scope="row">x86-64</th><td class="supported"><div class="bigdiv"><a class="release" href="http://builds.factorcode.org/release?os=windows&cpu=x86.64">0.98</a></div></td><td class="supported"><div class="bigdiv"><a class="release" href="http://builds.factorcode.org/release?os=macosx&cpu=x86.64">0.98</a></div></td><td class="supported"><div class="bigdiv"><a class="release" href="http://builds.factorcode.org/release?os=linux&cpu=x86.64">0.98</a></div></td></tr>
</table>
<p><b>Source code</b>:
<a class="release" href="http://downloads.factorcode.org/releases/0.98/factor-src-0.98.zip">0.98</a>
</p>
</blockquote>
<p>This release is brought to you with almost 4,300 commits by the following individuals:</p>
<blockquote>Alexander Ilin, Arkady Rost, Benjamin Pollack, Björn Lindqvist, Cat Stevens, Chris Double, Dimage Sapelkin, Doug Coleman, Friedrich von Never, John Benediktsson, Jon Harper, Mark Green, Mark Sweeney, Nicolas Pénet, Philip Dexter, Robert Vollmert, Samuel Tardieu, Sankaranarayanan Viswanathan, Shane Pelletier, <a href="https://github.com/catb0t">@catb0t</a>, <a href="https://github.com/hyphz">@hyphz</a>, <a href="https://github.com/thron7">@thron7</a>, <a href="https://github.com/xzenf">@xzenf</a></blockquote>
<p>Besides several years of bug fixes and library improvements, I want to highlight the
following changes:</p>
<ul>
<li>Improved user interface with light and dark themes and new icons</li>
<li>Fix GTK library issue affecting some Linux installations</li>
<li>Support Cocoa TouchBar on MacOS</li>
<li>Factor REPL version banner includes build information.</li>
<li>Bindings for <a href="https://github.com/couchbase/forestdb">ForestDB</a></li>
<li>New graphical demos including Minesweeper, Game of Life, Bubble Chamber, etc.</li>
<li>Better handling of "out of memory" errors</li>
<li>Improved VM and compiler documentation, test fixtures, and bug fixes</li>
<li>Much faster Heaps and Heapsort</li>
<li>Support for Adobe Brackets, CotEditor, and Microsoft Visual Studio Code editors</li>
<li>On Mac OS X, allow use of symlinks to factor binary</li>
<li>Lots of improvements to FUEL (Factor's emacs mode)</li>
</ul>
<p>Some possible backwards compatibility issues:</p>
<ul>
<li>Flattened <code>unicode</code> namespace (either <code>USE: ascii</code> or <code>USE: unicode</code>).</li>
<li>Unified <code>CONSTRUCTOR:</code> syntax to include generated word name</li>
<li>Returning a struct by value with two register-sized values on 64bit now works correctly</li>
<li>Since shutdown hooks run first, calling exit will now unconditionally exit even if there is an error</li>
<li>On Windows, don't call <code>cd</code> to change directory when launching processes; there is another mechanism for that</li>
<li>In <code>libc</code>, renamed <code>(io-error)</code> to <code>throw-errno</code></li>
<li>In <code>match</code>, renamed <code>?1-tail</code> to <code>?rest</code></li>
<li>In <code>sequences</code>, renamed <code>iota</code> to <code><iota></code></li>
<li>In <code>sequences</code>, renamed <code>start</code>/<code>start*</code> to <code>subseq-start</code>/<code>subseq-start-from</code></li>
<li>In <code>syntax</code>, renamed <code>GENERIC#</code> to <code>GENERIC#:</code></li>
<li>Improve command-line argument parsing of "executable"</li>
<li>Make <code>buffered-port</code> not have a length, because of problem with Linux virtual files and TCP sockets</li>
<li>Fix broken optimization that made floats work for integer keys in <code>case</code> statements</li>
<li>Growable sequences expand by factor of 2 (instead of 3) when growing</li>
<li>Removed support for "triple-quote" strings</li>
</ul>
<br />
<h2>What is Factor</h2>
<p>Factor is a <a href="http://www.concatenative.org/">concatenative</a>, stack-based programming language with <a href="http://concatenative.org/wiki/view/Factor/Features/The%20language">high-level features</a> including dynamic types, extensible syntax, macros, and garbage collection. On a practical side, Factor has a <a href="http://docs.factorcode.org/content/article-vocab-index.html">full-featured library</a>, supports many different platforms, and has been extensively documented.</p>
<p>The implementation is <a href="http://concatenative.org/wiki/view/Factor/Optimizing%20compiler">fully compiled</a> for performance, while still supporting <a href="http://concatenative.org/wiki/view/Factor/Interactive%20development">interactive development</a>. Factor applications are portable between all common platforms. Factor can <a href="http://concatenative.org/wiki/view/Factor/Deployment">deploy stand-alone applications</a> on all platforms. Full source code for the Factor project is available under a BSD license.</p>
<br />
<h2>New Libraries:</h2>
<ul>
<li><a href="http://docs.factorcode.org/content/vocab-backticks.html">backticks</a>: process <a href="https://re-factor.blogspot.com/2016/07/backticks.html">backtick syntax</a> for processes</li>
<li><a href="http://docs.factorcode.org/content/vocab-bencode.html">bencode</a>: support for <a href="https://en.wikipedia.org/wiki/Bencode">bencoding</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-boolean-expr.html">boolean-expr</a>: simple boolean expression evaluator and simplifier</li>
<li><a href="http://docs.factorcode.org/content/vocab-bubble-chamber.html">bubble-chamber</a>: variation of Jared Tarbell's <a href="http://complexification.net/gallery/machines/bubblechamber/">Bubble Chamber</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-calendar.elapsed.html">calendar.elapsed</a>: rendering elapsed time to text</li>
<li><a href="http://docs.factorcode.org/content/vocab-calendar.english.html">calendar.english</a>: separated out English localization</li>
<li><a href="http://docs.factorcode.org/content/vocab-checksums.crc16.html">checksums.crc16</a>: CRC16 checksum implementation</li>
<li><a href="http://docs.factorcode.org/content/vocab-checksums.metrohash.html">checksums.metrohash</a>: MetroHash checksum implementation</li>
<li><a href="http://docs.factorcode.org/content/vocab-checksums.multi.html">checksums.multi</a>: support multiple checksums in one pass</li>
<li><a href="http://docs.factorcode.org/content/vocab-checksums.process.html">checksums.process</a>: support for using command-line checksums</li>
<li><a href="http://docs.factorcode.org/content/vocab-checksums.ripemd.html">checksums.ripemd</a>: RIPEMD checksum implementation</li>
<li><a href="http://docs.factorcode.org/content/vocab-checksums.sodium.html">checksums.sodium</a>: <a href="https://download.libsodium.org/doc/">Sodium crypto library</a> checksum implementation</li>
<li><a href="http://docs.factorcode.org/content/vocab-cocoa.touchbar.html">cocoa.touchbar</a>: support for the Apple Touchbar</li>
<li><a href="http://docs.factorcode.org/content/vocab-colors.flex-hex.html">colors.flex-hex</a>: implement "flex hex" color algorithm</li>
<li><a href="http://docs.factorcode.org/content/vocab-crontab.html">crontab</a>: parsing the <a href="http://www.nncron.ru/help/EN/working/cron-format.htm">cron format</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-cuckoo-filters.html">cuckoo-filters</a>: implementation of <a href="https://re-factor.blogspot.com/2016/08/cuckoo-filters.html">Cuckoo Filter data structure</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-dbf.html">dbf</a>: parsers for <a href="https://en.wikipedia.org/wiki/DBase#File_formats">DBase file format</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-editors.brackets.html">editors.brackets</a>: support for Adobe Brackets</li>
<li><a href="http://docs.factorcode.org/content/vocab-editors.cot.html">editors.cot</a>: support for CotEditor</li>
<li><a href="http://docs.factorcode.org/content/vocab-editors.visual-studio-code.html">editors.visual-studio-code</a>: support for Microsoft Visual Studio Code</li>
<li><a href="http://docs.factorcode.org/content/vocab-emojify.html">emojify</a>: add emoji's to text (<code>"I :heart: Factor :+1:"</code>)</li>
<li><a href="http://docs.factorcode.org/content/vocab-english.html">english</a>: tools for English language words</li>
<li><a href="http://docs.factorcode.org/content/vocab-enigma.html">enigma</a>: implementation of Enigma cipher machine</li>
<li><a href="http://docs.factorcode.org/content/vocab-escape-strings.html">escape-strings</a>: minimal string escaping algorithm</li>
<li><a href="http://docs.factorcode.org/content/vocab-etc-hosts.html">etc-hosts</a>: cross-platform <code>/etc/hosts</code> file parser</li>
<li><a href="http://docs.factorcode.org/content/vocab-file-monitor.html">file-monitor</a>: cross-platform <a href="https://re-factor.blogspot.com/2015/05/file-monitor.html">file change event monitor</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-file-picker.html">file-picker</a>: cross-platform file picker user interface</li>
<li><a href="http://docs.factorcode.org/content/vocab-file-server.html">file-server</a>: cross-platform <a href="https://re-factor.blogspot.com/2015/05/file-server.html">file server web interface</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-flip-text.html">flip-text</a>: fun with "uʍop ǝpᴉsdn" text flipping </li>
<li><a href="http://docs.factorcode.org/content/vocab-forestdb.html">forestdb</a>: bindings for <a href="https://github.com/couchbase/forestdb">ForestDB</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-game-of-life.html">game-of-life</a>: implementation of <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">John Conway's Game of Life</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-google.gmail.html">google.gmail</a>: adding Google GMail API support</li>
<li><a href="http://docs.factorcode.org/content/vocab-gopher.html">gopher</a>: library for querying <a href="https://re-factor.blogspot.com/2014/12/gopher.html">Gopher servers</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-gopher.server.html">gopher.server</a>: cross-platform <a href="https://re-factor.blogspot.com/2016/10/gopher-server.html">Gopher file server</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-gopher.ui.html">gopher.ui</a>: interface for viewing Gopher servers</li>
<li><a href="http://docs.factorcode.org/content/vocab-ifaddrs.html">ifaddrs</a>: list network interfaces</li>
<li><a href="http://docs.factorcode.org/content/vocab-ip-parser.html">ip-parser</a>: parsing IPv4 and IPv6 addresses</li>
<li><a href="http://docs.factorcode.org/content/vocab-ldcache.html">ldcache</a>: parsing <code>/etc/ld.so.cache</code> file</li>
<li><a href="http://docs.factorcode.org/content/vocab-libtls.html">libtls</a>: wrapper for using <code>libtls</code> functions</li>
<li><a href="http://docs.factorcode.org/content/vocab-linked-sets.html">linked-sets</a>: sets that yield items in insertion order</li>
<li><a href="http://docs.factorcode.org/content/vocab-lru-cache.html">lru-cache</a>: <a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)">least recently used</a> cache algorithm</li>
<li><a href="http://docs.factorcode.org/content/vocab-machine-learning.html">machine-learning</a>: experiments with some machine learning algorithms</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.factorials.html">math.factorials</a>: adding <a href="https://re-factor.blogspot.com/2016/11/reverse-factorial.html">reverse-factorial</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-math.functions.integer-logs.html">math.functions.integer-logs</a>: support for integer log2 and log10</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.primes.erato.fast.html">math.primes.erato.fast</a>: faster <a href="https://re-factor.blogspot.com/2015/06/genuine-sieve-of-eratosthenes.html">Sieve of Eratosthenes</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-metar.html">metar</a>: parsers for METAR and TAF weather reports</li>
<li><a href="http://docs.factorcode.org/content/vocab-midi.html">midi</a>: <a href="https://re-factor.blogspot.com/2015/04/reading-midi-files.html">reading MIDI files</a> and <a href="https://re-factor.blogspot.com/2015/04/writing-midi-files.html">writing MIDI files</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-minesweeper.html">minesweeper</a>: playing the <a href="https://re-factor.blogspot.com/2018/02/minesweeper.html">classic game of Minesweeper</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-named-tuples.html">named-tuples</a>: using tuples as a sequence and assoc</li>
<li><a href="http://docs.factorcode.org/content/vocab-oauth1.html">oauth1</a>: support <a href="https://oauth.net/1/">OAuth 1.0</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-oauth2.html">oauth2</a>: support <a href="https://oauth.net/2/">OAuth 2.0</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-odbc.html">odbc</a>: support ODBC database query</li>
<li><a href="http://docs.factorcode.org/content/vocab-picomath.html">picomath</a>: implement <a href="https://hewgill.com/picomath/">picomath.org</a> small math words</li>
<li><a href="http://docs.factorcode.org/content/vocab-robohash.html">robohash</a>: adding a robot-based hashing tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-sequences.frozen.html">sequences.frozen</a>: virtual "frozen" sequence</li>
<li><a href="http://docs.factorcode.org/content/vocab-sequences.interleaved.html">sequences.interleaved</a>: virtual "interleaved" sequence</li>
<li><a href="http://docs.factorcode.org/content/vocab-shapefiles.html">shapefiles</a>: parser for <a href="https://www.esri.com/library/whitepapers/pdfs/shapefile.pdf">ESRI Shapefiles</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-shell.parser.html">shell.parser</a>: parser for shell expressions</li>
<li><a href="http://docs.factorcode.org/content/vocab-snake-game.html">snake-game</a>: implementation of the <a href="https://en.wikipedia.org/wiki/Snake_(video_game_genre)">snake video game</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-sodium.html">sodium</a>: wrapper for <a href="https://download.libsodium.org/doc/">libsodium</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-sorting.bubble.html">sorting.bubble</a>: adding Bubblesort</li>
<li><a href="http://docs.factorcode.org/content/vocab-stream.extras.html">stream.extras</a>: few helper words</li>
<li><a href="http://docs.factorcode.org/content/vocab-subrip-subtitles.html">subrip-subtitles</a>: parser for <a href="https://en.wikipedia.org/wiki/SubRip">SubRip</a> <code>.SRT</code> files</li>
<li><a href="http://docs.factorcode.org/content/vocab-successor.html">successor</a>: algorithm to find successor to a given string</li>
<li><a href="http://docs.factorcode.org/content/vocab-text-analysis.html">text-analysis</a>: analyze the complexity of English text</li>
<li><a href="http://docs.factorcode.org/content/vocab-text-to-pdf.html">text-to-pdf</a>: simple "Text to PDF" utility</li>
<li><a href="http://docs.factorcode.org/content/vocab-text-to-speech.html">text-to-speech</a>: cross-platform "Text to Speech" utility</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.cal.html">tools.cal</a>: command-line "cal" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.cat.html">tools.cat</a>: command-line "cat" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.copy.html">tools.copy</a>: command-line "copy" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.echo.html">tools.echo</a>: command-line"echo" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.grep.html">tools.grep</a>: command-line"grep" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.image-analyzer.html">tools.image-analyzer</a>: examine Factor image files</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.move.html">tools.move</a>: command-line "move" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.seq.html">tools.seq</a>: command-line "seq" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.tree.html">tools.tree</a>: command-line "tree" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.uniq.html">tools.uniq</a>: command-line "uniq" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.wc.html">tools.wc</a>: command-line "wc" tool</li>
<li><a href="http://docs.factorcode.org/content/vocab-ui.gadgets.charts.html">ui.gadgets.charts</a>: UI gadget for rendering line charts</li>
<li><a href="http://docs.factorcode.org/content/vocab-ui.gadgets.frame-buffer.html">ui.gadgets.frame-buffer</a>: UI gadget that supports a couple games</li>
<li><a href="http://docs.factorcode.org/content/vocab-ui.theme.html">ui.theme</a>: support for light and dark themes</li>
<li><a href="http://docs.factorcode.org/content/vocab-windows.comdlg32.html">windows.comdlg32</a>: using <code>comdlg32.dll</code> functions</li>
<li><a href="http://docs.factorcode.org/content/vocab-windows.crypt32.html">windows.crypt32</a>: using <code>crypt32.dll</code> functions</li>
<li><a href="http://docs.factorcode.org/content/vocab-windows.dragdrop-listener.html">windows.dragdrop-listener</a>: allow dropping files into the Listener</li>
<li><a href="http://docs.factorcode.org/content/vocab-windows.dropfiles.html">windows.dropfiles</a>: implementing "file drop" gesture on Windows</li>
<li><a href="http://docs.factorcode.org/content/vocab-windows.surface-dial.html">windows.surface-dial</a>: support for Microsoft Surface Dial</li>
<li><a href="http://docs.factorcode.org/content/vocab-xdg.html">xdg</a>: support for <a href="https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG Base Directory Specification</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-zealot.html">zealot</a>: continuous build and testing library</li>
</ul>
<br/>
<h2>Improved Libraries:</h2>
<ul>
<li><a href="http://docs.factorcode.org/content/vocab-boids.html">boids</a>: adding Cocoa TouchBar buttons</li>
<li><a href="http://docs.factorcode.org/content/vocab-cap.html">cap</a>: support for screenshot on retina displays</li>
<li><a href="http://docs.factorcode.org/content/vocab-checksums.html">checksums</a>: cleanup and improved checksum API</li>
<li><a href="http://docs.factorcode.org/content/vocab-color-table.html">color-table</a>: add columns for filled color and hex code</li>
<li><a href="http://docs.factorcode.org/content/vocab-cpu.architecture.html">cpu.architecture</a>: adding <a href="https://re-factor.blogspot.com/2015/06/bit-test.html">Bit Test primitive</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-cpu.x86.64.html">cpu.x86.64</a>: fix for return register on x86.64</li>
<li><a href="http://docs.factorcode.org/content/vocab-colors.hex.html">colors.hex</a>: support varying length hex notations</li>
<li><a href="http://docs.factorcode.org/content/vocab-combinators.extras.html">combinators.extras</a>: adding <code>swap-when</code></li>
<li><a href="http://docs.factorcode.org/content/vocab-compiler.cfg.html">compiler.cfg</a>: fix scheduling issue</li>
<li><a href="http://docs.factorcode.org/content/vocab-concurrency.distributed.html">concurrency.distributed</a>: fix serializing of remote threads</li>
<li><a href="http://docs.factorcode.org/content/vocab-elf.html">elf</a>: cleanup and performance improvements</li>
<li><a href="http://docs.factorcode.org/content/vocab-formatting.html">formatting</a>: support "space" prefix for numbers</li>
<li><a href="http://docs.factorcode.org/content/vocab-furnace.utilities.html">furnace.utilities</a>: improve template path resolution</li>
<li><a href="http://docs.factorcode.org/content/vocab-heaps.html">heaps</a>: <a href="https://re-factor.blogspot.com/2014/12/heaps.html">performance improvements</a> to the heap algorithm</li>
<li><a href="http://docs.factorcode.org/content/vocab-help.html">help</a>: default word help (if not provided)</li>
<li><a href="http://docs.factorcode.org/content/vocab-html.parser.printer.html">html.parser.printer</a>: improved plain text printer</li>
<li><a href="http://docs.factorcode.org/content/vocab-http.client.html">http.client</a>: support HTTP proxies and bug fixes</li>
<li><a href="http://docs.factorcode.org/content/vocab-http.server.html">http.server</a>: fix use of port remapping</li>
<li><a href="http://docs.factorcode.org/content/vocab-http.server.static.html">http.server.static</a>: support sorting by columns</li>
<li><a href="http://docs.factorcode.org/content/vocab-images.png.html">images.png</a>: support reading color profiles</li>
<li><a href="http://docs.factorcode.org/content/vocab-images.tiff.html">images.tiff</a>: fix bugs exposed by AFL test suite</li>
<li><a href="http://docs.factorcode.org/content/vocab-interpolate.html">interpolate</a>: simplify and <a href="https://re-factor.blogspot.com/2015/04/interpolate.html">some minor improvements</a></li>
<li><a href="http://docs.factorcode.org/content/vocab-io.directories.search.html">io.directories.search</a>: simplify interface and allow BFS and DFS searches</li>
<li><a href="http://docs.factorcode.org/content/vocab-io.encodings.8-bit.html">io.encodings.8-bit</a>: more encodings and simplify hierarchy</li>
<li><a href="http://docs.factorcode.org/content/vocab-io.files.info.windows.html">io.files.info.windows</a>: fix <code>file-readable?</code> to check current user's permissions</li>
<li><a href="http://docs.factorcode.org/content/vocab-io.files.unique.html">io.files.unique</a>: create multiple unique files at same time</li>
<li><a href="http://docs.factorcode.org/content/vocab-io.launcher.html">io.launcher</a>: cleanup interface and support hidden processes on Windows</li>
<li><a href="http://docs.factorcode.org/content/vocab-io.streams.c.html">io.streams.c</a>: faster <code>M\ c-reader stream-read-until</code></li>
<li><a href="http://docs.factorcode.org/content/vocab-json.writer.html">json.writer</a>: better support for non-string keys, unicode and special floats</li>
<li><a href="http://docs.factorcode.org/content/vocab-lexer.html">lexer</a>: support universal comments</li>
<li><a href="http://docs.factorcode.org/content/vocab-logging.server.html">logging.server</a>: simplify code</li>
<li><a href="http://docs.factorcode.org/content/vocab-macho.html">macho</a>: updated structures and tests</li>
<li><a href="http://docs.factorcode.org/content/vocab-mason.release.html">mason.release</a>: code signing on macOS and Windows</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.binpack.html">math.binpack</a>: faster <code>binpack</code> and <code>map-binpack</code></li>
<li><a href="http://docs.factorcode.org/content/vocab-math.combinatorics.html">math.combinatorics</a>: performance improvements</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.extras.html">math.extras</a>: adding Möbius function and Kelly criterion</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.functions.html">math.functions</a>: adding <code>logn</code></li>
<li><a href="http://docs.factorcode.org/content/vocab-math.parser.html">math.parser</a>: faster <code>format-float</code> for known format strings</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.primes.erato.html">math.primes.erato</a>: performance improvements</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.primes.factors.html">math.primes.factors</a>: command-line support</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.primes.solovay-strassen.html">math.primes.solovay-strassen</a>: adding Solovay-Strassen primality test</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.statistics.html">math.statistics</a>: fixes and new words</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.text.french.html">math.text.french</a>: more proper use of French</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.transforms.bwt.html">math.transforms.bwt</a>: performance improvements</li>
<li><a href="http://docs.factorcode.org/content/vocab-math.vectors.html">math.vectors</a>: adding <code>v>integer</code></li>
<li><a href="http://docs.factorcode.org/content/vocab-mime.types.html">mime.types</a>: update for new mime types</li>
<li><a href="http://docs.factorcode.org/content/vocab-multiline.html">multiline</a>: support "Lua-style" strings</li>
<li><a href="http://docs.factorcode.org/content/vocab-peg.ebnf.html">peg.ebnf</a>: handle escapes in strings better</li>
<li><a href="http://docs.factorcode.org/content/vocab-pong.html">pong</a>: bug fixes and a bit fancier graphics</li>
<li><a href="http://docs.factorcode.org/content/vocab-readline-listener.html">readline-listener</a>: adding vocab word completions</li>
<li><a href="http://docs.factorcode.org/content/vocab-reddit.html">reddit</a>: fix for Reddit API changes</li>
<li><a href="http://docs.factorcode.org/content/vocab-sequences.extras">sequences.extras</a>: some possibly useful new words</li>
<li><a href="http://docs.factorcode.org/content/vocab-simple-tokenizer.html">simple-tokenizer</a>: consider TAB, CR, LF as spaces</li>
<li><a href="http://docs.factorcode.org/content/vocab-smalltalk.html">smalltalk</a>: cleanup grammar and fix underscore bug</li>
<li><a href="http://docs.factorcode.org/content/vocab-splitting.monotonic.html">splitting.monotonic</a>: faster <code>monotonic-split</code>.</li>
<li><a href="http://docs.factorcode.org/content/vocab-sorting.html">sorting</a>: faster <code>sort-keys</code> and <code>sort-values</code> on <code>hashtables</code></li>
<li><a href="http://docs.factorcode.org/content/vocab-sorting.extras.html">sorting.extras</a>: faster <code>map-sort</code></li>
<li><a href="http://docs.factorcode.org/content/vocab-strings.parser.html">strings.parser</a>: allow both <code>\u{snowman}</code> and <code>\u{2603}</code>
<li><a href="http://docs.factorcode.org/content/vocab-terminfo.html">terminfo</a>: look in multiple directories for terminfo files</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.disassembler.html">tools.disassembler</a>: allow disassemble of compose and curry</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.dns.html">tools.dns</a>: enable use from command-line</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.hexdump.html">tools.hexdump</a>: support stdin hexdump</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.scaffold.html">tools.scaffold</a>: Better examples scaffold. Add more types.</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.test.html">tools.test</a>: adding <code>with-test-file</code> and <code>with-test-directory</code> helper words</li>
<li><a href="http://docs.factorcode.org/content/vocab-tools.which.html">tools.which</a>: enable use from command-line</li>
<li><a href="http://docs.factorcode.org/content/vocab-ui.tools.browser.html">ui.tools.browser</a>: adding Cocoa TouchBar buttons</li>
<li><a href="http://docs.factorcode.org/content/vocab-ui.tools.inspector.html">ui.tools.inspector</a>: improved performance with large arrays and hashtables</li>
<li><a href="http://docs.factorcode.org/content/vocab-ui.tools.listener.html">ui.tools.listener</a>: adding Cocoa TouchBar buttons, Ctrl-Break support on Windows, and vocab word completions</li>
<li><a href="http://docs.factorcode.org/content/vocab-unicode.html">unicode</a>: simplified hierarchy</li>
<li><a href="http://docs.factorcode.org/content/vocab-urls.html">urls</a>: improved parsing of scheme component</li>
<li><a href="http://docs.factorcode.org/content/vocab-wrap.html">wrap</a>: performance improvements using better algorithm</li>
<li><a href="http://docs.factorcode.org/content/vocab-xkcd.html">xkcd</a>: fix mouseover text.</li>
<li><a href="http://docs.factorcode.org/content/vocab-xml.data.html">xml.data</a>: make tags support assoc protocol</li>
<li><a href="http://docs.factorcode.org/content/vocab-z-algorithm.html">z-algorithm</a>: faster <code>z-values</code></li>
</ul>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]14tag:blogger.com,1999:blog-8513438391157777465.post-26911576823556375562018-02-12T13:39:00.001-08:002018-02-12T15:35:34.391-08:00Minesweeper<p><a href="https://en.wikipedia.org/wiki/Minesweeper_(video_game)">Minesweeper</a> is a fun game that was probably made most popular by its inclusion in <a href="https://en.wikipedia.org/wiki/Microsoft_Minesweeper">various Microsoft Windows versions</a> since the early 1990's.</p>
<p>I thought it would be fun to build a simple Minesweeper clone using <a href="http://factorcode.org">Factor</a>.
<blockquote><img src="https://i.imgur.com/ZFQPR9a.png" width="224" height="330" /></blockquote>
<p>You can run this by updating to the <a href="https://github.com/factor/factor">latest code</a> and running:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span> <span style="color: #0000FF; font-weight: bold">scratchpad</span> <span style="color: #BA2121">"minesweeper"</span> run</pre></blockquote>
<h3>Game Engine</h3>
<p>We are going to represent our game grid as a two-dimensional array of "cells".<p>
<p>Each cell contains the number of mines contained in the (up to eight) adjacent cells, whether the cell contains a mine, and a "state" flag showing whether the cell was <code>+clicked+</code>, <code>+flagged+</code>, or marked with a <code>+question+</code> mark.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">SYMBOLS: </span><span style="color: #0000FF">+clicked+</span> <span style="color: #0000FF">+flagged+</span> <span style="color: #0000FF">+question+</span> <span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">TUPLE:</span> <span style="color: #0000FF; font-weight: bold">cell</span> <span style="color: #19177C">#adjacent</span> <span style="color: #19177C">mined?</span> <span style="color: #19177C">state</span> <span style="color: #008000; font-weight: bold">;</span>
</pre></blockquote>
<p>Making a <code>(rows, cols)</code> grid of cells:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">make-cells</span> <span style="color: #0000FF">( </span><span style="color: #19177C">rows</span> <span style="color: #19177C">cols</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">)</span>
'[ _ [ cell <span style="color: #008000">new </span>] <span style="color: #008000">replicate </span>] <span style="color: #008000">replicate </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can lookup a particular cell using its <code>(row, col)</code> index:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">cell-at</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">cell/f</span> <span style="color: #0000FF">)</span>
row cells <span style="color: #008000">?nth </span>[ col <span style="color: #008000">swap ?nth </span>] [ <span style="color: #880000">f </span>] <span style="color: #008000">if* </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Placing a number of mines into cells, just looks for a certain number of unmined cells at random, and then marks them as mined:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">unmined-cell</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">cell</span> <span style="color: #0000FF">)</span>
<span style="color: #880000">f </span>[ <span style="color: #008000">dup </span>mined?>> ] [ <span style="color: #008000">drop dup </span>random random ] <span style="color: #008000">do while nip </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">place-mines</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #19177C">n</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">)</span>
[ <span style="color: #008000">dup </span>unmined-cell <span style="color: #880000">t </span>>>mined? <span style="color: #008000">drop </span>] <span style="color: #008000">times </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can count the number of adjacent mines for each cell, by looking at its neighbors:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span> <span style="color: #0000FF">neighbors</span> {
{ <span style="color: #666666">-1 -1 </span>} { <span style="color: #666666">-1 </span> <span style="color: #666666">0 </span>} { <span style="color: #666666">-1 </span> <span style="color: #666666">1 </span>}
{ <span style="color: #666666">0 -1 </span>} { <span style="color: #666666">0 </span> <span style="color: #666666">1 </span>}
{ <span style="color: #666666">1 -1 </span>} { <span style="color: #666666">1 </span> <span style="color: #666666">0 </span>} { <span style="color: #666666">1 </span> <span style="color: #666666">1 </span>}
}
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">adjacent-mines</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">#mines</span> <span style="color: #0000FF">)</span>
neighbors [
<span style="color: #008000">first2 </span>[ <span style="color: #008000">+ </span>] <span style="color: #008000">bi-curry@ bi* </span>cell-at
[ mined?>> ] [ <span style="color: #880000">f </span>] <span style="color: #008000">if*</span>
] <span style="color: #008000">with with with count </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>The <code>each-cell</code> word looks at all the cells, helping us update the "adjacent mines" counts:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">each-cell</span> <span style="color: #0000FF">( </span><span style="color: #19177C">...</span> <span style="color: #19177C">cells</span> <span style="color: #19177C">quot:</span> <span style="color: #0000FF">( </span><span style="color: #19177C">...</span> <span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #19177C">cell</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">...</span> <span style="color: #0000FF">) -- </span><span style="color: #19177C">...</span> <span style="color: #0000FF">)</span>
cells [| row |
[| cell col | row col cell quot <span style="color: #008000">call </span>] <span style="color: #008000">each-index</span>
] <span style="color: #008000">each-index </span><span style="color: #008000; font-weight: bold">; inline</span>
<span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">update-counts</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">)</span>
cells [| row col cell |
cells row col adjacent-mines cell #adjacent<<
] each-cell cells <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Since we aren't storing the number of rows and columns, we can get it from the array of cells:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">cells-dim</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">rows</span> <span style="color: #19177C">cols</span> <span style="color: #0000FF">)</span>
[ <span style="color: #008000">length </span>] [ <span style="color: #008000">first length </span>] <span style="color: #008000">bi </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can get the number of mines contained in the grid by counting them up:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">#mines</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">n</span> <span style="color: #0000FF">)</span>
[ [ mined?>> ] <span style="color: #008000">count </span>] <span style="color: #008000">map-sum </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can reset the game by making new cells and then placing the same number of mines in them:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">reset-cells</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">)</span>
[ cells-dim make-cells ] [ #mines place-mines ] <span style="color: #008000">bi </span>update-counts <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>The player wins if they click on all cells that aren't mines:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">won?</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">?</span> <span style="color: #0000FF">)</span>
[ [ { [ state>> +clicked+ <span style="color: #008000">= </span>] [ mined?>> ] } 1|| ] <span style="color: #008000">all? </span>] <span style="color: #008000">all? </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>The player loses if they click on any cell that's a mine:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">lost?</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">?</span> <span style="color: #0000FF">)</span>
[ [ { [ state>> +clicked+ <span style="color: #008000">= </span>] [ mined?>> ] } 1&& ] <span style="color: #008000">any? </span>] <span style="color: #008000">any? </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>And then the game is over if the player either wins or loses:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">game-over?</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">?</span> <span style="color: #0000FF">)</span>
{ [ lost? ] [ won? ] } 1|| <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We can tell this is a new game if no cells are clicked on:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">new-game?</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">?</span> <span style="color: #0000FF">)</span>
[ [ state>> +clicked+ <span style="color: #008000">= </span>] <span style="color: #008000">any? </span>] <span style="color: #008000">any? not </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>When we click on a cell, if it is not adjacent to any mines, we click on all the "clickable" (non-mined) cells around it:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">DEFER:</span> <span style="color: #0000FF">click-cell-at</span>
<span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">click-cells-around</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #0000FF">-- )</span>
neighbors [
<span style="color: #008000">first2 </span>[ row <span style="color: #008000">+ </span>] [ col <span style="color: #008000">+ </span>] <span style="color: #008000">bi* </span>:> <span style="color: #0000FF">( </span><span style="color: #19177C">row'</span> <span style="color: #19177C">col'</span> <span style="color: #0000FF">)</span>
cells row' col' cell-at [
mined?>> [
cells row' col' click-cell-at
] <span style="color: #008000">unless</span>
] <span style="color: #008000">when*</span>
] <span style="color: #008000">each </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Handle clicking a cell. If it's the first click and the cell is mined, we move it to another random cell, then continue with the click. The click is ignored if the cell was already clicked or flagged. Continue clicking around any cells that have no adjacent mines and are not themselves mined.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">click-cell-at</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #0000FF">-- )</span>
cells row col cell-at [
cells new-game? [
<span style="color: #408080; font-style: italic">! first click shouldn't be a mine</span>
<span style="color: #008000">dup </span>mined?>> [
cells unmined-cell <span style="color: #880000">t </span>>>mined? <span style="color: #008000">drop </span><span style="color: #880000">f </span>>>mined?
cells update-counts <span style="color: #008000">drop</span>
] <span style="color: #008000">when</span>
] <span style="color: #008000">when</span>
<span style="color: #008000">dup </span>state>> { +clicked+ +flagged+ } <span style="color: #008000">member? </span>[ <span style="color: #008000">drop </span>] [
+clicked+ >>state
{ [ mined?>> <span style="color: #008000">not </span>] [ #adjacent>> <span style="color: #666666">0 </span><span style="color: #008000">= </span>] } 1&& [
cells row col click-cells-around
] <span style="color: #008000">when</span>
] <span style="color: #008000">if</span>
] <span style="color: #008000">when* </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Handle marking a cell. First by flagging it as a likely mine, or marking with a question mark to come back to later. If the cell is not clicked, we just cycle through flagging, question, or not clicked.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">mark-cell-at</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cells</span> <span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #0000FF">-- )</span>
cells row col cell-at [
<span style="color: #008000">dup </span>state>> {
{ +clicked+ [ +clicked+ ] }
{ +flagged+ [ +question+ ] }
{ +question+ [ <span style="color: #880000">f </span>] }
{ <span style="color: #880000">f </span>[ +flagged+ ] }
} <span style="color: #008000">case </span>>>state <span style="color: #008000">drop</span>
] <span style="color: #008000">when* </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<h3>Graphical Interface</h3>
<p>Our graphical interface is going to consist of a <a href="http://docs.factorcode.org:8080/content/word-gadget,ui.gadgets.html">gadget</a> with an array of cells and a cache of <a href="http://docs.factorcode.org:8080/content/word-__lt__texture__gt__%2Copengl.textures.html">OpenGL texture objects</a> that can be easily drawn on the screen.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">TUPLE:</span> <span style="color: #0000FF; font-weight: bold">grid-gadget</span> < <span style="color: #0000FF; font-weight: bold">gadget</span> <span style="color: #19177C">cells</span> <span style="color: #19177C">textures</span> <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>When you make a new <code>grid-gadget</code>, it initializes the game to a specified number of rows, columns, and number of mines:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF"><grid-gadget></span> <span style="color: #0000FF">( </span><span style="color: #19177C">rows</span> <span style="color: #19177C">cols</span> <span style="color: #19177C">mines</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">gadget</span> <span style="color: #0000FF">)</span>
grid-gadget <span style="color: #008000">new</span>
rows cols make-cells
mines place-mines update-counts >>cells
H{ } <span style="color: #008000">clone </span>>>textures
COLOR: gray <solid> >>interior <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>When <a href="http://docs.factorcode.org:8080/content/word-ungraft__star__,ui.gadgets.html">ungraft*</a> is called to indicate the gadget is no longer visible on the screen, we clean up the cached textures:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">M:</span> <span style="color: #0000FF; font-weight: bold">grid-gadget</span> <span style="color: #0000FF">ungraft*</span>
<span style="color: #008000">dup </span>find-gl-context
[ <span style="color: #008000">values </span>dispose-each H{ } <span style="color: #008000">clone </span>] change-textures
call-next-method <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Our images are going to be <code>32 x 32</code> squares, so the preferred dimension is number of rows and columns times 32 pixels for each square.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">M:</span> <span style="color: #0000FF; font-weight: bold">grid-gadget</span> <span style="color: #0000FF">pref-dim*</span>
cells>> cells-dim [ <span style="color: #666666">32 </span><span style="color: #008000">* </span>] <span style="color: #008000">bi@ swap 2array </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Some slightly complex logic to decide which image to display for each cell, taking into account whether the game is over so we can show the positions of all the mines and whether the player was correct in flagging a cell as mined, etc:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">cell-image-path</span> <span style="color: #0000FF">( </span><span style="color: #19177C">cell</span> <span style="color: #19177C">game-over?</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">image-path</span> <span style="color: #0000FF">)</span>
game-over? cell mined?>> <span style="color: #008000">and </span>[
cell state>> +clicked+ <span style="color: #008000">= </span><span style="color: #BA2121">"mineclicked.gif"</span> <span style="color: #BA2121">"mine.gif"</span> <span style="color: #008000">?</span>
] [
cell state>>
{
{ +question+ [ <span style="color: #BA2121">"question.gif"</span> ] }
{ +flagged+ [ game-over? <span style="color: #BA2121">"misflagged.gif"</span> <span style="color: #BA2121">"flagged.gif"</span> <span style="color: #008000">? </span>] }
{ +clicked+ [
cell mined?>> [
<span style="color: #BA2121">"mine.gif"</span>
] [
cell #adjacent>> <span style="color: #666666">0 </span><span style="color: #008000">or </span>number>string
<span style="color: #BA2121">"open"</span> <span style="color: #BA2121">".gif"</span> <span style="color: #008000">surround</span>
] <span style="color: #008000">if </span>] }
{ <span style="color: #880000">f </span>[ <span style="color: #BA2121">"blank.gif"</span> ] }
} <span style="color: #008000">case</span>
] <span style="color: #008000">if </span><span style="color: #BA2121">"vocab:minesweeper/_resources/"</span> <span style="color: #008000">prepend </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Drawing a cached texture is a matter of looking up the image in our texture cache and then rendering to the screen:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">draw-cached-texture</span> <span style="color: #0000FF">( </span><span style="color: #19177C">path</span> <span style="color: #19177C">gadget</span> <span style="color: #0000FF">-- )</span>
textures>> [ load-image { <span style="color: #666666">0 0 </span>} <texture> ] <span style="color: #008000">cache</span>
[ dim>> ] [ draw-scaled-texture ] <span style="color: #008000">bi </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Drawing our gadget, is basically drawing all of the cells at their proper locations on the screen:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">M::</span> <span style="color: #0000FF; font-weight: bold">grid-gadget</span> <span style="color: #0000FF">draw-gadget*</span> <span style="color: #0000FF">( </span><span style="color: #19177C">gadget</span> <span style="color: #0000FF">-- )</span>
gadget cells>> game-over? :> game-over?
gadget cells>> [| row col cell |
col row [ <span style="color: #666666">32 </span><span style="color: #008000">* </span>] <span style="color: #008000">bi@ 2array </span>[
cell game-over? cell-image-path
gadget draw-cached-texture
] with-translation
] each-cell <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Basic handling for the gadget being left-clicked on:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">on-click</span> <span style="color: #0000FF">( </span><span style="color: #19177C">gadget</span> <span style="color: #0000FF">-- )</span>
gadget hand-rel <span style="color: #008000">first2 </span>:> <span style="color: #0000FF">( </span><span style="color: #19177C">w</span> <span style="color: #19177C">h</span> <span style="color: #0000FF">)</span>
h w [ <span style="color: #666666">32 </span><span style="color: #008000">/i </span>] <span style="color: #008000">bi@ </span>:> <span style="color: #0000FF">( </span><span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #0000FF">)</span>
gadget cells>> :> cells
cells game-over? [
cells row col click-cell-at
] <span style="color: #008000">unless </span>gadget relayout-1 <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Basic handling for the gadget being right-clicked on:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">on-mark</span> <span style="color: #0000FF">( </span><span style="color: #19177C">gadget</span> <span style="color: #0000FF">-- )</span>
gadget hand-rel <span style="color: #008000">first2 </span>:> <span style="color: #0000FF">( </span><span style="color: #19177C">w</span> <span style="color: #19177C">h</span> <span style="color: #0000FF">)</span>
h w [ <span style="color: #666666">32 </span><span style="color: #008000">/i </span>] <span style="color: #008000">bi@ </span>:> <span style="color: #0000FF">( </span><span style="color: #19177C">row</span> <span style="color: #19177C">col</span> <span style="color: #0000FF">)</span>
gadget cells>> :> cells
cells game-over? [
cells row col mark-cell-at
] <span style="color: #008000">unless </span>gadget relayout-1 <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Logic for creating new games of varying difficulties: easy, medium, and hard:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">new-game</span> <span style="color: #0000FF">( </span><span style="color: #19177C">gadget</span> <span style="color: #19177C">rows</span> <span style="color: #19177C">cols</span> <span style="color: #19177C">mines</span> <span style="color: #0000FF">-- )</span>
[ make-cells ] <span style="color: #008000">dip </span>place-mines update-counts >>cells
relayout-window <span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">com-easy</span> <span style="color: #0000FF">( </span><span style="color: #19177C">gadget</span> <span style="color: #0000FF">-- ) </span><span style="color: #666666">7 7 10 </span>new-game <span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">com-medium</span> <span style="color: #0000FF">( </span><span style="color: #19177C">gadget</span> <span style="color: #0000FF">-- ) </span><span style="color: #666666">15 15 40 </span>new-game <span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">com-hard</span> <span style="color: #0000FF">( </span><span style="color: #19177C">gadget</span> <span style="color: #0000FF">-- ) </span><span style="color: #666666">15 30 99 </span>new-game <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We set our gesture handlers for keyboard and mouse inputs:</p>
<blockquote><pre>grid-gadget {
{ T{ key-down { sym <span style="color: #BA2121">"1"</span> } } [ com-easy ] }
{ T{ key-down { sym <span style="color: #BA2121">"2"</span> } } [ com-medium ] }
{ T{ key-down { sym <span style="color: #BA2121">"3"</span> } } [ com-hard ] }
{ T{ button-up { # <span style="color: #666666">1 </span>} } [ on-click ] }
{ T{ button-up { # <span style="color: #666666">3 </span>} } [ on-mark ] }
{ T{ key-down { sym <span style="color: #BA2121">" "</span> } } [ on-mark ] }
} set-gestures</pre></blockquote>
<p>And a main word that creates an easy game in our <code>grid-gadget</code> and opens it in a new window:</p>
<blockquote><pre>MAIN-WINDOW: run-minesweeper {
{ title <span style="color: #BA2121">"Minesweeper"</span> }
{ window-controls
{ normal-title-bar close-button minimize-button } }
} <span style="color: #666666">7 7 10 </span><grid-gadget> >>gadgets <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>The implementation above is about 200 lines of code and contains the full game logic. The <a href="https://github.com/factor/factor/blob/master/extra/minesweeper/minesweeper.factor">final version</a> is just under 300 lines of code, and adds:</p>
<ul>
<li>support for a toolbar to easily start new games</li>
<li>the traditional counter of the number of mines remaining</li>
<li>display of the number of seconds elapsed</li>
<li>a smiley face showing a funny "uh-oh!" face when you are about to click as well as winning and losing smileys</li>
<li>support for <a href="https://en.wikipedia.org/wiki/Retina_Display">retina displays</a> using <code>2x</code> images</li>
</ul>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]2tag:blogger.com,1999:blog-8513438391157777465.post-52606479657122217512017-02-11T14:05:00.000-08:002018-02-18T08:25:35.041-08:00$7.11<p>Today, <a href="https://wrongsideofmemphis.wordpress.com/2017/02/11/7-11-in-four-prices-and-the-decimal-type-revisited/">someone blogged</a> about a fun problem:</p>
<blockquote><i>“A mathematician purchased four items in a grocery store. He noticed that when he added the prices of the four items, the sum came to $7.11, and when he multiplied the prices of the four items, the product came to $7.11.”</i></blockquote>
<p>In some ways, this is similar to the <a href="https://re-factor.blogspot.com/2015/06/send-more-money.html">SEND + MORE = MONEY</a> problem that I blogged about awhile ago. You can always approach this problem with an direct and iterative solution, but instead we will use the <a href="http://docs.factorcode.org/content/vocab-backtrack.html">backtrack</a> vocabulary to solve this problem with less code.</p>
<p>We'll be solving this exactly, using integer "numbers of cents", progressively restricting the options, and then calling <a href="http://docs.factorcode.org/content/word-fail%2Cbacktrack.html">fail</a> if the solution is not found, so we check the next. The first valid solution will be returned:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">solve-711</span> <span style="color: #0000FF">( -- </span><span style="color: #19177C">seq</span> <span style="color: #0000FF">)</span>
<span style="color: #666666">711 </span><span style="color: #008000"><iota> </span>amb-lazy :> w
<span style="color: #666666">711 </span>w <span style="color: #008000">- <iota> </span>amb-lazy :> x
<span style="color: #666666">711 </span>w <span style="color: #008000">- </span>x <span style="color: #008000">- <iota> </span>amb-lazy :> y
<span style="color: #666666">711 </span>w <span style="color: #008000">- </span>x <span style="color: #008000">- </span>y <span style="color: #008000">- </span>:> z
w x <span style="color: #008000">* </span>y <span style="color: #008000">* </span>z <span style="color: #008000">* </span><span style="color: #666666">711,000,000 </span><span style="color: #008000">= </span>[ fail ] <span style="color: #008000">unless</span>
{ w x y z } <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Using it, we get our answer:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">IN:</span> <span style="color: #0000FF; font-weight: bold">scratchpad</span> solve-711 <span style="color: #666666">.</span>
{ <span style="color: #666666">120 125 150 316 </span>}</pre></blockquote>
<p>And that is: $1.20, $1.25, $1.50, and $3.16.</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]2tag:blogger.com,1999:blog-8513438391157777465.post-60515110963607176952017-02-05T15:59:00.000-08:002017-02-05T17:11:48.434-08:00Dirty Money: Code Challenge<p>There's a fun coding challenge to <a href="https://jorin.me/dirtymoney">follow the dirty money</a> that I discovered recently.<p>
<blockquote><i><p>A shady Internet business has been discovered.</p>
<p>The website has been made public by a whistle blower. We have enough evidence about the dirty deals they did. But to charge them we need to get hands on precise numbers about the transactions that happened on their platform.</p>
<p>Unfortunately no record of the transactions could be seized so far. The only hint we have is this one transaction:</p>
<a href="https://gist.githubusercontent.com/jorinvo/6f68380dd07e5db3cf5fd48b2465bb04/raw/c02b1e0b45ecb2e54b36e4410d0631a66d474323/fd0d929f-966f-4d1a-89cd-feee5a1c5347.json">fd0d929f-966f-4d1a-89cd-feee5a1c5347.json</a>
<p>What we need is the total of all transactions in Dollar. Can you trace down all other transactions and get the total?</p>
<p>Be careful to count each transaction only once, even if it is linked multiple times. You can use whatever tool works best.</p></i></blockquote>
<p>We need a way to extract the dollar amount from the transaction text. The dollars might be specified with period or a comma to represent the decimal point. We use <a href="http://docs.factorcode.org/content/article-regexp.html">regular expressions</a> to look for the dollar amount and then convert to a number.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">dollars</span> <span style="color: #0000FF">( </span><span style="color: #19177C">str</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">$</span> <span style="color: #0000FF">)</span>
R/ \$\d*[,.]\d+/ first-match <span style="color: #008000">rest</span>
<span style="color: #BA2121">","</span> <span style="color: #BA2121">"."</span> replace string>number <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>We will use a <a href="http://docs.factorcode.org/content/article-hash-sets.html">hash-set</a> of visited links, and only if the link has not been visited will we <a href="http://docs.factorcode.org/content/word-http-get,http.client.html">http-get</a> the contents of the URL, parse the <a href="http://docs.factorcode.org/content/article-json.reader.html">JSON</a>, and extract the dollar amount of both the transaction and any links it contains. The set of visited links will tell us how many total transactions we traced.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">transaction</span> <span style="color: #0000FF">( </span><span style="color: #19177C">url</span> <span style="color: #19177C">visited</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">dollars</span> <span style="color: #0000FF">)</span>
url visited ?adjoin [
url http-get <span style="color: #008000">nip </span>json> :> data
data <span style="color: #BA2121">"content"</span> <span style="color: #008000">of </span>dollars
data <span style="color: #BA2121">"links"</span> <span style="color: #008000">of </span>[ visited transaction ] <span style="color: #008000">map-sum +</span>
] [ <span style="color: #666666">0 </span>] <span style="color: #008000">if </span><span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">transactions</span> <span style="color: #0000FF">( </span><span style="color: #19177C">url</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">dollars</span> <span style="color: #19177C">#transactions</span> <span style="color: #0000FF">)</span>
HS{ } <span style="color: #008000">clone </span>[ transaction ] [ cardinality ] <span style="color: #008000">bi </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>That's all we need to solve the problem. We can run this with the initial URL and get the answer:</p>
<blockquote><pre>$9064.79 in 50 transactions.</pre></blockquote>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-33914799575506684682016-12-24T11:21:00.000-08:002016-12-25T11:45:32.250-08:00The Twelve Days of Christmas<p><a href="https://programmingpraxis.com">Programming Praxis</a> posted a task to <a href="https://programmingpraxis.com/2016/12/23/a-partridge-in-a-pear-tree/">write a program to print the words</a> to <a href="https://en.wikipedia.org/wiki/The_Twelve_Days_of_Christmas_(song)">The Twelve Days of Christmas</a> song. We are going to solve it in <a href="http://factorcode.org">Factor</a>.</p>
<p>We start off by defining all the gifts received on each day:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">CONSTANT:</span> <span style="color: #0000FF">gifts</span> {
{ <span style="color: #BA2121">"first"</span> <span style="color: #BA2121">"a partridge in a pear tree"</span> }
{ <span style="color: #BA2121">"second"</span> <span style="color: #BA2121">"two turtle doves and "</span> }
{ <span style="color: #BA2121">"third"</span> <span style="color: #BA2121">"three French hens, "</span> }
{ <span style="color: #BA2121">"fourth"</span> <span style="color: #BA2121">"four calling birds, "</span> }
{ <span style="color: #BA2121">"fifth"</span> <span style="color: #BA2121">"five golden rings, "</span> }
{ <span style="color: #BA2121">"sixth"</span> <span style="color: #BA2121">"six geese a-laying, "</span> }
{ <span style="color: #BA2121">"seventh"</span> <span style="color: #BA2121">"seven swans a-swimming, "</span> }
{ <span style="color: #BA2121">"eighth"</span> <span style="color: #BA2121">"eight maids a-milking, "</span> }
{ <span style="color: #BA2121">"ninth"</span> <span style="color: #BA2121">"nine ladies dancing, "</span> }
{ <span style="color: #BA2121">"tenth"</span> <span style="color: #BA2121">"ten lords a-leaping, "</span> }
{ <span style="color: #BA2121">"eleventh"</span> <span style="color: #BA2121">"eleven pipers piping, "</span> }
{ <span style="color: #BA2121">"twelfth"</span> <span style="color: #BA2121">"twelve drummers drumming, "</span> }
}</pre></blockquote>
<p>Then we iterate through the days, gathering all the gifts in reverse for each day, and formatting them, and wrapping to 72 columns of text for display.</p>
<blockquote><pre>gifts [
[ <span style="color: #008000">first </span>] [ <span style="color: #666666">1 </span><span style="color: #008000">+ </span>gifts <span style="color: #008000">swap head values reverse concat </span>] <span style="color: #008000">bi*</span>
<span style="color: #BA2121">"On the %s day of Christmas my true love gave to me %s."</span> sprintf
<span style="color: #666666">72 </span>wrap-string <span style="color: #008000">print nl</span>
] <span style="color: #008000">each-index</span></pre></blockquote>
<p>Which gives us these words:</p>
<blockquote><i><p>On the first day of Christmas my true love gave to me a partridge in a
pear tree.</p>
<p>On the second day of Christmas my true love gave to me two turtle doves
and a partridge in a pear tree.</p>
<p>On the third day of Christmas my true love gave to me three French hens,
two turtle doves and a partridge in a pear tree.</p>
<p>On the fourth day of Christmas my true love gave to me four calling
birds, three French hens, two turtle doves and a partridge in a pear
tree.</p>
<p>On the fifth day of Christmas my true love gave to me five golden rings,
four calling birds, three French hens, two turtle doves and a partridge
in a pear tree.</p>
<p>On the sixth day of Christmas my true love gave to me six geese
a-laying, five golden rings, four calling birds, three French hens, two
turtle doves and a partridge in a pear tree.</p>
<p>On the seventh day of Christmas my true love gave to me seven swans
a-swimming, six geese a-laying, five golden rings, four calling birds,
three French hens, two turtle doves and a partridge in a pear tree.</p>
<p>On the eighth day of Christmas my true love gave to me eight maids
a-milking, seven swans a-swimming, six geese a-laying, five golden
rings, four calling birds, three French hens, two turtle doves and a
partridge in a pear tree.</p>
<p>On the ninth day of Christmas my true love gave to me nine ladies
dancing, eight maids a-milking, seven swans a-swimming, six geese
a-laying, five golden rings, four calling birds, three French hens, two
turtle doves and a partridge in a pear tree.</p>
<p>On the tenth day of Christmas my true love gave to me ten lords
a-leaping, nine ladies dancing, eight maids a-milking, seven swans
a-swimming, six geese a-laying, five golden rings, four calling birds,
three French hens, two turtle doves and a partridge in a pear tree.</p>
<p>On the eleventh day of Christmas my true love gave to me eleven pipers
piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking,
seven swans a-swimming, six geese a-laying, five golden rings, four
calling birds, three French hens, two turtle doves and a partridge in a
pear tree.</p>
<p>On the twelfth day of Christmas my true love gave to me twelve drummers
drumming, eleven pipers piping, ten lords a-leaping, nine ladies
dancing, eight maids a-milking, seven swans a-swimming, six geese
a-laying, five golden rings, four calling birds, three French hens, two
turtle doves and a partridge in a pear tree.</p></i></blockquote>
mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-6021973486304216092016-11-28T12:58:00.000-08:002016-11-29T09:10:29.803-08:00AnyBar<p><a href="https://github.com/tonsky/AnyBar">AnyBar</a> is a <a href="http://apple.com/macos">macOS</a> status indicator that displays a "colored dot" in the menu bar that can be changed programatically. What it means and when it changes is entirely up to the user.</p>
<img src="https://i.imgur.com/tk042yQ.png" width="536" height="94" />
<p>You can easily install it with <a href="http://caskroom.io/">Homebrew-cask</a>:</p>
<blockquote><pre>$ brew cask install anybar</pre></blockquote>
<p>The <a href="https://github.com/tonsky/AnyBar#alternative-clients">README</a> lists a number of alternative clients in different programming languages. I thought it would be fun to show how to use it from <a href="http://factorcode.org">Factor</a>. Since AnyBar responds to AppleScript (and I added <a href="https://re-factor.blogspot.com/2013/10/applescript.html">support for AppleScript</a> a few years ago), we could do this:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">USE:</span> <span style="color: #0000FF; font-weight: bold">cocoa.apple-script</span>
<span style="color: #BA2121">"tell application \"AnyBar\" to set image name to \"blue\""</span>
run-apple-script</pre></blockquote>
<p>The AnyBar application also listens to a UDP port (default: 1738) and can be instructed to change from a Terminal using a simple <a href="https://linux.die.net/man/1/echo">echo</a> | <a href="https://linux.die.net/man/1/nc">nc</a> command:</p>
<blockquote><pre>$ echo -n "blue" | nc -4u -w0 localhost 1738</pre></blockquote>
<p>Using our <a href="http://docs.factorcode.org/content/article-network-streams.html">networking</a> words similarly is pretty simple:</p>
<blockquote><pre><span style="color: #BA2121">"blue"</span> >byte-array <span style="color: #BA2121">"127.0.0.1"</span> <span style="color: #666666">1738 </span><inet4> send-once</pre></blockquote>
<p>But if we wanted to get more fancy, we could use <a href="http://docs.factorcode.org/content/article-words.symbol.html">symbols</a> to configure which AnyBar instance to send to, with default values to make it easy to use, and <a href="http://docs.factorcode.org/content/word-resolve-host,io.sockets.html">resolve-host</a> to lookup hostnames:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">SYMBOL:</span> <span style="color: #0000FF">anybar-host</span>
<span style="color: #BA2121">"localhost"</span> anybar-host <span style="color: #008000">set-global</span>
<span style="color: #008000; font-weight: bold">SYMBOL:</span> <span style="color: #0000FF">anybar-port</span>
<span style="color: #666666">1738 </span>anybar-port <span style="color: #008000">set-global</span>
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">anybar</span> <span style="color: #0000FF">( </span><span style="color: #19177C">str</span> <span style="color: #0000FF">-- )</span>
ascii encode
anybar-host <span style="color: #008000">get </span>resolve-host <span style="color: #008000">first</span>
anybar-port <span style="color: #008000">get </span>with-port send-once <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>AnyBar is a neat little program!</p>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0tag:blogger.com,1999:blog-8513438391157777465.post-67001791583048495832016-11-26T22:28:00.000-08:002016-11-26T22:28:07.912-08:00Reverse Factorial<p>A few years ago, I wrote about implementing various <a href="http://re-factor.blogspot.com/2013/04/factorial.html">factorials</a> using <a href="http://factorcode.org">Factor</a>. Recently, I came across a <a href="https://reddit.com/r/dailyprogrammer/comments/55nior/20161003_challenge_286_easy_reverse_factorial">programming challenge</a> to implement a "reverse factorial" function to determine what factorial produces a number, or none if it is not a factorial.</p>
<p>To do this, we examine each factorial in order, checking against the number being tested:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">reverse-factorial</span> <span style="color: #0000FF">( </span><span style="color: #19177C">m</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">n</span> <span style="color: #0000FF">)</span>
<span style="color: #666666">1 1 </span>[ <span style="color: #008000">2over > </span>] [ <span style="color: #666666">1 </span><span style="color: #008000">+ </span>[ <span style="color: #008000">* </span>] <span style="color: #008000">keep </span>] <span style="color: #008000">while </span>[ <span style="color: #008000">= </span>] <span style="color: #008000">dip and </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>And some unit tests:</p>
<blockquote><pre>{ <span style="color: #666666">10 </span>} [ <span style="color: #666666">3628800 </span>reverse-factorial ] unit-test
{ <span style="color: #666666">12 </span>} [ <span style="color: #666666">479001600 </span>reverse-factorial ] unit-test
{ <span style="color: #666666">3 </span>} [ <span style="color: #666666">6 </span>reverse-factorial ] unit-test
{ <span style="color: #880000">f </span>} [ <span style="color: #666666">18 </span>reverse-factorial ] unit-test</pre></blockquote>mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]2tag:blogger.com,1999:blog-8513438391157777465.post-55094315666053108152016-10-27T14:14:00.000-07:002016-10-28T15:15:23.090-07:00Gopher Server<p>A few days ago, I noticed a post about building a <a href="http://blogs.perl.org/users/aaron_baugher/2015/10/the-old-becomes-new-again-a-gopher-server-in-perl-6.html">Gopher Server in Perl 6</a>. I had already implemented a <a href="http://re-factor.blogspot.com/2014/12/gopher.html">Gopher Client in Factor</a>, and thought it might be fun to show a simple Gopher Server in <a href="http://factorcode.org">Factor</a> in around 50 lines of code.</p>
<p>Using the <a href="http://docs.factorcode.org/content/article-io.servers.html">io.servers</a> vocabulary, we will define a new multi-threaded server that has a directory to serve content from and hostname that it can be accessed at:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">TUPLE:</span> <span style="color: #0000FF; font-weight: bold">gopher-server</span> < <span style="color: #0000FF; font-weight: bold">threaded-server</span>
{ <span style="color: #19177C">serving-hostname</span> string }
{ <span style="color: #19177C">serving-directory</span> string } <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>When a file is requested, it can be streamed back to clients:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">send-file</span> <span style="color: #0000FF">( </span><span style="color: #19177C">path</span> <span style="color: #0000FF">-- )</span>
binary [ [ <span style="color: #008000">write </span>] <span style="color: #008000">each-block </span>] with-file-reader <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>The Gopher protocol is defined in <a href="https://tools.ietf.org/html/rfc1436">RFC 1436</a> and lists a few differentiated file types. We use the <a href="http://docs.factorcode.org/content/vocab-mime.types.html">mime.types</a> vocabulary to return the correct one.</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">gopher-type</span> <span style="color: #0000FF">( </span><span style="color: #19177C">entry</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">type</span> <span style="color: #0000FF">)</span>
<span style="color: #008000">dup </span>directory? [
<span style="color: #008000">drop </span><span style="color: #BA2121">"1"</span>
] [
name>> mime-type {
{ [ <span style="color: #008000">dup </span><span style="color: #BA2121">"text/"</span> <span style="color: #008000">head? </span>] [ <span style="color: #008000">drop </span><span style="color: #BA2121">"0"</span> ] }
{ [ <span style="color: #008000">dup </span><span style="color: #BA2121">"image/gif"</span> <span style="color: #008000">= </span>] [ <span style="color: #008000">drop </span><span style="color: #BA2121">"g"</span> ] }
{ [ <span style="color: #008000">dup </span><span style="color: #BA2121">"image/"</span> <span style="color: #008000">head? </span>] [ <span style="color: #008000">drop </span><span style="color: #BA2121">"I"</span> ] }
[ <span style="color: #008000">drop </span><span style="color: #BA2121">"9"</span> ]
} <span style="color: #008000">cond</span>
] <span style="color: #008000">if </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>When a directory is requested, we can send a listing of all the sub-directories and files it contains, sending their relative path to the root directory being served so they can be requested properly by the client:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">::</span> <span style="color: #0000FF">send-directory</span> <span style="color: #0000FF">( </span><span style="color: #19177C">server</span> <span style="color: #19177C">path</span> <span style="color: #0000FF">-- )</span>
path [
[
[ gopher-type ] [ name>> ] <span style="color: #008000">bi</span>
<span style="color: #008000">dup </span>path prepend-path
server serving-directory>> ?head <span style="color: #008000">drop</span>
server serving-hostname>>
server insecure>>
<span style="color: #BA2121">"%s%s\t%s\t%s\t%d\r\n"</span> sprintf utf8 encode <span style="color: #008000">write</span>
] <span style="color: #008000">each</span>
] with-directory-entries <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>To know which path was requested, we read the line, split on the first tab, carriage return, or newline character we see:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">read-gopher-path</span> <span style="color: #0000FF">( -- </span><span style="color: #19177C">path</span> <span style="color: #0000FF">)</span>
<span style="color: #008000">readln </span>[ <span style="color: #BA2121">"\t\r\n"</span> <span style="color: #008000">member? </span>] split1-when <span style="color: #008000">drop</span> <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>With all of that built, we can now implement a word to handle a client request:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">M:</span> <span style="color: #0000FF; font-weight: bold">gopher-server</span> <span style="color: #0000FF">handle-client*</span>
<span style="color: #008000">dup </span>serving-directory>> read-gopher-path append-path
<span style="color: #008000">dup </span>file-info directory? [
send-directory
] [
send-file <span style="color: #008000">drop</span>
] <span style="color: #008000">if flush </span><span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>Initializing a <code>gopher-server</code> instance and providing a convenience word to start one:</p>
<blockquote><pre><span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF"><gopher-server></span> <span style="color: #0000FF">( </span><span style="color: #19177C">directory</span> <span style="color: #19177C">port</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">server</span> <span style="color: #0000FF">)</span>
utf8 gopher-server new-threaded-server
<span style="color: #BA2121">"gopher.server"</span> >>name
<span style="color: #008000">swap </span>>>insecure
binary >>encoding
<span style="color: #BA2121">"localhost"</span> >>serving-hostname
<span style="color: #008000">swap </span>resolve-symlinks >>serving-directory <span style="color: #008000; font-weight: bold">;</span>
<span style="color: #008000; font-weight: bold">:</span> <span style="color: #0000FF">start-gopher-server</span> <span style="color: #0000FF">( </span><span style="color: #19177C">directory</span> <span style="color: #19177C">port</span> <span style="color: #0000FF">-- </span><span style="color: #19177C">server</span> <span style="color: #0000FF">)</span>
<gopher-server> start-server <span style="color: #008000; font-weight: bold">;</span></pre></blockquote>
<p>This is available in the <a href="https://github.com/factor/factor/blob/master/extra/gopher/server/server.factor">gopher.server</a> vocabulary with a few improvements such as:</p>
<ul>
<li>Support for <code>.gophermap</code> files for alternate results when content is requested.</li>
<li>Support for <code>.gopherhead</code> files to print headers above directory listings.</li>
<li>Navigation to parent directories using <code>..</code> links.</li>
<li>Display file modified timestamp and file sizes.</li>
<li>Improved error handling.</li>
</ul>
mrjbq7http://www.blogger.com/profile/06842721076008035602[email protected]0