<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Blog of Endle</title>
    <subtitle>Endle&#x27;s Blog. Since 2014.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.zhenbo.pro/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-04-05T00:00:00+00:00</updated>
    <id>https://blog.zhenbo.pro/atom.xml</id>
    <entry xml:lang="en">
        <title>ComfyUI on Fedora 43 Silverblue using AMD Radeon RX 7600 XT</title>
        <published>2026-04-05T00:00:00+00:00</published>
        <updated>2026-04-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/run-comfyui-fedora-43-amd-radeon-7600/"/>
        <id>https://blog.zhenbo.pro/run-comfyui-fedora-43-amd-radeon-7600/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/run-comfyui-fedora-43-amd-radeon-7600/">&lt;h4 id=&quot;background&quot;&gt;Background&lt;&#x2F;h4&gt;
&lt;p&gt;In January 2026, I saw some recommendations of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Comfy-Org&#x2F;ComfyUI&quot;&gt;ComfyUI&lt;&#x2F;a&gt;.
I found a great article &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ojambo.com&#x2F;getting-started-with-comfyui-on-fedora-42-using-amd-instinct-mi60&quot;&gt;Getting Started with ComfyUI on Fedora 42 Using AMD Instinct MI60&lt;&#x2F;a&gt; on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.ojambo.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.ojambo.com&#x2F;&lt;&#x2F;a&gt;.
Since &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fedoraproject.org&#x2F;wiki&#x2F;SIGs&#x2F;HC&quot;&gt;Fedora HC group&lt;&#x2F;a&gt; has been updating ROCm rapidly, and package names are different in Fedora 42 and 43
(may shift again in 44) . After a few tweaks, ComfyUI ran perfectly on my desktop.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;environment&quot;&gt;Environment&lt;&#x2F;h4&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Component&lt;&#x2F;th&gt;&lt;th&gt;Spec&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;OS&lt;&#x2F;td&gt;&lt;td&gt;Fedora 43 Kinoite&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CPU&lt;&#x2F;td&gt;&lt;td&gt;AMD Ryzen 9 7950X3D&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;RAM&lt;&#x2F;td&gt;&lt;td&gt;64 GB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;GPU&lt;&#x2F;td&gt;&lt;td&gt;AMD Radeon RX 7600 XT, 16 GB VRAM&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Storage&lt;&#x2F;td&gt;&lt;td&gt;1 TB SSD&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h4 id=&quot;process&quot;&gt;Process&lt;&#x2F;h4&gt;
&lt;h5 id=&quot;dependencies&quot;&gt;Dependencies&lt;&#x2F;h5&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;rpm-ostree install rocminfo rocm-smi
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note: if you have steam.i686 installed from rpmfusion, be aware that ROCm packages may conflict with i686 packages.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;clone-comfyui&quot;&gt;Clone ComfyUI&lt;&#x2F;h5&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;Comfy-Org&#x2F;ComfyUI.git
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd ComfyUI
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;python3 -m venv venv
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;source venv&#x2F;bin&#x2F;activate
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the version I used&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;commit bbe2c13a7075bcf4de3b6744f96d84d12c334350
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Date:   Thu Jan 29 20:52:22 2026 -0800
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As of 2026-Jan, ComfyUI suggests ROCm 6.4&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pip install torch torchvision torchaudio --index-url https:&#x2F;&#x2F;download.pytorch.org&#x2F;whl&#x2F;rocm6.4
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;pip install -r requirements.txt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;run-comfyui&quot;&gt;Run ComfyUI&lt;&#x2F;h5&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;python main.py --lowvram
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can monitor GPU usage&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;watch -n 1 rocm-smi
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;amdgpu_top     # https:&#x2F;&#x2F;github.com&#x2F;Umio-Yasuno&#x2F;amdgpu_top
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is a harmless warning message &lt;code&gt;&#x2F;opt&#x2F;amdgpu&#x2F;share&#x2F;libdrm&#x2F;amdgpu.ids: No such file or directory&lt;&#x2F;code&gt;. Just ignore it&lt;&#x2F;p&gt;
&lt;h5 id=&quot;pick-model&quot;&gt;Pick model&lt;&#x2F;h5&gt;
&lt;p&gt;Open &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;127.0.0.1:8188&#x2F;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:8188&#x2F;&lt;&#x2F;a&gt;,  ComfyUI will suggest several templates. For simplicity, we just use &lt;code&gt;Z-Image-Turbo Text to Image&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2026&#x2F;comfyui&#x2F;model_select.png&quot; alt=&quot;model_select&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After clicking it, ComfyUI will provide a list of model files. We download them and copy them to &lt;code&gt;models&#x2F;&lt;&#x2F;code&gt;, e.g. models&#x2F;checkpoints&#x2F;, models&#x2F;diffusion_models&#x2F;, models&#x2F;vae&#x2F;, models&#x2F;text_encoders&#x2F;&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2026&#x2F;comfyui&#x2F;model_file_list.png&quot; alt=&quot;model_file_list&quot; &#x2F;&gt;
&lt;p&gt;Adjust the prompt, then click the &quot;Run&quot; button at upper right.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;performance&quot;&gt;Performance&lt;&#x2F;h5&gt;
&lt;p&gt;My RX 7600 XT could finish one image in 16 seconds, with 2.98s&#x2F;it&lt;&#x2F;p&gt;
&lt;h5 id=&quot;why-lowvram&quot;&gt;Why lowvram&lt;&#x2F;h5&gt;
&lt;p&gt;Although 16G VRAM should be feasible to run this model, but ComfyUI is very reluctant to free VRAM after a single pass. I tried &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;SeanScripts&#x2F;ComfyUI-Unload-Model&quot;&gt;Unload&lt;&#x2F;a&gt; following  &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;comfyui&#x2F;comments&#x2F;1mkxh74&#x2F;how_do_i_clear_vram_properly_after_every_run&#x2F;&quot;&gt;this reddit post&lt;&#x2F;a&gt;, but it didn&#x27;t work for me. I also tried &lt;code&gt;--reserve-vram 2&lt;&#x2F;code&gt; but my desktop would still freeze after several passes due to no VRAM. The only workaround is to add &lt;code&gt;--lowvram&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>LycanArena - 有发言规则的 AI 狼人杀</title>
        <published>2026-03-15T00:00:00+00:00</published>
        <updated>2026-03-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/lycanarena-ai-werewolf-arena/"/>
        <id>https://blog.zhenbo.pro/lycanarena-ai-werewolf-arena/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/lycanarena-ai-werewolf-arena/">&lt;p&gt;2017 年的时候，我看到了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cmu.edu&#x2F;news&#x2F;stories&#x2F;archives&#x2F;2017&#x2F;january&#x2F;AI-beats-poker-pros.html&quot;&gt;Libratus
击败了人类德州扑克玩家&lt;&#x2F;a&gt; 这条新闻，我就在想，能不能设计出狼人杀的 AI war。进而，可以试着寻找一些 Game Theory Optimal 的策略，比如狼人应该用什么频率自刀，村民应该用什么频率穿神牌的衣服。&lt;&#x2F;p&gt;
&lt;p&gt;到了 2023 年，随着 ChatGPT 的风行，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;zhenboli1&#x2F;status&#x2F;1627091896983240708&quot;&gt;我在想能不能用 LLM 实现狼人杀的 AI
War&lt;&#x2F;a&gt;。但是，
LLM也带来了自然语言的模糊与不精确。&lt;&#x2F;p&gt;
&lt;p&gt;最近，我终于抽出时间，实现了我最初的创意：AI War Arena。在发言阶段，每个玩家的发言不再是自然语言，而是从严格规定的
Utterance object 中选取的若干语句。这种设计，也让人类玩家，Rule-based AI 和 LLM 可以参与同一场游戏。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rule-based-ai&quot;&gt;Rule-based AI&lt;&#x2F;h4&gt;
&lt;p&gt;我实现了一个使用 Rule-based AI，里面有一些简单的发言规则（比如预言家一定会跳身份）。如果加入的玩家数量不足，就可以用这个简单的
bot 把板子填满。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;natural-language-to-utterance-objects&quot;&gt;Natural Language to Utterance Objects&lt;&#x2F;h4&gt;
&lt;p&gt;我现在使用了 &lt;code&gt;llama-3.1-70b&lt;&#x2F;code&gt;，一个是因为它支持
&lt;code&gt;response_format&lt;&#x2F;code&gt;，另一个原因就是它便宜。&lt;&#x2F;p&gt;
&lt;p&gt;这种最常见的发言&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;我是预言家，player-3是狼&lt;&#x2F;code&gt;
会被转换成这两个   Utterance Objects&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;RoleClaimD 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;自称身份为 Role(Seer)。
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;BeliefReport
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;判断 Bot player-3 (player-3) 属于 Group(Wolf)（High confidence）。
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;不过，我现在的 NL parser 还很不成熟。有可能是 70b 的模型不够强，需要新模型加上新 prompt，才能涵盖更多类型的发言。&lt;&#x2F;p&gt;
&lt;p&gt;这里有一个五分钟的演示视频
https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=rauAgqg9XJE&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2025 年的年终流水帐</title>
        <published>2026-01-01T00:00:00+00:00</published>
        <updated>2026-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/my-stories-in-2025/"/>
        <id>https://blog.zhenbo.pro/my-stories-in-2025/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/my-stories-in-2025/">&lt;p&gt;自己小学的时候，每过一年都感觉非常激动，从二年级升至三年级，像是跨过了一道天坎。但成年以后，时间一年年地走过，并没有什么实感。如果从自己六岁开始算，小学时候过了一年，自己人生的体验增加了三分之一，而现在则不到二十分之一，感觉平淡也理所应当。但是如果换一个角度看，不算百分比，而是算加减法，那我现在一年里的经历，比小时候还是要多的多得多。所以，我就在元旦这天，用小学生写流水帐日记的形式，记录 2025 年我的一些有趣的琐事。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;xue-hui-liao-grub-cfg&quot;&gt;学会了 grub.cfg&lt;&#x2F;h4&gt;
&lt;p&gt;字面意思，我不是说我给 grub 贡献了某个 patch，而是我今年第一次学会了用 &lt;code&gt;configfile&lt;&#x2F;code&gt; &lt;code&gt;linux&lt;&#x2F;code&gt; &lt;code&gt;boot&lt;&#x2F;code&gt; 这些 grub shell commands. 大家不要笑，虽然我从2012年开始算用了十四年 Linux 了，但我一直把 bootloader 当成禁忌的魔法。如果电脑的启动出问题了，我只会两手一摊，快速地用 LiveCD 重装。现在学会了以后才发现，其实这些命令非常简单直接，只要进入了 Grub Shell 那就很简单了。同样，grub.cfg 只是一个简单的文本文档，虽然常见发行版生成的 grub.cfg 非常唬人，但核心部分也很简单。bootloader 出问题的时候手动指定 kernel path 再进系统里面修并不难。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;fen-qing-chu-liao-jiao-huan-ji-he-lu-you-qi-de-qu-bie&quot;&gt;分清楚了交换机和路由器的区别&lt;&#x2F;h4&gt;
&lt;p&gt;我之前的印象里，路由器的 WAN 口接到魔法盒子 Modem 上，消耗一个公网 IP，让我的十几个设备可以同时联网。至于交换机，我不知道它有什么用，它甚至不是一个网络设备，祝它在思科的仓库里一切顺利。今年夏天有幸被迫阅读了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;search.worldcat.org&#x2F;title&#x2F;1105777650&quot;&gt;Computer Networks&lt;&#x2F;a&gt; 自己对网络也有了一点点的理解，解答了自己多年来的疑惑。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cha.fan&#x2F;questions&#x2F;4bHeXFQ8VuQywzbt3zZB&#x2F;answers&#x2F;63Je7ZidNkMEb6oUCBuC&quot;&gt;https:&#x2F;&#x2F;cha.fan&#x2F;questions&#x2F;4bHeXFQ8VuQywzbt3zZB&#x2F;answers&#x2F;63Je7ZidNkMEb6oUCBuC&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zai-neovim-0-11-xia-zhong-xin-pei-zhi-liao-lsp&quot;&gt;在 neovim 0.11 下重新配置了 LSP&lt;&#x2F;h4&gt;
&lt;p&gt;Neovim 0.11 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vonheikemen.github.io&#x2F;learn-nvim&#x2F;feature&#x2F;treesitter.html&quot;&gt;内置的 treesitter&lt;&#x2F;a&gt; 让配置 LSP 变得异常容易。我也&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;127.0.0.1:1111&#x2F;lsp-on-neovim-0-11-from-stratch&#x2F;&quot;&gt;写了一篇博客&lt;&#x2F;a&gt;，从零开始配置 LSP。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;fa-xian-de-you-qu-de-ying-yong&quot;&gt;发现的有趣的应用&lt;&#x2F;h4&gt;
&lt;h5 id=&quot;koreader-readwisereader&quot;&gt;KOReader + Readwisereader&lt;&#x2F;h5&gt;
&lt;p&gt;今年很惊喜地发现，（在2022年）我的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mobileread.com&#x2F;forums&#x2F;showthread.php?t=346037&quot;&gt;Kindle Voyage 可以越狱了&lt;&#x2F;a&gt;. 抱着坏了就买一个KOBO 的心态，我顺利地装上了 KOReader。更大的惊喜是，我发现 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tomtom800&#x2F;readwisereader&quot;&gt;tomtom800&#x2F;readwisereader&lt;&#x2F;a&gt; 把我一年前的点子实现了出来。现在我的 Kindle 和我的 Readwise Reader 可以双向同步，我积攒的 read-later 的博客的数量也终于开始下降了。在我心理，这个插件就是 Software Of The Year 2025.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;supasend&quot;&gt;Supasend&lt;&#x2F;h5&gt;
&lt;p&gt;我是多年的 Logseq 用户，但手机上做快速记录时启动 LogSeq 又太慢。之前我用过 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hankhank10&#x2F;loglink-plugin&quot;&gt;Loglink&lt;&#x2F;a&gt;，可以用 WhatsApp 发消息，但还是要手动 Sync 一次，太繁琐。我今年发现了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;supasend.app&#x2F;&quot;&gt;Supasend&lt;&#x2F;a&gt;, 发现它简直是我梦想中的速记软件。急速、离线。每个角度看，都打磨的很完美。Supasend 支持很多记录软件，除了Logseq 也包括 Obsidian, Notion, Things 等。我觉得非常值得一试。&lt;&#x2F;p&gt;
&lt;h5 id=&quot;inkwell&quot;&gt;Inkwell&lt;&#x2F;h5&gt;
&lt;p&gt;一个用来生成 LLVM IR 的 Rust 库。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TheDan64&#x2F;inkwell&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;TheDan64&#x2F;inkwell&lt;&#x2F;a&gt; 我先前试着用它写一个迷你编译器，虽然中途弃坑了，但过程还是很好玩的。我也是写了才发现，LLVM IR 居然功能这么多. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mcyoung.xyz&#x2F;2023&#x2F;08&#x2F;01&#x2F;llvm-ir&#x2F;&quot;&gt;Ref blog&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;yin-le&quot;&gt;音乐&lt;&#x2F;h4&gt;
&lt;p&gt;虽然我在 2025 年没玩过维多利亚三，但我在通勤路上无数次播放了它的 OST。我觉得B站评论区引述的一句话非常妙：&lt;em&gt;一个时代结束的标志就是它开始被浪漫化&lt;&#x2F;em&gt;. Rule The World 把这个时代最积极的一面，用昂扬的旋律描绘了出来。世博会、飞艇、铁路等意向仿佛就浮现在了眼前。我现在还能背出首个官宣 PV 的内容，金色的笔墨划过地图，将整个世界联系在一起。我觉得 &lt;em&gt;鎏金&lt;&#x2F;em&gt; 这个词在这里无比贴切。&lt;&#x2F;p&gt;
&lt;p&gt;在写这篇文章的时候，我还特意考证了一下。Randall Collins 的确在它的著作 《Discovery of Society》第一章开篇提到， One sign that an era is over is that it begins to be romanticized. 书里的这句话是描写十九世纪的法国人怀念中世纪。现在，十九世纪也在被人们怀念了。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;guan-zhan-ti-yu-bi-sai&quot;&gt;观战体育比赛&lt;&#x2F;h4&gt;
&lt;p&gt;今年是我第一年关注英雄联盟的 S 赛。我非常喜欢的选手是多兰，他也受到了很多不合理的非议。试问一下，一位选手谦逊、勤勉，在比赛中愿意吃最少的资源干脏活类活，为了团队战术牺牲自己的数据，这样的队友谁不喜欢呢？不过，部分观众不喜欢这样的选手也不意外。有的人夺冠后被质疑硬实力不强，有的人双冠后还需要继续证明自己。天佑勇者，也许真的在冥冥之中有天意，值得一个冠军却功亏一篑的选手很多，但胜利的桂冠没有办法给德不配位的人。至于 Faker, 虽然他已经拿了六个冠军了，但还是需要在明年再拿下S赛来证明自己不是昙花一现的版本冠军 &#x2F;s&lt;&#x2F;p&gt;
&lt;p&gt;今年石油杯我也看了格斗项目——绿冲捡手柄真的是太有节目效果了。为此我已经在本地图书馆排队了街霸6的光盘，看看明年我的年终总结里会不会记录我被打破防的体验。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gao-bie-2025&quot;&gt;告别 2025&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;fM667pMQ9AE?si=yI1cwDcYdyUEJSkG&quot;&gt;BGM: Rule the World&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在最后说一点个人的看法。社交平台上都充斥着大家的年终总结，里面写了不少实现的雄心壮志。不过，这是不是也是一种 reporting bias 呢？至少我的2025年度总结里的内容应该足够 trivial. 或者说，时间流逝本身就是non-trivial的吧&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Compile Wine in Container (Distrobox)</title>
        <published>2025-09-06T00:00:00+00:00</published>
        <updated>2025-09-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/compile-wine-with-distrobox/"/>
        <id>https://blog.zhenbo.pro/compile-wine-with-distrobox/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/compile-wine-with-distrobox/">&lt;h4 id=&quot;preface&quot;&gt;Preface&lt;&#x2F;h4&gt;
&lt;p&gt;A usable Wine build &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.winehq.org&#x2F;wine&#x2F;wine&#x2F;-&#x2F;wikis&#x2F;Building-Wine#shared-wow64:~:text=64%2Dbit%20Wine%20built%20without%2032%2Dbit%20support%20will%20not%20be%20able%20to%20run%20ANY%2032%2Dbit%20applications&quot;&gt;needs shared WoW64&lt;&#x2F;a&gt;, and to compile it, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.winehq.org&#x2F;wine&#x2F;wine&#x2F;-&#x2F;wikis&#x2F;Building-Wine#multi-arch-and-multi-lib:~:text=There%27s,time&quot;&gt;multi-lib dependencies are required&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As a Fedora Silverblue user, the conflict between &lt;code&gt;i686&lt;&#x2F;code&gt; and  &lt;code&gt;x86_64&lt;&#x2F;code&gt; libs happens very frequently, and &lt;code&gt;rpm-ostree install&lt;&#x2F;code&gt; will be blocked for weeks. As suggested by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.winehq.org&#x2F;wine&#x2F;wine&#x2F;-&#x2F;wikis&#x2F;Building-Wine#containers&quot;&gt;Wine Wiki&lt;&#x2F;a&gt;, I&#x27;m trying to compile wine within &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;distrobox.it&#x2F;&quot;&gt;Distrobox&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;create-distrobox-image&quot;&gt;Create Distrobox Image&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;rpm-ostree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; install distrobox&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Fedora 42: distrobox: 1.8.1.2&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;distrobox&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; create&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;name&lt;&#x2F;span&gt; wine&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;init&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;image&lt;&#x2F;span&gt; fedora:42&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;distrobox&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; enter wine&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By default, distrobox shows a lovely emoji 📦&lt;&#x2F;p&gt;
&lt;h4 id=&quot;install-dependency&quot;&gt;Install Dependency&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; dnf builddep wine.i686 wine.x86_64&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Some packages are coved by builddep, but somes are not. There are overlaps, definitely&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; dnf install libgcc.i686 glibc-devel.i686 glibc.i686&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; dnf install libX11-devel libX11-devel.i686 libXcomposite-devel libXcomposite-devel.i686 libXcomposite-devel.x86_64 libxcrypt-compat&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; dnf install libxcrypt-compat.i686 libxcrypt-compat.x86_64 libXcursor-devel                           libXcursor-devel.i686 libXcursor-devel.x86_64 libXext-devel libXext-devel.i686 libXext-devel.x86_64 libXfixes-devel.i686                           libXfixes-devel.x86_64 libXfixes.i686 libXi-devel                           libXi-devel.i686 libXi-devel.x86_64 libXinerama-devel libXinerama-devel.x86_64                           libxml2-devel.i686 libXmu-devel libXrandr-devel                           libXrandr-devel.i686 libXrandr-devel.x86_64 libXrender-devel                           libXrender-devel.i686 libXrender-devel.x86_64 freetype-devel.x86_64                           freetype-devel.i686 fontforge.x86_64 fontforge.i686&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; dnf install mesa-libGL-devel.i686 mesa-libGL-devel.x86_64 gnutls-devel.i686 gnutls-devel.x86_64&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; dnf install pulseaudio-libs-devel.i686 pulseaudio-libs-devel.x86_64&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;build-wine-64&quot;&gt;Build Wine 64&lt;&#x2F;h4&gt;
&lt;p&gt;According to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.winehq.org&#x2F;wine&#x2F;wine&#x2F;-&#x2F;wikis&#x2F;Building-Wine#shared-wow64:~:text=Compile%20the%2064%2Dbit%20version%20of%20Wine%20first%20with%20the%20%2D%2Denable%2Dwin64%20configure%20flag%2C%20in%20a%20separate%20build%20directory%20of%20course%20%3B%29&quot;&gt;Wine Wiki&lt;&#x2F;a&gt;, I need to build the 64-bit version first.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-storage z-modifier z-shell&quot;&gt;export&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;WINE_INSTALL_PATH&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&#x2F;home&#x2F;lizhenbo&#x2F;apps&#x2F;wine&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; make install as non-root later&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;p&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine64&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-and z-shell&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-cd z-shell&quot;&gt;cd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine64&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;PKG_CONFIG_PATH&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&#x2F;usr&#x2F;lib64&#x2F;pkgconfig&lt;&#x2F;span&gt;    &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CFLAGS&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-Og&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CROSSCFLAGS&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-Og&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CC&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;sccache gcc&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;i386_CC&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;sccache i686-w64-mingw32-gcc&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;x86_64_CC&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;sccache x86_64-w64-mingw32-gcc&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;&lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine&#x2F;configure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;enable-win64&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;prefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-option z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;WINE_INSTALL_PATH&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;make&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;j&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;nproc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Just taking one second to show off my 16-core CPU and 64 GB of RAM&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Outside the container, I can use wine64 directly. However, almost no Windows binaries can be executed by this 64-bit only wine.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;.&#x2F;wine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; notepad&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;.&#x2F;wine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&#x2F;var&#x2F;home&#x2F;lizhenbo&#x2F;.wine&#x2F;drive_c&#x2F;Program Files&#x2F;7-Zip&#x2F;7zFM.exe&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;build-wine-32&quot;&gt;Build Wine 32&lt;&#x2F;h4&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;p&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine32&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-and z-shell&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-cd z-shell&quot;&gt;cd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine32&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;PKG_CONFIG_PATH&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&#x2F;usr&#x2F;lib&#x2F;pkgconfig&lt;&#x2F;span&gt;    &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CFLAGS&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-Og&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CROSSCFLAGS&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-Og&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CC&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;sccache gcc -m32&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;&lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine&#x2F;configure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;with-wine64&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-option z-shell&quot;&gt;=&lt;&#x2F;span&gt;..&#x2F;wine64&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;prefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-option z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-expansion z-parameter z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-shell&quot;&gt;WINE_INSTALL_PATH&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;make&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;j&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;nproc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Configuration&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;.&#x2F;wine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; winecfg &lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-begin z-shell&quot;&gt;#&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt; Graphics -&amp;gt; Screen resolution -&amp;gt; 192 dpi&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-shell&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;.&#x2F;wine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;Downloads&#x2F;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;setup_slay_the_spire_2020-12-15-8735c9fe3cc2280b76aa3ec47c953352a7df1f65_(64bit)_(43443).exe&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;.&#x2F;wine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;.wine&#x2F;drive_c&#x2F;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;GOG Games&#x2F;Slay the Spire&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;SlayTheSpire.exe&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It worked well&lt;&#x2F;p&gt;
&lt;h4 id=&quot;install-wine&quot;&gt;Install Wine&lt;&#x2F;h4&gt;
&lt;p&gt;According to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.winehq.org&#x2F;wine&#x2F;wine&#x2F;-&#x2F;wikis&#x2F;Building-Wine#:~:text=you%20should%20run%20make%20install%20in%20the%2032%2Dbit%20build%20tree%20first%2C%20then%20in%20the%2064%2Dbit%20one&quot;&gt;Wine Wiki&lt;&#x2F;a&gt;, I should install 32-bit first.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-cd z-shell&quot;&gt;cd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine32&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;make&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; install&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;j&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;nproc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-cd z-shell&quot;&gt;cd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine64&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;make&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; install&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;j&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;nproc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;which&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; wine&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;&lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;apps&#x2F;wine&#x2F;bin&#x2F;wine&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;quicknote-about-lsp&quot;&gt;Quicknote about LSP&lt;&#x2F;h4&gt;
&lt;p&gt;Some of Wine&#x27;s header files are generated by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.winehq.org&#x2F;wine&#x2F;wine&#x2F;-&#x2F;wikis&#x2F;Man-Pages&#x2F;widl&quot;&gt;Wine IDL&lt;&#x2F;a&gt;, so I need to compile Wine once under the source code directory.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; dnf install bear&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-cd z-shell&quot;&gt;cd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;src&#x2F;wine&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;PKG_CONFIG_PATH&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&#x2F;usr&#x2F;lib64&#x2F;pkgconfig&lt;&#x2F;span&gt;    &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CFLAGS&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-Og&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CROSSCFLAGS&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-Og&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;CC&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;sccache gcc&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;i386_CC&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;sccache i686-w64-mingw32-gcc&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-variable z-other z-readwrite z-assignment z-shell&quot;&gt;x86_64_CC&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-assignment z-shell&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-unquoted z-shell&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;sccache x86_64-w64-mingw32-gcc&lt;span class=&quot;z-punctuation z-definition z-string z-end z-shell&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-separator z-continuation z-line z-shell&quot;&gt;\
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;    &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;.&#x2F;configure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;enable-win64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;bear&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-end-of-options z-shell&quot;&gt; --&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; make -j&lt;span class=&quot;z-meta z-group z-expansion z-command z-parens z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-variable z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-begin z-shell&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;nproc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parens z-end z-shell&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.bkryza.com&#x2F;about&#x2F;&quot;&gt;Bartek Kryza&lt;&#x2F;a&gt; has &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.bkryza.com&#x2F;posts&#x2F;compile-commands-json-gallery&#x2F;&quot;&gt;a great article about compile_commands.json&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>From Scratch: Set LSP on neovim 0.11</title>
        <published>2025-08-03T00:00:00+00:00</published>
        <updated>2025-08-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/lsp-on-neovim-0-11-from-stratch/"/>
        <id>https://blog.zhenbo.pro/lsp-on-neovim-0-11-from-stratch/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/lsp-on-neovim-0-11-from-stratch/">&lt;p&gt;In this guide, I walk through setting up a fresh Neovim configuration from scratch, using the latest features provided by Neovim 0.11.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-0-clean-my-old-neovim-config&quot;&gt;Step 0: Clean my old Neovim config&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~&#x2F;.config&#x2F;nvim&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;nvim&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;~&#x2F;.local&#x2F;state&#x2F;nvim&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I created a new git branch for my brand new neovim config.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;$&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; nvim&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;NVIM&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; v0.11.2&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;step-1-install-package-manager-lazy-nvim&quot;&gt;Step 1: Install Package Manager (lazy.nvim)&lt;&#x2F;h4&gt;
&lt;p&gt;In Neovim 0.11, the built-in plugin manager &lt;code&gt;vim.pack&lt;&#x2F;code&gt; is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20250725133537&#x2F;https:&#x2F;&#x2F;neovim.io&#x2F;doc&#x2F;user&#x2F;pack.html#vim.pack&quot;&gt;still in early stage&lt;&#x2F;a&gt;. (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;bsky.app&#x2F;profile&#x2F;neovim.io&#x2F;post&#x2F;3lt5en72xq22r&quot;&gt;Development News&lt;&#x2F;a&gt;) I&#x27;m going with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lazy.folke.io&#x2F;&quot;&gt;lazy.nvim&lt;&#x2F;a&gt; by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;folke&quot;&gt;folke&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Following the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lazy.folke.io&#x2F;installation&quot;&gt;official guide&lt;&#x2F;a&gt;, I create &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;config&#x2F;lazy.lua&lt;&#x2F;code&gt; and add &lt;code&gt;require(&quot;config.lazy&quot;)&lt;&#x2F;code&gt; to the brand new &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;init.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;lazy.nvim&lt;&#x2F;code&gt; fails to load when we have no plugins. So I add &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;johnfrankmorgan&#x2F;whitespace.nvim&quot;&gt;johnfrankmorgan&#x2F;whitespace&lt;&#x2F;a&gt; to my nvim. Create &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;plugins&#x2F;whitespace.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; class=&quot;language-lua z-code&quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-keyword z-control z-return z-lua&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;    &lt;span class=&quot;z-string z-quoted z-single z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;johnfrankmorgan&#x2F;whitespace.nvim&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;    &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-key z-lua&quot;&gt;&lt;span class=&quot;z-entity z-name z-function z-lua&quot;&gt;config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-lua&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt; &lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-lua&quot;&gt;function&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;        &lt;span class=&quot;z-support z-function z-builtin z-lua&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;whitespace-nvim&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;&lt;span class=&quot;z-variable z-function z-lua&quot;&gt;setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;            &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-key z-lua&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-key z-lua&quot;&gt;highlight&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-lua&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt; &lt;span class=&quot;z-string z-quoted z-single z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;DiffDelete&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;            &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-key z-lua&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-key z-lua&quot;&gt;ignored_filetypes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-lua&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt; &lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;TelescopePrompt&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;Trouble&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;help&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-single z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;dashboard&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;            &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-key z-lua&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-key z-lua&quot;&gt;ignore_terminal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-lua&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-lua&quot;&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;            &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-key z-lua&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-key z-lua&quot;&gt;return_cursor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-lua&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-lua&quot;&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;        &lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;        &lt;span class=&quot;z-comment z-line z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-lua&quot;&gt;--&lt;&#x2F;span&gt; remove trailing whitespace with a keybinding
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;        &lt;span class=&quot;z-comment z-line z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-lua&quot;&gt;--&lt;&#x2F;span&gt; vim.keymap.set(&amp;#39;n&amp;#39;, &amp;#39;&amp;lt;Leader&amp;gt;t&amp;#39;, require(&amp;#39;whitespace-nvim&amp;#39;).trim)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function z-lua&quot;&gt;&lt;span class=&quot;z-meta z-block z-lua&quot;&gt;    &lt;span class=&quot;z-keyword z-control z-end z-lua&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Restart nvim, this plugin is automatically installed. Executing &lt;code&gt;:checkhealth lazy&lt;&#x2F;code&gt; should say OK.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;move-vim-key-binding-to-a-new-config-file&quot;&gt;Move vim key binding to a new config file&lt;&#x2F;h5&gt;
&lt;p&gt;Personally I prefer to gather my vim keybind settings. I create a new file &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;config&#x2F;key_binding.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; class=&quot;language-lua z-code&quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-variable z-other z-lua&quot;&gt;vim&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;g&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;mapleader&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-lua&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-variable z-other z-lua&quot;&gt;vim&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;g&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;maplocalleader&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-lua&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape z-lua&quot;&gt;\\&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also remove them from &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;config&#x2F;lazy.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My &lt;code&gt;init.lua&lt;&#x2F;code&gt; now changes to&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; class=&quot;language-lua z-code&quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin z-lua&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;config.key_binding&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin z-lua&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;config.lazy&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;step-2-install-mason&quot;&gt;Step 2: Install Mason&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mason-org&#x2F;mason.nvim&quot;&gt;Mason.nvim&lt;&#x2F;a&gt; is the most popular neovim plugin for LSP servers. I only use &lt;code&gt;lazy.nvim&lt;&#x2F;code&gt; as package manager, so I don&#x27;t follow the best practice on readme. I create &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;plugins&#x2F;mason.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; class=&quot;language-lua z-code&quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-keyword z-control z-return z-lua&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;    &lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;mason-org&#x2F;mason.nvim&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;    &lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;mason-org&#x2F;mason-lspconfig.nvim&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I put my lsp config in a separate file &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;config&#x2F;lsp_config.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; class=&quot;language-lua z-code&quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin z-lua&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;mason&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;&lt;span class=&quot;z-variable z-function z-lua&quot;&gt;setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin z-lua&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;mason-lspconfig&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;&lt;span class=&quot;z-variable z-function z-lua&quot;&gt;setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also add &lt;code&gt;require(&quot;config.lsp_config&quot;)&lt;&#x2F;code&gt; to &lt;code&gt;init.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-3-install-lsp-server-via-mason&quot;&gt;Step 3: Install LSP Server via Mason&lt;&#x2F;h4&gt;
&lt;p&gt;I use mason to manage my installed LSP servers, so my life could be easier when I&#x27;m migrating my neovim config between multiple computers.
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mason-registry.dev&#x2F;registry&#x2F;list&quot;&gt;mason-registry&lt;&#x2F;a&gt; shows all the plugins managed by mason.
For now, I install &lt;code&gt;clangd&lt;&#x2F;code&gt;. Modify &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;config&#x2F;lsp_config.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; class=&quot;language-lua z-code&quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin z-lua&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;mason&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;&lt;span class=&quot;z-variable z-function z-lua&quot;&gt;setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-support z-function z-builtin z-lua&quot;&gt;require&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;mason-lspconfig&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;&lt;span class=&quot;z-variable z-function z-lua&quot;&gt;setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;    &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-key z-lua&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-key z-lua&quot;&gt;ensure_installed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-lua&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt; &lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;clangd&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-variable z-other z-lua&quot;&gt;vim&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;lsp&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;&lt;span class=&quot;z-variable z-function z-lua&quot;&gt;enable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;    &lt;span class=&quot;z-string z-quoted z-double z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;clangd&lt;span class=&quot;z-punctuation z-definition z-string z-end z-lua&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;neovim&#x2F;nvim-lspconfig&quot;&gt;neovim&#x2F;nvim-lspconfig&lt;&#x2F;a&gt; collects basic config files for most LSP servers. I prefer to only copy files needed instead of cloning the whole repo.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;mkdir&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; -&lt;&#x2F;span&gt;p&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;.config&#x2F;nvim&#x2F;lsp&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-logical z-and z-shell&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-cd z-shell&quot;&gt;cd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;.config&#x2F;nvim&#x2F;lsp&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;wget&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;neovim&#x2F;nvim-lspconfig&#x2F;refs&#x2F;heads&#x2F;master&#x2F;lsp&#x2F;clangd.lua&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Restart neovim, mason automatically downloads &lt;code&gt;clangd&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash z-code&quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;&lt;span class=&quot;z-meta z-group z-expansion z-tilde&quot;&gt;&lt;span class=&quot;z-variable z-language z-tilde z-shell&quot;&gt;~&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&#x2F;.local&#x2F;share&#x2F;nvim&#x2F;mason&#x2F;bin&#x2F;clangd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-option z-shell&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-parameter z-shell&quot;&gt; --&lt;&#x2F;span&gt;version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;clangd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; version 20.1.8 (https:&#x2F;&#x2F;github.com&#x2F;llvm&#x2F;llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;&#x2F;span&gt;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;Features:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; linux+grpc&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;Platform:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; x86_64-unknown-linux-gnu&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;try-clangd-in-a-real-c-c-project&quot;&gt;Try clangd in a real C&#x2F;C++ project&lt;&#x2F;h4&gt;
&lt;p&gt;I use &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rizsotto&#x2F;Bear&quot;&gt;rizsotto&#x2F;Bear&lt;&#x2F;a&gt; by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rizsotto&quot;&gt;László Nagy&lt;&#x2F;a&gt; to generate &lt;code&gt;compile_commands.json&lt;&#x2F;code&gt;.
Neovim 0.11 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gpanders.com&#x2F;blog&#x2F;whats-new-in-neovim-0-11&#x2F;#more-default-mappings&quot;&gt;provides a few default lsp key mappings&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-4-show-inline-error-message&quot;&gt;Step 4: Show inline error message&lt;&#x2F;h4&gt;
&lt;p&gt;As &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@vonheikemen&quot;&gt;Heiker&lt;&#x2F;a&gt; says &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vonheikemen.github.io&#x2F;devlog&#x2F;tools&#x2F;neovim-lsp-client-guide&#x2F;#:~:text=There%20is%20an%20option%20to%20show%20the%20error%20message%20inline%2E%20This%20is%20called%20%22virtual%20text%2E%22%20This%20used%20to%20be%20enabled%20by%20default%2C%20but%20now%20on%20Neovim%20v0%2E11%20is%20disabled%2E&quot;&gt;in their blog&lt;&#x2F;a&gt; &lt;code&gt;Virtual text is an option to show the error message inline.&lt;&#x2F;code&gt; We add it to &lt;code&gt;~&#x2F;.config&#x2F;nvim&#x2F;lua&#x2F;config&#x2F;lsp_config.lua&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; class=&quot;language-lua z-code&quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-variable z-other z-lua&quot;&gt;vim&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;diagnostic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-lua&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-property z-lua&quot;&gt;&lt;span class=&quot;z-variable z-function z-lua&quot;&gt;config&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-lua&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-lua&quot;&gt;{&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;  &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-key z-lua&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-key z-lua&quot;&gt;virtual_text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-lua&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-value z-lua&quot;&gt; &lt;span class=&quot;z-constant z-language z-boolean z-true z-lua&quot;&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-separator z-field z-lua&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-lua&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-lua&quot;&gt;&lt;span class=&quot;z-meta z-group z-lua&quot;&gt;&lt;span class=&quot;z-meta z-mapping z-lua&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-lua&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-lua&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;epilogue&quot;&gt;Epilogue&lt;&#x2F;h4&gt;
&lt;p&gt;The step-by-step code is hosted at &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;my-neovim-config&#x2F;commits&#x2F;nvim_11_demo&#x2F;&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;my-neovim-config&#x2F;commits&#x2F;nvim_11_demo&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@vonheikemen&quot;&gt;Heiker&lt;&#x2F;a&gt; has written several articles about nvim, including &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vonheikemen.github.io&#x2F;learn-nvim&#x2F;feature&#x2F;treesitter.html&quot;&gt;built-in treesitter&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vonheikemen.github.io&#x2F;devlog&#x2F;tools&#x2F;neovim-lsp-client-guide&#x2F;&quot;&gt;LSP guide&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vonheikemen.github.io&#x2F;devlog&#x2F;tools&#x2F;simple-neovim-config&#x2F;&quot;&gt;simple config&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Sync CalDAV, Replace Google&#x2F;MS Calendar, using northmail</title>
        <published>2025-04-27T00:00:00+00:00</published>
        <updated>2025-04-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/sync-caldav-replace-google-microsoft-calendar-with-northmail/"/>
        <id>https://blog.zhenbo.pro/sync-caldav-replace-google-microsoft-calendar-with-northmail/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/sync-caldav-replace-google-microsoft-calendar-with-northmail/">&lt;h4 id=&quot;preface&quot;&gt;Preface&lt;&#x2F;h4&gt;
&lt;p&gt;Self-host a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;CalDAV&quot;&gt;CalDAV&lt;&#x2F;a&gt; server is definitely doable, and I recommend having a try. I&#x27;m not a tech-savvy, and I&#x27;d promote a Canadian business, so I&#x27;m using northmail.ca in this article. It&#x27;s easy to replace northmail.ca with self-hosted server or another service provider. I&#x27;m not affiliated with northmail.ca&lt;&#x2F;p&gt;
&lt;h4 id=&quot;caldav-by-northmail-ca&quot;&gt;CalDAV by northmail.ca&lt;&#x2F;h4&gt;
&lt;p&gt;Click Calendar button at header, then &lt;code&gt;Calendar Settings&lt;&#x2F;code&gt; at bottom-left.&lt;&#x2F;p&gt;
&lt;p&gt;Click &lt;code&gt;Copy primary CalDAV address&lt;&#x2F;code&gt;, we will get &lt;code&gt;https:&#x2F;&#x2F;cloud.northmail.ca&#x2F;remote.php&#x2F;dav&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Click &lt;code&gt;Copy iOS&#x2F;macOS CalDAV address&lt;&#x2F;code&gt;, we will get &lt;code&gt;https:&#x2F;&#x2F;cloud.northmail.ca&#x2F;remote.php&#x2F;dav&#x2F;principals&#x2F;users&#x2F;{user_email}&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;add-to-thunderbird-calendar&quot;&gt;Add to Thunderbird Calendar&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.thunderbird.net&#x2F;en-CA&#x2F;&quot;&gt;Thunderbird&lt;&#x2F;a&gt; is an open-source, cross-platform email client. First, click the upper-left button or press Ctrl-3.&lt;&#x2F;p&gt;
&lt;p&gt;Click &lt;code&gt;New Calendar&lt;&#x2F;code&gt; at bottom-left, &lt;code&gt;On The Network&lt;&#x2F;code&gt;. &lt;code&gt;Username&lt;&#x2F;code&gt; is &lt;code&gt;username@northmail.ca&lt;&#x2F;code&gt;, and Location is CalDAV address.&lt;&#x2F;p&gt;
&lt;p&gt;After typing the password, you can select calendars you need. For now (2025-04-27), northmail.ca doesn&#x27;t support &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Multi-factor_authentication&quot;&gt;2FA&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;support.google.com&#x2F;accounts&#x2F;answer&#x2F;185833?hl=en&quot;&gt;app password&lt;&#x2F;a&gt;, which is not the best practice.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;add-to-ios-18-calendar&quot;&gt;Add to iOS 18 Calendar&lt;&#x2F;h4&gt;
&lt;p&gt;In &lt;code&gt;Settings&lt;&#x2F;code&gt;, scroll to bottom and click &lt;code&gt;Apps&lt;&#x2F;code&gt;. Click &lt;code&gt;Calendar&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;Add Account&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;Add CalDAV Account&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;code&gt;Server&lt;&#x2F;code&gt;, paste the iOS CalDAV address. In &lt;code&gt;User Name&lt;&#x2F;code&gt; type the full email address. For now (2025-04-27), northmail.ca doesn&#x27;t support &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Multi-factor_authentication&quot;&gt;2FA&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;support.google.com&#x2F;accounts&#x2F;answer&#x2F;185833?hl=en&quot;&gt;app password&lt;&#x2F;a&gt;, which is not the best practice.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;screenshots&quot;&gt;Screenshots&lt;&#x2F;h4&gt;
&lt;p&gt;I&#x27;ve taken screenshots at all critical steps. To keep the article brief, I moved all screenshots to the end of this article.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;screenshot_northmail.png&quot; alt=&quot;Northmail Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;thunderbird_calendar.png&quot; alt=&quot;Thunderbird Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;thunderbird_add_account.png&quot; alt=&quot;Thunderbird Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;thunderbird_password.png&quot; alt=&quot;Thunderbird Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;thunderbird_calendar_select.png&quot; alt=&quot;Thunderbird Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;ios_setting.jpg&quot; alt=&quot;iOS Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;ios_apps.jpg&quot; alt=&quot;iOS Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;ios_add_account.jpg&quot; alt=&quot;iOS Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;ios_add_other.jpg&quot; alt=&quot;iOS Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;ios_caldav.jpg&quot; alt=&quot;iOS Screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;caldav&#x2F;ios_password.jpg&quot; alt=&quot;iOS Screenshot&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>macOS Sequoia 15.3.1 Share VPN to Windows Laptop</title>
        <published>2025-03-15T00:00:00+00:00</published>
        <updated>2025-03-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/macos-sequoia-1531-share-vpn-to-windows-laptop/"/>
        <id>https://blog.zhenbo.pro/macos-sequoia-1531-share-vpn-to-windows-laptop/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/macos-sequoia-1531-share-vpn-to-windows-laptop/">&lt;h4 id=&quot;tl-dr&quot;&gt;TL;DR&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Layer_2_Tunneling_Protocol&quot;&gt;L2TP&lt;&#x2F;a&gt; is a must. WireGuard and IKEv2 are not supported.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;background&quot;&gt;Background&lt;&#x2F;h4&gt;
&lt;p&gt;I have a personal MacBook running macOS Sequoia 15.3.1, and a Windows 11 laptop managed by my school. While using a public Wi-Fi, I want my MacBook to share the VPN to the Windows laptop.&lt;&#x2F;p&gt;
&lt;p&gt;When MacBook is connected to Wi-Fi, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apple.stackexchange.com&#x2F;questions&#x2F;25878&#x2F;how-can-i-create-an-ad-hoc-connection-from-my-macbook-pro-wifi-connection&quot;&gt;it can only share Internet with Ethernet&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;physical-set-up&quot;&gt;Physical Set-up&lt;&#x2F;h4&gt;
&lt;p&gt;A USB-C hub adapter needs to be connected to macbook to provide an ethernet port. My Windows laptop doesn&#x27;t have an ethernet port,so we need the second USB hub adapter.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;macos-set-up&quot;&gt;macOS Set-up&lt;&#x2F;h4&gt;
&lt;p&gt;Settings -&amp;gt; VPN -&amp;gt; Add VPN Configuration -&amp;gt; L2TP over IPSec&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;macos_vpn&#x2F;add_l2tp.png&quot; alt=&quot;Add VPN&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;L2TP -&amp;gt; Options -&amp;gt; Send all traffic over VPN connection&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;macos_vpn&#x2F;send_all.png&quot; alt=&quot;Send traffic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When the L2TP VPN is added and enabled, we can share the Internet now.&lt;&#x2F;p&gt;
&lt;p&gt;Settings -&amp;gt; General -&amp;gt; Internet Sharing -&amp;gt; More&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;macos_vpn&#x2F;sharing.png&quot; alt=&quot;Internet Sharing&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the config dialog, set &lt;em&gt;Share your connection from&lt;&#x2F;em&gt; to &lt;em&gt;Wi-FI&lt;&#x2F;em&gt;, and enable AX88179A.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2025&#x2F;macos_vpn&#x2F;share_more.png&quot; alt=&quot;Internet Sharing Device&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy!&lt;&#x2F;p&gt;
&lt;h4 id=&quot;epilogue&quot;&gt;Epilogue&lt;&#x2F;h4&gt;
&lt;p&gt;When MacBook is connected to Wi-Fi, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;support.apple.com&#x2F;en-ca&#x2F;guide&#x2F;mac-help&#x2F;mchld53dd2f5&#x2F;mac&quot;&gt;it can also share Internet with Thunderbolt&lt;&#x2F;a&gt;. Sadly, my Windows laptop doesn&#x27;t have one.&lt;&#x2F;p&gt;
&lt;p&gt;iPhone&#x27;s USB tethering is limited. Only the cellular connection can be shared. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;support.apple.com&#x2F;en-ca&#x2F;guide&#x2F;iphone&#x2F;iph45447ca6&#x2F;ios&quot;&gt;link&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Vanilla Android doesn&#x27;t route VPN traffic when tethering &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;protonvpn.com&#x2F;support&#x2F;share-vpn-connection-android-hotspot&quot;&gt;link&lt;&#x2F;a&gt;. Modified Android like AOSP may have an option &lt;strong&gt;Allow clients to use VPNs&lt;&#x2F;strong&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;VPN&#x2F;comments&#x2F;m13d0h&#x2F;comment&#x2F;gqbdist&#x2F;&quot;&gt;link1&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;PixelExperience&#x2F;android-issues&#x2F;issues&#x2F;5932&quot;&gt;link2&lt;&#x2F;a&gt;. After rooting, there are several apps doing so, including &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Mygod&#x2F;VPNHotspot&quot;&gt;VPNHotspot&lt;&#x2F;a&gt;. Sadly (again), my Samsung Tablet at hand &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;androidroot&#x2F;comments&#x2F;1g2xhd9&#x2F;unlock_bootloader_for_newer_samsung_devices_in&#x2F;&quot;&gt;can&#x27;t unlock bootloader&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Compile .NET&#x27;s Test Suite and Run .NET Tests under Wine</title>
        <published>2024-05-03T00:00:00+00:00</published>
        <updated>2024-05-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/compile-nets-test-suite/"/>
        <id>https://blog.zhenbo.pro/compile-nets-test-suite/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/compile-nets-test-suite/">&lt;p&gt;In 2024 March, I had been trying to build .NET test suite and running
them under Wine.
Although I didn&#x27;t gather valuable Wine results yet, I&#x27;d write this
article to record the pitfalls I&#x27;ve met when compiling .NET, and I hope
it may help readers (including me) in future.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;preparation&quot;&gt;Preparation&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;install-visual-studio-2019&quot;&gt;Install Visual Studio 2019&lt;&#x2F;h4&gt;
&lt;p&gt;According to the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;v5.0.18&#x2F;docs&#x2F;workflow&#x2F;requirements&#x2F;windows-requirements.md&quot;&gt;requirements page&lt;&#x2F;a&gt;, Visual Studio 2019 is required
for &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;tree&#x2F;v5.0.18&quot;&gt;v5.0.18&lt;&#x2F;a&gt;. I created a brand new Windows 11 instance, and
installed Visual Studio Community 2019 at
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;visualstudio.microsoft.com&#x2F;vs&#x2F;older-downloads&#x2F;&quot;&gt;https:&#x2F;&#x2F;visualstudio.microsoft.com&#x2F;vs&#x2F;older-downloads&#x2F;&lt;&#x2F;a&gt;. With the
Visual Studio Installer, I can install the required SDKs listed in the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;v5.0.18&#x2F;docs&#x2F;workflow&#x2F;requirements&#x2F;windows-requirements.md&quot;&gt;requirements page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Initially I installed VS 2022, and installed Win10 SDK. However, the
required dependencies couldn&#x27;t be located by the toolchain.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;install-dependencies&quot;&gt;Install Dependencies&lt;&#x2F;h4&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;package-manager&#x2F;winget&#x2F;&quot;&gt;Windows App
Installer&lt;&#x2F;a&gt; is included on Windows 11 and later version of Windows 10. If not, it&#x27;s easy to install it via &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apps.microsoft.com&#x2F;detail&#x2F;9nblggh4nns1?hl=en-us&amp;amp;gl=CA&quot;&gt;Windows App
Store&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Install dependencies with &lt;code&gt;winget&lt;&#x2F;code&gt; is similar to package managers:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;winget install -e --id Python.Python.3.9
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;winget install -e --id Git.Git
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note: Don&#x27;t install LLVM with winget here. As we&#x27;re building
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;tree&#x2F;v5.0.18&quot;&gt;v5.0.18&lt;&#x2F;a&gt;, the manually installed LLVM might be too new.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;build&quot;&gt;Build&lt;&#x2F;h3&gt;
&lt;p&gt;Following the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;v5.0.18&#x2F;docs&#x2F;workflow&#x2F;README.md&quot;&gt;workflow
guide&lt;&#x2F;a&gt;, we can start building now.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;building-coreclr-common-language-runtime&quot;&gt;Building CoreCLR (Common Language Runtime)&lt;&#x2F;h4&gt;
&lt;p&gt;Execute &lt;code&gt;build.cmd -subset clr&lt;&#x2F;code&gt; in cmd. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;v5.0.18&#x2F;docs&#x2F;workflow&#x2F;building&#x2F;coreclr&#x2F;README.md&quot;&gt;Ref&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note: &lt;code&gt;build.cmd&lt;&#x2F;code&gt; is just a wrapper to a PowerShell script
&lt;code&gt;eng&#x2F;build.ps1&lt;&#x2F;code&gt;. Seems that Microsoft had been migrating cmd into ps1.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;building-libraries&quot;&gt;Building Libraries&lt;&#x2F;h4&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;v5.0.18&#x2F;docs&#x2F;workflow&#x2F;building&#x2F;coreclr&#x2F;README.md&quot;&gt;official
guide&lt;&#x2F;a&gt; is messy to me. I executed &lt;code&gt;build.cmd clr+libs -c Release&lt;&#x2F;code&gt; here.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;building-tests-for-coreclr&quot;&gt;Building Tests for CoreCLR&lt;&#x2F;h4&gt;
&lt;p&gt;Following the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dotnet&#x2F;runtime&#x2F;blob&#x2F;v5.0.18&#x2F;docs&#x2F;workflow&#x2F;testing&#x2F;coreclr&#x2F;testing.md&quot;&gt;official guide&lt;&#x2F;a&gt;, we can build all tests by  &lt;code&gt;src\coreclr\build-test.cmd&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is a time consuming step.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;running-all-tests-under-windows&quot;&gt;Running All Tests under Windows&lt;&#x2F;h4&gt;
&lt;p&gt;.NET shipped &lt;code&gt;src\coreclr\tests\runtest.cmd&lt;&#x2F;code&gt; to invoke the test cases. However, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.winehq.org&#x2F;wine&#x2F;wine&#x2F;-&#x2F;tree&#x2F;master&#x2F;programs&#x2F;cmd?ref_type=heads&quot;&gt;Wine&#x27;s CMD implementation&lt;&#x2F;a&gt; lacks basic features to execute this complex cmd script, so I&#x27;ve been finding a method to workaround it.&lt;&#x2F;p&gt;
&lt;p&gt;In Windows CMD, I can invoke all tests by&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;quot;C:\Users\li\Documents\runtime\.dotnet\dotnet.exe&amp;quot; msbuild C:\Users\li\Documents\runtime\src\coreclr\tests\src\runtest.proj &#x2F;p:Runtests=true &#x2F;clp:showcommandline 
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;running-tests-under-wine&quot;&gt;Running Tests under Wine&lt;&#x2F;h4&gt;
&lt;p&gt;I copied the whole directory from my Windows 11 Guest into my Fedora host, stored at &lt;code&gt;&#x2F;home&#x2F;lizhenbo&#x2F;winp&#x2F;runtime&lt;&#x2F;code&gt;. Wine mapped it to &lt;code&gt;Z:\home\lizhenbo\winp\runtime&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;First, I need to set &lt;code&gt;CORE_ROOT&lt;&#x2F;code&gt;. Wine will pass the shell environment (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;askubuntu.com&#x2F;a&#x2F;672625&#x2F;134171&quot;&gt;Ref&lt;&#x2F;a&gt;), so I need&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;export CORE_ROOT=Z:\\home\\lizhenbo\\winp\\runtime\\artifacts\\tests\\coreclr\\Windows_NT.x64.Debug\\Tests\\Core_Root
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then I can run the test suite&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;wine &amp;quot;Z:\\home\\lizhenbo\\winp\\runtime\\.dotnet\\dotnet.exe&amp;quot; msbuild Z:\\home\\lizhenbo\\winp\\runtime\\src\\coreclr\\tests\\src\\runtest.proj &#x2F;p:Runtests=true &#x2F;clp:showcommandline 
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, the test crased just a few seconds later. I have no ideas yet&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Unhandled exception: page fault on read access to 0x0000000000000000 in 64-bit code (0x006fffffe62475).
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Backtrace:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;=&amp;gt;0 0x006fffffe62475 wcslen+0x1f(str=0x00000000000000000) [&#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;wine64&#x2F;..&#x2F;wine&#x2F;dlls&#x2F;ntdll&#x2F;wcstring.c:218] in ntdll (0x007ffffe27e570)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1 0x006fffffdedd1b strdupW+0x18(str=0x00000000000000000) [&#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;wine64&#x2F;..&#x2F;wine&#x2F;dlls&#x2F;ntdll&#x2F;actctx.c:660] in ntdll (0x007ffffe27e5b0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  2 0x006fffffdfd4aa RtlCreateActivationContext+0x237(handle=00007FFFFE27E708, ptr=00007FFFFE27E758) [&#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;wine64&#x2F;..&#x2F;wine&#x2F;dlls&#x2F;ntdll&#x2F;actctx.c:5292] in ntdll (0x007ffffe27e640)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  3 0x006fffff8bee72 CreateActCtxW+0x84(ctx=00007FFFFE27E758) [&#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;wine64&#x2F;..&#x2F;wine&#x2F;dlls&#x2F;kernelbase&#x2F;loader.c:1157] in kernelbase (0x007ffffe27e720)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  4 0x0000014002f362 in corerun (+0x2f362) (0x007ffffe27ffa0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  12 0x006fffffcc1cc6 BaseThreadInitThunk+0x20(unknown=0, entry=000000014000B160, arg=000000007FFD0000) [&#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;wine64&#x2F;..&#x2F;wine&#x2F;dlls&#x2F;kernel32&#x2F;thread.c:61] in kernel32 (0x007ffffe27ffa0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  13 0x006fffffe4c0a7 in ntdll (+0x6c0a7) (0000000000000000)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;0x006fffffe62475 wcslen+0x1f [&#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;wine64&#x2F;..&#x2F;wine&#x2F;dlls&#x2F;ntdll&#x2F;wcstring.c:218] in ntdll: movzxw (%rax), %eax
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;218	    while (*s) s++;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>LeetCode 2948 题解 (又是 Sliding Window)</title>
        <published>2024-03-12T00:00:00+00:00</published>
        <updated>2024-03-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/leetcode-2948-solution-yet-another-sliding-window/"/>
        <id>https://blog.zhenbo.pro/leetcode-2948-solution-yet-another-sliding-window/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/leetcode-2948-solution-yet-another-sliding-window/">&lt;p&gt;题目链接 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;leetcode.com&#x2F;problems&#x2F;apply-operations-to-maximize-frequency-score&#x2F;description&#x2F;&quot;&gt;https:&#x2F;&#x2F;leetcode.com&#x2F;problems&#x2F;apply-operations-to-maximize-frequency-score&#x2F;description&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ti-mu-miao-shu&quot;&gt;题目描述&lt;&#x2F;h3&gt;
&lt;p&gt;在一个一维坐标轴上有 N 个村庄 (0-indexed)，坐标 &lt;code&gt;A[i]&lt;&#x2F;code&gt; 为正整数。  给定最大交通成本 &lt;code&gt;k&lt;&#x2F;code&gt;.&lt;br &#x2F;&gt;
我们准备设立一个邮局。选择一个区间 &lt;code&gt;[L,R]&lt;&#x2F;code&gt; 和邮局坐标 &lt;code&gt;x&lt;&#x2F;code&gt;, 定义交通成本 $c=\sum_{i=L}^{R}|x-A_i|$ 。 在满足 $c&amp;lt;=k$ 的前提下，找到最大的区间。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ti-mu-fen-xi&quot;&gt;题目分析&lt;&#x2F;h3&gt;
&lt;p&gt;我们先考虑一个子问题：对于给定的区间 &lt;code&gt;[L,R]&lt;&#x2F;code&gt;，我们如何求出最低的交通成本。对此，我们先引入三个引理：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;$A_L &amp;lt;= x_{opt} &amp;lt;= A_R$  (Too Trivial)&lt;&#x2F;li&gt;
&lt;li&gt;最优的邮局坐标一定和某个村庄(M-th)重合，即 $x_{opt}=A_M$.&lt;&#x2F;li&gt;
&lt;li&gt;如果区间长度为奇数，则 $M_{opt}$ 就是最中心的一个村庄。如果为偶数，那最中间的两个村庄都是最优的选择.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;在此基础上，我们可以通过如下变换，利用前缀和，在常数时间内求出特定区间的交通成本 $c$&lt;&#x2F;p&gt;
&lt;p&gt;$$
\begin{aligned}
c &amp;amp;= c_L + c_R \
c_L &amp;amp;= \sum_{i=L}^{M} A_M - A_i \
&amp;amp;= (M-L+1)A_M - \sum_{i=L}^{M}A_i \
&amp;amp;= (M-L+1)A_M - ( \sum_{i=0}^{M}A_i  - \sum_{i=0}^{L-1} A_i)\
c_R &amp;amp;= \sum_{i=M}^{R} A_i - A_M \
&amp;amp;= \sum_{i=0}^{R} A_i - \sum_{i=0}^{M-1}A_i - (R-M+1)A_M
\end{aligned}
$$&lt;&#x2F;p&gt;
&lt;h3 id=&quot;suan-fa-shi-xian&quot;&gt;算法实现&lt;&#x2F;h3&gt;
&lt;p&gt;有了如上的分析，实现 Sliding Window 算法就非常容易了。将所有坐标排序后，先求出前缀和 $P_V = \sum_{i=0}^{V} A_i$&lt;&#x2F;p&gt;
&lt;p&gt;我们将初始的 Window 设置为 $L=R=0$. 显然，此时 $c=0$,是一种合法的情况。按照通常的 Sliding Window 的做法，我们不断移动右边界 $R$，在 $c&amp;gt;k$ 时移动左边界 $L$. 我们的贪心算法会找出最优解。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yin-li-zheng-ming&quot;&gt;引理证明&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;lemma-2&quot;&gt;Lemma 2&lt;&#x2F;h4&gt;
&lt;p&gt;使用反证法，假设 $A_M &amp;lt; x^* &amp;lt; A_{M+1} $, 此时交通成本&lt;br &#x2F;&gt;
$$
\begin{aligned}
c &amp;amp;= c_L + c_R \
&amp;amp;= \sum_{i=L}^{M}( x^* - A_i )+ \sum_{i=M+1}^{R} (A_i - x^*)\
\end{aligned}
$$&lt;&#x2F;p&gt;
&lt;p&gt;我们将邮局向左移动至 $A_M = x&#x27; &amp;lt; x^* &amp;lt; A_{M+1} $,  $x^*-x&#x27;=d&amp;gt;0$&lt;&#x2F;p&gt;
&lt;p&gt;左侧的 &lt;code&gt;(M-L+1)&lt;&#x2F;code&gt; 个村庄的成本会下降  &lt;code&gt;(M-L+1)d&lt;&#x2F;code&gt;,  而右侧的 &lt;code&gt;(R-M)&lt;&#x2F;code&gt; 个村庄的成本会上升 &lt;code&gt;(R-M)d&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;这样，我们分三种情况讨论：　　&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;如果 $x^*$ 左侧的村庄更多，即 $M-L+1 &amp;gt; R-M$,　则 $x&#x27;＝A_M$　优于 $x^*$&lt;&#x2F;li&gt;
&lt;li&gt;如果两侧的村庄一样多，移动至 $x&#x27;＝A_M$ 不会得到更差的结果　　&lt;&#x2F;li&gt;
&lt;li&gt;如果右侧的村庄更多，易证 $x&#x27;&#x27;＝A_{M+1}$　优于 $x^*$&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Lemma 2 得证。　　&lt;&#x2F;p&gt;
&lt;h4 id=&quot;lemma-3&quot;&gt;Lemma 3&lt;&#x2F;h4&gt;
&lt;p&gt;可以沿用类似　Lemma 2　的计算方法。区间长为奇数时，根据引理，&lt;code&gt;M = (R-L) &#x2F; 2 + L&lt;&#x2F;code&gt;。 如果我们要向左移动到 $A_{M-1}$, $A_M - A_{M-1}=d&amp;gt;0$。 左侧的村庄 &lt;code&gt;[L, M-1]&lt;&#x2F;code&gt; 的成本会下降，右侧 &lt;code&gt;[M+1, R]&lt;&#x2F;code&gt; 的成本会上升。这两部分抵消后，村庄 M 的交通成本会增加 &lt;code&gt;d&lt;&#x2F;code&gt;，得到一个更差的结果。&lt;&#x2F;p&gt;
&lt;p&gt;如果区间长为偶数，用同样的方法可以得到，在最中间的两个村庄之间移动邮局的 $\Delta=0$.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h3&gt;
&lt;p&gt;这道题几个月前被丢到了我的 list上。我最开始没有意识到 Lemma 3, 而是试图维护一个三元数组 &lt;code&gt;(L, M, R)&lt;&#x2F;code&gt;，在Sliding的过程中动态调整邮局的位置。顺着这个思路想下去，就是越想越偏离正解。  看了答案以后，意识到最优的邮局地点是固定的，也学到了借助前缀和来求特定区间的交通成本，从而避免了手动维护当前Window的成本。&lt;&#x2F;p&gt;
&lt;p&gt;在写题解的过程中，发现 Lemmy 2&amp;amp;3 的证明过程其实可以合并。我之前做这类题目时，对证明过程理解不到位。写这篇题解让我的认识深入了很多，也发现完全可以用很简明的方式证明。&lt;&#x2F;p&gt;
&lt;p&gt;LeetCode 原题里没有提到坐标，我借用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.luogu.com.cn&#x2F;problem&#x2F;solution&#x2F;P4767&quot;&gt;IOI2000 邮局&lt;&#x2F;a&gt; 的题目背景，感觉直观了不少。&lt;&#x2F;p&gt;
&lt;p&gt;{% include mathjax.html %}&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>TopCoder SRM 848 AlternateParity 题解</title>
        <published>2023-08-06T00:00:00+00:00</published>
        <updated>2023-08-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/srm-848-alternateparity-solution/"/>
        <id>https://blog.zhenbo.pro/srm-848-alternateparity-solution/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/srm-848-alternateparity-solution/">&lt;p&gt;2023年8月3日，我参加了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;community.topcoder.com&#x2F;stat?c=round_overview&amp;amp;rd=19668&quot;&gt;TopCoder SRM 848&lt;&#x2F;a&gt;，结果 Div I 第一题就没做出来，零分完赛。赛后看了&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;community.topcoder.com&#x2F;stat?c=problem_solution&amp;amp;cr=90029704&amp;amp;rd=19668&amp;amp;pm=18085&quot;&gt;其他选手的代码&lt;&#x2F;a&gt;，意识到这其实是一个比较基础的组合数学问题。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;alternate-parity-ti-mu-miao-shu&quot;&gt;Alternate Parity &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;community.topcoder.com&#x2F;stat?c=problem_statement&amp;amp;pm=18085&amp;amp;rd=19668&amp;amp;rm=&amp;amp;cr=90029704&quot;&gt;题目描述&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;相比于原有的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;community.topcoder.com&#x2F;stat?c=problem_statement&amp;amp;pm=18085&amp;amp;rd=19668&amp;amp;rm=&amp;amp;cr=90029704&quot;&gt;题目描述&lt;&#x2F;a&gt;， 我决定将其转换为如下形式： 给定两个正整数 &lt;code&gt;X&lt;&#x2F;code&gt; &lt;code&gt;L&lt;&#x2F;code&gt;, 生成一个长度为 &lt;code&gt;L&lt;&#x2F;code&gt; 的数列，要求：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;数列严格单调递增&lt;&#x2F;li&gt;
&lt;li&gt;相邻的两个数字奇偶性不同&lt;&#x2F;li&gt;
&lt;li&gt;数列的每个元素为正整数&lt;&#x2F;li&gt;
&lt;li&gt;数列最后一个元素 $a_L=X$&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;求所有合法的序列数量，并对某大质数取模。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-da&quot;&gt;解答&lt;&#x2F;h2&gt;
&lt;p&gt;可以通过若干步变换，将它转化为一个&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stars_and_bars_(combinatorics)&quot;&gt;隔板法（Stars and bars）&lt;&#x2F;a&gt;问题。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a1-de-qi-ou-xing&quot;&gt;a1 的奇偶性&lt;&#x2F;h3&gt;
&lt;p&gt;对 X 和 L 的奇偶性分类讨论，一共有四种情况。易证 $(X+L)%2==0$ &amp;lt;=&amp;gt; $a_1%2==0$&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gou-jian-fu-zhu-xu-lie-b&quot;&gt;构建辅助序列 b&lt;&#x2F;h3&gt;
&lt;p&gt;数列 a 要求相邻的两个数字奇偶性不同，也就是相邻的元素的差为基数。通过求差值，我们可以定义一个辅助数列 &lt;code&gt;b&lt;&#x2F;code&gt;，使序列中的每一个元素都为偶数。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;a1-wei-qi-shu&quot;&gt;a1 为奇数&lt;&#x2F;h4&gt;
&lt;p&gt;$$
b =  \left{ \
\begin{aligned}
&amp;amp; b_1 = a_1 - 1 \
&amp;amp; b_i = a_i - a_{i-1} - 1
\end{aligned}
\right.
$$&lt;&#x2F;p&gt;
&lt;p&gt;$\sum_{i=1}^{L}b_i = a_L-L = X-L$&lt;&#x2F;p&gt;
&lt;h4 id=&quot;a1-wei-ou-shu&quot;&gt;a1 为偶数&lt;&#x2F;h4&gt;
&lt;p&gt;$$
b =  \left{ \
\begin{aligned}
&amp;amp; b_1 = a_1 - 2 \
&amp;amp; b_i = a_i - a_{i-1} - 1
\end{aligned}
\right.
$$&lt;br &#x2F;&gt;
$\sum_{i=1}^{L}b_i = a_L-L -1= X-L-1$&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shu-lie-bde-xing-zhi&quot;&gt;数列b的性质&lt;&#x2F;h4&gt;
&lt;p&gt;无论 $a_1$ 的奇偶性，数列b都符合：&lt;br &#x2F;&gt;
$b_i % 2 == 0$&lt;br &#x2F;&gt;
$b_i \geq 0$&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gou-jian-fu-zhu-shu-lie-c-yi-bian-yong-ge-ban-fa-qiu-jie&quot;&gt;构建辅助数列 c 以便用隔板法求解&lt;&#x2F;h3&gt;
&lt;p&gt;定义 $c_i = \dfrac{b_i}{2} + 1$, 则 $$c_i \in \mathbb{R}+$$&lt;&#x2F;p&gt;
&lt;p&gt;$$\sum_{i=1}^{L}c_i = L + \dfrac{\sum_{i=1}^{L}b_i}{2}$$&lt;&#x2F;p&gt;
&lt;p&gt;$$
\sum_{i=1}^{L}c_i  =  \left{ \
\begin{aligned}
&amp;amp; \dfrac{X+L}{2},   \text{same_parity} \
&amp;amp; \dfrac{X+L-1}{2},   \text{diff_parity}
\end{aligned}
\right.
$$&lt;&#x2F;p&gt;
&lt;p&gt;求数列c一共有多少种合法的情况，正是 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stars_and_bars_(combinatorics)&quot;&gt;隔板法&lt;&#x2F;a&gt; 的标准模型。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dui-zu-he-shu-qu-mo&quot;&gt;对组合数取模&lt;&#x2F;h3&gt;
&lt;p&gt;这个基础问题触及了我的知识盲区。查到的两份资料（&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xienaoban.github.io&#x2F;posts&#x2F;36480.html#n%E7%9B%B8%E5%AF%B9%E5%B0%8F%E6%96%B9%E4%BE%BF%E6%89%93%E8%A1%A8p%E5%8F%AF%E4%BB%A5%E5%BE%88%E5%A4%A7p%E8%A6%81%E6%B1%82%E4%B8%BA%E7%B4%A0%E6%95%B0&quot;&gt;A&lt;&#x2F;a&gt;，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cp-algorithms.com&#x2F;algebra&#x2F;module-inverse.html#finding-the-modular-inverse-using-binary-exponentiation&quot;&gt;B&lt;&#x2F;a&gt;）提到，可以使用&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fermat%27s_little_theorem&quot;&gt;费马小定理 Fermat&#x27;s little theorem&lt;&#x2F;a&gt; 解决这一问题。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bi-sai-fu-pan&quot;&gt;比赛复盘&lt;&#x2F;h2&gt;
&lt;p&gt;比赛时，我先写出了朴素的 DP 代码 &lt;code&gt;f[L,X] = f[L-1,X-1] + f[L-1,X-3] +...&lt;&#x2F;code&gt;，并把这个表格显示了出来，试图找它的规律。初看起来，这和杨辉三角的形态比较类似，但我没有顺着这个方向思考下去，而试图去优化 DP 方程。赛后看到其他选手的代码，才发现是组合数学的问题。&lt;&#x2F;p&gt;
&lt;p&gt;我的代码存档: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;Endle&#x2F;1381aa72eb07fe1c92a5de2ff1cb83f6&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;Endle&#x2F;1381aa72eb07fe1c92a5de2ff1cb83f6&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
抢在&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.topcoder.com&#x2F;blog&#x2F;tag&#x2F;srm&#x2F;&quot;&gt;官方题解&lt;&#x2F;a&gt;发布前写完了本文，希望搜索引擎们能给我送一些流量。&lt;&#x2F;p&gt;
&lt;p&gt;{% include mathjax.html %}&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Codeforces 885A Vika and Her Friends (1848A) 题解</title>
        <published>2023-07-22T00:00:00+00:00</published>
        <updated>2023-07-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/codeforces-885a-vika-and-her-friends-1848a-solution/"/>
        <id>https://blog.zhenbo.pro/codeforces-885a-vika-and-her-friends-1848a-solution/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/codeforces-885a-vika-and-her-friends-1848a-solution/">&lt;p&gt;2023年7月16日，我参加了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;118293&quot;&gt;Codeforces 885&lt;&#x2F;a&gt;，没想到 A 题就碰了钉子。从赛后的&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;118333&quot;&gt;官方题解&lt;&#x2F;a&gt; 看，这道题分析起来有难度，但最终的结论非常简单。除了单纯写这篇题解，我也想写一下我比赛时的心路历程，看一看我落入的思维陷阱。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ti-mu-miao-shu&quot;&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;contest&#x2F;1848&#x2F;problem&#x2F;A&quot;&gt;题目描述&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Vika 和她的 k 个朋友们散布在一个有限大的国际棋盘上。在每个回合 t，Vika 和朋友都 &lt;strong&gt;必须同时&lt;&#x2F;strong&gt;   移动到相邻的四个格子里。虽然每次移动是同时的，但朋友们可以看到 Vika 的决策后再行动。&lt;&#x2F;p&gt;
&lt;p&gt;给定足够长的时间，问 Vika 是否可以躲开她的朋友们。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-da&quot;&gt;解答&lt;&#x2F;h2&gt;
&lt;p&gt;这道题存在这个性质：&lt;&#x2F;p&gt;
&lt;p&gt;Vika is safe &amp;lt;=&amp;gt; None of Vika&#x27;s friends is on a cell of same colour as Vika&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zheng-ming&quot;&gt;证明 (&amp;lt;=)&lt;&#x2F;h3&gt;
&lt;p&gt;每一次移动时，每个人脚下的颜色都必然翻转。因此，如果 &lt;strong&gt;None of Vika&#x27;s friends is on a cell of same colour as Vika&lt;&#x2F;strong&gt; 在时刻 &lt;code&gt;i&lt;&#x2F;code&gt; 成立，那也会在任何一个时刻成立。既然颜色不重合，也就抓不到 Vika。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zheng-ming-1&quot;&gt;证明 (=&amp;gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;我的证明和官方题解略有（措辞上的）区别。既然我们要证明 &lt;strong&gt;Vika is safe =&amp;gt; None of Vika&#x27;s friends is on a cell of same colour as Vika&lt;&#x2F;strong&gt;，那我们就可以证明其逆否命题(Contraposition):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Alice, one of Vika&#x27;s friends is on a cell of same colour as Vika =&amp;gt; Vika is not safe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;分情况讨论如下：&lt;&#x2F;p&gt;
&lt;h4 id=&quot;vika-he-alice-zai-tong-yi-xing-huo-tong-yi-lie-a&quot;&gt;Vika 和 Alice 在同一行或同一列(A)&lt;&#x2F;h4&gt;
&lt;p&gt;如果 Alice 和 Vika 在同一横行，也就是 Vika=&lt;code&gt;(x,y)&lt;&#x2F;code&gt;, Alice=&lt;code&gt;(p,y)&lt;&#x2F;code&gt;. 不失一般性，令 &lt;code&gt;p&amp;lt;x&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;vika-heng-xiang-yi-dong-a1&quot;&gt;Vika 横向移动(A1)&lt;&#x2F;h5&gt;
&lt;p&gt;如果 Vika 向右移动到 &lt;code&gt;(x+1,y)&lt;&#x2F;code&gt;, 那 Alice 同时移动到 &lt;code&gt;(p+1),y&lt;&#x2F;code&gt;. 两人间距离不变。如果重复这一步骤，Vika 会先撞墙。&lt;&#x2F;p&gt;
&lt;p&gt;如果 Vika 向左移动 &lt;code&gt;(x-1,y)&lt;&#x2F;code&gt;, 因为两人初始位置同色，所以间隔至少为2. Alice 向右移动，&lt;code&gt;p+1&amp;lt;=x-1&lt;&#x2F;code&gt;. 两人间距缩小，肯定不会错开。&lt;&#x2F;p&gt;
&lt;h5 id=&quot;vika-zong-xiang-yi-dong-a2&quot;&gt;Vika 纵向移动(A2)&lt;&#x2F;h5&gt;
&lt;p&gt;如果 Vika 向右移动到 &lt;code&gt;(x+1,y)&lt;&#x2F;code&gt;，那 Alice 移动到 &lt;code&gt;(p+1),y&lt;&#x2F;code&gt;. 两人间的 taxicab distance 保持不变，但转换为了不在同一行或同一列的情况&lt;&#x2F;p&gt;
&lt;h4 id=&quot;vika-he-alice-de-xing-lie-jun-bu-xiang-tong-b&quot;&gt;Vika 和 Alice 的行列均不相同(B)&lt;&#x2F;h4&gt;
&lt;p&gt;此时 Vika=&lt;code&gt;(x,y)&lt;&#x2F;code&gt;, Alice=&lt;code&gt;(p,q)&lt;&#x2F;code&gt;. 我们认为两人的位置构成了一个矩形 S，且 S 包含了两人当前的位置。&lt;&#x2F;p&gt;
&lt;h5 id=&quot;vika-xia-yi-bu-de-wei-zhi-zai-s-wai-b1&quot;&gt;Vika 下一步的位置在 S 外(B1)&lt;&#x2F;h5&gt;
&lt;p&gt;那 Alice 可以同向移动。这样，矩形 S 向棋盘边界平移了一格。&lt;&#x2F;p&gt;
&lt;h5 id=&quot;vika-xia-yi-bu-de-wei-zhi-zai-s-nei-b2&quot;&gt;Vika 下一步的位置在 S 内(B2)&lt;&#x2F;h5&gt;
&lt;p&gt;Alice 下一步的位置同样也在 S 内。如果 Vika 横向移动，Alice 就纵向移动，反之亦然。这样的回合后，两人的 taxicab distance 会下降2. 因为最初色块相同，所以 taxicab distance 在任何时刻，都是2的倍数（包含0）&lt;&#x2F;p&gt;
&lt;h4 id=&quot;ju-chi-hui-bu-duan-xia-jiang&quot;&gt;距离会不断下降&lt;&#x2F;h4&gt;
&lt;p&gt;Vika 重复 B1 可以保持两人距离相同，但因为棋盘有界，Vika 会先撞到边缘，导致 Vika 不得不采取行动 B2，缩短两人距离。&lt;&#x2F;p&gt;
&lt;p&gt;如果 Vika 采取 A1，那也会不可避免地先撞墙。如果 Vika 执行 A2，会将局面转化为 B，但两人距离并没有拉长，最终也会因为 B2 而被 Alice 抓住。&lt;&#x2F;p&gt;
&lt;p&gt;现在 &lt;code&gt;(&amp;lt;=)&lt;&#x2F;code&gt;  和  &lt;code&gt;(=&amp;gt;)&lt;&#x2F;code&gt; 都已经证明完毕，原命题得证。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cuo-wu-zong-jie&quot;&gt;错误总结&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;vika-he-peng-you-bi-xu-yi-dong&quot;&gt;Vika 和朋友必须移动&lt;&#x2F;h3&gt;
&lt;p&gt;我不假思索地采纳了一个推论：如果朋友的数量多于棋盘的列数，那就可以朋友们站成一排，像国际象棋的兵线一样推进，Vika 也就无法逃生了。&lt;&#x2F;p&gt;
&lt;p&gt;但是，这个判断是错误的。如果所有的朋友都站在白格上，那无论怎么排，也是无法拉成一条直线的。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ru-he-li-jie-tong-shi-xing-dong&quot;&gt;如何理解同时行动&lt;&#x2F;h3&gt;
&lt;p&gt;在比赛时，我没能理解同时行动的含义。所以，我想当然地认为，如果出现了下图中的这种“困兽斗”的情况，Vika 是无法离开的。但我们在 &lt;code&gt;(&amp;lt;=)&lt;&#x2F;code&gt; 里证明，这种情况下，Vika 可以躲开所有的朋友。甚至于，如果 Vika 主动想和朋友们碰面也是做不到的。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2023&#x2F;codeforces&#x2F;cf885A.png&quot; alt=&quot;An example of Vika being trapped in a corner&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;因为这两个错误的推论，我没有对 Vika 和朋友们脚下的色块深入思考，也就没机会自己找出文首的命题。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zheng-ming-zhui-zhu-bu-hui-wu-xian-zhi-di-chi-xu&quot;&gt;证明追逐不会无限制地持续&lt;&#x2F;h3&gt;
&lt;p&gt;在写本篇题解时，我本来想用下面一段话来证明 &lt;code&gt;(=&amp;gt;)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;虽然 Alice 和 Vika 要同时行动，但 Alice 可以看到 Vika 的决策。这样，如果 Vika 选择远离 Alice，Alice 可以只同向行动，就能保持两人的taxicab distance不变。因为地图是有界的，Vika 总会被 Alice 追到。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这句话看起来很符合直觉，但是我写下来的时候，发现这省略了一个问题：如果 Alice 一直在追逐 Vika，但无法缩短两人的 taxicab distance 怎么办？我只好花了不少时间，换了一种论证的方式。当然，这样的话又有些啰嗦。如果我一开始就使用矩形 S 来论证，可能就会精简一些。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>为 Rust 项目的 Github Action 启用 Sccache - 2023 年的新办法</title>
        <published>2023-01-27T00:00:00+00:00</published>
        <updated>2023-01-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/rust-github-action-enable-sccache/"/>
        <id>https://blog.zhenbo.pro/rust-github-action-enable-sccache/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/rust-github-action-enable-sccache/">&lt;p&gt;受到 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xuanwo.io&#x2F;reports&#x2F;2023-04&#x2F;&quot;&gt;Xuanwo 的博客&lt;&#x2F;a&gt; 的鼓动，我决定给自己的 Rust 项目
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&quot;&gt;fireSeqSearch&lt;&#x2F;a&gt; 加上 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;sccache&quot;&gt;Sccache&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;sccache-gai-shu&quot;&gt;sccache 概述&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;sccache&quot;&gt;Sccache&lt;&#x2F;a&gt; 可以简单看成 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ccache.dev&#x2F;&quot;&gt;ccache&lt;&#x2F;a&gt; 的 Rust
版。它有很多激动人心的特性，但那不在本文的范畴里。我在本地编译原有的项目时，只需运行
&lt;code&gt;RUSTC_WRAPPER=&quot;sccache&quot; cargo build&lt;&#x2F;code&gt; 即可启用 sccache。默认的缓存路径为
&lt;code&gt;~&#x2F;.cache&#x2F;sccache&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;huan-cun-cargo-registry&quot;&gt;缓存 Cargo Registry&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&#x2F;blob&#x2F;6e760731e1f91df5f647f0bd551aceb5d83bfcbb&#x2F;.github&#x2F;workflows&#x2F;rust.yml#L35&quot;&gt;我原先的代码里&lt;&#x2F;a&gt; 缓存了 &lt;code&gt;~&#x2F;.cargo&#x2F;registry&lt;&#x2F;code&gt;，但这只是编译前的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;cargo&#x2F;reference&#x2F;registries.html&quot;&gt;crate.io registry&lt;&#x2F;a&gt;, 而不是编译产生的目标代码，加速 CI 效果有限。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;2022-nian-de-ben-ban-fa&quot;&gt;2022 年的笨办法&lt;&#x2F;h4&gt;
&lt;p&gt;最开始，我仅仅是将 &lt;code&gt;~&#x2F;.cache&#x2F;sccache&lt;&#x2F;code&gt;
添加到了缓存路径里。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&#x2F;blob&#x2F;0e26622b8d794a2dbc83afaeb6fca1fa48cb7d01&#x2F;.github&#x2F;workflows&#x2F;rust.yml&quot;&gt;代码如下&lt;&#x2F;a&gt;：&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      - name: Cache cargo registry and sccache
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        uses: actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        continue-on-error: false
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        with:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          path: |
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            ~&#x2F;.cargo&#x2F;registry
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            ~&#x2F;.cache&#x2F;sccache
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&#x2F;commit&#x2F;0e26622b8d794a2dbc83afaeb6fca1fa48cb7d01&quot;&gt;我的这个改动&lt;&#x2F;a&gt;
误解了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;sccache-action&quot;&gt;sccache-action&lt;&#x2F;a&gt;
的用法，并没有发挥它真正的强大之处。使用笨办法，大概 600M 的 &lt;code&gt;~&#x2F;.cache&#x2F;sccache&lt;&#x2F;code&gt; 都会被 GitHub Action
缓存。随着时间推移，缓存文件的体积会膨胀，在 &lt;code&gt;restore&#x2F;save&lt;&#x2F;code&gt;
时浪费时间。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;Endle&#x2F;efe07ca76b6e148c4682e101ff9a6731&quot;&gt;我自己的经验&lt;&#x2F;a&gt;，在 macOS instance
上，接收 150M 的缓存就用了 3 分钟 （当然，这也与服务器实际运行情况有关）。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;2023-nian-de-xin-ban-fa&quot;&gt;2023 年的新办法&lt;&#x2F;h4&gt;
&lt;p&gt;感谢 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xuanwo.io&#x2F;&quot;&gt;Xuanwo&lt;&#x2F;a&gt; 本人的答疑，我将 &lt;code&gt;~&#x2F;.cache&#x2F;sccache&lt;&#x2F;code&gt; 移出了
&lt;code&gt;actions&#x2F;cache@v3&lt;&#x2F;code&gt;。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&#x2F;commit&#x2F;969950f7fdb794eab1b57880d0334b5285bb404f&quot;&gt;改动&lt;&#x2F;a&gt;后的&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&#x2F;blob&#x2F;969950f7fdb794eab1b57880d0334b5285bb404f&#x2F;.github&#x2F;workflows&#x2F;rust.yml&quot;&gt;代码如下&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;env:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  CARGO_TERM_COLOR: always
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  RUSTC_WRAPPER: &amp;quot;sccache&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  SCCACHE_GHA_ENABLED: &amp;quot;true&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;--- snip ---
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      - name: Run sccache-cache
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        uses: Xuanwo&#x2F;sccache-action@c94e27bef21ab3fb4a5152c8a878c53262b4abb0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        with:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          version: &amp;quot;v0.4.0-pre.6&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      - name: Cache cargo registry
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        uses: actions&#x2F;cache@v3
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        with:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          path: |
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            ~&#x2F;.cargo&#x2F;registry
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;zui-zhi-jie-de-you-dian-xi-li-du-huan-cun&quot;&gt;最直接的优点：细粒度缓存&lt;&#x2F;h4&gt;
&lt;p&gt;相比旧办法，使用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;sccache-action&quot;&gt;sccache-action&lt;&#x2F;a&gt;
并不会将 &lt;code&gt;~&#x2F;.cache&#x2F;sccache&lt;&#x2F;code&gt; 作为整体上传到 GitHub Action Cache 里，而是会细粒度地将每个对象上传。&lt;&#x2F;p&gt;
&lt;p&gt;在
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&#x2F;actions&#x2F;caches&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&#x2F;actions&#x2F;caches&lt;&#x2F;a&gt; 里可以看到，大小从几K到几百K，乃至20M的对象被存入了 GHA。这很好地避免了旧方法中缓存文件夹膨胀的问题。&lt;&#x2F;p&gt;
&lt;p&gt;![Screenshot of Github Action Cache](&#x2F;images&#x2F;2023&#x2F;rust&#x2F;Screenshot 2023-01-28 GitHub Action Cache.png)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h4&gt;
&lt;p&gt;在撰写完本文大概两周后，发现 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xxchan.me&#x2F;about&#x2F;&quot;&gt;xxchan&lt;&#x2F;a&gt; 写了一篇博文 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xxchan.me&#x2F;cs&#x2F;2023&#x2F;02&#x2F;11&#x2F;optimize-rust-comptime.html&quot;&gt;我如何动动小手就让 CI 时间减少了 10 分钟&lt;&#x2F;a&gt; 比本文更详细、更完整。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>从零写一个 Beancount CSV Importer</title>
        <published>2023-01-21T00:00:00+00:00</published>
        <updated>2023-01-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/write-beancount-csv-importer-from-scratch/"/>
        <id>https://blog.zhenbo.pro/write-beancount-csv-importer-from-scratch/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/write-beancount-csv-importer-from-scratch/">&lt;h4 id=&quot;yuan-you&quot;&gt;缘由&lt;&#x2F;h4&gt;
&lt;p&gt;在 2020 年，我曾经尝试过使用 Beancount 进行记账。但当时，我每一笔开销都是手动记录的。在最初的兴趣消退后，就再没有兴致去记账了。最近，感觉需要统计一下自己的开销比例，就重新翻出了 Beancount。当然，我要吸取上次的教训，选择直接导入信用卡账单，而非手动记录开销。最终的结果，是我在除夕忙了一整天，得到了一个初步可用的 python 程序，和这篇博客文章。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wei-he-zhuan-xie-ben-wen&quot;&gt;为何撰写本文&lt;&#x2F;h4&gt;
&lt;p&gt;Amex CA 可以直接从网页上下载 CSV 格式的账单，内容非常简单，脱敏示例如下&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;12&#x2F;14&#x2F;2022,&amp;quot;Reference: 003&amp;quot;,&amp;quot; 44.05&amp;quot;,&amp;quot;SHOPPERS DRUG MART&amp;quot;,&amp;quot;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;12&#x2F;14&#x2F;2022,&amp;quot;Reference: 002&amp;quot;,&amp;quot; 6.76&amp;quot;,&amp;quot;SOBEYS&amp;quot;,&amp;quot;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;12&#x2F;16&#x2F;2022,&amp;quot;Reference: 001&amp;quot;,&amp;quot; 12.99&amp;quot;,&amp;quot;MEMBERSHIP FEE INSTALLMENT&amp;quot;,&amp;quot;&amp;quot;,
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可是，我在网上找了很多圈，处理这种 CSV 好像都是 too trivial case, 包括&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;beancount.github.io&#x2F;docs&#x2F;importing_external_data.html&quot;&gt;官方文档&lt;&#x2F;a&gt;在内，对这个环节的描述都是凤毛麟角。&lt;&#x2F;p&gt;
&lt;p&gt;文章看了很多，工具也尝试了好几个，但最后，还是要自己撸代码，写一个 Importer。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;dai-ma-shi-xian&quot;&gt;代码实现&lt;&#x2F;h4&gt;
&lt;p&gt;参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mterwill.com&#x2F;&quot;&gt;Matt Terwilliger&lt;&#x2F;a&gt; 的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;mterwill&#x2F;7fdcc573dc1aa158648aacd4e33786e8&quot;&gt;Gist&lt;&#x2F;a&gt;, 我写出了一个简单的 Importer。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;Endle&#x2F;1033eb36135b50e19ea64ccc39be5ca7&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;Endle&#x2F;1033eb36135b50e19ea64ccc39be5ca7&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;基类 &lt;code&gt;importer.ImporterProtocol&lt;&#x2F;code&gt; 只有两个必须实现的函数&lt;&#x2F;p&gt;
&lt;h5 id=&quot;identify&quot;&gt;identify()&lt;&#x2F;h5&gt;
&lt;p&gt;返回 bool，判断是否要处理当前文件。有些人写的 &lt;code&gt;config.py&lt;&#x2F;code&gt; 比较完善，只要运行 &lt;code&gt;bean-extract  config.py ofx.csv ~&#x2F;Downloads&#x2F;statements&#x2F;*&lt;&#x2F;code&gt;, &lt;code&gt;bean-extract&lt;&#x2F;code&gt; 就会根据 &lt;code&gt;identify()&lt;&#x2F;code&gt; 的结果，确定要用哪一个 Importer。在这里，我的代码比较简单。&lt;&#x2F;p&gt;
&lt;h5 id=&quot;extract&quot;&gt;extract()&lt;&#x2F;h5&gt;
&lt;p&gt;这个函数处理 CSV 账单。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.sy-zhou.com&#x2F;&quot;&gt;哓哓&lt;&#x2F;a&gt; 的&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.sy-zhou.com&#x2F;%E7%94%A8%E4%BA%8E%E6%94%AF%E4%BB%98%E5%AE%9D%E5%92%8C%E5%BE%AE%E4%BF%A1%E8%B4%A6%E5%8D%95%E7%9A%84beancount-import&#x2F;&quot;&gt;文章里&lt;&#x2F;a&gt;介绍了如何沿袭 CSV Importer 的结构写 Importer，但在起步阶段，我没有遵循这个结构，而是手动读 CSV 以后逐行处理。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    def extract(self, f):
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        entries = []     
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        with open(f.name) as f:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            for index, row in enumerate(csv.reader(f)):
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                txn = self._process_row(index, row)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                entries.append(txn)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        return entries
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;ru-he-zai-windows-xia-an-zhuang-beancount-cygwin-vs-wsl&quot;&gt;如何在 Windows 下安装 Beancount (Cygwin vs WSL)&lt;&#x2F;h4&gt;
&lt;p&gt;买一台新电脑的计划还是搁置状态，所以我只好在 Windows 笔记本上先凑合用。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;beancount.github.io&#x2F;docs&#x2F;installing_beancount.html&quot;&gt;官方手册&lt;&#x2F;a&gt;上说，&lt;em&gt;It’s a breeze if you use Cygwin&lt;&#x2F;em&gt;. 但我在执行 &lt;code&gt;python3 -m pip install beancount-import smart_importer&lt;&#x2F;code&gt; 时，总会在安装依赖 &lt;code&gt;scikit-learn&lt;&#x2F;code&gt; 时卡住。&lt;&#x2F;p&gt;
&lt;p&gt;接下来，我就换到 WSL 里运行了。我在执行 &lt;code&gt;sudo pip3 install m3-cdecimal&lt;&#x2F;code&gt; 时失败了，但跳过这一步我也没看到有什么影响。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h4&gt;
&lt;p&gt;我也为这是一个很简单的需求，但没想到，真做起来还是花了不少的时间。有很多人上传了自己实现的 importer，但在不了解 context 和设计思路的情况下，想要拿来直接用很困难。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;plaintextaccounting.org&#x2F;#data-importconversion&quot;&gt;这个页面&lt;&#x2F;a&gt; 列举了很多 Importer，比如 Clojure 实现的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;PaNaVTEC&#x2F;csv2beancount&quot;&gt;csv2beancount&lt;&#x2F;a&gt;. 还有 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;gaocegege.com&#x2F;Blog&#x2F;&quot;&gt;高策&lt;&#x2F;a&gt; 等人用 Go 实现的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;deb-sig&#x2F;double-entry-generator&quot;&gt;double-entry-generator&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;此外，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;plaintextaccounting.org&#x2F;#data-importconversion&quot;&gt;这个页面&lt;&#x2F;a&gt;还有一些输出到 Ledger 和 HLedge 的工具。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;beancount.github.io&#x2F;docs&#x2F;a_comparison_of_beancount_and_ledger_hledger.html&quot;&gt;这篇文章&lt;&#x2F;a&gt; 介绍了一些区别。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>2022 年的总结</title>
        <published>2022-12-31T00:00:00+00:00</published>
        <updated>2022-12-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/2022-year-review/"/>
        <id>https://blog.zhenbo.pro/2022-year-review/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/2022-year-review/">&lt;p&gt;看到我社交网络里不少人都在发 2022 年的总结，我也有点心痒，准备写一篇流水账，总结一下我的 2022.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pl-men-wai-yi&quot;&gt;PL 门外汉&lt;&#x2F;h4&gt;
&lt;p&gt;给一个没有系统学过编程语言那套理论的人介绍一点点 PL 知识会发生什么呢？答案就是 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;ironCamel&quot;&gt;ironCamel&lt;&#x2F;a&gt;. 靠着三脚猫的知识，总算实现了自己人生中的第一个编译器，达成了程序员的一大浪漫。后面，还高强度地看了一大批 PL 的论文，虽然收获也仅仅是略懂皮毛而已。有点遗憾的是，自己在自己的热情耗尽前，没能读完 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.worldcat.org&#x2F;title&#x2F;types-and-programming-languages&#x2F;oclc&#x2F;51958338&quot;&gt;TAPL&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;注：也不太能算写完了编译器。自己并没有实现后端代码生成的部分，而是直接执行了 AST&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bi-ji-ruan-jian-da-sheng-ji&quot;&gt;笔记软件大升级&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ZhenboLi1&#x2F;status&#x2F;1583837511557984256&quot;&gt;在 2021 年 10 月 22 日&lt;&#x2F;a&gt;，我开始使用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;logseq&#x2F;logseq&#x2F;&quot;&gt;LogSeq&lt;&#x2F;a&gt; 作为自己的主力笔记软件。除了给 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;logseq&#x2F;logseq&#x2F;&quot;&gt;LogSeq&lt;&#x2F;a&gt; 打广告，更重要的是给我自己的开源项目 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;fireSeqSearch&quot;&gt;fireSeqSearch&lt;&#x2F;a&gt; 做广告。&lt;&#x2F;p&gt;
&lt;video src=&quot;https:&#x2F;&#x2F;user-images.githubusercontent.com&#x2F;3221521&#x2F;168455012-e1183f62-4682-4230-84e7-8a461d8985a0.mp4&quot; data-canonical-src=&quot;https:&#x2F;&#x2F;user-images.githubusercontent.com&#x2F;3221521&#x2F;168455012-e1183f62-4682-4230-84e7-8a461d8985a0.mp4&quot; controls=&quot;controls&quot; muted=&quot;muted&quot; class=&quot;d-block rounded-bottom-2 border-top width-fit&quot; style=&quot;max-height:640px; max-width: 100%;&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;这个项目花掉了我非常多的精力（拖延的一大理由），但&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fediscience.org&#x2F;@zhenboli&#x2F;109419358200038482&quot;&gt;也让我很开心&lt;&#x2F;a&gt;。希望看到这里的朋友可以考虑尝试一下 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;logseq&#x2F;logseq&#x2F;&quot;&gt;LogSeq&lt;&#x2F;a&gt;, 用开放格式将全部数据存储在本地的开源笔记软件。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;she-jiao-zhang-hu-de-qian-yi&quot;&gt;社交帐户的迁移&lt;&#x2F;h4&gt;
&lt;p&gt;感谢一位马姓天才，我&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fediscience.org&#x2F;@zhenboli&quot;&gt;顺利迁移到了长毛象&lt;&#x2F;a&gt;. 我由衷希望，看到这里的朋友，考虑一下 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ActivityPub&quot;&gt;ActivityPub&lt;&#x2F;a&gt;. 这一波告别推特的风潮，很可能是 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.usg.edu&#x2F;galileo&#x2F;skills&#x2F;unit07&#x2F;internet07_02.phtml&quot;&gt;1983 年发明互联网&lt;&#x2F;a&gt; 以来，建立一个去中心化的社交平台最好的机会了。就像星战衍生剧安多的剧情，逃出工厂，现在就是最好的，也是最后的机会。&lt;&#x2F;p&gt;
&lt;p&gt;另外，之前买的域名也派上了用场。现在我的博客&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fediscience.org&#x2F;@zhenboli&#x2F;109351164744707704&quot;&gt;迁移&lt;&#x2F;a&gt; 到了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.zhenbo.pro&#x2F;&quot;&gt;https:&#x2F;&#x2F;blog.zhenbo.pro&#x2F;&lt;&#x2F;a&gt;，不过还是 Github Hosted。替换掉 Jekyll 的计划也只停留在了纸面上。&lt;&#x2F;p&gt;
&lt;p&gt;一个小任务，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;fediscience.org&#x2F;@zhenboli&#x2F;109609779087692196&quot;&gt;换掉 Google Analytics&lt;&#x2F;a&gt;, 被拖延到了 2023 年。当然，这个任务没机会被拖延到 2024 了：&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;support.google.com&#x2F;analytics&#x2F;answer&#x2F;11583528?hl=en&quot;&gt;G家自己把旧的 Analytics 扬了&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;video-games&quot;&gt;Video Games&lt;&#x2F;h4&gt;
&lt;p&gt;现在是，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ZhenboLi1&#x2F;status&#x2F;1503416807046144009&quot;&gt;美梦&lt;&#x2F;a&gt;成真时间！在2022年，终于抢到了 PS5。靠着 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;psnine.com&#x2F;topic&#x2F;35604&quot;&gt;PS Collection&lt;&#x2F;a&gt;，体验了不少经典游戏。如果让我选择的话，《战地一》是我心目中的 Game of the Year （以我玩的时间为准）。&lt;&#x2F;p&gt;
&lt;p&gt;期待已久的维多利亚三终于发布了，不过我只玩了一点点。我现在非常羡慕那些说着 “不知道玩什么游戏” 的朋友。我现在的一大苦恼，就是我想玩的游戏，远远超过我可以花在游戏上的时间。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;music&quot;&gt;Music&lt;&#x2F;h4&gt;
&lt;p&gt;Spotify 的 2022 个人精选集出来了。排名第一的是 Christopher Tin 的 Sogno di Volare。虽然我没玩过文明六，但这首歌我是真的很喜欢。在这里先预祝 Tin 能赢得 2023 年的格莱美奖。（&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.paloaltoonline.com&#x2F;news&#x2F;2022&#x2F;12&#x2F;04&#x2F;around-town-composer-christopher-tin-collects-two-grammy-nominations&quot;&gt;获得两项提名&lt;&#x2F;a&gt;）&lt;&#x2F;p&gt;
&lt;p&gt;排名第二的是《潘多拉之心》的插曲 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mochijun.fandom.com&#x2F;wiki&#x2F;Everytime_you_kissed_me&quot;&gt;Everytime You Kissed Me&lt;&#x2F;a&gt;. 这部番我大概是15年左右看的。我对番剧内容的评价有所保留，但我认为音乐非常优秀。这首歌我不知道我重复播放了多少次。&lt;&#x2F;p&gt;
&lt;p&gt;第三名是《你的名字》 插曲 Sparkle English Ver. 我觉得英文版的歌曲更胜日文版，它带来了一种截然不同的体验。我听到这首歌，仿佛看到一位高中女生，开着父亲的皮卡，从旧金山开到了纽约，穿过了沙漠与山谷。可能是我对这首歌脑补过度吧。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bottom-end&quot;&gt;Bottom End&lt;&#x2F;h4&gt;
&lt;p&gt;引述一下&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;ZhenboLi1&#x2F;status&#x2F;1478567278186676228&quot;&gt;我在年初发的一条推&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
&lt;em&gt;感觉应该隔一段时间就学一门新的编程语言，看看不同的设计思路。就像天堂电影院的台词，如果你不出去走走，就会以为眼前的就是全世界。&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这句话里的 PL，应该替换成 &lt;code&gt;template &amp;lt;class T&amp;gt;&lt;&#x2F;code&gt;，适用于生活的方方面面。越是自己（自以为）了解的方面，就越会有未曾想到的可能性。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2022&#x2F;2022_summary_sfo2nyc.png&quot; alt=&quot;Image SFO to NYC&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Codeforces 282B Painting Eggs 题解与贪心算法的证明</title>
        <published>2022-06-21T00:00:00+00:00</published>
        <updated>2022-06-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/cf-282b-painting-eggs-solution-how-to-prove-the-greedy-algorithm/"/>
        <id>https://blog.zhenbo.pro/cf-282b-painting-eggs-solution-how-to-prove-the-greedy-algorithm/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/cf-282b-painting-eggs-solution-how-to-prove-the-greedy-algorithm/">&lt;p&gt;题目链接: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;problemset&#x2F;problem&#x2F;282&#x2F;B&quot;&gt;https:&#x2F;&#x2F;codeforces.com&#x2F;problemset&#x2F;problem&#x2F;282&#x2F;B&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;官方题解: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;6999&quot;&gt;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;6999&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;contest&#x2F;282&#x2F;submission&#x2F;3314492&quot;&gt;官方代码&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;CF 282B 我思考了很久，也没有什么头绪。看了官方题解，发现可以用一个很简单的贪心算法求解。证明的过程要用到数学归纳法(Mathematical induction)，我按照我的理解，把证明过程详细地写下来。&lt;&#x2F;p&gt;
&lt;p&gt;贪心算法：对于每一个鸡蛋，尝试让 A 喷绘。如果这会导致让 A 的收入高于 G 的收入，且超出了 500 的幅度，则把这个鸡蛋交给 G。&lt;&#x2F;p&gt;
&lt;p&gt;引理1：对于合法的输入数据，一定存在至少一种方法，使得两人的收入和之差
$$ |S_a - S_g| \leq 500 $$&lt;&#x2F;p&gt;
&lt;p&gt;求证：使用贪心算法，可以得到正确解。&lt;&#x2F;p&gt;
&lt;p&gt;Base case is trivial.&lt;&#x2F;p&gt;
&lt;p&gt;递推步骤，假设对于前 &lt;code&gt;i-1&lt;&#x2F;code&gt; 个鸡蛋，我们已经找到了一种方法，满足
$$ |S_a - S_g| \leq 500 $$
。不失一般性 (WLOG)，我们假定
$$ S_a - S_g = D$$
且
$$ 0 \leq D \leq 500$$&lt;&#x2F;p&gt;
&lt;p&gt;现在，我们尝试将第 &lt;code&gt;i&lt;&#x2F;code&gt; 个鸡蛋分配给 A. 如果
$$ D + a_i \leq 500 $$
仍然成立，那我们就得到了满足前 &lt;code&gt;i&lt;&#x2F;code&gt; 个鸡蛋的合法方案。&lt;&#x2F;p&gt;
&lt;p&gt;反之，我们将第 &lt;code&gt;i&lt;&#x2F;code&gt; 个鸡蛋分配给 G。此时，
$$ a_i &amp;gt; 500 - D \iff 1000 - g_i &amp;gt; 500 - D \iff g_i &amp;lt; 500 + D$$&lt;&#x2F;p&gt;
&lt;p&gt;新的差值是
$$D_i = (S_g + g_i) - S_a = g_i - D$$
显然
$$ -D \leq D_i &amp;lt; 500 $$
我们就得到了满足前 &lt;code&gt;i&lt;&#x2F;code&gt; 个鸡蛋的合法方案。&lt;&#x2F;p&gt;
&lt;p&gt;引理1 也由此得到了证明。Base case 选择一个报酬不超过 500 元的人，接着每次扩展，都不会打破 500 元工资差的限制。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;wan-lu&quot;&gt;弯路&lt;&#x2F;h4&gt;
&lt;p&gt;看答案之前，我没有想到引理1. 我猜到这是一道贪心的题目，但题目里的“无法满足时输出-1”迷惑了我，让我一直在想，我的贪心策略会不会让一个可行的输入被误认为不可能。&lt;&#x2F;p&gt;
&lt;p&gt;我一直试图寻找一个给输入数据排序的方法，接着用类似对对碰的方式进行贪心匹配。我一直试图找一个例子，证明错误的贪心顺序会导致错误。最终当然是找不到了。&lt;&#x2F;p&gt;
&lt;p&gt;这道题给定的限制条件是不超过 500 的工资差，而两人的工资和则恰巧是 1000 元。这其实给了我很大的提示，两人的工资差可能是钟摆式的摆动，但我没有顺着这个思路思考下去。&lt;&#x2F;p&gt;
&lt;p&gt;我在做这道题的时候，一度试图把 500 元的条件拿掉，去找一个让两人工资差最低的方案。我成功地给自己找了一个困难得多的问题。&lt;&#x2F;p&gt;
&lt;p&gt;{% include mathjax.html %}&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fedora Silverblue 35 设置为家庭服务器</title>
        <published>2022-05-09T00:00:00+00:00</published>
        <updated>2022-05-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/fedora-silverblue-35-as-homeserver/"/>
        <id>https://blog.zhenbo.pro/fedora-silverblue-35-as-homeserver/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/fedora-silverblue-35-as-homeserver/">&lt;p&gt;几周前，我领到了新的笔记本。从 2017 年为我工作的 Dell XPS 也光荣地退居二线了。我构思了很久的搭建家庭服务器的行动，也终于得以付诸实施了。写一篇流水账，记录一下我的操作流程。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;an-zhuang-fedora-silverblue-35-yi-ji-xfce&quot;&gt;安装 Fedora Silverblue 35 以及 XFCE&lt;&#x2F;h4&gt;
&lt;h4 id=&quot;pei-zhi-ssh-deng-lu&quot;&gt;配置 SSH 登录&lt;&#x2F;h4&gt;
&lt;p&gt;Server 的 &lt;code&gt;$HOME&#x2F;.SSH&lt;&#x2F;code&gt; 目录需要设置为 &lt;code&gt;700&lt;&#x2F;code&gt; 权限。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zi-dong-gua-zai-automount-yi-dong-ying-pan&quot;&gt;自动挂载(Automount) 移动硬盘&lt;&#x2F;h4&gt;
&lt;p&gt;在这里，我没能找到用 XFCE 的 GUI 工具设置自动挂载的方式。我的解决方案是，切换到 Gnome 3 里，选择 nautilus -&amp;gt; disk，进而手动选择挂载点。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;flatpak-ying-yong-de-zi-dong-qi-dong&quot;&gt;Flatpak 应用的自动启动&lt;&#x2F;h4&gt;
&lt;p&gt;使用 Flatpak 安装的应用的 &lt;code&gt;.desktop&lt;&#x2F;code&gt; 文件会存放在 &lt;code&gt;&#x2F;var&#x2F;lib&#x2F;flatpak&#x2F;exports&#x2F;share&#x2F;applications&lt;&#x2F;code&gt; 。打开后，就可以知道如何在命令行中运行某个应用。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;an-zhuang-nvidia-bi-yuan-qu-dong-shi-bai&quot;&gt;安装 Nvidia 闭源驱动 （失败）&lt;&#x2F;h4&gt;
&lt;p&gt;我参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20231211123426&#x2F;https:&#x2F;&#x2F;nudesystems.com&#x2F;how-to-install-nvidia-drivers-in-fedora-silverblue&#x2F;&quot;&gt;https:&#x2F;&#x2F;nudesystems.com&#x2F;how-to-install-nvidia-drivers-in-fedora-silverblue&#x2F;&lt;&#x2F;a&gt; 一文，使用 rpmfusion 的包安装 NVidia 闭源驱动。虽然软件包安装成功，但 &lt;code&gt;nvidia-smi&lt;&#x2F;code&gt; 等工具并不能正常运行。因为也不打算用这台家庭服务器玩游戏，就没有深究这个问题。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;an-zhuang-teamviewer&quot;&gt;安装 Teamviewer&lt;&#x2F;h4&gt;
&lt;p&gt;参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;community.teamviewer.com&#x2F;English&#x2F;kb&#x2F;articles&#x2F;30664-use-the-tar-package-for-linux&quot;&gt;https:&#x2F;&#x2F;community.teamviewer.com&#x2F;English&#x2F;kb&#x2F;articles&#x2F;30664-use-the-tar-package-for-linux&lt;&#x2F;a&gt;，可以从 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.teamviewer.com&#x2F;en-us&#x2F;download&#x2F;linux&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.teamviewer.com&#x2F;en-us&#x2F;download&#x2F;linux&#x2F;&lt;&#x2F;a&gt; 下载 tar 包。运行 &lt;code&gt;.&#x2F;tv-setup checklibs&lt;&#x2F;code&gt; 提示缺少 &lt;code&gt;libminizip.so.1&lt;&#x2F;code&gt; 和若干 QT 库。运行 &lt;code&gt;rpm-ostree install qt5-qtquickcontrols qt5-qtquickcontrols2 minizip-compat&lt;&#x2F;code&gt; 后，teamviewer 可以顺利运行了。&lt;&#x2F;p&gt;
&lt;!--This is a comment. Comments are not displayed in the browser



- Set up home server
	- 1. Install Fedora Silverblue 35
		2. Install XFCE
		3. Config httpd failed
		4. login with ssh pubkey failed. set ~&#x2F;.ssh to 700 on target machine!
		5. set auto mount with remote media (it didn&#x27;t work)
		6. set auto mount with gnome disk (nautilus then disk), specify mount point
		7. set auto start, check  &#x2F;var&#x2F;lib&#x2F;flatpak&#x2F;exports&#x2F;share&#x2F;applications
		8. add rpmfusion, and upgrade (https:&#x2F;&#x2F;nudesystems.com&#x2F;how-to-install-nvidia-drivers-in-fedora-silverblue&#x2F;)
		9. rpm-ostree install akmod-nvidia xorg-x11-drv-nvidia
		10. also add xorg-x11-drv-nvidia-cuda
		11. nvidia driver still not good
		12. download tar from https:&#x2F;&#x2F;www.teamviewer.com&#x2F;en-us&#x2F;download&#x2F;linux&#x2F;
		13. https:&#x2F;&#x2F;community.teamviewer.com&#x2F;English&#x2F;kb&#x2F;articles&#x2F;30664-use-the-tar-package-for-linux
		14. .&#x2F;tv-setup checklibs

	- Analyzing dependencies ...
	  libminizip.so.1 =&gt; not found
	- The libraries listed above seem to be missing.
	  Please find and install the corresponding packages.
	  Then, run this command again.
	- QtQuickControls seems to be missing
	- The following command may be helpful:
	  &#x27;libdbus-1.so.3()(64bit)&#x27; &#x27;libQt5Gui.so.5()(64bit)&#x27; &#x27;libQt5Widgets.so.5()(64bit)&#x27; &#x27;libQt5Qml.so.5()(64bit)&#x27; &#x27;libQt5Quick.so.5()(64bit)&#x27; &#x27;libQt5WebKitWidgets.so.5()(64bit)&#x27; &#x27;libQt5X11Extras.so.5()(64bit)&#x27;  qt5-qtdeclarative qt5-qtquickcontrols
	- dnf provides *&#x2F;libminizip.so.1
	- minizip-compat
	- rpm-ostree install qt5-qtquickcontrols qt5-qtquickcontrols2 minizip-compat

--&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My LaTex Cheatsheet</title>
        <published>2021-10-28T00:00:00+00:00</published>
        <updated>2021-10-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/my-latex-cheatsheet/"/>
        <id>https://blog.zhenbo.pro/my-latex-cheatsheet/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/my-latex-cheatsheet/">&lt;h2 id=&quot;table-biao-ge&quot;&gt;Table 表格&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{center}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{tabular}{ |c|c|c| } 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; \hline
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; cell1 &amp;amp; cell2 &amp;amp; cell3 \\ 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; cell4 &amp;amp; cell5 &amp;amp; cell6 \\ 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; cell7 &amp;amp; cell8 &amp;amp; cell9 \\ 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; \hline
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\end{tabular}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\end{center}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;pseudo-code-cha-ru-dai-ma&quot;&gt;Pseudo-code 插入代码&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\usepackage{listings}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{lstlisting}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;F(S, T, s[1..n], t[1..n])
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    a + b	
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\end{lstlisting}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Set tab size&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\lstset{
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	numbers=left,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	breaklines=true,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	tabsize=2,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;insert-picture-cha-ru-tu-pian&quot;&gt;Insert picture 插入图片&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{figure}[h]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\centering
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\includegraphics[width=.3\textwidth]{a.jpg}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\end{figure}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;insert-pdf-cha-ru-pdf&quot;&gt;Insert PDF 插入PDF&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;%https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;2739710&#x2F;1166518
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\usepackage{pdfpages}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\includepdf[pages=-]{q1_x20.pdf}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;duo-xing-de-da-gua-hao&quot;&gt;多行的大括号&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$$ W[i,j]= max \left\{ \\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{aligned}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	P[i,j], \\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	max_{1\leq x\leq i,1\leq y \leq j} W[x,y] + max(
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	W[x,j-y]+W[i-x,j], 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	&amp;amp; W[i-x,y]+W[i.j-y])
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\end{aligned}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\right.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$$
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;matrix-ju-zhen&quot;&gt;Matrix 矩阵&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{equation*}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    A=\begin{bmatrix}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    10 &amp;amp; -4 &amp;amp; 18\\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    11 &amp;amp; 0 &amp;amp; 0\\
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    0 &amp;amp; 16 &amp;amp; -3
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    \end{bmatrix}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\end{equation*}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;limit-at-infinity-qu-jin-yu-wu-qiong-de-ji-xian&quot;&gt;Limit at Infinity 趋近于无穷的极限&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$$\lim_{n \to \infty} y_n$$
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;reference-to-equations-nei-lian-gong-shi&quot;&gt;Reference to equations 内链公式&lt;&#x2F;h2&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{equation}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	\label{eqn:o3}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;0 = f(x^*) = f(x_k) + f&amp;#39;(x_k)(x^*-x_k) + \dfrac{1}{2}f&amp;#39;&amp;#39;(x_k)(x^*-x_k)^2 + \dfrac{1}{6}f&amp;#39;&amp;#39;&amp;#39;(u_1)(x^*-x_k)^3
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\end{equation}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;previous equation \ref{eqn:o3}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;jin-zhi-tu-pian-fu-dong&quot;&gt;禁止图片浮动&lt;&#x2F;h2&gt;
&lt;p&gt;如果希望禁止浮动，可以使用 float 宏包，结合 H 选项。
参考了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.zhihu.com&#x2F;question&#x2F;25082703&#x2F;answer&#x2F;30038248&quot;&gt;孟晨的回答 - 知乎&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\usepackage{float}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{figure}[H]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{table}[H]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;bu-jin-xing-zi-fu-zhuan-yi-write-in-plain-text-mode&quot;&gt;不进行字符转义  write in plain text mode&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tex.stackexchange.com&#x2F;a&#x2F;422207&#x2F;81692&quot;&gt;https:&#x2F;&#x2F;tex.stackexchange.com&#x2F;a&#x2F;422207&#x2F;81692&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\newenvironment{simplechar}{
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;   \catcode`\$=12
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; 这里应该还有一个列表，但是我没配置好 Jekyll 的字符转义
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}{}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;\begin{simplechar}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;This is a paragraph with \textbf{bold} and \emph{emphasized} text, but special characters are treated normally
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>自己动手为 Fedora 打包的几个小技巧</title>
        <published>2021-09-01T00:00:00+00:00</published>
        <updated>2021-09-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/packaging-rpm-upload-to-copr/"/>
        <id>https://blog.zhenbo.pro/packaging-rpm-upload-to-copr/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/packaging-rpm-upload-to-copr/">&lt;p&gt;这几天&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=27893303&quot;&gt;被人推荐&lt;&#x2F;a&gt;了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ahrm&#x2F;sioyek&quot;&gt;sioyek&lt;&#x2F;a&gt;，据说是一个很适合读论文的 PDF 阅读器。但是，软件还比较新，截止到 2021 年 9 月没有 Fedora 的包。在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;zh.fedoracommunity.org&#x2F;about&#x2F;&quot;&gt;FZUG 群&lt;&#x2F;a&gt; 里有人提过，自己手动编译软件时避免 &lt;code&gt;make install&lt;&#x2F;code&gt;，而是打成 RPM 包。这样自己的编译和运行环境更干净，便于后期维护，也也能在 copr 上和人分享。忙了两天，总算编译成功了，总结一下自己踩过的几个小坑。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zhun-bei-spec&quot;&gt;准备 spec&lt;&#x2F;h4&gt;
&lt;p&gt;spec 文件的规范我就不赘述了，我找到的最详细的文档是 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;ftp.rpm.org&#x2F;max-rpm&#x2F;s1-rpm-build-creating-spec-file.html&quot;&gt;Maximum RPM: Taking the RPM Package Manager to the Limit. Chapter 11. Building Packages: A Simple Example&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在实际操作上，我主要参考了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.fedoraproject.org&#x2F;deployment&#x2F;rpm&#x2F;about.html&quot;&gt;RPM Packaging&lt;&#x2F;a&gt; 一文。照着这个例子，运行如下命令&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ sudo dnf install fedora-packager rpmdevtools gcc
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ rpmdev-setuptree
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ cd ~&#x2F;rpmbuild&#x2F;SOURCES
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$  wget -O v0.31.6.tar.gz https:&#x2F;&#x2F;github.com&#x2F;ahrm&#x2F;sioyek&#x2F;archive&#x2F;refs&#x2F;tags&#x2F;v0.31.6.tar.gz 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ cd ~&#x2F;rpmbuild&#x2F;SPECS
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ rpmdev-newspec --macros sioyek.spec
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;并修改 spec 文件即可。&lt;&#x2F;p&gt;
&lt;p&gt;更详细的流程，可以参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;access.redhat.com&#x2F;documentation&#x2F;en-us&#x2F;red_hat_enterprise_linux&#x2F;8&#x2F;html-single&#x2F;packaging_and_distributing_software&#x2F;index&quot;&gt;Packaging and distributing software&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shi-yong-mock-bian-yi-srpm&quot;&gt;使用 mock 编译 SRPM&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rpm-software-management&#x2F;mock&#x2F;wiki&quot;&gt;Mock wiki&lt;&#x2F;a&gt; 介绍的很详细。我配置好 mock 以后，直接运行&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;rpmbuild -bs ~&#x2F;rpmbuild&#x2F;SPECS&#x2F;sioyek.spec  &amp;amp;&amp;amp; mock --enable-plugin=tmpfs --enable-plugin=yum_cache  ~&#x2F;rpmbuild&#x2F;SRPMS&#x2F;sioyek-0.31.6-1.fc34.src.rpm 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;shi-yong-container-podman-docker-de-cha-cuo-ji-qiao&quot;&gt;使用 Container (Podman&#x2F;Docker) 的查错技巧&lt;&#x2F;h4&gt;
&lt;p&gt;刚写好的 spec 编译错误很正常。为了使用 interactive shell,我决定在 podman 里进行测试。每次启动时，都要浪费大量的时间在 &lt;code&gt;dnf upgrade&#x2F;install&lt;&#x2F;code&gt; 上。受到 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;sysadmin.prod.acquia-sites.com&#x2F;sysadmin&#x2F;overlay-mounts&quot;&gt;https:&#x2F;&#x2F;sysadmin.prod.acquia-sites.com&#x2F;sysadmin&#x2F;overlay-mounts&lt;&#x2F;a&gt; 一文的启发，我用如下命令启动容器&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ sudo dnf install --downloadonly harfbuzz-devel # 这里可以替换成其他可能用到的软件包
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ trash &#x2F;dev&#x2F;shm&#x2F;src &amp;amp;&amp;amp; mkdir &#x2F;dev&#x2F;shm&#x2F;src &amp;amp;&amp;amp; cp ~&#x2F;rpmbuild&#x2F;SOURCES&#x2F;* &#x2F;dev&#x2F;shm&#x2F;src &amp;amp;&amp;amp; cd &#x2F;dev&#x2F;shm&#x2F;src &amp;amp;&amp;amp; tar xf v0.31.6.tar.gz
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ cp -r &#x2F;var&#x2F;cache&#x2F;dnf &#x2F;dev&#x2F;shm&#x2F;cache_dnf
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ podman run --rm -it -v &#x2F;dev&#x2F;shm&#x2F;cache_dnf:&#x2F;var&#x2F;cache&#x2F;dnf:z -v &#x2F;dev&#x2F;shm&#x2F;src:&#x2F;src:z docker.io&#x2F;fedora:34 bash
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;jian-cha-lian-jie-cuo-wu&quot;&gt;检查链接错误&lt;&#x2F;h4&gt;
&lt;p&gt;我在编译时，出现了若干链接错误&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;usr&#x2F;bin&#x2F;ld: &#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;x86_64-redhat-linux&#x2F;11&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;lib64&#x2F;libmupdf.a(pdf-font-add.o): undefined reference to symbol &amp;#39;FT_Get_Postscript_Name&amp;#39;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&#x2F;usr&#x2F;lib64&#x2F;libfreetype.so.6: error adding symbols: DSO missing from command line
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;虽然 BuildRequires 里有了必要的依赖，但还是要使用 &lt;code&gt;-lfreetype&lt;&#x2F;code&gt; 这一命令显式链接。其他库同理。&lt;&#x2F;p&gt;
&lt;p&gt;可以使用 &lt;code&gt;nm&lt;&#x2F;code&gt; 查看一个库的符号表 (symbols)，如&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;nm -D &#x2F;usr&#x2F;lib64&#x2F;libfontconfig.so | grep FT_Get_Postscript_Name 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;nm -D &#x2F;usr&#x2F;lib64&#x2F;libfreetype.so | grep FT_Get_Postscript_Name 
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;ji-shi-geng-xin&quot;&gt;及时更新&lt;&#x2F;h4&gt;
&lt;p&gt;copr 编译完成后，本地的缓存更新可能也许是要时间。为了避免重复下载所有的 metadata, 我会运行&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;sudo dnf --disablerepo=&amp;#39;*&amp;#39; --enablerepo=&amp;#39;copr*&amp;#39; --refresh upgrade  sioyek
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最后，再次感谢 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;zh.fedoracommunity.org&#x2F;about&#x2F;&quot;&gt;FZUG 群&lt;&#x2F;a&gt; 的朋友们，无私地解答了我许多问题。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>自动向 Crates.io 发布新版本</title>
        <published>2021-08-19T00:00:00+00:00</published>
        <updated>2021-08-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/rust-automatically-publish-cratesio-inspired-by-maven-release-plugin/"/>
        <id>https://blog.zhenbo.pro/rust-automatically-publish-cratesio-inspired-by-maven-release-plugin/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/rust-automatically-publish-cratesio-inspired-by-maven-release-plugin/">&lt;h4 id=&quot;yuan-qi&quot;&gt;缘起&lt;&#x2F;h4&gt;
&lt;p&gt;之前写 Java 时，自己所在的组遵循这样的 workflow:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;每当 master branch 有新 commit 时，会使用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;maven-release&#x2F;maven-release-plugin&#x2F;&quot;&gt;Maven Release Plugin&lt;&#x2F;a&gt; 修改 &lt;code&gt;pom.xml&lt;&#x2F;code&gt; 内的版本号&lt;&#x2F;li&gt;
&lt;li&gt;Bot 会将新版本的 Maven Package 上传到 JFrog 内。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;最近，我在尝试维护一个 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;rust-bundler-cp&quot;&gt;Rust 包 rust_bundler_cp&lt;&#x2F;a&gt;，想着复刻上文的 Maven Workflow，每当有新 commit 时自动向 crates.io 发布新版本。摸索一段时间后成功在 Github Action 上实现了这个功能。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shi-xian&quot;&gt;实现&lt;&#x2F;h4&gt;
&lt;p&gt;首先，需要一个修改 &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; 的工具。虽然自己写一个脚本识别版本号只需要几行，但我还是用了现有的软件包 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wraithan&#x2F;cargo-bump&quot;&gt;cargo-bump&lt;&#x2F;a&gt;. 使用非常简单，不再赘述了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&#x2F;reference&#x2F;events-that-trigger-workflows&quot;&gt;GitHub Action on&lt;&#x2F;a&gt; 可以设置触发条件。但是，我没有找到如何设置在不同的触发条件下执行不同的任务。在&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;rust-bundler-cp&#x2F;blob&#x2F;176f9f22cdbdcaa874c0ee0943dfe5ac810fa868&#x2F;.github&#x2F;workflows&#x2F;rust.yml#L5&quot;&gt;原有的代码中&lt;&#x2F;a&gt;，Pull Request 和新 commit 都会触发相同的任务。我不打算对原有的 workflow 做过多的修改，因此，我写了一个每次都会被执行的 Python 脚本，用它进行必要的操作。&lt;&#x2F;p&gt;
&lt;p&gt;首先，我在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;rust-bundler-cp&#x2F;blob&#x2F;176f9f22cdbdcaa874c0ee0943dfe5ac810fa868&#x2F;bump_version.py#L31&quot;&gt;脚本&lt;&#x2F;a&gt; 内判断当前是否是在 master branch 执行 CICD 任务。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    branch_name = shell_call(&amp;quot;git branch --show-current&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if branch_name not in [&amp;#39;master&amp;#39;]:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        print(&amp;quot;Current branch  ({})  is not for release. Exiting&amp;quot;.format(branch_name))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        return
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;可以使用如下命令创建新的 commit&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;def bump_version():
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    def extract_ver(s)-&amp;gt;str:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        with_quotes = s.split(&amp;quot;=&amp;quot;)[1].strip()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        wo_q = with_quotes.replace(&amp;#39;&amp;quot;&amp;#39;, &amp;#39;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        return wo_q
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    shell_call(&amp;quot;cargo bump patch&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    diff = shell_call(&amp;quot;git diff Cargo.toml| grep version | egrep ^[+-]&amp;quot;).split(&amp;quot;\n&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    versions = [extract_ver(v) for v in diff]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return versions[0], versions[1]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    # Snip 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    (old_ver, new_ver) = bump_version()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    version_change_info = &amp;quot; From {} To {}&amp;quot;.format(old_ver, new_ver)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    new_commit_message = MESSAGE_FLAG + version_change_info
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    git_commit_cmd = &amp;quot;git add Cargo.toml &amp;amp;&amp;amp; git commit  -m &amp;#39;{}&amp;#39;&amp;quot;.format(new_commit_message)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    subprocess.run(git_commit_cmd, shell=True)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在执行 &lt;code&gt;git commit&lt;&#x2F;code&gt; 前，需要先设置作者的姓名和 email. 我将&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Endle&#x2F;rust-bundler-cp&#x2F;blob&#x2F;176f9f22cdbdcaa874c0ee0943dfe5ac810fa868&#x2F;.github&#x2F;workflows&#x2F;rust.yml#L100&quot;&gt;这一步放到了 &lt;code&gt;rust.yml&lt;&#x2F;code&gt; 中&lt;&#x2F;a&gt;，在 Python 脚本前运行。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          git config --global user.name &amp;#39;Endle&amp;#39;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          git config --global user.email &amp;#39;Endle@users.noreply.github.com&amp;#39;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          git branch --show-current
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          python3 --version
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          python3 bump_version.py
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在 crates.io 上注册帐号后，需要创建自己的 Token. 接着，在 Github Project-&amp;gt;Settings-&amp;gt;Secrets 里存储该token, 如图所示：&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;github&#x2F;github_action_rust_crates_io.png&quot; alt=&quot;Github Screenshot&quot; width=&quot;100%&quot;&gt;
&lt;p&gt;这样，在 &lt;code&gt;rust.yml&lt;&#x2F;code&gt; 中，就可以使用如下命令上传到 crates.io:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      - name: Push to Github and crates
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        env:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;          CRATES_SECRET: ${{ secrets.CRATES_ENDLE }}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        run: |
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            git push origin master
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            cargo login &amp;quot;$CRATES_SECRET&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            cargo publish -v
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h4&gt;
&lt;p&gt;使用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;marketplace&#x2F;actions&#x2F;checkout&quot;&gt;Github actions&#x2F;checkout@v2&lt;&#x2F;a&gt;，向原有的 repo 执行 &lt;code&gt;git push&lt;&#x2F;code&gt; 不需要手动设置 token。可以参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;58393457&#x2F;1166518&quot;&gt;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;58393457&#x2F;1166518&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我在我的 Python 脚本中加入了对上一个 commit message 的判断，从而避免 Workflow 被重复触发。但实践中发现，bot 创建的 commit 并没有触发 GitHub Actions. 我还不太清楚这是什么机制导致的。我在撰写本文是意识到，我应该在 commit message 中加入 &lt;code&gt;[skip ci]&lt;&#x2F;code&gt; 作为标识。&lt;&#x2F;p&gt;
&lt;p&gt;如果设置了 crates.io 的镜像，如&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;[source.crates-io]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;registry = &amp;quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;crates.io-index&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;replace-with = &amp;quot;mirror&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;[source.mirror]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;registry = &amp;quot;https:&#x2F;&#x2F;mirrors.sjtug.sjtu.edu.cn&#x2F;git&#x2F;crates.io-index&#x2F;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在运行 &lt;code&gt;cargo publish&lt;&#x2F;code&gt; 前，需要将 &lt;code&gt;replace-with = &quot;mirror&quot;&lt;&#x2F;code&gt; 暂时注释掉。在网络条件不好的环境下，使用 Github Action 发布，可以省下不少时间。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>线段树作者是谁</title>
        <published>2021-08-07T00:00:00+00:00</published>
        <updated>2021-08-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/who-invented-segment-tree/"/>
        <id>https://blog.zhenbo.pro/who-invented-segment-tree/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/who-invented-segment-tree/">&lt;p&gt;省流助手：&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jon_Bentley_(computer_scientist)&quot;&gt;Jon Louis Bentley&lt;&#x2F;a&gt;, 于 1977 年发明.&lt;&#x2F;p&gt;
&lt;p&gt;线段树是 OI 中常用的基础算法。出于好奇，我简单考证了线段树的诞生过程。个人能力所限，疏漏在所难免，恳请朋友们不吝赐教。&lt;&#x2F;p&gt;
&lt;p&gt;在 1977 年，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Victor_Klee&quot;&gt;Victor L. Klee, Jr&lt;&#x2F;a&gt; 发表了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.jstor.org&#x2F;stable&#x2F;2318871&quot;&gt;Can the Measure of U Ai, Bi Be Computed in Less Than O(n Log N) Steps?&lt;&#x2F;a&gt;. 在同年，卡内基梅隆大学的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jon_Bentley_(computer_scientist)&quot;&gt;Bentley&lt;&#x2F;a&gt; 撰写了 Algorithms for Klee&#x27;s rectangle problems. 这篇文章并没有发表，我也没能在 2021 年的互联网上找到这篇文章的副本。不过，这份 Unpublished notes 被同期的多篇文章引用，如 Bentley 本人在 1980 年发表的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ieeexplore.ieee.org&#x2F;document&#x2F;1675628&quot;&gt;An Optimal Worst Case Algorithm for Reporting Intersections of Rectangles&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;link.springer.com&#x2F;book&#x2F;10.1007&#x2F;978-3-540-77974-2&quot;&gt;Computational Geometry&lt;&#x2F;a&gt; 在 Chapter 10 收录了 Interval Tree 和 Segment Tree. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Segment_tree&quot;&gt;维基百科&lt;&#x2F;a&gt; 引用了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;link.springer.com&#x2F;book&#x2F;10.1007&#x2F;978-3-540-77974-2&quot;&gt;Computational Geometry&lt;&#x2F;a&gt;. 部分近期发表的论文，如 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.sciencedirect.com&#x2F;science&#x2F;article&#x2F;pii&#x2F;S2215016119300391&quot;&gt;Wang, Lei, and Xiaodong Wang. “A Simple and Space Efficient Segment Tree Implementation.”&lt;&#x2F;a&gt; 也引用了&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;link.springer.com&#x2F;book&#x2F;10.1007&#x2F;978-3-540-77974-2&quot;&gt;此书&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;编辑于 2024-07-25: &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.danielzingaro.com&#x2F;&quot;&gt;Daniel Zingaro&lt;&#x2F;a&gt; 撰写的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nostarch.com&#x2F;algorithmic-thinking-2nd-edition&quot;&gt;Algorithmic Thinking&lt;&#x2F;a&gt; 提到，Segment Tree 有 interval trees, tournament trees, order-statistic trees, range query trees 等多个名字. 书中使用了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.oi.edu.pl&#x2F;old&#x2F;php&#x2F;show.php?ac=e181313&amp;amp;module=show&amp;amp;file=zadania&#x2F;oi7&#x2F;promocja&quot;&gt;POI 2000: Promotion&lt;&#x2F;a&gt; 作为例题.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;can-kao-zi-liao&quot;&gt;参考资料&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;Klee, Victor. &quot;Can the Measure of U[Ai, Bi] Be Computed in Less Than O(n Log N) Steps?&quot; &lt;i&gt;The American Mathematical Monthly&lt;&#x2F;i&gt; 84, no. 4 (1977): 284-85. Accessed August 7, 2021. doi:10.2307&#x2F;2318871.&lt;&#x2F;li&gt;
&lt;li&gt;Wang, Lei, and Xiaodong Wang. “A Simple and Space Efficient Segment Tree Implementation.” MethodsX 6 (January 1, 2019): 500–512. https:&#x2F;&#x2F;doi.org&#x2F;10.1016&#x2F;j.mex.2019.02.028.&lt;&#x2F;li&gt;
&lt;li&gt;Bentley, Jon Louis, and Derick Wood. 1980. “An Optimal Worst Case Algorithm for Reporting Intersections of Rectangles.” IEEE Transactions on Computers C-29 (7): 571–77. https:&#x2F;&#x2F;doi.org&#x2F;10.1109&#x2F;TC.1980.1675628.&lt;&#x2F;li&gt;
&lt;li&gt;de Berg, Mark, Cheong, Otfried, van Kreveld, Marc, and Overmars, Mark. Computational Geometry. Third Edition. Berlin, Heidelberg: Springer Berlin &#x2F; Heidelberg, 2008. https:&#x2F;&#x2F;doi.org&#x2F;10.1007&#x2F;978-3-540-77974-2.&lt;&#x2F;li&gt;
&lt;li&gt;Wu, Y., &amp;amp; Wang, J. (2018). Algorithm Design Practice for Collegiate Programming Contests and Education (1st ed.). CRC Press. https:&#x2F;&#x2F;doi-org.proxy.lib.uwaterloo.ca&#x2F;10.1201&#x2F;9780429401855&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;poj.org&#x2F;problem?id=2828&quot;&gt;POJ 2828 Buy Tickets - Monthly, 2006.05.28, Zhu Zeyuan&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>使用 git shallow clone 下载并编译 Thunderbird</title>
        <published>2021-08-03T00:00:00+00:00</published>
        <updated>2021-08-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/git-shallow-clone-build-thunderbird/"/>
        <id>https://blog.zhenbo.pro/git-shallow-clone-build-thunderbird/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/git-shallow-clone-build-thunderbird/">&lt;p&gt;最近在尝试编译 Thunderbird. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.thunderbird.net&#x2F;thunderbird-development&#x2F;getting-started&quot;&gt;官方的手册&lt;&#x2F;a&gt; 的建议是&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;hg clone https:&#x2F;&#x2F;hg.mozilla.org&#x2F;mozilla-central source&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd source&#x2F;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;hg clone https:&#x2F;&#x2F;hg.mozilla.org&#x2F;comm-central comm&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;因为我网络情况不好，硬盘空间也有些捉襟见肘，就只想下载最新的版本。可是,&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;4205246&#x2F;1166518&quot;&gt;Mercurial HG 并不支持&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Mozilla 已经在 GitHub 上有了实验性的 Mirror. 因此，我使用如下的方式下载 Thunderbird 的代码。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# My personal habit
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd ~&#x2F;src&#x2F;mozilla
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git clone --depth=1 https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;gecko-dev.git mozilla-central
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git clone --depth=1 https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;releases-comm-central comm-central
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cp -R --reflink=auto comm-central&#x2F; mozilla-central&#x2F;comm
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我会使用如下代码进行更新。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd mozilla-central &amp;amp;&amp;amp; git pull origin master &amp;amp;&amp;amp; trash comm &amp;amp;&amp;amp; cd ..
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd comm-central &amp;amp;&amp;amp; git pull origin master &amp;amp;&amp;amp; cd ..
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cp -R --reflink=auto comm-central&#x2F; mozilla-central&#x2F;comm
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd mozilla-central
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Codeforces #723 1526E Oolimry and Suffix Array 题解</title>
        <published>2021-07-15T00:00:00+00:00</published>
        <updated>2021-07-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/solution-codeforces-723-1526e-oolimry-and-suffix-array/"/>
        <id>https://blog.zhenbo.pro/solution-codeforces-723-1526e-oolimry-and-suffix-array/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/solution-codeforces-723-1526e-oolimry-and-suffix-array/">&lt;h4 id=&quot;ti-mu-miao-shu&quot;&gt;题目描述&lt;&#x2F;h4&gt;
&lt;p&gt;给定一个&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Suffix_array&quot;&gt;后缀数组 Suffix Array&lt;&#x2F;a&gt; 和词典大小(alphabet size)，求总共有多少种不同的可能性。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;contest&#x2F;1526&#x2F;problem&#x2F;E&quot;&gt;题目链接 1526 E&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这道题的描述很简单，但思维量很大。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;91195&quot;&gt;官方 Solution&lt;&#x2F;a&gt; 又写的非常简洁。忙了一个星期，我总算完成了这道题。&lt;&#x2F;p&gt;
&lt;p&gt;这道题可以拆分成两问&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-gei-ding-hou-zhui-shu-zu-shi-yong-zui-xiao-de-zi-dian-sheng-cheng-he-fa-de-zi-fu-chuan&quot;&gt;1. 给定后缀数组，使用最小的字典生成合法的字符串&lt;&#x2F;h4&gt;
&lt;p&gt;对于非空的字符串 S 和对应的后缀数组 SA,我们有如下性质&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;\( 0 \leq SA_i \leq n-1 \)&lt;&#x2F;li&gt;
&lt;li&gt;SA is a permutation of &lt;code&gt;[0, n-1]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;SA 对应的后缀字符串严格递增(lexical order)&lt;&#x2F;li&gt;
&lt;li&gt;对于本问，上界为 n, 下界为 1&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;根据后缀数组的定义，我们有&lt;&#x2F;p&gt;
&lt;p&gt;$$ i &amp;lt; j \Leftrightarrow S[SA_i,n-1] &amp;lt; S[SA_j,n-1] $$&lt;&#x2F;p&gt;
&lt;p&gt;我们将 \(S[SA_i,n-1]\) 拆成 \(xY\)，\(x\) 表示一个合法的字符，\(Y\) 则表示一个可能为空的字符串。同理，将 \(S[SA_j,n-1]\) 拆分成 \(aB\)，如下图&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;codeforces&#x2F;723e.jpg&quot; alt=&quot;xyab pic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;注意，在这里我们不清楚 \(Y B\) 的长度。因为 \(xY \neq aB\)，可知\(Y \neq B\)&lt;&#x2F;p&gt;
&lt;p&gt;显然，在这里 \(x \leq a \). 想要使用尽可能小的字典, 我们希望尽可能地使用相同的字符。那么，我们能得到&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;\(Y &amp;lt; B\ \Rightarrow x = a\)&lt;&#x2F;li&gt;
&lt;li&gt;\(Y &amp;gt; B\ \Rightarrow x &amp;lt; a\)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;当出现后者的情况时，我们就需要将一个新的字符插入字典，从而满足后缀数组 SA 的要求。因为 SA 是严格升序的， \(\forall i \in [0,n-2], j=i+1 \Rightarrow S[SA_i,n-1] &amp;lt; S[SA_j,n-1] \) 即可保证 SA 合法。&lt;&#x2F;p&gt;
&lt;p&gt;现在，我们需要高效地比较 \(Y B\)。因为后缀数组 SA 已知，这个问题非常简单。我们可以定义函数 &lt;code&gt;Rank(U)&lt;&#x2F;code&gt;，表明对于字符串 &lt;code&gt;S&lt;&#x2F;code&gt; 的后缀 &lt;code&gt;U&lt;&#x2F;code&gt;在所有后缀中的排名。为了方便，我们定义空串 &lt;code&gt;Rank($)=-1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;91195&quot;&gt;官方 Solution&lt;&#x2F;a&gt; 使用了 &lt;code&gt;pos&lt;&#x2F;code&gt; 这一宽泛的名称，不易理解。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.csdn.net&#x2F;qq_42101694&#x2F;article&#x2F;details&#x2F;117388502&quot;&gt;OneInDark 的博文&lt;&#x2F;a&gt; 准确地称之为 &lt;code&gt;rnk&lt;&#x2F;code&gt;，我也沿用了这一名词。&lt;&#x2F;p&gt;
&lt;p&gt;根据后缀数组的定义，我们可以使用下标 \(u\) 表示字符串 \(U = S[u, n-1]\). 那么，&lt;code&gt;Rank(n)=-1&lt;&#x2F;code&gt;. 这一问的代码实现如下：&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fn calculate_strict_increasing_pairs(sa: &amp;amp;Vec&amp;lt;i32&amp;gt;) -&amp;gt; usize {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut rank = vec![0; sa.len()+1];
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for i in 0..sa.len() {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        rank[ sa[i] as usize ] = i as i32;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; rank[n] = -1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; means an empty string $ is lexicographically smaller than all non-empty strings
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    rank[sa.len()] = -1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    let mut cnt = 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for i in 0..(sa.len()-1) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let j = i + 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; xY = [SAi, n-1]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; aB = [SAj, n-1]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; xY &amp;lt; aB
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &#x2F;&#x2F; if Y &amp;lt; B, x may be equal to a
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let y:usize = sa[i] as usize + 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        let b:usize = sa[j] as usize + 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        if rank[y] &amp;lt; rank[b] {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            ()
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        } else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            cnt += 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    cnt
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里的 &lt;code&gt;cnt&lt;&#x2F;code&gt; 指，为了满足要求我们需要多少次换用新的字符。也就是说，我们需要一个大小为 &lt;code&gt;cnt+1&lt;&#x2F;code&gt; 的字典。当且仅当 \(k&amp;lt;=cnt\) 时，不存在任何一个合法的字符串 &lt;code&gt;S&lt;&#x2F;code&gt;满足&lt;code&gt;SA&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;2-ge-ban-fa-stars-and-bars-qiu-ke-xing-de-fang-an-shu&quot;&gt;2. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stars_and_bars_(combinatorics)&quot;&gt;隔板法(Stars and bars)&lt;&#x2F;a&gt;求可行的方案数&lt;&#x2F;h4&gt;
&lt;p&gt;定义非负整数数列 \(X\), 令 \(x_i=0\) 表示字符串 \(S\) 中 \(S_i = S_{i-1} \). 如果 \(x_i \geq 1\)，那么 \(S_i\) 相比于 \(S_{i-1} \) 要向前推 \(x_i\) 个字符。这样我们将题目转化成了，数列 \(X\) 有多少种合法的可能。&lt;&#x2F;p&gt;
&lt;p&gt;定义一个由整数 \(0\) \(1\) 组成的数列 \(Y\). 如果第一问中 \(S_i &amp;gt; S_{i-1} \), 那么 \(y_i = 1\). 否则，\(y_i=0\). 数列 \(Y\) 仅由第一问中的 \(SA\) 确定。数列 \(X\) 合法的充要条件是 \(\forall i: x_i \geq y_i\)&lt;&#x2F;p&gt;
&lt;p&gt;定义非负整数数列 \(Z = X - Y\). 求数列 \(X\) 有多少种合法的可能性，等价于求数列 \(Z\) 有多少种合法的可能。&lt;&#x2F;p&gt;
&lt;p&gt;令 \(extra = k-1-cnt\), 表示我们有多少个额外的字母。 如果 \(cnt=k-1\)，那唯一合法的数列 \(Z\) 为 \(\forall i: z_i = 0\)，我们没有任何一个字母可以浪费。可以枚举 \(w \in [0, extra] \), 令 \(w = \sum Z\), 可以使用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stars_and_bars_(combinatorics)&quot;&gt;隔板法(Stars and bars) Theorem Two&lt;&#x2F;a&gt; 求解。&lt;&#x2F;p&gt;
&lt;p&gt;但是，枚举 \(w\) 效率过低。我们可以在 \(Z\) 的末尾添加一个元素，得到数列  \(Z^{\star}\). 这个额外元素的值表示 \(extra - w\). 那么，\( \sum Z^{\star} = extra\).  以 \(k = 26, n = 10\) 举例，如果我们希望字符串 \(S\) 的第一个元素是 &lt;code&gt;c&lt;&#x2F;code&gt;，我们就令 \(z_0^{\star} = 2\). 如果字符串 \(S\) 的第末尾元素是 &lt;code&gt;y&lt;&#x2F;code&gt;，我们就令 \(z^{\star}_{last} = 1\)&lt;&#x2F;p&gt;
&lt;p&gt;现在，我们就将前文的 枚举 \(w \in [0, extra] \) 转化为了使用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stars_and_bars_(combinatorics)&quot;&gt;隔板法(Stars and bars) Theorem Two&lt;&#x2F;a&gt; 求数列  \(Z^{\star}\) 的可能数。数列长度为 \(n+1\)，数列和为 \( extra = k-1-cnt\). 答案要取余，这要用到 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Modular_multiplicative_inverse&quot;&gt;Modular multiplicative inverse&lt;&#x2F;a&gt; 的知识。可以参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.geeksforgeeks.org&#x2F;multiplicative-inverse-under-modulo-m&#x2F;&quot;&gt;Geeksforgeeks&lt;&#x2F;a&gt; 的实现。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;91195&quot;&gt;官方 Solution&lt;&#x2F;a&gt; 写的过于简洁。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.csdn.net&#x2F;qq_42101694&#x2F;article&#x2F;details&#x2F;117388502&quot;&gt;OneInDark 的博文&lt;&#x2F;a&gt; 详细了不少，为我提供了很大的帮助。&lt;br &#x2F;&gt;
写题解时发现了 \( \LaTeX \) &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;oeis.org&#x2F;wiki&#x2F;List_of_LaTeX_mathematical_symbols&quot;&gt;速查表&lt;&#x2F;a&gt;，记录在此以备将来使用。&lt;br &#x2F;&gt;
意外读到了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;yihui.org&#x2F;cn&#x2F;vitae&#x2F;&quot;&gt;谢益辉&lt;&#x2F;a&gt; 的&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;yihui.org&#x2F;cn&#x2F;2017&#x2F;04&#x2F;mathjax-markdown&#x2F;&quot;&gt;博文: MathJax 与 Markdown 的究极融合&lt;&#x2F;a&gt;，感觉当前自己的 Jekyll + MathJax 的组合的确不是很方便。未来可能会寻找一个更好的写作方法。&lt;br &#x2F;&gt;
这道题目有两个主要的思维点，每一个都让我感觉很吃力。从前到后忙了得有一周，总算有了个比较圆满的结果。&lt;br &#x2F;&gt;
最近我在&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;92796&quot;&gt;尝试使用 Rust 打 Codeforces&lt;&#x2F;a&gt;，欢迎大家提出建议。&lt;&#x2F;p&gt;
&lt;p&gt;{% include mathjax.html %}&lt;&#x2F;p&gt;
&lt;!--
令 \\(x_i=0\\)表示字符串 `S` 中 \\(S_i = S_{i-1} \\). 如果 \\(x_i = 1\\)，那么 \\(S_i &gt; S_{i-1} \\) . 数列 \\(X\\) 为确定量。     
定义非负整数数列 `Z`, \\(\forall i : z_i \geq \x_i\\). \\(S_i\\) 相比于 \\(S_{i-1} \\) 要向前推 \\(Z_i\\) 个字符。根据题意，我们也要保证 \\( \sum Z_i \leq (k-1) \\). 我们要求出在限制条件下，数列 `Z` 有多少种可能性。\\(Z = X + Y\\).
--&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>初探 Z function 处理字符串</title>
        <published>2021-06-20T00:00:00+00:00</published>
        <updated>2021-06-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/string-z-function-intro/"/>
        <id>https://blog.zhenbo.pro/string-z-function-intro/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/string-z-function-intro/">&lt;p&gt;之前参加了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;blog&#x2F;entry&#x2F;91381&quot;&gt;Codeforces Round #726&lt;&#x2F;a&gt;，E2 这道题方法很多，推荐的方法是 Z Function. 我也借机学习一个新算法。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;han-shu-ding-yi&quot;&gt;函数定义&lt;&#x2F;h4&gt;
&lt;p&gt;本文中，所有的数组下标均为 0 开始。如无说明，所有的区间均为闭区间。对于字符串 S, &lt;code&gt;S[a, b]&lt;&#x2F;code&gt; 表示选取一个长度为 &lt;code&gt;b - a + 1&lt;&#x2F;code&gt;，范围是由 a 到 b 的子串。&lt;&#x2F;p&gt;
&lt;p&gt;函数 Z 的定义是，给定一个字符串 S. 对于每个下标 i，寻找一个最长的子串使 &lt;code&gt;S[i,i+Zi - 1] = S[0, Zi - 1]&lt;&#x2F;code&gt;. &lt;code&gt;Z[0]&lt;&#x2F;code&gt; 未被定义。如果 &lt;code&gt;S[i] != S[0]&lt;&#x2F;code&gt;，我们有 &lt;code&gt;Z[i] = 0&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;直观一些的理解是，一个序列的前缀 (prefix) 在可能在这个序列中重复出现。例如，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Open_reading_frame&quot;&gt;Open reading frame&lt;&#x2F;a&gt; 中，特定的密码子会在碱基序列的起始和前端重复出现。Z-function 反应了重复出现的情况。&lt;&#x2F;p&gt;
&lt;p&gt;我手绘了一张示意图，相同颜色表示重复的元素。向下延伸的部分，表示 &lt;code&gt;S[i, i+Zi - 1] = S[0, Zi - 1]&lt;&#x2F;code&gt;，也就是与前缀重合的部分。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;algorithm&#x2F;strings&#x2F;z_function_drawing.png&quot;
     alt=&quot;z-function-drawing&quot;
     width=&quot;100%&quot;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;xian-xing-shi-jian-qiu-jie&quot;&gt;线性时间求解&lt;&#x2F;h4&gt;
&lt;p&gt;根据定义，可以容易地写出一个 Brute-force 的算法，求出序列 S 对应的 Z. 但当序列 S 的前缀在序列中多次重复出现时，耗时会快速增加。极端情况下，&lt;code&gt;aaaaa&lt;&#x2F;code&gt; 这类序列，会达到 Brute-force 算法的最坏时间复杂度，&lt;code&gt;O(n^2)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;通常提到 Z Function 的时候，都是要在线性时间内求出结果。我们可以使用 Sliding Window Technique，维护一个与 S 的前缀相同的 window. 其范围是 &lt;code&gt;[L, R]&lt;&#x2F;code&gt; 。也就是说，这个 window:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;L &amp;gt; 0&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;S[0, R - L] = S[L, R]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;L 与 R 均保持递增，易证算法的时间复杂度为 &lt;code&gt;O(n)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;求 &lt;code&gt;Z[i]&lt;&#x2F;code&gt; 时，&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;若 &lt;code&gt;i &amp;gt; R&lt;&#x2F;code&gt;，则这个 window 没有为我们提供已知的信息。令 &lt;code&gt;L = R = i&lt;&#x2F;code&gt;，再向右尽可能地延伸 R&lt;&#x2F;li&gt;
&lt;li&gt;若 &lt;code&gt;i &amp;lt;= R&lt;&#x2F;code&gt;，我们将 &lt;code&gt;S[L, R]&lt;&#x2F;code&gt; 拆分成 &lt;code&gt;S[L, i]&lt;&#x2F;code&gt; 和 &lt;code&gt;S[i, R]&lt;&#x2F;code&gt; (有意重叠&lt;code&gt;S[i]&lt;&#x2F;code&gt;)
&lt;ul&gt;
&lt;li&gt;令 &lt;code&gt;k = i - L&lt;&#x2F;code&gt;. 因为 &lt;code&gt;S[0, R-L] = S[L, R]&lt;&#x2F;code&gt;， 我们可证 &lt;code&gt;S[0, k] = S[L, i]&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;同理，&lt;code&gt;S[k, R - L] = S[i, R]&lt;&#x2F;code&gt;. 这样，我们就用上了 window 内的信息。&lt;&#x2F;li&gt;
&lt;li&gt;如果 &lt;code&gt;Z[k] &amp;lt; R - i + 1&lt;&#x2F;code&gt; ，就说明在子串 &lt;code&gt;S[k, R - L]&lt;&#x2F;code&gt; 中存在 &lt;code&gt;p &amp;gt;= Z[k]&lt;&#x2F;code&gt;, 使 &lt;code&gt;S[k+p] != S[0+p]&lt;&#x2F;code&gt;。 这也就是说， &lt;code&gt;S[i+p] != S[p]&lt;&#x2F;code&gt;。同样，对于任意 &lt;code&gt;q &amp;lt; Z[k]&lt;&#x2F;code&gt;，我们有 &lt;code&gt;S[i+q] = S[k+q] = S[q]&lt;&#x2F;code&gt;。因此，&lt;code&gt;Z[i] = Z[k]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;如果 &lt;code&gt;Z[k] &amp;gt;= R - i + 1&lt;&#x2F;code&gt;，我们在保持 L 不变的基础上，尽可能延伸 R&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;使用 C++ 实现如下&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;void extend_window(const char *str, int str_len, int left, int &amp;amp;right) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; S[0:right-left] == S[left:right]   closed interval
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &#x2F;&#x2F; if S[0] != S[right], resulting in  left+1==right, indicating an empty range
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    while (right &amp;lt; str_len &amp;amp;&amp;amp; str[right - left] == str[right]) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        ++right;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    --right;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;void z_function(const char *str, int str_len, int Z[]) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    Z[0] = 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    int left = -1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    int right = -1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    for (int i=1; i &amp;lt; str_len; ++i) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        if (i &amp;gt; right) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            left = right = i;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            extend_window(str, str_len, left, right);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            Z[i] = right - left + 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        } else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            int k = i - left;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F; We know S[0:right-left]  ==  S[left:right] =&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F;      1. S[0:k]           ==  S[left:i]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            &#x2F;&#x2F;      2. S[k, right-left] ==  S[i:right]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            if (Z[k] &amp;lt; right - i + 1) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                &#x2F;&#x2F; exist p&amp;gt;=0, S[k+p] != S[p] =&amp;gt; S[i+p] != S[p]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                Z[i] = Z[k];
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            } else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                left = i;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                extend_window(str, str_len, left, right);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                Z[i] = right - left + 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;catalog.utdallas.edu&#x2F;2019&#x2F;graduate&#x2F;courses&#x2F;cs6333&quot;&gt;UT Dallas CS 6333&lt;&#x2F;a&gt; 提供了一个直观的&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;personal.utdallas.edu&#x2F;~besp&#x2F;demo&#x2F;John2010&#x2F;z-algorithm.htm&quot;&gt;网页演示&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h4&gt;
&lt;p&gt;介绍 Z Function 的英文文章不少，但我个人感觉 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.hackerearth.com&#x2F;practice&#x2F;algorithms&#x2F;string-algorithm&#x2F;z-algorithm&#x2F;tutorial&#x2F;&quot;&gt;HackerEarth&lt;&#x2F;a&gt; 的描述最为清晰。&lt;&#x2F;p&gt;
&lt;p&gt;Z Function 的英文定义可以参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;contest.cs.cmu.edu&#x2F;295&#x2F;tutorials&#x2F;z-string-matching.pdf&quot;&gt;CMU 295 z-string-matching Page 7&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cp-algorithms.com&#x2F;string&#x2F;z-function.html&quot;&gt;e-maxx&lt;&#x2F;a&gt; 提供了若干道例题，如 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeforces.com&#x2F;problemset&#x2F;problem&#x2F;126&#x2F;B&quot;&gt;Codeforces - Password&lt;&#x2F;a&gt;. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;discuss.codechef.com&#x2F;t&#x2F;z-algorithm-tutorial&#x2F;64274&quot;&gt;Codechef 的教程&lt;&#x2F;a&gt;也举出了两道例题。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;resilar&quot;&gt;resilar&lt;&#x2F;a&gt; 提供了&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;resilar&#x2F;e65745cf7a80ef364df034e96cfcc86d#file-z-c-L82&quot;&gt;更为精简的实现&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;personal.utdallas.edu&#x2F;~besp&#x2F;demo&#x2F;John2010&#x2F;z-algorithm.htm&quot;&gt;UT Dallas 的课程页面&lt;&#x2F;a&gt; 和 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;raywenderlich&#x2F;swift-algorithm-club&#x2F;blob&#x2F;2fdd8b8be1b3fcd17ad0394053e672f2bd1d3076&#x2F;Z-Algorithm&#x2F;README.markdown&quot;&gt;Matteo Dunnhofer 的文章&lt;&#x2F;a&gt; 都提到了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.worldcat.org&#x2F;title&#x2F;algorithms-on-strings-trees-and-sequences-computer-science-and-computational-biology&#x2F;oclc&#x2F;910017234&quot;&gt;Gusfield, Dan. Algorithms on Strings, Trees, and Sequences: Computer Science and Computational Biology&lt;&#x2F;a&gt;. 但我手头并没有这本书，并没有验证 Gusfield 的书上是否提到了这一算法。&lt;&#x2F;p&gt;
&lt;p&gt;非常感谢 ITX351 对本文提供的宝贵意见。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Tail Sum Formula 的学习笔记</title>
        <published>2021-06-14T00:00:00+00:00</published>
        <updated>2021-06-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/tail-sum-formula-notes/"/>
        <id>https://blog.zhenbo.pro/tail-sum-formula-notes/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/tail-sum-formula-notes/">&lt;p&gt;最近看书时，接触到了 Tail-Sum Formula. 公式的定义如下&lt;&#x2F;p&gt;
&lt;p&gt;$$E(X) =  \sum_{x=1}^{\infty} P(X \geq x) $$&lt;&#x2F;p&gt;
&lt;p&gt;它也有一个等价形式&lt;&#x2F;p&gt;
&lt;p&gt;$$E(X) =  \sum_{x=0}^{\infty} P(X \gt x) $$&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gong-shi-zheng-ming&quot;&gt;公式证明&lt;&#x2F;h4&gt;
&lt;p&gt;在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;inst.eecs.berkeley.edu&#x2F;~cs70&#x2F;su16&#x2F;static&#x2F;su16&#x2F;extra_note&#x2F;sinho_cs_70_notes.pdf&quot;&gt;Sinho Chewi 的笔记&lt;&#x2F;a&gt; 上，可以看到这一公式的证明。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;statistics&#x2F;tailsum_formula&#x2F;tailsum_formula_proof.png&quot; alt=&quot;proof&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;红框内的这步变换比较跳跃，我阅读时，在这里卡顿了很久，发现这里其实很简单。&lt;br &#x2F;&gt;
对于原式&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = \sum_{x=1}^{\infty} \sum_{k=1}^{k=x} P(X=x) $$&lt;&#x2F;p&gt;
&lt;p&gt;我们将每一行的结果 &lt;code&gt;Row(X=x)&lt;&#x2F;code&gt; 拆出来&lt;&#x2F;p&gt;
&lt;p&gt;$$ Row(X=x) = \sum_{k=1}^{k=x} P(X=x) $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = \sum_{x=1}^{\infty} Row(X=x) $$&lt;&#x2F;p&gt;
&lt;p&gt;示意图如下&lt;br &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;statistics&#x2F;tailsum_formula&#x2F;schematic_diagram.png&quot; alt=&quot;示意图&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我们如果竖向看这个示意图（红圈），那就可以得到另外一种形式&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = \sum_{k=1}^{\infty} Column(K=k) $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ Column(K=k) = \sum_{x=k}^{\infty} P(X=x)$$&lt;&#x2F;p&gt;
&lt;p&gt;这也就是 Sinho Chewi 所提的&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = \sum_{k=1}^{\infty} \sum_{x=k}^{\infty}  P(X=x) $$&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gong-shi-ying-yong&quot;&gt;公式应用&lt;&#x2F;h4&gt;
&lt;p&gt;在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.worldcat.org&#x2F;title&#x2F;probability-for-statistics-and-machine-learning-fundamentals-and-advanced-topics&#x2F;oclc&#x2F;706920643&amp;amp;referer=brief_results&quot;&gt;Probability for Statistics and Machine Learning&lt;&#x2F;a&gt; 一书中，对此定理有一个有趣的例题：&lt;&#x2F;p&gt;
&lt;p&gt;一对夫妇准备生若干个孩子，直到子女中既有男孩也有女孩。令生男孩的概率为 &lt;code&gt;p&lt;&#x2F;code&gt;，求期望的子女数。&lt;&#x2F;p&gt;
&lt;p&gt;有了 Tail Sum Formula，我们只需求解 $$ P(X &amp;gt; n) $$，也就是 &lt;code&gt;前 n 个孩子的性别相同(男或女)&lt;&#x2F;code&gt;。那么，可以得到&lt;&#x2F;p&gt;
&lt;p&gt;$$ P(X &amp;gt; n) = p^n + (1-p)^n \quad \textrm{if} \quad n \geq 2$$&lt;&#x2F;p&gt;
&lt;p&gt;注意，$$P(X = 0) = P(X = 1) = 1$$&lt;&#x2F;p&gt;
&lt;p&gt;现在套用公式，&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) =  \sum_{x=0}^{\infty} P(X \gt x) $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = P(X = 0) + P(X = 1) + \sum_{x=2}^{\infty} P(X \gt x)$$&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = 2 + \sum_{x=2}^{\infty} [p^n + (1-p)^n]$$&lt;&#x2F;p&gt;
&lt;p&gt;等比数列求和时，我们有&lt;&#x2F;p&gt;
&lt;p&gt;$$ \sum_{n=2}^{\infty} a^n = (\sum_{x=1}^{\infty} a^n) - a $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ \sum_{n=1}^{\infty} a^n = \frac{a}{1-a} \quad (-1 \lt a \lt 1) $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ \sum_{n=2}^{\infty} a^n = \frac{a^2}{1-a}  \quad (-1 \lt a \lt 1)   $$&lt;&#x2F;p&gt;
&lt;p&gt;带入原式，&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = 2 + \frac{p^2}{1-p} + \frac{(1-p)^2}{1-(1-p)} $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = 2 + \frac{p^3 + (1-p)^3}{p(1-p)}  $$&lt;&#x2F;p&gt;
&lt;p&gt;根据 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;math.stackexchange.com&#x2F;a&#x2F;1861172&#x2F;729703&quot;&gt;binomial expansion&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;$$ p^3 + (1-p)^3 = p^3 + 1 - 3p + 3p^2-p^3 = 3p^2-3p+1 $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = \frac{2p-2p^2}{p(1-p)} + \frac{3p^2-3p+1}{p(1-p)}  $$&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = \frac{p(p-1)+1}{p(1-p)}  $$&lt;&#x2F;p&gt;
&lt;p&gt;最终，得到结论&lt;&#x2F;p&gt;
&lt;p&gt;$$ E(X) = \frac{1}{p(1-p)} - 1 $$&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h4&gt;
&lt;p&gt;一个非常简单的公式，在书上只用了一页不到，但我却忙了一下午才搞清楚。既然如此，不如更进一步，写一篇笔记出来，也借机迫使自己把每一步的推导弄清楚。&lt;&#x2F;p&gt;
&lt;p&gt;感谢老同学 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;%E7%9D%BF%E5%85%8B-%E6%AF%95-61407698&#x2F;&quot;&gt;毕睿克&lt;&#x2F;a&gt; 为本文草稿提出的意见。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;images&#x2F;statistics&#x2F;tailsum_formula&#x2F;schematic_diagram.png&quot;&gt;示意图&lt;&#x2F;a&gt; 我是用 LibreOffice Calc 制作的，步骤繁琐且效率差。如果有人了解如何绘制类似图形，恳请您不吝赐教。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fcitx 5 在分辨率改变后候选框过大</title>
        <published>2021-05-30T00:00:00+00:00</published>
        <updated>2021-05-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/fcitx-5-preview-too-large-after-resolution-change/"/>
        <id>https://blog.zhenbo.pro/fcitx-5-preview-too-large-after-resolution-change/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/fcitx-5-preview-too-large-after-resolution-change/">&lt;p&gt;我当前使用的是 Fedore34+Gnome 40+Fcitx 5.0.8。今天早上为了用 Wine 玩星际争霸，把屏幕分辨率设置到 800x600，并将 Display Mode 设置为 Mirror。游戏结束后，我将分辨率改回了 1920x1080，但 Fcitx 的候选框就变得特别大，如该视频所示&lt;&#x2F;p&gt;
&lt;video width=&#x27;400&#x27; controls&gt;
    &lt;source src=&quot;{{site.baseurl}}&#x2F;resources&#x2F;fcitx&#x2F;font_size_error.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;尝试了重启（Fcitx, 系统）和恢复初始设置，都没有效果。把可能的选项试了一圈后，发现修复该问题的方法是关掉 &lt;code&gt;Use Per Screen DPI&lt;&#x2F;code&gt;，该选项在 &lt;code&gt;Addon-&amp;gt;Classic User Interface&lt;&#x2F;code&gt; 下，如图&lt;br &#x2F;&gt;
&lt;img src=&quot;&#x2F;resources&#x2F;fcitx&#x2F;dpi_option.png&quot; alt=&quot;Use Per Screen DPI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;关掉该选项后，我的界面恢复了正常。&lt;&#x2F;p&gt;
&lt;p&gt;后记：ffmpeg 技巧两则&lt;br &#x2F;&gt;
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;superuser.com&#x2F;q&#x2F;268985&#x2F;295652&quot;&gt;去除视频文件音轨&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;askubuntu.com&#x2F;questions&#x2F;396883&#x2F;how-to-simply-convert-video-files-i-e-mkv-to-mp4&#x2F;396906&quot;&gt;mkv2mp4&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>从 Python Bytecode 的角度看 List Comprehension 生成 Tuple</title>
        <published>2021-03-28T00:00:00+00:00</published>
        <updated>2021-03-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/python-bytecode-list-comprehension-list-vs-tuple/"/>
        <id>https://blog.zhenbo.pro/python-bytecode-list-comprehension-list-vs-tuple/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/python-bytecode-list-comprehension-list-vs-tuple/">&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.w3schools.com&#x2F;python&#x2F;python_lists_comprehension.asp&quot;&gt;Python List Comprehension&lt;&#x2F;a&gt; 使用非常广泛。通常认为，在生成的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;glossary.html#term-sequence&quot;&gt;sequence&lt;&#x2F;a&gt; 定长的情况下，应该生成 Tuple 而非 List。出于好奇，我简单研究一下这两者在 Bytecode 的区别。&lt;&#x2F;p&gt;
&lt;p&gt;示例代码如下，很简单的 List Comprehension。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;def transform(x:int)-&amp;gt;int:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return (x * 2) + 5
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;original_data = [3, 7, 6, 5, 4, 4, 8]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;result_1 = [transform(x) for x in original_data]
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;result_1b = list(transform(x) for x in original_data)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;result_2 = tuple(transform(x) for x in original_data)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;首先，看一下最常见的写法 &lt;code&gt;[transform(x) for x in original_data]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;import dis
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;dis.dis(&amp;quot;result_1 = [transform(x) for x in original_data]&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_CONST               0 (&amp;lt;code object &amp;lt;listcomp&amp;gt; at 0x7fe3345ea3a0, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              2 LOAD_CONST               1 (&amp;#39;&amp;lt;listcomp&amp;gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              4 MAKE_FUNCTION            0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              6 LOAD_NAME                0 (original_data)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              8 GET_ITER
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             10 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             12 STORE_NAME               1 (result_1)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 LOAD_CONST               2 (None)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             16 RETURN_VALUE
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Disassembly of &amp;lt;code object &amp;lt;listcomp&amp;gt; at 0x7fe3345ea3a0, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 BUILD_LIST               0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              2 LOAD_FAST                0 (.0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;gt;&amp;gt;    4 FOR_ITER                12 (to 18)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              6 STORE_FAST               1 (x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              8 LOAD_GLOBAL              0 (transform)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             10 LOAD_FAST                1 (x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             12 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 LIST_APPEND              2
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             16 JUMP_ABSOLUTE            4
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;gt;&amp;gt;   18 RETURN_VALUE
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;blob&#x2F;master&#x2F;Lib&#x2F;dis.py&quot;&gt;dis module&lt;&#x2F;a&gt; 仅仅是 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;functions.html#compile&quot;&gt;built-in function compile&lt;&#x2F;a&gt; 的 wrapper。&lt;&#x2F;p&gt;
&lt;p&gt;如果稍稍写的臃肿一些，写成 &lt;code&gt;list(transform(x) for x in original_data)&lt;&#x2F;code&gt; 我们会发现字节码有小幅的变动。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;dis.dis(&amp;quot;result_1b = list(transform(x) for x in original_data)&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_NAME                0 (list)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              2 LOAD_CONST               0 (&amp;lt;code object &amp;lt;genexpr&amp;gt; at 0x7fe3344dda80, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              4 LOAD_CONST               1 (&amp;#39;&amp;lt;genexpr&amp;gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              6 MAKE_FUNCTION            0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              8 LOAD_NAME                1 (original_data)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             10 GET_ITER
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             12 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             16 STORE_NAME               2 (result_1b)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             18 LOAD_CONST               2 (None)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             20 RETURN_VALUE
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Disassembly of &amp;lt;code object &amp;lt;genexpr&amp;gt; at 0x7fe3344dda80, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_FAST                0 (.0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;gt;&amp;gt;    2 FOR_ITER                14 (to 18)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              4 STORE_FAST               1 (x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              6 LOAD_GLOBAL              0 (transform)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              8 LOAD_FAST                1 (x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             10 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             12 YIELD_VALUE
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 POP_TOP
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             16 JUMP_ABSOLUTE            2
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;gt;&amp;gt;   18 LOAD_CONST               0 (None)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             20 RETURN_VALUE
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;第一种方式 &lt;code&gt;[]&lt;&#x2F;code&gt; 调用了 &lt;code&gt;code object &amp;lt;listcomp&amp;gt;&lt;&#x2F;code&gt;，第二种方式 &lt;code&gt;list()&lt;&#x2F;code&gt; 则是用了 &lt;code&gt;code object &amp;lt;genexpr&amp;gt;&lt;&#x2F;code&gt;，并多了一次 &lt;code&gt;CALL_FUNCTION&lt;&#x2F;code&gt;。在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;blob&#x2F;a81fca6ec8e0f748f8eafa12fb12cf9e12df465c&#x2F;Python&#x2F;compile.c#L4733&quot;&gt;Python&#x2F;compile.c&lt;&#x2F;a&gt; 里，我们能看到这两者的实现几乎是一样的。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;compiler_genexp(struct compiler *c, expr_ty e)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    static identifier name;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (!name) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        name = PyUnicode_InternFromString(&amp;quot;&amp;lt;genexpr&amp;gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        if (!name)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            return 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    assert(e-&amp;gt;kind == GeneratorExp_kind);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return compiler_comprehension(c, e, COMP_GENEXP, name,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                  e-&amp;gt;v.GeneratorExp.generators,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                  e-&amp;gt;v.GeneratorExp.elt, NULL);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;compiler_listcomp(struct compiler *c, expr_ty e)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    static identifier name;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (!name) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        name = PyUnicode_InternFromString(&amp;quot;&amp;lt;listcomp&amp;gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        if (!name)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            return 0;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    assert(e-&amp;gt;kind == ListComp_kind);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return compiler_comprehension(c, e, COMP_LISTCOMP, name,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                  e-&amp;gt;v.ListComp.generators,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                  e-&amp;gt;v.ListComp.elt, NULL);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这两者都会交由 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;blob&#x2F;a81fca6ec8e0f748f8eafa12fb12cf9e12df465c&#x2F;Python&#x2F;compile.c#L4635&quot;&gt;&lt;code&gt;compiler_comprehension&lt;&#x2F;code&gt; 来处理&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;static int
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;compiler_comprehension(...){
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;--------------- snip ---------------
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (type != COMP_GENEXP) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        int op;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        switch (type) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        case COMP_LISTCOMP:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            op = BUILD_LIST;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            break;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        case COMP_SETCOMP:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            op = BUILD_SET; break;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        case COMP_DICTCOMP:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            op = BUILD_MAP; break;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        default:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            PyErr_Format(PyExc_SystemError,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                         &amp;quot;unknown comprehension type %d&amp;quot;, type);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            goto error_in_scope;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        ADDOP_I(c, op, 0);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (!compiler_comprehension_generator(c, generators, 0, 0, elt,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;                                          val, type))
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        goto error_in_scope;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    if (type != COMP_GENEXP) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        ADDOP(c, RETURN_VALUE);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    --------------- snip ---------------
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return 1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;error_in_scope: error:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    --------------- snip: error handling ---------------
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;到这里，我可以判断 &lt;code&gt;code object &amp;lt;listcomp&amp;gt;&lt;&#x2F;code&gt; 的返回值已经是 &lt;code&gt;list object&lt;&#x2F;code&gt;。而 &lt;code&gt;code object &amp;lt;genexpr&amp;gt;&lt;&#x2F;code&gt; 的返回值需要额外的一组指令&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_NAME                0 (list)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;到这里，我们再看一下尝试生成 tuple 的 Bytecode。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;dis.dis(&amp;quot;result_2 = tuple(transform(x) for x in original_data)&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_NAME                0 (tuple)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              2 LOAD_CONST               0 (&amp;lt;code object &amp;lt;genexpr&amp;gt; at 0x7ff4af1ed450, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              4 LOAD_CONST               1 (&amp;#39;&amp;lt;genexpr&amp;gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              6 MAKE_FUNCTION            0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              8 LOAD_NAME                1 (original_data)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             10 GET_ITER
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             12 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             16 STORE_NAME               2 (result_2)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             18 LOAD_CONST               2 (None)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             20 RETURN_VALUE
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Disassembly of &amp;lt;code object &amp;lt;genexpr&amp;gt; at 0x7ff4af1ed450, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_FAST                0 (.0)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;gt;&amp;gt;    2 FOR_ITER                14 (to 18)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              4 STORE_FAST               1 (x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              6 LOAD_GLOBAL              0 (transform)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              8 LOAD_FAST                1 (x)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             10 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             12 YIELD_VALUE
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 POP_TOP
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             16 JUMP_ABSOLUTE            2
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;        &amp;gt;&amp;gt;   18 LOAD_CONST               0 (None)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             20 RETURN_VALUE
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;与上文的 &lt;code&gt;result_1b&lt;&#x2F;code&gt; 非常类似，&lt;code&gt;code object &amp;lt;genexpr&amp;gt;&lt;&#x2F;code&gt; 的结果会被&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_NAME                0 (tuple)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;处理，并将结果绑定在变量名 &lt;code&gt;result_2&lt;&#x2F;code&gt; 上。&lt;&#x2F;p&gt;
&lt;p&gt;我们同样可以讲 &lt;code&gt;code object &amp;lt;genexpr&amp;gt;&lt;&#x2F;code&gt; 的结果直接绑定在某个名称上，而非调用 &lt;code&gt;list()&lt;&#x2F;code&gt; 或 &lt;code&gt;tuple()&lt;&#x2F;code&gt; 。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt;result_3 = (transform(x) for x in original_data)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt;type(result_3)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;lt;class &amp;#39;generator&amp;#39;&amp;gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt;dis.dis(&amp;quot;result_3 = (transform(x) for x in original_data)&amp;quot;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  1           0 LOAD_CONST               0 (&amp;lt;code object &amp;lt;genexpr&amp;gt; at 0x7ff4af0e0c90, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              2 LOAD_CONST               1 (&amp;#39;&amp;lt;genexpr&amp;gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              4 MAKE_FUNCTION            0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              6 LOAD_NAME                0 (original_data)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;              8 GET_ITER
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             10 CALL_FUNCTION            1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             12 STORE_NAME               1 (result_3)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             14 LOAD_CONST               2 (None)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;             16 RETURN_VALUE
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Disassembly of &amp;lt;code object &amp;lt;genexpr&amp;gt; at 0x7ff4af0e0c90, file &amp;quot;&amp;lt;dis&amp;gt;&amp;quot;, line 1&amp;gt;:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;-----snip----
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;但是，这样创建出的 &lt;code&gt;generator&lt;&#x2F;code&gt; 会在遍历时被消耗掉(consume)。如果有访问这些元素的需求，还是要第一时间将其转换为 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;glossary.html#term-sequence&quot;&gt;sequence&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt; result_3 = (transform(x) for x in original_data)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt; sum(result_3)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;109
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt; sum(result_3)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;运行环境&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt; import sys
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;gt;&amp;gt;&amp;gt; sys.version
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;&amp;#39;3.9.2 (default, Feb 20 2021, 00:00:00) \n[GCC 10.2.1 20201125 (Red Hat 10.2.1-9)]&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;推荐阅读：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;opensource.com&#x2F;article&#x2F;18&#x2F;4&#x2F;introduction-python-bytecode&quot;&gt;An introduction to Python bytecode&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;realpython.com&#x2F;cpython-source-code-guide&#x2F;&quot;&gt;Your Guide to the CPython Source Code&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;leanpub.com&#x2F;insidethepythonvirtualmachine&quot;&gt;Inside the Python Virtual Machine&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>如何让 Git Submodule 恢复初始状态</title>
        <published>2021-02-09T00:00:00+00:00</published>
        <updated>2021-02-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/git-submodule-reset-all/"/>
        <id>https://blog.zhenbo.pro/git-submodule-reset-all/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/git-submodule-reset-all/">&lt;p&gt;这几天在尝试编译 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ValveSoftware&#x2F;Proton&quot;&gt;Proton&lt;&#x2F;a&gt; ，不小心把 submodule 搞乱了。如下图所示，submodule 对应的是 &lt;code&gt;origin&#x2F;master&#x2F;HEAD&lt;&#x2F;code&gt;，而不是应有的 commit。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;proton$ git status
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;On branch proton_5.13
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Your branch is up to date with &amp;#39;origin&#x2F;proton_5.13&amp;#39;.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Changes not staged for commit:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   FAudio (new commits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   OpenXR-SDK (new commits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   fonts&#x2F;liberation-fonts (new commits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   gst-orc (new commits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   gst-plugins-base (new commits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   gst-plugins-good (new commits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   gstreamer (new commits)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   vkd3d-proton (new commits)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;7882603&#x2F;how-to-revert-a-git-submodule-pointer-to-the-commit-stored-in-the-containing-rep&quot;&gt;参考这个回答&lt;&#x2F;a&gt;，使用 &lt;code&gt;git submodule update --init&lt;&#x2F;code&gt; 修复。&lt;&#x2F;p&gt;
&lt;p&gt;因为网络问题，我的 openvr 最开始没能下载成功。反复尝试 fetch 以后，发现我的 &lt;code&gt;git module&lt;&#x2F;code&gt; 损坏了。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ git status
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fatal: not a git repository: openvr&#x2F;..&#x2F;.git&#x2F;modules&#x2F;openvr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ git submodule
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; aa158544b6402e6a37517c0ffa142a5edae927b0 FAudio (20.12)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; 5197afbf199c026eca82a47a8573ed10b0c6fa4e OpenXR-SDK (release-1.0.13)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; 85c70ad5db0863ffbc4afee2ca57a6e6e92e8ef6 dxvk (experimental-dxvk-5.13-20210115)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; 9510ebd130bcb4dfc76b053b438d8a97a3ed4600 fonts&#x2F;liberation-fonts (2.00.3-2-g9510ebd)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; 9901a96eaff271c2d3b595214213f6805ff803c8 gst-orc (0.4.31)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; 9d3581b2e6f12f0b7e790d1ebb63b90cf5b1ef4e gst-plugins-base (1.16.0-91-g9d3581b2e)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; ce0723527aa37d5f4d19ef8021c0b2eb8f83b08d gst-plugins-good (1.16.0-48-gce0723527)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; 129493687793cbc109d6211bb0e465218e383e9d gstreamer (1.16.0-58-g129493687)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fatal: not a git repository: openvr&#x2F;..&#x2F;.git&#x2F;modules&#x2F;openvr
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;我删除了 &lt;code&gt;.git&#x2F;config&lt;&#x2F;code&gt;和 &lt;code&gt;.gitmodules&lt;&#x2F;code&gt; 中 openvr 的记录。接着就可以运行&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ rm -rf openvr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ git status
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;On branch proton_5.13
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Your branch is up to date with &amp;#39;origin&#x2F;proton_5.13&amp;#39;.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Changes not staged for commit:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  (use &amp;quot;git add&#x2F;rm &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	modified:   .gitmodules
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	deleted:    openvr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;到这里，就可以还原 &lt;code&gt;.gitmodules&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git restore .gitmodules
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git status
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;On branch proton_5.13
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Your branch is up to date with &amp;#39;origin&#x2F;proton_5.13&amp;#39;.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Changes not staged for commit:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  (use &amp;quot;git add&#x2F;rm &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;  (use &amp;quot;git restore &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;	deleted:    openvr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;有趣的是，我尝试 &lt;code&gt;submodulle update&lt;&#x2F;code&gt; 会遇到网络问题，而 &lt;code&gt;git clone&lt;&#x2F;code&gt; 则可以正常执行。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# Fail
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule update --depth=1 openvr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# Success
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;ValveSoftware&#x2F;openvr.git --depth=1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;用&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;endle.github.io&#x2F;2021&#x2F;02&#x2F;01&#x2F;git-submodule-fetch-from-local&#x2F;&quot;&gt;上一篇文章的技巧&lt;&#x2F;a&gt;，我也成功初始化了 openvr。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;ValveSoftware&#x2F;openvr.git --depth=1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git fetch origin master --deepin=20
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule set-url openvr &#x2F;dev&#x2F;shm&#x2F;openvr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule update --init openvr
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git restore . --recurse-submodules
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最后，使用 Vagrant 编译代码时，可能会遇到网络问题。参考&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.csdn.net&#x2F;haiyanghan&#x2F;article&#x2F;details&#x2F;107168972&quot;&gt;这篇 CSDN 的文章&lt;&#x2F;a&gt;，可以使用支持断点续传的 &lt;code&gt;wget&lt;&#x2F;code&gt; 下载镜像。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt; proton$ vagrant up
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Platform: 4 CPUs, 15979 MB memory
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Bringing machine &amp;#39;debian10&amp;#39; up with &amp;#39;libvirt&amp;#39; provider...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Box &amp;#39;generic&#x2F;debian10&amp;#39; could not be found. Attempting to find and install...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    debian10: Box Provider: libvirt
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    debian10: Box Version: &amp;gt;= 0
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Loading metadata for box &amp;#39;generic&#x2F;debian10&amp;#39;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    debian10: URL: https:&#x2F;&#x2F;vagrantcloud.com&#x2F;generic&#x2F;debian10
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Adding box &amp;#39;generic&#x2F;debian10&amp;#39; (v3.2.2) for provider: libvirt
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    debian10: Downloading: https:&#x2F;&#x2F;vagrantcloud.com&#x2F;generic&#x2F;boxes&#x2F;debian10&#x2F;versions&#x2F;3.2.2&#x2F;providers&#x2F;libvirt.box
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Box download is resuming from prior download progress
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Download redirected to host: vagrantcloud-files-production.s3.amazonaws.com
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Progress: 0% (Rate: 46507&#x2F;s, Estimated time remaining: 14:32:45)^C==&amp;gt; debian10: Waiting for cleanup before exiting...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Box download was interrupted. Exiting.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    debian10: Calculating and comparing box checksum...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;The checksum of the downloaded box did not match the expected
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;value. Please verify that you have the proper URL setup and that
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;you&amp;#39;re downloading the proper file.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Expected: 76118312e5a2f227544660566b7d0f6ad3d6bf50ff1fc0b92602cda66d9cf3a6
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Received: 9d80712a57190fb2821f11ac46e677be1c467afc15e02d3d29f84c069d4f9e8e
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;wget -c https:&#x2F;&#x2F;vagrantcloud.com&#x2F;generic&#x2F;boxes&#x2F;debian10&#x2F;versions&#x2F;3.2.2&#x2F;providers&#x2F;libvirt.box
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;下载成功后，再执行 &lt;code&gt;vagrant box add&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;~$ sha256sum libvirt.box 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;76118312e5a2f227544660566b7d0f6ad3d6bf50ff1fc0b92602cda66d9cf3a6  libvirt.box
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;vagrant box add generic&#x2F;debian10 libvirt.box
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Platform: 4 CPUs, 15979 MB memory
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Bringing machine &amp;#39;debian10&amp;#39; up with &amp;#39;libvirt&amp;#39; provider...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Uploading base box image as volume into Libvirt storage...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Creating image (snapshot of base box volume).
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;==&amp;gt; debian10: Creating domain with the following settings...
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Git Submodule 从本地源初始化</title>
        <published>2021-02-01T00:00:00+00:00</published>
        <updated>2021-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/git-submodule-fetch-from-local/"/>
        <id>https://blog.zhenbo.pro/git-submodule-fetch-from-local/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/git-submodule-fetch-from-local/">&lt;h4 id=&quot;bei-jing-jie-shao&quot;&gt;背景介绍&lt;&#x2F;h4&gt;
&lt;p&gt;在使用 Wine 运行 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;appdb.winehq.org&#x2F;objectManager.php?sClass=version&amp;amp;iId=37229&quot;&gt;Magic: The Gathering Arena&lt;&#x2F;a&gt; 的过程中，我想自行编译 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;madewokherd&#x2F;wine-mono&quot;&gt;Wine-Mono&lt;&#x2F;a&gt;。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;madewokherd&#x2F;wine-mono&quot;&gt;Wine-Mono&lt;&#x2F;a&gt; 用 &lt;code&gt;git submodule&lt;&#x2F;code&gt; 将 Mono 的 codebase 囊括了进来。&lt;&#x2F;p&gt;
&lt;p&gt;在此之前，我在电脑上已经下载过了 Mono 的代码 (&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;mono&#x2F;mono.git --depth=1&lt;&#x2F;code&gt;)。与其重复访问 GitHub，我希望能从本地的代码库初始化 submodule。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shi-yong-ben-di-yuan&quot;&gt;使用本地源&lt;&#x2F;h4&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;madewokherd&#x2F;wine-mono.git
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd wine-mono
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule init
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule set-url mono  &#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;mono
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule update mono 
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在完成初始化后，再还原该设置&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule set-url mono https:&#x2F;&#x2F;github.com&#x2F;madewokherd&#x2F;wine-mono.git
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;qie-huan-hui-shang-you-dai-ma&quot;&gt;切换回上游代码&lt;&#x2F;h4&gt;
&lt;p&gt;因为硬盘空间紧缺，我删掉了原有的 &lt;code&gt;&#x2F;home&#x2F;lizhenbo&#x2F;src&#x2F;mono&lt;&#x2F;code&gt; 。想要编辑上游代码，可以运行&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;cd wine-mono&#x2F;mono
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git remote add upstream https:&#x2F;&#x2F;github.com&#x2F;mono&#x2F;mono.git
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git fetch upstream master 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git checkout upstream&#x2F;master
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git submodule update --recursive --depth=1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在工作完成后，可以再&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;61751340&#x2F;1166518&quot;&gt;恢复 submodule 配置&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;git restore . --recurse-submodules
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;fan-wai-huo-qu-geng-shen-de-li-shi-ji-lu&quot;&gt;番外：获取更深的历史记录&lt;&#x2F;h4&gt;
&lt;p&gt;初次下载项目代码使用了 &lt;code&gt;--depth=1&lt;&#x2F;code&gt; 。如果想要看稍微久一些的历史记录，不需要 &lt;code&gt;git fetch --unshallow&lt;&#x2F;code&gt; 下载完整内容，而可以用 &lt;code&gt;git fetch origin master --deepen=10&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;我尝试了 &lt;code&gt;shallow-exclude&lt;&#x2F;code&gt;，但遇到了如下问题&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ git fetch origin master --shallow-exclude=fad8c19d8600b34e46984ba6dbb600f9343cd773 -v
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;POST git-upload-pack (306 bytes)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;POST git-upload-pack (400 bytes)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;error: RPC failed; curl 92 HTTP&#x2F;2 stream 0 was not closed cleanly: Unknown error code (err 2)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fatal: error reading section header &amp;#39;acknowledgments&amp;#39;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;59474908&#x2F;1166518   
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ git config http.version HTTP&#x2F;1.1
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ git fetch origin master --shallow-exclude=fad8c19d8600b34e46984ba6dbb600f9343cd773 -v
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;POST git-upload-pack (306 bytes)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;POST git-upload-pack (400 bytes)
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;error: RPC failed; curl 18 transfer closed with outstanding read data remaining
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;fatal: error reading section header &amp;#39;acknowledgments&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>经SSH将Fedora 30升级至31</title>
        <published>2020-01-01T00:00:00+00:00</published>
        <updated>2020-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/upgrade-fedora-30-to-31-via-ssh/"/>
        <id>https://blog.zhenbo.pro/upgrade-fedora-30-to-31-via-ssh/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/upgrade-fedora-30-to-31-via-ssh/">&lt;p&gt;之前提到，我的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;endle.github.io&#x2F;2018&#x2F;09&#x2F;10&#x2F;distcc-to-speedup&#x2F;&quot;&gt;客厅里有了一台服务器&lt;&#x2F;a&gt;。随着 Fedora 31 发布，原有的 Fedora 30 系统得到的更新变得很少，我也有了更新系统的打算。搜索了一些资料，有&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unix.stackexchange.com&#x2F;a&#x2F;58724&#x2F;258214&quot;&gt;在2012年的回答&lt;&#x2F;a&gt;说需要使用单独的ssh daemon进行F17升F18的操作。不过，也有人提到，新版本的 &lt;code&gt;dnf-plugin-system-upgrade&lt;&#x2F;code&gt; 插件可以正确处理这种情况。在做好重装系统的准备后，我决定试一试&lt;code&gt;dnf&lt;&#x2F;code&gt;的能力。&lt;&#x2F;p&gt;
&lt;p&gt;按照&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.fedoraproject.org&#x2F;en-US&#x2F;quick-docs&#x2F;dnf-system-upgrade&#x2F;&quot;&gt;官方手册&lt;&#x2F;a&gt;，顺次在 ssh 中执行如下命令&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;sudo dnf upgrade --refresh
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;sudo dnf install dnf-plugin-system-upgrade
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;sudo dnf system-upgrade download --refresh --releasever=31
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;执行这些内容不会影响 ssh 连接。在运行成功后，可以进行重启并更新&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sudo dnf system-upgrade reboot&quot; class=&quot;language-sudo dnf system-upgrade reboot z-code&quot;&gt;&lt;code class=&quot;language-sudo dnf system-upgrade reboot&quot; data-lang=&quot;sudo dnf system-upgrade reboot&quot;&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这一次重启的耗时会非常长，在我的电脑上持续了至少一小时。因为我在服务器上设定了 DHCP，所以系统升级后被分配了一个新的 IP 地址。在修改笔记本的 &lt;code&gt;~&#x2F;.ssh&#x2F;config&lt;&#x2F;code&gt; 以及 &lt;code&gt;known_hosts&lt;&#x2F;code&gt; 以后，就可以顺利访问自己的服务器了。如果当初配置时没有偷懒，而是分配了固定的IP，那系统升级就能无缝完成了。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fedora 删除 swap 后修复 Kernel Parameters</title>
        <published>2019-07-06T00:00:00+00:00</published>
        <updated>2019-07-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/fedora-remove-swap-and-fix-kernel-parameters/"/>
        <id>https://blog.zhenbo.pro/fedora-remove-swap-and-fix-kernel-parameters/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/fedora-remove-swap-and-fix-kernel-parameters/">&lt;p&gt;笔者近日为自己的笔记本电脑升级到了 32G 内存，就想着删掉 swap 分区，为自己捉襟见肘的 SSD 释放一些空间。参考着 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;LVM&quot;&gt;Arch Wiki&lt;&#x2F;a&gt;，整体而言比较顺利，但也有一点小小的插曲。&lt;&#x2F;p&gt;
&lt;p&gt;首先，运行 &lt;code&gt;# lvs&lt;&#x2F;code&gt; 查看分区情况。我的系统里，Volume Group 是 &lt;code&gt;fedora_zhenbo&lt;&#x2F;code&gt;。 删除 swap 分区不需要运行 &lt;code&gt;# umount &#x2F;&amp;lt;mountpoint&amp;gt;&lt;&#x2F;code&gt; 。相反，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;serverfault.com&#x2F;a&#x2F;684792&quot;&gt;运行 &lt;code&gt;swapoff -a&lt;&#x2F;code&gt; 即可&lt;&#x2F;a&gt;。 接下来，删除 LV &lt;code&gt;# lvremove &amp;lt;volume_group&amp;gt;&#x2F;&amp;lt;logical_volume&amp;gt;&lt;&#x2F;code&gt;，并修改 &lt;code&gt;etc&#x2F;fstab&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;可是，重启后，系统并没有正确启动，而是进入了 dracut 环境，提示 &lt;code&gt;fedora_zhenbo&#x2F;swap&lt;&#x2F;code&gt; 未找到。重新检查了一遍，发现 Kernel parameters 里有一行 &lt;code&gt;rd.lvm.lv=fedora_zhenbo&#x2F;swap&lt;&#x2F;code&gt;。将其删去后，可以顺利启动。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20221226031847&#x2F;https:&#x2F;&#x2F;docs.fedoraproject.org&#x2F;en-US&#x2F;fedora&#x2F;rawhide&#x2F;system-administrators-guide&#x2F;kernel-module-driver-configuration&#x2F;Working_with_the_GRUB_2_Boot_Loader&#x2F;&quot;&gt;尝试运行 &lt;code&gt;grub2-mkconfig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;，生成的 &lt;code&gt;grub.cfg&lt;&#x2F;code&gt; 里依旧有 swap。把 &lt;code&gt;&#x2F;etc&#x2F;grub.d&#x2F;&lt;&#x2F;code&gt; 翻了一遍，也没有任何头绪。&lt;&#x2F;p&gt;
&lt;p&gt;幸运的是，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;orenbell.com&#x2F;&quot;&gt;网友 Oren Bell&lt;&#x2F;a&gt; 在 2017 年&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;unix.stackexchange.com&#x2F;questions&#x2F;412149&#x2F;grub2-mkconfig-isnt-generating-correct-mount-paths-and-also-how-do-i-get-rid-o&quot;&gt;遇到了一样的问题&lt;&#x2F;a&gt;。LVM 分区被硬编码到了 &lt;code&gt;&#x2F;etc&#x2F;default&#x2F;grub&lt;&#x2F;code&gt;。编辑该文件的 &lt;code&gt;GRUB_CMDLINE_LINUX=&lt;&#x2F;code&gt; 一栏，删掉 &lt;code&gt;rd.lvm.lv=fedora_zhenbo&#x2F;swap&lt;&#x2F;code&gt; 后重新 &lt;code&gt;grub2-mkconfig -o &#x2F;boot&#x2F;efi&#x2F;EFI&#x2F;fedora&#x2F;grub.cfg&lt;&#x2F;code&gt; 即可。&lt;&#x2F;p&gt;
&lt;p&gt;最后，参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;LVM&quot;&gt;Arch Wiki&lt;&#x2F;a&gt;，把释放出来的空间添加到 home&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# lvresize -l +100%FREE --resizefs fedora_zhenbo&#x2F;home&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>跟着 clang 的 libcxx 学习二分查找</title>
        <published>2018-10-26T00:00:00+00:00</published>
        <updated>2018-10-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/clang-binary-search/"/>
        <id>https://blog.zhenbo.pro/clang-binary-search/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/clang-binary-search/">&lt;p&gt;哪个算法简单到初学编程的人都能轻松实现，但有多年编程经验的人也可能会写出严重的 bug 呢？没错，正是二分查找。既然普通人的二分查找容易写错，那专业人士会如何实现二分查找呢？不妨参考一下 clang 7.0 的实现。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lower-bound-zai-stl-de-ding-yi&quot;&gt;&lt;code&gt;lower_bound&lt;&#x2F;code&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.cppreference.com&#x2F;w&#x2F;cpp&#x2F;algorithm&#x2F;lower_bound&quot;&gt;在 STL 的定义&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;给定有序区间 &lt;code&gt;[first, last)&lt;&#x2F;code&gt;, &lt;code&gt;val&lt;&#x2F;code&gt; 和 &lt;code&gt;operator &amp;lt;&lt;&#x2F;code&gt;。寻找第一个元素 &lt;code&gt;p&lt;&#x2F;code&gt; 使得 &lt;code&gt;A[p] &amp;lt;  val&lt;&#x2F;code&gt; 为&lt;strong&gt;假&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;我们也可以这样理解，对于任意 &lt;code&gt;i &amp;lt; j&lt;&#x2F;code&gt;，若 &lt;code&gt;A[i]&amp;lt;val&lt;&#x2F;code&gt; 为假，则 &lt;code&gt;A[j]&amp;lt;val&lt;&#x2F;code&gt;
必为假。如下图，我们要寻找第一个绿色的元素&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;binary_search&#x2F;lower_bound.png&quot; alt=&quot;lower_bound&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;clang-7-0-de-shi-xian&quot;&gt;Clang 7.0 的实现&lt;&#x2F;h3&gt;
&lt;p&gt;让我们看一看 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;llvm-mirror&#x2F;libcxx&#x2F;blob&#x2F;dffe9e0f1dde084f2aab8010345aeb1b7c8f7d4c&#x2F;include&#x2F;algorithm#L4190&quot;&gt;lower_bound 的实现&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
{% highlight C++ %}
__lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp&amp;amp; _&lt;em&gt;value&lt;&#x2F;em&gt;, _Compare __comp)
{
typedef typename iterator_traits&amp;lt;_ForwardIterator&amp;gt;::difference_type difference_type;
difference_type __len = _VSTD::distance(__first, __last);
while (__len != 0)
{
difference_type __l2 = __len &#x2F; 2;
_ForwardIterator __m = __first;
_VSTD::advance(__m, __l2);
if (__comp(*__m, _&lt;em&gt;value&lt;&#x2F;em&gt;))
{
__first = ++__m;
__len -= __l2 + 1;
}
else
__len = __l2;
}
return __first;
}
&#x2F;&#x2F; This file is dual licensed under the MIT and the UIUC license.
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zhe-duan-dai-ma-de-yao-dian&quot;&gt;这段代码的要点&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;定义：&lt;code&gt;first&lt;&#x2F;code&gt; 是第一个&lt;strong&gt;有可能&lt;&#x2F;strong&gt;使 &lt;code&gt;*first&amp;lt;val&lt;&#x2F;code&gt; 为 &lt;strong&gt;假&lt;&#x2F;strong&gt;的元素。或者说，是第一个可能为绿色的元素。&lt;&#x2F;li&gt;
&lt;li&gt;迭代：&lt;code&gt;l2 &amp;lt; len&lt;&#x2F;code&gt; 恒成立，因此 &lt;code&gt;first+l2&lt;&#x2F;code&gt; 就不会越界。同时，每次迭代时 &lt;code&gt;len&lt;&#x2F;code&gt; 严格递减&lt;&#x2F;li&gt;
&lt;li&gt;迭代结束：&lt;code&gt;l2 == 0&lt;&#x2F;code&gt; 当且仅当 &lt;code&gt;len == 1&lt;&#x2F;code&gt;。这是我们最后一次进行迭代&lt;&#x2F;li&gt;
&lt;li&gt;特殊情况：如果所有的元素都为假，那最后一次迭代时， &lt;code&gt;++m&lt;&#x2F;code&gt; 的结果正是 &lt;code&gt;last&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;相比我之前写的不时丢弃真值、出现死循环的二分查找，clang 的代码可是高到不知道哪里去了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;llvm-mirror&#x2F;libcxx&#x2F;blob&#x2F;dffe9e0f1dde084f2aab8010345aeb1b7c8f7d4c&#x2F;include&#x2F;algorithm#L4238&quot;&gt;upper_bound 的实现&lt;&#x2F;a&gt; 看起来是相反的，不过从布尔值的角度看，与 &lt;code&gt;lower_bound&lt;&#x2F;code&gt; 是一样的，我就不复制代码了。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h3&gt;
&lt;p&gt;上文中的示意图使用 R 语言绘制，参考了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;50438532&#x2F;1166518&quot;&gt;Stack Overflow 上画棋盘的讨论&lt;&#x2F;a&gt;。代码如下&lt;br &#x2F;&gt;
{% highlight R %}
b &amp;lt;- matrix(nrow=1,ncol=6)&lt;br &#x2F;&gt;
colorindex &amp;lt;- c(rep(0, 4), rep(1, 2))&lt;&#x2F;p&gt;
&lt;h1 id=&quot;for-each-square&quot;&gt;for each square&lt;&#x2F;h1&gt;
&lt;p&gt;colors &amp;lt;- c(&quot;red&quot;, &quot;green&quot;)[colorindex+1] # choose colors
side &amp;lt;- 1&#x2F;8                               # side of one square
ux &amp;lt;- col(b)*side                         # upper x values
lx &amp;lt;- ux-side                             # lower x values
uy &amp;lt;- row(b)*side                         # upper y
ly &amp;lt;- uy-side                             # upper y
plot.new()                                # initialize R graphics
rect(lx, ly, ux, uy, col=colors, asp=1)   # draw the board
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;h4 id=&quot;geng-xin-yu-2023-08-20&quot;&gt;更新于 2023-08-20&lt;&#x2F;h4&gt;
&lt;p&gt;这几天翻出了这篇文章，照着 clang 的代码，怎么写都不对。重读了一遍，才发现我在 2018
年撰写本文时，对&lt;code&gt;真&#x2F;假&lt;&#x2F;code&gt;的使用有些混乱。抽出周末的时间，对文章进行了订正。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>用 distcc 加快编译 续篇-clang</title>
        <published>2018-09-15T00:00:00+00:00</published>
        <updated>2018-09-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/distcc-to-speedup-expansion/"/>
        <id>https://blog.zhenbo.pro/distcc-to-speedup-expansion/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/distcc-to-speedup-expansion/">&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;endle.github.io&#x2F;2018&#x2F;09&#x2F;10&#x2F;distcc-to-speedup&#x2F;&quot;&gt;上一篇文章&lt;&#x2F;a&gt; 简单介绍了使用 &lt;code&gt;distcc&lt;&#x2F;code&gt; 编译 wine 代码。有人在朋友圈里问我，能不能用 &lt;code&gt;clang&lt;&#x2F;code&gt; 编译。我当时的回答是，为什么不能呢？这周在摸鱼的时候，我完成了测试。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;li-lun-fen-xi&quot;&gt;理论分析&lt;&#x2F;h4&gt;
&lt;p&gt;我看了一下 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;distcc&#x2F;distcc&#x2F;blob&#x2F;24f73c5cd8f839bd520eb52e91d0d26e07689373&#x2F;src&#x2F;distcc.c#L249&quot;&gt;&lt;code&gt;distcc&lt;&#x2F;code&gt; 的代码&lt;&#x2F;a&gt;，发现其机制还是比较简单的。&lt;code&gt;distcc&lt;&#x2F;code&gt; 后的一个参数会被当成编译器名称。理论上，不管是 gcc 还是 clang，都应该可以用类似的机制在多台电脑间分发。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shi-zhan-yan-lian&quot;&gt;实战演练&lt;&#x2F;h4&gt;
&lt;p&gt;因为这里仅仅是为了测试，而不是实际运行，我就不用麻烦地在 32-bit 和 64-bit下编译 wine 两次了。workflow如下&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight bash %}
function mkwine_clang()
{
export CC=&quot;clang-6.0&quot;
export CXX=&quot;clang-6.0&quot;
export CFLAGS=&quot;-O0&quot;
export JOBS=8
mkdir -p ~&#x2F;src&#x2F;wine_n_extras&#x2F;wine-clang
cd ~&#x2F;src&#x2F;wine_n_extras&#x2F;wine-clang
..&#x2F;wine&#x2F;configure --enable-win64
make -j$JOBS
}
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.winehq.org&#x2F;Clang&quot;&gt;wine 项目组对 clang 投入的精力&lt;&#x2F;a&gt; 是有回报的。我没有做任何额外的设置，就在本机成功编译了 wine&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;apt.llvm.org&#x2F;&quot;&gt;在 Ubuntu 上安装 clang&lt;&#x2F;a&gt; 非常简单。很有趣的是，在我的 Ubuntu 14 工作站上，&lt;code&gt;clang&lt;&#x2F;code&gt; 默认是指向 &lt;code&gt;clang-3.3&lt;&#x2F;code&gt; 到 &lt;code&gt;clang-3.5&lt;&#x2F;code&gt; 的软链接。不过，在工作站和笔记本上，&lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;clang-6.0&lt;&#x2F;code&gt; 都是可用的。因此，我们可以对编译的 workflow 稍加修改&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight bash %}
function mkwine_clang()
{
export CC=&quot;distcc clang-6.0&quot;
export CXX=&quot;distcc clang-6.0&quot;
export CFLAGS=&quot;-O0&quot;
export JOBS=8
export DISTCC_HOSTS=&quot;@beddp,lzo&quot;
mkdir -p ~&#x2F;src&#x2F;wine_n_extras&#x2F;wine-clang
cd ~&#x2F;src&#x2F;wine_n_extras&#x2F;wine-clang
..&#x2F;wine&#x2F;configure --enable-win64
make -j$JOBS
}
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;在我的电脑上，这一流程非常的顺利。理论分析和实践演练同时成功的情景真是太少了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;In theory, theory and practice are the same. In practice, they are not.  -Anonymous&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bu-jin-jin-shi-c&quot;&gt;不仅仅是C&lt;&#x2F;h4&gt;
&lt;p&gt;上文提到，&lt;code&gt;distcc&lt;&#x2F;code&gt; 判断编译器的方法很简单。那么，能不能用它来分发其他语言呢？很抱歉，这可能不行。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;distcc&#x2F;distcc&#x2F;blob&#x2F;5de24577858687106c27dce1c1ae53edac2f6a6f&#x2F;src&#x2F;arg.c#L130&quot;&gt;&lt;code&gt;dcc_scan_args&lt;&#x2F;code&gt; 函数会尝试解析编译选项&lt;&#x2F;a&gt;。如果要使用其他的编译器，个人猜测，必要条件包括使用与 &lt;code&gt;gcc&lt;&#x2F;code&gt;，&lt;code&gt;clang&lt;&#x2F;code&gt; 相同的编译选项。[Rust 社区在 2017 年年末进行了讨论](https:&#x2F;&#x2F;users.rust-lang.org&#x2F;t&#x2F;contract-opportunity-mozilla-distributed-compilation-cache-written-in-rust&#x2F;13898）,但好像并没有得出太好的解决方案。&lt;br &#x2F;&gt;
不过，这也称不上太大的损失。人生苦短，在 C&#x2F;C++&#x2F;Rust 以外，我们也不再需要一门编译超慢的语言了，不是吗？&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>用 distcc 加快编译</title>
        <published>2018-09-10T00:00:00+00:00</published>
        <updated>2018-09-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/distcc-to-speedup/"/>
        <id>https://blog.zhenbo.pro/distcc-to-speedup/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/distcc-to-speedup/">&lt;p&gt;我的卧室迎来了新成员 - 拥有双 Xeon E5649 的工作站。在安顿好后，我就第一时间开始研究用 &lt;code&gt;distcc&lt;&#x2F;code&gt; 来加快编译速度了。毕竟，与其让我的 XPS 15 被折磨，不如把这个工作交给局域网内的工作站。官网提供了一份 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cdn.rawgit.com&#x2F;distcc&#x2F;distcc&#x2F;9a09372bd3f420cdd7021e52eda14fa536a3c10e&#x2F;doc&#x2F;web&#x2F;index.html&quot;&gt;60-second instructions&lt;&#x2F;a&gt; ，不妨一试。如果你是在60秒内完成了配置的幸运儿，那请关掉本页面。如果你遇到了阻碍，那么请看下一节。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;server-slaves-gong-zuo-zhan-she-zhi&quot;&gt;Server &#x2F; Slaves &#x2F; 工作站设置&lt;&#x2F;h3&gt;
&lt;p&gt;为了方便地调试，我选择在工作站上运行 &lt;code&gt;distccd --no-detach  --daemon  --allow 192.168.1.0&#x2F;24 --log-stderr --verbose -j 1&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
在新版本的 &lt;code&gt;distcc&lt;&#x2F;code&gt; 里，&lt;code&gt;--allow&lt;&#x2F;code&gt;参数已经是必选项了。在完成了调试后，我在生产环境中设置成了 &lt;code&gt;-j 10&lt;&#x2F;code&gt;，避免将 12 核都跑满后，ssh访问工作站会有一定延迟。另一种解决方法是，通过设置 &lt;code&gt;nice&lt;&#x2F;code&gt; 避免吃光资源。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ben-di-she-zhi&quot;&gt;本地设置&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;zhi-xian-ren-wu-ssh-name&quot;&gt;支线任务 - SSH name&lt;&#x2F;h4&gt;
&lt;p&gt;通过&lt;code&gt;~&#x2F;.ssh&#x2F;config&lt;&#x2F;code&gt; 可以给自己局域网中的电脑分配一个简单易记的名字。我将我的工作站命名为了 &lt;code&gt;beddp&lt;&#x2F;code&gt;。不设置不影响使用。如果第一次接触这一概念，可以阅读 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;serverfault.com&#x2F;a&#x2F;215027&quot;&gt;https:&#x2F;&#x2F;serverfault.com&#x2F;a&#x2F;215027&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zhu-xian-ren-wu&quot;&gt;主线任务&lt;&#x2F;h4&gt;
&lt;p&gt;首先要设置环境变量 &lt;code&gt;export DISTCC_HOSTS=&quot;@beddp,lzo,cpp&quot;&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
&lt;code&gt;@beddp&lt;&#x2F;code&gt; 的意思是，通过 ssh 连接到名为 &lt;code&gt;beddp&lt;&#x2F;code&gt; 的主机。&lt;code&gt;,lzo,cpp&lt;&#x2F;code&gt; 这两个设置是为了开启 pump模式。详情可以阅读 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Distcc#For_use_without_makepkg&quot;&gt;Arch Wiki&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;测试代码选择&lt;br &#x2F;&gt;
{% highlight C %}
#include &amp;lt;stdio.h&amp;gt;
int main(){
puts(&lt;strong&gt;VERSION&lt;&#x2F;strong&gt;); return 0;
}
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;在本地运行 &lt;code&gt;gcc k.c -o local.out &amp;amp;&amp;amp; .&#x2F;local.out&lt;&#x2F;code&gt; 的结果是 &lt;code&gt;8.1.1 20180712 (Red Hat 8.1.1-5)&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
在服务器运行 &lt;code&gt;gcc k.c -o server.out &amp;amp;&amp;amp; .&#x2F;server.out&lt;&#x2F;code&gt; 的结果是 &lt;code&gt;4.8.4&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;gcc 版本的差异可能会导致各类潜在的问题。但现阶段，可以以此测试 &lt;code&gt;distcc&lt;&#x2F;code&gt; 是否正常工作&lt;&#x2F;p&gt;
&lt;p&gt;在本机运行 &lt;code&gt;DISTCC_VERBOSE=1 pump distcc gcc -c k.c -o k.o &amp;amp;&amp;amp; gcc k.o -o server.out &amp;amp;&amp;amp; .&#x2F;server.out&lt;&#x2F;code&gt; 结果是 &lt;code&gt;4.8.4&lt;&#x2F;code&gt;。简单来说，添加了 &lt;code&gt;pump&lt;&#x2F;code&gt; 指令，意味着测试文件和涉及到的头文件会被发送到了工作站上，进行预处理以及编译，并在本地完成了最终的链接。我们也可以运行 &lt;code&gt;distcc gcc -c k.c -o k.o &amp;amp;&amp;amp; gcc k.o -o server_nopump.out &amp;amp;&amp;amp; .&#x2F;server_nopump.out&lt;&#x2F;code&gt; 结果就变为了 &lt;code&gt;8.1.1 20180712 (Red Hat 8.1.1-5)&lt;&#x2F;code&gt;。 可以看到，没有了 &lt;code&gt;pump&lt;&#x2F;code&gt; 指令，预处理工作是在本地处理的&lt;&#x2F;p&gt;
&lt;p&gt;在本机也可以运行 &lt;code&gt;distccmon-text 2&lt;&#x2F;code&gt;。这会启动一个两秒钟刷新一次的监视器，显示任务的分配状态。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xiang-mu-shi-zhan&quot;&gt;项目实战&lt;&#x2F;h3&gt;
&lt;p&gt;让我们开一瓶香槟，选一个项目实战一下。按照教程，我们只需要把运行 &lt;code&gt;make -j8 CC=distcc&lt;&#x2F;code&gt;即可。不过，很遗憾，我在 wine 项目上的测试失败了。即便 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.winehq.org&#x2F;Gcc&quot;&gt;Wine Wiki&lt;&#x2F;a&gt; 显示 gcc 4.8.4 应当可以成功编译 wine，&lt;code&gt;distcc&lt;&#x2F;code&gt; 也没有顺利地接过工作。相反，它不停地显示 &lt;code&gt;(dcc_build_somewhere) Warning: failed to distribute, running locally instead&lt;&#x2F;code&gt;。&lt;br &#x2F;&gt;
我在工作站上编译安装了 gcc 8。&lt;code&gt;distccd&lt;&#x2F;code&gt;命令支持指定 PATH，但我选择了笨办法：在本机和工作站上，都设置了一个软链接 &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;bin&#x2F;gcc-8&lt;&#x2F;code&gt;。在编译前，设置 &lt;code&gt;export CC=&quot;ccache distcc gcc-8&quot;&lt;&#x2F;code&gt;。在 ccache 缓存失败时，&lt;code&gt;distcc&lt;&#x2F;code&gt; 会将任务转交给工作站进行处理。我并没有统计具体的编译耗时，但个人体验是有断崖式提升的：在配置前，如果代码变化量大，编译代码时我的 XPS 机身会变得滚烫，同时风扇狂转。而配置好 &lt;code&gt;distcc&lt;&#x2F;code&gt; 后，即便在 ccache 缓存清空的情况下编译，自己的笔记本依旧凉爽安静。&lt;&#x2F;p&gt;
&lt;p&gt;最后附上我用 &lt;code&gt;distcc&lt;&#x2F;code&gt; 编译 wine 的 workflow，比较复杂，个别地方的写法也有待商榷。欢迎大家在评论区，或是向我发送邮件进行讨论。&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight bash %}
function cfgwine64()
{
cd ~&#x2F;src&#x2F;wine_n_extras&#x2F;wine64-build &amp;amp;&amp;amp; ..&#x2F;wine&#x2F;configure --enable-win64
}
function cfgwine32()
{
cd ~&#x2F;src&#x2F;wine_n_extras&#x2F;wine32-build &amp;amp;&amp;amp;&lt;br &#x2F;&gt;
PKG_CONFIG_PATH=&#x2F;usr&#x2F;lib&#x2F;pkgconfig &lt;br &#x2F;&gt;
..&#x2F;wine&#x2F;configure --with-wine64=..&#x2F;wine64-build
}
function mkwine()
{
export DISTCC_HOSTS=&quot;@beddp,lzo,cpp&quot;
export CC=&quot;ccache distcc gcc-8&quot;
export CFLAGS=&quot;-O0&quot;
export JOBS=16
export DISTCC_HOSTS=&quot;@beddp,lzo&quot;
cdwine
cfgwine64
make -j$JOBS
cfgwine32
make -j$JOBS
}
{% endhighlight %}&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>在 C++ 中使用 GLPK 求解线性规划</title>
        <published>2018-02-12T00:00:00+00:00</published>
        <updated>2018-02-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/cpp-glpk-linear-programming-kit/"/>
        <id>https://blog.zhenbo.pro/cpp-glpk-linear-programming-kit/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/cpp-glpk-linear-programming-kit/">&lt;p&gt;最近，参加了一个提交答案类的编程比赛，有一道题可用线性规划解决。搜索发现，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;glpk&quot;&gt;GLPK (GNU Linear Programming Kit)&lt;&#x2F;a&gt; 是一个免费的线性规划计算库，可以方便地被 C&#x2F;C++ 代码调用。现将基本使用方法整理如下：&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhun-bei-gong-zuo&quot;&gt;准备工作&lt;&#x2F;h2&gt;
&lt;h4 id=&quot;an-zhuang-glpk&quot;&gt;安装 GLPK&lt;&#x2F;h4&gt;
&lt;p&gt;多数的发行版都应该提供了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;glpk&quot;&gt;GLPK&lt;&#x2F;a&gt; 的包。在 Fedora 下，只需运行&lt;br &#x2F;&gt;
&lt;code&gt;sudo dnf install glpk glpk-devel&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bian-yi-glpk&quot;&gt;编译 GLPK&lt;&#x2F;h4&gt;
&lt;p&gt;Fedora 将 &lt;code&gt;glpk.h&lt;&#x2F;code&gt; 存在了 &lt;code&gt;&#x2F;usr&#x2F;include&#x2F;glpk.h&lt;&#x2F;code&gt;，因此，不需添加指令即可找到头文件。如果你是手动编译安装的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;glpk&quot;&gt;GLPK&lt;&#x2F;a&gt;，那可能需要使用 &lt;code&gt;-I&lt;&#x2F;code&gt; 指定头文件目录。链接的指令为 &lt;code&gt;-lglpk&lt;&#x2F;code&gt;。如果使用的是 C++ 的话，在调用头文件时，记得声明 &lt;code&gt;extern&lt;&#x2F;code&gt;。你可以试着使用 &lt;code&gt;g++ -lglpk test.cpp&lt;&#x2F;code&gt; 编译如下代码&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight C %}
#include &lt;iostream&gt;
extern &quot;C&quot;{
#include &quot;glpk.h&quot;
}
int main()
{
std::cout &amp;lt;&amp;lt; glp_version() &amp;lt;&amp;lt; std::endl;
return 0;
}
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;在我撰写本文时（2018年2月初），&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;glpk&quot;&gt;GLPK&lt;&#x2F;a&gt; 的版本应当为 4.61。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-zhan-yan-lian&quot;&gt;实战演练&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;glpk&quot;&gt;GLPK&lt;&#x2F;a&gt; 官方手册上的例子有点让人混淆。在这里，我将采用更精简的例子。&lt;br &#x2F;&gt;
Maximize
\[z=10x_1+6x_2\]
Subject to
\[x_1+x_2\leqslant200\]
\[x_1+2x_2\geqslant10\]
\[3x_1+x_2\leqslant275.5\]
where all variables are non-negative
\[x_1\geqslant 0, x_2\geqslant 0\]&lt;&#x2F;p&gt;
&lt;p&gt;对于三个约束条件，我们可以创建三个辅助变量(auxiliary variables)，将问题转化为如下形式：&lt;br &#x2F;&gt;
Maximize
\[z=10x_1+6x_2\]
Subject to
\[p=x_1+x_2\]
\[q=x_1+2x_2\]
\[r=3x_1+x_2\]
where all variables are non-negative
\[x_1\geqslant 0, x_2\geqslant 0\]
\[0\leqslant p\leqslant200,q\geqslant10,0\leqslant r\leqslant275.5\]&lt;&#x2F;p&gt;
&lt;p&gt;现在，可以将我们的问题输入程序了。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;glpk&quot;&gt;GLPK&lt;&#x2F;a&gt; 将各辅助变量看作行(row)，将原有的变量看作列(column)，用一个矩阵表示辅助变量和原有变量的关系。&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight c %}
#include &lt;cstdio&gt;
extern &quot;C&quot;{
#include &quot;glpk.h&quot;
}&lt;&#x2F;p&gt;
&lt;p&gt;int main() {
initialize:
glp_prob *lp;
lp = glp_create_prob();
glp_set_obj_dir(lp, GLP_MAX);
auxiliary_variables_rows:
glp_add_rows(lp, 3);
glp_set_row_name(lp, 1, &quot;p&quot;);
glp_set_row_bnds(lp, 1, GLP_DB, 0.0, 200.0);
glp_set_row_name(lp, 2, &quot;q&quot;);
glp_set_row_bnds(lp, 2, GLP_LO, 10.0, 0.0);
glp_set_row_name(lp, 3, &quot;r&quot;);
glp_set_row_bnds(lp, 3, GLP_DB, 0.0, 275.5);&lt;&#x2F;p&gt;
&lt;p&gt;variables_columns:
glp_add_cols(lp, 2);
glp_set_col_name(lp, 1, &quot;x1&quot;);
glp_set_col_bnds(lp, 1, GLP_LO, 0.0, 0.0);
glp_set_col_name(lp, 2, &quot;x2&quot;);
glp_set_col_bnds(lp, 2, GLP_LO, 0.0, 0.0);
to_maximize:
glp_set_obj_coef(lp, 1, 10.0);
glp_set_obj_coef(lp, 2, 6.0);&lt;&#x2F;p&gt;
&lt;p&gt;constrant_matrix:
int ia[7], ja[7];
double ar[7];
ia[1] = 1, ja[1] = 1, ar[1] = 1;
ia[2] = 1, ja[2] = 2, ar[2] = 1; &#x2F;&#x2F; p = x1 + x2
ia[3] = 2, ja[3] = 1, ar[3] = 1;
ia[4] = 2, ja[4] = 2, ar[4] = 2; &#x2F;&#x2F; q = x1 + 2x2
ia[5] = 3, ja[5] = 1, ar[5] = 3;
ia[6] = 3, ja[6] = 2, ar[6] = 1; &#x2F;&#x2F; r = 3x1 + x2
glp_load_matrix(lp, 6, ia, ja, ar);&lt;&#x2F;p&gt;
&lt;p&gt;calculate:
glp_simplex(lp, NULL);&lt;&#x2F;p&gt;
&lt;p&gt;output:
double z, x1, x2;
z = glp_get_obj_val(lp);
x1 = glp_get_col_prim(lp, 1);
x2 = glp_get_col_prim(lp, 2);
printf(&quot;z = %lf, x1 = %lf, x2 = %lf\n&quot;, z, x1, x2);&lt;&#x2F;p&gt;
&lt;p&gt;cleanup:
glp_delete_prob(lp);
return 0;
}
&#x2F;*
GLPK Simplex Optimizer, v4.61
3 rows, 2 columns, 6 non-zeros
0: obj =  -0.000000000e+00 inf =   1.000e+01 (1)
1: obj =   3.000000000e+01 inf =   0.000e+00 (0)&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;4: obj =   1.351000000e+03 inf =   0.000e+00 (0)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;OPTIMAL LP SOLUTION FOUND
z = 1351.000000, x1 = 37.750000, x2 = 162.250000
*&#x2F;
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;第一次看到示例代码，对于 &lt;code&gt;constrant_matrix&lt;&#x2F;code&gt; 部分可能会比较费解。在这里，&lt;code&gt;ia&lt;&#x2F;code&gt; 表示行号(第几个辅助变量)，&lt;code&gt;ja&lt;&#x2F;code&gt; 表示列号(第几个变量)，而 &lt;code&gt;ar&lt;&#x2F;code&gt; 的类型是 &lt;code&gt;double&lt;&#x2F;code&gt;，表示 constrant matrix 中的系数。将这些条件成功导入后，使用 &lt;code&gt;glp_simplex&lt;&#x2F;code&gt; 就可以求解了。&lt;&#x2F;p&gt;
&lt;p&gt;一个常见的需求是，需要求出对应的&lt;strong&gt;整数解&lt;&#x2F;strong&gt;。使用 glpk，这一问题也很好解决。&lt;br &#x2F;&gt;
{% highlight c %}
set_variables_to_integer:
glp_set_col_kind(lp, 1, GLP_IV);
glp_set_col_kind(lp, 2, GLP_IV);
calculate:
glp_simplex(lp, NULL);
glp_intopt(lp, NULL);
output:
double z, x1, x2;
z = glp_mip_obj_val(lp);
x1 = glp_mip_col_val(lp, 1);
x2 = glp_mip_col_val(lp, 2);
printf(&quot;z = %lf, x1 = %lf, x2 = %lf\n&quot;, z, x1, x2);
&#x2F;*
GLPK Simplex Optimizer, v4.61
3 rows, 2 columns, 6 non-zeros
0: obj =  -0.000000000e+00 inf =   1.000e+01 (1)
1: obj =   3.000000000e+01 inf =   0.000e+00 (0)&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;4: obj =   1.351000000e+03 inf =   0.000e+00 (0)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;OPTIMAL LP SOLUTION FOUND
GLPK Integer Optimizer, v4.61
3 rows, 2 columns, 6 non-zeros
2 integer variables, none of which are binary
Integer optimization begins...&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;4: mip =     not found yet &amp;lt;=              +inf        (1; 0)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Solution found by heuristic: 1346&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;6: &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;   1.348000000e+03 &amp;lt;=   1.348000000e+03   0.0% (1; 1)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;6: mip =   1.348000000e+03 &amp;lt;=     tree is empty   0.0% (0; 3)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;INTEGER OPTIMAL SOLUTION FOUND
z = 1348.000000, x1 = 37.000000, x2 = 163.000000
*&#x2F;
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;在运行过&lt;code&gt;glp_simplex&lt;&#x2F;code&gt;后，再运行&lt;code&gt;glp_intopt&lt;&#x2F;code&gt;即可得到整数解。但要注意的是，提取整数解的命令是 &lt;code&gt;glp_mip_obj_val&lt;&#x2F;code&gt; &#x2F;
&lt;code&gt;glp_mip_col_val&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;PS 写这篇 blog 时，我第一次使用了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.gastonsanchez.com&#x2F;visually-enforced&#x2F;opinion&#x2F;2014&#x2F;02&#x2F;16&#x2F;Mathjax-with-jekyll&#x2F;&quot;&gt;mathjax&lt;&#x2F;a&gt;。只能说，写作体验超乎想象地好。&lt;&#x2F;p&gt;
&lt;p style=&quot;color:grey&quot;&gt;讲一个逸闻吧。之前我曾把线性规划和高斯消元的时间复杂度弄混了，以至于我成功将大量NP问题转化为了线性规划问题后得到了“多项式”的解。&lt;&#x2F;p&gt;
&lt;p&gt;{% include mathjax.html %}&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>从 OI 到世界</title>
        <published>2015-06-07T00:00:00+00:00</published>
        <updated>2015-06-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/from-oi-to-world/"/>
        <id>https://blog.zhenbo.pro/from-oi-to-world/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/from-oi-to-world/">&lt;p&gt;又到了一年的高考季，而我，当年泡在机房里的高中生，已经毕业了两年。在参加 OI 比赛时，侧重点都在算法上，因而忽视了很多 C 语言的特性。在刚参与开源项目时，也多走了不少弯路。所以，我整理了一个小小的列表，希望这些问题能对你起到些许帮助。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yu-chu-li&quot;&gt;预处理&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;yu-chu-li-de-qian-zai-feng-xian&quot;&gt;预处理的潜在风险&lt;&#x2F;h3&gt;
&lt;p&gt;C 语言中，预处理与编译是独立的阶段。很多时候，这会引入额外的风险。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;举出至少两个可能带来风险的，在 OI 中较为常用的宏，说明其危险性&lt;&#x2F;li&gt;
&lt;li&gt;用 &lt;code&gt;inline&lt;&#x2F;code&gt;, &lt;code&gt;const&lt;&#x2F;code&gt; 等方式重写，在不增加额外代价的情况下解决该问题&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;chang-yong-de-hong&quot;&gt;常用的宏&lt;&#x2F;h3&gt;
&lt;p&gt;虽然宏很危险，但很多时候，宏有其不可替代的作用。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;举出至少一个例子，说明宏的必要性&lt;&#x2F;li&gt;
&lt;li&gt;解释 &lt;code&gt;__FILE__&lt;&#x2F;code&gt;, &lt;code&gt;__FUNCTION__&lt;&#x2F;code&gt;, &lt;code&gt;__LINE__&lt;&#x2F;code&gt; 等宏的作用&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;lian-jie-qi&quot;&gt;链接器&lt;&#x2F;h2&gt;
&lt;p&gt;与许多带有 &lt;code&gt;import&lt;&#x2F;code&gt; 的语言不同，C 语言中链接器与编译器是分开的。链接器的机制，并不是三言两语就可以说清的。所以，在这里，我只是提几个常见的基础问题。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;C 语言中，什么是函数的声明？什么是函数的定义？缺失了其中的一个会发生什么？&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;#include&lt;&#x2F;code&gt; 时发生了什么？为什么有时没有包含 &lt;code&gt;stdio.h&lt;&#x2F;code&gt; 也可以使用 &lt;code&gt;printf&lt;&#x2F;code&gt; 函数？&lt;&#x2F;li&gt;
&lt;li&gt;什么是动态链接？有什么好处？如何使用？&lt;&#x2F;li&gt;
&lt;li&gt;头文件中的 &lt;code&gt;extern &quot;C&quot;&lt;&#x2F;code&gt; 有什么用？如果没有，会发生什么？&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;shi-zhan-yan-lian&quot;&gt;实战演练&lt;&#x2F;h2&gt;
&lt;p&gt;此部分没有固定答案，希望大家的思路不要被拘束。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dai-mo-ban-de-qsort&quot;&gt;“带模版”的 qsort&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;stdlib.h&lt;&#x2F;code&gt; 中的 &lt;code&gt;qsort&lt;&#x2F;code&gt; 有四个参数。它们的意义都是什么？为什么要如此定义？&lt;br &#x2F;&gt;
尝试自己实现一个 &lt;code&gt;my_qsort&lt;&#x2F;code&gt; 函数，要求：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;具有普适性&lt;&#x2F;li&gt;
&lt;li&gt;在平均情况下为 &lt;code&gt;O(n lgn)&lt;&#x2F;code&gt; 的复杂度。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;better-string&quot;&gt;Better String&lt;&#x2F;h3&gt;
&lt;p&gt;C 语言中并没有内置的 &lt;code&gt;string&lt;&#x2F;code&gt;，而只有 &lt;code&gt;char *&lt;&#x2F;code&gt;。请举若干例子，说明这导致了哪些问题。&lt;br &#x2F;&gt;
尝试在不使用编译器扩展特性的情况下，实现一个字符串类型。要求如下：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;求字符串长度的时间复杂度为 &lt;code&gt;O(1)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;与 &lt;code&gt;string.h&lt;&#x2F;code&gt; 的 &lt;code&gt;strlen&lt;&#x2F;code&gt;, &lt;code&gt;strstr&lt;&#x2F;code&gt;, &lt;code&gt;strcat&lt;&#x2F;code&gt;, &lt;code&gt;strcpy&lt;&#x2F;code&gt; 兼容，或是重写对应的函数&lt;&#x2F;li&gt;
&lt;li&gt;额外的时间和空间代价必须是常数级的&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;br &#x2F;&gt;
&lt;br &#x2F;&gt;
&lt;br &#x2F;&gt;
&lt;h2 id=&quot;can-kao-xin-xi&quot;&gt;参考信息&lt;&#x2F;h2&gt;
&lt;p&gt;我并不会对这些问题给出标准答案。如果你感觉没什么头绪，可以参考如下信息&lt;&#x2F;p&gt;
&lt;h4 id=&quot;ju-chu-zhi-shao-yi-ge-li-zi-shuo-ming-hong-de-bi-yao-xing&quot;&gt;举出至少一个例子，说明宏的必要性&lt;&#x2F;h4&gt;
&lt;ol&gt;
&lt;li&gt;Windows API 中的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;msdn.microsoft.com&#x2F;en-us&#x2F;library&#x2F;windows&#x2F;desktop&#x2F;ms221627%28v=vs.85%29.aspx&quot;&gt;VARIANT&lt;&#x2F;a&gt; (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitcafe.com&#x2F;WineZH&#x2F;wine&#x2F;blob&#x2F;master&#x2F;include&#x2F;oleauto.h#L102&quot;&gt;Wine 中的实现&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;[用 C 语言实现链表][clink] 等数据结构&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;c-yu-yan-zhong-shen-me-shi-han-shu-de-sheng-ming-shen-me-shi-han-shu-de-ding-yi-que-shi-liao-qi-zhong-de-yi-ge-hui-fa-sheng-shen-me&quot;&gt;C 语言中，什么是函数的声明？什么是函数的定义？缺失了其中的一个会发生什么？&lt;&#x2F;h4&gt;
&lt;p&gt;参见 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B003BVBOOQ&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=536&amp;amp;creative=3132&amp;amp;creativeASIN=B003BVBOOQ&amp;amp;linkCode=as2&amp;amp;tag=blo-23&quot;&gt;C 语言程序设计 现代方法&lt;&#x2F;a&gt; 第九章 函数&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shen-me-shi-dong-tai-lian-jie-you-shen-me-hao-chu-ru-he-shi-yong&quot;&gt;什么是动态链接？有什么好处？如何使用？&lt;&#x2F;h4&gt;
&lt;p&gt;参见 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B0027VSA7U&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=536&amp;amp;creative=3132&amp;amp;creativeASIN=B0027VSA7U&amp;amp;linkCode=as2&amp;amp;tag=blo-23&quot;&gt;程序员的自我修养 链接、装载与库&lt;&#x2F;a&gt; 第7章(Linux) 第9章(Windows)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;tou-wen-jian-zhong-de-extern-c-you-shen-me-yong-ru-guo-mei-you-hui-fa-sheng-shen-me&quot;&gt;头文件中的 &lt;code&gt;extern &quot;C&quot;&lt;&#x2F;code&gt; 有什么用？如果没有，会发生什么？&lt;&#x2F;h4&gt;
&lt;p&gt;参见 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B0027VSA7U&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=536&amp;amp;creative=3132&amp;amp;creativeASIN=B0027VSA7U&amp;amp;linkCode=as2&amp;amp;tag=blo-23&quot;&gt;程序员的自我修养 链接、装载与库&lt;&#x2F;a&gt; 3.5.4&lt;br &#x2F;&gt;
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Name_mangling&quot;&gt;Wikipedia Name Mangling&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;dai-mo-ban-de-qsort-1&quot;&gt;“带模版”的 qsort&lt;&#x2F;h4&gt;
&lt;p&gt;参见 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B003BVBOOQ&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=536&amp;amp;creative=3132&amp;amp;creativeASIN=B003BVBOOQ&amp;amp;linkCode=as2&amp;amp;tag=blo-23&quot;&gt;C 语言程序设计 现代方法&lt;&#x2F;a&gt; 17.7.2 指针的高级应用&lt;&#x2F;p&gt;
&lt;h4 id=&quot;better-string-1&quot;&gt;Better String&lt;&#x2F;h4&gt;
&lt;p&gt;主要有两种思路：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;使用结构体封装，如 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;websnarf&#x2F;bstrlib&#x2F;blob&#x2F;master&#x2F;bstrlib.txt#L60&quot;&gt;bstrlib&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;在指针前附加信息，如 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;antirez&#x2F;sds&quot;&gt;SDS&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;msdn.microsoft.com&#x2F;en-us&#x2F;library&#x2F;ms221069.aspx&quot;&gt;BSTR&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;SDS 项目给出了&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;antirez&#x2F;sds&#x2F;blob&#x2F;master&#x2F;README.md#advantages-and-disadvantages-of-sds&quot;&gt;两种实现方式的对比&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;[clink]: {% post_url 2013-05-06-c_list %}&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shu-ji-tui-jian&quot;&gt;书籍推荐&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B00FF1Y53C&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;camp=536&amp;creative=3132&amp;creativeASIN=B00FF1Y53C&amp;linkCode=as2&amp;tag=blo-23&quot;&gt;一站式学习C编程&lt;&#x2F;a&gt;
&lt;a href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B0027VSA7U&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;camp=536&amp;creative=3132&amp;creativeASIN=B0027VSA7U&amp;linkCode=as2&amp;tag=blo-23&quot;&gt;程序员的自我修养:链接、装载与库&lt;&#x2F;a&gt;
&lt;a href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B003BVBOOQ&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;camp=536&amp;creative=3132&amp;creativeASIN=B003BVBOOQ&amp;linkCode=as2&amp;tag=blo-23&quot;&gt;C语言程序设计:现代方法(第2版)&lt;&#x2F;a&gt;
&lt;a href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B0012UMPBY&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;camp=536&amp;creative=3132&amp;creativeASIN=B0012UMPBY&amp;linkCode=as2&amp;tag=blo-23&quot;&gt;C陷阱与缺陷&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Qt5 中使用 log4qt 输出日志</title>
        <published>2015-02-20T00:00:00+00:00</published>
        <updated>2015-02-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/qt5-with-log4qt/"/>
        <id>https://blog.zhenbo.pro/qt5-with-log4qt/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/qt5-with-log4qt/">&lt;p&gt;最近在写一个 QT 的程序，需要打印日志。搜索了一下，选定了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;log4qt.sourceforge.net&#x2F;&quot;&gt;Log4Qt&lt;&#x2F;a&gt;。不过，原项目已经长期不更新了，而 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.devbean.net&#x2F;&quot;&gt;DevBean&lt;&#x2F;a&gt; 维护了一个 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;devbean&#x2F;log4qt&quot;&gt;Qt5 的版本&lt;&#x2F;a&gt;。所以，我执行了 &lt;code&gt;git submodule add https:&#x2F;&#x2F;github.com&#x2F;devbean&#x2F;log4qt.git&lt;&#x2F;code&gt; 。在我撰写本文时(2015年2月)，另一个团队也在孜孜不倦地 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20140808035437&#x2F;https:&#x2F;&#x2F;gitorious.org&#x2F;log4qt&quot;&gt;维护着 Log4qt&lt;&#x2F;a&gt;。可惜，在2023年，他们的网站已经无法访问了。&lt;&#x2F;p&gt;
&lt;p&gt;官方提供了一个比较详细的&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;log4qt.sourceforge.net&#x2F;&quot;&gt;使用介绍&lt;&#x2F;a&gt;，但也隐藏了几个小坑，整理如下：&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-1&quot;&gt;Step 1&lt;&#x2F;h4&gt;
&lt;p&gt;将这句话加入 .pro 文件即可
&lt;code&gt;include(&amp;lt;unpackdir&amp;gt;&#x2F;src&#x2F;log4qt&#x2F;log4qt.pri)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-2&quot;&gt;Step 2&lt;&#x2F;h4&gt;
&lt;p&gt;在预编译头文件里加入
{% highlight c %}
#include &quot;log4qt&#x2F;consoleappender.h&quot;
#include &quot;log4qt&#x2F;logger.h&quot;
&#x2F;&#x2F;我添加的内容
#include &quot;log4qt&#x2F;logmanager.h&quot;
#include &quot;log4qt&#x2F;patternlayout.h&quot;
&#x2F;&#x2F;被我移除的头文件
&#x2F;&#x2F;#include &quot;log4qt&#x2F;ttcclayout.h&quot;
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-3&quot;&gt;Step 3&lt;&#x2F;h4&gt;
&lt;p&gt;初始化 Log4Qt，我是在 &lt;code&gt;QApplication::exec()&lt;&#x2F;code&gt; 之前执行的这段代码
为了能灵活地处理 Layout，我小幅修改了原有的代码。&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight c %}&lt;&#x2F;p&gt;
&lt;p&gt;&#x2F;&#x2F;Configure a logger to generate output. The example uses the root logger
&#x2F;&#x2F;Create a layout
Log4Qt::LogManager::rootLogger();&lt;&#x2F;p&gt;
&lt;p&gt;&#x2F;&#x2F;TTCCLayout *p_layout = new TTCCLayout();
&#x2F;&#x2F;使用 PatternLayout，自定义输出格式
p_layout = new Log4Qt::PatternLayout(&quot;%-5p [%c] %m%n&quot;);
p_layout-&amp;gt;setName(QLatin1String(&quot;My Layout&quot;));
p_layout-&amp;gt;activateOptions();&lt;&#x2F;p&gt;
&lt;p&gt;&#x2F;&#x2F; Create an appender
ConsoleAppender *p_appender = new ConsoleAppender(p_layout, ConsoleAppender::STDOUT_TARGET);
p_appender-&amp;gt;setName(QLatin1String(&quot;My Appender&quot;));
p_appender-&amp;gt;activateOptions();
&#x2F;&#x2F; Set appender on root logger
Log4Qt::Logger::rootLogger()-&amp;gt;setAppender(p_appender);&lt;&#x2F;p&gt;
&lt;p&gt;{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;关于 Layout 的详细内容，可以参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;logging.apache.org&#x2F;log4j&#x2F;1.2&#x2F;apidocs&#x2F;org&#x2F;apache&#x2F;log4j&#x2F;PatternLayout.html&quot;&gt;Log4j 的一篇文档&lt;&#x2F;a&gt;，使用方法与 C 的格式化字符串是一样的。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-4&quot;&gt;Step 4&lt;&#x2F;h4&gt;
&lt;p&gt;然后，就可以直接输出 log 了，例如官方示例&lt;br &#x2F;&gt;
&lt;code&gt;Log4Qt::Logger::logger(QLatin1String(&quot;My Logger&quot;))-&amp;gt;info(&quot;Hello World!&quot;);&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;为了简化代码，我在预编译头文件里又加入了这段内容
{% highlight c++ %}
#define INITIATE_LOG4QT(ClassName) LOG4QT_DECLARE_STATIC_LOGGER(LOG4QT_LOGGER, ClassName)&lt;&#x2F;p&gt;
&lt;p&gt;#define ERROR(s) LOG4QT_LOGGER()-&amp;gt;error(QString(s))
#define WARN(s) LOG4QT_LOGGER()-&amp;gt;warn(QString(s))
#define INFO(s) LOG4QT_LOGGER()-&amp;gt;info(QString(s))
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;而在定义类的头文件里，只要初始化一下
{% highlight c++ %}
class MyClass
{
private:
INITIATE_LOG4QT(MyClass);&lt;&#x2F;p&gt;
&lt;p&gt;public:
MyClass();
~MyClass();
};
{% endhighlight %}
就可以直接在类的成员函数里用 &lt;code&gt;ERROR()&lt;&#x2F;code&gt; 这样的宏了。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;step-5&quot;&gt;Step 5&lt;&#x2F;h4&gt;
&lt;p&gt;Log4Qt 的功能非常强大。例如，四个 Level 用于输出不同的信息，可以在启动时动态确定输出哪些信息。同样，也可以使用条件编译，在 Release 里将 &lt;code&gt;INFO&lt;&#x2F;code&gt; 编译成 &lt;code&gt;(void)0&lt;&#x2F;code&gt; 来减少性能损失。不过，Log4Qt 的性能没有测试，希望了解的朋友贴出自己的经验 :-)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;last&quot;&gt;Last&lt;&#x2F;h4&gt;
&lt;p&gt;从我学 Qt 的第一天起就没少看 DevBeans 的博客，也在这里推荐一下他的书籍
&lt;a href=&quot;http:&#x2F;&#x2F;www.amazon.cn&#x2F;gp&#x2F;product&#x2F;B00SALSVVG&#x2F;ref=as_li_ss_tl?ie=UTF8&amp;camp=536&amp;creative=3132&amp;creativeASIN=B00SALSVVG&amp;linkCode=as2&amp;tag=blo-23&quot;&gt;《Qt 5编程入门》&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>如何教人编程</title>
        <published>2014-02-07T00:00:00+00:00</published>
        <updated>2014-02-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/how-to-teach-programming/"/>
        <id>https://blog.zhenbo.pro/how-to-teach-programming/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/how-to-teach-programming/">&lt;p&gt;这学期头脑发热，辅导了两个人编程（第一个 Python，第二个 C++），真的是很难忘的经历。&lt;br &#x2F;&gt;
学习编程是一件有挑战的事情。相比之下，教一个人编程更加困难。第一个障碍，就是思维方式的改变（针对大学生而言）。&lt;&#x2F;p&gt;
&lt;p&gt;有了高中的经历，学习者很希望能得到一个 “知识体系”，一个树状的知识结构图，就像这样
&lt;img src=&quot;&#x2F;images&#x2F;thinking&#x2F;biology_sample.gif&quot; alt=&quot;biology&quot; &#x2F;&gt;&lt;br &#x2F;&gt;
(原链接已失效)&lt;&#x2F;p&gt;
&lt;p&gt;问题是，编程并不是要回答原有的问题。我喜欢将编程比作乐高积木：给定若干规则，你要按照你的想法想起组合在一起。来 &lt;em&gt;搭建&lt;&#x2F;em&gt; 你所需要的东西。如果不能让他们掌握新的思维方式，那你就可以打 GG 然后 Alt + Q 了。&lt;&#x2F;p&gt;
&lt;p&gt;回头想想，有几点建议，还是留给自己吧：&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zhi-chu-cuo-wu-yao-zhi-jie&quot;&gt;指出错误要直接&lt;&#x2F;h4&gt;
&lt;p&gt;我并不时说我们要像 Linus Torvalds 那样 &lt;em&gt;“直白”&lt;&#x2F;em&gt;，但对于学习者的错误，要果断的指出。如果发现对概念有错误的理解，一定要第一时间指出。如果因为他们看上去很努力，就给了不客观的鼓励，那会是非常不负责任的行为。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bi-mian-shi-yong-zi-ran-yu-yan&quot;&gt;避免使用自然语言&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;thinking&#x2F;project_need.png&quot; alt=&quot;need description&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;大家应该很熟悉这幅漫画吧。一个团队内的成员，尚且会相互误解，更何况在一个半斤八两的教授者，和一个一片空白的学习者间呢？&lt;br &#x2F;&gt;
相比于含糊的自然语言，流程图是一种更可靠的交流方式。而且，还能帮助他们形成正确的思路。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bu-yao-xiang-xin-wo-ting-dong-liao&quot;&gt;不要相信“我听懂了”&lt;&#x2F;h4&gt;
&lt;p&gt;很多时候，“懂了”的意思，是“我知道了”（参见图一）。学习者经验少，不自觉地自欺欺人，如果你也被忽悠了，那结果将会是悲惨的。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bu-yao-zai-lian-xi-shang-lin-se-shi-jian&quot;&gt;不要在练习上吝啬时间&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;book.douban.com&#x2F;subject&#x2F;4923179&#x2F;&quot;&gt;《我编程，我快乐》&lt;&#x2F;a&gt; 一书中，作者将编程与音乐做了类比。虽然两者间的区别很大（比方说没有维也纳新年编程会），但个人愚见，有一点是共同的：需要大量的练习。贸然跳过一些自认为“简单”的练习，很可能埋下某些“定时炸弹”。今天坑挖的越深，明天掉进去的时候摔的就会越痛。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bu-yao-qing-yi-wa-keng&quot;&gt;不要轻易挖坑&lt;&#x2F;h3&gt;
&lt;P STYLE=&quot;margin-bottom: 0cm&quot;&gt;&lt;FONT COLOR=&quot;#c0c0c0&quot;&gt;但趁着年轻，多做一些有挑战性的事情吧&lt;&#x2F;FONT&gt;&lt;&#x2F;P&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>CentOS 使用 ELRepo 安装最新的 Linux Kernel</title>
        <published>2014-01-30T00:00:00+00:00</published>
        <updated>2014-01-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/centos-new-linux-kernel/"/>
        <id>https://blog.zhenbo.pro/centos-new-linux-kernel/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/centos-new-linux-kernel/">&lt;p&gt;回家给台式机装上了 CentOS 6.5，发现很多软件包都太旧，尤其是 kernel，还停留在 2.6 的时代。找了一些介绍，多数说要自己编译 kernel，实在太麻烦。不过，有一篇文章介绍了用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;elrepo.org&#x2F;tiki&#x2F;tiki-index.php&quot;&gt;ELRepo&lt;&#x2F;a&gt; 安装新内核的方法，我只想说：&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;太伟大了！&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;具体的步骤很简单：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;添加源&lt;br &#x2F;&gt;
&lt;code&gt;rpm -Uvh http:&#x2F;&#x2F;www.elrepo.org&#x2F;elrepo-release-6-5.el6.elrepo.noarch.rpm&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;安装kernel&lt;br &#x2F;&gt;
&lt;code&gt;yum --enablerepo=elrepo-kernel install kernel-ml&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
(解释一下，ml 是指 MainLine, kernel-lt 就是 Long-Term)&lt;&#x2F;li&gt;
&lt;li&gt;编辑 grub 的默认启动项&lt;br &#x2F;&gt;
这里我就懒了，直接 编辑的 &lt;code&gt;&#x2F;boot&#x2F;grub&#x2F;menu.lst&lt;&#x2F;code&gt;，好孩子们不要学我啊&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>DeleteFileA 的 testcase 的代码阅读笔记</title>
        <published>2013-12-18T00:00:00+00:00</published>
        <updated>2013-12-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/deletefilea-testcase-code/"/>
        <id>https://blog.zhenbo.pro/deletefilea-testcase-code/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/deletefilea-testcase-code/">&lt;p&gt;为了处理 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=34324&quot;&gt;Wine bug 34324&lt;&#x2F;a&gt;，我看了 &lt;code&gt;kernel32.dll&lt;&#x2F;code&gt; 里 &lt;code&gt;DeleteFileA()&lt;&#x2F;code&gt; 和其 testcase 的代码，发现埋藏了不少的坑。考虑到最近我会比较忙，就先做一些简单的记录，回头再进一步的处理。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;keng-you-na-xie&quot;&gt;坑有哪些&lt;&#x2F;h2&gt;
&lt;p&gt;个人以为，这段代码历史 &lt;strong&gt;悠久&lt;&#x2F;strong&gt; 是问题的关键。&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Windows API 的变化&lt;br &#x2F;&gt;
很多组 testcase 是 2002 年左右加上去的。当时 XP 刚刚面世。这么多个版本过来，很多 Win API 的行为都变了，尤其是非法情况下的返回值。&lt;&#x2F;li&gt;
&lt;li&gt;Wine 的代码质量&lt;br &#x2F;&gt;
翻了 git log，发现 Wine 的一些旧代码的质量真不敢恭维。欠缺了很多的注释，git commit 时写的 comment 也不准确，这给后来的工作带来了一些障碍。&lt;&#x2F;li&gt;
&lt;li&gt;Windows API 的诡异行为&lt;br &#x2F;&gt;
可能是我对 Windows 的了解太少，我做测试的时候，发现 &lt;code&gt;DeleteFile(&quot;nul&quot;)&lt;&#x2F;code&gt; 和 &lt;code&gt;DeleteFile(&quot;a.exe&quot;)&lt;&#x2F;code&gt; 遇到文件不存在的问题后，&lt;code&gt;GetLastError()&lt;&#x2F;code&gt; 的返回值是不一样的。看来，我最初的推测并不准确。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;ru-he-qu-zuo&quot;&gt;如何去做&lt;&#x2F;h2&gt;
&lt;p&gt;我的设想是这样的：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;DeleteFileA 的 testcase&lt;&#x2F;li&gt;
&lt;li&gt;DeleteFileA (要把很多组 todo_wine 解决掉)&lt;&#x2F;li&gt;
&lt;li&gt;hack SHFileOperation (in shell32.dll)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;yue-du-bi-ji&quot;&gt;阅读笔记&lt;&#x2F;h2&gt;
&lt;p&gt;好了，现在到正题了。我看到的是这段代码
{% highlight c %}
ret = DeleteFileA(NULL);
ok(!ret &amp;amp;&amp;amp; (GetLastError() == ERROR_INVALID_PARAMETER ||
GetLastError() == ERROR_PATH_NOT_FOUND),
&quot;DeleteFileA(NULL) returned ret=%d error=%d\n&quot;,ret,GetLastError());&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;ret = DeleteFileA(&amp;quot;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;ok(!ret &amp;amp;&amp;amp; (GetLastError() == ERROR_PATH_NOT_FOUND ||
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;            GetLastError() == ERROR_BAD_PATHNAME),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;   &amp;quot;DeleteFileA(\&amp;quot;\&amp;quot;) returned ret=%d error=%d\n&amp;quot;,ret,GetLastError());
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;{% endhighlight %}
这来自于  e948ad1fc7e18a2，由 Francois Gouget 在 2002 年 12 月提交。&lt;&#x2F;p&gt;
&lt;p&gt;还有这段&lt;br &#x2F;&gt;
{% highlight c %}
ret = DeleteFileA(&quot;nul&quot;);
ok(!ret &amp;amp;&amp;amp; (GetLastError() == ERROR_FILE_NOT_FOUND ||
GetLastError() == ERROR_INVALID_PARAMETER ||
GetLastError() == ERROR_ACCESS_DENIED ||
GetLastError() == ERROR_INVALID_FUNCTION),
&quot;DeleteFileA(&quot;nul&quot;) returned ret=%d error=%d\n&quot;,ret,GetLastError());
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;ERROR_INVALID_PARAMETER&lt;&#x2F;code&gt; 来自 c49b9485，由 Jakob Eriksson 在 2004 年 4 月提交。 至于 &lt;code&gt;ERROR_INVALID_FUNCTION&lt;&#x2F;code&gt; 来自 6cb97534 Jacek Caban， 在 2005 年 6 月提交。&lt;&#x2F;p&gt;
&lt;p&gt;PS &lt;code&gt;git blame file.c &amp;gt; &#x2F;dev&#x2F;shm&#x2F;log&lt;&#x2F;code&gt; 真神器。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>SHFileOperation 删除文件的小测试</title>
        <published>2013-12-14T00:00:00+00:00</published>
        <updated>2013-12-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/shfileoperation-delete-file/"/>
        <id>https://blog.zhenbo.pro/shfileoperation-delete-file/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/shfileoperation-delete-file/">&lt;p&gt;这段时间处理 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=34324&quot;&gt;Wine bug 34324: QQ2013 SP1 can&#x27;t install&lt;&#x2F;a&gt;， 发现问题没有想象的简单。把之前取得的成果先记录下来，希望能给同样遇到这个问题的人一点提示，也算给自己留下一点笔记。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;注意： 以下的所有结果是通过包括 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wiki.winehq.org&#x2F;WineTestBot&quot;&gt;Wine Testbot&lt;&#x2F;a&gt; 等黑箱测试得到的，没有任何反汇编的内容。如果你对 Wine 开发感兴趣，也请不要参考类似的资料&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bug-jian-jie&quot;&gt;bug 简介&lt;&#x2F;h2&gt;
&lt;p&gt;QQ2013 SP1 安装时，会调用 &lt;code&gt;SHFileOperation&lt;&#x2F;code&gt; 删除 &lt;code&gt;C:\\Program Files\\qqtest&lt;&#x2F;code&gt;。但如果该目录不存在，这次函数调用就会失败。&lt;br &#x2F;&gt;
在 WinXP 下，返回值时 &lt;code&gt;0x402&lt;&#x2F;code&gt;。而在 Vista 及以后版本中，返回值就是 &lt;code&gt;ERROR_FILE_NOT_FOUND&lt;&#x2F;code&gt; (&lt;code&gt;0x2&lt;&#x2F;code&gt;)。&lt;br &#x2F;&gt;
但是，在当前的版本(wine-1.7.8)中，返回值为 &lt;code&gt;ERROR_PATH_NOT_FOUND&lt;&#x2F;code&gt;(&lt;code&gt;0X3&lt;&#x2F;code&gt;)。所以，在 QQ2013 Installer 看来，这不符合预期，就拒绝继续安装。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ce-shi-jin-cheng&quot;&gt;测试进程&lt;&#x2F;h2&gt;
&lt;p&gt;为了完成这个 hack，我研究了一下 Wine 原有的 testcase (&lt;code&gt;dlls&#x2F;shell32&#x2F;tests&#x2F;shlfileop.c&lt;&#x2F;code&gt;, &lt;code&gt;commit a1762ba8a46eca5c7ef1e&lt;&#x2F;code&gt;)&lt;br &#x2F;&gt;
在 &lt;code&gt;test_delete()&lt;&#x2F;code&gt; 函数里，主要的情况是：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;delete a nonexistent file&lt;br &#x2F;&gt;
事实上，如果找不到文件，系统是无法区分 file 和 dictionary 的。在 XP 下，返回值是 &lt;code&gt;0x402&lt;&#x2F;code&gt;，而 Vista 及以后，返回值都是 &lt;code&gt;ERROR_FILE_NOT_FOUND&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;delete a nonexistent file in an existent dir or a nonexistent dir&lt;br &#x2F;&gt;
对于前者(existend dir)，结果与 delete nonexistent file 是一样的。&lt;br &#x2F;&gt;
对于后者，XP 的返回值依旧是&lt;code&gt;0x402&lt;&#x2F;code&gt;，而 Vista 及以后的版本中，返回值是 &lt;code&gt;DE_INVALIDFILES&lt;&#x2F;code&gt;(&lt;code&gt;0x7c&lt;&#x2F;code&gt;)。&lt;&#x2F;li&gt;
&lt;li&gt;delete a dir, and then a file inside the dir&lt;br &#x2F;&gt;
这是一个比较旧的 tetscase，当时 Vista 是最新的 Windows 版本。有人留下了说明， &lt;em&gt;Vista would throw up a dialog box that we can&#x27;t suppress&lt;&#x2F;em&gt;。但他的 hack 方式 &lt;code&gt;ret != ERROR_FILE_NOT_FOUND&lt;&#x2F;code&gt; 会忽略掉 Win 7&#x2F;8。我提交了一个 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;source.winehq.org&#x2F;patches&#x2F;data&#x2F;100895&quot;&gt;patch&lt;&#x2F;a&gt;，在经过 Wine-devel 中的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.winehq.org&#x2F;pipermail&#x2F;wine-devel&#x2F;2013-December&#x2F;102211.html&quot;&gt;讨论&lt;&#x2F;a&gt; 后，这个 patch 还是被拒绝了。Qian Hong 的解释是：&lt;br &#x2F;&gt;
&lt;em&gt;根据Windows版本来区分api的特性是不靠谱的，因为Microsoft会不断地发布service pack，每次发布都会修复一些bug，表面上看，某个特性可能在Windows XP和Windows 7上是不同的，可是经过更新打上某个service pack之后，Windows XP的特性和Windows 7可能就相同了。因此，要根据feature来决定要不要skip某个test。&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;附上我的补丁核心部分：&lt;br &#x2F;&gt;
{% highlight c %}&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;   ok(ret == ERROR_PATH_NOT_FOUND  ||  &#x2F;* XP *&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      broken(ret == ERROR_SUCCESS) ||  &#x2F;* NT4 *&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      ret == DE_INVALIDFILES,          &#x2F;* Win 7 or 8 *&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;      &amp;quot;Expected ERROR_PATH_NOT_FOUND or DE_INVALIDFILES, got 0x%x\n&amp;quot;, ret);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;我还做了一个测试（补丁找不到了）：删除同一个文件两次，运行结果和情况 3 是一样的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-xia-lai-de-ren-wu&quot;&gt;接下来的任务&lt;&#x2F;h2&gt;
&lt;p&gt;在 Wine 的实现里，&lt;code&gt;SHFileOperation&lt;&#x2F;code&gt; 会调用 &lt;code&gt;BOOL DeleteFileW&lt;&#x2F;code&gt;。而 &lt;code&gt;DeleteFileW&lt;&#x2F;code&gt; 是靠 &lt;code&gt;SetLastError()&lt;&#x2F;code&gt; 设置错误代码的。(&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;msdn.microsoft.com&#x2F;en-us&#x2F;library&#x2F;windows&#x2F;desktop&#x2F;aa363915%28v=vs.85%29.aspx&quot;&gt;MSDN: DeleteFile function&lt;&#x2F;a&gt;) 我看了一下 kernel32 的相关测试，覆盖的并不是很好。我的 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;source.winehq.org&#x2F;patches&#x2F;data&#x2F;101039&quot;&gt;patch 101039&lt;&#x2F;a&gt; 被 reject 掉了，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.winehq.org&#x2F;pipermail&#x2F;wine-devel&#x2F;2013-December&#x2F;102265.html&quot;&gt;相关讨论&lt;&#x2F;a&gt; 里提到，kernel32 的很多 testcase 比较旧。接下来，我打算花时间完善一下这些 testcase。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>为 wine 添加 testcase</title>
        <published>2013-11-16T00:00:00+00:00</published>
        <updated>2013-11-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/wine-add-testcase/"/>
        <id>https://blog.zhenbo.pro/wine-add-testcase/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/wine-add-testcase/">&lt;p&gt;之前处理一个 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=34324&quot;&gt;QQ2013安装程序无法完成的 bug&lt;&#x2F;a&gt;，经过分析，问题锁定在了 &lt;code&gt;SHFileOperation&lt;&#x2F;code&gt; 函数上。&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;msdn.microsoft.com&#x2F;en-us&#x2F;library&#x2F;windows&#x2F;desktop&#x2F;bb762164%28v=vs.85%29.aspx&quot;&gt;MSDN 的函数介绍&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;QQ 的安装程序会尝试删去 &lt;code&gt;C:\Program Files\qqtest&lt;&#x2F;code&gt;。文件不存在时，&lt;code&gt;SHFileOperation&lt;&#x2F;code&gt; 函数会有一个返回值（XP 和 Vista 及以后不同）。但是 wine 并没有正确的提供改返回值，所以 QQ 的安装程序就认为发生了错误，而停止了安装。&lt;&#x2F;p&gt;
&lt;p&gt;###Wine Test
要保证 Wine 的函数实现和 windows 下的表现一样，就需要一些测试。进入 wine 的源代码目录下的 dlls 文件夹，然后选定一个 dll（本例是 shell32），然后里面会有一个 tests 文件夹。里面，就有了一些 test case。&lt;&#x2F;p&gt;
&lt;p&gt;例如 &lt;code&gt;shlfileop.c&lt;&#x2F;code&gt;，里面有许多 static 函数。在函数内，大量的调用了 &lt;code&gt;ok&lt;&#x2F;code&gt; 宏。它了用法很简单，就像 &lt;code&gt;assert&lt;&#x2F;code&gt; 宏一样。如果某句假定不成立，那就可以在前面一行加上 &lt;code&gt;todo_wine&lt;&#x2F;code&gt;。这样的话，该 ok 宏会在 windows 下测试，而不会在 wine 下测试。&lt;&#x2F;p&gt;
&lt;p&gt;编辑好后，在 tests 目录下运行 &lt;code&gt;make test&lt;&#x2F;code&gt;。然后，就能看到对 wine 的运行情况的反馈。&lt;&#x2F;p&gt;
&lt;p&gt;####Test bot
一个人要测试 windows API 在不同版本下的表现是很繁琐的，所以，有了 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wiki.winehq.org&#x2F;WineTestBot&quot;&gt;WineTestBot&lt;&#x2F;a&gt;。把自己的补丁提交到 &amp;lt;newtestbot.winehq.org&amp;gt; 上，就会自动进行测试，并在结束后把结果发送到你的邮箱里（为什么想到了OJ？）&lt;&#x2F;p&gt;
&lt;p&gt;附：&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.freelists.org&#x2F;post&#x2F;wine-zh&#x2F;SHFileOperationW-about-Bug-34324&quot;&gt;我们在邮件列表里进行的讨论&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>BOINC 的源代码阅读笔记 2</title>
        <published>2013-10-26T00:00:00+00:00</published>
        <updated>2013-10-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/boinc-2/"/>
        <id>https://blog.zhenbo.pro/boinc-2/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/boinc-2/">&lt;p&gt;想研究一下 BOINC 客户端获取任务的方式，望文生义，找到了 &lt;code&gt;client&#x2F;cs_scheduler.cpp&lt;&#x2F;code&gt; 里的 &lt;code&gt;request_work_fetch(const char *)&lt;&#x2F;code&gt;。这个函数做的很少，只是修改了 &lt;code&gt;CLIENT_STATE&lt;&#x2F;code&gt; 的一个 &lt;code&gt;private&lt;&#x2F;code&gt; 变量： &lt;code&gt;must_check_work_fetch&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;接下来要看的是一个关键函数：&lt;code&gt;client&#x2F;cs_scheduler.cpp&lt;&#x2F;code&gt; 里的 &lt;code&gt;scheduler_rpc_poll()&lt;&#x2F;code&gt; 。它首先会尝试上报任务，然后再申请任务。如果两次申请的时间差别不大，那它是不会申请任务的。但如果 &lt;code&gt;must_check_work_fetch&lt;&#x2F;code&gt; 为真，那就不用担心时间间隔的问题了。&lt;&#x2F;p&gt;
&lt;p&gt;但 &lt;code&gt;scheduler_rpc_poll()&lt;&#x2F;code&gt; 的目的是从全局考虑，保证电脑“有饭吃”。因此，它每次只会通过 &lt;code&gt;work_fetch.choose_project()&lt;&#x2F;code&gt; 找一个项目来申请。&lt;&#x2F;p&gt;
&lt;p&gt;这里的 &lt;code&gt;work_fetch&lt;&#x2F;code&gt; 在 &lt;code&gt;client&#x2F;work_fetch.cpp&lt;&#x2F;code&gt; 文件中，是定义在全局的 &lt;code&gt;WORK_FETCH&lt;&#x2F;code&gt; 变量。他会对显卡和处理器分别讨论，调用 &lt;code&gt;cpu_work_fetch&lt;&#x2F;code&gt; 和 &lt;code&gt;cuda_work_fetch&lt;&#x2F;code&gt;。我们以 cpu 为例。&lt;&#x2F;p&gt;
&lt;p&gt;首先，是遍历一遍所有的项目，找到最合适的，存储到 &lt;code&gt;PROJECT* pbst&lt;&#x2F;code&gt; 里。然后，根据不同的需求，调用 &lt;code&gt;RSC_WORK_FETCH::set_request(PROJECT*, double)&lt;&#x2F;code&gt; 来执行这项工作（说实话，这里的机制我理解的还不好）。&lt;&#x2F;p&gt;
&lt;p&gt;如果想要强行获取任务的话，不停把 &lt;code&gt;must_check_work_fetch&lt;&#x2F;code&gt; 设置成真应该是一个办法。但这种对于全局的操作，可能不适合对某个项目进行屯包。所以，接下来，我应该对 &lt;code&gt;PROJECT&lt;&#x2F;code&gt; 类研究一下。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>BOINC 源代码的阅读笔记</title>
        <published>2013-09-25T00:00:00+00:00</published>
        <updated>2013-09-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/boinc-code/"/>
        <id>https://blog.zhenbo.pro/boinc-code/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/boinc-code/">&lt;p&gt;在论坛上跟人聊天，提到了修改 BOINC 客户端，以便于屯包的设想。在开学前没什么事，就挖下了这个坑。我修改后的版本，可以在 [这里][gcrepo] 看到。当然，现在还没有什么可用性。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;huo-qu-dai-ma&quot;&gt;获取代码&lt;&#x2F;h2&gt;
&lt;p&gt;BOINC 的代码可以从官方网站 [下载][offi]，也可以从我在 GitCafe 上留的镜像 [下载][master]（注意是 master 分支）。可以参照 [官方手册][man] 编译安装。
[gcrepo]: https:&#x2F;&#x2F;gitcafe.com&#x2F;endle&#x2F;BOINCc&#x2F;tree&#x2F;dev
[offi]: http:&#x2F;&#x2F;boinc.berkeley.edu&#x2F;trac&#x2F;wiki&#x2F;SourceCodeGit
[master]: https:&#x2F;&#x2F;gitcafe.com&#x2F;endle&#x2F;BOINCc&#x2F;tree&#x2F;master
[man]: http:&#x2F;&#x2F;boinc.berkeley.edu&#x2F;wiki&#x2F;Compiling_the_core_client&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xi-tong-ji-zhi&quot;&gt;系统机制&lt;&#x2F;h2&gt;
&lt;p&gt;软件最基本的是两部分：&lt;code&gt;boinc&lt;&#x2F;code&gt; 和 &lt;code&gt;boinccmd&lt;&#x2F;code&gt;。&lt;code&gt;boinc&lt;&#x2F;code&gt; 使用 &lt;code&gt;recv()&lt;&#x2F;code&gt; 函数接收来自 &lt;code&gt;boinccmd&lt;&#x2F;code&gt; 的消息，并维护一个消息队列。而 &lt;code&gt;boinccmd&lt;&#x2F;code&gt; 负责跟用户沟通，将用户输入的指令转化成 xml 格式，然后用 &lt;code&gt;send()&lt;&#x2F;code&gt; 发送给 &lt;code&gt;boinc&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ru-shou-dian&quot;&gt;入手点&lt;&#x2F;h2&gt;
&lt;p&gt;第一个思路，就是找 &lt;code&gt;main&lt;&#x2F;code&gt; 函数。在 &lt;code&gt;client&#x2F;boinc_cmd.cpp&lt;&#x2F;code&gt; 里的 main 函数，会对用户输入的命令进行初步的检查。接着，&lt;code&gt;boinccmd&lt;&#x2F;code&gt; 会通过&lt;code&gt;retval = rpc.init(hostname, port)&lt;&#x2F;code&gt;尝试与 HOST 建立连接，并调用 &lt;code&gt;RPC_CLIENT&lt;&#x2F;code&gt; 类的方法。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rpc-client&quot;&gt;RPC_CLIENT&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;boinc.berkeley.edu&#x2F;trac&#x2F;wiki&#x2F;GuiRpc&quot;&gt;官方wiki&lt;&#x2F;a&gt; 上给的解释是：The BOINC client provides a set of RPCs (&lt;strong&gt;remote procedure calls&lt;&#x2F;strong&gt;) for control and state interrogation. This enables the development of GUI (graphical user interface) programs. These RPCs &lt;em&gt;send XML request&lt;&#x2F;em&gt; and reply messages over a &lt;strong&gt;TCP&lt;&#x2F;strong&gt; connection. The XML formats are not documented, but can be inferred from the source code.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;RPC_CLIENT&lt;&#x2F;code&gt; 的定义在 &lt;code&gt;lib&#x2F;gui_rpc_client.h&lt;&#x2F;code&gt; 里。可以看出，这是一个负责与 HOST 进行沟通的模块。以 &lt;code&gt;lib&#x2F;gui_rpc_client_ops.cpp&lt;&#x2F;code&gt; 中的 &lt;code&gt;project_op&lt;&#x2F;code&gt; 函数为例（部分精简）：
{% highlight c %}
int RPC_CLIENT::project_op(PROJECT&amp;amp; project, const char* op) {
int retval;
SET_LOCALE sl;
char buf[512];
const char *tag;
RPC rpc(this);&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (!strcmp(op, &amp;quot;reset&amp;quot;)) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    tag = &amp;quot;project_reset&amp;quot;;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;} else if (!strcmp(op, &amp;quot;detach&amp;quot;)) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    tag = &amp;quot;project_detach&amp;quot;;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;} else if (!strcmp(op, &amp;quot;update&amp;quot;)) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    tag = &amp;quot;project_update&amp;quot;;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;} else if (!strcmp(op, &amp;quot;suspend&amp;quot;)) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    tag = &amp;quot;project_suspend&amp;quot;;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    project.suspended_via_gui = true;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;} else if (!strcmp(op, &amp;quot;resume&amp;quot;)) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    tag = &amp;quot;project_resume&amp;quot;;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    project.suspended_via_gui = false;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;} else {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    return -1;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;sprintf(buf,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &amp;quot;&amp;lt;%s&amp;gt;\n&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &amp;quot;  &amp;lt;project_url&amp;gt;%s&amp;lt;&#x2F;project_url&amp;gt;\n&amp;quot;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    &amp;quot;&amp;lt;&#x2F;%s&amp;gt;\n&amp;quot;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    tag,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    project.master_url.c_str(),
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    tag
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;retval = rpc.do_rpc(buf);
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;if (!retval) {
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;    retval = rpc.parse_reply();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;return retval;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;}
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;用户的指令会被包装成 xml 格式，然后使用 &lt;code&gt;send_request(const chap *p)&lt;&#x2F;code&gt; 函数发送给 &lt;code&gt;boinc&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-shou-ming-ling&quot;&gt;接收命令&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;client&#x2F;gui_rpc_server_ops.cpp&lt;&#x2F;code&gt; 的 &lt;code&gt;GUI_RPC_CONN::handle_rpc()&lt;&#x2F;code&gt; 用 &lt;code&gt;recv&lt;&#x2F;code&gt; 或 &lt;code&gt;read&lt;&#x2F;code&gt; 来接收消息，并进行处理。它通过调用 &lt;code&gt;lib&#x2F;parse&#x2F;h&lt;&#x2F;code&gt; 的 &lt;code&gt;match_tag(const char *, const char *)&lt;&#x2F;code&gt; 来解析 xml，并调用对应的函数。
在进行初步的匹配后，用户指令会以字符串的形式发送给 &lt;code&gt;handle_project_op&lt;&#x2F;code&gt;。对于多数的命令，程序会通过 gstate 的方式来处理。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xia-yi-bu-gong-zuo&quot;&gt;下一步工作&lt;&#x2F;h2&gt;
&lt;p&gt;折腾了一段时间，对 BOINC 的程序框架（尤其是用户交互部分）有了些许了解。接下来，就要啃啃 &lt;code&gt;client&#x2F;client_state.h&lt;&#x2F;code&gt; 里的 &lt;code&gt;class CLIENT_STATE&lt;&#x2F;code&gt;，与 BOINC 的核心部分打交道了。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Vim 自动加载 cscope.out</title>
        <published>2013-09-01T00:00:00+00:00</published>
        <updated>2013-09-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/vim-auto-cscope/"/>
        <id>https://blog.zhenbo.pro/vim-auto-cscope/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/vim-auto-cscope/">&lt;p&gt;这两天看 BOINC 的代码，需要 find usage 的功能。花了一上午，摸索出了让 vim 自动加载生成的 cscope.out 的方法。&lt;&#x2F;p&gt;
&lt;p&gt;在 .vimrc 中插入如下代码即可：
{% highlight bash %}
&quot;Configure for cscope
set nocscopeverbose
set cscopequickfix=s-,c-,d-,i-,t-,e-
set cst
function LoadCscope(path)
&quot;防止无限递归
if a:path == $HOME
return
endif
if (executable(&quot;cscope&quot;) &amp;amp;&amp;amp; has(&quot;cscope&quot;))
let l:outfile=a:path.&quot;&#x2F;cscope.out&quot;
let l:outpath=a:path
if filereadable(outfile)
cs reset
exe &quot;cs add&quot; outfile outpath
else
&quot;递归
let l:newpath=a:path.&quot;&#x2F;..&quot;
let newpath=resolve(newpath)
&quot;echo newpath
call LoadCscope(newpath)
endif
endif
endfunction
call LoadCscope(getcwd())
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;这是我 &lt;em&gt;第一次&lt;&#x2F;em&gt; 写的 vimrc 的函数。思路很简单，就是递归找父目录。但有几处障碍让我花费了一个上午。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;if a:path == $HOME&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;vim 中，使用函数传入的参数时要加前缀 a，这一点跟 C，python 之类的就不太一样&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;let newpath=resolve(newpath)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果不加这句话，vim 脚本是无法正确的识别文件链接 &lt;code&gt;..&lt;&#x2F;code&gt; 的&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;set nocscopeverbose&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;有的时候，vim 会自动加载 cscope.out。这个时候，就会发生冲突。加上这句话就能解决该问题。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fedora 19 64bit 解决开源 AMD 显卡驱动问题</title>
        <published>2013-07-08T00:00:00+00:00</published>
        <updated>2013-07-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/fedora-19-64bit-amd-driver/"/>
        <id>https://blog.zhenbo.pro/fedora-19-64bit-amd-driver/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/fedora-19-64bit-amd-driver/">&lt;p&gt;自从装上了 64 位的 Fedora，就没能用 wine 带起过 3D 游戏。设置上&lt;code&gt;LIBGL_DEBUG=verbose&lt;&#x2F;code&gt;，用 crossover 测试，有这样的提示
{% highlight c %}
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;tls&#x2F;r600_dri.so
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so
libGL error: dlopen &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so failed (&#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so: cannot open shared object file: No such file or directory)
libGL error: unable to load driver: r600_dri.so
libGL error: driver pointer missing
libGL error: failed to load driver: r600
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;tls&#x2F;swrast_dri.so
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;swrast_dri.so
libGL error: dlopen &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;swrast_dri.so failed (&#x2F;usr&#x2F;lib&#x2F;dri&#x2F;swrast_dri.so: cannot open shared object file: No such file or directory)
libGL error: unable to load driver: swrast_dri.so
libGL error: failed to load driver: swrast
{% endhighlight %}
我的电脑上，只有 &lt;code&gt;&#x2F;usr&#x2F;lib64&#x2F;dri&#x2F;r600_dri.so&lt;&#x2F;code&gt;。很显然，我缺的是 32 位的驱动。第一反应，是建一个软链接。但这次，提示变成了
{% highlight c %}
libGL error: dlopen &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so failed (&#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so: cannot open shared object file: Too many levels of symbolic links)
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;那我拷贝过去呢？很遗憾，没这么简单。
{% highlight c %}
libGL error: dlopen &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so failed (&#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so: wrong ELF class: ELFCLASS64)
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;这里，我就只好用一个笨方法了：我从网上下载了 Fedora 19 32bit 的 ISO，然后装到了虚拟机里，再把这个文件拷贝了出来（后面我会提供链接）。&lt;&#x2F;p&gt;
&lt;p&gt;问题是，最后还是出了一点小问题
{% highlight c %}
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;tls&#x2F;r600_dri.so
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so
libGL error: dlopen &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;r600_dri.so failed (libLLVM-3.3.so: cannot open shared object file: No such file or directory)
libGL error: unable to load driver: r600_dri.so
libGL error: driver pointer missing
libGL error: failed to load driver: r600
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;tls&#x2F;swrast_dri.so
libGL: OpenDriver: trying &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;swrast_dri.so
libGL error: dlopen &#x2F;usr&#x2F;lib&#x2F;dri&#x2F;swrast_dri.so failed (&#x2F;usr&#x2F;lib&#x2F;dri&#x2F;swrast_dri.so: cannot open shared object file: No such file or directory)
libGL error: unable to load driver: swrast_dri.so
libGL error: failed to load driver: swrast
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;到这里，问题就简单多了。&lt;code&gt;yum install llvm.i686&lt;&#x2F;code&gt;，搞定。&lt;&#x2F;p&gt;
&lt;p&gt;32 位驱动下载链接：&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;skydrive.live.com&#x2F;redir?resid=902E5583A63BC02B!120&quot;&gt;https:&#x2F;&#x2F;skydrive.live.com&#x2F;redir?resid=902E5583A63BC02B!120&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;sha1sum: &lt;code&gt;86351d2d73e63c7088e90d442969643f7ad79111&lt;&#x2F;code&gt;
&lt;br &#x2F;&gt;
&lt;br &#x2F;&gt;
P.S.  DOTA里的神灵果然是一个（虐AI）强大的英雄。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>用 ssh 安装 Linux</title>
        <published>2013-06-26T00:00:00+00:00</published>
        <updated>2013-06-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/ssh-linux/"/>
        <id>https://blog.zhenbo.pro/ssh-linux/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/ssh-linux/">&lt;p&gt;这两天不知道为啥抽风，又打算在虚拟机里装 Gentoo 了。据说为了复制粘贴命令方便，用 ssh 安装是更好的选择。摸索了一下，总算学会了。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zai-guest-li-qi-dong-ssh&quot;&gt;在 Guest 里启动 ssh&lt;&#x2F;h4&gt;
&lt;p&gt;参考 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.gentoo.org&#x2F;doc&#x2F;zh_cn&#x2F;handbook&#x2F;handbook-x86.xml?part=1&amp;amp;chap=2&quot;&gt;Gentoo 文档&lt;&#x2F;a&gt; ，在 Guest 里运行 &lt;code&gt;&#x2F;etc&#x2F;init.d&#x2F;sshd start&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pei-zhi-ssh-zhang-hao&quot;&gt;配置 ssh 帐号&lt;&#x2F;h4&gt;
&lt;p&gt;在这里，我偷懒了，没有在 gentoo 里多建立一个用户，而是直接 &lt;code&gt;passwd root&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;zhu-ji-deng-lu-ssh&quot;&gt;主机登录 ssh&lt;&#x2F;h4&gt;
&lt;p&gt;首先，在 Guest 里运行 &lt;code&gt;ifconfig&lt;&#x2F;code&gt;，看一下 IP 地址。然后，在主机的终端里运行 &lt;code&gt;ssh root@ip_address&lt;&#x2F;code&gt;，输入刚才设置的 root 密码就可以了。如果无法登陆，应该检查一下 &lt;code&gt;.ssh&#x2F;known_hosts&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;hou-ji&quot;&gt;后记&lt;&#x2F;h4&gt;
&lt;p&gt;ssh 是一个很强大的工具，功能远不仅仅是给 Guest 装系统。我的设置，也感觉有安全隐患。至于 ssh 的深入挖掘，恩，反正我是不搞了 T_T&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>给 Wine 报 bug 的错题本</title>
        <published>2013-06-07T00:00:00+00:00</published>
        <updated>2013-06-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/wine-bug/"/>
        <id>https://blog.zhenbo.pro/wine-bug/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/wine-bug/">&lt;p&gt;查了一下，我保送后第一次在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;bugs.winehq.org&quot;&gt;http:&#x2F;&#x2F;bugs.winehq.org&lt;&#x2F;a&gt; 上报 bug， 已经是13年1月的事了。尝试着参与了一些 debug 工作，也犯了一些错误。写下这篇短文，权当笔记吧。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bi-mian-zhong-fu&quot;&gt;避免重复&lt;&#x2F;h2&gt;
&lt;p&gt;在上报了一个 bug，结果被 &lt;code&gt;CLOSED DUPLICATE&lt;&#x2F;code&gt;，是很令人沮丧的。而且，这也不能给开发人员提供有效的帮助。除了明显的重复（比如 [bug 32751][32751]），还有一些不容易发现的重复，比如 [bug 33433][33433]。
[32751]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=32751
[33433]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=33433&lt;&#x2F;p&gt;
&lt;p&gt;我在搜索 &lt;em&gt;Fetion&lt;&#x2F;em&gt; 的时候，只找到了 [Bug 29769 - Fetion IM Client: crashes when clicking on profile icon ][29769]，所以我就上报了 [Bug 33433 - Fetion IM crashed after opening a chat dialog][33433]。 但实际上，因为 backtrace 相同， [bug 29769#3][] 就把 [Bug 29770 - Fetion IM Client: crashes when opening a chat window][29770] 关掉了。所以，虽然表现不同，但这都是同一个 bug。
[29769]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=29769
[33433]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=33433
[bug 29769#3]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=29769#c3
[29770]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=29770&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhi-tao-lun-yi-ge-bug&quot;&gt;只讨论一个 bug&lt;&#x2F;h2&gt;
&lt;p&gt;我不知道怎么表述更好；在这里，我有两个例子。&lt;&#x2F;p&gt;
&lt;p&gt;首先是 [29769#12][f-12] 。我在安装的时候出现了错误提示的对话框。Jactry Zeng 和 Qian Hong 都提醒我，我应当新建一个 bug。
[f-12]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=29769&lt;&#x2F;p&gt;
&lt;p&gt;其次，大家在 bugzilla 上研究 workaround，只是针对该 bug，而不是为了让软件能正常运行。比如 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=29638#c7&quot;&gt;bug 29638#7&lt;&#x2F;a&gt;， Qian Hong 的回复是 &lt;em&gt;There are two crashing bug related to QQ, winetricks -q riched20 is for Bug 29636.&lt;&#x2F;em&gt; 一口气 winetricks 一串，只会让问题复杂化。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhong-wen-xian-shi-de-wen-ti&quot;&gt;中文显示的问题&lt;&#x2F;h2&gt;
&lt;p&gt;[Bug 33666 - YY6 Installer can&#x27;t show Chinese][33666] 是我最近报的一个 bug。这类 bug，多数的原因都是字体缺失。在 [文泉驿][wen] 之类的的字体屈指可数的情况下， [FontReplacement][fr] 就是最有效的解决方法了。所以，遇到类似问题时，最好先检查一下，自己是否打好了 [补丁][patch]。
[33666]: http:&#x2F;&#x2F;bugs.winehq.org&#x2F;show_bug.cgi?id=33666
[wen]: http:&#x2F;&#x2F;wenq.org&#x2F;wqy2&#x2F;index.cgi
[fr]: http:&#x2F;&#x2F;wiki.winehq.org&#x2F;FontReplacements
[patch]: https:&#x2F;&#x2F;gitcafe.com&#x2F;endle&#x2F;FontReplacements--TestingPatch
&lt;br &#x2F;&gt;
在学习报 bug 的过程中，非常感谢 [wine-zh][] 列表中大家对我的帮助，特别是 Qian Hong，回答了我大量的问题。有关于 Wine 的疑问，不妨试试 Google &lt;code&gt;site:http:&#x2F;&#x2F;www.freelists.org&#x2F;archive&#x2F;wine-zh&#x2F;&lt;&#x2F;code&gt; ，你会有意想不到的收获的。
[wine-zh]: www.freelists.org&#x2F;list&#x2F;wine-zh&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Linux 下利用网盘同步 Firefox 扩展</title>
        <published>2013-05-28T00:00:00+00:00</published>
        <updated>2013-05-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/linux-firefox/"/>
        <id>https://blog.zhenbo.pro/linux-firefox/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/linux-firefox/">&lt;p&gt;不知道为什么，最近我的 Firefox 加载一些网页总是有问题。参考了 [Bugzilla 上我收到的回复][bug]，我打算重置我的个人配置。&lt;&#x2F;p&gt;
&lt;p&gt;但这么做，就意味着我要重新安装一下自己的扩展，和积累的一票油猴脚本。尤其是 [ForTrick][fox]，还要手动去下载 .xpi 文件。网上有人推荐了 [FEBE][febe]，但不知道为什么，我就是没装上。所以，就打算试试手动备份。
[bug]: https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=851867
[febe]: http:&#x2F;&#x2F;www.williamlong.info&#x2F;archives&#x2F;2217.html
[fox]: http:&#x2F;&#x2F;www.foxtrick.org&lt;&#x2F;p&gt;
&lt;h2 id=&quot;linux-xia-de-pei-zhi-wen-jian&quot;&gt;Linux 下的配置文件&lt;&#x2F;h2&gt;
&lt;p&gt;自己摸索了一下，Linux 把 FF 的配置文件保存在了 &lt;code&gt;~&#x2F;.mozilla&#x2F;firefox&lt;&#x2F;code&gt; 里。 &lt;code&gt;profiles.ini&lt;&#x2F;code&gt; 则决定了加载哪个用户的配置文件。默认情况下，会有一个名字很奇怪的文件夹（我的是 &lt;code&gt;hptzpdkk.default&lt;&#x2F;code&gt;），作为默认文件夹。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jian-li-xin-de-pei-zhi-wen-jian&quot;&gt;建立新的配置文件&lt;&#x2F;h2&gt;
&lt;p&gt;关掉 Firefox，输入 &lt;code&gt;firefox -p&lt;&#x2F;code&gt;（本人测试不区分大小写），然后会出现一个很和谐的窗口。
&lt;img src=&quot;&#x2F;images&#x2F;firefox&#x2F;sync.png&quot; alt=&quot;screen&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;新建一个用户，然后指定新的文件夹即可。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;she-zhi-ruan-lian-jie&quot;&gt;设置软链接&lt;&#x2F;h2&gt;
&lt;p&gt;关于 Linux 下软链接的知识，可以阅读 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.ibm.com&#x2F;developerworks&#x2F;cn&#x2F;linux&#x2F;l-lpic1-v3-104-6&#x2F;&quot;&gt;创建和更改硬链接和符号链接&lt;&#x2F;a&gt;，但如果没有耐心，也没必要读完这篇文章。&lt;&#x2F;p&gt;
&lt;p&gt;我平时使用 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jianguoyun.com&#x2F;&quot;&gt;坚果云&lt;&#x2F;a&gt; 来同步我的文件。在创建了新的用户配置以后，首先备份 &lt;code&gt;profiles.ini&lt;&#x2F;code&gt;，我使用的代码是 &lt;code&gt;ln -s ~&#x2F;nutstore&#x2F;firefox&#x2F;profiles.ini ~&#x2F;.mozilla&#x2F;firefox&#x2F;profiles.ini&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;ln -s&lt;&#x2F;code&gt; 后的两个路径，第一个是文件实际存放的位置（坚果云自动同步文件夹），第二个是链接的位置（相当于快捷方式）。用同样的方法，备份 &lt;code&gt;gm_scripts&lt;&#x2F;code&gt; 和 &lt;code&gt;extensions&lt;&#x2F;code&gt; 两个文件夹就可以了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;试了半天，没有找到扩展的配置文件的位置，只好重新设置一遍了。如果你有解决方案，请告诉我，谢谢&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>我的TED精选集（一）</title>
        <published>2013-05-22T00:00:00+00:00</published>
        <updated>2013-05-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/ted-1/"/>
        <id>https://blog.zhenbo.pro/ted-1/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/ted-1/">&lt;p&gt;之前零零散散的看了不少 TED 的视频，就整理一个精选集吧，权当自己的笔记了。&lt;&#x2F;p&gt;
&lt;p&gt;####&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;v.youku.com&#x2F;v_show&#x2F;id_XNDcyMTY5NDAw.html&quot;&gt;康拉德·沃尔夫拉姆Conrad Wolfram：用计算机教导孩子真正的数学&lt;&#x2F;a&gt;
&lt;strong&gt;大力推荐！！&lt;&#x2F;strong&gt; 我看了一些 TED 的视频，但没看到哪个有这么大的信息量！简单摘录一下：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;数学不等于计算 &lt;em&gt;(math != calculate)&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;计算不是数学的基础&lt;&#x2F;li&gt;
&lt;li&gt;学习的顺序，不一定要遵循发明的顺序&lt;&#x2F;li&gt;
&lt;li&gt;人应当从繁重而无意义的工作（计算）中解脱出来&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;####&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;v.youku.com&#x2F;v_show&#x2F;id_XNDYwMDQ0MjY4.html&quot;&gt;达恩·迈尔Dan Meyer:数学课需要改革&lt;&#x2F;a&gt;
看到了这个视频，我第一个想到了CSDN上 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;blog.csdn.net&#x2F;myan&#x2F;article&#x2F;details&#x2F;647511&quot;&gt;孟岩关于线性代数&lt;&#x2F;a&gt; 的这一段话&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们不认为直觉性与抽象性一定相互矛盾，特别是在数学教育中和数学教材中，帮助学生建立直觉，有助于它们理解那些抽象的概念，进而理解数学的本质。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;惭愧地说，我还没真正接触线性代数、集合论等东西，对这些高度抽象的东西，没什么发言的资格。而原视频中过于简单的例子（往桶里倒水），更像是对小学生（无贬义）的启发，而不是严格的数学教学。所以，如何平衡直觉与严谨，各位见仁见智了。&lt;&#x2F;p&gt;
&lt;p&gt;但这段视频里提出了一点：为什么人们不会像推荐一首好歌一样推荐数学？Dan Meyer 努力培养学生对数学的爱，从这个角度讲，他是当之无愧的 &lt;em&gt;优秀&lt;&#x2F;em&gt; 的数学教师。&lt;&#x2F;p&gt;
&lt;p&gt;####&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;v.youku.com&#x2F;v_show&#x2F;id_XNDYwMDI0NDEy.html&quot;&gt;Ken Robinson:认为学校扼杀创造力&lt;&#x2F;a&gt;
美帝的教育制度在 TED 上中枪早就不新鲜了，我朝也不必把人家当成正面典型学。整个视频其实就一句话：&lt;strong&gt;教育的目的是什么？&lt;&#x2F;strong&gt; 老调重提，但仍值得我们深思。&lt;&#x2F;p&gt;
&lt;p&gt;####&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;v.youku.com&#x2F;v_show&#x2F;id_XNTIzNTUwNjEy.html&quot;&gt;帕旺辛哈:大脑如何学习辨识物体&lt;&#x2F;a&gt;
虽然这个视频没少讲对视觉障碍儿童的治疗，但真正让我激动的，还是对机器人的研究（职业病？）&lt;&#x2F;p&gt;
&lt;p&gt;从对机器学习的研究，我们是否也能窥探，我们出生后，是如何走出懵懂，认识到这个世界？&lt;&#x2F;p&gt;
&lt;p&gt;很 13 的再扯一句，我们有没有办法，想孩童一样，无比好奇地，探索这个壮丽的世界？&lt;&#x2F;p&gt;
&lt;p&gt;####&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;v.youku.com&#x2F;v_show&#x2F;id_XNDIyOTU3NTI0.html&quot;&gt;Salman Khan：视频重塑教育&lt;&#x2F;a&gt;
这个视频的道理很好理解：你看到我的这篇文章，就能知道，额，30% 吧。&lt;&#x2F;p&gt;
&lt;p&gt;不要把这个视频当成简单的广告。可汗先生提到的功能太强大了：通过统计学生按暂停的位置，来统计课程中的难点。进而，对有困难的学生，提供更精准的帮助。&lt;&#x2F;p&gt;
&lt;p&gt;恩，老师们就要逆天了。&lt;&#x2F;p&gt;
&lt;p&gt;####&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;v.youku.com&#x2F;v_show&#x2F;id_XNTIzNDY1NDYw.html&quot;&gt;Robert Full:谈生物进化启发工程设计&lt;&#x2F;a&gt;
可能因为我对工程学不感兴趣，这个视频没给我留下太深的印象。只是最后，开发出的翻越障碍能力极强的机器人，真的要让人怀疑，多足生物近乎完美的腿部结构，背后，是否藏着上帝的智慧？&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;简单就整理了这么几个，大家尽情吐槽（优酷是不是得给我点广告费？）&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>C语言的通用链表</title>
        <published>2013-05-06T00:00:00+00:00</published>
        <updated>2013-05-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/c-list/"/>
        <id>https://blog.zhenbo.pro/c-list/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/c-list/">&lt;p&gt;C++ 里的 STL 能方便的定义一个 &lt;code&gt;list&lt;&#x2F;code&gt; 类，而 C 语言里，没有对模板的支持，想要写出通用的库，难度显然要大不少。但可以肯定的是，一定有一个 &lt;em&gt;可靠&lt;&#x2F;em&gt; 的解决办法的。&lt;&#x2F;p&gt;
&lt;p&gt;在 Google 上搜索时，找到了一篇文章 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.cnblogs.com&#x2F;wwang&#x2F;archive&#x2F;2010&#x2F;11&#x2F;28&#x2F;1889281.html&quot;&gt;玩转C链表&lt;&#x2F;a&gt;。这篇文章写的很好，但我第一眼看的时候，却没找到入手点。琢磨了一个下午，总算有了一点思路。&lt;&#x2F;p&gt;
&lt;p&gt;相比于传统的链表
{% highlight c %}
struct int_node {
int val;
struct int_node *next, *prev;
};
{% endhighlight %}
Linux Kernel 把两个指针拆了出来。
{% highlight c %}
struct list_head {
struct list_head *next, *prev;
};
struct int_node {
int val;
struct list_head list;
};
{% endhighlight %}
这样，对链表的操作，可以只针对于 &lt;code&gt;struct list_head&lt;&#x2F;code&gt;，而有了通用的写法。下一步的问题，就是由 &lt;code&gt;struct list_head&lt;&#x2F;code&gt; 来获得 &lt;code&gt;struct int_node&lt;&#x2F;code&gt; 中存储的数据 &lt;code&gt;val&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;因为结构体的数据在内存中是连续的，所以，可以用指针的偏移量来存取 val。内核中有这样的代码
{% highlight c %}
&#x2F;**&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;container_of - cast a member of a structure out to the containing structure&lt;&#x2F;li&gt;
&lt;li&gt;@ptr:    the pointer to the member.&lt;&#x2F;li&gt;
&lt;li&gt;@type:    the type of the container struct this is embedded in.&lt;&#x2F;li&gt;
&lt;li&gt;@member:    the name of the member within the struct.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;*&#x2F;
#define container_of(ptr, type, member) (type *)((char *)ptr -offsetof(type,member))
{% endhighlight %}
传入的三个参数是：指向当前的 &lt;code&gt;struct list_head&lt;&#x2F;code&gt; 的指针，整个容器的类型（本例中 &lt;code&gt;struct int_node&lt;&#x2F;code&gt;），和 &lt;code&gt;list_head&lt;&#x2F;code&gt; 的名称（本例中 &lt;code&gt;list&lt;&#x2F;code&gt;）。而这个宏所返回的，就是指向整个容器的指针（本例中 &lt;code&gt;struct int_node *&lt;&#x2F;code&gt;）。&lt;&#x2F;p&gt;
&lt;p&gt;现在，就要求偏移量 &lt;code&gt;offsetof&lt;&#x2F;code&gt; 了。kernel 中的实现很巧妙，但要实现这个功能方法不少（比如 &lt;code&gt;sizeof(struct int_node) - sizeof(struct list_head)&lt;&#x2F;code&gt;）。至于哪种方法更好。。。我觉得这个世界上能写出比 Linux Kernel 质量更好的代码的人为数不多吧。&lt;&#x2F;p&gt;
&lt;p&gt;解决了这些，插入和删除的操作就难度不大了（参数类型都是 &lt;code&gt;struct list_head *&lt;&#x2F;code&gt;）。略有不足的是，内存分配与回收的细节没有被隐藏起来。不过总的来说，通用链表的问题，算是比较优雅的解决了吧。&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>几个让人头疼的代码习惯</title>
        <published>2013-04-04T00:00:00+00:00</published>
        <updated>2013-04-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/c-plusplus-code-quality/"/>
        <id>https://blog.zhenbo.pro/c-plusplus-code-quality/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/c-plusplus-code-quality/">&lt;p&gt;之前，我写代码并不是很重视代码风格，觉得自己看着舒服就好。毕竟，自己写的解题报告不多，看过的代码也很少。这两天跟同学一起写小游戏，花了心思去看别人的代码，才意识到，不好的习惯，很让人头疼。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nan-kan-de-biao-da-shi&quot;&gt;难看的表达式&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;C&#x2F;C++&lt;&#x2F;em&gt; 里的表达式是相当灵活了，但灵活是有代价的，包括让人难以阅读（尤其是阅读者的水平也是半斤八两）。个人的经验，有这几种常见情况。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bu-jia-kong-ge-de-xi-guan&quot;&gt;不加空格的习惯&lt;&#x2F;h4&gt;
&lt;p&gt;例如这句代码 &lt;code&gt;if(k1&amp;lt;k2||!pos) pos=i;&lt;&#x2F;code&gt; 这是我在 2011 年的时候写的，看着不舒服吧？后来经人介绍，在 [清澄][1] 上刷了几道水题，被 [风格分][2] 蹂躏了一顿，就养成了加空格的习惯。（当然，不要迷信风格分）
[1]: http:&#x2F;&#x2F;www.tsinsen.com&#x2F;
[2]: http:&#x2F;&#x2F;www.tsinsen.com&#x2F;Help.page#ss&lt;&#x2F;p&gt;
&lt;h4 id=&quot;tai-fu-za-de-biao-da-shi&quot;&gt;太复杂的表达式&lt;&#x2F;h4&gt;
&lt;p&gt;我承认，我阅读代码的能力很差。但写代码的时候，也不能要求每个人都是天才。例如&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;for(i=(dy&amp;gt;0?floor((j-now.x)*df):ceil((j-now.x)*df))+now.y;(i!=(dy&amp;lt;0?floor((j-now.x+dx)*df):ceil((j-now.x+dx)*df))+now.y);i+=dy)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;一个很 &lt;em&gt;简单&lt;&#x2F;em&gt; 的 for 循环，我觉得，我看不懂，主要的责任并不在我。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bu-he-gua-de-ji-qiao&quot;&gt;不合适的技巧&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;code&gt;current_player=++current_player%player_num;&lt;&#x2F;code&gt; 在编译这句话的时候，gcc 给出了 warning。虽然巧用 &lt;code&gt;++&lt;&#x2F;code&gt; 能让代码简洁不少，但以我这个半吊子水平看来，在这里多写两句话，不是什么坏事。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tai-chang-de-xing&quot;&gt;太长的行&lt;&#x2F;h2&gt;
&lt;p&gt;这个问题其实在上面已经涉及到了。每行 80 的限制已经不适应现在的情况，但避免让某一行的代码太复杂，还是一个值得遵守的习惯。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shen-mi-chang-liang&quot;&gt;神秘常量&lt;&#x2F;h2&gt;
&lt;p&gt;C 语言里的 &lt;code&gt;define&lt;&#x2F;code&gt; 和 &lt;code&gt;const&lt;&#x2F;code&gt; 很方便，可以很好的回避这样的情况&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;   if(temp==0xff00A2E8) plyr[player-1].check_1(); 
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;   if(temp==0xffED1C24) plyr[player-1].check_2();
&lt;&#x2F;span&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;   if(temp==0xffB5E61D) plyr[player-1].bypass();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;第一眼看，这三个常量就像是天书一样。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;qu-fen-lei-ming-yu-bian-liang-ming&quot;&gt;区分类名与变量名&lt;&#x2F;h2&gt;
&lt;p&gt;还是看一个例子 &lt;code&gt;player ply[4];&lt;&#x2F;code&gt; 有的编辑器（比如 Geany）能高亮用户定义的类名。但当代码长了，维护起来的单独就大了。如果规模小，也不必严格遵守大工程里的命名原则。把首字母大写，很多问题都能得到解决。&lt;&#x2F;p&gt;
&lt;p&gt;说了一点点，但要承认，对于代码风格，我也是一知半解。如果有哪里说的不对，也恳请大家指正。&lt;&#x2F;p&gt;
&lt;P STYLE=&quot;margin-bottom: 0cm&quot;&gt;&lt;FONT COLOR=&quot;#cccccc&quot;&gt;我会说我是看了&lt;FONT FACE=&quot;Liberation Serif, serif&quot;&gt;&lt;FONT SIZE=3&gt;&lt;SPAN LANG=&quot;en-US&quot;&gt;SEO&lt;&#x2F;SPAN&gt;&lt;&#x2F;FONT&gt;&lt;&#x2F;FONT&gt;介绍“网站要保持更新频率”才写着篇文章凑数的？&lt;&#x2F;FONT&gt;&lt;&#x2F;P&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fedora 下安装 mingw32 以交叉编译</title>
        <published>2013-02-05T00:00:00+00:00</published>
        <updated>2013-02-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/fedora-mingw32/"/>
        <id>https://blog.zhenbo.pro/fedora-mingw32/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/fedora-mingw32/">&lt;p&gt;Qian Hong 在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.freelists.org&#x2F;list&#x2F;wine-zh&quot;&gt;wine-zh 邮件列表&lt;&#x2F;a&gt; 里发表了&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.freelists.org&#x2F;post&#x2F;wine-zh&#x2F;-WIne&quot;&gt;分享: 如何入门 Wine 的开发调试?&lt;&#x2F;a&gt;，提到了要尝试使用 mingw 交叉编译代码为 exe，然后尝试 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wiki.winehq.org&#x2F;DebugChannels&quot;&gt;debug channel&lt;&#x2F;a&gt;。兜了一个圈子，总算在我的 Fedora 16上装好了 mingw.&lt;&#x2F;p&gt;
&lt;p&gt;首先，要去 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;apps.fedoraproject.org&#x2F;packages&#x2F;&quot;&gt;fedora packages&lt;&#x2F;a&gt; 搜索 mingw 的包。搜完以后，把看着舒服的装上吧（我不太清楚哪些必要就装了一大堆。。。反正 mingw-gcc 肯定是必要的）。&lt;&#x2F;p&gt;
&lt;p&gt;装好后，我尝试输入 &lt;code&gt;mingw&lt;&#x2F;code&gt;, &lt;code&gt;mingw32&lt;&#x2F;code&gt;, &lt;code&gt;gcc-mingw&lt;&#x2F;code&gt; 等，但都提示“找不到命令”。最后解决的方法很简单： 输入 &lt;code&gt;ls &#x2F;usr&#x2F;bin | grep mingw&lt;&#x2F;code&gt; ， 在里面找到需要的命令就可以了。在我的电脑上，就是 &lt;code&gt;i686-pc-mingw32-gcc&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;接着，就是 Hello World 的时间了：&lt;&#x2F;p&gt;
&lt;p&gt;$ i686-pc-mingw32-gcc hello.c -o hello.exe&lt;&#x2F;p&gt;
&lt;p&gt;$ wine hello.exe&lt;&#x2F;p&gt;
&lt;p&gt;Hello, MingW!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;font size=&quot;0&quot; color=&quot;#F3F3F3&quot;&gt;距上一次写日志已经很久很久了吧。。。&lt;&#x2F;font&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>C语言生成随机数的小实验</title>
        <published>2013-01-30T00:00:00+00:00</published>
        <updated>2013-01-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/crand/"/>
        <id>https://blog.zhenbo.pro/crand/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/crand/">&lt;p&gt;C语言的随机函数 &lt;code&gt;rand()&lt;&#x2F;code&gt;， 理论上会在[1, RAND_MAX]之间生成均匀的随机数。但通常情况下，我们需要一个范围很小的随机数（例如随机化快排）。&lt;&#x2F;p&gt;
&lt;p&gt;多数人的做法（包括之前的我），直接采用 &lt;code&gt;rand() % N&lt;&#x2F;code&gt; 的做法。但根据 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;c-faq.com&#x2F;lib&#x2F;rand.html&quot;&gt;C-faq 13.15&lt;&#x2F;a&gt; 的介绍，这并不是一个好方法。相比之下，提供了另外两种方案：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;(int)((double)rand() &#x2F; ((double)RAND_MAX + 1) * N)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;rand() &#x2F; (RAND_MAX &#x2F; N + 1)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;为了验证哪种方式生成的随机数更均匀，我就打算做一个小小的测试。&lt;&#x2F;p&gt;
&lt;p&gt;实验步骤：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;使用&lt;code&gt;rand()&lt;&#x2F;code&gt; 生成 10^7 个随机数。&lt;&#x2F;li&gt;
&lt;li&gt;对于每个随机数，分别带入3个函数中来生成随机数，并将结果输出到文件中。&lt;&#x2F;li&gt;
&lt;li&gt;使用R语言，利用 &lt;code&gt;hist(x, breaks=range)&lt;&#x2F;code&gt; 来生成图像（感谢SHLUG上的朋友们）。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;注：使用 &lt;code&gt;rand()%N&lt;&#x2F;code&gt; 方案生成的随机数被存储到了向量 r0 中，另外两种被存储到了 r1 和 r2 中。经过测试， r1 与 r2 绝大多数元素都是一样的。所以，接下来我仅对比 r0 和 r1&lt;&#x2F;p&gt;
&lt;p&gt;我把范围设到了5000，最后统计的结果让我很震惊：r0 和 r1 的频率分布都很均匀&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;c-lang&#x2F;rand-a-r0.jpg&quot; alt=&quot;a0&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;c-lang&#x2F;rand-a-r1.jpg&quot; alt=&quot;a1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;考虑到可能由于数据量太大抹掉了一些区别，我将数据范围缩减至 10^4&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;c-lang&#x2F;rand-b-r0.jpg&quot; alt=&quot;b0&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;c-lang&#x2F;rand-b-r1.jpg&quot; alt=&quot;b1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;依旧看不到明显的区别。
我将随机数范围缩减到 &lt;code&gt;[0,100)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;c-lang&#x2F;rand-c-r0.jpg&quot; alt=&quot;c0&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;images&#x2F;c-lang&#x2F;rand-c-r1.jpg&quot; alt=&quot;c1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;直观上看，仍然没有本质上的区别。
在 &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;c-faq.com&#x2F;lib&#x2F;notveryrand.html&quot;&gt;C-faq 13.18&lt;&#x2F;a&gt; 上提到，使用 &lt;code&gt;rand() % N&lt;&#x2F;code&gt; 会让随机数具有周期性。但是我将范围设置为2，也没有找到明显的周期。&lt;&#x2F;p&gt;
&lt;p&gt;由此看来，&lt;code&gt;rand() % N&lt;&#x2F;code&gt; 并没有 &lt;em&gt;显著&lt;&#x2F;em&gt; 的缺陷。但为什么除了 C-FAQ，&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;book.douban.com&#x2F;subject&#x2F;1148265&#x2F;&quot;&gt;C语言的科学与艺术&lt;&#x2F;a&gt; 也这么说呢？看来，找寻答案的道路还很漫长啊。&lt;&#x2F;p&gt;
&lt;P STYLE=&quot;margin-bottom: 0cm&quot;&gt;&lt;FONT COLOR=&quot;#e6e6e6&quot;&gt;折腾这么久得到这样的一个结论，实在是太让人沮丧了。。。&lt;&#x2F;FONT&gt;&lt;&#x2F;P&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>重学quicksort</title>
        <published>2013-01-20T00:00:00+00:00</published>
        <updated>2013-01-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/quicksort/"/>
        <id>https://blog.zhenbo.pro/quicksort/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/quicksort/">&lt;p&gt;前两天写一道水题，想尝试一下手写快排，可没想到花了一个多小时都没有搞定。看来， &lt;strong&gt;The devil is in the detail&lt;&#x2F;strong&gt; ，细节的地方埋藏了许多知识盲点。看来，有的草，是迟早要花时间除的。&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight c++ %}
void quicksort(int A[], int p, int r)
{
if (p &amp;gt;= r)		return;
int q;
q = partition(A, p, r);
quicksort(A, p, q - 1);
quicksort(A, q + 1, r);
}
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;为了保证 O(nlgn)的复杂度，partition 需要达到O(n)的复杂度。算法导论上，提供了这样一种实现
{% highlight c++ %}
int partition(int A[], int p, int r)
{
int i, j, x;
x = A[r];
i = p - 1;
for (j = p; j &amp;lt; r; ++j){
if (A[j] &amp;lt;= x){
++i;
SWAP(A[i], A[j]);
}
}
SWAP(A[i+1], A[r]);
return i + 1;
}
{% endhighlight %}&lt;&#x2F;p&gt;
&lt;p&gt;这个算法可以这样理解：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;选取主元&lt;&#x2F;li&gt;
&lt;li&gt;让 &lt;code&gt;j&lt;&#x2F;code&gt; 扫描一遍 &lt;code&gt;A[p, r - 1]&lt;&#x2F;code&gt; ，找出所有比 &lt;code&gt;x&lt;&#x2F;code&gt; 小的元素。这样，当循环结束时，&lt;code&gt;A[p, i]&lt;&#x2F;code&gt; 中存放的元素都不会比主元大。通过 &lt;code&gt;SWAP(A[i+1], A[r])&lt;&#x2F;code&gt; ，把主元交换到了 &lt;code&gt;i+1&lt;&#x2F;code&gt; 的位置。
显然，&lt;code&gt;partition()&lt;&#x2F;code&gt; 过程为线性复杂度。我们有个一个不到20行的高效的排序算法。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;但在我重学快排时，看到了[v_JULY_v][1] 写的 [快速排序算法的深入分析][2] ，觉得自己正如他所说
[1]: http:&#x2F;&#x2F;my.csdn.net&#x2F;v_july_v&#x2F;message
[2]: http:&#x2F;&#x2F;blog.csdn.net&#x2F;v_july_v&#x2F;article&#x2F;details&#x2F;6211155&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;只知其表，不知其里，只知其用，不知其本质。很多东西，都是可以从本质看本质的。而大部分人没有做到这一点。从而看了又忘，忘了再看，如此，在对知识的一次一次重复记忆中，始终未能透析本质，从而形成不好的循环。
看来，除草之路漫漫啊。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>hello world</title>
        <published>2013-01-17T00:00:00+00:00</published>
        <updated>2013-01-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.zhenbo.pro/hello-world/"/>
        <id>https://blog.zhenbo.pro/hello-world/</id>
        
        <content type="html" xml:base="https://blog.zhenbo.pro/hello-world/">&lt;p&gt;折腾了将近一周，总算把jekyll架了起来。要是一开始就老老实实照着Jekyll Bootstrap做，就费不了这么多事吧。。。&lt;&#x2F;p&gt;
&lt;p&gt;现在看来，我是得学markdown了。看了一下语法，也不是很复杂。但不大算花太块时间去学了。从现在开始，争取每周更新一篇文章，在写博客的过程中学习markdown吧。&lt;&#x2F;p&gt;
&lt;p&gt;{% highlight c %}
#include &amp;lt;stdio.h&amp;gt;
int main()
{
printf(&quot;Hello, Jekyll World!\n&quot;);
return 0;
}
{% endhighlight %}&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
