<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on StarryReverie 繁星筑梦 星缀夜空</title>
    <link>https://blog.starryreverie.cc/posts/</link>
    <description>Recent content in Posts on StarryReverie 繁星筑梦 星缀夜空</description>
    <generator>Hugo -- 0.146.0</generator>
    <language>zh-cn</language>
    <copyright>Copyright (C) 2025 Justin Chen · Licensed under CC BY-NC-SA 4.0</copyright>
    <lastBuildDate>Wed, 18 Feb 2026 20:47:53 +0800</lastBuildDate>
    <atom:link href="https://blog.starryreverie.cc/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>现代 Linux 桌面会话管理</title>
      <link>https://blog.starryreverie.cc/posts/contemporary-linux-desktop-session-management/</link>
      <pubDate>Wed, 18 Feb 2026 20:47:53 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/contemporary-linux-desktop-session-management/</guid>
      <description>&lt;p&gt;最近尝试了使用 Wayfire 作为 Wayland 混成器，整体体验后，Wayfire 的功能是不错的，可惜其没有 systemd 支持，对于依赖 systemd 的我来说，这显然是不能够满足日常需求的。因此，我研究了如何为其添加基于 systemd 的会话管理机制，记录在此文中。&lt;/p&gt;
&lt;h2 id=&#34;桌面会话的生命周期&#34;&gt;桌面会话的生命周期&lt;/h2&gt;
&lt;h3 id=&#34;传统方式&#34;&gt;传统方式&lt;/h3&gt;
&lt;p&gt;在 Linux 中，本没有什么非常精细的会话管理机制，无论是 Shell 登录还是图形界面登录，一切都可以是非常简洁的流程完成。 Shell 登录时，Login Manager 以登录用户身份启动 Login Interactive Shell 进程，Shell 则运行相应的 Profile 脚本，如 &lt;code&gt;bash(1)&lt;/code&gt; 加载 &lt;code&gt;~/.bash_profile&lt;/code&gt; 或 &lt;code&gt;~/.profile&lt;/code&gt;，作为最简单的会话管理机制，运行一些后台任务。对于图形界面来说，可以直接 &lt;code&gt;startx&lt;/code&gt;、加载 &lt;code&gt;~/.xinitrc&lt;/code&gt; 、启动 X 服务器和 Window Manager 等进程，或者运行 Wayland 混成器，或者由 Display Manager 负责创建 X 服务器或 Wayland 混成器进程，而图形环境所需要的程序要么通过 &lt;code&gt;~/.xinitrc&lt;/code&gt; 这样的脚本启动，要么在 Window Manager 的配置文件中指定。&lt;/p&gt;
&lt;p&gt;无论是什么方法，都是原始的机制。尽管它们原理十分简单，却存在诸多弊端，比如配置服务启动的方式不统一，Shell 登录、X 环境登录和 Wayland 环境登录需要各种不同的配置文件、需要写脚本以命令式的方式完成；比如这种方式没有充分利用 systemd 的支持，集成差、体验割裂。在 Linux 桌面系统中 systemd 早已普及的今天，这种原始方式显然跟不上时代。&lt;/p&gt;
&lt;h3 id=&#34;基于-systemd-的现代方式&#34;&gt;基于 systemd 的现代方式&lt;/h3&gt;
&lt;p&gt;systemd 早已为 Linux 的各种应用场景设计好了一套服务和会话管理的框架，其关键点在于各种预定义的 &lt;code&gt;target&lt;/code&gt;。&lt;code&gt;target&lt;/code&gt; 是系统的生命周期的各个阶段起始或终止的标志，只要为每个程序写好 &lt;code&gt;service&lt;/code&gt; 文件，并设置好其与 &lt;code&gt;target&lt;/code&gt; 的关系，systemd 即可完全自动地在特定时间节点做好规划的工作，运行各种服务。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为 EndeavourOS 构建 Linux 内核</title>
      <link>https://blog.starryreverie.cc/posts/build-linux-kernel-for-endeavouros/</link>
      <pubDate>Wed, 15 Oct 2025 22:59:02 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/build-linux-kernel-for-endeavouros/</guid>
      <description>&lt;p&gt;简单记录一下在 EndeavourOS 上手动编译 Linux 内核的过程。&lt;/p&gt;
&lt;h2 id=&#34;环境准备&#34;&gt;环境准备&lt;/h2&gt;
&lt;p&gt;整个过程在虚拟机上完成，具体配置参数如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hypervisor: VMware Workstation Pro 17&lt;/li&gt;
&lt;li&gt;OS: EndeavourOS Mercury Neo&lt;/li&gt;
&lt;li&gt;Bootloader: systemd-boot&lt;/li&gt;
&lt;li&gt;Old Kernel: 6.16.8-arch3-1&lt;/li&gt;
&lt;li&gt;New Kernel: 6.17.1&lt;/li&gt;
&lt;li&gt;CPU: 8 x 13th Gen Intel(R) Core(TM) i7-13700HX @ 2.30 GHz&lt;/li&gt;
&lt;li&gt;RAM: 8 GB&lt;/li&gt;
&lt;li&gt;IP：VMnet8 (NAT) 192.168.255.11&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了操作方便，通过 SSH 从宿主机连接到虚拟机，运行以下命令连接，其中 &lt;code&gt;starryreverie&lt;/code&gt; 为已创建用户：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh starryreverie@192.168.255.11
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;获取内核源码&#34;&gt;获取内核源码&lt;/h2&gt;
&lt;p&gt;内核源码可以从 &lt;a href=&#34;https://www.kernel.org/&#34;&gt;kernel.org&lt;/a&gt; 下载。选择 &lt;code&gt;linux-6.17.1.tar.xz&lt;/code&gt; 源码包，并使用以下命令下载：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.17.1.tar.xz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在本地新建目录 &lt;code&gt;build&lt;/code&gt;，并解压源码包：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir build
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tar -xvf linux-6.17.1.tar.xz -C build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;进入源码目录并查看目录：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd build/linux-6.17.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls -alh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;配置编译环境&#34;&gt;配置编译环境&lt;/h2&gt;
&lt;p&gt;在编译内核前，需要安装必要的工具和依赖包。使用以下命令安装：&lt;/p&gt;</description>
    </item>
    <item>
      <title>结合 SPPM 的 Path Tracing</title>
      <link>https://blog.starryreverie.cc/posts/path-tracing-with-sppm/</link>
      <pubDate>Sun, 10 Aug 2025 19:55:17 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/path-tracing-with-sppm/</guid>
      <description>&lt;p&gt;随机渐进式光子映射（Stochastic Progressive Photon Mapping, SPPM）是光子映射系列算法的进阶算法，本文介绍如何实现 SPPM 以及如何将 SPPM 与路径追踪（Path Tracing, PT）结合。&lt;/p&gt;
&lt;h2 id=&#34;光子映射系列算法&#34;&gt;光子映射系列算法&lt;/h2&gt;
&lt;h3 id=&#34;光子映射&#34;&gt;光子映射&lt;/h3&gt;
&lt;p&gt;光子映射（Photon Mapping, PM）是光子映射系列算法中的初代算法，是一个两阶段算法，包括光子追踪和光子映射。其思想就是首先从光源发射光子，光子在场景中不断反弹，模拟现实世界的过程，然后再使用 Path Tracing，过程中在合适的地方使用光子来估计 radiance，加速渲染。&lt;/p&gt;
&lt;h4 id=&#34;光子的发射&#34;&gt;光子的发射&lt;/h4&gt;
&lt;p&gt;光子是光源的通量/功率的载体，光源实质上是通过发射光子来实现照明。PM 中的光子与物理中的光子不同，物理学中的光子是光能传递的最小单元，即量子化的性质，单个光子的能量是 $E = h\nu$，而 PM 中的光子实际上是一堆物理中的光子，是更符合应用的模型。所以，接下来所讨论的光子都是 PM 意义下的光子。&lt;/p&gt;
&lt;p&gt;如何计算单个光子的通量呢？发射光子的过程实际上就类似于对光源的总通量进行 Monte Carlo 估计的过程，每个光子都是一个样本，每一份通量相加就得到了总通量。所以我们就对光源表面和出射方向进行采样，按照类似的过程计算每一个光子的通量。总通量按照以下积分计算&lt;/p&gt;
$$
\begin{aligned}
    \Phi &amp; = \int_M \int_{\Omega^+} L_e(x, \omega) (n \cdot \omega) \mathrm d\omega \mathrm dA \\
    &amp; \approx \dfrac{1}{N} \sum_{i = 1}^N \dfrac{L_e(x, \omega) (n \cdot \omega)}{p(x)p(\omega)}
\end{aligned}
$$&lt;p&gt;所以单个光子的通量就是 $\dfrac{1}{N} \dfrac{L_e(x, \omega) (n \cdot \omega)}{p(x)p(\omega)}$，$N$ 表示被发射光子的总数。严格意义上来说，通量的定义是不考虑立体角和照明面积的，所以所谓的单个光子的通量实际上是通量的二阶差分 $\Delta^2 \Phi$。在实际实现中，我们在计算通量时并不会除以 $N$，这是因为在 SPPM 中光子数量会随着迭代次数增加而增加，因此我们也将其 $\dfrac{L_e(x, \omega) (n \cdot \omega)}{p(x)p(\omega)}$ 视为一种没有放缩的通量，直到最后再除以 $N$ 得到真正的通量。&lt;/p&gt;</description>
    </item>
    <item>
      <title>光线追踪中的 Multiple Importance Sampling</title>
      <link>https://blog.starryreverie.cc/posts/multiple-importance-sampling-in-ray-tracing/</link>
      <pubDate>Thu, 07 Aug 2025 09:34:03 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/multiple-importance-sampling-in-ray-tracing/</guid>
      <description>&lt;p&gt;本文主要介绍多重重要性采样（Multiple Importance Sampling）及其在光线追踪中的典型应用。&lt;/p&gt;
&lt;h2 id=&#34;multiple-importance-sampling&#34;&gt;Multiple Importance Sampling&lt;/h2&gt;
&lt;h3 id=&#34;monte-carlo-path-tracing&#34;&gt;Monte Carlo Path Tracing&lt;/h3&gt;
&lt;p&gt;在 Ray Tracing 中，最核心的技术就是 Monte Carlo Estimation。通过使用 Monte Carlo Estimation，我们可以求解 Rendering Equation。对于 Monte Carlo Estimation 来说，一个合适的采样方法可以极大提升收敛的速度，具体来说，对于以下积分及其 Estimator&lt;/p&gt;
$$
I = \int_D f(x) \mathrm dx \approx \dfrac{1}{N} \sum_{i = 1}^N \dfrac{f(X_i)}{p(X_i)}
$$&lt;p&gt;$X_i$ 的最佳的采样分布应该满足 $p(X_i) \propto f(X_i)$。&lt;/p&gt;
&lt;p&gt;然而对于 Rendering Equation 来说，这个条件难以满足，因为其被积函数包含了 cosine-weighted BSDF 和 radiance 两部分因子，后者更是需要递归求解的未知项，显然没有办法进行完美的采样。&lt;/p&gt;
$$
L_o(x, \omega_o) = L_e(x, \omega_o) + \int_{\Omega^+} f_s(x, \omega_o, \omega_i) L_i(x, \omega_i) (n \cdot \omega_i) \mathrm d\omega_i
$$&lt;p&gt;一般情况下，我们只能选择让采样分布于其中一部分因子的形状近似，也就产生了两种采样——BSDF 采样（包括了余弦项）和光源采样（光源贡献的 radiance 应该比非直接的更大）。&lt;/p&gt;</description>
    </item>
    <item>
      <title>无需宏为 Trait Objects 实现 Any</title>
      <link>https://blog.starryreverie.cc/posts/implement-as-any-for-trait-objects-without-macros/</link>
      <pubDate>Mon, 17 Mar 2025 20:48:11 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/implement-as-any-for-trait-objects-without-macros/</guid>
      <description>&lt;p&gt;&lt;code&gt;std::any::Any&lt;/code&gt; 是 Rust 在运行时进行类型擦除和转换的工具，所有 &lt;code&gt;&#39;static&lt;/code&gt; 类型都实现了 &lt;code&gt;Any&lt;/code&gt;，因此装箱为 &lt;code&gt;Box&amp;lt;dyn Any&amp;gt;&lt;/code&gt; 后可以借助 &lt;code&gt;Any::type_id()&lt;/code&gt; 获取 &lt;code&gt;TypeId&lt;/code&gt;，还可以通过 &lt;code&gt;dyn Any&lt;/code&gt; 的 &lt;code&gt;downcast_*()&lt;/code&gt; 等方法再转换回具体类型。&lt;/p&gt;
&lt;p&gt;然而问题也出在 &lt;code&gt;dyn Any&lt;/code&gt; 的 &lt;code&gt;downcast_*()&lt;/code&gt; 方法。这些方法是 &lt;code&gt;dyn Any&lt;/code&gt; 的方法，而不是 &lt;code&gt;Any&lt;/code&gt; trait 中的方法，所以其他任何 &lt;code&gt;dyn Trait&lt;/code&gt; 都不会拥有这些方法，即使有 &lt;code&gt;Trait: Any&lt;/code&gt;。另一方面，由于 trait upcasting 直到最近才成为稳定特性，且还没进入 stable 版本，所以依赖语言支持的 trait upcasting 来转换为 &lt;code&gt;dyn Any&lt;/code&gt; 对于较早版本的项目并不合适。&lt;/p&gt;
&lt;p&gt;因此，本文则通过纯 Rust 语法来扩展 trait object，以实现 &lt;code&gt;Any&lt;/code&gt; 的所有功能。这些实现都可以在 &lt;a href=&#34;https://github.com/StarryReverie/better-as-any&#34;&gt;&lt;code&gt;better-as-any&lt;/code&gt;&lt;/a&gt; 找到。&lt;/p&gt;
&lt;h2 id=&#34;现有解决方案&#34;&gt;现有解决方案&lt;/h2&gt;
&lt;h3 id=&#34;downcast&#34;&gt;&lt;code&gt;downcast&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;downcast&lt;/code&gt; crate 通过宏来生成代码，直接为指定的 &lt;code&gt;dyn Trait&lt;/code&gt; 添加 &lt;code&gt;is()&lt;/code&gt;、&lt;code&gt;downcast_*()&lt;/code&gt; 方法。这种做法在最终效果上与 &lt;code&gt;dyn Any&lt;/code&gt; 完全一样，但是需要使用 &lt;code&gt;impl_downcast!()&lt;/code&gt; 宏来实现。我个人则偏好能使用语言特性实现就不使用宏。&lt;/p&gt;
&lt;h3 id=&#34;as-any&#34;&gt;&lt;code&gt;as-any&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;这个 crate 不使用宏实现了功能，其中的 &lt;code&gt;AsAny&lt;/code&gt; 可以把任意类型的引用转换为对 &lt;code&gt;dyn Any&lt;/code&gt; 的应用，即 trait upcasting。同时其有 &lt;code&gt;Downcast&lt;/code&gt; trait 可以把引用向下转型为具体类型的引用。&lt;/p&gt;</description>
    </item>
    <item>
      <title>anyerr 上下文中的类型体操</title>
      <link>https://blog.starryreverie.cc/posts/type-exercise-in-anyerr-context/</link>
      <pubDate>Thu, 27 Feb 2025 10:27:45 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/type-exercise-in-anyerr-context/</guid>
      <description>&lt;p&gt;在&lt;a href=&#34;https://blog.starryreverie.cc/posts/thoughts-on-error-handling-in-rust-and-anyerr/&#34;&gt;对 Rust 错误处理的思考和 anyerr&lt;/a&gt;一文中，我介绍了 &lt;a href=&#34;https://github.com/StarryReverie/anyerr&#34;&gt;&lt;code&gt;anyerr&lt;/code&gt;&lt;/a&gt; 这一个错误处理库，其可以携带上下文信息，且储存上下文的数据结构是可定制的。本文则聚焦 &lt;code&gt;anyerr&lt;/code&gt; 是如何实现这样的特性的。&lt;/p&gt;
&lt;h2 id=&#34;上下文的核心特性&#34;&gt;上下文的核心特性&lt;/h2&gt;
&lt;h3 id=&#34;基本结构和表示&#34;&gt;基本结构和表示&lt;/h3&gt;
&lt;p&gt;在设计之前，首先我们要明确需求。什么样的上下文数据结构是我们所需要的？携带上下文是为了能够记录某些变量所保存的值，我们需要记录变量的名称和其中的值。上下文可以有很多种类，但所有的上下文都可以表示为一个键值映射表。&lt;/p&gt;
&lt;p&gt;所以以下是我们对一个上下文储存的基本特性的定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;pub&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;trait&lt;/span&gt; AbstractContext: Default &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Debug &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Send &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Sync &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &amp;#39;static {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Entry&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;Entry&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Self::Key, Value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Self::Value&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Iter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;#39;a&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;Iter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;#39;a&lt;/span&gt;, Entry &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Self::Entry&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Self: &lt;span style=&#34;color:#a6e22e&#34;&gt;&amp;#39;a&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Self&lt;/span&gt;::Iter&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&amp;#39;_&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样的一个 trait 定义仅仅规定了一个上下文的键值对类型和迭代其中元素的方法，却没有插入或者其他查询的方法。这是因为一个上下文不一定需要真的携带有信息，如果不需要上下文，那么一个不带有任何信息的上下文就可以非常好地适用于这种场景，这也是为什么这个 trait 叫做 &lt;code&gt;AbstractContext&lt;/code&gt;。&lt;code&gt;anyerr&lt;/code&gt; 针对这样的情况有特殊的优化，这些后面再说。&lt;/p&gt;
&lt;h3 id=&#34;上下文的元素&#34;&gt;上下文的元素&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;AbstractContext::Entry&lt;/code&gt; 规定了上下文中每一个元素的类型，其应当实现 &lt;code&gt;Entry&lt;/code&gt; trait。以下则是 &lt;code&gt;Entry&lt;/code&gt; 的定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;pub&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;trait&lt;/span&gt; Entry: &lt;span style=&#34;color:#a6e22e&#34;&gt;Debug&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Send &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Sync &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &amp;#39;static {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;Borrow&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Self::KeyBorrowed&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Debug &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Send &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Sync &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &amp;#39;static;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;KeyBorrowed&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;Debug&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Display &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Eq &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Hash &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;Sized &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Send &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Sync &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &amp;#39;static;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;Borrow&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Self::ValueBorrowed&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Debug &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Send &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Sync &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &amp;#39;static;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ValueBorrowed&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;Debug&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;Sized &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Send &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Sync &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &amp;#39;static;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Q, R&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;(key: &lt;span style=&#34;color:#a6e22e&#34;&gt;Q&lt;/span&gt;, value: &lt;span style=&#34;color:#a6e22e&#34;&gt;R&lt;/span&gt;) -&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Self&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Q: Into&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Self::Key&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        R: Into&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Self::Value&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Self&lt;/span&gt;::KeyBorrowed;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;self) -&amp;gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Self&lt;/span&gt;::ValueBorrowed;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Entry&lt;/code&gt; trait 具体规定了键和值的类型以及创建和访问的方法。在 &lt;code&gt;AbstractContext&lt;/code&gt; 中，还要将 &lt;code&gt;AbstractContext::Key&lt;/code&gt;、&lt;code&gt;AbstractContext::Value&lt;/code&gt; 和 &lt;code&gt;Entry::Key&lt;/code&gt;、&lt;code&gt;Entry::Value&lt;/code&gt; 匹配。&lt;/p&gt;</description>
    </item>
    <item>
      <title>对 Rust 错误处理的思考和 anyerr</title>
      <link>https://blog.starryreverie.cc/posts/thoughts-on-error-handling-in-rust-and-anyerr/</link>
      <pubDate>Thu, 20 Feb 2025 11:08:21 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/thoughts-on-error-handling-in-rust-and-anyerr/</guid>
      <description>&lt;p&gt;错误处理是 Rust 中核心的一部分，从标准库中的 &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; 和 &lt;code&gt;Error&lt;/code&gt; 到社区的 &lt;code&gt;anyhow&lt;/code&gt;、&lt;code&gt;thiserror&lt;/code&gt;、&lt;code&gt;color-eyre&lt;/code&gt;、&lt;code&gt;snafu&lt;/code&gt; 等 crates，可见其重要地位。但是在我看来，这些仅仅是错误处理机制的基础，而不是一个十分完备的框架，同时某些 crate 的设计，要么不能符合实际需要，要么使用起来很麻烦。本文将阐述我对 Rust 错误处理的理解和自己的实践 &lt;a href=&#34;https://github.com/StarryReverie/anyerr&#34;&gt;&lt;code&gt;anyerr&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;std-中的基础设施&#34;&gt;&lt;code&gt;std&lt;/code&gt; 中的基础设施&lt;/h2&gt;
&lt;p&gt;在讨论我对错误处理的理解之前，有必要先回顾标准库中与错误处理相关的基础设施。&lt;/p&gt;
&lt;p&gt;截至本文写作时间，Rust 的最新版本为 1.84.1，接下来将以此版本为基础进行讨论。&lt;/p&gt;
&lt;h3 id=&#34;错误与结果&#34;&gt;错误与结果&lt;/h3&gt;
&lt;p&gt;标准库中最广为人知的一个类型就是 &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt;，用来表示一个可能成功或失败的结果。这是一个非常精妙的设计，主要体现其可以编码成功或失败中的一者，而不是将失败结果糅合进成功结果的值中。C 广泛采用后一种处理方式，导致 API 的混乱，当然这很大一部分是历史原因。在 Java 等以异常为主要错误处理机制的语言中，这一问题有了很大改善，但这又引入了隐式控制流的问题，&lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; 则又避开了这个问题。所以，&lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; 应该是一个很不错的机制。&lt;/p&gt;
&lt;p&gt;尽管 &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; 中的 &lt;code&gt;E&lt;/code&gt; 代表错误，但标准库对其具体应是什么类型没有什么限制。一般情况下，&lt;code&gt;E&lt;/code&gt; 是一个实现了 &lt;code&gt;Error&lt;/code&gt; trait 的类型，或者是其他可以间接地访问到内部的错误的类型，如 &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;错误类型的统一契约&#34;&gt;错误类型的统一契约&lt;/h3&gt;
&lt;p&gt;若某类型实现了 &lt;code&gt;Error&lt;/code&gt;，那么其就可以以一种标准的形式来被集成的错误处理的框架中。&lt;code&gt;Error&lt;/code&gt; 规定了如何显示错误信息（通过 &lt;code&gt;Display&lt;/code&gt; trait）和如何溯源错误（通过 &lt;code&gt;&amp;lt;Self as Error&amp;gt;::source()&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;在 Rust 早期，并没有 &lt;code&gt;Error&lt;/code&gt;，错误处理是处于一种野蛮生长的状态。直到 &lt;code&gt;Error&lt;/code&gt; 的引入，Rust 才可以算是有了一套标准的错误处理机制。&lt;/p&gt;
&lt;h3 id=&#34;错误的传播&#34;&gt;错误的传播&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; 虽好，但在深层函数调用中，一次次手动向上传播错误却很麻烦。&lt;code&gt;?&lt;/code&gt; 运算符则有效解决了这个问题，实现了把错误方便的提前返回，传播给调用方。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;?&lt;/code&gt; 的使用不要求被传播错误类型和接受的错误类型完全相同，只需要有 &lt;code&gt;From&lt;/code&gt; 的联系，即如果 &lt;code&gt;E1: From&amp;lt;E2&amp;gt;&lt;/code&gt;，那么对 &lt;code&gt;Result&amp;lt;U, E2&amp;gt;&lt;/code&gt; 使用 &lt;code&gt;?&lt;/code&gt; 就可以把错误传播为 &lt;code&gt;Result&amp;lt;T, E1&amp;gt;&lt;/code&gt;。&lt;/p&gt;</description>
    </item>
    <item>
      <title>统一 Linux GUI 框架主题和外观</title>
      <link>https://blog.starryreverie.cc/posts/unify-look-of-linux-gui-frameworks/</link>
      <pubDate>Wed, 31 Jul 2024 23:21:07 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/unify-look-of-linux-gui-frameworks/</guid>
      <description>&lt;p&gt;在 Linux 下，GUI 外观配置一直是一个复杂的话题。本文试图梳理 Qt 和 GTK 两种 GUI 框架的相关概念，并给出不同情况下的配置方案，实现外观的统一。&lt;/p&gt;
&lt;p&gt;本文讨论的 Qt 包括 Qt 5 和 Qt 6，GTK 包括 GTK 2、GTK 3、GTK 4，并且将以 Qt 6 和 GTK 4 为重点。测试的 DE 和 WM 包括 GNOME 4.46、KDE Plamsa 6.1、Hyprland 0.41。&lt;/p&gt;
&lt;h2 id=&#34;配置组成&#34;&gt;配置组成&lt;/h2&gt;
&lt;h3 id=&#34;基本概念&#34;&gt;基本概念&lt;/h3&gt;
&lt;p&gt;外观配置一般包括以下几个方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主题/Theme：这是一个比较广泛的概念，一般包括了样式、图标和鼠标指针等各配置项在内。&lt;/li&gt;
&lt;li&gt;样式/Style：一般指程序窗口、面板、组件的外观。&lt;/li&gt;
&lt;li&gt;图标/Icon&lt;/li&gt;
&lt;li&gt;指针/Cursor&lt;/li&gt;
&lt;li&gt;字体/Font&lt;/li&gt;
&lt;li&gt;配色方案/Color Scheme：较细粒度的配置项，诸如主要颜色、强调颜色的配置都属于配置方案。&lt;/li&gt;
&lt;li&gt;声音/Sound&lt;/li&gt;
&lt;li&gt;……&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是一个比较广泛的定义，具体到各框架，又会产生一定的变化。&lt;/p&gt;
&lt;h3 id=&#34;gtk&#34;&gt;GTK&lt;/h3&gt;
&lt;p&gt;GTK 中可直接配置的部分相对较少，主要是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主题/Theme：主要与一般定义中的样式/Style 对应。&lt;/li&gt;
&lt;li&gt;图标/Icon Theme：与一般定义中的图标/Icon 相同。&lt;/li&gt;
&lt;li&gt;指针/Cursor Theme：与一般定义中的指针/Cursor 相同。&lt;/li&gt;
&lt;li&gt;字体/Font：与一般定义中的字体/Font 相同。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;包括 GNOME 在内的基于 GTK 开发的 DE 基本上直接使用上述概念，利用这些 DE 的工具配置外观，基本上就是对 GTK 的配置直接修改。&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 NixOS 部署 Minecraft 服务器</title>
      <link>https://blog.starryreverie.cc/posts/deploy-a-minecraft-server-with-nixos/</link>
      <pubDate>Fri, 05 Jul 2024 15:42:09 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/deploy-a-minecraft-server-with-nixos/</guid>
      <description>&lt;p&gt;紧张的考试周过后，终于有时间和朋友玩 Minecraft 了。为了方便进行多人游戏，我和 &lt;a href=&#34;https://github.com/whitepaperdog&#34;&gt;@whitepaperdog&lt;/a&gt; 决定购买一台云主机作为服务器。鉴于 NixOS 强大的 reproducibility 以及我这半年使用 NixOS 的优秀体验，我决定将服务器的系统更换为 NixOS，并在其之上部署 Minecraft 服务。&lt;/p&gt;
&lt;h2 id=&#34;准备工作&#34;&gt;准备工作&lt;/h2&gt;
&lt;h3 id=&#34;nixos-安装&#34;&gt;NixOS 安装&lt;/h3&gt;
&lt;p&gt;这台服务器并不是用我的账号买的，所以我没有办法直接在控制台上传 NixOS 的镜像进行安装。等我拿到 root 密码后，系统就已经是 Ubuntu 了。在此情况下，我选择了使用 &lt;a href=&#34;https://github.com/elitak/nixos-infect&#34;&gt;NixOS-infect&lt;/a&gt;。NixOS-infect 是一个 shell 脚本，其在服务器上安装 Nix，再用 Nix 构建出 NixOS，最后修改 bootloader 配置，添加 NixOS 的启动项并删除其他东西。&lt;/p&gt;
&lt;p&gt;使用 NixOS-infect 前需要配置好访问 root 的 SSH 公钥，这是因为 NixOS-infect 并没有提供设置 root 密码的步骤，并且 NixOS 默认情况下将禁用 SSH 通过密码登录 root。NixOS-infect 脚本会将原有的 root 的公钥重新导入到新的 NixOS 里。&lt;/p&gt;
&lt;p&gt;由于服务器在国内，所以访问 nixpkgs 的 cache 将会非常慢，在安装 NixOS 之前需要配置好 cache 镜像。编辑脚本，在完成 Nix 的安装后，配置镜像源：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;infect&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  NIX_INSTALL_URL&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;NIX_INSTALL_URL&lt;span style=&#34;color:#66d9ef&#34;&gt;:-&lt;/span&gt;https://nixos.org/nix/install&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  curl -L &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;NIX_INSTALL_URL&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | sh -s -- --no-channel-add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# 添加以下 3 行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; EOF &amp;gt; /etc/nix/nix.conf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;substituters = https://mirror.sjtu.edu.cn/nix-channels/store https://mirrors.ustc.edu.cn/nix-channels/store https://cache.nixos.org/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# shellcheck disable=SC1090&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  source ~/.nix-profile/etc/profile.d/nix.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;随后就是运行脚本，等待 SSH 断开，安装完成。&lt;/p&gt;</description>
    </item>
    <item>
      <title>体验 Helix 编辑器</title>
      <link>https://blog.starryreverie.cc/posts/experiencing-helix-editor/</link>
      <pubDate>Sat, 08 Apr 2023 21:49:01 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/experiencing-helix-editor/</guid>
      <description>&lt;p&gt;最近发现了一个用 Rust 写的 Vim-like 编辑器 Helix，用有强大的性能和各种开箱即用的功能。经过短暂时间的体验，我认为 Helix 已经可以在大部分领域替代 Vim/Neovim/VS Code。&lt;/p&gt;
&lt;h2 id=&#34;性能&#34;&gt;性能&lt;/h2&gt;
&lt;p&gt;作为一款用 Rust 写的编辑器，Helix 自然拥有优异的性能。得益于其丰富的内置功能，Helix 无需插件就可以完成许多 Vim 只能用插件做到的事，并且还通过 Rust 获得了性能上的优势。现在我的 Neovim 安装有 &lt;code&gt;coc.nvim&lt;/code&gt;、&lt;code&gt;vim-visual-multi&lt;/code&gt; 等插件，共十个，启动需要延迟近 0.5 秒，虽然已经显著快于 VS Code，但相比 Helix 的小于 0.1 秒，还是稍显逊色。（当然这可能与我现在用的是 Windows 有关）&lt;/p&gt;
&lt;p&gt;虽然现在（2023年4月8日）Helix 还没有插件系统，但未来的插件系统大概率会用 WASM 实现，相比 Vim Script 以及 Neovim 用的 Lua 等脚本语言会更快。&lt;/p&gt;
&lt;h2 id=&#34;编辑体验&#34;&gt;编辑体验&lt;/h2&gt;
&lt;h3 id=&#34;整体思路&#34;&gt;整体思路&lt;/h3&gt;
&lt;p&gt;首先 Helix 和 Vim 一样都是模态编辑器，具有多种模式，最基本的操作思路是一样的，比如都使用 &lt;code&gt;hjkl&lt;/code&gt; 进行移动，都使用 &lt;code&gt;i&lt;/code&gt;、&lt;code&gt;a&lt;/code&gt; 等进行插入，都使用 &lt;code&gt;y&lt;/code&gt;，&lt;code&gt;d&lt;/code&gt;、&lt;code&gt;p&lt;/code&gt; 进行复制删除粘贴。但是 Helix 采用了 &lt;code&gt;selection -&amp;gt; action&lt;/code&gt; 模式，比如向右删除 3 个字符需要按 &lt;code&gt;3ld&lt;/code&gt; 而不是 &lt;code&gt;d3l&lt;/code&gt;，先选择在操作，就我个人而言，这种方式确实更舒适。&lt;/p&gt;
&lt;p&gt;另外，Helix 的命令有丰富的提示，而且还可以通过 &lt;code&gt;&amp;lt;space&amp;gt; ?&lt;/code&gt; 打开命令面板查找命令，并带有相关键位提示，包括自定义的键位。&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 Rust 开发 Brainfuck 解释器</title>
      <link>https://blog.starryreverie.cc/posts/develop-a-brainfuck-interpreter-with-rust/</link>
      <pubDate>Thu, 26 Jan 2023 22:26:51 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/develop-a-brainfuck-interpreter-with-rust/</guid>
      <description>&lt;p&gt;项目地址：&lt;a href=&#34;https://github.com/StarryReverie/brainfuck-interpreter&#34;&gt;https://github.com/StarryReverie/brainfuck-interpreter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Brainfuck 是什么就不具体介绍了，可以看&lt;a href=&#34;https://esolangs.org/wiki/brainfuck&#34;&gt;这里&lt;/a&gt;。以下简称 bf。&lt;/p&gt;
&lt;p&gt;这个解释器实现总体上是比较简单的，但是相比其他的大多数解释器还是有比较多的不同之处，具体如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;更多的配置选项
&lt;ul&gt;
&lt;li&gt;内存的长度与地址范围：可配置为负数&lt;/li&gt;
&lt;li&gt;单个内存单元的数据类型&lt;/li&gt;
&lt;li&gt;数据溢出处理机制：wrap 或错误&lt;/li&gt;
&lt;li&gt;读到 &lt;code&gt;EOF&lt;/code&gt; 的处理机制：返回 &lt;code&gt;0&lt;/code&gt;、&lt;code&gt;EOF&lt;/code&gt; 本身或不改变&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优化指令&lt;/li&gt;
&lt;li&gt;采用类似编译为字节码的机制&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个项目可以算是学习 Rust 的练手项目，尝试着用了如 clap 这样的 crate。接下来就介绍一些技术细节。&lt;/p&gt;
&lt;h2 id=&#34;使用方法&#34;&gt;使用方法&lt;/h2&gt;
&lt;p&gt;具体说明在项目 &lt;a href=&#34;https://github.com/StarryReverie/brainfuck-interpreter#readme&#34;&gt;README&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;编译、安装、执行全过程：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ git clone https://github.com/StarryReverie/brainfuck-interpreter.git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cd brainfuck-interpreter
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cargo install --path ./crates/bf-exec &lt;span style=&#34;color:#75715e&#34;&gt;# The program will be installed to ~/.cargo/bin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bf-exec ./examples/helloworld.bf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;./examples/helloworld.bf&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-brainfuck&#34; data-lang=&#34;brainfuck&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;++++++++++&lt;span style=&#34;color:#66d9ef&#34;&gt;[&lt;/span&gt;&amp;gt;+++++++&amp;gt;++++++++++&amp;gt;+++&amp;gt;+&amp;lt;&amp;lt;&amp;lt;&amp;lt;-&lt;span style=&#34;color:#66d9ef&#34;&gt;]&lt;/span&gt;&amp;gt;++&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&amp;gt;+&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;+++++++&lt;span style=&#34;color:#f92672&#34;&gt;..&lt;/span&gt;+++&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&amp;gt;++&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&amp;lt;&amp;lt;+++++++++++++++&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;+++&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;------&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;--------&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&amp;gt;+&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;实现原理&#34;&gt;实现原理&lt;/h2&gt;
&lt;p&gt;目前，项目分为两个 crate：&lt;code&gt;common&lt;/code&gt; 与 &lt;code&gt;bf-exec&lt;/code&gt;，其中 &lt;code&gt;common&lt;/code&gt; 实现了解释器的所有逻辑，而 &lt;code&gt;bf-exec&lt;/code&gt; 是 &lt;code&gt;common&lt;/code&gt; 的前端，负责处理输入和配置。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;common&lt;/code&gt; 的模块树如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;compiler&lt;/code&gt;：解析代码并转换为 &lt;code&gt;IR (Intermediate Representation，中间表示)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lexer&lt;/code&gt;：词法分析&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parser&lt;/code&gt;：语法分析并优化，生成 AST &lt;code&gt;SyntaxTree&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;syntax&lt;/code&gt;：AST 生成&lt;/li&gt;
&lt;li&gt;&lt;code&gt;optimizer&lt;/code&gt;：AST 优化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;instruction&lt;/code&gt;：根据 AST 生成 &lt;code&gt;IR&lt;/code&gt;，同时也是最终执行的指令&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;execution&lt;/code&gt;：&lt;code&gt;IR&lt;/code&gt; 的执行与相关环境
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;memory&lt;/code&gt;：按照 bf 的内存模型实现的可配置内存
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;strategy&lt;/code&gt;：基于策略模式实现的可配置组件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config&lt;/code&gt;：构建 &lt;code&gt;Memory&lt;/code&gt; 的配置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stream&lt;/code&gt;：bf 的 IO 实现
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;config&lt;/code&gt;：构建 &lt;code&gt;InStream&lt;/code&gt;、&lt;code&gt;OutStream&lt;/code&gt; 的配置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;context&lt;/code&gt;：&lt;code&gt;Memory&lt;/code&gt; 与 &lt;code&gt;InStream&lt;/code&gt;、&lt;code&gt;OutStream&lt;/code&gt; 的组合&lt;/li&gt;
&lt;li&gt;&lt;code&gt;processor&lt;/code&gt;：运行指令，并调用 &lt;code&gt;Context&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;实现细节&#34;&gt;实现细节&lt;/h2&gt;
&lt;h3 id=&#34;代码优化&#34;&gt;代码优化&lt;/h3&gt;
&lt;h4 id=&#34;同质代码合并&#34;&gt;同质代码合并&lt;/h4&gt;
&lt;p&gt;首先最简单的就是这个优化，它是指把相邻的 &lt;code&gt;+&lt;/code&gt; 与 &lt;code&gt;-&lt;/code&gt;、&lt;code&gt;&amp;lt;&lt;/code&gt; 与 &lt;code&gt;&amp;gt;&lt;/code&gt; 合并到一起并加上一个重复次数。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Python 网络爬虫获取 SYZOJ AC 代码</title>
      <link>https://blog.starryreverie.cc/posts/fetching-accpeted-codes-in-syzoj-using-python-crawler/</link>
      <pubDate>Mon, 18 Apr 2022 15:23:12 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/fetching-accpeted-codes-in-syzoj-using-python-crawler/</guid>
      <description>&lt;p&gt;写本文之前，我共有两百多道题目在校内 &lt;code&gt;OJ&lt;/code&gt; 上通过，由于某些原因，想要保存这些代码，于是想到使用 &lt;code&gt;Python&lt;/code&gt; 实现自动爬取代码。同时考虑到效率问题，决定使用 &lt;code&gt;aiohttp&lt;/code&gt; 编写一个高性能异步爬虫。&lt;/p&gt;
&lt;h2 id=&#34;实现目标分析&#34;&gt;实现目标分析&lt;/h2&gt;
&lt;p&gt;这个爬虫需要能够爬取所有的已通过题目的列表，并继续爬取这些已通过题目的代码，随后保存到文件中。&lt;/p&gt;
&lt;p&gt;校内 &lt;code&gt;OJ&lt;/code&gt; 基于 &lt;code&gt;SYZOJ&lt;/code&gt; 搭建，该 &lt;code&gt;OJ&lt;/code&gt; 的项目地址为 &lt;a href=&#34;https://github.com/syzoj/syzoj&#34;&gt;https://github.com/syzoj/syzoj&lt;/a&gt;，故接下来的代码都是基于其实现的。&lt;/p&gt;
&lt;p&gt;由于这个 &lt;code&gt;OJ&lt;/code&gt; 的外网域名的带宽很小，一个网页最坏情况下需要花费 &lt;code&gt;2～3&lt;/code&gt; 秒的时间，所以必须采用异步实现。这里就使用 &lt;code&gt;aiohttp&lt;/code&gt; 了。&lt;/p&gt;
&lt;h2 id=&#34;获取-cookie&#34;&gt;获取 Cookie&lt;/h2&gt;
&lt;p&gt;由于直接从浏览器里获取的 &lt;code&gt;Cookie&lt;/code&gt; 无法正常使用，传给服务器无法识别，猜测是编码问题，所以使用在爬虫运行时即时获取 &lt;code&gt;Cookie&lt;/code&gt; 的办法。&lt;/p&gt;
&lt;p&gt;分析 &lt;code&gt;SYZOJ&lt;/code&gt; 的登陆&lt;a href=&#34;https://github.com/syzoj/syzoj/blob/master/views/login.ejs&#34;&gt;页面源码&lt;/a&gt;，找到如下代码片段：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;login&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;md5&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;$&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#password&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;syzoj2_xxx&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;$&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#login&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;addClass&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;loading&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ajax&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/api/login&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;$&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#username&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;error_code&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;error_code&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error_code&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1001&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;show_error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;用户不存在&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1002&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;show_error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;密码错误&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1003&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;show_error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;您尚未设置密码，请通过下方「找回密码」来设置您的密码。&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;success&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;session_id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;show_error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;未知错误&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;$&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#login&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;登录&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;$&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#login&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;removeClass&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;loading&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;XMLHttpRequest&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;textStatus&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;errorThrown&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;alert&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;XMLHttpRequest&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;responseText&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;show_error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;未知错误&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;$&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#login&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;登录&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以发现，&lt;code&gt;SYZOJ&lt;/code&gt; 通过 &lt;code&gt;/api/login&lt;/code&gt; 这个 &lt;code&gt;Web API&lt;/code&gt; 发送请求获取 &lt;code&gt;Cookie&lt;/code&gt;，使用 &lt;code&gt;POST&lt;/code&gt; 方法，数据为 &lt;code&gt;username&lt;/code&gt; 和 &lt;code&gt;password&lt;/code&gt;，分别为用户名和密码加上 &lt;code&gt;syzoj2_xxx&lt;/code&gt; 这个 &lt;code&gt;salt&lt;/code&gt; 的 &lt;code&gt;MD5&lt;/code&gt;，所以可以使用以下 &lt;code&gt;Python&lt;/code&gt; 代码获取 &lt;code&gt;Cookie&lt;/code&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>MultiGenerator 使用文档</title>
      <link>https://blog.starryreverie.cc/posts/multigenerator-documentation/</link>
      <pubDate>Mon, 04 Apr 2022 22:15:15 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/multigenerator-documentation/</guid>
      <description>&lt;h2 id=&#34;概述&#34;&gt;概述&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;MultiGenerator&lt;/code&gt; 作为早年我的试验项目，已不再适合于实际使用。如果想要生成数据，请使用性能更高且更简单的 Rust 实现：&lt;a href=&#34;https://github.com/StarryReverie/data-gen-rs&#34;&gt;&lt;code&gt;data-gen-rs&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;MultiGenerator&lt;/code&gt; 是一个为 &lt;code&gt;OI&lt;/code&gt; 而生的多线程并行数据生成库，基于 &lt;code&gt;C++ 17&lt;/code&gt;，使用面向对象和泛型等 &lt;code&gt;Morden C++&lt;/code&gt; 高级特性，只需要添加最少的额外代码，就可以获得最高的性能。以下是一个能够指定数据范围的 &lt;code&gt;A + B Problem&lt;/code&gt; 数据生成器的示例代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;random&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;MultiGenerator.hpp&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; MultiGenerator&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;DataConfig;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; MultiGenerator&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;GeneratingTask;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; MultiGenerator&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;SolutionTask;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; MultiGenerator&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;NormalTemplate;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; MultiGenerator&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;entry;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; MultiGenerator&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;testcase;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/** 指定数据生成器，仅需继承一个抽象类和实现一个成员函数 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AddGenerator&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; GeneratingTask {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; generate(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;ostream &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;data, &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; DataConfig &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;config) &lt;span style=&#34;color:#66d9ef&#34;&gt;override&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;/** DataConfig 为配置信息，可以用于储存数据范围等元信息 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; minValue &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;stoi(config.get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;minValue&amp;#34;&lt;/span&gt;).value());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; maxValue &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;stoi(config.get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;maxValue&amp;#34;&lt;/span&gt;).value());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;random_device rd;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;mt19937 gen(rd());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;uniform_int_distribution&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; dist(minValue, maxValue);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;/** 像 cout 一样输出生成结果 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        data &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; dist(gen) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; dist(gen) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/** 指定数据求解器，也仅需继承一个抽象类和实现一个成员函数 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AddSolution&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; SolutionTask {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/** 假如你有标程，仅需要把程序用这个类包装起来，再把 main() 改为这个成员函数即可 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; solve(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;istream &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;dataIn, std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;ostream &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;dataOut, &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; DataConfig &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;override&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, b;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;/** 像 cin 一样读入数据 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        dataIn &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; b;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;/** 像 cout 一样输出答案 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        dataOut &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;constexpr&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; MAX_THREAD_COUNT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;constexpr&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; MAX_TESTCASE_COUNT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;constexpr&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; PROBLEM_NAME[] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;add&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/** 创建一个题目生成模板，指定数据文件名为 add#.in/add#.out，# 是测试点编号，可以含子任务编号 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    MultiGenerator&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;NormalTemplate temp(PROBLEM_NAME);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; MAX_TESTCASE_COUNT; &lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;/** 添加测试点配置，并指定生成器和求解器 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        temp.add&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;AddGenerator, AddSolution&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;(testcase(i, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            entry(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;minValue&amp;#34;&lt;/span&gt;, i &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000000&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            entry(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;maxValue&amp;#34;&lt;/span&gt;, (i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/** 开始根据指定的线程数生成数据 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    temp.execute(MAX_THREAD_COUNT);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;要求&#34;&gt;要求&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C++ 17 Compiler&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C++&lt;/code&gt; 基础知识，包括最基本的模板的使用（基本都可以满足）&lt;/li&gt;
&lt;li&gt;能够认真阅读文档&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;h3 id=&#34;编译器支持&#34;&gt;编译器支持&lt;/h3&gt;
&lt;p&gt;首先确保你有支持 &lt;code&gt;C++ 17&lt;/code&gt; 的编译器，如果你已经有了，可以跳过这一步。&lt;/p&gt;</description>
    </item>
    <item>
      <title>二分图匹配学习笔记</title>
      <link>https://blog.starryreverie.cc/posts/bipartite-graph-matching-note/</link>
      <pubDate>Tue, 22 Feb 2022 13:31:06 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/bipartite-graph-matching-note/</guid>
      <description>&lt;h2 id=&#34;二分图&#34;&gt;二分图&lt;/h2&gt;
&lt;h3 id=&#34;定义&#34;&gt;定义&lt;/h3&gt;
&lt;p&gt;如果一个图 $G=(V,E)$ 中的结点可以被分为两个部分，且两个部分之内的点互相没有连边，则称这种图为二分图。如果每个结点的度数相等且都为 $k$，则称这种二分图为 $k$ - 正则二分图。&lt;/p&gt;
&lt;h3 id=&#34;判断&#34;&gt;判断&lt;/h3&gt;
&lt;p&gt;对图的结点进行黑白染色，相连的结点染不同的颜色，若最后满足每条边的两个端点颜色不相同，则这个图是二分图。树也是二分图。&lt;/p&gt;
&lt;h3 id=&#34;染色&#34;&gt;染色&lt;/h3&gt;
&lt;p&gt;这里的染色不同于判断的染色，它指的是对边进行染色。若染 $k$ 种颜色，则称其为二分图的 $k$ 染色。&lt;/p&gt;
&lt;p&gt;对于一个 $k$ - 正则二分图来说，它一定可以被 $k$ 染色，而对于一般的二分图，若其最大度数为 $k$，则它也可以被 $k$ 染色。&lt;/p&gt;
&lt;h3 id=&#34;匹配&#34;&gt;匹配&lt;/h3&gt;
&lt;p&gt;在二分图的边集中选出一个非空子集，且这个非空子集内没有两条边连接了一个相同的端点，则这个非空子集被称为二分图的一个匹配，同样对于一般图也有类似的概念。&lt;/p&gt;
&lt;p&gt;若一个匹配的所含的边数最大，则称这个匹配为最大匹配。&lt;/p&gt;
&lt;p&gt;若二分图两边的结点个数相同，且存在一个匹配满足其中的边连接了所有的点，则这个匹配被称为这个二分图的完美匹配或者完备匹配。&lt;/p&gt;
&lt;p&gt;若每条边有边权，且一个匹配的所含的边的权值和最大，则这个匹配被称为这个二分图的最大权匹配。&lt;/p&gt;
&lt;h2 id=&#34;匈牙利算法&#34;&gt;匈牙利算法&lt;/h2&gt;
&lt;h3 id=&#34;交替路&#34;&gt;交替路&lt;/h3&gt;
&lt;p&gt;假设在二分图最大匹配的过程中，已经找到了一些边作为一个匹配，则一条交替路就是一个匹配边和非匹配边交替连接组成的简单路径。&lt;/p&gt;
&lt;p&gt;根据二分图的定义，假设一条交替路的起点在左边，且第一条边是匹配边，则这条交替路中的匹配边一定都是从左边到右边，而非匹配边一定是从右边到左边。&lt;/p&gt;
&lt;p&gt;交替路上的结点最多连接一条匹配边和一条非匹配边，所以如果把交替路上的匹配边变为非匹配边，非匹配边变为匹配边，则仍然满足条件，也就是交替路边集的匹配边集的补集也可以是匹配。&lt;/p&gt;
&lt;h3 id=&#34;增广路&#34;&gt;增广路&lt;/h3&gt;
&lt;p&gt;匈牙利算法的核心就是找增广路，这条增广路是一条交替路。每次增广从左边开始，第一条边是非匹配边，最后一条边也是非匹配边，根据上文的描述，这样的路径的边数为奇数，非匹配边个数比匹配边个数多 $1$，若把匹配边与非匹配边反转，则反转后的边集仍然是一个合法的匹配，且比原来的匹配的边数多了 $1$。如果能找到一条这样的增广路，则此次增广成功。&lt;/p&gt;
&lt;p&gt;具体来说，比如一个左边的结点 $x$ 找到了一个右边的结点 $y$，它没有与其他左边的结点匹配过，则 $e(x,y)$ 可以作为一个匹配边，反转边集前，有 $1$ 条匹配边，$0$ 条非匹配边，增广成功。&lt;/p&gt;
&lt;p&gt;如果右边的结点 $y$ 已经有匹配了，那么就可以让原来 $y$ 的匹配 $x&#39;$ 新找一个点 $y&#39;$，如果能够找到这个 $y&#39;$，那么 $e(x&#39;,y&#39;)$ 成为一个匹配边，$y$ 就无需和 $x&#39;$ 匹配了，也就是说 $y$ 已经变为了一个没有匹配的点，$x$ 就可以与 $y$ 匹配了。如果 $x&#39;$ 找不到 $y&#39;$，那么 $x&#39;$ 就要保持原样，和 $y$ 匹配，那么 $x$ 就不可以和 $y$ 匹配，只能继续寻找下一个可能的结点。&lt;/p&gt;</description>
    </item>
    <item>
      <title>莫比乌斯反演学习笔记</title>
      <link>https://blog.starryreverie.cc/posts/mobius-inversion-note/</link>
      <pubDate>Sun, 20 Feb 2022 11:23:07 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/mobius-inversion-note/</guid>
      <description>&lt;p&gt;莫比乌斯反演可以用于优化一类式子的计算。&lt;/p&gt;
&lt;h2 id=&#34;莫比乌斯函数&#34;&gt;莫比乌斯函数&lt;/h2&gt;
&lt;h3 id=&#34;定义&#34;&gt;定义&lt;/h3&gt;
&lt;p&gt;莫比乌斯函数的定义如下：&lt;/p&gt;
$$
\mu(n)=
\begin{cases}
0 &amp; \exists\ p^2 \mid n \wedge p&gt;1\\\\
(-1)^k &amp; n=\prod_{i=1}^{k} p_i
\end{cases}
$$&lt;p&gt;也就是说，如果 $n$ 含有平方约数，则 $\mu(n)=0$，否则 $\mu(n)=(-1)^k$，其中 $k$ 为 $n$ 中的本质不同的质约数个数。&lt;/p&gt;
&lt;h3 id=&#34;性质&#34;&gt;性质&lt;/h3&gt;
&lt;h4 id=&#34;积性函数&#34;&gt;积性函数&lt;/h4&gt;
&lt;p&gt;$\mu(n)$ 为积性函数，证明如下：&lt;/p&gt;
&lt;p&gt;设 $a,b$ 满足 $\gcd(a,b)=1$。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;若 $a,b$ 其中一个数含有平方约数，则它们的乘积也一定含有平方约数，此时它们的莫比乌斯函数值都为 $0$，即 $\mu(ab)=\mu(a)\mu(b)=0$。&lt;/li&gt;
&lt;li&gt;否则 $a,b$ 都不含有平方约数，则设它们的质约数个数分别为 $k_a,k_b$，则 $\mu(a)=(-1)^{k_a},\mu(b)=(-1)^{k_b}$，因为满足 $\gcd(a,b)=1$，故 $ab$ 的质约数个数为 $k_a+k_b$，$\mu(ab)=(-1)^{k_a+k_b}=(-1)^{k_a}(-1)^{k_b}=\mu(a)\mu(b)$。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;Q.E.D.&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&#34;与常数函数的卷积&#34;&gt;与常数函数的卷积&lt;/h4&gt;
&lt;p&gt;这个性质十分重要，是反演的基础。这个性质可以写成 $I * \mu= \epsilon$，或者：&lt;/p&gt;
$$
\sum_{d\mid n}I(\frac{n}{d})\mu(d)=\epsilon(n)
$$&lt;p&gt;经常简记为：&lt;/p&gt;
$$
\sum_{d\mid n} \mu(d) = [n=1]
$$&lt;p&gt;$\epsilon$ 是 &lt;code&gt;Dirchlet&lt;/code&gt; 卷积的单位元函数。&lt;/p&gt;</description>
    </item>
    <item>
      <title>同余方程学习笔记</title>
      <link>https://blog.starryreverie.cc/posts/congruence-equation-note/</link>
      <pubDate>Sun, 13 Feb 2022 21:40:02 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/congruence-equation-note/</guid>
      <description>&lt;p&gt;解各种同余方程是同余问题的一个重要部分，本文介绍各种同余方程的求解方法。&lt;/p&gt;
&lt;h2 id=&#34;二元线性不定方程&#34;&gt;二元线性不定方程&lt;/h2&gt;
&lt;h3 id=&#34;形式&#34;&gt;形式&lt;/h3&gt;
&lt;p&gt;不定方程的范围很广泛，只要没有确定的解的方程都可以叫不定方程。本文主要讨论数论中的不定方程。&lt;/p&gt;
$$
ax+by=c\ (a,b,c,x,y\in \mathrm{Z})
$$&lt;p&gt;形如上面这种形式，满足各项的系数和解均为整数的方程就是数论中的不定方程。&lt;/p&gt;
&lt;h3 id=&#34;求解&#34;&gt;求解&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Theorem 1&lt;/strong&gt;：$\forall a, b \in \mathrm{Z}$，一定存在 $x,y\in \mathrm{Z}$，使得 $ax+by=\gcd(a,b)$。&lt;/p&gt;
&lt;p&gt;这个定理又叫做裴蜀定理，根据这个定理，可以得出不定方程 $ax+by=c$ 有解的充要条件就是 $\gcd(a,b)\mid c$。记 $d=\gcd(a,b)$，则我们可以先求出 $ax&#39;+by&#39;=d$ 的解，再令 $x=\frac{c}{d}x&#39;,y=\frac{c}{d}y&#39;$，就得出原方程的一组解。&lt;/p&gt;
&lt;p&gt;求 $x&#39;,y&#39;$ 的过程可以用扩展欧几里得算法，即 &lt;code&gt;exgcd&lt;/code&gt; 解决。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theorem 2&lt;/strong&gt;：若 $\gcd(a,b)=1$，且 $x_0,y_0$ 是方程 $ax+by=c$ 的一组解，则该方程的通解为 $x=x_0+kb,y=y_0-ka$，其中 $k\in \mathrm{Z}$。&lt;/p&gt;
&lt;p&gt;换句话说，$x,y$ 这两个解是具有周期性的。根据这个定理，我们知道一定有一个 $x\in [0,b)$，而对于方程 $ax+by=c$，则一定有一个 $x\in [0,\frac{b}{\gcd(a,b)})$，由此可以求一个不定方程的一个元的最小非负整数解，具体地，假设知道了一个解 $x_0$，则最小非负整数 $x=(x_0 \bmod m + m) \bmod m$，其中 $m=\frac{b}{\gcd(a,b)}$。&lt;/p&gt;
&lt;p&gt;总结一下，求解二元线性不定方程 $ax+by=c$ 的流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;求出 $ax+by=\gcd(a,b)$ 的一组解 $x&#39;,y&#39;$，并根据 $\gcd(a,b)\mid c$ 判断方程有无解&lt;/li&gt;
&lt;li&gt;令 $x=\frac{c}{\gcd(a,b)}x&#39;,y=\frac{c}{\gcd(a,b)}y&#39;$，$x,y$ 为 $ax+by=c$ 的一组解&lt;/li&gt;
&lt;li&gt;若需要求 $x$ 的最小非负整数解，则将 $x=(x_0 \bmod m + m) \bmod m$ 作为最终的答案，其中 $m=\frac{b}{\gcd(a,b)}$&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;线性同余方程&#34;&gt;线性同余方程&lt;/h2&gt;
&lt;h3 id=&#34;形式-1&#34;&gt;形式&lt;/h3&gt;
$$
ax \equiv c \pmod{p}
$$&lt;p&gt;形如这样的方程，就被称线性同余方程。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Burnside 引理 &amp; Pólya 定理学习笔记</title>
      <link>https://blog.starryreverie.cc/posts/burnside-lemma-and-polya-theorem-note/</link>
      <pubDate>Sat, 12 Feb 2022 19:14:24 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/burnside-lemma-and-polya-theorem-note/</guid>
      <description>&lt;p&gt;Burnside 引理和 Pólya 定理主要用于解决计算本质不同方案数的计数问题。&lt;/p&gt;
&lt;h2 id=&#34;群论&#34;&gt;群论&lt;/h2&gt;
&lt;h3 id=&#34;基本定义&#34;&gt;基本定义&lt;/h3&gt;
&lt;p&gt;群可以看成是一个由集合和某个二元运算组成的二元组 $(S,\cdot)$，这里的 $\cdot$ 就代表一个二元运算，这个二元运算需要满足结合律、存在单位元和逆元。群有很多的种类，比如置换群、循环群、矩阵群等，其中置换群就是我们接下来要介绍的。&lt;/p&gt;
&lt;h3 id=&#34;性质&#34;&gt;性质&lt;/h3&gt;
&lt;p&gt;若一个二元组 $(S,\cdot)$ 是群，则它满足以下性质：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;封闭性：$\forall x,y\in S$，若 $z=x\cdot y$，则 $z\in S$&lt;/li&gt;
&lt;li&gt;结合律：$\forall x,y,z\in S$，$(x\cdot y)\cdot z=x\cdot (y \cdot z)$&lt;/li&gt;
&lt;li&gt;单位元：存在唯一一个元素 $e \in S$ 满足 $\forall x \in S$，$e\cdot x=x$&lt;/li&gt;
&lt;li&gt;逆元：$\forall x \in S$，都可以找到唯一一个元素 $x^{-1}$ 满足 $x \cdot x^{-1} = e$，这个元素称为 $x$ 的逆元&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;举一个简单的例子，$(\mathrm{Z},+)$ 就是一个群，无论取哪两个元素进行加法运算，结果仍然是整数；加法满足结合律；单位元 $e = 0$；任意一个元素 $x$ 的逆元 $x^{-1} = -x$。&lt;/p&gt;
&lt;h2 id=&#34;置换与置换群&#34;&gt;置换与置换群&lt;/h2&gt;
&lt;h3 id=&#34;置换的基本定义&#34;&gt;置换的基本定义&lt;/h3&gt;
&lt;p&gt;顾名思义，置换群就是置换组成的群。置换可以看成一个有限集合到自身的双射，具体一点，假设有两个长度相同的集合 $p,q$（这里假设集合可以有序），则置换 $f$ 作用于 $p$ 就是生成一个新的集合 $p&#39;$，满足 $p&#39;_i=p\_{q_i}$。&lt;/p&gt;</description>
    </item>
    <item>
      <title>欧拉函数 &amp; 欧拉定理学习笔记</title>
      <link>https://blog.starryreverie.cc/posts/euler-function-and-euler-theorem-note/</link>
      <pubDate>Thu, 10 Feb 2022 08:37:50 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/euler-function-and-euler-theorem-note/</guid>
      <description>&lt;p&gt;欧拉函数 $\varphi(n)$ 是一个重要的数论函数，它表示 $[1, n]$ 中与 $n$ 互质的数的个数。&lt;/p&gt;
&lt;h2 id=&#34;欧拉函数性质&#34;&gt;欧拉函数性质&lt;/h2&gt;
&lt;h3 id=&#34;积性函数&#34;&gt;积性函数&lt;/h3&gt;
&lt;p&gt;积性函数的定义是：如果一个函数 $f(n)$ 满足 $\gcd(a,b)=1$ 时 $f(ab)=f(a)f(b)$，则 $f(n)$ 为积性函数。
若无需 $\gcd(a,b)=1$ 就有 $f(ab)=f(a)f(b)$，则它为完全积性函数。&lt;/p&gt;
$$
n = \prod p_i^{c_i}\\\\
f(n) = \prod f(p_i^{c_i})
$$&lt;p&gt;一般来说，各种积性函数都是可以使用欧拉筛计算出来的，尽管方法各不相同。&lt;/p&gt;
&lt;p&gt;$\varphi(n)$ 也是积性函数，证明就省略了。&lt;/p&gt;
&lt;h3 id=&#34;计算公式&#34;&gt;计算公式&lt;/h3&gt;
&lt;p&gt;因为 $\varphi(n)$ 是积性函数，所以我们可以先研究 $\varphi(p^k)$ 怎么计算。$p^k$ 的约数有 $1, p, p^2, \dots, p^k$，而 $p^2, \dots, p^k$ 都含有 $p$ 这个约数，如果一个数不与 $p^k$ 互质，则一点有 $p$ 这个约数，我们只有知道小于等于 $p^k$ 的数中 $p$ 的倍数有多少即可。由此得出 $\varphi(n) = p^k - \frac{p^k}{p} = p^k - p^{k-1}=p^k\frac{p-1}{p}$。&lt;/p&gt;
&lt;p&gt;根据 $\varphi(n)$ 的积性函数性质，得出对于任意的 $n$：&lt;/p&gt;</description>
    </item>
    <item>
      <title>拉格朗日插值学习笔记</title>
      <link>https://blog.starryreverie.cc/posts/lagrange-interpolation-note/</link>
      <pubDate>Mon, 07 Feb 2022 23:53:43 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/lagrange-interpolation-note/</guid>
      <description>&lt;p&gt;拉格朗日插值是众多插值算法中的一种，插值是通过一些点来求出过这些点的多项式函数的过程。&lt;/p&gt;
&lt;h2 id=&#34;算法思想&#34;&gt;算法思想&lt;/h2&gt;
&lt;h3 id=&#34;构造函数&#34;&gt;构造函数&lt;/h3&gt;
&lt;p&gt;给出 $n + 1$ 个点 $(x_1,y_1),(x_2,y_2),\dots,(x_n,y_n),(x_{n+1},y_{n+1})$，要求出过这些点的 $n$ 次多项式函数（也称该函数的度为 $n$），先考虑对每个点构造一个函数，第 $i$ 个点的函数为 $f_i(x)$，满足：&lt;/p&gt;
$$
f_i(x) =
\begin{cases}
y_i &amp; x = x_i\\\\
0 &amp; x \ne x_i
\end{cases}
\ (x\in \\{x_1,x_2,\dots,x_n,x_{n+1}\\})
$$&lt;p&gt;最后将这些函数组合为最终的函数：&lt;/p&gt;
$$
f(x) = \sum_{i=1}^{n+1}f_i(x)
$$&lt;p&gt;接下来就是找到一种合适的形式来表示 $f_i(x)$ 的分段规定，可以让 $f_i(x)$ 含有一些因式，满足 $x=x_j,j\ne i$ 时，其值为 $0$，$x=x_i$ 时，其值为 $y_i$。显然下面这种满足条件：&lt;/p&gt;
$$
f_i(x) = y_i \prod_{i\ne j} \frac{x-x_j}{x_i-x_j}
$$&lt;p&gt;所以最终的 $f(x)$ 的解析式为：&lt;/p&gt;
$$
f(x) = \sum_{i=1}^{n + 1} y_i \prod_{i\ne j} \frac{x-x_j}{x_i-x_j}
$$&lt;p&gt;求单个函数值的时间复杂度是 $\Theta(n^2)$。&lt;/p&gt;</description>
    </item>
    <item>
      <title>高斯消元学习笔记</title>
      <link>https://blog.starryreverie.cc/posts/gauss-elimination-note/</link>
      <pubDate>Sun, 06 Feb 2022 16:06:35 +0800</pubDate>
      <guid>https://blog.starryreverie.cc/posts/gauss-elimination-note/</guid>
      <description>&lt;p&gt;高斯消元主要用于求解线性方程组的解，同时可以解决某些有后效性的 DP 问题，&lt;/p&gt;
&lt;h2 id=&#34;算法思想&#34;&gt;算法思想&lt;/h2&gt;
&lt;h3 id=&#34;增广矩阵&#34;&gt;增广矩阵&lt;/h3&gt;
&lt;p&gt;为了更方便地求解方程组，可以将系数和常数项放入矩阵，接下来就可以用一些矩阵的操作来消元了。&lt;/p&gt;
&lt;p&gt;比如有一个方程组如下：&lt;/p&gt;
$$
\begin{cases}
3x+2y+3z=10\\\\
3x+y+4z=12\\\\
x+y+z=4
\end{cases}
$$&lt;p&gt;我们可以这么用矩阵表示:&lt;/p&gt;
$$
\begin{bmatrix}
2 &amp; 2 &amp; 3 \\\\
3 &amp; 1 &amp; 4 \\\\
1 &amp; 1 &amp; 1
\end{bmatrix}
\begin{bmatrix}
10 \\\\
12 \\\\
1
\end{bmatrix}
$$&lt;p&gt;在理论分析和实现代码时，为了方便，通常把两个矩阵拼在一起，这个矩阵就是增广矩阵：&lt;/p&gt;
$$
\begin{bmatrix}
2 &amp; 2 &amp; 3 &amp; 10 \\\\
3 &amp; 1 &amp; 4 &amp; 12 \\\\
1 &amp; 1 &amp; 1 &amp; 1
\end{bmatrix}
$$&lt;p&gt;增广矩阵的第 $i$ 行代笔第 $i$ 个方程，第 $i$ 列表示第 $i$ 个未知数的系数或常数项，接下来用 $x_i$ 表示第 $i$ 个未知数。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
