404
Page not found
How did we get here?
diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..d16d8765 --- /dev/null +++ b/404.html @@ -0,0 +1,40 @@ + + +
+ + + + +如您希望改善 Hydro 的访问质量,在您的服务器带宽较小的情况下,您可以考虑使用内容分发网络,即 CDN ,通常情况下您仅需设置资源 CDN 即可大幅度改善访问质量,关于如何使用内容分发网络和使用中的任何问题请联系各大云厂商。
创建并配置好资源 CDN ,在控制面板中将 server.cdn
设置项修改为 CDN 域名。(如 https://cdn.hydro.ac/
,以 /
结尾)。
如果您预算充足,可以使用全站 CDN。
Note
全站 CDN 配置较为繁琐,如您没有相关使用经验请仅建立并配置资源 CDN ,配置资源 CDN 在大部分情况下即可大幅度改善访问质量。 因各服务商全站 CDN 配置方法不同,配置过程不同,需要配置的内容较多,以下仅为参考配置且配置补全,如果有任何问题建议先查阅云服务商文档及咨询云服务商工程师。
缓存设置可以参考以下设置:
类型 | 内容 | 缓存行为 |
---|---|---|
全部文件 | 全部文件 | 遵循源站 |
文件后缀 | jpg,js,png,css,gif | 缓存一天 |
同时设置添加回源HTTP请求头
头部参数 | 头部取值 |
---|---|
X-Forward-For | 参考所使用服务商CDN文档来设置用户来源IP |
且您应当修改系统设置中的 server.xff
值为 X-Forwarded-For
,该设置是为了能够让系统正确获取到用户的IP地址。
Tips
在使用 cli 之前,请完成数据库配置。
指令中使用 <>
括起来的参数必须给出,用 []
括起来的参数可以给出,若不给出则按照默认设置。
用户请根据自己的情况替换掉用 <>
或是 []
包括起来的部分(括号也应替换)
cli 可以帮助用户在控制台下快捷地进行一些操作。
这些命令需要以在终端以 root 用户执行(安装时执行命令的位置)。
下面给出了一些常用的例子。
Tips
很少使用。建议通过 控制面板>导入用户 功能代替
hydrooj cli user create <mail> <username> <password> <uid>
+# 该用户的邮箱、用户名和密码
+
+# 如创建邮箱为 hydro@hydro.local,用户名为 Hydro,密码为 hydrohydro ,UID 为 2 的用户:
+# 请确保 UID 为不小于 2 的正整数且未被占用,邮箱和用户名均不重复。
+hydrooj cli user create hydro@hydro.local Hydro hydrohydro 2
+
若一切正常,运行该指令后您会从命令行窗口中看到该用户 uid。
hydrooj cli user setSuperAdmin <uid>
+
+# 如设置 uid 为 2 的用户为管理员:
+hydrooj cli user setSuperAdmin 2
+
hydrooj cli user setPriv <uid> <priv>
+
hydrooj cli user setPassword <uid> <password>
+
+# 如将 uid 为 1 的用户的密码改为 hydrohydro:
+hydrooj cli user setPassword 1 hydrohydro
+
先 创建一个账号。
您需要留意运行此指令返回的数字,表示该用户的 uid
,需要填入下面的指令中,然后给予该账号评测权限。
hydrooj cli user setJudge <uid>
+
完成后将配置的用户名及密码写入评测机配置文件,评测机即可连接到网页端。
用户封禁:
hydrooj cli user setPriv <uid> 0
+
IP/邮箱域名封禁:
# key 格式为 ip::xxx.xxx.xxx.xxx (封禁 IP 访问) 或是 mail::xxx.com (禁止 xxx.com 的邮箱注册)
+hydrooj cli blacklist add <key> [duration] # 将 <key> 拉入黑名单,时长为 [duration] (以月为单位的整数,默认为 12,若 duration=0 则永久封禁)
+hydrooj cli blacklist get <key> # 获取黑名单中有关 <key> 的信息
+hydrooj cli blacklist del <key> # 将 <key> 移出黑名单
+
这里并没有列出所有可以运行的指令,因为其中很多功能我们更推荐通过 Web 访问。
hydrooj cli user create <mail> <uname> <password> [uid] [regip] [priv]
+# 创建邮箱为 <mail>,用户名为 <uname>,密码为 <password>,ID 为 [uid],注册 ip 为 [regip],权限为 [priv] 的用户
+hydrooj cli user setUname <uid> <unmae> # 将 ID 为 <uid> 的用户的用户名设置为 <uname>
+hydrooj cli user setPriv <uid> <priv> # 将 ID 为 <uid> 的用户的权限设为 <priv>
+hydrooj cli user setPassword <uid> <password> # 将 ID 为 <uid> 的用户的密码设置为 <password>
+hydrooj cli user setEmail <uid> <mail> # 将 ID 为 <uid> 的用户的邮箱设置为 <mail>
+hydrooj cli user setSuperAdmin <uid> # 将 ID 为 <uid> 的用户设为全站管理员
+hydrooj cli problem import <domainId> <file/path> # 将 <file/path> 的Hydro格式题目包导入至 <domainId> 域中
+hydrooj cli problem export <domainId> # 将 <domainId> 域中的所有题目包导出
+hydrooj cli system set <key> <value> # 修改系统设置 <key> 值为 <value>
+
从 2022/8/12 开始,Hydro 为了避免宿主机环境变化对于评测的影响,对于 此后新安装的实例 默认使用 nix 管理环境。
如果你是在这之前安装的 Hydro,请使用 apt 安装编译器后使用 pm2 restart hydro-sandbox
重启沙箱,并忽略本章节。
以下是 nix 的简要操作说明:
使用 nix-env -iA nixpkgs.编译器名
安装新编译器,后重启沙箱 pm2 restart hydro-sandbox
生效。
nix-env -iA nixpkgs.busybox nixpkgs.bash nixpkgs.diffutils nixpkgs.unzip # 基础组件,已预装,不建议删除
+nix-env -iA nixpkgs.gcc nixpkgs.fpc # C/C++ 和 Pascal,已预装,不建议删除
+nix-env -iA nixpkgs.ghc # Haskell
+nix-env -iA nixpkgs.rustc # Rust
+nix-env -iA nixpkgs.python2 # Python2
+nix-env -iA nixpkgs.pythonPackages.numpy # Python2 Numpy 库
+nix-env -iA nixpkgs.python3Minimal
+nix-env -iA nixpkgs.python3Packages.numpy
+nix-env -iA nixpkgs.php # PHP
+nix-env -iA nixpkgs.go # Golang
+nix-env -iA nixpkgs.nodejs # NodeJS
+nix-env -iA nixpkgs.openjdk_headless # Java
+nix-env -iA nixpkgs.ruby # Ruby
+nix-env -iA nixpkgs.mono # C#
+nix-env -iA nixpkgs.julia_17-bin # Julia
+
使用 nix-env -q
查看已安装的列表,后使用 nix-env -e 编译器名
即可删除对应的编译器。
请注意不要误删 Hydro 基础组件,且操作完成后需要重启沙箱 pm2 restart hydro-sandbox
生效。
如果你需要更加复杂的编译环境配置,我们建议使用编写单独的 nix 文件。
{
+ system ? builtins.currentSystem,
+ pkgs ? import <nixpkgs> { system = system; }
+}:
+
+pkgs.buildEnv {
+ name = "hydrojudge-rootfs";
+ paths = with pkgs; [
+ coreutils bash diffutils nix zip unzip gcc
+ # 上方包是评测所需要的,请勿删除,
+ # 在下方列出你所需要的包,查找方式同上文:
+ fpc python3 rustc
+ ];
+ ignoreCollisions = true;
+ pathsToLink = [ "/" ];
+ # 导出一些基本信息和部分编译器所需的 /etc/passwd
+ postBuild = ''
+ mkdir $out/buildInfo
+ echo 'root:x:0:0:root:/root:/bin/bash' >$out/etc/passwd
+ date >$out/buildInfo/timestamp
+ '';
+}
+
复制以上文件,保存为 default.nix
,使用 nix-build
进行构建。
构建后会产生一个 result
文件夹,记住该文件夹所在的路径。
打开 ~/.hydro/mount.yaml
将其中 /root/.nix-profile
替换为编译出的 result
文件夹(切换到新的环境)
之后保存并重启沙箱。
后续若需更改环境配置,仅需要修改 default.nix
文件之后 nix-build
重新构建,再重启沙箱即可生效。
构建过程中的缓存文件可以使用 nix-collect-garbage
进行清理。
复制题目可以帮助用户将任何有权提交的题目复制到有权创建题目的域中以在比赛/作业/训练计划中使用这些题目。
请参考 FAQS 内的数据库连接教程。
为了保证数据安全,请定期备份。
若您使用自动脚本安装,可使用 hydrooj backup
快捷备份数据,备份完成后会在当前目录生成备份压缩包文件,您可使用 hydrooj restore <备份文件路径>
恢复之前备份的数据。
域功能类似团队,允许在一套系统中创建多个环境(如不同班级,或是不同功能,等等)
用户可以创建多个域。(需要用户有 PRIV_CREATE_DOMAIN 权限,默认仅开放给管理员账户)。 域间数据完全独立,仅用户信息相通(注册账户后,在该实例的所有域中均有效)。
登录账号后,在“我的”选项卡中找到“我的域”,并点击“创建域”,填入以下信息:
gravatar:email
或 qq:id
或 github:name
或 url:link
的格式添加。将会在“我的域”界面内显示。创建域后,您将在此域中拥有管理员权限,可以在域内进行添加题目/创建比赛等操作。
您可以在“管理域”选项卡中点击“初始化讨论节点”按钮初始化讨论节点。
未登录用户将默认使用 guest
权限,登录用户将默认使用 default
权限。(所以将登陆用户设为 default
权限后并不会显示在“管理用户”页内,这也表示所有用户默认不会出现在管理列表中)
所以将一个用户的权限设为 default
和将用户移出该域是等价的。
对于不在列表中的用户,点击右上角“添加用户”,在左侧选中用户,右侧选择权限组,再点击“确定”即可。
若您想要创建比赛/作业,您可以在“比赛”或“作业”选项卡中,在页面右侧找到“创建”按钮, 题目一栏支持根据题目ID或是题目名自动筛选。设置完后可点击“创建”按钮创建比赛(描述这类的框不知道写啥就随便填,不能留空)。
Tips
若因为删除作业/比赛内题目导致无法打开,可以通过 /contest/<id>/edit
或 /homework/<id>/edit
(即在无法打开的页面页面后加上 /edit
)直接访问编辑页并修正。
若您想要创建训练,您可以在“训练” 项卡中点击“新建训练计划”,填写以下信息:
[
+ {章节详细信息},
+ {章节详细信息},
+ ...
+ {章节详细信息}
+]
+
其中,“章节详细信息”的包含以下部分:
_id
:章节数字编号;title
:章节标题;requireNids
:训练此章节之前需要完成的章节数字编号,若无要求则留空,若有多个则使用逗号分隔;pids
:此章节中包含的题目的 ID,若有多个则使用逗号分隔。举例:若要在训练中创建三个章节,章节中分别有 ID 为 1,2,3 的题目。其中章节一、二无前置条件,章节三需要同时完成章节一、二后才能进行,则格式如下:
[
+ {
+ "_id": 1,
+ "title": "入门",
+ "requireNids": [],
+ "pids": [1]
+ },
+ {
+ "_id": 2,
+ "title": "精通",
+ "requireNids": [],
+ "pids": [2]
+ },
+ {
+ "_id": 3,
+ "title": "大师",
+ "requireNids": [1,2],
+ "pids": [3]
+ }
+]
+
Tips
若因为删除训练计划内题目导致训练计划无法打开,可以通过 /training/<id>/edit
(即训练计划页面后加上 /edit
)直接访问训练计划编辑页并修正配置文件。
在题库右侧“创建题目”一栏中选择“从 FPS 文件导入”。
在打开的窗口中,您可以上传:
由于防止解析 fps 文件消耗大量内存,将拒绝导入超过 64MiB 的文件。
xml 文件需要为 UTF8 编码,否则可能出现中文题面乱码;
若您的文件超过大小限制,可考虑先在本地使用 Easy-Fps-Viewer 等工具进行拆分。
此指南将教您修改前端文件。
如果您正在使用开发者模式,请直接修改 packages/ui-default/templates
下的文件。
如果您使用安装脚本部署:
请先使用 hydrooj addon create
创建一个本地插件(如果之前没有做过的话)。
请注意:在阅读本节之前,请确认您已阅读【使用 TypeScript 编写插件】一节并已完成了插件的创建。
import { Context, Time } from 'hydrooj';
+
+export async function apply(ctx: Context) {
+ // handler 表示路由事件
+ // after 表示在主逻辑完成后运行
+ // RecordDetail 为需要捕获的路由名
+ // get 表示仅捕获 GET 请求
+ ctx.on('handler/after/RecordDetail#get', (h) => {
+ if (h.rdoc._id.getTimestamp() < new Date(Date.now() - Time.day) {
+ h.rdoc.code = '';
+ }
+ });
+}
+
Tips
由于用附加组件安装的评测机与 Hydro 必须在同一台服务器上,每一个 Hydro 实例最多只能有一台评测机由附加组件安装。
在安装 Hydro 的机器上运行下面的指令安装 @hydrooj/hydrojudge
:
yarn global add @hydrooj/hydrojudge
+hydrooj addon add @hydrooj/hydrojudge
+
重启 Hydro 后 hydrojudge 即可正常运行。
Tips
该方法可以帮助您在任意服务器上部署评测机。
然后在运行评测机的服务器上安装 HydroJudge :
. <(curl https://hydro.ac/setup.sh) --judge
+
创建目录 ~/.config/hydro
,在该目录下创建文件 judge.yaml
,配置文件格式如下:
hosts:
+ localhost:
+ type: hydro # vj4 用户请在此处填写 vj4
+ server_url: http://localhost/ # Hydro 运行的网址
+ uname: judge # 评测账号用户名
+ password: abc123 # 评测账号密码
+ detail: true # 默认为 true
+
设置完之后,使用下面的指令即可开始运行(可以使用 pm2 管理):
hydrojudge
+
HydroJudge 会发布不定期更新。您可以使用 yarn global upgrade-interactive --latest
来检测并进行更新。
在 系统设置>hydrojudge 中有一栏 Disable builtin judge,将它勾上,然后重启 Hydro 即可。
根据开启的方法关闭即可。
关闭后运行下面指令即可。
yarn global remove @hydrooj/hydrojudge
+hydrooj addon remove @hydrooj/hydrojudge
+
在 系统设置>hydrojudge 修改对应的参数,然后重启 Hydro 和 hydrojudge 即可。
如果有需要修改单题测试点数量上限等设置,可以在 ~/.config/hydro/judge.yaml
的末尾添加下列内容:
testcases_max: 100 # 单题最多测试点数量
+total_time_limit: 120 # 单题最大总测试时长
+parallelism: 2 # 单评测机评测进程数量
+# 更多可选配置均可添加在此处,格式与前面的三排类似
+
Note
如果不调整沙箱的空间大小,当您的评测使用文件 IO 且输入输出文件较大时可能会引发错误。
对于 2022/8/12 前安装的用户:
在服务器上运行下面的代码找到 hydro-sandbox 的运行目录:
pm2 info hydro-sandbox | grep "exec cwd"
+
对于 2022/8/12 后安装的用户:
编辑 /root/.hydro/mount.yaml
,修改 size 即可。
-fdiagnostics-color=always
例:
c:
+ compile: /usr/bin/gcc -O2 -Wall -std=c99 -o \${name} foo.c -lm -fdiagnostics-color=always
+
2022/8/12 后安装的实例默认已开启无限栈空间,无需手动操作
在很多时候系统默认为程序提供的栈空间并不能满足我们的需求,此时我们需要手动为用户程序提供更大的栈空间。
修改 pm2 中 hydro-sandbox 的启动参数为 ulimit -s unlimited && /usr/bin/hydro-sandbox
:
pm2 del hydro-sandbox
+pm2 start bash --name hydro-sandbox -- -c "ulimit -s unlimited && hydro-sandbox"
+
禁用 CPU 频率缩放与 Intel 睿频加速技术,防止 CPU 频率波动。
禁用内存地址空间随机化,以使得存在内存寻址错误的代码能够得到更多可重复的结果,可以通过在 /etc/sysctl.conf
中添加下面这行并运行 sudo sysctl -p
应用:
kernel.randomize_va_space = 0
+
部分 Linux 设备默认使用 cgroup2,而 cgroup2 中移除了精确计量内存消耗的接口。 若要获得更精确的内存计量,推荐启用 cgroup v1 (您可以通过检查 /sys/fs/cgroup/memory/memory.memsw.usage_in_bytes
是否存在来验证是否当前系统是否启用了 cgroup v1 ):
以 Ubuntu 的默认引导器 GRUB 2 为例,编辑 /etc/default/grub
: 在其中
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
+
后,加入 cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0 syscall.x32=y
,变为:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0 syscall.x32=y"
+
运行以下命令更新 GRUB 2 的配置,然后重新启动。
update-grub && reboot
+
目前支持 csv 格式(用 ,
分隔)或 Excel 格式(用 TAB 分隔) 导入用户数据, 数据既可以用文本编辑器创建,也可以用 Excel 等软件来辅助创建。
每行最少三列,最多五列,分别为: 邮箱,用户名,密码,显示名,用户信息。(显示名和用户信息为可选)
请使用 UTF-8 编码,否则中文可能会乱码。
如果使用 CSV 格式(逗号分隔),则用户信息列不可用。
foo@undefined.moe user1 password1
+bar@undefined.moe user2 password2 temp
+test@undefined.moe user3 password3 test {"group":"class1","studentId":"123","school":"Hydro School"}
+
可以在粘贴后点击预览验证复制入的数据的有效性
这将创建三个用户:
user1
密码为 password1
, 邮箱 foo@undefined.moe
;user2
密码为 password2
,邮箱 bar@undefined.moe
,显示名为 temp
;user3
密码为 password3
,邮箱 test@undefined.moe
,显示名为 test
,学校为 Hydro School
,学号为 123
,该用户将会被分配至当前域的 class1
小组内;Note
用户创建后无法删除,请谨慎操作
Tips
兼容大部分 Linux 发行版,推荐使用 Ubuntu 22.04 (教程多,成功率高,上手简单),以下列表中系统不受支持:
Tips
Hydro 需要使用以下端口: 80, 443, 2019, 8888, 5050, 27017,请确保这些端口空闲。
安装和安装后的所有操作均需要在 root 权限下进行!(sudo su
)。
宝塔面板已知出现多次高危漏洞,为防止数据丢失,请不要在生产环境中使用!
运行下面的脚本,等待几分钟即可(建议复制防止敲错):
LANG=zh . <(curl https://hydro.ac/setup.sh)
+
Tips
如果有特殊需求,安装脚本支持一些可选的高级选项,以此方式调用: . <(curl https://hydro.ac/setup.sh) --foo --bar
阿里云/腾讯云/华为云等等用户安装后如果不能访问 请百度搜索 xx云 放行80端口
脚本默认使用的为清华大学镜像。
安装完成后,从 http://服务器ip/
访问网页端,注册一个账号,之后在终端中使用
hydrooj cli user setSuperAdmin 2
+
将首个注册用户设置为超级管理员。之后刷新页面,您应当能在上方导航栏看到控制面板入口。
进入控制面板,右侧系统设置,验证管理员密码后按需修改配置,注意 Server BaseURL 一项需要填写访问网站用的完整的 URL,以 /
结尾。(重要,务必正确填写,样例:https://hydro.ac/
)
系统安装后需要题库,可前往 https://hydro.ac/d/tk/p 免费专区进行下载,支持批量导入。 至此,基础功能安装已全部完成,另,如果你的服务部署在内网,且希望外网的用户可以访问,可以百度搜索 “端口映射” 相关教程。
`,10);function x(m,f){const o=n("RouterLink"),t=n("ExternalLinkIcon");return r(),c("div",null,[h,s("p",null,[e("这里提供了几套方案帮助您建立自己的站点,请选择适合您的方案并继续。"),u,e(" 搭建过程中如果遇到问题欢迎 "),a(o,{to:"/#%E8%81%94%E7%B3%BB%E6%88%91%E4%BB%AC"},{default:i(()=>[e("联系我们")]),_:1}),e(" 提问。")]),_,y,s("p",null,[e("CentOS 8 "),s("a",E,[e("已于 2021-12-31 停止支持"),a(t)]),e(",后续不会为安全漏洞发布补丁,建议重装为其他操作系统。"),b,e(" 如果使用虚拟机安装,推荐购买 UPS 保证供电稳定或是经常备份,突然断电易导致数据库损坏无法读取!")]),v])}const F=l(d,[["render",x],["__file","index.html.vue"]]);export{F as default}; diff --git a/assets/index.html-22922d8e.js b/assets/index.html-22922d8e.js new file mode 100644 index 00000000..0b5b9998 --- /dev/null +++ b/assets/index.html-22922d8e.js @@ -0,0 +1 @@ +const e=JSON.parse(`{"key":"v-74379e72","path":"/FAQ/","title":"常见问题集合","lang":"en-US","frontmatter":{"description":"好消息!本页和各大浏览器均达成了合作,使用 Ctrl-F 即可快速搜索关键词! 更多教程请点击右上角的常用教程查看。 用户QQ群 1085853538 如何快速上手了解系统功能? 参照本文 https://hydro.ac/discuss/6172ceeed850d38c79ae18f9 (https://hydro.ac/discuss/6172ce...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/FAQ/"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"常见问题集合"}],["meta",{"property":"og:description","content":"好消息!本页和各大浏览器均达成了合作,使用 Ctrl-F 即可快速搜索关键词! 更多教程请点击右上角的常用教程查看。 用户QQ群 1085853538 如何快速上手了解系统功能? 参照本文 https://hydro.ac/discuss/6172ceeed850d38c79ae18f9 (https://hydro.ac/discuss/6172ce..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-02-25T05:29:05.000Z"}],["meta",{"property":"article:modified_time","content":"2023-02-25T05:29:05.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"常见问题集合\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-02-25T05:29:05.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"好消息!本页和各大浏览器均达成了合作,使用 Ctrl-F 即可快速搜索关键词!","slug":"好消息-本页和各大浏览器均达成了合作-使用-ctrl-f-即可快速搜索关键词","link":"#好消息-本页和各大浏览器均达成了合作-使用-ctrl-f-即可快速搜索关键词","children":[]},{"level":2,"title":"更多教程请点击右上角的常用教程查看。","slug":"更多教程请点击右上角的常用教程查看。","link":"#更多教程请点击右上角的常用教程查看。","children":[]},{"level":2,"title":"用户QQ群 1085853538","slug":"用户qq群-1085853538","link":"#用户qq群-1085853538","children":[]},{"level":2,"title":"如何快速上手了解系统功能?","slug":"如何快速上手了解系统功能","link":"#如何快速上手了解系统功能","children":[]},{"level":2,"title":"什么是 OJ?","slug":"什么是-oj","link":"#什么是-oj","children":[]},{"level":2,"title":"Arch Linux 开发模式安装时出现 [ERR_STREAM_PREMATURE_CLOSE]: Premature close 错误","slug":"arch-linux-开发模式安装时出现-err-stream-premature-close-premature-close-错误","link":"#arch-linux-开发模式安装时出现-err-stream-premature-close-premature-close-错误","children":[]},{"level":2,"title":"为什么我安装完成之后仍然无法访问?","slug":"为什么我安装完成之后仍然无法访问","link":"#为什么我安装完成之后仍然无法访问","children":[]},{"level":2,"title":"为什么我配置完反向代理(caddy/nginx)之后无法登录(出现CsrfTokenError)?","slug":"为什么我配置完反向代理-caddy-nginx-之后无法登录-出现csrftokenerror","link":"#为什么我配置完反向代理-caddy-nginx-之后无法登录-出现csrftokenerror","children":[]},{"level":2,"title":"怎么备份/还原备份/迁移数据?","slug":"怎么备份-还原备份-迁移数据","link":"#怎么备份-还原备份-迁移数据","children":[]},{"level":2,"title":"恢复备份时出现 '/data/file/hydro': Directory not empty","slug":"恢复备份时出现-data-file-hydro-directory-not-empty","link":"#恢复备份时出现-data-file-hydro-directory-not-empty","children":[]},{"level":2,"title":"更新升级","slug":"更新升级","link":"#更新升级","children":[]},{"level":2,"title":"怎么导入题目/创建题目?","slug":"怎么导入题目-创建题目","link":"#怎么导入题目-创建题目","children":[]},{"level":2,"title":"如何限制未登录用户访问?","slug":"如何限制未登录用户访问","link":"#如何限制未登录用户访问","children":[]},{"level":2,"title":"比赛作业里面的时间是什么含义,OI排名跟普通排名有何区别?","slug":"比赛作业里面的时间是什么含义-oi排名跟普通排名有何区别","link":"#比赛作业里面的时间是什么含义-oi排名跟普通排名有何区别","children":[]},{"level":2,"title":"脚本把 OJ 装在哪里了?","slug":"脚本把-oj-装在哪里了","link":"#脚本把-oj-装在哪里了","children":[]},{"level":2,"title":"题目的限时和内存限制的精度是怎样的?","slug":"题目的限时和内存限制的精度是怎样的","link":"#题目的限时和内存限制的精度是怎样的","children":[]},{"level":2,"title":"我想让 Python 支持 numpy 等等库,怎么办?","slug":"我想让-python-支持-numpy-等等库-怎么办","link":"#我想让-python-支持-numpy-等等库-怎么办","children":[]},{"level":2,"title":"使用安装脚本后忘记 MongoDB 的账号密码怎么办?","slug":"使用安装脚本后忘记-mongodb-的账号密码怎么办","link":"#使用安装脚本后忘记-mongodb-的账号密码怎么办","children":[]},{"level":2,"title":"如何关闭、打开用户注册?","slug":"如何关闭、打开用户注册","link":"#如何关闭、打开用户注册","children":[]},{"level":2,"title":"用户名为 Hydro 的用户是干什么的?密码是什么?可以登录么?","slug":"用户名为-hydro-的用户是干什么的-密码是什么-可以登录么","link":"#用户名为-hydro-的用户是干什么的-密码是什么-可以登录么","children":[]},{"level":2,"title":"如何修改网站图标?","slug":"如何修改网站图标","link":"#如何修改网站图标","children":[]},{"level":2,"title":"如何重置数据?","slug":"如何重置数据","link":"#如何重置数据","children":[]},{"level":2,"title":"评测显示“总时限超过 60s,评测取消”","slug":"评测显示-总时限超过-60s-评测取消","link":"#评测显示-总时限超过-60s-评测取消","children":[]},{"level":2,"title":"如何在背景中添加线条特效?","slug":"如何在背景中添加线条特效","link":"#如何在背景中添加线条特效","children":[]},{"level":2,"title":"使用 Hydro 要花多少钱?","slug":"使用-hydro-要花多少钱","link":"#使用-hydro-要花多少钱","children":[]},{"level":2,"title":"execve: no such file or directory","slug":"execve-no-such-file-or-directory","link":"#execve-no-such-file-or-directory","children":[]},{"level":2,"title":"怎么自定义用户标签?","slug":"怎么自定义用户标签","link":"#怎么自定义用户标签","children":[]},{"level":2,"title":"为什么我无法批量下载多个文件?","slug":"为什么我无法批量下载多个文件","link":"#为什么我无法批量下载多个文件","children":[]},{"level":2,"title":"为什么我的提交页面没有语言可选?","slug":"为什么我的提交页面没有语言可选","link":"#为什么我的提交页面没有语言可选","children":[]},{"level":2,"title":"The 'yarn global' commands have been remove in 2.x - consider using 'yarn dlx' or a third-party plugin instead","slug":"the-yarn-global-commands-have-been-remove-in-2-x-consider-using-yarn-dlx-or-a-third-party-plugin-instead","link":"#the-yarn-global-commands-have-been-remove-in-2-x-consider-using-yarn-dlx-or-a-third-party-plugin-instead","children":[]},{"level":2,"title":"我是机房用户,大量用户同 IP 操作触发了频率限制,怎么解决?","slug":"我是机房用户-大量用户同-ip-操作触发了频率限制-怎么解决","link":"#我是机房用户-大量用户同-ip-操作触发了频率限制-怎么解决","children":[]}],"git":{"createdTime":1647254801000,"updatedTime":1677302945000,"contributors":[{"name":"undefined","email":"i@undefined.moe","commits":9},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":1},{"name":"pt3155420267","email":"33685002+pt3155420267@users.noreply.github.com","commits":1}]},"readingTime":{"minutes":10.2,"words":3060},"filePathRelative":"FAQ/README.md","localizedDate":"March 14, 2022","autoDesc":true,"excerpt":""}`);export{e as data}; diff --git a/assets/index.html-231822f9.js b/assets/index.html-231822f9.js new file mode 100644 index 00000000..c1732ec3 --- /dev/null +++ b/assets/index.html-231822f9.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-0efcd172.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-274fe549.js b/assets/index.html-274fe549.js new file mode 100644 index 00000000..026c9ce5 --- /dev/null +++ b/assets/index.html-274fe549.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7445cd33","path":"/dev/","title":"开发环境部署","lang":"en-US","frontmatter":{"description":"如果您没有过 TypeScript 项目的开发经验,非常不建议自建开发环境。 使用自动脚本安装也可以基于插件系统完成大部分的定制需求(参照左侧【使用 TypeScript 编写插件】章节)。 您可以使用 Gitpod (https://gitpod.io/#https://github.com/hydro-dev/Hydro) 快速打开配置完成的开发环...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/dev/"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"开发环境部署"}],["meta",{"property":"og:description","content":"如果您没有过 TypeScript 项目的开发经验,非常不建议自建开发环境。 使用自动脚本安装也可以基于插件系统完成大部分的定制需求(参照左侧【使用 TypeScript 编写插件】章节)。 您可以使用 Gitpod (https://gitpod.io/#https://github.com/hydro-dev/Hydro) 快速打开配置完成的开发环..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-06-06T04:14:26.000Z"}],["meta",{"property":"article:modified_time","content":"2023-06-06T04:14:26.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"开发环境部署\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-06-06T04:14:26.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"安装依赖","slug":"安装依赖","link":"#安装依赖","children":[]},{"level":2,"title":"安装 Hydro","slug":"安装-hydro","link":"#安装-hydro","children":[]},{"level":2,"title":"插件","slug":"插件","link":"#插件","children":[]},{"level":2,"title":"启动 Hydro","slug":"启动-hydro","link":"#启动-hydro","children":[]},{"level":2,"title":"更新","slug":"更新","link":"#更新","children":[]}],"git":{"createdTime":1611599229000,"updatedTime":1686024866000,"contributors":[{"name":"undefined","email":"i@undefined.moe","commits":10},{"name":"Macesuted Kysic","email":"macesuted@foxmail.com","commits":4},{"name":"Macesuted","email":"macesuted@qq.com","commits":3},{"name":"(Macesuted)","email":"macesuted@foxmail.com","commits":2},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":2},{"name":"(Macesuted)","email":"1912192337@qq.com","commits":1},{"name":"Macesuted Kysic","email":"57275149+Macesuted@users.noreply.github.com","commits":1}]},"readingTime":{"minutes":2.32,"words":696},"filePathRelative":"dev/README.md","localizedDate":"January 25, 2021","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/index.html-668ce669.js b/assets/index.html-668ce669.js new file mode 100644 index 00000000..4ff8ff14 --- /dev/null +++ b/assets/index.html-668ce669.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as i,e as r}from"./app-0efcd172.js";const d={},h=r('Hydro 中题目的难度,根据递交数、通过率以及每个递交的递交时间和评测结果,通过算法计算得出。
您可以在比赛的详细界面内点击“参与比赛”按钮进行参与。 比赛过程中“成绩表”会根据比赛规则显示排名。 在比赛截止之后,您仍然可以订正其中的题目,但“成绩表”将停止更新。
若您想发布一个讨论,请先进入一个讨论节点,之后点击“创建一个讨论”按钮并填写:
之后点击“创建”按钮进行发布。
您可以在作业的详情页面中,点击“认领作业”。
在作业开始之前,您无法查看作业中的题目。
在作业持续时间内,您与他人的做题情况会被实时统计在“成绩表”内。
在作业进入延期阶段后,您仍然可以提交题目,但成绩表内的分数将根据延期扣分规则按百分比折算。
在作业截止之后,您仍然可以订正其中的题目,但“成绩表”将停止更新。
为什么使用 Hydro?
安全:使用 cgroup 进行隔离,杜绝卡评测;
高效:Hydro 使用了沙箱复用技术,拥有极高的评测效率;
扩展:Hydro 支持安装额外模块进行扩展;
强大:配合 Judge 模块(或 HydroJudge 独立评测机),可支持 spj,交互题,提交答案题,文件IO 等多种特性;
自定:所有权限节点均可自由设置;
易上手:无需改动源代码,系统设置中预留了大量可自行修改的内容;管理逻辑简洁;
社区:Hydro 正在持续维护中;
如果您正在使用 HustOJ,可以导出题目为 FPS 文件后使用 fps-importer 插件 直接导入 Hydro。
如果您正在使用 QDUOJ, 可以导出题目为 QDUOJ-zip 格式后使用 import-qduoj 插件直接导入 Hydro。
如果您正在使用 Vijos / SYZOJ / HustOJ / UniversalOJ, 可以直接使用 migrate 插件 导入所有数据至 Hydro。
Hydro 支持很多其他系统无法支持的题型,可在 https://hydro.ac/d/system_test/p 中查看并免费下载使用样例。(需要登录)
下方对比了 Hydro 与其他主流 OJ 系统的功能。(只进行在不修改源代码情况下的对比)
+:支持
+=:部分支持
+?: 未知
+-:不支持
+
功能 | Hydro | HustOJ | SYZOJ[1] | QDUOJ | Vijos |
---|---|---|---|---|---|
安装 | 一键脚本 | 一键脚本 | 手动搭建 | docker | docker |
数据库 | MongoDB | MySQL | MariaDB | Postgres | MongoDB |
测试数据存储 | 本地/S3 [2] | 本地 | 本地 | 本地 | 数据库 |
多评测机 | + | =[3] | =[4] | = | + |
测试数据同步 | 按需抓取 | 全量同步 | 全量同步 | 全量同步 | 按需抓取 |
比赛 | ACM/OI/IOI/乐多 | ACM/OI | ACM/OI/IOI | ACM/OI/IOI | ACM/OI |
封榜 | + | + | - | - | - |
作业功能 | + | + | - | - | + |
修改编译命令/添加语言 | + | =[5] | - | - | + |
权限系统 [6] | + | = | - | - | + |
训练计划(题单) | + | + | -[7] | - | + |
团队 | + [8] | - | - | - | + |
Hack | + | - | - | - | - |
SpecialJudge | + [9] | + | + | - | = |
Subtask | + | - | + | - | - |
交互题 | + | - | + | - | - |
RemoteJudge | CF/SPOJ/UOJ/POJ/Luogu | HDU/PKU | - | - | - |
题目导入 | fps/syzoj/qduoj/hydro | fps/qduoj | syzoj | fps/qduoj | - |
SYZOJ 和 Lyrio (曾用名 syzoj-ng,loj.ac 当前所用系统) 是两套不同的系统,这意味着使用 SYZOJ 无法再导入 loj.ac 的题目,同时 Lyrio 无比赛功能。 ↩︎
S3 指所有兼容 Amazon S3 协议的服务,包括腾讯COS,阿里OSS 等。 ↩︎
安装配置繁琐,且需要手动在服务器间同步数据。 ↩︎
需要手动在服务器间同步数据。 ↩︎
仅能修改部分编译参数,添加语言需要修改源代码。 ↩︎
此处的权限系统指 除用户/管理员二元化权限之外的 的更细粒度的权限划分。 ↩︎
部分二次开发版本有此功能。 ↩︎
通过域功能,允许用户创建域并在域内拥有管理员权限。域之间仅共享账号数据,也可使用域内小组进行权限控制。 ↩︎
支持所有主流 SPJ 格式。 ↩︎
Hydro 支持使用插件扩展自身所支持的功能。
Note
插件对站点的所有内容具有完全的访问权限,请不要启用来历不明的插件。
Tips
部分斜体字插件需配合额外支持软件,如您只安装插件是无法使用的,详情请前往左侧插件详情查看。
如您为旧版本升级失去博客功能,请直接安装 @hydrooj/blog
,原数据不会丢失。
Hydro 官方目前提供了以下附加组件:
ID | 描述 |
---|---|
@hydrooj/blog | 博客功能 |
@hydrooj/fps-importer | 导入 fps 格式的题目 |
@hydrooj/geoip | 显示用户登录地(需要IP库支持) |
@hydrooj/hydrojudge | 评测组件 |
@hydrooj/import-qduoj | 导入 QDUOJ 导出的题库 |
@hydrooj/login-with-github | 允许用户使用 GitHub 登录 |
@hydrooj/login-with-google | 允许用户使用 Google 登录 |
@hydrooj/migrate | 从 vijos4/HustOJ/SYZOJ/UniversalOJ 升级 |
@hydrooj/recaptcha | 注册时启用 reCAPTCHA 验证码 |
@hydrooj/ui-default | Hydro 的默认用户界面 |
@hydrooj/onlyoffice | 显示 doc/docx 格式题目 |
@hydrooj/sonic | 基于 sonic 的题目搜索增强 |
@hydrooj/elastic-search | 基于 Elastic 的题目搜索增强 |
@hydrooj/vjudge | Codeforces/SPOJ/UOJ/POJ/Luogu |
@hydrooj/prom-client | 导出系统状态至 Prometheus |
大部分插件的配置均可在安装后于 控制面板>系统设置 中找到。
部分插件若安装后没有正确配置可能会影响系统正常使用!
先全局安装所需模块,再向 hydrooj 注册即可。例:安装 @hydrooj/geoip
yarn global add @hydrooj/geoip
+hydrooj addon add @hydrooj/geoip
+
或者,如果你正在安装一个其他途径获取的插件(自行创建等),请直接使用文件夹 绝对路径: (文件夹路径即为 包含 package.json 的文件夹)
hydrooj addon add /root/xxx
+
请不要将插件与插件嵌套摆放,否则可能会导致一些不可预知的问题。
安装完插件后需要重启 hydrooj 以使插件生效。
hydrooj addon list
+
yarn global upgrade-interactive --latest
+
yarn global remove @hydrooj/geoip
+hydrooj addon remove @hydrooj/geoip
+
Hydro 是一个高效的信息学在线测评系统。特点:易于部署(且提供安装脚本),轻量,功能强大且易于扩展。
',2),c={href:"https://github.com/hydro-dev/Hydro",target:"_blank",rel:"noopener noreferrer"},u=e("br",null,null,-1),_=e("br",null,null,-1),p=e("br",null,null,-1),b=e("br",null,null,-1),m={href:"https://github.com/hydro-dev/Hydro/issues",target:"_blank",rel:"noopener noreferrer"},y={href:"https://gitpod.io/#https://github.com/hydro-dev/Hydro",target:"_blank",rel:"noopener noreferrer"},g={href:"https://hydro.ac/",target:"_blank",rel:"noopener noreferrer"},f=e("h2",{id:"联系我们",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#联系我们","aria-hidden":"true"},"#"),o(" 联系我们")],-1),H=e("a",{href:"mailto:i@undefined.moe"},"i@undefined.moe",-1),k=e("br",null,null,-1),v=e("br",null,null,-1),x={href:"https://t.me/webpack_exports_undefined",target:"_blank",rel:"noopener noreferrer"},j=i('注:Hydro 为开源框架,任何人均能够在遵守协议的情况下使用这套框架。
版权申诉相关问题请联系对应网站管理者,(通常为其 UID=2 的用户),与开发者无关。
本项目基于 AGPL v3 开源。
在您部署 Hydro 时,需要保留底部的 Powered by Hydro
字样,其中的 Hydro
字样需指向 hydro.js.org/本仓库/fork
之一的链接。
若您对源码做出修改,同样需要以 AGPL v3 开源,您可以以 Powered by Hydro, Modified by xxx
格式在页脚注明。
此限制对以下模块仍然有效:
如果您对以上条目感到不适,建议您停止使用本项目。
排名不分先后,按照链接字典序。
',4),I={href:"https://github.com/",target:"_blank",rel:"noopener noreferrer"},L={href:"https://github.com/criyle",target:"_blank",rel:"noopener noreferrer"},N={href:"https://github.com/vijos/vj4",target:"_blank",rel:"noopener noreferrer"};function P(V,B){const r=l("ExternalLinkIcon");return s(),d("div",null,[a,e("p",null,[e("a",c,[o("Github 仓库"),t(r)])]),e("p",null,[o("欢迎 star 本项目,这是对开发者的鼓励。"),u,o(" 项目的开发与维护需要资金,欢迎进行捐助。"),_,o(" Hydro 提供收费的功能定制服务,如您需要可与我们联系。"),p,o(" 相关文档若说明的不够详细,请提交 Pull Request 或联系开发组说明。"),b,o(" Bug 和功能建议请在 "),e("a",m,[o("Issues"),t(r)]),o(" 提出。")]),e("p",null,[e("a",y,[o("在 Gitpod 打开已配置完成的测试环境"),t(r)])]),e("p",null,[e("a",g,[o("Hydro Online Judge"),t(r)])]),f,e("p",null,[o("Email "),H,k,o(" Hydro 用户群:1085853538"),v,o(" Telegram "),e("a",x,[o("@webpack_exports_undefined"),t(r)])]),j,e("p",null,[o("若您需要对这些模块闭源处理,请考虑联系我们以购买许可。"),G,o(" 鉴于 Mirai 处的 "),e("a",w,[o("不和谐事件"),t(r)]),o(" 对此项目做如下声明:")]),E,e("ul",null,[e("li",null,[e("a",I,[o("Github"),t(r)]),o(" 为 Hydro 提供了代码托管与自动构建。")]),e("li",null,[e("a",L,[o("criyle"),t(r)]),o(" 提供评测沙箱实现。")]),e("li",null,[e("a",N,[o("Vijos"),t(r)]),o(" 为 Hydro 提供了UI框架。")])])])}const C=n(h,[["render",P],["__file","index.html.vue"]]);export{C as default}; diff --git a/assets/index.html-c0e68838.js b/assets/index.html-c0e68838.js new file mode 100644 index 00000000..3e05b8ca --- /dev/null +++ b/assets/index.html-c0e68838.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-67d16688","path":"/plugins/","title":"插件","lang":"en-US","frontmatter":{"description":"Hydro 支持使用插件扩展自身所支持的功能。 插件对站点的所有内容具有完全的访问权限,请不要启用来历不明的插件。 附加组件列表 部分斜体字插件需配合额外支持软件,如您只安装插件是无法使用的,详情请前往左侧插件详情查看。 如您为旧版本升级失去博客功能,请直接安装 @hydrooj/blog ,原数据不会丢失。 Hydro 官方目前提供了以下附加组件: ...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/plugins/"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"插件"}],["meta",{"property":"og:description","content":"Hydro 支持使用插件扩展自身所支持的功能。 插件对站点的所有内容具有完全的访问权限,请不要启用来历不明的插件。 附加组件列表 部分斜体字插件需配合额外支持软件,如您只安装插件是无法使用的,详情请前往左侧插件详情查看。 如您为旧版本升级失去博客功能,请直接安装 @hydrooj/blog ,原数据不会丢失。 Hydro 官方目前提供了以下附加组件: ..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-05-12T03:18:30.000Z"}],["meta",{"property":"article:modified_time","content":"2023-05-12T03:18:30.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"插件\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-05-12T03:18:30.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"附加组件列表","slug":"附加组件列表","link":"#附加组件列表","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"查看已注册的插件列表","slug":"查看已注册的插件列表","link":"#查看已注册的插件列表","children":[]},{"level":2,"title":"更新","slug":"更新","link":"#更新","children":[]},{"level":2,"title":"卸载","slug":"卸载","link":"#卸载","children":[]}],"git":{"createdTime":1595413301000,"updatedTime":1683861510000,"contributors":[{"name":"Macesuted Kysic","email":"macesuted@foxmail.com","commits":6},{"name":"undefined","email":"i@undefined.moe","commits":6},{"name":"(Macesuted)","email":"macesuted@foxmail.com","commits":3},{"name":"Macesuted","email":"macesuted@qq.com","commits":3},{"name":"Macesuted","email":"57275149+Macesuted@users.noreply.github.com","commits":2},{"name":"无限UCW","email":"45730483+wuxianucw@users.noreply.github.com","commits":2},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":1},{"name":"undefined","email":"masnn0@outlook.com","commits":1}]},"readingTime":{"minutes":1.76,"words":528},"filePathRelative":"plugins/README.md","localizedDate":"July 22, 2020","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/index.html-f38274db.js b/assets/index.html-f38274db.js new file mode 100644 index 00000000..d1c35a44 --- /dev/null +++ b/assets/index.html-f38274db.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-147825fb","path":"/docs/","title":"介绍","lang":"en-US","frontmatter":{"description":"为什么使用 Hydro? 安全:使用 cgroup 进行隔离,杜绝卡评测;; 高效:Hydro 使用了沙箱复用技术,拥有极高的评测效率;; 扩展:Hydro 支持安装额外模块进行扩展;; 强大:配合 Judge 模块(或 HydroJudge 独立评测机),可支持 spj,交互题,提交答案题,文件IO 等多种特性;; 自定:所有权限节点均可自由设置;;...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/docs/"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"介绍"}],["meta",{"property":"og:description","content":"为什么使用 Hydro? 安全:使用 cgroup 进行隔离,杜绝卡评测;; 高效:Hydro 使用了沙箱复用技术,拥有极高的评测效率;; 扩展:Hydro 支持安装额外模块进行扩展;; 强大:配合 Judge 模块(或 HydroJudge 独立评测机),可支持 spj,交互题,提交答案题,文件IO 等多种特性;; 自定:所有权限节点均可自由设置;;..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:image","content":"https://hydro.js.org/"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-06-21T14:07:07.000Z"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:alt","content":"介绍"}],["meta",{"property":"article:modified_time","content":"2023-06-21T14:07:07.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"介绍\\",\\"image\\":[\\"https://hydro.js.org/\\"],\\"dateModified\\":\\"2023-06-21T14:07:07.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"功能对比","slug":"功能对比","link":"#功能对比","children":[]},{"level":2,"title":"截图","slug":"截图","link":"#截图","children":[]},{"level":2,"title":"现在开始","slug":"现在开始","link":"#现在开始","children":[]}],"git":{"createdTime":1611599229000,"updatedTime":1687356427000,"contributors":[{"name":"undefined","email":"i@undefined.moe","commits":14},{"name":"Macesuted","email":"macesuted@qq.com","commits":4},{"name":"(Macesuted)","email":"macesuted@foxmail.com","commits":2},{"name":"Macesuted","email":"57275149+Macesuted@users.noreply.github.com","commits":1},{"name":"Macesuted Kysic","email":"macesuted@foxmail.com","commits":1},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":1}]},"readingTime":{"minutes":2.47,"words":740},"filePathRelative":"docs/README.md","localizedDate":"January 25, 2021","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/index.html-f4902402.js b/assets/index.html-f4902402.js new file mode 100644 index 00000000..58814107 --- /dev/null +++ b/assets/index.html-f4902402.js @@ -0,0 +1,7 @@ +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as r,c as d,a as s,b as o,d as e,e as c}from"./app-0efcd172.js";const p={},t=s("h1",{id:"开发环境部署",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#开发环境部署","aria-hidden":"true"},"#"),o(" 开发环境部署")],-1),i=s("p",null,[o("如果您没有过 TypeScript 项目的开发经验,非常不建议自建开发环境。"),s("br"),o(" 使用自动脚本安装也可以基于插件系统完成大部分的定制需求(参照左侧【使用 TypeScript 编写插件】章节)。")],-1),y={href:"https://gitpod.io/#https://github.com/hydro-dev/Hydro",target:"_blank",rel:"noopener noreferrer"},h=s("h2",{id:"安装依赖",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#安装依赖","aria-hidden":"true"},"#"),o(" 安装依赖")],-1),u=s("li",null,"系统要求:Hydro开发环境目前仅支持 Linux/Unix 系统,如您使用 Windows 请使用 WSL2 。",-1),E={href:"https://www.mongodb.com/try/download/community",target:"_blank",rel:"noopener noreferrer"},D=s("li",null,[o("NodeJS:请安装 NodeJS >=18 版本。(若使用 apt 请使用 nodesource 提供的源替代官方源) (推荐使用 nix ,可通过"),s("code",null,". <(curl https://hydro.ac/nix.sh)"),o(" 快速安装)")],-1),b=s("li",null,[o("yarn:安装 yarn 前请先完成 NodeJS 安装。 "),s("code",null,"npm install -g yarn")],-1),F=c(`尽管这不是必须的,但文档多数区域使用了 npx
工具来调用工作区的程序。如果此命令不存在,你可以在 Hydro 项目文件夹外使用 yarn global add npx
安装它。
git clone https://github.com/hydro-dev/Hydro.git /root/Hydro --recursive # 下载至 /root/Hydro 文件夹
+cd /root/Hydro # 进入工作目录
+yarn # 安装依赖包
+yarn build:ui:production # 编译前端
+
开发环境部署完成后默认不启用任何插件。
所有官方插件均随源码仓库下载到安装文件夹的 packages
子文件夹下,可以通过下面的命令启用官方插件(以启用 @hydrooj/ui-default
为例):
npx hydrooj addon add @hydrooj/ui-default
+
对于非官方插件,下载后通过下面的命令启用即可(以启用位置在 /root/addon
下的插件为例):
npx hydrooj addon add /root/addon
+
支持如下启动参数:
--port=2333
指定启动端口--debug
启用开发模式使用 yarn debug --port=2333 --watch
启动 Hydro,并在后台运行 yarn build:ui:dev
,可以对前端源码进行实时转译,在反复修改时可节省编译时间。启动完成后,您可以在 8000 端口访问到 Hydro 实例,且前端的任何更改将即时生效。(后端热重载可能存在 bug,部分模块修改后可能仍需重启才能生效)
请注意:此功能仅在启用了 @hydrooj/ui-default
插件的情况下才会生效。
首次启动会要求您填写数据库连接信息。请根据您数据库的安装填写(若无密码则留空)
请按照下文说明创建管理员账户即可正常使用。
需要更新的时候进入对应仓库文件夹执行 git pull
,然后重新 yarn && yarn build:ui:production
即可。
系统中的用户只有编辑、禁用功能,没有删除功能,这是为了从根源上防止出现“教学事故”,请不要要求增加相关功能,如果认为自己绝不会误操作,请自行开发相关功能。
现在你所使用的评测系统也仅仅是一个程序,并没有人工智能。因此很多地方需要你来迁就它,如果不这样做,你的答案即使本质上是正确的,由于形式的错误造成系统不能理解,也会导致错误。
系统运行过程如下:
在了解了上面的情况以后,同学们应该理解,如果题目没有要求程序输出“Please Input Two Number”之类的提示信息,那么自行输出这些文字将导致你的程序输出与老师事先告诉系统的输出不能做到“一字不差”,因而将导致系统报答案错误。
如果题目要求每两行输出之间要空一行,结果你没有空,会是格式错误,反之亦然。
也许你会觉得,哦,这系统太烂了,这点东西都不能自动识别;实际上正是这样才能有效训练大家编程的精确性、养成良好的代码习惯。很多程序高手都跟你一样,是从对这个系统吐槽开始学习如何认真仔细的、一丝不苟的进行编程的。
系统为了能用统一的方式运行所有同学的答案,不得不对所有人提交的答案的形式进行限定。
对于学习C、C++语言的同学来说,所有提交给系统的答案必须包含并且只有一个main函数,这个main函数必须返回int类型,并且最好返回0,因为操作系统对非零的返回值认为是运行出错。
编译错误发生时,点击“编译错误”的文字链接可以得到详细解释。
删除 .yarnrc.yml
和 .yarn
后再试。
如果您使用的是 阿里云/腾讯云 等服务商,请确保安全组放行了 80 和 443 端口。
hydrooj backup
hydrooj restore backup-xxx.zip
百度学习 crontab 的用法后,可以使用 sudo crontab -e
定制自动备份计划。
关闭 minio
(pm2 stop minio
) 后手动删除 /data/file/hydro
文件夹再重试。
yarn global upgrade-interactive --latest
然后按空格选中除 pm2 之外的所有包更新,回车确认。 然后 pm2 restart hydrooj
重启服务。 重启后 pm2 logs hydrooj --lines 100
没有看到报错并看到了 Server started
则一切正常。
Hydro 的所有历史版本,都可以无损升级到最新版本。如果老系统更新有疑问,随时加官方群咨询群主。
题目列表右侧有相应入口。
切记:不要导入过多你暂时用不上的题目,正确的方式是每次训练、作业,导入所需的5-10个题目,比赛作业结束后让题目成为训练题库的一部分。这样能保证题库中题号靠前的题目难度依次上升,适合后来的同学自行训练。不要贪图题目数量而忽视其质量。自己看不懂解法的题目,少用、慎用。
管理域 -> 管理权限 将 Guest 权限组的 查看此域 权限禁用。
时间是指参与人员做出对应题目“花费”的时间:
即:做出题目的时刻 – 比赛开始的时刻 + 惩罚时间
惩罚时间 = 做对之前错误的提交数 * 20分钟。
普通排名按做对的题目数和“花费”的时间进行排名。
OI排名,按得分排名,题目可以按通过的比例进行记分,每题100分。如果希望数据的分值不平均分配,可以使用 config 配置。
Hydro 的默认位置可以运行 yarn global dir
得到。(不要直接改代码,更新会覆盖此处的所有修改,请使用插件API)
默认的数据库文件放置在 /data/db
,但是不要直接复制文件,而是推荐用 hydrooj backup
进行备份。
测试数据等文件存储于 /data/file
。
配置文件位于 /root/.config/hydro
和 /root/.hydro
。 对于正在运行中的生产服务器,任何操作前请做好离线备份。
备份文件一定要解压查看内部是否包含全部数据,关注备份的大小(大系统备份应该有上百兆),有条件找虚拟机实测还原是否成功
题目限时允许设定的字面精度是 1ms,但是由于操作系统内核参数的限定,实测的精度通常为4ms。 内存限制的精度是1MB,对于本地native的编译型语言c/c++/pascal/freebasic/clang等是考察程序本身的内存申请空间; 对于虚拟机和脚本语言,则包含了虚拟机本身或解释器本身的内存消耗。
如果你是 2022/8/12 日前安装,使用 pip3 install numpy
后 pm2 restart hydro-sandbox
否则参照请参照 编译器 章节。
看 /root/.hydro/config.json
。
用户注册由 Guest 用户(uid 为 0)的 PRIV_REGISTER_USER 权限控制,默认允许注册。 使用 hydrooj cli user setPriv 0 0
即可关闭注册。 若要重新打开,可使用 hydrooj cli user setPriv 0 8
。
变更后,请重启 hydrooj 服务:pm2 restart hydrooj
用户名为 Hydro 的用户(uid 为 1)仅用于发送系统消息(与 QQ 中的 10000 类似),无法登录。
使用 hydrooj addon create
创建一个插件,这会自动创建 /root/addon
目录。
进入 /root/addon/public
目录,将您的站点图标放置于该文件夹下。
您需要将以下文件放在该目录中(适配不同浏览器):
favicon-16x16.png
favicon-32x32.png
favicon-96x96.png
favicon.ico
(32x32)android-chrome-192x192.png
apple-touch-icon-180x180.png
分辨率应尽可能对应,但不强制要求。以上图片将在浏览器标签页图片上显示。
您还需要将以下文件放在该目录:
nav_logo_dark.png
以上图片将在页面左上角 logo 显示。 之后前往系统设置,找到 nav_logo_dark
设置项,改为 /nav_logo_dark.png
,重启 Hydro 即可应用更改。
记得清理浏览器缓存。
Note
此操作会删除所有用户/题目/比赛等数据。请谨慎操作!
您可按需更改,显示顺序与配置中的排列顺序相同。
在系统设置中修改 total_time_limit 为允许的单题最大评测时长即可。
在系统设置中找到 footer_extra_html
项,加上如下内容:(写在一行内,不要多加换行)
基于 https://github.com/hustcc/canvas-nest.js ,MIT
<script>(()=>{function e(e,n,t){return e.getAttribute(n)||t}function n(){l=i.width=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,u=i.height=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight}function c(){var t,o,i,a,m;r.clearRect(0,0,l,u),s.forEach(function(e,n){for(e.x+=e.xa,e.y+=e.ya,e.xa*=e.x>l||e.x<0?-1:1,e.ya*=e.y>u||e.y<0?-1:1,r.fillRect(e.x-.5,e.y-.5,1,1),o=n+1;o<d.length;o++)null!==(t=d[o]).x&&null!==t.y&&(i=e.x-t.x,a=e.y-t.y,(m=i*i+a*a)<t.max&&(t===y&&m>=t.max/2&&(e.x-=.03*i,e.y-=.03*a),m=(t.max-m)/t.max,r.beginPath(),r.lineWidth=m/2,r.strokeStyle="rgba("+x.c+","+(.2+m)+")",r.moveTo(e.x,e.y),r.lineTo(t.x,t.y),r.stroke()))}),w(c)}var l,u,d,t,o,i=document.createElement("canvas"),x=(t=(o=document.getElementsByTagName("script")).length,o=o[t-1],{l:t,z:e(o,"zIndex",-1),o:e(o,"opacity",.5),c:e(o,"color","0,0,0"),n:e(o,"count",99)}),a="c_n"+x.l,r=i.getContext("2d"),w=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1e3/45)},m=Math.random,y={x:null,y:null,max:2e4};i.id=a,i.style.cssText="position:fixed;top:0;left:0;z-index:"+x.z+";opacity:"+x.o,document.getElementsByClassName("main")[0].appendChild(i),n(),window.onresize=n,window.onmousemove=function(e){e=e||window.event,y.x=e.clientX,y.y=e.clientY},window.onmouseout=function(){y.x=null,y.y=null};for(var s=[],h=0;x.n>h;h++){var f=m()*l,g=m()*u,p=2*m()-1,v=2*m()-1;s.push({x:f,y:g,xa:p,ya:v,max:6e3})}d=s.concat([y]),setTimeout(function(){c()},100)})();</script>
+
脚本安装默认只装了一小部分编译器。请参照 编译器 章节安装配置其他编译器。
进入 MongoDB,执行下面的操作即可(根据具体情况替换尖括号中的部分):
use hydro
+db.user.update({"_id": <用户 UID>}, {$set: {"badge": "<标签内容>#<背景颜色(HEX)>#<文字颜色(HEX)>"}})
+
请使用现代浏览器进行操作,并尝试给网站设置 https。 否则请选择逐个下载文件(Ctrl+点击文件名)。
题目 -> 评测设置 -> 允许的语言
域设置 -> 编辑域资料 -> 允许的语言
这两个地方,应该填英文逗号分隔的语言ID,不会填请留空。
如果你搞不明白这个问题,请老实用安装脚本,不要折腾开发模式。
方案一: 通过在内网架设代理服务器,将内网 ip 发送至服务端。(推荐)
方案二: 使用形如 hydrooj cli system set limit.user_login 999
的命令设置新的频率限制(可在错误页面查看具体是触发了哪条限制)
方案三: 使用 pm2 start hydrooj -- --benchmark
启动,关闭所有频率限制(不推荐)
使用一键安装脚本安装的 Hydro 使用 PM2 对进程进行管理。
可以通过下面的指令查看当前 PM2 正在管理的所有进程。
pm2 ls
+
一键安装脚本默认会创建下面几个进程:
hydrooj
: Hydro 主进程hydro-sandbox
: Hydro 评测沙箱mongodb
: MongoDB 数据库caddy
: 反向代理后文的指令中将用 <name>
替代此处的进程名称,用 <id>
替代进程 ID(进程 ID 可通过 pm2 ls
查看)。(尖括号同样需要替换)
pm2 ls # 查看进程列表
+pm2 start <id> # 启动进程
+pm2 stop <id> # 关闭进程
+pm2 restart <id> # 重启进程
+pm2 del <id> # 删除进程
+pm2 log <id/name> --lines=100 # 查看该进程的后 100 行日志
+pm2 attach <id> # 与进程交互
+pm2 save # 保存对 PM2 进行的修改(在添加、修改、删除进程后需要执行该指令)
+
在部分环境(常见于 lxc 容器或是精简版系统)下,服务器重启后 Hydro 可能不能正常自启动,这时请使用 pm2 resurrect
手动载入进程列表。
如果手动修改进程列表且已经覆盖掉保存的原列表,请使用 pm2 stop all && pm2 del all
清空所有进程之后重新运行安装脚本。原有数据不会丢失。
Hydro 主进程同样支持多进程启动,但在中低端服务器(不超过4核)中没有必要使用多进程启动 Hydro,会降低性能且成倍提高内存占用。
pm2 start hydrooj -i <n> # 以 n 进程启动 Hydro 主进程
+
Hydro 系统会不定期发布更新,可以使用下面的命令获取更新。
无特殊情况请 不要更新PM2 !此操作可能导致进程列表丢失!
yarn global upgrade-interactive --latest # 在交互式界面中选择想要更新的组件
+pm2 restart hydrooj # 更新完后需重启 hydrooj
+
cd \`yarn global dir\` && yarn list --pattern hydrooj
+
yarn cache clean && nix-collect-garbage
+
Note
迁移会删除当前 Hydro 的所有数据(含用户账户信息)并移入 HUSTOJ 的数据。
请注意备份相关文件。
请先完成 Hydro 的部署并完成对文件服务的配置(setting_file)。 在迁移数据之前,请先停止正在运行的 HUSTOJ 服务,仅保留其数据库开启。 请注意 Hydro 所在的数据库不应和源 HUSTOJ 数据库相同。
安装插件后,您应当能够在 控制面板>脚本管理 中找到名为 migrateHustoj
的脚本。 其参数格式如下:
{"host":"localhost","port":3306,"name":"jol","username":"","password":"","domainId":"system","contestType":"","dataDir":"","uploadDir":""}
+
jol
system
oi
或者 acm
,视情况而定/home/judge/src/web/upload/
,如果此路径与您路径相同,请不要填写此项)当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 HUSTOJ 的管理员账号登录实例。
SYZOJ 与 HUSTOJ 迁移方法类似,迁移脚本应运行名为 migrateSyzoj
的脚本外,SYZOJ无需 contestType
参数。 其参数格式如下:
{"host":"localhost","port":3306,"name":"syzoj","username":"","password":"","domainId":"system","dataDir":""}
+
syzoj
system
由于SYZOJ脚本会将原站所有数据迁移,所以运行耗时较长。 当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 SYZOJ 的管理员账号登录实例。
UniversalOJ (常称作UOJ社区版)与前两者迁移方法类似,迁移脚本应运行名为 migrateUniversalOJ
的脚本。
由于其升级过程较为麻烦,安装脚本已提供自动升级服务,如您需要可运行安装脚本一键迁移,手动迁移请在开发群中提问。
由于UniversalOJ脚本会将原站所有数据迁移,所以运行耗时较长。 当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 UniversalOJ 的管理员账号登录实例。
Note
迁移会删除当前 Hydro 的所有数据(含用户账户信息)并移入 vj4 的数据。
请注意备份相关文件。
请先完成 Hydro 的部署并完成对文件服务的配置(setting_file)。
在迁移数据之前,请先停止正在运行的 vj4 服务,仅保留其数据库开启。
请注意 Hydro 所在的数据库不应和源 vj4 数据库相同。
若您使用 vj4-docker ,可更改 docker-compose.yml
将数据库映射至其他本机端口。
安装插件后,您应当能够在 控制面板>脚本管理 中找到名为 migrateVijos
的脚本。
其参数格式如下:
{"host":"localhost","port":27017,"name":"vj4","username":"","password":""}
+
Tips
vj4-docker 中数据库名为 vj4,无账号密码。
当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 vj4 的管理员账号登录实例。
Note
若您的 vj4 是由 vj2 或 tyvj 升级而来,在迁移完成后请不要卸载该插件,否则可能导致部分用户无法登录。
作者: laomai
qq: 29985091
网址: http://82.157.98.222:8888/
日期: 2022/03/16
本文为作者使用hydro时的实验记录,希望对大家有帮助.包括如下内容
零. hydro题目存储格式
一. 制做最简单的oj题
二. 含有自定义头文件的oj题,即函数交互式的题目
三. 半自动对拍的oj题,即不需要录入预期输出的题
四. 完全对拍题,即不需要录入输入数据和输出数据的题
五. 指定输入文件和输出文件
六. 多个子任务
七. 客观题,即制做有标准答案的填空和选择题.
如果想在本地建立好题目,然后批量上传的话. 下面的格式应该对你有帮助
每个题目应自占一个目录,目录名为题目编号比如1 ,2,3,4等等.
每个题目目录下一般有下面的元素:problem_zh.md
这个文件是就是题目的内容,即题目的描述,是一个markdown格式的文档.probelm.yaml
文件.这个是题目的配置信息. 比如标题和标签等。
testdata 子目录,对应网站里的测试数据部分里的文件,
里面至少有一个config.yaml文件 用来说明测试的类型.具体内容见后面的例子
如果题目有测试用例,则每个用例至少要提供一个in文件和一个输出文件(但有些测试类型不用,详情见后)
additional_file子目录用来存放给做题者用的额外文件,比如头文件,图片,pdf文档等,在题目的 markdown 文档中可以用下面的格式为这些文件提供下载链接:[提示文字](file://xxx.txt)
下面为手工录入各种题型的步骤,即在网站登录后点创建题目之后的操作.如下图所示
我们假定题目内容均为下面的markdown文档
# 要求
+
+输入两个整数,输出他们的和
+
+# 样例
+
+\`\`\`input1
+123 500
+\`\`\`
+
+\`\`\`output1
+623
+\`\`\`
+
题目网址: http://82.157.98.222:8888/p/P10000
1.in
, 表示用例 1 的输入1.in
的内容为两个整数比如 2 和 3 ,空格分开,如下图所示,然后点确定1.out
文件,内容为 5 ,注意数字编号必须和 in 文件一致,创建之后的文件列表和下面类似.#include <iostream>
+using namespace std;
+int main(int argc,char* argv[]){
+ int a,b;
+ cin>>a>>b;
+ cout <<(a+b);
+ return 0;
+}
+
题目网址: http://82.157.98.222:8888/p/P10001
本类型和类型一的区别在于出题者要向做题者提供一个额外的头文件,做题者的主函数里可以包含这个头文件以调用出题者提供的某些函数,或者实现头文件里指定的函数.
tools.h
和 config.yaml
,如下图所示tools.h
的内容为
#include<iostream>
+using namespace std;
+
+int add(int x,int y); //留待做题者实现
+
+int main(int argc,char* argv[]){
+ int a,b;
+ cin>>a>>b;
+ cout << add(a,b);
+ return 0;
+}
+
这个头文件里实现了一个主函数,并且声明了需要做题者实现的函数add,当然,出题者应该在题目要求里写明这个函数的原型以及把tools.h文件上传到附加文件列表中,以方便做题者.
题面里可以用如下格式为用户提供下载链接,中括号内的内容可以自己写,[tools.h](file://tools.h) 如下面所示
config.yaml文件的内容为
type: default
+filename: null
+user_extra_files:
+ - tools.h
+
本题的ac代码为
#include "tools.h"
+int add(int x,int y){
+ return x+y;
+}
+
可见本类型的题目,做题者包含给定的头文件后,可以不需要自己实现主函数,只需要专心实现给定的函数即可.
例题网址:http://82.157.98.222:8888/p/P10002
本题型的特点是不需要手工给出每个用例的预期输出,但是要自己编写一个样本程序,测试时会把用户的输出和样本程序的输出进行对比。
仍以两数求和为例
checker.cc
,内容如下:#include "testlib.h"
+int main(int argc, char * argv[]) {
+ registerTestlibCmd(argc, argv);
+ int a = inf.readInt(); // 读取输入流的第一个整数
+ int b = inf.readInt(); // 读取输入流的下一个整数
+ int d = a+b;
+ int c = ouf.readInt(); // 读取输出流的下一个整数
+ if (a+b != c)
+ quitf(_wa, "%d + %d expected %d, found %d", a, b,d,c); //输出错误的具体信息,便于做题者调试
+ else
+ quitf(_ok, "answer of %d + %d is %d",a,b,c);
+}
+
checker_type: testlib
+checker: checker.cc
+cases:
+ - input: 1.in
+ output: /dev/null # 无输出
+
最终的测试文件列表如下所示:
当程序有错误时,输出的效果如下
可见这里输出了错误细节,便于做题者调试
本题的ac代码和类型一的一样,内容为
#include <iostream>
+using namespace std;
+int main(int argc,char* argv[]){
+ int a,b;
+ cin>>a>>b;
+ cout <<(a+b);
+ return 0;
+}
+
如果不希望自己录入输入数据,而是在每次测试时自动动态生成的话,可以将题目类型设为interactive
,并提供一个对拍程序.仍以求和为例
例题网址: http://82.157.98.222:8888/p/P10005
最后测试数据部分的文件列表如下图所示
checker.cc
,内容为:#include "testlib.h"
+#include <iostream>
+using namespace std;
+
+int main(int argc, char* argv[]) {
+ setName("Interactor A+B");
+ registerInteraction(argc, argv);
+ //自动生成两个随机整数
+ rnd.setSeed(time(NULL));
+ int a = rnd.next(1000);
+ int b = rnd.next(1000);
+ int d = a+b;
+ // 本程序的输出将作为用户程序的输入
+ cout << a << " " << b << endl;
+ int c;
+ // 用户程序的最后输出将作为本程序的输入
+ cin >> c;
+ //对比用户结果和预期结果
+ if (a+b != ans)
+ quitf(_wa, "%d + %d expected %d, found %d", a, b,d,c); //输出错误的具体信息,便于做题者调试
+ else
+ quitf(_ok, "answer of %d + %d is %d",a,b,c);
+}
+
config.yaml
文件的内容为:type: interactive
+interactor: checker.cc
+cases:
+- input: /dev/null # no input and no output, dynamic generated
+ output: /dev/null
+- input: /dev/null # no input and no output, dynamic generated
+ output: /dev/null
+
AC 代码和类型一中的相同
例题网址:http://82.157.98.222:8888/p/P10003
有时希望指定输入和输出文件,此时测试文件 1.in
和 1.out
和类型一类似, 但是要提供config.yaml文件,内容类似于下
file: test
+
则运行时测试环境会自动把每个输入文件复制到test.in中,输出内容和test.out的内容进行对比. ac的代码如下:
#include <fstream>
+using namespace std;
+int main(int argc,char* argv[]){
+ int a,b;
+ ifstream ifs("test.in");
+ ifs>>a>>b;
+ ofstream ofs("test.out");
+ ofs <<(a+b);
+ return 0;
+}
+
例题网址: https://hydro.ac/d/system_test/p/7
data<id>-<数字>
id为子任务编号time: 100ms
+memory: 8m
+subtasks:
+ - score: 20
+ id: 0
+ cases:
+ - input: data1-1.in
+ output: data1-1.ans
+ - input: data1-2.in
+ output: data1-2.ans
+ - input: data1-3.in
+ output: data1-3.ans
+ - input: data1-4.in
+ output: data1-4.ans
+ - input: data1-5.in
+ output: data1-5.ans
+ - score: 20
+ id: 1
+ cases:
+ - input: data2-1.in
+ output: data2-1.ans
+ - input: data2-2.in
+ output: data2-2.ans
+ - input: data2-3.in
+ output: data2-3.ans
+ - input: data2-4.in
+ output: data2-4.ans
+ - input: data2-5.in
+ output: data2-5.ans
+ - score: 20
+ id: 2
+ cases:
+ - input: data3-1.in
+ output: data3-1.ans
+ - input: data3-2.in
+ output: data3-2.ans
+ - input: data3-3.in
+ output: data3-3.ans
+ - input: data3-4.in
+ output: data3-4.ans
+ - input: data3-5.in
+ output: data3-5.ans
+ - score: 20
+ id: 3
+ if: [2]
+ cases:
+ - input: data4-1.in
+ output: data4-1.ans
+ - input: data4-2.in
+ output: data4-2.ans
+ - input: data4-3.in
+ output: data4-3.ans
+ - input: data4-4.in
+ output: data4-4.ans
+ - input: data4-5.in
+ output: data4-5.ans
+ - score: 20
+ id: 4
+ if: [1, 3]
+ cases:
+ - input: data5-1.in
+ output: data5-1.ans
+ - input: data5-2.in
+ output: data5-2.ans
+ - input: data5-3.in
+ output: data5-3.ans
+ - input: data5-4.in
+ output: data5-4.ans
+ - input: data5-5.in
+ output: data5-5.ans
+
可以看出if 用来指定前置子任务.
此外,如果某个子任务没有提供cases部分时,测试时会自动寻找类似于 data<id>-x.in
和data<id>-x.out
的文件,id为子任务编号
上面的例子故意设计为子任务编号和用例文件中的编号不同,所有每个子任务都需要手工指定对应的cases.
注意新版的客观题,格式已经更新。
例题网址:http://82.157.98.222:8888/p/P10004
客观题只需要题面和config.yaml文件. 例子如下:
1. 填空题
+
+1+1 = {{ input(1) }}
+
+2. 选择题
+
+{{ select(2) }}
+- 1+1=2
+- 1+1=3
+- 1+1=4
+
+3. 多选题
+
+{{ multiselect(3) }}
+- A
+- B
+- C
+
+
上传的 config.yaml
内容为
type: objective # 表明该题为客观题
+answers: # 列举出每一题的正确选项与对应的得分
+ '1': ['2', 50]
+ '2': [['A', 'B'], 30] # 填空题支持多答案,满足其一得分
+ '3': [['A', 'B'], 20] # 多选题答案为数组,有部分分
+
+
题目运行效果如下:
做完之后点提交,效果如下
可见评分结果正确.
对所有编程题目,题面是必须录入的,如果指定了测试程序时,可以不需要录入输出数据.
如果设置测试方式为interactive,输入数据也不需要手工录入.
想指特殊的测试方式时,一般需要上传一个config.yaml文件,并设置对应字段的值.
对编程题,本文档中用到的字段有
type字段一般为default, 对全自动对拍题,设为interactive
checker_type: testlib checker: checker.cc 用来指定自定义的测试程序,即对拍程序
filename: test用来指定对test.in文件和test.out文件进行读写.
cases:
用来指定测试用例.
更详细的介绍见 https://hydro.js.org/docs/user/testdata/
',87),v=[C];function u(B,b){return n(),a("div",null,v)}const h=s(d,[["render",u],["__file","problem-create.html.vue"]]);export{h as default}; diff --git a/assets/problem-format.html-147c48c2.js b/assets/problem-format.html-147c48c2.js new file mode 100644 index 00000000..c5527894 --- /dev/null +++ b/assets/problem-format.html-147c48c2.js @@ -0,0 +1,23 @@ +import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as n,c as a,e}from"./app-0efcd172.js";const l={},p=e(`为了便于系统间进行数据交换,Hydro 定义了一种基于 zip 的标准格式用于题目传输。压缩包内文件结构如下:
喵? tree
+.
+├── 任意文件名的文件夹
+│ ├── problem.yaml
+│ ├── problem_zh.md
+│ ├── testdata
+│ │ ├── config.yaml
+│ │ ├── a1.in
+│ │ ├── a1.out
+│ │ ├── a2.in
+│ │ ├── a2.out
+│ │ ├── a3.in
+│ │ └── a3.out
+│ └── additional_file
+│ ├── a.png
+│ └── b.pdf
+└── ...
+
其中 problem.yaml 内容如下:
title: 题目名
+tag:
+- 标签1
+- 标签2
+pid: 题号(字母+数字)
+
problem_*.md 中为 markdown 格式的题面,语言代号支持完整形式(如 zh_CN),也支持短形式(如 zh)。若同时存在多个语言的题面,Hydro 将会自动识别并提供切换功能。
testdata 文件夹中存放所有测试数据文件,命名规则和配置文件格式请参照【测试数据格式】章节。
additional_file 中存储附加文件,通常用于存放图片,PDF 等文件。这些文件可以在题面中使用 file://文件名 的路径访问。
`,8),i=[p];function d(o,c){return n(),a("div",null,i)}const m=s(l,[["render",d],["__file","problem-format.html.vue"]]);export{m as default}; diff --git a/assets/problem-format.html-e4912d61.js b/assets/problem-format.html-e4912d61.js new file mode 100644 index 00000000..3fc43c06 --- /dev/null +++ b/assets/problem-format.html-e4912d61.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-dc52bc6a","path":"/docs/user/problem-format.html","title":"Hydro Problem Format","lang":"en-US","frontmatter":{"description":"为了便于系统间进行数据交换,Hydro 定义了一种基于 zip 的标准格式用于题目传输。压缩包内文件结构如下: 其中 problem.yaml 内容如下: problem*.md 中为 markdown 格式的题面,语言代号支持完整形式(如 zhCN),也支持短形式(如 zh)。若同时存在多个语言的题面,Hydro 将会自动识别并提供切换功能。 tes...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/docs/user/problem-format.html"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"Hydro Problem Format"}],["meta",{"property":"og:description","content":"为了便于系统间进行数据交换,Hydro 定义了一种基于 zip 的标准格式用于题目传输。压缩包内文件结构如下: 其中 problem.yaml 内容如下: problem*.md 中为 markdown 格式的题面,语言代号支持完整形式(如 zhCN),也支持短形式(如 zh)。若同时存在多个语言的题面,Hydro 将会自动识别并提供切换功能。 tes..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-05-19T14:49:26.000Z"}],["meta",{"property":"article:modified_time","content":"2023-05-19T14:49:26.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Hydro Problem Format\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-05-19T14:49:26.000Z\\",\\"author\\":[]}"]]},"headers":[],"git":{"createdTime":1684507766000,"updatedTime":1684507766000,"contributors":[{"name":"undefined","email":"i@undefined.moe","commits":1}]},"readingTime":{"minutes":0.78,"words":233},"filePathRelative":"docs/user/problem-format.md","localizedDate":"May 19, 2023","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/problem.html-4095f91c.js b/assets/problem.html-4095f91c.js new file mode 100644 index 00000000..63ca4562 --- /dev/null +++ b/assets/problem.html-4095f91c.js @@ -0,0 +1,32 @@ +import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as d,c as t,a as n,b as s,d as l,w as p,f as r,e as a}from"./app-0efcd172.js";const E="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOQAAACOCAMAAAAW5lSDAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAolBMVEX////d3d3x8fHbrXxmlMTx8cSTZmZmZmZmZnyu2/HElGZ8rdvx266uxfHxxZNmZpPE8fGTxfF8ZpOTlGbx8duufGZmfK7b8fF8ZmbElJOTrdv//86XZmZmfrXm////57V/ZmaXzv///+a1fmb/zpdmZpfO///mtH9mmc5mZn+15//OmWZ/tObm/+Z/Zn+1zv9/ZpdmfpeXfmaXZn+15+bOmZdUWXSRAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAAd0SU1FB+UIDQQsA+92IUUAAALQSURBVHja7dzbctowEAZgEwPbAE3rJmnTQmp8ADmR06Sn93+1yjQznSnrYUHOWN78ey2MP2tl1pJFFCGUxUhbsMizzmPUZ0sggQQSSCCBBBJIIP9FPO4WOZkSvTkXnvpkOpa0nJGL+clI9/GOkYu37sIxSv4Kkww59+nJi3fvZ+Pu03UyTUQtFx8uJcir67lnuvaIvLpOZjJkEiBy9vGTpKX7ahGyGebMkOwTGbMnxLSM3aUQIXeZfTMPrCdjEqSrux+cyZF/m4eE5A462htotIv9G7EiZPu3t2TH/jjvD/nZXfALSrpELr6ENiZjOqLiESJv3DET1K5AAgkkkEACCSSQr2oRVr4m3XnDYxbEvRoCCSSQQAIJJJBAAhkucrkiuv2qG5muM/U9mReZ/nQtN1v9SFMZort73UhLtZPud6cuZHNjXa5q3enaIPNCN3J341muMuU/IdVzzmouBvKCK3hQuwIJJJA6kLaW3gmpHiayOXcRssxan/+CR5rbh6IWH9JWA03XHMj/pmQy9ci8YGqwI5A9LsKKkenaJ1mH0ZMlZZF2ZMlMxqhD2jpSj9wVPOzcGmpXIIEEEkhtSFOFjnz+VaKTJ5ctUTWMnjQnTy6Xm60dBpJ7epOn60CQxmsGfRhI9jFcG7LkHsOVIdmHA23I9Nu9fqRhJ410IflsVYZsmeJE7QokkEACCSSQQAJ56OPYCfuyh/TsciCBBBJIIIEEEkggD7RM17WgYbNplXuLh9v6SUQtuwZ7Q1qSINPHKDKifYDp0za4niy//6iFh5StmJab4JDLn7+KjpFeb8VGL/KifZ2LkVa0LGw8h2T3SFtFQqThd0y0XI6QbjzN2pm8Jw0J32JoW6/qBdn8LhCXXfLdBOEjW0/IF+m176A/5O8t/xo639KGVgzIkIakFY8ldn8ralcggQQSSCCBBBLIQ8jX8HfECAQCgUAcF38AwrhosQM5iFAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDgtMTNUMDQ6NDI6MzUrMDA6MDAUWYFrAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTA4LTEzVDA0OjQyOjM1KzAwOjAwZQQ51wAAAABJRU5ErkJggg==",F={},y=a('拥有 PERM_CREATE_PROBLEM 的用户均可以新建题目。
请点击题库页面右下角的 创建题目
按钮。
Tips
题目 ID 不能全为数字。若留空则使用自动分配的数字题号。
上传 Hydro 导出的题目压缩包即可。
如果您的压缩包较大无法上传我们也提供cli导入方法:
hydrooj cli problem import <domainId> <file/path> # 将 <file(压缩文件)/path(解压后的文件夹)> 的Hydro格式题目包导入至 <domainId> 域中。
+
题面使用 Markdown 语法,并进行了部分扩展。
支持对样例数据分组显示:
```input1\n1 2\n```\n\n```output1\n3\n```
后接的数字为测试点编号,将自动合并,并左右分栏显示。
支持从附加文件引用资源。(您可以先创建题目,上传相关文件后再编辑该题目)
[file](file://input.in)
![img](file://foo.jpg)
@[pdf](file://foo.pdf)
(部分情况下若无法使用,请尝试 @[pdf](file://foo.pdf?noDisposition=1)
)@[doc](file://foo.docx)
(依赖 onlyoffice 插件)题面支持合并表格:
| 1 | 1 | 3 | 4 | 5 |
+| --- | --- | --- | --- | --- |
+| 1 | 1 | 2 | 2 | 6 |
+| 1 | 1 | 2 | 2 | 7 |
+| 1 | 4 | 3 | 5 | 5 |
+
将被渲染为:
支持内嵌 HTML:(用来对付部分 Markdown 搞不定的东西)
<span bgcolor="red">foo</span>
+
可点击右侧分类面板快速添加标签,也可以用英文半角逗号分隔多个标签。
您可以在题目右侧“文件”面板上传测试数据和附加文件。(支持拖拽文件至相应位置进行上传)
测试数据格式。
1. 填空题
+
+1+1 = {{ input(1) }}
+
+2. 选择题
+
+{{ select(2) }}
+- 1+1=2
+- 1+1=3
+- 1+1=4
+
+3. 多选题
+
+{{ multiselect(3) }}
+- A
+- B
+- C
+
仅需要配置 config.yaml 即可,不需要上传其他文件。
type: objective # 表明该题为客观题
+answers: # 列举出每一题的正确选项与对应的得分
+ '1': ['2', 50] # 填空题/选择题,单答案
+ '2': # 填空题/选择题,多答案,不同答案对应不同分数,注意空格缩进
+ 'A': 30 # 也可以使用相同分数,即同时存在多个正确答案
+ 'B': 10
+ '3': [['A', 'B'], 20] # 多选题答案为数组,有部分分
+
~/.hydro/Caddyfile
的配置文件!Note
若使用反向代理,请注意系统设置中的 server.xff 和 server.xhost 设置项需要填写正确(小写),分别对应反向代理所添加的标头名(通常为 x-forwarded-for 和 x-forwarded-host,部分反代工具会使用 x-real-ip 替代 x-forwarded-for)。
server.xhost 设置项配置错误会导致用户无法登录等问题。(CsrfTokenError)
server.xff 设置项配置错误会导致无法记录用户IP。
除一键安装脚本在安装 Caddy 后会自动配置设置项,其他工具请在使用工具前先配置好系统设置!否则将造成用户无法登录、无法记录用户IP等问题。
若您使用 Nginx,请注意配置 WebSocket 协议的反向代理,否则会导致评测状态无法自动刷新,在线 IDE 无法正常使用等问题。
hydro.ac {
+ log {
+ output file /data/access.log {
+ roll_size 1gb
+ roll_keep_for 72h
+ }
+ format json
+ }
+ root * /root/.hydro/static
+ @static {
+ file {
+ try_files {path}
+ }
+ }
+ handle @static {
+ file_server
+ }
+ handle {
+ reverse_proxy http://127.0.0.1:8888
+ }
+}
+
Tips
使用一键安装脚本部署的 Hydro 一般已自动完成存储配置。
文件默认存储于本地的 /data/file/hydro
目录下,切换存储后端时需要手动复制或移动原有文件。
请根据你使用的存储端类型阅读对应教程。(同时欢迎使用其他存储类型的用户向我们提供详细的存储教程)
MinIO
Note
新版本 MinIO 占用较大,如果您为轻量使用,我们不建议使用 MinIO 。旧版本 Hydro 用户大部分可直接切换至 file 模式,由 Hydro 代替读取。
file.type
: s3
file.endPoint
: http://127.0.0.1:9000
(即 MinIO 启动时显示的 endPoint)file.accessKey
: 参照 MinIO 配置file.secretKey
: 参照 MinIO 配置file.bucket
: hydro
(MinIO 内部存储桶名称)file.region
: us-east-1
file.pathStyle
: truefile.endPointForUser
: /fs/
file.endPointForJudge
: /fs/
保存后重启,已有文件请自行复制。
腾讯云COS
进入 控制面板>系统设置>存储桶设置。
file.type
: s3
file.endPoint
: http://cos.<存储桶地域>.myqcloud.com
(或是 https)file.accessKey
: 您的腾讯云 API 密钥的 SecretIdfile.secretKey
: 您的腾讯云 API 密钥的 SecretKeyfile.bucket
: <存储桶名称>file.region
: Autofile.pathStyle
: truefile.endPointForUser
: 给用户使用的 EndPoint,若填 /fs/
则表示由服务器转发file.endPointForJudge
: 给 judge 使用的 EndPoint,若填 /fs/
则表示由服务器转发保存后重启,已有文件请自行复制。
阿里云OSS
// TODO & PRs Welcome
",8);function h(_,u){const c=t("ExternalLinkIcon");return l(),n("div",null,[a,e("p",null,[e("a",p,[o("安装 MinIO"),r(c)]),o(" 后进入 控制面板>manage_config。")]),f])}const y=d(s,[["render",h],["__file","s3.html.vue"]]);export{y as default}; diff --git a/assets/s3.html-96da8c94.js b/assets/s3.html-96da8c94.js new file mode 100644 index 00000000..ca72799c --- /dev/null +++ b/assets/s3.html-96da8c94.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6b4a62ea","path":"/docs/install/s3.html","title":"存储","lang":"en-US","frontmatter":{"description":"使用一键安装脚本部署的 Hydro 一般已自动完成存储配置。 文件默认存储于本地的 /data/file/hydro 目录下,切换存储后端时需要手动复制或移动原有文件。 请根据你使用的存储端类型阅读对应教程。(同时欢迎使用其他存储类型的用户向我们提供详细的存储教程) MinIO 新版本 MinIO 占用较大,如果您为轻量使用,我们不建议使用 MinIO...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/docs/install/s3.html"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"存储"}],["meta",{"property":"og:description","content":"使用一键安装脚本部署的 Hydro 一般已自动完成存储配置。 文件默认存储于本地的 /data/file/hydro 目录下,切换存储后端时需要手动复制或移动原有文件。 请根据你使用的存储端类型阅读对应教程。(同时欢迎使用其他存储类型的用户向我们提供详细的存储教程) MinIO 新版本 MinIO 占用较大,如果您为轻量使用,我们不建议使用 MinIO..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-05-12T03:32:53.000Z"}],["meta",{"property":"article:modified_time","content":"2023-05-12T03:32:53.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"存储\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-05-12T03:32:53.000Z\\",\\"author\\":[]}"]]},"headers":[],"git":{"createdTime":1631714121000,"updatedTime":1683862373000,"contributors":[{"name":"Macesuted","email":"macesuted@qq.com","commits":5},{"name":"undefined","email":"i@undefined.moe","commits":3},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":2}]},"readingTime":{"minutes":1.18,"words":355},"filePathRelative":"docs/install/s3.md","localizedDate":"September 15, 2021","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/smtp.html-00b75177.js b/assets/smtp.html-00b75177.js new file mode 100644 index 00000000..163a8de8 --- /dev/null +++ b/assets/smtp.html-00b75177.js @@ -0,0 +1 @@ +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as e,e as t}from"./app-0efcd172.js";const o={},a=t('以 QQ 邮箱为例。
已知能够完整兼容的服务商有:
如果使用其他服务商且没有发现问题,欢迎向此列表 Pull Request。
',6),s=[a];function _(c,r){return i(),e("div",null,s)}const S=l(o,[["render",_],["__file","smtp.html.vue"]]);export{S as default}; diff --git a/assets/smtp.html-b6526407.js b/assets/smtp.html-b6526407.js new file mode 100644 index 00000000..a948c03f --- /dev/null +++ b/assets/smtp.html-b6526407.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-68002798","path":"/docs/install/smtp.html","title":"SMTP","lang":"en-US","frontmatter":{"description":"以 QQ 邮箱为例。 SMTP_USER: 12345678@qq.com ; SMTP_PASS: 提供的 SMTP 密码 ; SMTP_HOST: smtp.qq.com ; SMTP_PORT: 465/587 (请参照邮件服务商说明) ; SMTP_SECURE: 是否使用加密 TLS 连接(请参照邮件服务商说明,使用 STARTTLS 的服务...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/docs/install/smtp.html"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"SMTP"}],["meta",{"property":"og:description","content":"以 QQ 邮箱为例。 SMTP_USER: 12345678@qq.com ; SMTP_PASS: 提供的 SMTP 密码 ; SMTP_HOST: smtp.qq.com ; SMTP_PORT: 465/587 (请参照邮件服务商说明) ; SMTP_SECURE: 是否使用加密 TLS 连接(请参照邮件服务商说明,使用 STARTTLS 的服务..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2022-12-09T10:31:58.000Z"}],["meta",{"property":"article:modified_time","content":"2022-12-09T10:31:58.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"SMTP\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2022-12-09T10:31:58.000Z\\",\\"author\\":[]}"]]},"headers":[],"git":{"createdTime":1631785934000,"updatedTime":1670581918000,"contributors":[{"name":"undefined","email":"i@undefined.moe","commits":2},{"name":"Macesuted","email":"macesuted@qq.com","commits":1}]},"readingTime":{"minutes":0.5,"words":151},"filePathRelative":"docs/install/smtp.md","localizedDate":"September 16, 2021","autoDesc":true,"excerpt":""}');export{t as data}; diff --git a/assets/sonic.html-334946eb.js b/assets/sonic.html-334946eb.js new file mode 100644 index 00000000..98b53049 --- /dev/null +++ b/assets/sonic.html-334946eb.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-52692a96","path":"/plugins/sonic.html","title":"Sonic","lang":"en-US","frontmatter":{"description":"安装 安装 sonic-server (https://github.com/valeriansaliou/sonic) 使用 root 用户执行如下命令: 安装 sonic 插件 使用 root 用户执行如下命令: 启动 在 /root/.sonic/config.cfg(没有的自行建立,也可以换成其他的你喜欢的路径)按照以下配置示例写入配置。 配置...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/plugins/sonic.html"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"Sonic"}],["meta",{"property":"og:description","content":"安装 安装 sonic-server (https://github.com/valeriansaliou/sonic) 使用 root 用户执行如下命令: 安装 sonic 插件 使用 root 用户执行如下命令: 启动 在 /root/.sonic/config.cfg(没有的自行建立,也可以换成其他的你喜欢的路径)按照以下配置示例写入配置。 配置..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-02-25T05:29:05.000Z"}],["meta",{"property":"article:modified_time","content":"2023-02-25T05:29:05.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Sonic\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-02-25T05:29:05.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[{"level":3,"title":"安装 sonic-server","slug":"安装-sonic-server","link":"#安装-sonic-server","children":[]},{"level":3,"title":"安装 sonic 插件","slug":"安装-sonic-插件","link":"#安装-sonic-插件","children":[]}]},{"level":2,"title":"启动","slug":"启动","link":"#启动","children":[]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[{"level":3,"title":"后端地址配置","slug":"后端地址配置","link":"#后端地址配置","children":[]},{"level":3,"title":"重启 HydroOJ","slug":"重启-hydrooj","link":"#重启-hydrooj","children":[]},{"level":3,"title":"重建题目索引","slug":"重建题目索引","link":"#重建题目索引","children":[]}]},{"level":2,"title":"FAQ","slug":"faq","link":"#faq","children":[{"level":3,"title":"安装后题目搜索不正常","slug":"安装后题目搜索不正常","link":"#安装后题目搜索不正常","children":[]}]}],"git":{"createdTime":1621263423000,"updatedTime":1677302945000,"contributors":[{"name":"panda","email":"panda_dtdyy@outlook.com","commits":2},{"name":"undefined","email":"i@undefined.moe","commits":2},{"name":"立言","email":"liyanqwq@duianit.cn","commits":2}]},"readingTime":{"minutes":1.11,"words":333},"filePathRelative":"plugins/sonic.md","localizedDate":"May 17, 2021","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/sonic.html-fca0b4b5.js b/assets/sonic.html-fca0b4b5.js new file mode 100644 index 00000000..23bfaa4f --- /dev/null +++ b/assets/sonic.html-fca0b4b5.js @@ -0,0 +1,72 @@ +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as i,c as d,a as s,b as n,d as p,e as c}from"./app-0efcd172.js";const o={},r=s("h1",{id:"sonic",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#sonic","aria-hidden":"true"},"#"),n(" Sonic")],-1),t=s("h2",{id:"安装",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#安装","aria-hidden":"true"},"#"),n(" 安装")],-1),f={id:"安装-sonic-server",tabindex:"-1"},v=s("a",{class:"header-anchor",href:"#安装-sonic-server","aria-hidden":"true"},"#",-1),u={href:"https://github.com/valeriansaliou/sonic",target:"_blank",rel:"noopener noreferrer"},m=c(`使用 root 用户执行如下命令:
nix-env -iA nixpkgs.sonic-server
+
使用 root 用户执行如下命令:
yarn global add @hydrooj/sonic
+hydrooj addon add @hydrooj/sonic
+
在 /root/.sonic/config.cfg
(没有的自行建立,也可以换成其他的你喜欢的路径)按照以下配置示例写入配置。
配置示例:
# Sonic
+# Fast, lightweight and schema-less search backend
+# Configuration file
+# Example: https://github.com/valeriansaliou/sonic/blob/master/config.cfg
+
+
+[server]
+
+log_level = "error"
+
+
+[channel]
+
+inet = "127.0.0.1:1491" # 默认监听本机
+tcp_timeout = 300
+
+auth_password = "SecretPassword"
+
+[channel.search]
+
+query_limit_default = 10
+query_limit_maximum = 100
+query_alternates_try = 4
+
+suggest_limit_default = 5
+suggest_limit_maximum = 20
+
+
+[store]
+
+[store.kv]
+
+path = "/data/sonic/store/kv/"
+
+retain_word_objects = 1000
+
+[store.kv.pool]
+
+inactive_after = 1800
+
+[store.kv.database]
+
+flush_after = 900
+
+compress = true
+parallelism = 2
+max_files = 100
+max_compactions = 1
+max_flushes = 1
+write_buffer = 16384
+write_ahead_log = true
+
+[store.fst]
+
+path = "/data/sonic/store/fst/"
+
+[store.fst.pool]
+
+inactive_after = 300
+
+[store.fst.graph]
+
+consolidate_after = 180
+
+max_size = 2048
+max_words = 250000
+
执行如下命令:
pm2 start sonic -- -c /root/.sonic/config.cfg
+pm2 save
+
进入 HydroOJ 控制面板,配置 sonic 后端地址。
如果您直接复制配置示例,那么按照以下内容配置:
127.0.0.1
1491
SecretPassword
修改完成后,重启 HydroOJ。
执行命令 pm2 restart hydrooj
。
进入 HydroOJ 控制面板,在脚本管理中找到重建题目索引,点击运行,参数留空即可。
至此,搜索功能应当可以正常使用。
请更新到 HydroOJ 最新版本后,再运行重建题目索引。
`,25);function b(h,y){const e=l("ExternalLinkIcon");return i(),d("div",null,[r,t,s("h3",f,[v,n(" 安装 "),s("a",u,[n("sonic-server"),p(e)])]),m])}const g=a(o,[["render",b],["__file","sonic.html.vue"]]);export{g as default}; diff --git a/assets/style-3c7defdd.css b/assets/style-3c7defdd.css new file mode 100644 index 00000000..157224cc --- /dev/null +++ b/assets/style-3c7defdd.css @@ -0,0 +1 @@ +html[data-theme=dark]{--text-color: #9e9e9e;--bg-color: #0d1117;--bg-color-secondary: #161b22;--bg-color-tertiary: #21262c;--border-color: #30363d;--box-shadow: #282a32;--card-shadow: rgba(0, 0, 0, .3);--black: #fff;--dark-grey: #999;--light-grey: #666;--white: #000;--grey3: #bbb;--grey12: #333;--grey14: #111;--bg-color-light: #161b22;--bg-color-back: #0d1117;--bg-color-float: #161b22;--bg-color-blur: rgba(13, 17, 23, .9);--bg-color-float-blur: rgba(22, 27, 34, .9);--text-color-light: #a8a8a8;--text-color-lighter: #b1b1b1;--text-color-bright: #c5c5c5;--border-color-light: #2e333a;--border-color-dark: #394048}:root{--theme-color: #3eaf7c;--text-color: #2c3e50;--bg-color: #fff;--bg-color-secondary: #f8f8f8;--bg-color-tertiary: #efeef4;--border-color: #eaecef;--box-shadow: #f0f1f2;--card-shadow: rgba(0, 0, 0, .15);--black: #000;--dark-grey: #666;--light-grey: #999;--white: #fff;--grey3: #333;--grey12: #bbb;--grey14: #eee;--navbar-height: 3.75rem;--navbar-horizontal-padding: 1.5rem;--navbar-vertical-padding: .7rem;--navbar-mobile-height: 3.25rem;--navbar-mobile-horizontal-padding: 1rem;--navbar-mobile-vertical-padding: .5rem;--sidebar-width: 18rem;--sidebar-mobile-width: 16rem;--content-width: 780px;--home-page-width: 1160px;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", STHeiti, "Microsoft YaHei", SimSun, sans-serif;--font-family-heading: Georgia Pro, Crimson, Georgia, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", STHeiti, "Microsoft YaHei", SimSun, sans-serif;--font-family-mono: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--line-numbers-width: 2.5rem;--color-transition: .3s ease;--transform-transition: .3s ease;--vp-bg: var(--bg-color);--vp-bgl: var(--bg-color-light);--vp-bglt: var(--bg-color-tertiary);--vp-c: var(--text-color);--vp-cl: var(--text-color-light);--vp-clt: var(--text-color-lighter);--vp-brc: var(--border-color);--vp-brcd: var(--border-color-dark);--vp-tc: var(--theme-color);--vp-tcl: var(--theme-color-light);--vp-ct: var(--color-transition);--vp-tt: var(--transform-transition);--bg-color-light: #fff;--bg-color-back: #f8f8f8;--bg-color-float: #fff;--bg-color-blur: rgba(255, 255, 255, .9);--bg-color-float-blur: rgba(255, 255, 255, .9);--text-color-light: #3a5169;--text-color-lighter: #476582;--text-color-bright: #6a8bad;--border-color-light: #eceef1;--border-color-dark: #cfd4db;--theme-color-dark: #389e70;--theme-color-light: #4abf8a;--theme-color-mask: rgba(62, 175, 124, .15)}:root{--badge-tip-color: #42b983;--badge-warning-color: #f4cd00;--badge-danger-color: #f55;--badge-info-color: #0295ff;--badge-note-color: #666}.badge{display:inline-block;vertical-align:top;height:18px;padding:0 6px;border-radius:3px;background:var(--vp-tc);color:var(--white);font-size:14px;line-height:18px;transition:background var(--vp-ct),color var(--vp-ct)}.badge+.badge{-webkit-margin-start:5px;margin-inline-start:5px}.table-of-contents .badge,#toc .badge{vertical-align:middle}.badge.tip{background:var(--badge-tip-color)}.badge.warning{background:var(--badge-warning-color)}.badge.danger{background:var(--badge-danger-color)}.badge.info{background:var(--badge-info-color)}.badge.note{background:var(--badge-note-color)}.font-icon{display:inline-block}.theme-hope-content .font-icon{vertical-align:middle}:root{--balloon-border-radius: 2px;--balloon-color: rgba(16, 16, 16, .95);--balloon-text-color: #fff;--balloon-font-size: 12px;--balloon-move: 4px}button[aria-label][data-balloon-pos]{overflow:visible}[aria-label][data-balloon-pos]{position:relative;cursor:pointer}[aria-label][data-balloon-pos]:after{opacity:0;pointer-events:none;transition:all .18s ease-out .18s;text-indent:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-weight:400;font-style:normal;text-shadow:none;font-size:var(--balloon-font-size);background:var(--balloon-color);border-radius:2px;color:var(--balloon-text-color);border-radius:var(--balloon-border-radius);content:attr(aria-label);padding:.5em 1em;position:absolute;white-space:nowrap;z-index:10}[aria-label][data-balloon-pos]:before{width:0;height:0;border:5px solid transparent;border-top-color:var(--balloon-color);opacity:0;pointer-events:none;transition:all .18s ease-out .18s;content:"";position:absolute;z-index:10}[aria-label][data-balloon-pos]:hover:before,[aria-label][data-balloon-pos]:hover:after,[aria-label][data-balloon-pos][data-balloon-visible]:before,[aria-label][data-balloon-pos][data-balloon-visible]:after,[aria-label][data-balloon-pos]:not([data-balloon-nofocus]):focus:before,[aria-label][data-balloon-pos]:not([data-balloon-nofocus]):focus:after{opacity:1;pointer-events:none}[aria-label][data-balloon-pos].font-awesome:after{font-family:FontAwesome,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif}[aria-label][data-balloon-pos][data-balloon-break]:after{white-space:pre}[aria-label][data-balloon-pos][data-balloon-break][data-balloon-length]:after{white-space:pre-line;word-break:break-word}[aria-label][data-balloon-pos][data-balloon-blunt]:before,[aria-label][data-balloon-pos][data-balloon-blunt]:after{transition:none}[aria-label][data-balloon-pos][data-balloon-pos=up]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=up][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=down]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=down][data-balloon-visible]:after{transform:translate(-50%)}[aria-label][data-balloon-pos][data-balloon-pos=up]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=up][data-balloon-visible]:before,[aria-label][data-balloon-pos][data-balloon-pos=down]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=down][data-balloon-visible]:before{transform:translate(-50%)}[aria-label][data-balloon-pos][data-balloon-pos*=-left]:after{left:0}[aria-label][data-balloon-pos][data-balloon-pos*=-left]:before{left:5px}[aria-label][data-balloon-pos][data-balloon-pos*=-right]:after{right:0}[aria-label][data-balloon-pos][data-balloon-pos*=-right]:before{right:5px}[aria-label][data-balloon-pos][data-balloon-po*=-left]:hover:after,[aria-label][data-balloon-pos][data-balloon-po*=-left][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos*=-right]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos*=-right][data-balloon-visible]:after{transform:translate(0)}[aria-label][data-balloon-pos][data-balloon-po*=-left]:hover:before,[aria-label][data-balloon-pos][data-balloon-po*=-left][data-balloon-visible]:before,[aria-label][data-balloon-pos][data-balloon-pos*=-right]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos*=-right][data-balloon-visible]:before{transform:translate(0)}[aria-label][data-balloon-pos][data-balloon-pos^=up]:before,[aria-label][data-balloon-pos][data-balloon-pos^=up]:after{bottom:100%;transform-origin:top;transform:translateY(var(--balloon-move))}[aria-label][data-balloon-pos][data-balloon-pos^=up]:after{margin-bottom:10px}[aria-label][data-balloon-pos][data-balloon-pos=up]:before,[aria-label][data-balloon-pos][data-balloon-pos=up]:after{left:50%;transform:translate(-50%,var(--balloon-move))}[aria-label][data-balloon-pos][data-balloon-pos^=down]:before,[aria-label][data-balloon-pos][data-balloon-pos^=down]:after{top:100%;transform:translateY(calc(var(--balloon-move) * -1))}[aria-label][data-balloon-pos][data-balloon-pos^=down]:after{margin-top:10px}[aria-label][data-balloon-pos][data-balloon-pos^=down]:before{width:0;height:0;border:5px solid transparent;border-bottom-color:var(--balloon-color)}[aria-label][data-balloon-pos][data-balloon-pos=down]:after,[aria-label][data-balloon-pos][data-balloon-pos=down]:before{left:50%;transform:translate(-50%,calc(var(--balloon-move) * -1))}[aria-label][data-balloon-pos][data-balloon-pos=left]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=left][data-balloon-visible]:after,[aria-label][data-balloon-pos][data-balloon-pos=right]:hover:after,[aria-label][data-balloon-pos][data-balloon-pos=right][data-balloon-visible]:after{transform:translateY(-50%)}[aria-label][data-balloon-pos][data-balloon-pos=left]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=left][data-balloon-visible]:before,[aria-label][data-balloon-pos][data-balloon-pos=right]:hover:before,[aria-label][data-balloon-pos][data-balloon-pos=right][data-balloon-visible]:before{transform:translateY(-50%)}[aria-label][data-balloon-pos][data-balloon-pos=left]:after,[aria-label][data-balloon-pos][data-balloon-pos=left]:before{right:100%;top:50%;transform:translate(var(--balloon-move),-50%)}[aria-label][data-balloon-pos][data-balloon-pos=left]:after{margin-right:10px}[aria-label][data-balloon-pos][data-balloon-pos=left]:before{width:0;height:0;border:5px solid transparent;border-left-color:var(--balloon-color)}[aria-label][data-balloon-pos][data-balloon-pos=right]:after,[aria-label][data-balloon-pos][data-balloon-pos=right]:before{left:100%;top:50%;transform:translate(calc(var(--balloon-move) * -1),-50%)}[aria-label][data-balloon-pos][data-balloon-pos=right]:after{margin-left:10px}[aria-label][data-balloon-pos][data-balloon-pos=right]:before{width:0;height:0;border:5px solid transparent;border-right-color:var(--balloon-color)}[aria-label][data-balloon-pos][data-balloon-length]:after{white-space:normal}[aria-label][data-balloon-pos][data-balloon-length=small]:after{width:80px}[aria-label][data-balloon-pos][data-balloon-length=medium]:after{width:150px}[aria-label][data-balloon-pos][data-balloon-length=large]:after{width:260px}[aria-label][data-balloon-pos][data-balloon-length=xlarge]:after{width:380px}@media screen and (max-width: 768px){[aria-label][data-balloon-pos][data-balloon-length=xlarge]:after{width:90vw}}[aria-label][data-balloon-pos][data-balloon-length=fit]:after{width:100%}.back-to-top{border-width:0;background:transparent;cursor:pointer;position:fixed!important;right:16px;bottom:64px;z-index:100;width:48px;height:48px;padding:8px;border-radius:50%;background:var(--vp-bg);color:var(--vp-tc);box-shadow:2px 2px 10px 4px var(--card-shadow);transition:background var(--vp-ct),color var(--vp-ct),box-shadow var(--vp-ct)}@media (max-width: 719px){.back-to-top{width:36px;height:36px}}@media print{.back-to-top{display:none}}html[dir=rtl] .back-to-top{right:unset;left:16px}.back-to-top:hover{color:var(--vp-tcl)}.back-to-top .back-to-top-icon{overflow:hidden;width:100%;border-radius:50%;fill:currentcolor}.scroll-progress{position:absolute;right:-2px;bottom:-2px;width:52px;height:52px}@media (max-width: 719px){.scroll-progress{width:40px;height:40px}}.scroll-progress circle{opacity:.9;fill:none;stroke:var(--vp-tc);transform:rotate(-90deg);transform-origin:50% 50%;r:22;stroke-dasharray:0% 314.1593%;stroke-width:3px}@media (max-width: 719px){.scroll-progress circle{r:18}}.fade-enter-active,.fade-leave-active{transition:opacity var(--vp-ct)}.fade-enter-from,.fade-leave-to{opacity:0}@media screen{.sr-only{position:absolute;overflow:hidden;clip:rect 0,0,0,0;width:1px;height:1px;margin:-1px;padding:0;border:0}}@media print{.sr-only{display:none}}.auto-catalog-wrapper{margin-top:8px;margin-bottom:8px}.auto-catalog-wrapper .catalog-title{color:inherit;text-decoration:none}.auto-catalog-wrapper .catalog-title:hover{color:var(--vp-tc)}.auto-catalog-wrapper .main-title{font-size:1.75rem}.auto-catalog-wrapper .child-title{font-size:1.3rem}.auto-catalog-wrapper .child-title.has-children{border-bottom:1px solid var(--vp-brc);transition:border-color var(--vp-ct)}.auto-catalog-wrapper .sub-title{color:var(--vp-clt);font-size:1.1rem}.auto-catalog-wrapper .main-title,.auto-catalog-wrapper .child-title,.auto-catalog-wrapper .sub-title{margin-top:calc(.5rem - var(--navbar-height, 3.6rem));margin-bottom:.5rem;padding-top:var(--navbar-height, 3.6rem);font-weight:500}.auto-catalog-wrapper .main-title:first-child,.auto-catalog-wrapper .child-title:first-child,.auto-catalog-wrapper .sub-title:first-child{margin-bottom:.5rem}.auto-catalog-wrapper .font-icon{vertical-align:baseline;-webkit-margin-end:.25rem;margin-inline-end:.25rem}.auto-catalog-wrapper .child-catalog-wrapper{margin:0}.auto-catalog-wrapper .child-catalog-item::marker{color:var(--vp-clt)}.auto-catalog-wrapper .sub-catalog-wrapper{display:flex;flex-wrap:wrap}.auto-catalog-wrapper .sub-catalog-item{display:inline-block;margin:4px 8px;padding:4px 8px;border-radius:6px;background-color:var(--vp-bgl);line-height:1.5;overflow-wrap:break-word;transition:background-color var(--vp-ct),color var(--vp-ct)}.auto-catalog-wrapper .sub-catalog-item:hover{background-color:var(--vp-tcl);color:var(--vp-bg);text-decoration:none}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}:root{--copy-icon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");--copied-icon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E")}div[class*=language-]>button.copy-code-button{border-width:0;background:transparent;position:absolute;outline:none;cursor:pointer}@media print{div[class*=language-]>button.copy-code-button{display:none}}div[class*=language-]>button.copy-code-button .copy-icon{background:currentcolor;-webkit-mask-image:var(--copy-icon);mask-image:var(--copy-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:1em;mask-size:1em}div[class*=language-]>button.copy-code-button:not(.fancy){border-width:0;background:transparent;cursor:pointer;position:absolute;top:0;right:3em;z-index:5;width:2.5rem;height:2.5rem;padding:0;border-radius:.5rem;opacity:0;transition:opacity .4s}div[class*=language-]>button.copy-code-button:not(.fancy):hover{background:var(--code-hl-bg-color, rgba(0, 0, 0, .66))}div[class*=language-]>button.copy-code-button:not(.fancy).copied:after{content:attr(data-copied);position:absolute;top:0;right:100%;display:block;height:1.25rem;padding:.625rem;border-radius:.5rem;background:var(--code-hl-bg-color, rgba(0, 0, 0, .66));color:var(--code-ln-color, #9e9e9e);font-weight:500;line-height:1.25rem;white-space:nowrap}div[class*=language-]>button.copy-code-button:not(.fancy) .copy-icon{width:1.25rem;height:1.25rem;padding:.625rem;color:var(--code-ln-color, #9e9e9e);font-size:1.25rem}div[class*=language-]>button.copy-code-button.fancy{right:-14px;bottom:-14px;z-index:5;width:2rem;height:2rem;padding:7px 8px;border-radius:50%;background:#339af0;color:#fff}@media (max-width: 419px){div[class*=language-]>button.copy-code-button.fancy{right:0;bottom:0;width:28px;height:28px;border-radius:50% 10% 0}}div[class*=language-]>button.copy-code-button.fancy:hover{background:#228be6}div[class*=language-]>button.copy-code-button.fancy .copy-icon{width:100%;height:100%;color:#fff;font-size:1.25rem}@media (max-width: 419px){div[class*=language-]>button.copy-code-button.fancy .copy-icon{position:relative;top:2px;left:2px}}div[class*=language-]>button.copy-code-button.copied .copy-icon{-webkit-mask-image:var(--copied-icon);mask-image:var(--copied-icon)}div[class*=language-]:hover>button.copy-code-button:not(.fancy),div[class*=language-]>button.copy-code-button:not(.fancy):focus{opacity:1}:root{--info-title-color: #193c47;--info-bg-color: #eef9fd;--info-border-color: #4cb3d4;--info-code-bg-color: rgb(76 179 212 / 10%);--note-title-color: #474748;--note-bg-color: #fdfdfe;--note-border-color: #ccc;--note-code-bg-color: rgb(212 213 216 / 20%);--tip-title-color: #003100;--tip-bg-color: #e6f6e6;--tip-border-color: #009400;--tip-code-bg-color: rgb(0 148 0 / 15%);--warning-title-color: #4d3800;--warning-bg-color: #fff8e6;--warning-border-color: #e6a700;--warning-code-bg-color: rgb(230 167 0 / 15%);--danger-title-color: #4b1113;--danger-bg-color: #ffebec;--danger-border-color: #e13238;--danger-code-bg-color: rgb(225 50 56 / 15%);--detail-bg-color: #eee;--detail-text-color: inherit;--detail-code-bg-color: rgb(127 127 127 / 15%)}html[data-theme=dark]{--info-title-color: #eef9fd;--info-bg-color: #193c47;--note-title-color: #fdfdfe;--note-bg-color: #474748;--tip-title-color: #e6f6e6;--tip-bg-color: #003100;--warning-title-color: #fff8e6;--warning-bg-color: #4d3800;--danger-title-color: #ffebec;--danger-bg-color: #4b1113;--detail-bg-color: #333;--detail-text-color: #a8a8a8}.hint-container{position:relative;transition:background var(--vp-ct),border-color var(--vp-ct),color var(--vp-ct)}@media print{.hint-container{page-break-inside:avoid}}.hint-container .hint-container-title{position:relative;font-weight:600;line-height:1.25}.hint-container.info,.hint-container.note,.hint-container.tip,.hint-container.warning,.hint-container.danger{margin:1rem 0;padding:.25rem 1rem;border-inline-start-width:.3rem;border-inline-start-style:solid;border-radius:.5rem;color:inherit}.hint-container.info .hint-container-title,.hint-container.note .hint-container-title,.hint-container.tip .hint-container-title,.hint-container.warning .hint-container-title,.hint-container.danger .hint-container-title{-webkit-padding-start:1.75rem;padding-inline-start:1.75rem}@media print{.hint-container.info .hint-container-title,.hint-container.note .hint-container-title,.hint-container.tip .hint-container-title,.hint-container.warning .hint-container-title,.hint-container.danger .hint-container-title{-webkit-padding-start:0;padding-inline-start:0}}.hint-container.info .hint-container-title:before,.hint-container.note .hint-container-title:before,.hint-container.tip .hint-container-title:before,.hint-container.warning .hint-container-title:before,.hint-container.danger .hint-container-title:before{content:" ";position:absolute;top:calc(50% - .6125em);left:0;width:1.25em;height:1.25em;background-position:left;background-repeat:no-repeat}@media print{.hint-container.info .hint-container-title:before,.hint-container.note .hint-container-title:before,.hint-container.tip .hint-container-title:before,.hint-container.warning .hint-container-title:before,.hint-container.danger .hint-container-title:before{display:none}}html[dir=rtl] .hint-container.info .hint-container-title:before,html[dir=rtl] .hint-container.note .hint-container-title:before,html[dir=rtl] .hint-container.tip .hint-container-title:before,html[dir=rtl] .hint-container.warning .hint-container-title:before,html[dir=rtl] .hint-container.danger .hint-container-title:before{right:0;left:unset}.hint-container.info p,.hint-container.note p,.hint-container.tip p,.hint-container.warning p,.hint-container.danger p{line-height:1.5}.hint-container.info a,.hint-container.note a,.hint-container.tip a,.hint-container.warning a,.hint-container.danger a{color:var(--vp-tc)}.hint-container.info{border-color:var(--info-border-color);background:var(--info-bg-color)}.hint-container.info>.hint-container-title{color:var(--info-title-color)}.hint-container.info>.hint-container-title:before{background-image:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%234cb3d4'/%3E%3C/svg%3E")}.hint-container.info code{background:var(--info-code-bg-color)}.hint-container.note{border-color:var(--note-border-color);background:var(--note-bg-color)}.hint-container.note>.hint-container-title{color:var(--note-title-color)}.hint-container.note>.hint-container-title:before{background-image:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%23ccc'/%3E%3C/svg%3E")}.hint-container.note code{background:var(--note-code-bg-color)}.hint-container.tip{border-color:var(--tip-border-color);background:var(--tip-bg-color)}.hint-container.tip>.hint-container-title{color:var(--tip-title-color)}.hint-container.tip>.hint-container-title:before{background-image:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23009400' d='M7.941 18c-.297-1.273-1.637-2.314-2.187-3a8 8 0 1 1 12.49.002c-.55.685-1.888 1.726-2.185 2.998H7.94zM16 20v1a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-1h8zm-3-9.995V6l-4.5 6.005H11v4l4.5-6H13z'/%3E%3C/svg%3E")}.hint-container.tip code{background:var(--tip-code-bg-color)}.hint-container.warning{border-color:var(--warning-border-color);background:var(--warning-bg-color)}.hint-container.warning>.hint-container-title{color:var(--warning-title-color)}.hint-container.warning>.hint-container-title:before{background-image:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M576.286 752.57v-95.425q0-7.031-4.771-11.802t-11.3-4.772h-96.43q-6.528 0-11.3 4.772t-4.77 11.802v95.424q0 7.031 4.77 11.803t11.3 4.77h96.43q6.528 0 11.3-4.77t4.77-11.803zm-1.005-187.836 9.04-230.524q0-6.027-5.022-9.543-6.529-5.524-12.053-5.524H456.754q-5.524 0-12.053 5.524-5.022 3.516-5.022 10.547l8.538 229.52q0 5.023 5.022 8.287t12.053 3.265h92.913q7.032 0 11.803-3.265t5.273-8.287zM568.25 95.65l385.714 707.142q17.578 31.641-1.004 63.282-8.538 14.564-23.354 23.102t-31.892 8.538H126.286q-17.076 0-31.892-8.538T71.04 866.074q-18.582-31.641-1.004-63.282L455.75 95.65q8.538-15.57 23.605-24.61T512 62t32.645 9.04 23.605 24.61z' fill='%23e6a700'/%3E%3C/svg%3E")}.hint-container.warning code{background:var(--warning-code-bg-color)}.hint-container.danger{border-color:var(--danger-border-color);background:var(--danger-bg-color)}.hint-container.danger>.hint-container-title{color:var(--danger-title-color)}.hint-container.danger>.hint-container-title:before{background-image:url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E")}.hint-container.danger code{background:var(--danger-code-bg-color)}.hint-container.details{position:relative;display:block;margin:1.6em 0;padding:1.5rem;border-radius:.5rem;background:var(--detail-bg-color);color:var(--detail-text-color);transition:background var(--vp-tt),color var(--vp-tt)}@media print{.hint-container.details{display:none}}.hint-container.details h4{margin-top:0}.hint-container.details figure:last-child,.hint-container.details p:last-child{margin-bottom:0;padding-bottom:0}.hint-container.details a{color:var(--vp-tc)}.hint-container.details code{background:var(--detail-code-bg-color)}.hint-container.details summary{position:relative;margin:-1.5rem;padding-top:1.5rem;padding-bottom:1.5rem;-webkit-padding-start:4rem;padding-inline-start:4rem;-webkit-padding-end:1.5rem;padding-inline-end:1.5rem;list-style:none;cursor:pointer}.hint-container.details summary::-webkit-details-marker,.hint-container.details summary::marker{color:transparent;font-size:0}.hint-container.details summary:before,.hint-container.details summary:after{content:" ";position:absolute;top:calc(50% - .75rem);left:1.5rem;width:1.5rem;height:1.5rem}@media print{.hint-container.details summary:before,.hint-container.details summary:after{display:block}}html[dir=rtl] .hint-container.details summary:before,html[dir=rtl] .hint-container.details summary:after{right:1.5rem;left:unset}.hint-container.details summary:before{border-radius:50%;background:#ccc;transition:background var(--vp-ct),transform var(--vp-tt)}html[data-theme=dark] .hint-container.details summary:before{background:#555}.hint-container.details summary:after{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");line-height:normal;transition:transform var(--vp-tt);transform:rotate(90deg)}html[data-theme=dark] .hint-container.details summary:after{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.hint-container.details[open]>summary{margin-bottom:.5em}.hint-container.details[open]>summary:after{transform:rotate(180deg)}.footnote-item{margin-top:calc(0rem - var(--navbar-height, 3.6rem));padding-top:calc(var(--navbar-height, 3.6rem) + .5rem)}.footnote-item>p{margin-bottom:0}.footnote-ref{position:relative}.footnote-anchor{position:absolute;top:calc(-.5rem - var(--navbar-height, 3.6rem))}@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(/assets/KaTeX_AMS-Regular-0cdd387c.woff2) format("woff2"),url(/assets/KaTeX_AMS-Regular-30da91e8.woff) format("woff"),url(/assets/KaTeX_AMS-Regular-68534840.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(/assets/KaTeX_Caligraphic-Bold-de7701e4.woff2) format("woff2"),url(/assets/KaTeX_Caligraphic-Bold-1ae6bd74.woff) format("woff"),url(/assets/KaTeX_Caligraphic-Bold-07d8e303.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Caligraphic-Regular-5d53e70a.woff2) format("woff2"),url(/assets/KaTeX_Caligraphic-Regular-3398dd02.woff) format("woff"),url(/assets/KaTeX_Caligraphic-Regular-ed0b7437.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(/assets/KaTeX_Fraktur-Bold-74444efd.woff2) format("woff2"),url(/assets/KaTeX_Fraktur-Bold-9be7ceb8.woff) format("woff"),url(/assets/KaTeX_Fraktur-Bold-9163df9c.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Fraktur-Regular-51814d27.woff2) format("woff2"),url(/assets/KaTeX_Fraktur-Regular-5e28753b.woff) format("woff"),url(/assets/KaTeX_Fraktur-Regular-1e6f9579.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(/assets/KaTeX_Main-Bold-0f60d1b8.woff2) format("woff2"),url(/assets/KaTeX_Main-Bold-c76c5d69.woff) format("woff"),url(/assets/KaTeX_Main-Bold-138ac28d.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(/assets/KaTeX_Main-BoldItalic-99cd42a3.woff2) format("woff2"),url(/assets/KaTeX_Main-BoldItalic-a6f7ec0d.woff) format("woff"),url(/assets/KaTeX_Main-BoldItalic-70ee1f64.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(/assets/KaTeX_Main-Italic-97479ca6.woff2) format("woff2"),url(/assets/KaTeX_Main-Italic-f1d6ef86.woff) format("woff"),url(/assets/KaTeX_Main-Italic-0d85ae7c.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Main-Regular-c2342cd8.woff2) format("woff2"),url(/assets/KaTeX_Main-Regular-c6368d87.woff) format("woff"),url(/assets/KaTeX_Main-Regular-d0332f52.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(/assets/KaTeX_Math-BoldItalic-dc47344d.woff2) format("woff2"),url(/assets/KaTeX_Math-BoldItalic-850c0af5.woff) format("woff"),url(/assets/KaTeX_Math-BoldItalic-f9377ab0.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(/assets/KaTeX_Math-Italic-7af58c5e.woff2) format("woff2"),url(/assets/KaTeX_Math-Italic-8a8d2445.woff) format("woff"),url(/assets/KaTeX_Math-Italic-08ce98e5.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:700;src:url(/assets/KaTeX_SansSerif-Bold-e99ae511.woff2) format("woff2"),url(/assets/KaTeX_SansSerif-Bold-ece03cfd.woff) format("woff"),url(/assets/KaTeX_SansSerif-Bold-1ece03f7.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:italic;font-weight:400;src:url(/assets/KaTeX_SansSerif-Italic-00b26ac8.woff2) format("woff2"),url(/assets/KaTeX_SansSerif-Italic-91ee6750.woff) format("woff"),url(/assets/KaTeX_SansSerif-Italic-3931dd81.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:400;src:url(/assets/KaTeX_SansSerif-Regular-68e8c73e.woff2) format("woff2"),url(/assets/KaTeX_SansSerif-Regular-11e4dc8a.woff) format("woff"),url(/assets/KaTeX_SansSerif-Regular-f36ea897.ttf) format("truetype")}@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Script-Regular-036d4e95.woff2) format("woff2"),url(/assets/KaTeX_Script-Regular-d96cdf2b.woff) format("woff"),url(/assets/KaTeX_Script-Regular-1c67f068.ttf) format("truetype")}@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Size1-Regular-6b47c401.woff2) format("woff2"),url(/assets/KaTeX_Size1-Regular-c943cc98.woff) format("woff"),url(/assets/KaTeX_Size1-Regular-95b6d2f1.ttf) format("truetype")}@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Size2-Regular-d04c5421.woff2) format("woff2"),url(/assets/KaTeX_Size2-Regular-2014c523.woff) format("woff"),url(/assets/KaTeX_Size2-Regular-a6b2099f.ttf) format("truetype")}@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(data:font/woff2;base64,d09GMgABAAAAAA4oAA4AAAAAHbQAAA3TAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAgRQIDgmcDBEICo1oijYBNgIkA14LMgAEIAWJAAeBHAyBHBvbGiMRdnO0IkRRkiYDgr9KsJ1NUAf2kILNxgUmgqIgq1P89vcbIcmsQbRps3vCcXdYOKSWEPEKgZgQkprQQsxIXUgq0DqpGKmIvrgkeVGtEQD9DzAO29fM9jYhxZEsL2FeURH2JN4MIcTdO049NCVdxQ/w9NrSYFEBKTDKpLKfNkCGDc1RwjZLQcm3vqJ2UW9Xfa3tgAHz6ivp6vgC2yD4/6352ndnN0X0TL7seypkjZlMsjmZnf0Mm5Q+JykRWQBKCVCVPbARPXWyQtb5VgLB6Biq7/Uixcj2WGqdI8tGSgkuRG+t910GKP2D7AQH0DB9FMDW/obJZ8giFI3Wg8Cvevz0M+5m0rTh7XDBlvo9Y4vm13EXmfttwI4mBo1EG15fxJhUiCLbiiyCf/ZA6MFAhg3pGIZGdGIVjtPn6UcMk9A/UUr9PhoNsCENw1APAq0gpH73e+M+0ueyHbabc3vkbcdtzcf/fiy+NxQEjf9ud/ELBHAXJ0nk4z+MXH2Ev/kWyV4k7SkvpPc9Qr38F6RPWnM9cN6DJ0AdD1BhtgABtmoRoFCvPsBAumNm6soZG2Gk5GyVTo2sJncSyp0jQTYoR6WDvTwaaEcHsxHfvuWhHA3a6bN7twRKtcGok6NsCi7jYRrM2jExsUFMxMQYuJbMhuWNOumEJy9hi29Dmg5zMp/A5+hhPG19j1vBrq8JTLr8ki5VLPmG/PynJHVul440bxg5xuymHUFPBshC+nA9I1FmwbRBTNHAcik3Oae0cxKoI3MOriM42UrPe51nsaGxJ+WfXubAsP84aabUlQSJ1IiE0iPETLUU4CATgfXSCSpuRFRmCGbO+wSpAnzaeaCYW1VNEysRtuXCEL1kUFUbbtMv3Tilt/1c11jt3Q5bbMa84cpWipp8Elw3MZhOHsOlwwVUQM3lAR35JiFQbaYCRnMF2lxAWoOg2gyoIV4PouX8HytNIfLhqpJtXB4vjiViUI8IJ7bkC4ikkQvKksnOTKICwnqWSZ9YS5f0WCxmpgjbIq7EJcM4aI2nmhLNY2JIUgOjXZFWBHb+x5oh6cwb0Tv1ackHdKi0I9OO2wE9aogIOn540CCCziyhN+IaejtgAONKznHlHyutPrHGwCx9S6B8kfS4Mfi4Eyv7OU730bT1SCBjt834cXsf43zVjPUqqJjgrjeGnBxSG4aYAKFuVbeCfkDIjAqMb6yLNIbCuvXhMH2/+k2vkNpkORhR59N1CkzoOENvneIosjYmuTxlhUzaGEJQ/iWqx4dmwpmKjrwTiTGTCVozNAYqk/zXOndWxuWSmJkQpJw3pK5KX6QrLt5LATMqpmPAQhkhK6PUjzHUn7E0gHE0kPE0iKkolgkUx9SZmVAdDgpffdyJKg3k7VmzYGCwVXGz/tXmkOIp+vcWs+EMuhhvN0h9uhfzWJziBQmCREGSIFmQIkgVpAnSBRmC//6hkLZwaVhwxlrJSOdqlFtOYxlau9F2QN5Y98xmIAsiM1HVp2VFX+DHHGg6Ecjh3vmqtidX3qHI2qycTk/iwxSt5UzTmEP92ZBnEWTk4Mx8Mpl78ZDokxg/KWb+Q0QkvdKVmq3TMW+RXEgrsziSAfNXFMhDc60N5N9jQzjfO0kBKpUZl0ZmwJ41j/B9Hz6wmRaJB84niNmQrzp9eSlQCDDzazGDdVi3P36VZQ+Jy4f9UBNp+3zTjqI4abaFAm+GShVaXlsGdF3FYzZcDI6cori4kMxUECl9IjJZpzkvitAoxKue+90pDMvcKRxLl53TmOKCmV/xRolNKSqqUxc6LStOETmFOiLZZptlZepcKiAzteG8PEdpnQpbOMNcMsR4RR2Bs0cKFEvSmIjAFcnarqwUL4lDhHmnVkwu1IwshbiCcgvOheZuYyOteufZZwlcTlLgnZ3o/WcYdzZHW/WGaqaVfmTZ1aWCceJjkbZqsfbkOtcFlUZM/jy+hXHDbaUobWqqXaeWobbLO99yG5N3U4wxco0rQGGcOLASFMXeJoham8M+/x6O2WywK2l4HGbq1CoUyC/IZikQhdq3SiuNrvAEj0AVu9x2x3lp/xWzahaxidezFVtdcb5uEnzyl0ZmYiuKI0exvCd4Xc9CV1KB0db00z92wDPde0kukbvZIWN6jUWFTmPIC/Y4UPCm8UfDTFZpZNon1qLFTkBhxzB+FjQRA2Q/YRJT8pQigslMaUpFyAG8TMlXigiqmAZX4xgijKjRlGpLE0GdplRfCaJo0JQaSxNBk6ZmMzcya0FmrcisDdn0Q3HI2sWSppYigmlM1XT/kLQZSNpMJG0WkjYbSZuDpM1F0uYhFc1HxU4m1QJjDK6iL0S5uSj5rgXc3RejEigtcRBtqYPQsiTskmO5vosV+q4VGIKbOkDg0jtRrq+Em1YloaTFar3EGr1EUC8R0kus1Uus00usL97ABr2BjXoDm/QGNhuWtMVBKOwg/i78lT7hBsAvDmwHc/ao3vmUbBmhjeYySZNWvGkfZAgISDSaDo1SVpzGDsAEkF8B+gEapViUoZgUWXcRIGFZNm6gWbAKk0bp0k1MHG9fLYtV4iS2SmLEQFARzRcnf9PUS0LVn05/J9MiRRBU3v2IrvW974v4N00L7ZMk0wXP1409CHo/an8zTRHD3eSJ6m8D4YMkZNl3M79sqeuAsr/m3f+8/yl7A50aiAEJgeBeMWzu7ui9UfUBCe2TIqZIoOd/3/udRBOQidQZUERzb2/VwZN1H/Sju82ew2H2Wfr6qvfVf3hqwDvAIpkQVFy4B9Pe9e4/XvPeceu7h3dvO56iJPf0+A6cqA2ip18ER+iFgggiuOkvj24bby0N9j2UHIkgqIt+sVgfodC4YghLSMjSZbH0VR/6dMDrYJeKHilKTemt6v6kvzvn3/RrdWtr0GoN/xL+Sex/cPYLUpepx9cz/D46UPU5KXgAQa+NDps1v6J3xP1i2HtaDB0M9aX2deA7SYff//+gUCovMmIK/qfsFcOk+4Y5ZN97XlG6zebqtMbKgeRFi51vnxTQYBUik2rS/Cn6PC8ADR8FGxsRPB82dzfND90gIcshOcYUkfjherBz53odpm6TP8txlwOZ71xmfHHOvq053qFF/MRlS3jP0ELudrf2OeN8DHvp6ZceLe8qKYvWz/7yp0u4dKPfli3CYq0O13Ih71mylJ80tOi10On8wi+F4+LWgDPeJ30msSQt9/vkmHq9/Lvo2b461mP801v3W4xTcs6CbvF9UDdrSt+A8OUbpSh55qAUFXWznBBfdeJ8a4d7ugT5tvxUza3h9m4H7ptTqiG4z0g5dc0X29OcGlhpGFMpQo9ytTS+NViZpNdvU4kWx+LKxNY10kQ1yqGXrhe4/1nvP7E+nd5A92TtaRplbHSqoIdOqtRWti+fkB5/n1+/VvCmz12pG1kpQWsfi1ftlBobm0bpngs16CHkbIwdLnParxtTV3QYRlfJ0KFskH7pdN/YDn+yRuSd7sNH3aO0DYPggk6uWuXrfOc+fa3VTxFVvKaNxHsiHmsXyCLIE5yuOeN3/Jdf8HBL/5M6shjyhxHx9BjB1O0+4NLOnjLLSxwO7ukN4jMbOIcD879KLSi6Pk61Oqm2377n8079PXEEQ7cy7OKEC9nbpet118fxweTafpt69x/Bt8UqGzNQt7aelpc44dn5cqhwf71+qKp/Zf/+a0zcizOUWpl/iBcSXip0pplkatCchoH5c5aUM8I7/dWxAej8WicPL1URFZ9BDJelUwEwTkGqUhgSlydVes95YdXvhh9Gfz/aeFWvgVb4tuLbcv4+wLdutVZv/cUonwBD/6eDlE0aSiKK/uoH3+J1wDE/jMVqY2ysGufN84oIXB0sPzy8ollX/LegY74DgJXJR57sn+VGza0x3DnuIgABFM15LmajjjsNlYj+JEZGbuRYcAMOWxFkPN2w6Wd46xo4gVWQR/X4lyI/R6K/YK0110GzudPRW7Y+UOBGTfNNzHeYT0fiH0taunBpq9HEW8OKSaBGj21L0MqenEmNRWBAWDWAk4CpNoEZJ2tTaPFgbQYj8HxtFilErs3BTRwT8uO1NXQaWfIotchmPkAF5mMBAliEmZiOGVgCG9LgRzpscMAOOwowlT3JhusdazXGSC/hxR3UlmWVwWHpOIKheqONvjyhSiTHIkVUco5bnji8m//zL7PKaT1Vl5I6UE609f+gkr6MZKVyKc7zJRmCahLsdlyA5fdQkRSan9LgnnLEyGSkaKJCJog0wAgvepWBt80+1yKln1bMVtCljfNWDueKLsWwaEbBSfSPTEmVRsUcYYMnEjcjeyCZzBXK9E9BYBXLKjOSpUDR+nEV3TFSUdQaz+ot98QxgXwx0GQ+EEUAKB2qZPkQQ0GqFD8UPFMqyaCHM24BZmSGic9EYMagKizOw9Hz50DMrDLrqqLkTAhplMictiCAx5S3BIUQdeJeLnBy2CNtMfz6cV4u8XKoFZQesbf9YZiIERiHjaNodDW6LgcirX/mPnJIkBGDUpTBhSa0EIr38D5hCIszhCM8URGBqImoWjpvpt1ebu/v3Gl3qJfMnNM+9V+kiRFyROTPHQWOcs1dNW94/ukKMPZBvDi55i5CttdeJz84DLngLqjcdwEZ87bFFR8CIG35OAkDVN6VRDZ7aq67NteYqZ2lpT8oYB2CytoBd6VuAx4WgiAsnuj3WohG+LugzXiQRDeM3XYXlULv4dp5VFYC) format("woff2"),url(/assets/KaTeX_Size3-Regular-6ab6b62e.woff) format("woff"),url(/assets/KaTeX_Size3-Regular-500e04d5.ttf) format("truetype")}@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Size4-Regular-a4af7d41.woff2) format("woff2"),url(/assets/KaTeX_Size4-Regular-99f9c675.woff) format("woff"),url(/assets/KaTeX_Size4-Regular-c647367d.ttf) format("truetype")}@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(/assets/KaTeX_Typewriter-Regular-71d517d6.woff2) format("woff2"),url(/assets/KaTeX_Typewriter-Regular-e14fed02.woff) format("woff"),url(/assets/KaTeX_Typewriter-Regular-f01f3e87.ttf) format("truetype")}.katex{text-rendering:auto;font: 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.7"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}.katex{font-size:1.05em;direction:ltr}.katex-display{overflow:auto hidden;-webkit-overflow-scrolling:touch;padding-top:.2em;padding-bottom:.2em}.katex-display::-webkit-scrollbar{height:3px}.katex-display .katex{font-size:1.21em}.katex-error{color:red}/*! PhotoSwipe main CSS by Dmytro Semenov | photoswipe.com */.pswp{--pswp-bg: #000;--pswp-placeholder-bg: #222;--pswp-root-z-index: 100000;--pswp-preloader-color: rgba(79, 79, 79, .4);--pswp-preloader-color-secondary: rgba(255, 255, 255, .9);--pswp-icon-color: #fff;--pswp-icon-color-secondary: #4f4f4f;--pswp-icon-stroke-color: #4f4f4f;--pswp-icon-stroke-width: 2px;--pswp-error-text-color: var(--pswp-icon-color)}.pswp{position:fixed;top:0;left:0;width:100%;height:100%;z-index:var(--pswp-root-z-index);display:none;touch-action:none;outline:0;opacity:.003;contain:layout style size;-webkit-tap-highlight-color:rgba(0,0,0,0)}.pswp:focus{outline:0}.pswp *{box-sizing:border-box}.pswp img{max-width:none}.pswp--open{display:block}.pswp,.pswp__bg{transform:translateZ(0);will-change:opacity}.pswp__bg{opacity:.005;background:var(--pswp-bg)}.pswp,.pswp__scroll-wrap{overflow:hidden}.pswp__scroll-wrap,.pswp__bg,.pswp__container,.pswp__item,.pswp__content,.pswp__img,.pswp__zoom-wrap{position:absolute;top:0;left:0;width:100%;height:100%}.pswp__img,.pswp__zoom-wrap{width:auto;height:auto}.pswp--click-to-zoom.pswp--zoom-allowed .pswp__img{cursor:zoom-in}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img{cursor:move;cursor:grab}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img:active{cursor:grabbing}.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img,.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img:active,.pswp__img{cursor:zoom-out}.pswp__container,.pswp__img,.pswp__button,.pswp__counter{-webkit-user-select:none;-moz-user-select:none;user-select:none}.pswp__item{z-index:1;overflow:hidden}.pswp__hidden{display:none!important}.pswp__content{pointer-events:none}.pswp__content>*{pointer-events:auto}.pswp__error-msg-container{display:grid}.pswp__error-msg{margin:auto;font-size:1em;line-height:1;color:var(--pswp-error-text-color)}.pswp .pswp__hide-on-close{opacity:.005;will-change:opacity;transition:opacity var(--pswp-transition-duration) cubic-bezier(.4,0,.22,1);z-index:10;pointer-events:none}.pswp--ui-visible .pswp__hide-on-close{opacity:1;pointer-events:auto}.pswp__button{position:relative;display:block;width:50px;height:60px;padding:0;margin:0;overflow:hidden;cursor:pointer;background:none;border:0;box-shadow:none;opacity:.85;-webkit-appearance:none;-webkit-touch-callout:none}.pswp__button:hover,.pswp__button:active,.pswp__button:focus{transition:none;padding:0;background:none;border:0;box-shadow:none;opacity:1}.pswp__button:disabled{opacity:.3;cursor:auto}.pswp__icn{fill:var(--pswp-icon-color);color:var(--pswp-icon-color-secondary)}.pswp__icn{position:absolute;top:14px;left:9px;width:32px;height:32px;overflow:hidden;pointer-events:none}.pswp__icn-shadow{stroke:var(--pswp-icon-stroke-color);stroke-width:var(--pswp-icon-stroke-width);fill:none}.pswp__icn:focus{outline:0}div.pswp__img--placeholder,.pswp__img--with-bg{background:var(--pswp-placeholder-bg)}.pswp__top-bar{position:absolute;left:0;top:0;width:100%;height:60px;display:flex;flex-direction:row;justify-content:flex-end;z-index:10;pointer-events:none!important}.pswp__top-bar>*{pointer-events:auto;will-change:opacity}.pswp__button--close{margin-right:6px}.pswp__button--arrow{position:absolute;top:0;width:75px;height:100px;top:50%;margin-top:-50px}.pswp__button--arrow:disabled{display:none;cursor:default}.pswp__button--arrow .pswp__icn{top:50%;margin-top:-30px;width:60px;height:60px;background:none;border-radius:0}.pswp--one-slide .pswp__button--arrow{display:none}.pswp--touch .pswp__button--arrow{visibility:hidden}.pswp--has_mouse .pswp__button--arrow{visibility:visible}.pswp__button--arrow--prev{right:auto;left:0px}.pswp__button--arrow--next{right:0px}.pswp__button--arrow--next .pswp__icn{left:auto;right:14px;transform:scaleX(-1)}.pswp__button--zoom{display:none}.pswp--zoom-allowed .pswp__button--zoom{display:block}.pswp--zoomed-in .pswp__zoom-icn-bar-v{display:none}.pswp__preloader{position:relative;overflow:hidden;width:50px;height:60px;margin-right:auto}.pswp__preloader .pswp__icn{opacity:0;transition:opacity .2s linear;animation:pswp-clockwise .6s linear infinite}.pswp__preloader--active .pswp__icn{opacity:.85}@keyframes pswp-clockwise{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.pswp__counter{height:30px;margin-top:15px;-webkit-margin-start:20px;margin-inline-start:20px;font-size:14px;line-height:30px;color:var(--pswp-icon-color);text-shadow:1px 1px 3px var(--pswp-icon-color-secondary);opacity:.85}.pswp--one-slide .pswp__counter{display:none}.photo-swipe-loading{position:absolute;top:0;left:0;display:flex;align-items:center;justify-content:center;width:100vw;height:100vh}.photo-swipe-bullets-indicator{position:absolute;bottom:30px;left:50%;display:flex;flex-direction:row;align-items:center;transform:translate(-50%)}.photo-swipe-bullet{width:12px;height:6px;margin:0 5px;border-radius:3px;background:#fff;transition:width var(--vp-tt),color var(--vp-ct)}.photo-swipe-bullet.active{width:30px;background:var(--vp-tc)}:root{--navbar-bg-color: var(--bg-color-float-blur);--sidebar-bg-color: var(--bg-color-blur)}html[data-theme=dark]{--navbar-bg-color: var(--bg-color-blur);--sidebar-bg-color: var(--bg-color-blur)}#app{--code-hl-bg-color: var(--code-highlight-line-color);--code-ln-color: var(--code-line-color);--code-ln-wrapper-width: var(--line-numbers-width);--code-tabs-nav-text-color: var(--code-color);--code-tabs-nav-bg-color: var(--code-border-color);--code-tabs-nav-hover-color: var(--code-highlight-line-color);--sidebar-space: var(--sidebar-width)}@media (max-width: 959px){#app{--navbar-height: var(--navbar-mobile-height);--navbar-vertical-padding: var(--navbar-mobile-vertical-padding);--navbar-horizontal-padding: var(--navbar-mobile-horizontal-padding);--sidebar-width: var(--sidebar-mobile-width)}}@media (min-width: 1440px){#app{--sidebar-space: clamp( var(--sidebar-width), max(0px, calc((100vw - var(--content-width)) / 2 - 2rem)), 100vw )}}.DocSearch-Button,.DocSearch{--docsearch-primary-color: var(--vp-tc);--docsearch-text-color: var(--vp-c);--docsearch-highlight-color: var(--vp-tc);--docsearch-muted-color: var(--light-grey);--docsearch-container-background: rgb(9 10 17 / 80%);--docsearch-modal-background: var(--bg-color-float);--docsearch-searchbox-background: var(--bg-color-secondary);--docsearch-searchbox-focus-background: var(--vp-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--vp-tc);--docsearch-hit-color: var(--vp-cl);--docsearch-hit-active-color: var(--vp-bg);--docsearch-hit-background: var(--vp-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--border-color);--docsearch-footer-background: var(--vp-bg)}html[data-theme=dark] .DocSearch-Button,html[data-theme=dark] .DocSearch{--docsearch-logo-color: var(--vp-c);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgb(3 4 9 / 30%);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgb(73 76 106 / 50%), 0 -4px 8px 0 rgb(0 0 0 / 20%)}#nprogress{--nprogress-color: var(--vp-tc)}.search-box{--search-bg-color: var(--vp-bg);--search-accent-color: var(--vp-tc);--search-text-color: var(--vp-c);--search-border-color: var(--border-color);--search-item-text-color: var(--vp-clt);--search-item-focus-bg-color: var(--bg-color-secondary)}.external-link-icon{--external-link-icon-color: var(--light-grey)}html,body{margin:0;padding:0;background:#fff}body{min-height:100vh;color:#2c3e50;font-size:16px;font-display:optional;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-tap-highlight-color:transparent}@media print{body{font-size:12pt}}a{color:#3eaf7c;font-weight:500;text-decoration:none;overflow-wrap:break-word}kbd{display:inline-block;min-width:1em;margin-inline:.125rem;padding:.25em;border:1px solid #eee;border-radius:.25em;box-shadow:1px 1px 4px #00000026;line-height:1;letter-spacing:-.1em;text-align:center}code{margin:0;padding:.2rem .4rem;border-radius:5px;background:rgba(127,127,127,.12);font-size:.85em;overflow-wrap:break-word}table code{padding:.1rem .4rem}p a code{color:#3eaf7c;font-weight:400}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:500;line-height:1.25;overflow-wrap:break-word}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2rem}h2{padding-bottom:.3rem;border-bottom:1px solid #eaecef;font-size:1.65rem}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}a.header-anchor{float:left;margin-top:.125em;-webkit-margin-start:-.87em;margin-inline-start:-.87em;-webkit-padding-end:.23em;padding-inline-end:.23em;font-size:.85em;opacity:0;transition:opacity .2s}@media print{a.header-anchor{display:none!important}}a.header-anchor:hover{text-decoration:none}a.header-anchor:focus-visible{opacity:1}p,ul,ol{line-height:1.6;overflow-wrap:break-word}@media print{p,ul,ol{line-height:1.5}}ul,ol{-webkit-padding-start:1.2em;padding-inline-start:1.2em}blockquote{margin:1rem 0;padding:.25rem 0 .25rem 1rem;-webkit-border-start:.2rem solid #ddd;border-inline-start:.2rem solid #ddd;color:#666;font-size:1rem;overflow-wrap:break-word}blockquote>p{margin:0}hr{border:0;border-top:1px solid #eaecef}table{display:block;overflow-x:auto;margin:1rem 0;border-collapse:collapse}tr:nth-child(2n){background:#f6f8fa}th,td{padding:.6em 1em;border:1px solid #dfe2e5}pre{direction:ltr}@page{margin:2cm;font-size:12pt;size:a4}@media print{*,:after,:before{box-shadow:none!important;text-shadow:none!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}a{color:inherit;font-weight:inherit!important;font-size:inherit!important;text-decoration:underline}a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}abbr[title]:after{content:" (" attr(title) ")"}pre{border:1px solid #eee;white-space:pre-wrap!important}pre>code{white-space:pre-wrap!important}blockquote{-webkit-border-start:.2rem solid #ddd;border-inline-start:.2rem solid #ddd;color:inherit}blockquote,pre{orphans:5;widows:5}img,tr,canvas{page-break-inside:avoid}}@font-face{font-weight:400;font-style:normal;font-family:Crimson;src:url(data:font/truetype;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTYr5mwEAAAyMAAAAHEdERUYAKQATAAAMbAAAAB5PUy8yVsJ0MgAAAVgAAABgY21hcBiKDzgAAAHcAAABWGdhc3D//wADAAAMZAAAAAhnbHlmr+DBdQAAA1AAAAdsaGVhZBZwt+8AAADcAAAANmhoZWEFawEuAAABFAAAACRobXR4BksA9gAAAbgAAAAibG9jYQlsC24AAAM0AAAAHG1heHAAEQBZAAABOAAAACBuYW1lLaFDVAAACrwAAAFrcG9zdAC1AHoAAAwoAAAAPAABAAAAAQAAqBd2H18PPPUACwQAAAAAANqqufwAAAAA2qq5/AAb/9wB4QMeAAAACAACAAAAAAAAAAEAAAMs/ywAXAH9AAAAAAHhAAEAAAAAAAAAAAAAAAAAAAAEAAEAAAANAFkAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAH1AZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAIABgMAAAAAAAAAAAABEAAAAAAAAAAAAAAAUGZFZADAADAAOQMs/ywAXAMsANQAAAABAAAAAAMYAAAAAAAgAAEBpwAfAAAAAAFVAAAB/QAfAH0ALQA+ABsAPgAyACgAPgAxAAAAAAADAAAAAwAAABwAAQAAAAAAUgADAAEAAAAcAAQANgAAAAQABAABAAAAOf//AAAAL///AAAAAQAEAAAAAAADAAQABQAGAAcACAAJAAoACwAMAAABBgAAAQAAAAAAAAABAgAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAwQFBgcICQoLDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYAJgAmAGIAwAEeAZIBzgJAApYC2gNiA7YAAQAf/9wBhwMeABIAAAEGBwYHATAXFjM2NzY3ASYnJjcBgxwLCgH+zgMECxIKCgIBLgEDAwMDHhQFBgP85wMEAQgJBgMOAwMDEwAAAAIAH//9Ad0CkAAQACEAABMWFxYXNjc2NzQnJicGBwYHNyY3NjcWFxYXFAcGByYnJjcfATo6amo7OQE5OmxrOjkBXQIlJEE5IyIBIyJEOSQjAgFOkV5eBAReXoqJXl4EBF5eggJ0UlEDA09Qe3xVVgMDU1OEAAAAAAEAff/9AYACkQA+AAA3FAcGBwYHBiMGFQYXNjc2MzIXFhc2JzQnIicmJyY1JjURNjc2MSYnJicjBgcGBwYVFBUUFxYXNjc2NzIXFhXkAQEEBRgYDAMBBB4ZGhweGxofBAEDDBgZBQQBAQMEAQIDBAIFNTZCAgMDBA0XFw0LBQV3GBMVDAgEBAUKCgUCAQICAQIFCgoFBAQIDBUTGAGnLxkbBAYFAQIZGh4BAgECBQUEAwUHBwEICRYAAAAAAQAtAAAB0QKRADoAADcGFxYXITY3NjcmJyYjIgcGBwYHBisBNjc2NzY3NjUmJyYnBgcGBxQXFhc2NzY3FhcWFxYHBgcGBwYHLgEEAwMBYwURERADBwYFBAMDAg8VEx/LJkBAOhsQDwIxMkxSMjIHCAYGCSYmPTIfHwEBCgoeLkJBQg8EBQQCETAwKQICAgEBBCgUEylJSUYhJicsRDIzAgY1NRoEBQYBEyEhAwEjIjYlJCQtQlBQSAAAAAABAD7/+wG+ApEASgAANwYXFhcWFxYzNjc2NyYnJic2NzY3JicmIwYHBgcUFxYXNjc2NxYXFhcGBwYHBgcUFRQXNjc2NxYXFhcGBwYnIicmJyYnJiciBwYXPwEIBwUaHB0VZU5NBAMvLi8eIB4DAywsKzwrKxgEAwUIHR4wLRscAQMvLz8BAQYKEhEQNSYmAgImJSsWExQPCw0NFREMDQE7DgsLBQwFBgE8PWpMKSoGECQkMkAiIQIdHyUHBwcBCRscAwEbGSpCIyUOAgMCAwwIAwUEAQEoKD9XJSQBBQYODg8PAQ0NFQAAAgAb//oB4QKTACIAJQAANxQXFhchFRQXFjMyNzYjNTM2NzY1NCcmJyMRNCcmIwYHBgcBExEbAgMFASEJCRIdCAkBRgIBAQUEBTwFAwgHCQkG/vjmxgUGBgOwBQIBAwKzAgQDCBAMDQEBlAYGBgEICQf+cwEs/tQAAQA+//sBvgKTAEoAADcGFxYXFhcWMzY3NjcmJyYnIgcGBzY3NjczMjc2NzY3NjU0JyYnBgcGByMGBwYHFBcWMzY3NjMWFxYHBgcGJyInJicmJyYnIgcGFz8BCAcFGhwdFWVOTQQBMjJbFx8gFwoJCQlWKB0dFQ4JCAQDBQMdHSKXCREQEgMCBA4bGhNYJyUBAiYlKxYTFA8LDQ0VEQwNATsOCwsFDAUGATw9akU2NwMFBggrMC8uAgICExcZBgQCAgMBAwQBMVNUWAUFBAYFBAMxMTNZIyQBBQYODg8PAQ0NFQAAAgAy//oBzQKXACAAMwAANxQXFhc2NzY3NicmJyIHBgc2NzY3NCcmJwYHBgcGBwYXNyY3Njc2FxYXFgcGBwYHJicmNzM1NV5aOTsCAioqahoiIRsnWFhFAwIHQ0tMOTAZGQFbBAQaGxkXRB8fAQEfIDE9Hh4E511FRwQDPT1ZPEJBBQwLF4Y9PRMGCwwBEiwsPDZFRkkTHyAbCAcBAjAwREYsLQEFREVQAAAAAAEAKP/7AdUCiwApAAATFhcWMzI3Njc2NzYzIQYHBgcWFxYzMjcBNjc2NzQnJiMiBwYjIQYHBgcoAwYHAwYDAwELEBEdAQUJYWJXAQ8PDgcDAQ4LCQgBAQEEBhUVFv7JBgsNDAH6DQMCAQEFKRITFMjHjQcFBgMCPxYSEwoEAgMBAhkrKiAAAAADAD7/9wG/ApIAKABBAFgAADcGFxYXNjc2NyYnJicmJzQ3Njc2NyYnJiMGBwYHFhcWFxYVFAcGBwYHNyY3Njc2MzIzMhcyFxYXFhcGBwYHIicmNxMmNzY3FhcWFRQHBgcGByIjIicmJyY3PwE1M1ZQODgDAykpMQIBAyYlJQMCMC9HRjExAgIiIiMCAiMvLwNTBBQTKgEBAQECAQIBEjU1CAEdHjMrISICGAMYGSYvGxoTEx8CAQIBBAMfJCQBoU8tLQECMjFPOC4uGwIBAgEWJiU7SCYoAjEwQzopKhMBAgECEykpQAQsIiEbAQEBBywsQjUeHQEiI0QBZSMhIAECJiYvKh8gFAEBAhAfIEYAAAIAMf/6AcsClwAgADMAABMGFxYXMjc2NwYHBgcUFxYXNjc2NzY3NjUmJyYnBgcGBzcmNzY3FhcWFRQHBgcGJyYnJjc0AyopahoiIRsoV1hFAwIHQ0tMODEZGQE2NF5ZOjoBWgMfHzE9Hh4EGhoaF0QeHwUBy0dBQgUMCxeFPj0SBwsLAREsLD01RkVPV0dFBQQ8PU8UPCwtAQVFRUklIRsHCAECMDBPAAAADACWAAEAAAAAAAEABwAQAAEAAAAAAAIABwAoAAEAAAAAAAMABwBAAAEAAAAAAAQABwBYAAEAAAAAAAUAHgCeAAEAAAAAAAYABwDNAAMAAQQJAAEADgAAAAMAAQQJAAIADgAYAAMAAQQJAAMADgAwAAMAAQQJAAQADgBIAAMAAQQJAAUAPABgAAMAAQQJAAYADgC9AEMAcgBpAG0AcwBvAG4AAENyaW1zb24AAEMAcgBpAG0AcwBvAG4AAENyaW1zb24AAEMAcgBpAG0AcwBvAG4AAENyaW1zb24AAEMAcgBpAG0AcwBvAG4AAENyaW1zb24AAFYAZQByAHMAaQBvAG4AIAAxAC4AMAA7ACAARgBvAG4AdABFAGQAaQB0AG8AcgAgACgAdgAxAC4AMAApAABWZXJzaW9uIDEuMDsgRm9udEVkaXRvciAodjEuMCkAAEMAcgBpAG0AcwBvAG4AAENyaW1zb24AAAACAAAAAAAAADIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AAAABAAIAEwAUABUAFgAXABgAGQAaABsAHAAAAAH//wACAAEAAAAMAAAAFgAAAAIAAQADAAwAAQAEAAAAAgAAAAAAAAABAAAAANWkJwgAAAAA2qq5/AAAAADaqrn8) format("truetype")}html,body{background:var(--bg-color);transition:background var(--color-transition)}:root{color-scheme:light}html[data-theme=dark]{color-scheme:dark}body{color:var(--text-color);font-family:var(--font-family)}@media (min-width: 1440px){body{font-size:17px}}a{color:var(--theme-color)}kbd{border-color:var(--border-color-dark);background:var(--bg-color-secondary);font-family:var(--font-family-mono)}code{font-family:var(--font-family-mono);transition:background var(--color-transition),color var(--color-transition)}html[data-theme=dark] code{background:#333}p a code{color:var(--theme-color)}blockquote{border-color:#eee;color:#666;transition:border-color var(--color-transition),color var(--color-transition)}html[data-theme=dark] blockquote{border-color:#333}@media (max-width: 419px){h1{font-size:1.9rem}}h2{border-color:var(--border-color);transition:border-bottom-color var(--color-transition)}hr{border-color:var(--border-color);transition:border-top-color var(--color-transition)}tr:nth-child(2n){background:var(--bg-color-secondary)}th,td{border-color:var(--border-color-dark)}@media print{@page{--text-color: #000 !important;--bg-color: #fff !important}div[class*=language-]{position:relative!important}}.theme-hope-content:not(.custom)>*:first-child{margin-top:0}.breadcrumb{max-width:var(--content-width, 740px);-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem;-webkit-padding-end:2.5rem;padding-inline-end:2.5rem;position:relative;z-index:2;padding-top:1rem;font-size:15px}@media (max-width: 959px){.breadcrumb{-webkit-padding-start:1.5rem;padding-inline-start:1.5rem;-webkit-padding-end:1.5rem;padding-inline-end:1.5rem}}@media print{.breadcrumb{max-width:unset}}@media (max-width: 959px){.breadcrumb{font-size:14px}}@media (max-width: 419px){.breadcrumb{padding-top:.5rem;font-size:12.8px}}@media print{.breadcrumb{display:none}}.breadcrumb .icon{-webkit-margin-end:.25em;margin-inline-end:.25em}.breadcrumb a{display:inline-block;padding:0 .5em}.breadcrumb a:before{position:relative;bottom:.125rem;-webkit-margin-end:.25em;margin-inline-end:.25em}.breadcrumb a:hover{color:var(--theme-color)}.breadcrumb ol{margin:0;-webkit-padding-start:0;padding-inline-start:0;list-style:none}.breadcrumb li{display:inline-block;line-height:1.5}.breadcrumb li:first-child a{-webkit-padding-start:0;padding-inline-start:0}.breadcrumb li:last-child a{-webkit-padding-end:0;padding-inline-end:0}.breadcrumb li.is-active a{color:var(--light-grey);cursor:default;pointer-events:none}.breadcrumb li+li:before{content:"/";color:var(--light-grey)}.toggle-sidebar-wrapper{position:fixed;top:var(--navbar-height);bottom:0;left:var(--sidebar-space);z-index:100;display:flex;align-items:center;justify-content:center;font-size:2rem;transition:left var(--transform-transition)}@media (max-width: 719px){.toggle-sidebar-wrapper{display:none}}@media (min-width: 1440px){.toggle-sidebar-wrapper{display:none}}html[dir=rtl] .toggle-sidebar-wrapper{right:var(--sidebar-space);left:unset}.toggle-sidebar-wrapper:hover{background:rgba(127,127,127,.05);cursor:pointer}.toggle-sidebar-wrapper .arrow{display:inline-block;vertical-align:middle;width:1em;height:1em;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");line-height:normal;transition:all .3s}html[data-theme=dark] .toggle-sidebar-wrapper .arrow{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.toggle-sidebar-wrapper .arrow.down{transform:rotate(180deg)}html[dir=rtl] .toggle-sidebar-wrapper .arrow.down{transform:rotate(-180deg)}.toggle-sidebar-wrapper .arrow.end{transform:rotate(90deg)}html[dir=rtl] .toggle-sidebar-wrapper .arrow.end,.toggle-sidebar-wrapper .arrow.start{transform:rotate(-90deg)}html[dir=rtl] .toggle-sidebar-wrapper .arrow.start{transform:rotate(90deg)}.theme-container{display:flex;flex-direction:column;justify-content:space-between;min-height:100vh}.theme-container .page{padding-top:var(--navbar-height);-webkit-padding-start:calc(var(--sidebar-space) + 2rem);padding-inline-start:calc(var(--sidebar-space) + 2rem)}@media (max-width: 719px){.theme-container .page{-webkit-padding-start:0;padding-inline-start:0;-webkit-padding-end:0;padding-inline-end:0}}@media (min-width: 1440px){.theme-container .page{-webkit-padding-end:calc(100vw - var(--content-width) - var(--sidebar-space) - 6rem);padding-inline-end:calc(100vw - var(--content-width) - var(--sidebar-space) - 6rem)}}.theme-container .sidebar{top:var(--navbar-height)}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}@media (max-width: 719px){.theme-container.no-navbar .sidebar{top:0}}@media (max-width: 719px){.theme-container.hide-navbar .sidebar{top:0}}.theme-container.sidebar-collapsed .page{-webkit-padding-start:0;padding-inline-start:0}.theme-container.sidebar-collapsed .sidebar{box-shadow:none;transform:translate(-100%)}html[dir=rtl] .theme-container.sidebar-collapsed .sidebar{transform:translate(100%)}.theme-container.sidebar-collapsed .toggle-sidebar-wrapper{left:0}html[dir=rtl] .theme-container.sidebar-collapsed .toggle-sidebar-wrapper{right:0;left:unset}.theme-container.no-sidebar .page{-webkit-padding-start:0;padding-inline-start:0;-webkit-padding-end:0;padding-inline-end:0}@media (min-width: 1440px){.theme-container.no-sidebar.has-toc .page{-webkit-padding-end:16rem;padding-inline-end:16rem}}.theme-container.no-sidebar .toggle-sidebar-button,.theme-container.no-sidebar .toggle-sidebar-wrapper,.theme-container.no-sidebar .sidebar{display:none}.theme-container.sidebar-open .sidebar{box-shadow:2px 0 8px var(--card-shadow);transform:translate(0)}.fade-slide-y-enter-active{transition:all .3s ease!important}.fade-slide-y-leave-active{transition:all .3s cubic-bezier(1,.5,.8,1)!important}.fade-slide-y-enter-from,.fade-slide-y-leave-to{opacity:0;transform:translateY(10px)}@keyframes bounce{0%{transform:scale(1) translateY(0)}10%{transform:scale(1.1,.9) translateY(0)}30%{transform:scale(.9,1.1) translateY(-.5em)}50%{transform:scale(1.05,.95) translateY(0)}57%{transform:scale(1) translateY(-.125em)}64%{transform:scale(1) translateY(0)}to{transform:scale(1) translateY(0)}}.feature-header{margin:0 1.75rem}.feature-wrapper{display:flex;flex-wrap:wrap;align-content:stretch;align-items:stretch;justify-content:center}@media print{.feature-wrapper{display:block}}.feature-wrapper:first-child{border-top:1px solid var(--border-color);transition:border-color var(--color-transition)}.feature-item{position:relative;flex-basis:calc(33% - 3rem);margin:.5rem;padding:1rem;border-radius:.5rem;transition:background var(--color-transition),box-shadow var(--color-transition),transform var(--transform-transition)}@media (min-width: 1440px){.feature-item{flex-basis:calc(25% - 3rem)}}@media (max-width: 959px){.feature-item{flex-basis:calc(50% - 3rem)}}@media (max-width: 719px){.feature-item{flex-basis:100%;font-size:.95rem}}@media (max-width: 419px){.feature-item{margin:.5rem 0;font-size:.9rem}}.feature-item.link{cursor:pointer}@media print{.feature-item.link{text-decoration:none}}.feature-item .icon{display:inline-block;width:1.1em;-webkit-margin-end:.5rem;margin-inline-end:.5rem;color:var(--theme-color);font-weight:400;font-size:1.1em}.feature-item:hover{background-color:var(--bg-color-secondary);box-shadow:0 2px 12px 0 var(--card-shadow);transform:scale(1.05)}.feature-item:only-child{flex-basis:100%}.feature-item:first-child:nth-last-child(2),.feature-item:nth-child(2):last-child{flex-basis:calc(50% - 3rem)}@media (max-width: 719px){.feature-item:first-child:nth-last-child(2),.feature-item:nth-child(2):last-child{flex-basis:100%}}.feature-item h3{margin:.25rem 0 .5rem;color:var(--text-color-light);font-weight:700;font-size:1.3rem}@media (max-width: 419px){.feature-item h3{font-size:1.2rem}}.feature-item p{margin:0;color:var(--text-color-lighter);line-height:1.4}.footer-wrapper{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-evenly;padding-top:.75rem;padding-bottom:.75rem;-webkit-padding-start:calc(var(--sidebar-space) + 2rem);padding-inline-start:calc(var(--sidebar-space) + 2rem);-webkit-padding-end:2rem;padding-inline-end:2rem;border-top:1px solid var(--border-color);background:var(--bg-color);color:var(--dark-grey);text-align:center;transition:border-top-color var(--color-transition),background var(--color-transition),padding var(--transform-transition)}@media (max-width: 719px){.footer-wrapper{-webkit-padding-start:2rem;padding-inline-start:2rem}}@media (min-width: 1440px){.footer-wrapper{z-index:50;-webkit-padding-start:2rem;padding-inline-start:2rem}}@media print{.footer-wrapper{margin:0!important;padding:0!important}}@media (max-width: 419px){.footer-wrapper{display:block}}.no-sidebar .footer-wrapper,.sidebar-collapsed .footer-wrapper{-webkit-padding-start:2rem;padding-inline-start:2rem}.footer-wrapper .footer{margin:.5rem 1rem;font-size:14px}@media print{.footer-wrapper .footer{display:none}}.footer-wrapper .copyright{margin:6px 0;font-size:13px}.page:not(.not-found)+.footer-wrapper{margin-top:-2rem}@media (min-width: 959px){.hero-info-wrapper{display:flex;align-items:center;justify-content:space-evenly}}.hero-info-wrapper img{display:block;max-width:100%;max-height:18rem;margin:1rem}@media (max-width: 959px){.hero-info-wrapper img{margin:2rem auto}}@media (max-width: 719px){.hero-info-wrapper img{max-height:16rem;margin:1.5rem auto}}@media (max-width: 419px){.hero-info-wrapper img{max-height:14rem}}.hero-info-wrapper img.light{display:block}html[data-theme=dark] .hero-info-wrapper img.light,.hero-info-wrapper img.dark{display:none}html[data-theme=dark] .hero-info-wrapper img.dark{display:block}.hero-info-wrapper h1{margin:.5rem 0;background:linear-gradient(120deg,var(--theme-color-light),var(--theme-color) 30%,#3e71af 100%);-webkit-background-clip:text;background-clip:text;font-weight:700;font-size:3.6rem;line-height:1.5;-webkit-text-fill-color:transparent}@media (max-width: 719px){.hero-info-wrapper h1{margin:0;font-size:2.5rem}}@media (max-width: 959px){.hero-info-wrapper h1{text-align:center}}@media (max-width: 419px){.hero-info-wrapper h1{margin:0 auto;font-size:2rem}}.hero-info-wrapper .description,.hero-info-wrapper .actions{margin:1.8rem 0}@media (max-width: 719px){.hero-info-wrapper .description,.hero-info-wrapper .actions{margin:1.5rem 0}}@media (max-width: 959px){.hero-info-wrapper .description,.hero-info-wrapper .actions{margin:1.5rem auto;text-align:center}}@media (max-width: 419px){.hero-info-wrapper .description,.hero-info-wrapper .actions{margin:1.2rem 0}}.hero-info-wrapper .description{max-width:35rem;color:var(--text-color-bright);font-weight:500;font-size:1.6rem;line-height:1.3}@media (max-width: 719px){.hero-info-wrapper .description{font-size:1.4rem}}@media (max-width: 419px){.hero-info-wrapper .description{font-size:1.2rem}}.hero-info-wrapper .action-button{display:inline-block;overflow:hidden;min-width:4rem;margin:.5rem;padding:.5em 1.5rem;border-radius:2rem;background:var(--bg-color-secondary);color:var(--text-color);font-size:1.2rem;text-align:center;transition:color var(--color-transition),color var(--color-transition),transform var(--transform-transition)}@media (max-width: 719px){.hero-info-wrapper .action-button{padding:.5rem 1rem;font-size:1.1rem}}@media (max-width: 419px){.hero-info-wrapper .action-button{font-size:1rem}}@media print{.hero-info-wrapper .action-button{text-decoration:none}}.hero-info-wrapper .action-button:hover{background:var(--bg-color-tertiary)}.hero-info-wrapper .action-button.primary{border-color:var(--theme-color);background:var(--theme-color);color:var(--white)}.hero-info-wrapper .action-button.primary:hover{border-color:var(--theme-color-light);background:var(--theme-color-light)}.home.project:not(.pure) .hero-info-wrapper .action-button:active{transform:scale(.96)}.home.project{--content-width: var(--home-page-width);display:block;flex:1;padding-top:var(--navbar-height)}.home.project .hero-info-wrapper{max-width:var(--home-page-width);-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem;-webkit-padding-end:2.5rem;padding-inline-end:2.5rem}@media (max-width: 959px){.home.project .hero-info-wrapper{-webkit-padding-start:1.5em;padding-inline-start:1.5em;-webkit-padding-end:1.5rem;padding-inline-end:1.5rem}}@media print{.home.project .hero-info-wrapper{max-width:unset}}.home.project .feature-panel{max-width:var(--home-page-width);-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;-webkit-padding-start:1rem;padding-inline-start:1rem;-webkit-padding-end:1rem;padding-inline-end:1rem}@media print{.home.project .feature-panel{max-width:unset}}.home.project .theme-hope-content{padding-bottom:1.5rem}.not-found-hint{padding:2rem}.not-found-hint .error-code{margin:0;font-weight:700;font-size:4rem;line-height:4rem}.not-found-hint .error-title{font-weight:700}.not-found-hint .error-hint{margin:0;padding:12px 0;font-weight:600;font-size:20px;line-height:20px;letter-spacing:2px}.page.not-found{display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box;width:100vw;max-width:var(--home-page-width);margin:0 auto;padding:calc(var(--navbar-height) + 1rem) 1rem 1rem!important;text-align:center}.page.not-found .action-button{display:inline-block;box-sizing:border-box;margin:.25rem;padding:.75rem 1rem;border-width:0;border-bottom:1px solid var(--theme-color-dark);border-radius:3rem;background:var(--theme-color);color:var(--white);outline:none;font-size:1rem;transition:background .1s ease}.page.not-found .action-button:hover{background:var(--theme-color-light);cursor:pointer}.page-nav{max-width:var(--content-width, 740px);-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem;-webkit-padding-end:2.5rem;padding-inline-end:2.5rem;display:flex;flex-wrap:wrap;min-height:2rem;margin-top:0;padding-top:.5rem;padding-bottom:.5rem;border-top:1px solid var(--border-color);transition:border-top var(--color-transition)}@media (max-width: 959px){.page-nav{-webkit-padding-start:1.5rem;padding-inline-start:1.5rem;-webkit-padding-end:1.5rem;padding-inline-end:1.5rem}}@media print{.page-nav{max-width:unset}}@media print{.page-nav{display:none}}.page-nav .nav-link{display:inline-block;flex-grow:1;margin:.25rem;padding:.25rem .5rem;border:1px solid var(--border-color);border-radius:.25rem}.page-nav .nav-link:hover{background:var(--bg-color-secondary)}.page-nav .nav-link .hint{color:var(--light-grey);font-size:.875rem;line-height:2}.page-nav .nav-link .arrow{display:inline-block;vertical-align:middle;width:1em;height:1em;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");line-height:normal;transition:all .3s;font-size:.75rem}html[data-theme=dark] .page-nav .nav-link .arrow{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.page-nav .nav-link .arrow.down{transform:rotate(180deg)}html[dir=rtl] .page-nav .nav-link .arrow.down{transform:rotate(-180deg)}.page-nav .nav-link .arrow.end{transform:rotate(90deg)}html[dir=rtl] .page-nav .nav-link .arrow.end,.page-nav .nav-link .arrow.start{transform:rotate(-90deg)}html[dir=rtl] .page-nav .nav-link .arrow.start{transform:rotate(90deg)}.page-nav .prev{text-align:start}.page-nav .prev .icon{-webkit-margin-end:.25em;margin-inline-end:.25em}.page-nav .next{text-align:end}.page-nav .next .icon{-webkit-margin-start:.25em;margin-inline-start:.25em}.page-title{max-width:var(--content-width, 740px);-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem;-webkit-padding-end:2.5rem;padding-inline-end:2.5rem;position:relative;z-index:1;padding-top:1rem;padding-bottom:0}@media (max-width: 959px){.page-title{-webkit-padding-start:1.5rem;padding-inline-start:1.5rem;-webkit-padding-end:1.5rem;padding-inline-end:1.5rem}}@media print{.page-title{max-width:unset}}@media print{.page-title{-webkit-padding-start:0!important;padding-inline-start:0!important;-webkit-padding-end:0!important;padding-inline-end:0!important}}@media (max-width: 959px){.page-title{padding-top:.5rem}}.page-title h1{margin-top:calc(0px - var(--navbar-height))!important;margin-bottom:1rem;padding-top:var(--navbar-height)!important;font-size:2.2rem}@media (max-width: 959px){.page-title h1{margin-bottom:.5rem}}.page-title h1 .icon{-webkit-margin-end:.25em;margin-inline-end:.25em;color:var(--theme-color);font-size:.9em}.theme-hope-content:not(.custom){padding-top:0!important}.theme-hope-content:not(.custom) h1:first-child,.theme-hope-content:not(.custom) h2:first-child,.theme-hope-content:not(.custom) h3:first-child,.theme-hope-content:not(.custom) h4:first-child,.theme-hope-content:not(.custom) h5:first-child,.theme-hope-content:not(.custom) h6:first-child{margin-top:calc(.5rem - var(--navbar-height))!important;padding-top:var(--navbar-height)!important}.theme-hope-content:not(.custom)>h1:first-child{display:none}.page{display:block;flex-grow:1;padding-bottom:2rem;transition:padding var(--transform-transition)}@media print{.page{min-height:auto!important;margin:0!important;padding:0!important}}.page-cover{-o-object-fit:cover;object-fit:cover;width:calc(100% + 2rem);max-height:25vh;margin-left:-2rem}@media (max-width: 719px){.page-cover{width:100%;margin:0;border-radius:0}}@media (min-width: 1440px){.page-cover{width:calc(100% - 2rem);margin:0 1rem;border-radius:.5rem}}.page-cover .sidebar-collapsed{width:100%;margin-left:0}.skip-link{top:.25rem;left:.25rem;z-index:999;padding:.65rem 1.5rem;border-radius:.5rem;background:var(--bg-color);color:var(--theme-color);box-shadow:var(--card-shadow);font-weight:700;font-size:.9em;text-decoration:none}@media print{.skip-link{display:none}}.skip-link:focus{clip:auto;width:auto;height:auto;-webkit-clip-path:none;clip-path:none}.theme-hope-content pre{overflow:auto;margin:.85rem 0;padding:1rem;border-radius:6px;line-height:1.375}.theme-hope-content pre code{padding:0;border-radius:0;background:transparent!important;color:var(--code-color);font-family:var(--font-family-mono);text-align:left;white-space:pre;word-spacing:normal;word-wrap:normal;word-break:normal;overflow-wrap:unset;-webkit-hyphens:none;hyphens:none;transition:color var(--color-transition);-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}@media print{.theme-hope-content pre code{white-space:pre-wrap}}.theme-hope-content .line-number{font-family:var(--font-family-mono)}div[class*=language-]{position:relative;border-radius:6px;background:var(--code-bg-color);transition:background var(--color-transition)}@media (max-width: 419px){.theme-hope-content>div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}div[class*=language-]:before{content:attr(data-ext);position:absolute;top:.8em;right:1em;z-index:3;color:var(--code-line-color);font-size:.75rem;transition:color var(--color-transition)}div[class*=language-] pre{position:relative;z-index:1}div[class*=language-] .highlight-lines{position:absolute;top:0;left:0;width:100%;padding-top:1rem;line-height:1.375;-webkit-user-select:none;-moz-user-select:none;user-select:none}div[class*=language-] .highlight-line{background:var(--code-highlight-line-color);transition:background var(--color-transition)}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;z-index:2;width:var(--line-numbers-width);height:100%;border-right:1px solid var(--code-highlight-line-color);border-radius:6px 0 0 6px;transition:border-color var(--color-transition)}@media (max-width: 419px){div[class*=language-].line-numbers-mode:after{border-radius:0}}@media print{div[class*=language-].line-numbers-mode:after{display:none}}div[class*=language-].line-numbers-mode .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-line:before{content:" ";position:absolute;top:0;left:0;z-index:3;display:block;width:var(--line-numbers-width);height:100%}div[class*=language-].line-numbers-mode pre{vertical-align:middle;margin-left:var(--line-numbers-width);padding-left:.5rem}@media print{div[class*=language-].line-numbers-mode pre{margin-left:0;padding-left:1rem}}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;left:0;width:var(--line-numbers-width);padding:1rem 0;color:var(--code-line-color);line-height:1.375;counter-reset:line-number;text-align:center;transition:color var(--color-transition);transform:translateY(1px)}@media print{div[class*=language-].line-numbers-mode .line-numbers{display:none}}div[class*=language-].line-numbers-mode .line-number{position:relative;z-index:4;height:1.375em;-webkit-user-select:none;-moz-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-number:before{content:counter(line-number);font-size:.85em;counter-increment:line-number}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}:root{--code-color: #D8DEE9;--code-line-color: rgba(216, 222, 233, .67);--code-bg-color: #2e3440;--code-border-color: #38455e;--code-highlight-line-color: #343d4e}div[class*=language-] pre{background:transparent!important}.sr-only{position:absolute;overflow:hidden;clip:rect(0,0,0,0);width:1px;height:1px;margin:-1px;padding:0;border-width:0;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;user-select:none}@media print{.theme-hope-content{margin:0!important;-webkit-padding-start:0!important;padding-inline-start:0!important;-webkit-padding-end:0!important;padding-inline-end:0!important}}.theme-hope-content.custom{margin:0;padding:0}.theme-hope-content:not(.custom){max-width:var(--content-width, 740px);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.theme-hope-content:not(.custom){padding:1.5rem}}@media (max-width: 419px){.theme-hope-content:not(.custom){padding:1rem 1.5rem}}@media print{.theme-hope-content:not(.custom){max-width:unset}}.theme-hope-content:not(.custom)>h1,.theme-hope-content:not(.custom)>h2,.theme-hope-content:not(.custom)>h3,.theme-hope-content:not(.custom)>h4,.theme-hope-content:not(.custom)>h5,.theme-hope-content:not(.custom)>h6{margin-top:calc(.5rem - var(--navbar-height));margin-bottom:.5rem;padding-top:calc(1rem + var(--navbar-height));outline:none}.theme-container.no-navbar .theme-hope-content:not(.custom)>h1,.theme-container.no-navbar .theme-hope-content:not(.custom)>h2,.theme-container.no-navbar .theme-hope-content:not(.custom)>h3,.theme-container.no-navbar .theme-hope-content:not(.custom)>h4,.theme-container.no-navbar .theme-hope-content:not(.custom)>h5,.theme-container.no-navbar .theme-hope-content:not(.custom)>h6{margin-top:1.5rem;padding-top:0}.theme-hope-content:not(.custom)>p,.theme-hope-content:not(.custom)>ul p,.theme-hope-content:not(.custom)>ol p{text-align:justify;overflow-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}@media (max-width: 419px){.theme-hope-content:not(.custom)>p,.theme-hope-content:not(.custom)>ul p,.theme-hope-content:not(.custom)>ol p{text-align:start}}@media print{.theme-hope-content:not(.custom)>p,.theme-hope-content:not(.custom)>ul p,.theme-hope-content:not(.custom)>ol p{text-align:start}}.theme-hope-content a:hover{text-decoration:underline}.theme-hope-content img{max-width:100%}@media (min-width: 1280px){.chart-wrapper::-webkit-scrollbar,.flowchart-wrapper::-webkit-scrollbar,.mermaid-wrapper::-webkit-scrollbar{width:8px;height:8px}.chart-wrapper::-webkit-scrollbar-track-piece,.flowchart-wrapper::-webkit-scrollbar-track-piece,.mermaid-wrapper::-webkit-scrollbar-track-piece{border-radius:8px;background:rgba(0,0,0,.1)}}html[dir=rtl] a.header-anchor{float:right}#docsearch-container{min-width:145.7px!important}@media (max-width: 959px){#docsearch-container{min-width:36px!important}}.DocSearch.DocSearch-Button{margin-left:0}@media (max-width: 959px){.DocSearch.DocSearch-Button{min-width:36px!important}}.DocSearch .DocSearch-Button-Placeholder{display:inline-block;padding:4px 12px 4px 6px;font-size:14px}@media (max-width: 719px){.DocSearch .DocSearch-Button-Placeholder{display:none}}.DocSearch .DocSearch-Search-Icon{width:1.25em;height:1.25em}@media (max-width: 959px){.DocSearch .DocSearch-Button-Keys{display:none}}.DocSearch .DocSearch-Button-Key{background:var(--bg-color);box-shadow:none}@media (prefers-reduced-motion: no-preference){:root{scroll-behavior:smooth}}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track-piece{border-radius:6px;background:rgba(0,0,0,.1)}::-webkit-scrollbar-thumb{border-radius:6px;background:var(--theme-color)}::-webkit-scrollbar-thumb:active{background:var(--theme-color-light)}@media (max-width: 719px){.hide-in-mobile{display:none!important}}@media (max-width: 959px){.hide-in-pad{display:none!important}}.page-author-item{display:inline-block;margin:0 4px;font-weight:400;overflow-wrap:break-word}.page-category-info{flex-wrap:wrap}.page-category-item{display:inline-block;margin:.125em .25em;padding:0 .25em;border-radius:.25em;background:var(--bg-color-secondary);color:var(--text-color-light);font-weight:700;font-size:.75rem;line-height:2;transition:background var(--color-transition),color var(--color-transition)}@media print{.page-category-item{padding:0;font-weight:400}.page-category-item:after{content:", "}.page-category-item:last-of-type:after{content:""}}.page-category-item.clickable>span:hover{color:var(--theme-color);cursor:pointer}.page-category-item.category0{background:#fde5e7;color:#ec2f3e}html[data-theme=dark] .page-category-item.category0{background:#340509;color:#ba111f}.page-category-item.category0:hover{background:#f9bec3}html[data-theme=dark] .page-category-item.category0:hover{background:#53080e}.page-category-item.category1{background:#ffeee8;color:#fb7649}html[data-theme=dark] .page-category-item.category1{background:#441201;color:#f54205}.page-category-item.category1:hover{background:#fed4c6}html[data-theme=dark] .page-category-item.category1:hover{background:#6d1d02}.page-category-item.category2{background:#fef5e7;color:#f5b041}html[data-theme=dark] .page-category-item.category2{background:#3e2703;color:#e08e0b}.page-category-item.category2:hover{background:#fce6c4}html[data-theme=dark] .page-category-item.category2:hover{background:#633f05}.page-category-item.category3{background:#eafaf1;color:#55d98d}html[data-theme=dark] .page-category-item.category3{background:#0c331c;color:#29b866}.page-category-item.category3:hover{background:#caf3db}html[data-theme=dark] .page-category-item.category3:hover{background:#12522d}.page-category-item.category4{background:#e6f9ee;color:#36d278}html[data-theme=dark] .page-category-item.category4{background:#092917;color:#219552}.page-category-item.category4:hover{background:#c0f1d5}html[data-theme=dark] .page-category-item.category4:hover{background:#0f4224}.page-category-item.category5{background:#e1fcfc;color:#16e1e1}html[data-theme=dark] .page-category-item.category5{background:#042929;color:#0e9595}.page-category-item.category5:hover{background:#b4f8f8}html[data-theme=dark] .page-category-item.category5:hover{background:#064242}.page-category-item.category6{background:#e4f0fe;color:#2589f6}html[data-theme=dark] .page-category-item.category6{background:#021b36;color:#0862c3}.page-category-item.category6:hover{background:#bbdafc}html[data-theme=dark] .page-category-item.category6:hover{background:#042c57}.page-category-item.category7{background:#f7f1fd;color:#bb8ced}html[data-theme=dark] .page-category-item.category7{background:#2a0b4b;color:#9851e4}.page-category-item.category7:hover{background:#eadbfa}html[data-theme=dark] .page-category-item.category7:hover{background:#431277}.page-category-item.category8{background:#fdeaf5;color:#ef59ab}html[data-theme=dark] .page-category-item.category8{background:#400626;color:#e81689}.page-category-item.category8:hover{background:#facbe5}html[data-theme=dark] .page-category-item.category8:hover{background:#670a3d}.page-original-info{position:relative;display:inline-block;vertical-align:middle;overflow:hidden;padding:0 .5em;border:.5px solid var(--dark-grey);border-radius:.75em;background:var(--bg-color);font-size:.75em;line-height:1.5}.page-info{display:flex;flex-wrap:wrap;align-content:stretch;align-items:center;justify-content:flex-start;color:var(--dark-grey);font-size:14px}@media print{.page-info{display:flex!important}}.page-info>span{display:flex;align-items:center;max-width:100%;-webkit-margin-end:.5em;margin-inline-end:.5em;line-height:2}@media (min-width: 1440px){.page-info>span{font-size:1.1em}}@media (max-width: 419px){.page-info>span{-webkit-margin-end:.3em;margin-inline-end:.3em;font-size:.875em}}@media print{.page-info>span{display:flex!important}}.page-info .icon{position:relative;display:inline-block;vertical-align:middle;width:1em;height:1em;-webkit-margin-end:.25em;margin-inline-end:.25em}.page-info a{color:inherit}.page-info a:hover,.page-info a:active{color:var(--theme-color)}.page-meta{max-width:var(--content-width, 740px);-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem;-webkit-padding-end:2.5rem;padding-inline-end:2.5rem;display:flex;flex-wrap:wrap;justify-content:space-between;overflow:auto;padding-top:.75rem;padding-bottom:.75rem}@media (max-width: 959px){.page-meta{-webkit-padding-start:1.5rem;padding-inline-start:1.5rem;-webkit-padding-end:1.5rem;padding-inline-end:1.5rem}}@media print{.page-meta{max-width:unset}}@media print{.page-meta{margin:0!important;-webkit-padding-start:0!important;padding-inline-start:0!important;-webkit-padding-end:0!important;padding-inline-end:0!important}}@media (max-width: 719px){.page-meta{display:block}}.page-meta .meta-item{flex-grow:1}.page-meta .meta-item .label{font-weight:500}.page-meta .meta-item .label:not(a){color:var(--text-color-lighter)}.page-meta .meta-item .info{color:var(--dark-grey);font-weight:400}.page-meta .git-info{text-align:end}.page-meta .edit-link{margin-top:.25rem;margin-bottom:.25rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem;font-size:14px}@media print{.page-meta .edit-link{display:none}}.page-meta .edit-link .icon{position:relative;bottom:-.125em;width:1em;height:1em;-webkit-margin-end:.25em;margin-inline-end:.25em}.page-meta .update-time,.page-meta .contributors{margin-top:.25rem;margin-bottom:.25rem;font-size:14px}@media (max-width: 719px){.page-meta .update-time,.page-meta .contributors{font-size:13px;text-align:start}}.print-button{border-width:0;background:transparent;cursor:pointer;box-sizing:content-box;width:1rem;height:1rem;padding:.5rem;border-radius:.25em;color:inherit;font-size:1rem;transform:translateY(.25rem)}@media print{.print-button{display:none}}.page-tag-info{flex-wrap:wrap}.page-tag-item{position:relative;display:inline-block;vertical-align:middle;overflow:hidden;min-width:1.5rem;margin:.125rem;padding:.125rem .25rem .125rem .625rem;background:var(--bg-color-secondary);background:linear-gradient(135deg,transparent .75em,var(--bg-color-secondary) 0) top,linear-gradient(45deg,transparent .75em,var(--bg-color-secondary) 0) bottom;background-size:100% 52%!important;background-repeat:no-repeat!important;color:var(--text-color-light);font-weight:700;font-size:.625rem;line-height:1.5;text-align:center;transition:background var(--color-transition),color var(--color-transition)}@media print{.page-tag-item{padding:0;font-weight:400}.page-tag-item:after{content:", "}.page-tag-item:last-of-type:after{content:""}}.page-tag-item.clickable:hover{cursor:pointer}.page-tag-item.tag0{background:#fde5e7;background:linear-gradient(135deg,transparent .75em,#fde5e7 0) top,linear-gradient(45deg,transparent .75em,#fde5e7 0) bottom;color:#ec2f3e}html[data-theme=dark] .page-tag-item.tag0{background:#340509;background:linear-gradient(135deg,transparent .75em,#340509 0) top,linear-gradient(45deg,transparent .75em,#340509 0) bottom;color:#ba111f}.page-tag-item.tag0.clickable:hover{background:#f9bec3;background:linear-gradient(135deg,transparent .75em,#f9bec3 0) top,linear-gradient(45deg,transparent .75em,#f9bec3 0) bottom}html[data-theme=dark] .page-tag-item.tag0.clickable:hover{background:#53080e;background:linear-gradient(135deg,transparent .75em,#53080e 0) top,linear-gradient(45deg,transparent .75em,#53080e 0) bottom}.page-tag-item.tag1{background:#ffeee8;background:linear-gradient(135deg,transparent .75em,#ffeee8 0) top,linear-gradient(45deg,transparent .75em,#ffeee8 0) bottom;color:#fb7649}html[data-theme=dark] .page-tag-item.tag1{background:#441201;background:linear-gradient(135deg,transparent .75em,#441201 0) top,linear-gradient(45deg,transparent .75em,#441201 0) bottom;color:#f54205}.page-tag-item.tag1.clickable:hover{background:#fed4c6;background:linear-gradient(135deg,transparent .75em,#fed4c6 0) top,linear-gradient(45deg,transparent .75em,#fed4c6 0) bottom}html[data-theme=dark] .page-tag-item.tag1.clickable:hover{background:#6d1d02;background:linear-gradient(135deg,transparent .75em,#6d1d02 0) top,linear-gradient(45deg,transparent .75em,#6d1d02 0) bottom}.page-tag-item.tag2{background:#fef5e7;background:linear-gradient(135deg,transparent .75em,#fef5e7 0) top,linear-gradient(45deg,transparent .75em,#fef5e7 0) bottom;color:#f5b041}html[data-theme=dark] .page-tag-item.tag2{background:#3e2703;background:linear-gradient(135deg,transparent .75em,#3e2703 0) top,linear-gradient(45deg,transparent .75em,#3e2703 0) bottom;color:#e08e0b}.page-tag-item.tag2.clickable:hover{background:#fce6c4;background:linear-gradient(135deg,transparent .75em,#fce6c4 0) top,linear-gradient(45deg,transparent .75em,#fce6c4 0) bottom}html[data-theme=dark] .page-tag-item.tag2.clickable:hover{background:#633f05;background:linear-gradient(135deg,transparent .75em,#633f05 0) top,linear-gradient(45deg,transparent .75em,#633f05 0) bottom}.page-tag-item.tag3{background:#eafaf1;background:linear-gradient(135deg,transparent .75em,#eafaf1 0) top,linear-gradient(45deg,transparent .75em,#eafaf1 0) bottom;color:#55d98d}html[data-theme=dark] .page-tag-item.tag3{background:#0c331c;background:linear-gradient(135deg,transparent .75em,#0c331c 0) top,linear-gradient(45deg,transparent .75em,#0c331c 0) bottom;color:#29b866}.page-tag-item.tag3.clickable:hover{background:#caf3db;background:linear-gradient(135deg,transparent .75em,#caf3db 0) top,linear-gradient(45deg,transparent .75em,#caf3db 0) bottom}html[data-theme=dark] .page-tag-item.tag3.clickable:hover{background:#12522d;background:linear-gradient(135deg,transparent .75em,#12522d 0) top,linear-gradient(45deg,transparent .75em,#12522d 0) bottom}.page-tag-item.tag4{background:#e6f9ee;background:linear-gradient(135deg,transparent .75em,#e6f9ee 0) top,linear-gradient(45deg,transparent .75em,#e6f9ee 0) bottom;color:#36d278}html[data-theme=dark] .page-tag-item.tag4{background:#092917;background:linear-gradient(135deg,transparent .75em,#092917 0) top,linear-gradient(45deg,transparent .75em,#092917 0) bottom;color:#219552}.page-tag-item.tag4.clickable:hover{background:#c0f1d5;background:linear-gradient(135deg,transparent .75em,#c0f1d5 0) top,linear-gradient(45deg,transparent .75em,#c0f1d5 0) bottom}html[data-theme=dark] .page-tag-item.tag4.clickable:hover{background:#0f4224;background:linear-gradient(135deg,transparent .75em,#0f4224 0) top,linear-gradient(45deg,transparent .75em,#0f4224 0) bottom}.page-tag-item.tag5{background:#e1fcfc;background:linear-gradient(135deg,transparent .75em,#e1fcfc 0) top,linear-gradient(45deg,transparent .75em,#e1fcfc 0) bottom;color:#16e1e1}html[data-theme=dark] .page-tag-item.tag5{background:#042929;background:linear-gradient(135deg,transparent .75em,#042929 0) top,linear-gradient(45deg,transparent .75em,#042929 0) bottom;color:#0e9595}.page-tag-item.tag5.clickable:hover{background:#b4f8f8;background:linear-gradient(135deg,transparent .75em,#b4f8f8 0) top,linear-gradient(45deg,transparent .75em,#b4f8f8 0) bottom}html[data-theme=dark] .page-tag-item.tag5.clickable:hover{background:#064242;background:linear-gradient(135deg,transparent .75em,#064242 0) top,linear-gradient(45deg,transparent .75em,#064242 0) bottom}.page-tag-item.tag6{background:#e4f0fe;background:linear-gradient(135deg,transparent .75em,#e4f0fe 0) top,linear-gradient(45deg,transparent .75em,#e4f0fe 0) bottom;color:#2589f6}html[data-theme=dark] .page-tag-item.tag6{background:#021b36;background:linear-gradient(135deg,transparent .75em,#021b36 0) top,linear-gradient(45deg,transparent .75em,#021b36 0) bottom;color:#0862c3}.page-tag-item.tag6.clickable:hover{background:#bbdafc;background:linear-gradient(135deg,transparent .75em,#bbdafc 0) top,linear-gradient(45deg,transparent .75em,#bbdafc 0) bottom}html[data-theme=dark] .page-tag-item.tag6.clickable:hover{background:#042c57;background:linear-gradient(135deg,transparent .75em,#042c57 0) top,linear-gradient(45deg,transparent .75em,#042c57 0) bottom}.page-tag-item.tag7{background:#f7f1fd;background:linear-gradient(135deg,transparent .75em,#f7f1fd 0) top,linear-gradient(45deg,transparent .75em,#f7f1fd 0) bottom;color:#bb8ced}html[data-theme=dark] .page-tag-item.tag7{background:#2a0b4b;background:linear-gradient(135deg,transparent .75em,#2a0b4b 0) top,linear-gradient(45deg,transparent .75em,#2a0b4b 0) bottom;color:#9851e4}.page-tag-item.tag7.clickable:hover{background:#eadbfa;background:linear-gradient(135deg,transparent .75em,#eadbfa 0) top,linear-gradient(45deg,transparent .75em,#eadbfa 0) bottom}html[data-theme=dark] .page-tag-item.tag7.clickable:hover{background:#431277;background:linear-gradient(135deg,transparent .75em,#431277 0) top,linear-gradient(45deg,transparent .75em,#431277 0) bottom}.page-tag-item.tag8{background:#fdeaf5;background:linear-gradient(135deg,transparent .75em,#fdeaf5 0) top,linear-gradient(45deg,transparent .75em,#fdeaf5 0) bottom;color:#ef59ab}html[data-theme=dark] .page-tag-item.tag8{background:#400626;background:linear-gradient(135deg,transparent .75em,#400626 0) top,linear-gradient(45deg,transparent .75em,#400626 0) bottom;color:#e81689}.page-tag-item.tag8.clickable:hover{background:#facbe5;background:linear-gradient(135deg,transparent .75em,#facbe5 0) top,linear-gradient(45deg,transparent .75em,#facbe5 0) bottom}html[data-theme=dark] .page-tag-item.tag8.clickable:hover{background:#670a3d;background:linear-gradient(135deg,transparent .75em,#670a3d 0) top,linear-gradient(45deg,transparent .75em,#670a3d 0) bottom}.toc-place-holder{-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem;-webkit-padding-end:2.5rem;padding-inline-end:2.5rem;position:sticky;top:calc(var(--navbar-height) + 2rem);z-index:99;max-width:var(--content-width, 740px)}@media (max-width: 959px){.toc-place-holder{-webkit-padding-start:1.5rem;padding-inline-start:1.5rem;-webkit-padding-end:1.5rem;padding-inline-end:1.5rem}}@media print{.toc-place-holder{max-width:unset}}.toc-place-holder+.theme-hope-content:not(.custom){padding-top:0}#toc{position:absolute;left:calc(100% + 1rem);display:none;min-width:10rem;max-width:15rem}@media (min-width: 1440px){.has-toc #toc{display:block}}@media print{#toc{display:none!important}}html[dir=rtl] #toc{right:calc(100% + 1rem);left:unset}#toc .toc-header{margin-bottom:.75rem;-webkit-margin-start:.5rem;margin-inline-start:.5rem;font-weight:600;font-size:.875rem}#toc .toc-wrapper{position:relative;overflow-x:hidden;overflow-y:auto;max-height:75vh;margin:0 .5rem;-webkit-padding-start:8px;padding-inline-start:8px;text-overflow:ellipsis;white-space:nowrap;scroll-behavior:smooth}#toc .toc-wrapper::-webkit-scrollbar-track-piece{background:transparent}#toc .toc-wrapper::-webkit-scrollbar{width:3px}#toc .toc-wrapper::-webkit-scrollbar-thumb:vertical{background:#ddd}html[data-theme=dark] #toc .toc-wrapper::-webkit-scrollbar-thumb:vertical{background:#333}#toc .toc-wrapper:before{content:" ";position:absolute;top:0;bottom:0;left:0;z-index:-1;width:2px;background:var(--border-color)}html[dir=rtl] #toc .toc-wrapper:before{right:0;left:unset}#toc .toc-list{position:relative;margin:0;padding:0}#toc .toc-marker{content:" ";position:absolute;top:0;left:0;z-index:2;width:2px;height:1.7rem;background:var(--theme-color);transition:top var(--vp-tt)}html[dir=rtl] #toc .toc-marker{right:0;left:unset}#toc .toc-link{position:relative;display:block;overflow:hidden;max-width:100%;color:var(--light-grey);line-height:inherit;text-overflow:ellipsis;white-space:nowrap}#toc .toc-link.level2{-webkit-padding-start:0px;padding-inline-start:0px;font-size:14px}#toc .toc-link.level3{-webkit-padding-start:8px;padding-inline-start:8px;font-size:13px}#toc .toc-link.level4{-webkit-padding-start:16px;padding-inline-start:16px;font-size:12px}#toc .toc-link.level5{-webkit-padding-start:24px;padding-inline-start:24px;font-size:11px}#toc .toc-link.level6{-webkit-padding-start:32px;padding-inline-start:32px;font-size:10px}#toc .toc-item{position:relative;box-sizing:border-box;height:1.7rem;padding:0 .5rem;list-style:none;line-height:1.7rem}#toc .toc-item:hover>.toc-link{color:var(--theme-color)}#toc .toc-item.active>.toc-link{color:var(--theme-color);font-weight:700}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper:not(:hover) .arrow{transform:rotate(-180deg)}.dropdown-wrapper .dropdown-title{border-width:0;background:transparent;cursor:pointer;padding:0 .25rem;color:var(--dark-grey);font-weight:500;font-size:inherit;font-family:inherit;line-height:inherit;cursor:inherit}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .icon{-webkit-margin-end:.25em;margin-inline-end:.25em;font-size:1em}.dropdown-wrapper .dropdown-title .arrow{display:inline-block;vertical-align:middle;width:1em;height:1em;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");line-height:normal;transition:all .3s;font-size:1.2em}html[data-theme=dark] .dropdown-wrapper .dropdown-title .arrow{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.dropdown-wrapper .dropdown-title .arrow.down{transform:rotate(180deg)}html[dir=rtl] .dropdown-wrapper .dropdown-title .arrow.down{transform:rotate(-180deg)}.dropdown-wrapper .dropdown-title .arrow.end{transform:rotate(90deg)}html[dir=rtl] .dropdown-wrapper .dropdown-title .arrow.end,.dropdown-wrapper .dropdown-title .arrow.start{transform:rotate(-90deg)}html[dir=rtl] .dropdown-wrapper .dropdown-title .arrow.start{transform:rotate(90deg)}.dropdown-wrapper ul{margin:0;padding:0;list-style-type:none}.dropdown-wrapper .nav-dropdown{position:absolute;top:100%;right:0;overflow-y:auto;box-sizing:border-box;min-width:6rem;max-height:calc(100vh - var(--navbar-height));margin:0;padding:.5rem .75rem;border:1px solid var(--grey14);border-radius:.25rem;background:var(--bg-color);box-shadow:2px 2px 10px var(--card-shadow);text-align:start;white-space:nowrap;opacity:0;visibility:hidden;transition:all .18s ease-out;transform:scale(.8)}html[dir=rtl] .dropdown-wrapper .nav-dropdown{right:unset;left:0}.dropdown-wrapper:hover .nav-dropdown,.dropdown-wrapper.open .nav-dropdown{z-index:2;opacity:1;visibility:visible;transform:scale(1)}.dropdown-wrapper .nav-link{position:relative;display:block;margin-bottom:0;border-bottom:none;color:var(--dark-grey);font-weight:400;font-size:.875rem;line-height:1.7rem;transition:color var(--color-transition)}.dropdown-wrapper .nav-link:hover,.dropdown-wrapper .nav-link.active{color:var(--theme-color)}.dropdown-wrapper .dropdown-subtitle{margin:0;padding:.5rem .25rem 0;color:var(--light-grey);font-weight:600;font-size:.75rem;line-height:2;text-transform:uppercase;transition:color var(--color-transition)}.dropdown-wrapper .dropdown-subitem-wrapper{padding:0 0 .25rem}.dropdown-wrapper .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .dropdown-item:last-child .dropdown-subtitle{padding-top:0}.dropdown-wrapper .dropdown-item:last-child .dropdown-subitem-wrapper{padding-bottom:0}.nav-screen-dropdown-title{border-width:0;background:transparent;position:relative;display:flex;align-items:center;width:100%;padding:0;color:var(--dark-grey);font-size:inherit;font-family:inherit;text-align:start;cursor:pointer}.nav-screen-dropdown-title:hover,.nav-screen-dropdown-title.active{color:var(--text-color)}.nav-screen-dropdown-title .title{flex:1}.nav-screen-dropdown-title .arrow{display:inline-block;vertical-align:middle;width:1em;height:1em;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");line-height:normal;transition:all .3s}html[data-theme=dark] .nav-screen-dropdown-title .arrow{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.nav-screen-dropdown-title .arrow.down{transform:rotate(180deg)}html[dir=rtl] .nav-screen-dropdown-title .arrow.down{transform:rotate(-180deg)}.nav-screen-dropdown-title .arrow.end{transform:rotate(90deg)}html[dir=rtl] .nav-screen-dropdown-title .arrow.end,.nav-screen-dropdown-title .arrow.start{transform:rotate(-90deg)}html[dir=rtl] .nav-screen-dropdown-title .arrow.start{transform:rotate(90deg)}.nav-screen-dropdown{overflow:hidden;margin:.5rem 0 0;padding:0;list-style:none;transition:transform .1s ease-out;transform:scaleY(1);transform-origin:top}.nav-screen-dropdown.hide{height:0;margin:0;transform:scaleY(0)}.nav-screen-dropdown .nav-link{position:relative;display:block;-webkit-padding-start:.5rem;padding-inline-start:.5rem;font-weight:400;line-height:2}.nav-screen-dropdown .nav-link:hover,.nav-screen-dropdown .nav-link.active{color:var(--theme-color)}.nav-screen-dropdown .nav-link .icon{font-size:1em}.nav-screen-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.nav-screen-dropdown .dropdown-subtitle{margin:0;-webkit-padding-start:.25rem;padding-inline-start:.25rem;color:var(--light-grey);font-weight:600;font-size:.75rem;line-height:2;text-transform:uppercase;transition:color var(--color-transition)}.nav-screen-dropdown .dropdown-subtitle .nav-link{padding:0}.nav-screen-dropdown .dropdown-subitem-wrapper{margin:0;padding:0;list-style:none}.nav-screen-dropdown .dropdown-subitem{-webkit-padding-start:.5rem;padding-inline-start:.5rem;font-size:.9em}.nav-screen-links{display:none;padding-bottom:.75rem}@media (max-width: 719px){.nav-screen-links{display:block}}.nav-screen-links .navbar-links-item{position:relative;display:block;padding:12px 4px 11px 0;border-bottom:1px solid var(--border-color);font-size:16px;line-height:1.5rem;transition:border-bottom-color var(--color-transition)}.nav-screen-links .nav-link{display:inline-block;width:100%;color:var(--dark-grey);font-weight:400}.nav-screen-links .nav-link:hover{color:var(--text-color)}.nav-screen-links .nav-link.active{color:var(--theme-color)}#nav-screen{position:fixed;top:var(--navbar-height);right:0;bottom:0;left:0;z-index:150;display:none;overflow-y:auto;padding:0 2rem;background:var(--bg-color);transition:background .5s}@media (max-width: 719px){#nav-screen{display:block}}#nav-screen .container{max-width:320px;margin:0 auto;padding:2rem 0 4rem}#nav-screen.fade-enter-active,#nav-screen.fade-leave-active{transition:opacity .25s}#nav-screen.fade-enter-active .container,#nav-screen.fade-leave-active .container{transition:transform .25s ease}#nav-screen.fade-enter-from,#nav-screen.fade-leave-to{opacity:0}#nav-screen.fade-enter-from .container,#nav-screen.fade-leave-to .container{transform:translateY(-8px)}#nav-screen .outlook-wrapper{display:flex;justify-content:space-around}#nav-screen .icon{-webkit-margin-end:.25em;margin-inline-end:.25em}.navbar .logo{vertical-align:top;height:var(--navbar-line-height);-webkit-margin-end:.8rem;margin-inline-end:.8rem}.navbar .logo.light{display:inline-block}.navbar .logo.dark,html[data-theme=dark] .navbar .logo.light{display:none}html[data-theme=dark] .navbar .logo.dark{display:inline-block}.navbar .site-name{position:relative;color:var(--text-color);font-size:1.25rem}@media (max-width: 719px){.navbar .site-name{overflow:hidden;width:calc(100vw - 9.4rem);text-overflow:ellipsis;white-space:nowrap}}.brand:hover .navbar .site-name{color:var(--theme-color)}.navbar .nav-links{display:flex;align-items:center;font-size:.875rem}.navbar .nav-item{position:relative;margin:0 .25rem;line-height:2rem}.navbar .nav-item:first-child{-webkit-margin-start:0;margin-inline-start:0}.navbar .nav-item:last-child{-webkit-margin-end:0;margin-inline-end:0}.navbar .nav-item>.nav-link{color:var(--dark-grey)}.navbar .nav-item>.nav-link:after{content:" ";position:absolute;right:50%;bottom:0;left:50%;height:2px;border-radius:1px;background:var(--theme-color-light);visibility:hidden;transition:left .2s ease-in-out,right .2s ease-in-out}.navbar .nav-item>.nav-link.active{color:var(--theme-color)}.navbar .nav-item>.nav-link:hover:after,.navbar .nav-item>.nav-link.active:after{right:0;left:0;visibility:visible}.navbar{--navbar-line-height: calc( var(--navbar-height) - var(--navbar-vertical-padding) * 2 );position:fixed;top:0;right:0;left:0;z-index:175;display:flex;align-items:center;justify-content:space-between;box-sizing:border-box;height:var(--navbar-height);padding:var(--navbar-vertical-padding) var(--navbar-horizontal-padding);background:var(--navbar-bg-color);box-shadow:0 2px 8px var(--card-shadow);line-height:var(--navbar-line-height);white-space:nowrap;transition:transform ease-in-out .3s,background var(--color-transition),box-shadow var(--color-transition);-webkit-backdrop-filter:saturate(150%) blur(12px);backdrop-filter:saturate(150%) blur(12px)}@media print{.navbar{display:none}}.hide-navbar .navbar.auto-hide{transform:translateY(-100%)}.navbar .nav-link{padding:0 .25rem;color:var(--dark-grey)}.navbar .nav-link.active{color:var(--theme-color)}.navbar .nav-link .icon{-webkit-margin-end:.25em;margin-inline-end:.25em;font-size:1em}.navbar.hide-icon .nav-links .icon{display:none!important}.navbar-start,.navbar-end,.navbar-center{display:flex;flex:1;align-items:center}.navbar-start>*,.navbar-end>*,.navbar-center>*{position:relative;margin:0 .25rem!important}.navbar-start>*:first-child,.navbar-end>*:first-child,.navbar-center>*:first-child{-webkit-margin-start:0!important;margin-inline-start:0!important}.navbar-start>*:last-child,.navbar-end>*:last-child,.navbar-center>*:last-child{-webkit-margin-end:0!important;margin-inline-end:0!important}.navbar-start{justify-content:start}.navbar-center{justify-content:center}.navbar-end{justify-content:end}.navbar .repo-link{display:inline-block;margin:auto;padding:6px;color:var(--dark-grey);line-height:1}.navbar .repo-link:hover,.navbar .repo-link:active{color:var(--theme-color)}.toggle-navbar-button{border-width:0;background:transparent;cursor:pointer;position:relative;display:none;align-items:center;justify-content:center;padding:6px}@media screen and (max-width: 719px){.toggle-navbar-button{display:flex}}.toggle-navbar-button .button-container{position:relative;overflow:hidden;width:16px;height:14px}.toggle-navbar-button .button-top,.toggle-navbar-button .button-middle,.toggle-navbar-button .button-bottom{position:absolute;width:16px;height:2px;background:var(--dark-grey);transition:top .25s,background .5s,transform .25s}.toggle-navbar-button .button-top{top:0;left:0;transform:translate(0)}.toggle-navbar-button .button-middle{top:6px;left:0;transform:translate(8px)}.toggle-navbar-button .button-bottom{top:12px;left:0;transform:translate(4px)}.toggle-navbar-button:hover .button-top{top:0;left:0;transform:translate(4px)}.toggle-navbar-button:hover .button-middle{top:6;left:0;transform:translate(0)}.toggle-navbar-button:hover .button-bottom{top:12px;left:0;transform:translate(8px)}.toggle-navbar-button.is-active .button-top{top:6px;transform:translate(0) rotate(225deg)}.toggle-navbar-button.is-active .button-middle{top:6px;transform:translate(16px)}.toggle-navbar-button.is-active .button-bottom{top:6px;transform:translate(0) rotate(135deg)}.toggle-navbar-button.is-active:hover .button-top,.toggle-navbar-button.is-active:hover .button-middle,.toggle-navbar-button.is-active:hover .button-bottom{background:var(--theme-color);transition:top .25s,background .25s,transform .25s}.toggle-sidebar-button{border-width:0;background:transparent;cursor:pointer;display:none;vertical-align:middle;box-sizing:content-box;width:1rem;height:1rem;padding:.5rem;font:unset;transition:transform .2s ease-in-out}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block;-webkit-padding-end:var(--navbar-mobile-horizontal-padding);padding-inline-end:var(--navbar-mobile-horizontal-padding)}}.toggle-sidebar-button:before,.toggle-sidebar-button:after,.toggle-sidebar-button .icon{display:block;width:100%;height:2px;border-radius:.05em;background:var(--dark-grey);transition:transform .2s ease-in-out}.toggle-sidebar-button:before{content:" ";margin-top:.125em}.sidebar-open .toggle-sidebar-button:before{transform:translateY(.34rem) rotate(135deg)}.toggle-sidebar-button:after{content:" ";margin-bottom:.125em}.sidebar-open .toggle-sidebar-button:after{transform:translateY(-.34rem) rotate(-135deg)}.toggle-sidebar-button .icon{margin:.2em 0}.sidebar-open .toggle-sidebar-button .icon{transform:scale(0)}.appearance-title{display:block;margin:0;padding:0 .25rem;color:var(--light-grey);font-weight:600;font-size:.75rem;line-height:2;transition:color var(--color-transition)}#appearance-switch{border-width:0;background:transparent;vertical-align:middle;padding:6px;color:var(--dark-grey);cursor:pointer;transition:color var(--color-transition)}#appearance-switch:hover{color:var(--theme-color)}#appearance-switch .icon{width:1.25rem;height:1.25rem}.outlook-button{border-width:0;background:transparent;cursor:pointer;position:relative;padding:.375rem;color:var(--dark-grey)}.outlook-button .icon{vertical-align:middle;width:1.25rem;height:1.25rem}.outlook-dropdown{position:absolute;top:100%;right:0;overflow-y:auto;box-sizing:border-box;min-width:100px;margin:0;padding:.5rem .75rem;border:1px solid var(--grey14);border-radius:.25rem;background:var(--bg-color);box-shadow:2px 2px 10px var(--card-shadow);text-align:start;white-space:nowrap;opacity:0;visibility:hidden;transition:all .18s ease-out;transform:scale(.8)}html[dir=rtl] .outlook-dropdown{right:unset;left:0}.outlook-dropdown>*:not(:last-child){padding-bottom:.5rem;border-bottom:1px solid var(--grey14)}.outlook-button:hover .outlook-dropdown,.outlook-button.open .outlook-dropdown{z-index:2;opacity:1;visibility:visible;transform:scale(1)}.theme-color-title{display:block;margin:0;padding:0 .25rem;color:var(--light-grey);font-weight:600;font-size:.75rem;line-height:2;transition:color var(--color-transition)}#theme-color-picker{display:flex;margin:0;padding:0;list-style-type:none;font-size:14px}#theme-color-picker li span{display:inline-block;vertical-align:middle;width:15px;height:15px;margin:0 2px;border-radius:2px}#theme-color-picker li span.theme-color,#theme-color-picker li span.theme-color html[data-theme=dark]{background:#3eaf7c}@media print{.full-screen-wrapper{display:none}}.full-screen-title{display:block;margin:0;padding:0 .25rem;color:var(--light-grey);font-weight:600;font-size:.75rem;line-height:2;transition:color var(--color-transition)}.full-screen,.cancel-full-screen{border-width:0;background:transparent;vertical-align:middle;padding:.375rem;color:var(--dark-grey);cursor:pointer}.full-screen:hover,.cancel-full-screen:hover{color:var(--theme-color)}.full-screen .icon,.cancel-full-screen .icon{width:1.25rem;height:1.25rem}.enter-fullscreen-icon:hover,.cancel-fullscreen-icon{color:var(--theme-color)}.cancel-fullscreen-icon:hover{color:var(--dark-grey)}.sidebar-heading{display:flex;align-items:center;overflow:hidden;box-sizing:border-box;width:calc(100% - 1rem);margin:0;margin-inline:.5rem;padding:.25rem .5rem;border-width:0;border-radius:.375rem;background:transparent;color:var(--text-color);font-size:1.1em;line-height:1.5;-webkit-user-select:none;-moz-user-select:none;user-select:none;transition:color .15s ease;transform:rotate(0)}.sidebar-heading.open{color:inherit}.sidebar-heading.clickable:hover{background:var(--bg-color-secondary)}.sidebar-heading.clickable.exact{border-inline-start-color:var(--theme-color);color:var(--theme-color)}.sidebar-heading.clickable.exact a{color:inherit}.sidebar-heading .icon{-webkit-margin-end:.25em;margin-inline-end:.25em}.sidebar-heading .title{flex:1}.sidebar-heading .arrow{display:inline-block;vertical-align:middle;width:1em;height:1em;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E");line-height:normal;transition:all .3s;font-size:1.5em}html[data-theme=dark] .sidebar-heading .arrow{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.5)' d='M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z'/%3E%3C/svg%3E")}.sidebar-heading .arrow.down{transform:rotate(180deg)}html[dir=rtl] .sidebar-heading .arrow.down{transform:rotate(-180deg)}.sidebar-heading .arrow.end{transform:rotate(90deg)}html[dir=rtl] .sidebar-heading .arrow.end,.sidebar-heading .arrow.start{transform:rotate(-90deg)}html[dir=rtl] .sidebar-heading .arrow.start{transform:rotate(90deg)}button.sidebar-heading{outline:none;font-weight:inherit;font-family:inherit;line-height:inherit;text-align:start;cursor:pointer}.sidebar-link{display:inline-block;box-sizing:border-box;width:calc(100% - 1rem);margin-inline:.5rem;padding:.25rem .5rem;border-radius:.375rem;color:var(--text-color);font-weight:400;font-size:1em;line-height:1.5}.sidebar-link .icon{-webkit-margin-end:.25em;margin-inline-end:.25em}.sidebar-link:hover{background:var(--bg-color-secondary)}.sidebar-link.active{background:var(--theme-color-mask);color:var(--theme-color);font-weight:500}.sidebar-link.active .icon{color:var(--theme-color)}.sidebar-sub-headers .sidebar-link{padding-top:.25rem;padding-bottom:.25rem;-webkit-border-start:none;border-inline-start:none}.sidebar-sub-headers .sidebar-link.active{background:transparent;font-weight:500}.sidebar-group .sidebar-group{-webkit-padding-start:.75rem;padding-inline-start:.75rem}.sidebar-group .sidebar-group .sidebar-heading{font-size:1em}.sidebar-group:not(.collapsible) .sidebar-heading:not(.clickable){color:inherit;cursor:auto}.sidebar-group .sidebar-link{-webkit-padding-start:1.25rem;padding-inline-start:1.25rem}.sidebar-links,.sidebar-links ul{margin:0;padding:0}.sidebar-links ul.sidebar-sub-headers{-webkit-padding-start:.75rem;padding-inline-start:.75rem;font-size:.95em}@media (min-width: 1440px){.has-toc .sidebar-links ul.sidebar-sub-headers{display:none}}.sidebar-links li{list-style-type:none}.sidebar>.sidebar-links{padding:1.5rem 0}@media (max-width: 719px){.sidebar>.sidebar-links{padding:1rem 0}}.sidebar>.sidebar-links>li>.sidebar-link{font-size:1.1em}.sidebar>.sidebar-links>li:not(:first-child){margin-top:.5rem}.sidebar{position:fixed;top:0;bottom:0;left:0;z-index:1;overflow-y:auto;width:var(--sidebar-width);margin:0;-webkit-padding-start:calc(var(--sidebar-space) - var(--sidebar-width));padding-inline-start:calc(var(--sidebar-space) - var(--sidebar-width));background:var(--sidebar-bg-color);box-shadow:2px 0 8px var(--card-shadow);font-size:.94rem;transition:background var(--color-transition),box-shadow var(--color-transition),padding var(--transform-transition),transform var(--transform-transition);-webkit-backdrop-filter:saturate(150%) blur(12px);backdrop-filter:saturate(150%) blur(12px);scrollbar-color:var(--theme-color) var(--border-color);scrollbar-width:thin}@media (max-width: 959px){.sidebar{font-size:.86em}}@media (max-width: 719px){.sidebar{z-index:125;box-shadow:none;transform:translate(-100%)}html[dir=rtl] .sidebar{transform:translate(100%)}}@media (min-width: 1440px){.sidebar{padding-bottom:3rem;box-shadow:none;font-size:1rem}}@media print{.sidebar{display:none}}html[dir=rtl] .sidebar{right:0;left:unset}.sidebar a{display:inline-block;color:var(--text-color);font-weight:400}.sidebar .icon{-webkit-margin-end:.25em;margin-inline-end:.25em}.sidebar.hide-icon .icon{display:none!important}.sidebar .blogger-info.mobile{display:none}@media (max-width: 719px){.sidebar .blogger-info.mobile{display:block}}.sidebar .blogger-info.mobile+hr{display:none}@media (max-width: 719px){.sidebar .blogger-info.mobile+hr{display:block;margin-top:1rem}}.sidebar-mask{position:fixed;top:0;left:0;z-index:9;width:100vw;height:100vh;background:rgba(0,0,0,.15)}.sidebar-mask.fade-enter-active,.sidebar-mask.fade-leave-active{transition:opacity .25s}.sidebar-mask.fade-enter-from,.sidebar-mask.fade-leave-to{opacity:0}.search-pro-button{border-width:0;background:transparent;display:inline-flex;align-items:center;box-sizing:content-box;height:1.25rem;margin-inline:1rem 0;margin-top:0;margin-bottom:0;padding:.5rem;border:0;border:1px solid var(--vp-bgl);border-radius:1rem;background:var(--vp-bgl);color:var(--vp-c);font-weight:500;cursor:pointer;transition:background var(--vp-ct),color var(--vp-ct)}@media print{.search-pro-button{display:none}}@media (max-width: 959px){.search-pro-button{border-radius:50%}}.search-pro-button:hover{border:1px solid var(--vp-tc);background-color:var(--vp-bglt);color:var(--vp-clt)}.search-pro-button .search-icon{width:1.25rem;height:1.25rem}.search-pro-button .placeholder{margin-inline:.25rem;font-size:1rem}@media (max-width: 959px){.search-pro-button .placeholder{display:none}}.search-pro-button .key-hints{font-size:.75rem}@media (max-width: 959px){.search-pro-button .key-hints{display:none}}.search-pro-button .key-hints .key{display:inline-block;min-width:1em;margin-inline:.125rem;padding:.25rem;border:1px solid var(--vp-brc);border-radius:4px;box-shadow:1px 1px 4px 0 var(--card-shadow);line-height:1;letter-spacing:-.1em;transition:background var(--vp-ct),color var(--vp-ct),border var(--vp-ct) box-shadow var(--vp-ct)}@keyframes search-pro-fade-in{0%{opacity:.2}to{opacity:1}}.search-pro-modal-wrapper{position:fixed;top:0;left:0;z-index:997;display:flex;align-items:center;justify-content:center;overflow:auto;width:100vw;height:100vh;cursor:default}.search-pro-modal-wrapper button{border-width:0;background:transparent;cursor:pointer}.search-pro-modal-wrapper .background{position:fixed;top:0;left:0;z-index:998;width:100vw;height:100vh;animation:.25s search-pro-fade-in;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.search-pro-modal{position:absolute;z-index:999;display:flex;flex-direction:column;width:calc(100% - 6rem);max-width:50em;border-radius:10px;background:var(--vp-bg);box-shadow:2px 2px 10px 0 var(--card-shadow);transition:background var(--vp-ct);animation:.15s pwa-opened}@media (max-width: 1280px){.search-pro-modal{animation:.25s pwa-mobile}}@media (max-width: 719px){.search-pro-modal{width:100vw;max-width:unset;height:100vh}}.search-pro-box{display:flex;margin:1rem}.search-pro-box form{position:relative;display:flex;flex:1}.search-pro-box label{position:absolute;top:calc(50% - .75rem);left:.5rem;color:var(--vp-tc)}html[dir=rtl] .search-pro-box label{right:.5rem;left:unset}.search-pro-box label .search-icon{width:1.5rem;height:1.5rem}.search-pro-box input{flex:1;min-width:0;margin:0;padding-top:.25rem;padding-bottom:.25rem;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem;-webkit-padding-end:1rem;padding-inline-end:1rem;border:0;border:2px solid var(--vp-tc);border-radius:8px;background:var(--vp-bg);color:var(--vp-c);outline:none;font-size:1.25rem;line-height:2.5;-webkit-appearance:none;-moz-appearance:none;appearance:none}.search-pro-box .close-button{border-width:0;background:transparent;cursor:pointer;display:none;-webkit-margin-start:.5rem;margin-inline-start:.5rem;-webkit-margin-end:-.5rem;margin-inline-end:-.5rem;padding:.5rem;color:var(--grey3)}@media (max-width: 719px){.search-pro-box .close-button{display:block}}.search-pro-result{flex-grow:1;overflow-y:auto;min-height:40vh;max-height:calc(80vh - 10rem);padding:0 1rem}@media (max-width: 719px){.search-pro-result{min-height:unset;max-height:unset}}.search-pro-result.loading,.search-pro-result.empty{display:flex;align-items:center;justify-content:center;padding:1.5rem;font-weight:600;font-size:22px;text-align:center}.search-pro-hints{margin-top:1rem;padding:.75rem .5rem;box-shadow:0 -1px 4px 0 var(--card-shadow);line-height:1}.search-pro-hint{display:inline-flex;align-items:center;margin:0 .5rem}.search-pro-hint kbd{margin:0 .5rem;padding:2px;border:1px solid var(--vp-brc);border-radius:4px;box-shadow:1px 1px 4px 0 var(--card-shadow)}.search-pro-hint kbd+kbd{-webkit-margin-start:-.25rem;margin-inline-start:-.25rem}.search-pro-hint svg{display:block;width:15px;height:15px}.search-pro-result{scrollbar-color:var(--vp-tc) var(--vp-brc);scrollbar-width:thin}@media (max-width: 419px){.search-pro-result{font-size:13px}}.search-pro-result::-webkit-scrollbar{width:6px;height:6px}.search-pro-result::-webkit-scrollbar-track-piece{border-radius:6px;background:rgba(0,0,0,.1)}.search-pro-result::-webkit-scrollbar-thumb{border-radius:6px;background:var(--vp-tc)}.search-pro-result::-webkit-scrollbar-thumb:active{background:var(--vp-tcl)}.search-pro-result-list{margin:0;padding:0}.search-pro-result-list-item{display:block;list-style:none}.search-pro-result-title{position:sticky;top:-2px;z-index:10;margin:-4px;margin-bottom:.25rem;padding:4px;background:var(--vp-bg);color:var(--vp-tc);font-weight:600;font-size:.85em;line-height:2rem;text-indent:.5em}.search-pro-result-item.active .search-pro-result-title{color:var(--vp-tc)}.search-pro-result-type{display:block;width:1rem;height:1rem;-webkit-margin-start:-.5rem;margin-inline-start:-.5rem;padding:.5rem;color:var(--vp-tc)}.search-pro-close-icon{box-sizing:content-box;height:1.5rem;padding:0;border-radius:50%;color:var(--vp-tc)}.search-pro-close-icon svg{width:1.5rem;height:1.5rem}.search-pro-close-icon:hover{background:rgba(128,128,128,.3)}.search-pro-result-content{display:flex;flex-grow:1;flex-direction:column;align-items:stretch;justify-content:center;line-height:1.5}.search-pro-result-content .content-header{margin-bottom:.25rem;border-bottom:1px solid var(--vp-brcd);font-size:.9em}.search-pro-result-item{display:flex;align-items:center;margin:.5rem 0;padding:.5rem .75rem;border-radius:.25rem;background:var(--vp-bgl);color:inherit;box-shadow:0 1px 3px 0 var(--card-shadow);font-weight:400;white-space:pre-wrap;word-wrap:break-word}.search-pro-result-item strong{color:var(--vp-tc)}.search-pro-result-item:hover,.search-pro-result-item.active{background-color:var(--vp-tcl);color:var(--white);cursor:pointer}.search-pro-result-item:hover .search-pro-result-type,.search-pro-result-item:hover .search-pro-close-icon,.search-pro-result-item:hover strong,.search-pro-result-item.active .search-pro-result-type,.search-pro-result-item.active .search-pro-close-icon,.search-pro-result-item.active strong{color:var(--white)} diff --git a/assets/testdata.html-32bf797b.js b/assets/testdata.html-32bf797b.js new file mode 100644 index 00000000..857b4f65 --- /dev/null +++ b/assets/testdata.html-32bf797b.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-51b3f914","path":"/docs/user/testdata.html","title":"测试数据格式","lang":"en-US","frontmatter":{"description":"自动模式 您可以直接选择文件(支持多选)上传或将文件拖拽至相应位置上传。 若上传文件为 zip 格式,将会自动进行解压操作。 对于一般的题目,您只需提供 .in 和 .out/.ans 文件,以下是一个例子。 请务必确保文件名中含有数字。形如 sample.in 的文件是不会被自动识别的。 测试数据将被自动识别,并使用 1S 256MB 的限制。 使用...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/docs/user/testdata.html"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"测试数据格式"}],["meta",{"property":"og:description","content":"自动模式 您可以直接选择文件(支持多选)上传或将文件拖拽至相应位置上传。 若上传文件为 zip 格式,将会自动进行解压操作。 对于一般的题目,您只需提供 .in 和 .out/.ans 文件,以下是一个例子。 请务必确保文件名中含有数字。形如 sample.in 的文件是不会被自动识别的。 测试数据将被自动识别,并使用 1S 256MB 的限制。 使用..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-05-12T03:18:30.000Z"}],["meta",{"property":"article:modified_time","content":"2023-05-12T03:18:30.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"测试数据格式\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-05-12T03:18:30.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"自动模式","slug":"自动模式","link":"#自动模式","children":[]},{"level":2,"title":"使用配置文件","slug":"使用配置文件","link":"#使用配置文件","children":[]}],"git":{"createdTime":1631785934000,"updatedTime":1683861510000,"contributors":[{"name":"Macesuted","email":"macesuted@qq.com","commits":2},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":2},{"name":"undefined","email":"i@undefined.moe","commits":2},{"name":"laomai","email":"lmxin@tom.com","commits":1}]},"readingTime":{"minutes":3.3,"words":989},"filePathRelative":"docs/user/testdata.md","localizedDate":"September 16, 2021","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/testdata.html-79bb90f0.js b/assets/testdata.html-79bb90f0.js new file mode 100644 index 00000000..83830b5f --- /dev/null +++ b/assets/testdata.html-79bb90f0.js @@ -0,0 +1,104 @@ +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as e,c as o,a as n,b as s,d as c,e as i}from"./app-0efcd172.js";const r={},t=i(`Tips
您可以直接选择文件(支持多选)上传或将文件拖拽至相应位置上传。
若上传文件为 zip 格式,将会自动进行解压操作。
对于一般的题目,您只需提供 .in
和 .out/.ans
文件,以下是一个例子。
请务必确保文件名中含有数字。形如 sample.in
的文件是不会被自动识别的。
喵? tree
+.
+├── a1.in
+├── a1.out
+├── a2.in
+├── a2.out
+├── a3.in
+└── a3.out
+
测试数据将被自动识别,并使用 1S 256MB 的限制。
Tips
推荐您通过 评测设置 在线编辑题目配置,可以拥有更好的编辑体验。
上传 config.yaml
文件即可,文件格式如下(下方所有样例均为可选项,若无说明则预填写的内容即为默认值):
# 题目类型,可以为 default(比对输出,可以含spj), objective(客观题), interactive(交互题)
+type: default
+
+# 全局时空限制(此处的限制优先级低于测试点的限制)
+time: 1s
+memory: 128m
+
+# 输入输出文件名(例:使用 foo.in 和 foo.out),若使用标准 IO 删除此配置项即可
+filename: foo
+
+# 此部分设置当题目类型为 default 时生效
+# 比较器类型,支持的值有 default(直接比对,忽略行末空格和文件末换行), ccr, cena, hustoj, lemon, qduoj, syzoj, testlib(比较常用)
+checker_type: default
+# 比较器文件(当比较器类型不为 default 时填写)
+# 文件路径(位于压缩包中的路径)
+# 将通过扩展名识别语言,与编译命令处一致。在默认配置下,C++ 扩展名应为 .cc 而非 .cpp
+checker: chk.cc
+
+# 此部分设置当题目类型为interactive时生效
+# 交互器路径(位于压缩包中的路径)
+interactor: interactor.cc
+
+# Extra files 额外文件
+# These files will be copied to the working directory 这些文件将被复制到工作目录。
+# 提示:您无需手动上传 testlib.h。
+user_extra_files:
+ - extra_input.txt
+judge_extra_files:
+ - extra_file.txt
+
+# Test Cases 测试数据列表
+# If neither CASES or SUBTASKS are set(or config.yaml doesn't exist), judge will try to locate them automaticly.
+# 如果 CASES 和 SUBTASKS 都没有设置或 config.yaml 不存在, 系统会自动尝试识别数据点。
+# We support these names for auto mode: 自动识别支持以下命名方式:
+# 1. [name(optional)][number].(in/out/ans) RegExp: /^([a-zA-Z]*)([0-9]+).in$/
+# examples:
+# - c1.in / c1.out
+# - 1.in / 1.out
+# - c1.in / c1.ans
+# 2. input[number].txt / output[number].txt RegExp: /^(input)([0-9]+).txt$/
+# - example: input1.txt / input2.txt
+#
+# The CASES option has higher priority than the SUBTASKS option!
+# 在有 CASES 设置项时,不会读取 SUBTASKS 设置项!
+#
+# The CASES option has been deprecated in the new version, please use the more personalized SUBTASKS!
+# CASES 已于新版本中被废弃,请使用个性化程度更高的SUBTASKS!
+# score: 50 # 单个测试点分数
+# time: 1s # 时间限制
+# memory: 256m # 内存限制
+# cases:
+# - input: abc.in
+# output: def.out
+# - input: ghi.in
+# output: jkl.out
+# 或使用Subtask项:
+subtasks:
+ - score: 30
+ type: min # 可选 min/max/sum,分别表示取所有测试点最小值、所有测试点最大值、所有测试点之和
+ time: 1s
+ memory: 64m
+ cases:
+ - time: 0.5s
+ memory: 32m # 可对单个测试点单独设置时间限制和内存限制
+ input: a.in
+ output: a.out
+ - input: b.in
+ output: b.out
+ - score: 70
+ time: 0.5s
+ memory: 32m
+ if: [0] # 可选,传入数组,表示仅在subtask0通过时此subtask才计分
+ cases:
+ - input: c.in
+ output: c.out
+ - input: d.in
+ output: d.out
+
+# 提交语言限制
+# 列举出所有本题允许使用的语言对应的代码(需要和评测机 lang.yaml 内的语言代码相同)
+# 使用语言ID而非名称!对于有子类的选项,请详细至子分类!
+langs:
+ - c
+ - cc
+ - cc.cc11o2
+ - pas
+
+# 时间内存倍率
+# 对某语言设置时间或内存倍率(需要和评测机 lang.yaml 内的语言代码相同)
+# 部分语言默认已存在倍率,请前往控制面板中查看!
+# 使用语言ID而非名称!对于有子类的选项,请详细至子分类!
+time_limit_rate:
+ py.py3: 2
+memory_limit_rate:
+ java: 1.5
+
前置条件:NodeJS>=18
此教程将以编写剪贴板插件为例进行说明。
使用 hydrooj addon create
快速在 /root/addon
下初始化一个插件或是在一个空文件夹中运行 yarn init
并按照提示填写相关信息。
# 使用 yarn init 的样例
+/workspace/hydro-plugin $ yarn init
+yarn init v1.22.4
+question name (hydro-plugin): @hydrooj/pastebin
+question version (1.0.0): 0.0.1
+question description: HydroOJ的剪贴板组件
+question entry point (index.js): index.ts
+question repository url: https://github.com/hydro-dev/pastebin.git
+question author: undefined <i@undefined.moe>
+question license (MIT): MIT
+question private:
+success Saved package.json
+
分析:剪贴板组件需要以下功能:
在路由中定义所有的函数应均为异步函数,支持的函数有:prepare, get, post, post[Operation], cleanup
具体流程如下:
先执行 prepare(args) (如果存在)
+args 为传入的参数集合(包括 QueryString, Body, Path)中的全部参数,
+再执行 prepare(args) (如果存在)
+检查请求类型:
+
+为 GET ?
+ -> 执行 get(args)
+为 POST ?
+ -> 执行 post(args)
+ -> 含有 operation 字段?
+ -> 执行 post[Operation]
+
+执行 cleanup()
+
如果在 this.response.template 指定模板则渲染,否则直接返回 this.response.body 中的内容。
如 <input type="hidden" name="operation" value="confirm_delete">
对应 postConfirmDelete
函数。
应当提供 apply
函数,并与定义的 Handler 一同挂载到 global.Hydro.handler[模块名]
位置。 apply
函数将在初始化阶段被调用。
// @noErrors
+// @module: esnext
+// @filename: index.ts
+import { definePlugin, Handler, Types, param, db, PRIV, validator, NotFoundError, PermissionError } from 'hydrooj';
+
+const coll = db.collection('paste');
+
+interface Paste {
+ _id: string;
+ owner: number;
+ content: string;
+ isPrivate: boolean;
+}
+
+declare module 'hydrooj' {
+ interface Model {
+ pastebin: typeof pastebinModel;
+ }
+ interface Collections {
+ paste: Paste; // 声明数据表类型
+ }
+}
+
+async function add(userId: number, content: string, isPrivate: boolean): Promise<string> {
+ const pasteId = String.random(16); // Hydro 提供了此方法,创建一个长度为16的随机字符串
+ // 使用 mongodb 为数据库驱动,相关操作参照其文档
+ const result = await coll.insertOne({
+ _id: pasteId,
+ owner: userId,
+ content,
+ isPrivate,
+ });
+ return result.insertedId; // 返回插入的文档ID
+}
+
+async function get(pasteId: string): Promise<Paste> {
+ return await coll.findOne({ _id: pasteId });
+}
+
+// 暴露这些接口,使得 cli 也能够正常调用这些函数;
+const pastebinModel = { add, get };
+global.Hydro.model.pastebin = pastebinModel;
+
+// 创建新路由
+class PasteCreateHandler extends Handler {
+ // Get请求时触发该函数
+ async get() {
+ // 检查用户是否登录,此处为多余(因为底部注册路由时已声明所需权限)
+ // 此方法适用于权限的动态检查
+ // this.checkPriv(PRIV.PRIV_USER_PROFILE);
+ this.response.template = 'paste_create.html'; // 返回此页面
+ }
+
+ // 使用 isContent 检查输入
+ @param('content', Types.String, isContent)
+ @param('private', Types.Boolean)
+ // 从用户提交的表单中取出content和private字段
+ // domainId 为固定传入参数
+ async post(domainId: string, content: string, isPrivate = false) {
+ // 在HTML表单提交的多选框中,选中值为 'on',未选中则为空,需要进行转换
+ const pasteid = await pastebin.add(this.user._id, content, !!isPrivate);
+ // 将用户重定向到创建完成的url
+ this.response.redirect = this.url('paste_show', { id: pasteid });
+ // 相应的,提供了 this.back() 方法用于将用户重定向至前一个地址(通常用于 Ajax 或是部分更新操作)
+ }
+}
+
+class PasteShowHandler extends Handler {
+ @param('id', Types.String)
+ async get(domainId: string, id: string) {
+ const doc = await pastebin.get(id);
+ if (!doc) throw new NotFoundError(id);
+ if (doc.isPrivate && this.user._id !== doc.owner) {
+ throw new PermissionError();
+ }
+ this.response.body = { doc };
+ this.response.template = 'paste_show.html';
+ }
+
+ @param('id', Types.String)
+ async postDelete(domainId: string, id: string) {
+ // 当提交表单并存在 operation 值为 delete 时执行。
+ // 本例中未实现删除功能,仅作为说明。
+ }
+}
+
+// 定义为一个插件
+export default definePlugin({
+ apply(ctx) {
+ // 注册一个名为 paste_create 的路由,匹配 '/paste/create',
+ // 使用PasteCreateHandler处理,访问改路由需要PRIV.PRIV_USER_PROFILE权限
+ // 提示:路由匹配基于 path-to-regexp
+ ctx.Route('paste_create', '/paste/create', PasteCreateHandler, PRIV.PRIV_USER_PROFILE);
+ ctx.Route('paste_show', '/paste/show/:id', PasteShowHandler);
+ }
+});
+
模板采用 nunjucks 语法。放置于 templates/
文件夹下。
会在请求结束时根据 response.template
的值选择模板,并使用 response.body
的值进行渲染,存入 response.body
中。
若 response.template
为空或 request.headers['accept'] == 'application/json'
,则跳过渲染步骤。
用于提供多国翻译。格式与 Hydro 的 locale 文件夹格式相同。
`,20),e=[o];function c(r,E){return n(),a("div",null,e)}const y=s(p,[["render",c],["__file","typescript.html.vue"]]);export{y as default}; diff --git a/assets/typescript.html-9acb6c36.js b/assets/typescript.html-9acb6c36.js new file mode 100644 index 00000000..1edbe7a6 --- /dev/null +++ b/assets/typescript.html-9acb6c36.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5ddc85e1","path":"/dev/typescript.html","title":"使用 TypeScript 编写插件","lang":"en-US","frontmatter":{"description":"前置条件:NodeJS>=18 此教程将以编写剪贴板插件为例进行说明。 Step1 初始化项目 使用 hydrooj addon create 快速在 /root/addon 下初始化一个插件或是在一个空文件夹中运行 yarn init 并按照提示填写相关信息。 Step2 准备编写组件 分析:剪贴板组件需要以下功能: 与数据库交互来存储/检索相应文档...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/dev/typescript.html"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"使用 TypeScript 编写插件"}],["meta",{"property":"og:description","content":"前置条件:NodeJS>=18 此教程将以编写剪贴板插件为例进行说明。 Step1 初始化项目 使用 hydrooj addon create 快速在 /root/addon 下初始化一个插件或是在一个空文件夹中运行 yarn init 并按照提示填写相关信息。 Step2 准备编写组件 分析:剪贴板组件需要以下功能: 与数据库交互来存储/检索相应文档..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-02-25T05:29:05.000Z"}],["meta",{"property":"article:modified_time","content":"2023-02-25T05:29:05.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 TypeScript 编写插件\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-02-25T05:29:05.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"Step1 初始化项目","slug":"step1-初始化项目","link":"#step1-初始化项目","children":[]},{"level":2,"title":"Step2 准备编写组件","slug":"step2-准备编写组件","link":"#step2-准备编写组件","children":[]},{"level":2,"title":"Step4 template","slug":"step4-template","link":"#step4-template","children":[]},{"level":2,"title":"Step5 locale","slug":"step5-locale","link":"#step5-locale","children":[]}],"git":{"createdTime":1612251809000,"updatedTime":1677302945000,"contributors":[{"name":"undefined","email":"i@undefined.moe","commits":4},{"name":"Macesuted Kysic","email":"macesuted@foxmail.com","commits":1},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":1},{"name":"无限UCW","email":"45730483+wuxianucw@users.noreply.github.com","commits":1}]},"readingTime":{"minutes":3.46,"words":1039},"filePathRelative":"dev/typescript.md","localizedDate":"February 2, 2021","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/vjudge.html-b94954ed.js b/assets/vjudge.html-b94954ed.js new file mode 100644 index 00000000..167efafd --- /dev/null +++ b/assets/vjudge.html-b94954ed.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-3096c190","path":"/plugins/vjudge.html","title":"Vjudge","lang":"en-US","frontmatter":{"description":"此文档已过时,仅作留存使用,请前往 FAQS 查看使用指南。 Codeforces 由于 vjudge 更新了反爬虫机制,Codeforces RemoteJudge 需要一些特殊手段才能正常工作。 详情请 阅读源码 (https://github.com/hydro-dev/Hydro/blob/master/packages/vjudge/src/...","head":[["meta",{"property":"og:url","content":"https://hydro.js.org/plugins/vjudge.html"}],["meta",{"property":"og:site_name","content":"Hydro"}],["meta",{"property":"og:title","content":"Vjudge"}],["meta",{"property":"og:description","content":"此文档已过时,仅作留存使用,请前往 FAQS 查看使用指南。 Codeforces 由于 vjudge 更新了反爬虫机制,Codeforces RemoteJudge 需要一些特殊手段才能正常工作。 详情请 阅读源码 (https://github.com/hydro-dev/Hydro/blob/master/packages/vjudge/src/..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2023-05-12T03:18:30.000Z"}],["meta",{"property":"article:modified_time","content":"2023-05-12T03:18:30.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Vjudge\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-05-12T03:18:30.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"Codeforces","slug":"codeforces","link":"#codeforces","children":[]}],"git":{"createdTime":1624333296000,"updatedTime":1683861510000,"contributors":[{"name":"undefined","email":"i@undefined.moe","commits":3},{"name":"panda","email":"panda_dtdyy@outlook.com","commits":2},{"name":"Macesuted","email":"macesuted@qq.com","commits":1}]},"readingTime":{"minutes":1.67,"words":501},"filePathRelative":"plugins/vjudge.md","localizedDate":"June 22, 2021","autoDesc":true,"excerpt":""}');export{e as data}; diff --git a/assets/vjudge.html-d5665d8b.js b/assets/vjudge.html-d5665d8b.js new file mode 100644 index 00000000..74886ea8 --- /dev/null +++ b/assets/vjudge.html-d5665d8b.js @@ -0,0 +1,136 @@ +import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as e,c,a as n,b as s,d as r,e as a}from"./app-0efcd172.js";const F={},E=a('Note
此文档已过时,仅作留存使用,请前往 FAQS 查看使用指南。
安装插件后创建名为 codeforces 的域,进入数据库 db.domain.updateOne({_id:'codeforces'},{$set:{mount:'codeforces'}});
在 codeforces 的域设置中,将 allowedLangs
如下配置(在新版即在允许提交的语言中选中所有 codeforces
开头的语言):
codeforces,codeforces.43,codeforces.52,codeforces.50,codeforces.54,codeforces.59,codeforces.61,codeforces.65,codeforces.9,codeforces.28,codeforces.32,codeforces.12,codeforces.60,codeforces.36,codeforces.48,codeforces.19,codeforces.3,codeforces.4,codeforces.51,codeforces.13,codeforces.6,codeforces.7,codeforces.31,codeforces.40,codeforces.41,codeforces.67,codeforces.49,codeforces.20,codeforces.34,codeforces.55
+
在 vjudge 表中插入如下条目:
{type:'codeforces', handle:'<codeforces login handle>', password:'<codeforces login password>'}
+
将如下配置添加至 langs 设置末尾:
codeforces:
+ execute: none
+ display: Codeforces
+ domain:
+ - codeforces # Allow domain 'codeforces' to use these languages
+codeforces.43:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU GCC C11 5.1.0
+ comment: //
+codeforces.52:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: Clang++17 Diagnostics
+ comment: //
+codeforces.50:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU G++14 6.4.0
+ comment: //
+codeforces.54:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU G++17 7.3.0
+ comment: //
+codeforces.59:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: Microsoft Visual C++ 2017
+ comment: //
+codeforces.61:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU G++17 9.2.0 (64 bit, msys 2)
+ comment: //
+codeforces.65:
+ highlight: cpp astyle-cs
+ monaco: csharp
+ display: C# 8, .NET Core 3.1
+ comment: //
+codeforces.9:
+ highlight: cpp astyle-cs
+ monaco: csharp
+ display: C# Mono 6.8
+ comment: //
+codeforces.28:
+ highlight: d
+ monaco: plain
+ display: D DMD32 v2.091.0
+ comment: //
+codeforces.32:
+ highlight: go
+ display: Go 1.15.6
+ comment: //
+codeforces.12:
+ highlight: haskell
+ display: Haskell GHC 8.10.1
+ comment: --
+codeforces.60:
+ highlight: java astyle-java
+ monaco: java
+ display: Java 11.0.6
+ comment: //
+codeforces.36:
+ highlight: java astyle-java
+ monaco: java
+ display: Java 1.8.0_241
+ comment: //
+codeforces.48:
+ highlight: kotlin
+ display: Kotlin 1.4.0
+ comment: //
+codeforces.19:
+ highlight: ocaml
+ monaco: plain
+ display: OCaml 4.02.1
+ comment: ['(*','*)']
+codeforces.3:
+ highlight: pascal
+ display: Delphi 7
+ comment: //
+codeforces.4:
+ highlight: pascal
+ display: Free Pascal 3.0.2
+ comment: //
+codeforces.51:
+ highlight: pascal
+ display: PascalABC.NET 3.4.2
+ comment: //
+codeforces.13:
+ highlight: perl
+ display: Perl 5.20.1
+ comment: '#'
+codeforces.6:
+ highlight: php
+ display: PHP 7.2.13
+ comment: //
+codeforces.7:
+ highlight: python
+ display: Python 2.7.18
+ comment: '#'
+codeforces.31:
+ highlight: python
+ display: Python 3.9.1
+ comment: '#'
+codeforces.40:
+ highlight: python
+ display: PyPy 2.7 (7.3.0)
+ comment: '#'
+codeforces.41:
+ highlight: python
+ display: PyPy 3.7 (7.3.0)
+ comment: '#'
+codeforces.67:
+ highlight: ruby
+ display: Ruby 3.0.0
+ comment: '#'
+codeforces.49:
+ highlight: rust
+ display: Rust 1.49.0
+ comment: //
+codeforces.20:
+ highlight: scala
+ display: Scala 2.12.8
+ comment: //
+codeforces.34:
+ highlight: javascript
+ display: JavaScript V8 4.8.0
+ comment: //
+codeforces.55:
+ highlight: javascript
+ display: Node.js 12.6.3
+ comment: //
+
之后再重启 Hydro 即可。
`,8);function B(d,D){const l=o("ExternalLinkIcon");return e(),c("div",null,[E,n("p",null,[s("由于 vjudge 更新了反爬虫机制,Codeforces RemoteJudge 需要一些特殊手段才能正常工作。"),t,s(" 详情请 "),n("a",i,[s("阅读源码"),r(l)])]),y])}const m=p(F,[["render",B],["__file","vjudge.html.vue"]]);export{m as default}; diff --git a/dev/PERM_PRIV.html b/dev/PERM_PRIV.html new file mode 100644 index 00000000..7cb948de --- /dev/null +++ b/dev/PERM_PRIV.html @@ -0,0 +1,40 @@ + + + + + + + +Hydro 的权限使用位运算处理。
例:若某用户具有 PRIV_EDIT_SYSTEM
与 PRIV_SET_PERM
权限,应设置为 (1<<0)|(1<<1)
(即 3)
可以看 代码 中关于此部分的内容。
参考 前端修改。
请注意:在阅读本节之前,请确认您已阅读【使用 TypeScript 编写插件】一节并已完成了插件的创建。
import { Context, Time } from 'hydrooj';
+
+export async function apply(ctx: Context) {
+ // handler 表示路由事件
+ // after 表示在主逻辑完成后运行
+ // RecordDetail 为需要捕获的路由名
+ // get 表示仅捕获 GET 请求
+ ctx.on('handler/after/RecordDetail#get', (h) => {
+ if (h.rdoc._id.getTimestamp() < new Date(Date.now() - Time.day) {
+ h.rdoc.code = '';
+ }
+ });
+}
+
如果您没有过 TypeScript 项目的开发经验,非常不建议自建开发环境。
使用自动脚本安装也可以基于插件系统完成大部分的定制需求(参照左侧【使用 TypeScript 编写插件】章节)。
您可以使用 Gitpod 快速打开配置完成的开发环境或是按照下方说明进行手动配置。(由于 Gitpod 的限制,hydrojudge 模块无法正常运行,若需要开发 hydrojudge 相关内容请自行部署)
. <(curl https://hydro.ac/nix.sh)
快速安装)npm install -g yarn
尽管这不是必须的,但文档多数区域使用了 npx
工具来调用工作区的程序。如果此命令不存在,你可以在 Hydro 项目文件夹外使用 yarn global add npx
安装它。
git clone https://github.com/hydro-dev/Hydro.git /root/Hydro --recursive # 下载至 /root/Hydro 文件夹
+cd /root/Hydro # 进入工作目录
+yarn # 安装依赖包
+yarn build:ui:production # 编译前端
+
开发环境部署完成后默认不启用任何插件。
所有官方插件均随源码仓库下载到安装文件夹的 packages
子文件夹下,可以通过下面的命令启用官方插件(以启用 @hydrooj/ui-default
为例):
npx hydrooj addon add @hydrooj/ui-default
+
对于非官方插件,下载后通过下面的命令启用即可(以启用位置在 /root/addon
下的插件为例):
npx hydrooj addon add /root/addon
+
支持如下启动参数:
--port=2333
指定启动端口--debug
启用开发模式使用 yarn debug --port=2333 --watch
启动 Hydro,并在后台运行 yarn build:ui:dev
,可以对前端源码进行实时转译,在反复修改时可节省编译时间。启动完成后,您可以在 8000 端口访问到 Hydro 实例,且前端的任何更改将即时生效。(后端热重载可能存在 bug,部分模块修改后可能仍需重启才能生效)
请注意:此功能仅在启用了 @hydrooj/ui-default
插件的情况下才会生效。
首次启动会要求您填写数据库连接信息。请根据您数据库的安装填写(若无密码则留空)
请按照下文说明创建管理员账户即可正常使用。
需要更新的时候进入对应仓库文件夹执行 git pull
,然后重新 yarn && yarn build:ui:production
即可。
前置条件:NodeJS>=18
此教程将以编写剪贴板插件为例进行说明。
使用 hydrooj addon create
快速在 /root/addon
下初始化一个插件或是在一个空文件夹中运行 yarn init
并按照提示填写相关信息。
# 使用 yarn init 的样例
+/workspace/hydro-plugin $ yarn init
+yarn init v1.22.4
+question name (hydro-plugin): @hydrooj/pastebin
+question version (1.0.0): 0.0.1
+question description: HydroOJ的剪贴板组件
+question entry point (index.js): index.ts
+question repository url: https://github.com/hydro-dev/pastebin.git
+question author: undefined <i@undefined.moe>
+question license (MIT): MIT
+question private:
+success Saved package.json
+
分析:剪贴板组件需要以下功能:
在路由中定义所有的函数应均为异步函数,支持的函数有:prepare, get, post, post[Operation], cleanup
具体流程如下:
先执行 prepare(args) (如果存在)
+args 为传入的参数集合(包括 QueryString, Body, Path)中的全部参数,
+再执行 prepare(args) (如果存在)
+检查请求类型:
+
+为 GET ?
+ -> 执行 get(args)
+为 POST ?
+ -> 执行 post(args)
+ -> 含有 operation 字段?
+ -> 执行 post[Operation]
+
+执行 cleanup()
+
如果在 this.response.template 指定模板则渲染,否则直接返回 this.response.body 中的内容。
如 <input type="hidden" name="operation" value="confirm_delete">
对应 postConfirmDelete
函数。
应当提供 apply
函数,并与定义的 Handler 一同挂载到 global.Hydro.handler[模块名]
位置。 apply
函数将在初始化阶段被调用。
// @noErrors
+// @module: esnext
+// @filename: index.ts
+import { definePlugin, Handler, Types, param, db, PRIV, validator, NotFoundError, PermissionError } from 'hydrooj';
+
+const coll = db.collection('paste');
+
+interface Paste {
+ _id: string;
+ owner: number;
+ content: string;
+ isPrivate: boolean;
+}
+
+declare module 'hydrooj' {
+ interface Model {
+ pastebin: typeof pastebinModel;
+ }
+ interface Collections {
+ paste: Paste; // 声明数据表类型
+ }
+}
+
+async function add(userId: number, content: string, isPrivate: boolean): Promise<string> {
+ const pasteId = String.random(16); // Hydro 提供了此方法,创建一个长度为16的随机字符串
+ // 使用 mongodb 为数据库驱动,相关操作参照其文档
+ const result = await coll.insertOne({
+ _id: pasteId,
+ owner: userId,
+ content,
+ isPrivate,
+ });
+ return result.insertedId; // 返回插入的文档ID
+}
+
+async function get(pasteId: string): Promise<Paste> {
+ return await coll.findOne({ _id: pasteId });
+}
+
+// 暴露这些接口,使得 cli 也能够正常调用这些函数;
+const pastebinModel = { add, get };
+global.Hydro.model.pastebin = pastebinModel;
+
+// 创建新路由
+class PasteCreateHandler extends Handler {
+ // Get请求时触发该函数
+ async get() {
+ // 检查用户是否登录,此处为多余(因为底部注册路由时已声明所需权限)
+ // 此方法适用于权限的动态检查
+ // this.checkPriv(PRIV.PRIV_USER_PROFILE);
+ this.response.template = 'paste_create.html'; // 返回此页面
+ }
+
+ // 使用 isContent 检查输入
+ @param('content', Types.String, isContent)
+ @param('private', Types.Boolean)
+ // 从用户提交的表单中取出content和private字段
+ // domainId 为固定传入参数
+ async post(domainId: string, content: string, isPrivate = false) {
+ // 在HTML表单提交的多选框中,选中值为 'on',未选中则为空,需要进行转换
+ const pasteid = await pastebin.add(this.user._id, content, !!isPrivate);
+ // 将用户重定向到创建完成的url
+ this.response.redirect = this.url('paste_show', { id: pasteid });
+ // 相应的,提供了 this.back() 方法用于将用户重定向至前一个地址(通常用于 Ajax 或是部分更新操作)
+ }
+}
+
+class PasteShowHandler extends Handler {
+ @param('id', Types.String)
+ async get(domainId: string, id: string) {
+ const doc = await pastebin.get(id);
+ if (!doc) throw new NotFoundError(id);
+ if (doc.isPrivate && this.user._id !== doc.owner) {
+ throw new PermissionError();
+ }
+ this.response.body = { doc };
+ this.response.template = 'paste_show.html';
+ }
+
+ @param('id', Types.String)
+ async postDelete(domainId: string, id: string) {
+ // 当提交表单并存在 operation 值为 delete 时执行。
+ // 本例中未实现删除功能,仅作为说明。
+ }
+}
+
+// 定义为一个插件
+export default definePlugin({
+ apply(ctx) {
+ // 注册一个名为 paste_create 的路由,匹配 '/paste/create',
+ // 使用PasteCreateHandler处理,访问改路由需要PRIV.PRIV_USER_PROFILE权限
+ // 提示:路由匹配基于 path-to-regexp
+ ctx.Route('paste_create', '/paste/create', PasteCreateHandler, PRIV.PRIV_USER_PROFILE);
+ ctx.Route('paste_show', '/paste/show/:id', PasteShowHandler);
+ }
+});
+
模板采用 nunjucks 语法。放置于 templates/
文件夹下。
会在请求结束时根据 response.template
的值选择模板,并使用 response.body
的值进行渲染,存入 response.body
中。
若 response.template
为空或 request.headers['accept'] == 'application/json'
,则跳过渲染步骤。
用于提供多国翻译。格式与 Hydro 的 locale 文件夹格式相同。
为什么使用 Hydro?
安全:使用 cgroup 进行隔离,杜绝卡评测;
高效:Hydro 使用了沙箱复用技术,拥有极高的评测效率;
扩展:Hydro 支持安装额外模块进行扩展;
强大:配合 Judge 模块(或 HydroJudge 独立评测机),可支持 spj,交互题,提交答案题,文件IO 等多种特性;
自定:所有权限节点均可自由设置;
易上手:无需改动源代码,系统设置中预留了大量可自行修改的内容;管理逻辑简洁;
社区:Hydro 正在持续维护中;
如果您正在使用 HustOJ,可以导出题目为 FPS 文件后使用 fps-importer 插件 直接导入 Hydro。
如果您正在使用 QDUOJ, 可以导出题目为 QDUOJ-zip 格式后使用 import-qduoj 插件直接导入 Hydro。
如果您正在使用 Vijos / SYZOJ / HustOJ / UniversalOJ, 可以直接使用 migrate 插件 导入所有数据至 Hydro。
Hydro 支持很多其他系统无法支持的题型,可在 https://hydro.ac/d/system_test/p 中查看并免费下载使用样例。(需要登录)
下方对比了 Hydro 与其他主流 OJ 系统的功能。(只进行在不修改源代码情况下的对比)
+:支持
+=:部分支持
+?: 未知
+-:不支持
+
功能 | Hydro | HustOJ | SYZOJ[1] | QDUOJ | Vijos |
---|---|---|---|---|---|
安装 | 一键脚本 | 一键脚本 | 手动搭建 | docker | docker |
数据库 | MongoDB | MySQL | MariaDB | Postgres | MongoDB |
测试数据存储 | 本地/S3 [2] | 本地 | 本地 | 本地 | 数据库 |
多评测机 | + | =[3] | =[4] | = | + |
测试数据同步 | 按需抓取 | 全量同步 | 全量同步 | 全量同步 | 按需抓取 |
比赛 | ACM/OI/IOI/乐多 | ACM/OI | ACM/OI/IOI | ACM/OI/IOI | ACM/OI |
封榜 | + | + | - | - | - |
作业功能 | + | + | - | - | + |
修改编译命令/添加语言 | + | =[5] | - | - | + |
权限系统 [6] | + | = | - | - | + |
训练计划(题单) | + | + | -[7] | - | + |
团队 | + [8] | - | - | - | + |
Hack | + | - | - | - | - |
SpecialJudge | + [9] | + | + | - | = |
Subtask | + | - | + | - | - |
交互题 | + | - | + | - | - |
RemoteJudge | CF/SPOJ/UOJ/POJ/Luogu | HDU/PKU | - | - | - |
题目导入 | fps/syzoj/qduoj/hydro | fps/qduoj | syzoj | fps/qduoj | - |
点击 部署 ,开始部署您的 OJ 吧!
SYZOJ 和 Lyrio (曾用名 syzoj-ng,loj.ac 当前所用系统) 是两套不同的系统,这意味着使用 SYZOJ 无法再导入 loj.ac 的题目,同时 Lyrio 无比赛功能。 ↩︎
S3 指所有兼容 Amazon S3 协议的服务,包括腾讯COS,阿里OSS 等。 ↩︎
安装配置繁琐,且需要手动在服务器间同步数据。 ↩︎
需要手动在服务器间同步数据。 ↩︎
仅能修改部分编译参数,添加语言需要修改源代码。 ↩︎
此处的权限系统指 除用户/管理员二元化权限之外的 的更细粒度的权限划分。 ↩︎
部分二次开发版本有此功能。 ↩︎
通过域功能,允许用户创建域并在域内拥有管理员权限。域之间仅共享账号数据,也可使用域内小组进行权限控制。 ↩︎
支持所有主流 SPJ 格式。 ↩︎
从 2022/8/12 开始,Hydro 为了避免宿主机环境变化对于评测的影响,对于 此后新安装的实例 默认使用 nix 管理环境。
如果你是在这之前安装的 Hydro,请使用 apt 安装编译器后使用 pm2 restart hydro-sandbox
重启沙箱,并忽略本章节。
以下是 nix 的简要操作说明:
使用 nix-env -iA nixpkgs.编译器名
安装新编译器,后重启沙箱 pm2 restart hydro-sandbox
生效。
可以在 Nixos Search 中搜索你需要的编译器。
以下是常用编译器的示例:
nix-env -iA nixpkgs.busybox nixpkgs.bash nixpkgs.diffutils nixpkgs.unzip # 基础组件,已预装,不建议删除
+nix-env -iA nixpkgs.gcc nixpkgs.fpc # C/C++ 和 Pascal,已预装,不建议删除
+nix-env -iA nixpkgs.ghc # Haskell
+nix-env -iA nixpkgs.rustc # Rust
+nix-env -iA nixpkgs.python2 # Python2
+nix-env -iA nixpkgs.pythonPackages.numpy # Python2 Numpy 库
+nix-env -iA nixpkgs.python3Minimal
+nix-env -iA nixpkgs.python3Packages.numpy
+nix-env -iA nixpkgs.php # PHP
+nix-env -iA nixpkgs.go # Golang
+nix-env -iA nixpkgs.nodejs # NodeJS
+nix-env -iA nixpkgs.openjdk_headless # Java
+nix-env -iA nixpkgs.ruby # Ruby
+nix-env -iA nixpkgs.mono # C#
+nix-env -iA nixpkgs.julia_17-bin # Julia
+
使用 nix-env -q
查看已安装的列表,后使用 nix-env -e 编译器名
即可删除对应的编译器。
请注意不要误删 Hydro 基础组件,且操作完成后需要重启沙箱 pm2 restart hydro-sandbox
生效。
如果你需要更加复杂的编译环境配置,我们建议使用编写单独的 nix 文件。
{
+ system ? builtins.currentSystem,
+ pkgs ? import <nixpkgs> { system = system; }
+}:
+
+pkgs.buildEnv {
+ name = "hydrojudge-rootfs";
+ paths = with pkgs; [
+ coreutils bash diffutils nix zip unzip gcc
+ # 上方包是评测所需要的,请勿删除,
+ # 在下方列出你所需要的包,查找方式同上文:
+ fpc python3 rustc
+ ];
+ ignoreCollisions = true;
+ pathsToLink = [ "/" ];
+ # 导出一些基本信息和部分编译器所需的 /etc/passwd
+ postBuild = ''
+ mkdir $out/buildInfo
+ echo 'root:x:0:0:root:/root:/bin/bash' >$out/etc/passwd
+ date >$out/buildInfo/timestamp
+ '';
+}
+
复制以上文件,保存为 default.nix
,使用 nix-build
进行构建。
构建后会产生一个 result
文件夹,记住该文件夹所在的路径。
打开 ~/.hydro/mount.yaml
将其中 /root/.nix-profile
替换为编译出的 result
文件夹(切换到新的环境)
之后保存并重启沙箱。
后续若需更改环境配置,仅需要修改 default.nix
文件之后 nix-build
重新构建,再重启沙箱即可生效。
构建过程中的缓存文件可以使用 nix-collect-garbage
进行清理。
更详细的 nix 语言介绍,请参照 Nix Guide 和 Nix Manual。
这里提供了几套方案帮助您建立自己的站点,请选择适合您的方案并继续。
搭建过程中如果遇到问题欢迎 联系我们 提问。
不同服务商提供的 CPU 主频不同,下方数据仅供参考。
最低服务器配置: CPU: 1核 内存: 2G。(约可允许 100 人使用)
请尽量不要使用突发性能实例或共享型实例,这可能会导致评测时间计量不准确
CentOS 8 已于 2021-12-31 停止支持,后续不会为安全漏洞发布补丁,建议重装为其他操作系统。
如果使用虚拟机安装,推荐购买 UPS 保证供电稳定或是经常备份,突然断电易导致数据库损坏无法读取!
Tips
兼容大部分 Linux 发行版,推荐使用 Ubuntu 22.04 (教程多,成功率高,上手简单),以下列表中系统不受支持:
Tips
Hydro 需要使用以下端口: 80, 443, 2019, 8888, 5050, 27017,请确保这些端口空闲。
安装和安装后的所有操作均需要在 root 权限下进行!(sudo su
)。
宝塔面板已知出现多次高危漏洞,为防止数据丢失,请不要在生产环境中使用!
运行下面的脚本,等待几分钟即可(建议复制防止敲错):
LANG=zh . <(curl https://hydro.ac/setup.sh)
+
Tips
如果有特殊需求,安装脚本支持一些可选的高级选项,以此方式调用: . <(curl https://hydro.ac/setup.sh) --foo --bar
阿里云/腾讯云/华为云等等用户安装后如果不能访问 请百度搜索 xx云 放行80端口
脚本默认使用的为清华大学镜像。
安装完成后,从 http://服务器ip/
访问网页端,注册一个账号,之后在终端中使用
hydrooj cli user setSuperAdmin 2
+
将首个注册用户设置为超级管理员。之后刷新页面,您应当能在上方导航栏看到控制面板入口。
进入控制面板,右侧系统设置,验证管理员密码后按需修改配置,注意 Server BaseURL 一项需要填写访问网站用的完整的 URL,以 /
结尾。(重要,务必正确填写,样例:https://hydro.ac/
)
系统安装后需要题库,可前往 https://hydro.ac/d/tk/p 免费专区进行下载,支持批量导入。 至此,基础功能安装已全部完成,另,如果你的服务部署在内网,且希望外网的用户可以访问,可以百度搜索 “端口映射” 相关教程。
~/.hydro/Caddyfile
的配置文件!Note
若使用反向代理,请注意系统设置中的 server.xff 和 server.xhost 设置项需要填写正确(小写),分别对应反向代理所添加的标头名(通常为 x-forwarded-for 和 x-forwarded-host,部分反代工具会使用 x-real-ip 替代 x-forwarded-for)。
server.xhost 设置项配置错误会导致用户无法登录等问题。(CsrfTokenError)
server.xff 设置项配置错误会导致无法记录用户IP。
除一键安装脚本在安装 Caddy 后会自动配置设置项,其他工具请在使用工具前先配置好系统设置!否则将造成用户无法登录、无法记录用户IP等问题。
若您使用 Nginx,请注意配置 WebSocket 协议的反向代理,否则会导致评测状态无法自动刷新,在线 IDE 无法正常使用等问题。
Hydro 支持使用 Caddy, HaProxy 等工具进行反向代理,此处 提供了一些配置样例。
Hydro 推荐您使用 Caddy。以下为样例 Caddyfile。 提示:如果您的服务器位于国内,则需要进行备案后才能使用 80 和 443 端口。
hydro.ac {
+ log {
+ output file /data/access.log {
+ roll_size 1gb
+ roll_keep_for 72h
+ }
+ format json
+ }
+ root * /root/.hydro/static
+ @static {
+ file {
+ try_files {path}
+ }
+ }
+ handle @static {
+ file_server
+ }
+ handle {
+ reverse_proxy http://127.0.0.1:8888
+ }
+}
+
Tips
使用一键安装脚本部署的 Hydro 一般已自动完成存储配置。
文件默认存储于本地的 /data/file/hydro
目录下,切换存储后端时需要手动复制或移动原有文件。
请根据你使用的存储端类型阅读对应教程。(同时欢迎使用其他存储类型的用户向我们提供详细的存储教程)
MinIO
Note
新版本 MinIO 占用较大,如果您为轻量使用,我们不建议使用 MinIO 。旧版本 Hydro 用户大部分可直接切换至 file 模式,由 Hydro 代替读取。
安装 MinIO 后进入 控制面板>manage_config。
file.type
: s3
file.endPoint
: http://127.0.0.1:9000
(即 MinIO 启动时显示的 endPoint)file.accessKey
: 参照 MinIO 配置file.secretKey
: 参照 MinIO 配置file.bucket
: hydro
(MinIO 内部存储桶名称)file.region
: us-east-1
file.pathStyle
: truefile.endPointForUser
: /fs/
file.endPointForJudge
: /fs/
保存后重启,已有文件请自行复制。
腾讯云COS
进入 控制面板>系统设置>存储桶设置。
file.type
: s3
file.endPoint
: http://cos.<存储桶地域>.myqcloud.com
(或是 https)file.accessKey
: 您的腾讯云 API 密钥的 SecretIdfile.secretKey
: 您的腾讯云 API 密钥的 SecretKeyfile.bucket
: <存储桶名称>file.region
: Autofile.pathStyle
: truefile.endPointForUser
: 给用户使用的 EndPoint,若填 /fs/
则表示由服务器转发file.endPointForJudge
: 给 judge 使用的 EndPoint,若填 /fs/
则表示由服务器转发保存后重启,已有文件请自行复制。
阿里云OSS
// TODO & PRs Welcome
以 QQ 邮箱为例。
已知能够完整兼容的服务商有:
如果使用其他服务商且没有发现问题,欢迎向此列表 Pull Request。
已移动至 此处
如您希望改善 Hydro 的访问质量,在您的服务器带宽较小的情况下,您可以考虑使用内容分发网络,即 CDN ,通常情况下您仅需设置资源 CDN 即可大幅度改善访问质量,关于如何使用内容分发网络和使用中的任何问题请联系各大云厂商。
创建并配置好资源 CDN ,在控制面板中将 server.cdn
设置项修改为 CDN 域名。(如 https://cdn.hydro.ac/
,以 /
结尾)。
如果您预算充足,可以使用全站 CDN。
Note
全站 CDN 配置较为繁琐,如您没有相关使用经验请仅建立并配置资源 CDN ,配置资源 CDN 在大部分情况下即可大幅度改善访问质量。 因各服务商全站 CDN 配置方法不同,配置过程不同,需要配置的内容较多,以下仅为参考配置且配置补全,如果有任何问题建议先查阅云服务商文档及咨询云服务商工程师。
缓存设置可以参考以下设置:
类型 | 内容 | 缓存行为 |
---|---|---|
全部文件 | 全部文件 | 遵循源站 |
文件后缀 | jpg,js,png,css,gif | 缓存一天 |
同时设置添加回源HTTP请求头
头部参数 | 头部取值 |
---|---|
X-Forward-For | 参考所使用服务商CDN文档来设置用户来源IP |
且您应当修改系统设置中的 server.xff
值为 X-Forwarded-For
,该设置是为了能够让系统正确获取到用户的IP地址。
Tips
在使用 cli 之前,请完成数据库配置。
指令中使用 <>
括起来的参数必须给出,用 []
括起来的参数可以给出,若不给出则按照默认设置。
用户请根据自己的情况替换掉用 <>
或是 []
包括起来的部分(括号也应替换)
cli 可以帮助用户在控制台下快捷地进行一些操作。
这些命令需要以在终端以 root 用户执行(安装时执行命令的位置)。
下面给出了一些常用的例子。
Tips
很少使用。建议通过 控制面板>导入用户 功能代替
hydrooj cli user create <mail> <username> <password> <uid>
+# 该用户的邮箱、用户名和密码
+
+# 如创建邮箱为 hydro@hydro.local,用户名为 Hydro,密码为 hydrohydro ,UID 为 2 的用户:
+# 请确保 UID 为不小于 2 的正整数且未被占用,邮箱和用户名均不重复。
+hydrooj cli user create hydro@hydro.local Hydro hydrohydro 2
+
若一切正常,运行该指令后您会从命令行窗口中看到该用户 uid。
hydrooj cli user setSuperAdmin <uid>
+
+# 如设置 uid 为 2 的用户为管理员:
+hydrooj cli user setSuperAdmin 2
+
hydrooj cli user setPriv <uid> <priv>
+
关于参数 [priv]
,可阅读 此处。
hydrooj cli user setPassword <uid> <password>
+
+# 如将 uid 为 1 的用户的密码改为 hydrohydro:
+hydrooj cli user setPassword 1 hydrohydro
+
先 创建一个账号。
您需要留意运行此指令返回的数字,表示该用户的 uid
,需要填入下面的指令中,然后给予该账号评测权限。
hydrooj cli user setJudge <uid>
+
完成后将配置的用户名及密码写入评测机配置文件,评测机即可连接到网页端。
用户封禁:
hydrooj cli user setPriv <uid> 0
+
IP/邮箱域名封禁:
# key 格式为 ip::xxx.xxx.xxx.xxx (封禁 IP 访问) 或是 mail::xxx.com (禁止 xxx.com 的邮箱注册)
+hydrooj cli blacklist add <key> [duration] # 将 <key> 拉入黑名单,时长为 [duration] (以月为单位的整数,默认为 12,若 duration=0 则永久封禁)
+hydrooj cli blacklist get <key> # 获取黑名单中有关 <key> 的信息
+hydrooj cli blacklist del <key> # 将 <key> 移出黑名单
+
所有于 此文件夹 下的函数均可用 cli 调用。
这里并没有列出所有可以运行的指令,因为其中很多功能我们更推荐通过 Web 访问。
hydrooj cli user create <mail> <uname> <password> [uid] [regip] [priv]
+# 创建邮箱为 <mail>,用户名为 <uname>,密码为 <password>,ID 为 [uid],注册 ip 为 [regip],权限为 [priv] 的用户
+hydrooj cli user setUname <uid> <unmae> # 将 ID 为 <uid> 的用户的用户名设置为 <uname>
+hydrooj cli user setPriv <uid> <priv> # 将 ID 为 <uid> 的用户的权限设为 <priv>
+hydrooj cli user setPassword <uid> <password> # 将 ID 为 <uid> 的用户的密码设置为 <password>
+hydrooj cli user setEmail <uid> <mail> # 将 ID 为 <uid> 的用户的邮箱设置为 <mail>
+hydrooj cli user setSuperAdmin <uid> # 将 ID 为 <uid> 的用户设为全站管理员
+hydrooj cli problem import <domainId> <file/path> # 将 <file/path> 的Hydro格式题目包导入至 <domainId> 域中
+hydrooj cli problem export <domainId> # 将 <domainId> 域中的所有题目包导出
+hydrooj cli system set <key> <value> # 修改系统设置 <key> 值为 <value>
+
请参考 FAQS 内的数据库连接教程。
为了保证数据安全,请定期备份。
若您使用自动脚本安装,可使用 hydrooj backup
快捷备份数据,备份完成后会在当前目录生成备份压缩包文件,您可使用 hydrooj restore <备份文件路径>
恢复之前备份的数据。
可使用 MongoDB 自带的 mongodump 进行数据库备份。并将 /data/file
文件夹备份即可。
对于数据库,请请不要在数据库运行时直接拷贝数据库文件夹。请每次备份后检查生成的备份文件的大小和内容,确保备份成功。
请不要把备份数据和 Hydro 系统放在同一台机器上,这样数据丢失的风险仍然较高。
使用 MongoDB 自带的 mongorestore 导入备份的数据库文件,并还原 /data/file
目录文件。
如果只是想不同机器之间迁移部署,只需要在停止 Hydro 和 MongoDB 服务后将相关文件夹(通常为 /data/db
与 /data/file
与 /root/.hydro/config.json
)复制即可。
此指南将教您修改前端文件。
如果您正在使用开发者模式,请直接修改 packages/ui-default/templates
下的文件。
如果您使用安装脚本部署:
请先使用 hydrooj addon create
创建一个本地插件(如果之前没有做过的话)。
~/addon/locales/zh.yaml
。通常的,在您访问的 url 前加上 view-source:
(如 view-source:https://hydro.ac
即可查看页面源代码,在第二行的 <html data-page="xxx">
中 data-page
值即为页面名(首页例外,为 main.html
)。 在 默认 templates 中找到对应文件,将文件的全部内容 复制到 ~/addon/templates/
文件夹下后进行修改即可。
特别的,创建题目时的默认模板位于 partials/problem_default.md
,创建训练计划时的默认模板位于 partials/training_default.json
,修改方式同上。
以上所有更改均会在重启 Hydro 后生效。
目前支持 csv 格式(用 ,
分隔)或 Excel 格式(用 TAB 分隔) 导入用户数据, 数据既可以用文本编辑器创建,也可以用 Excel 等软件来辅助创建。
每行最少三列,最多五列,分别为: 邮箱,用户名,密码,显示名,用户信息。(显示名和用户信息为可选)
请使用 UTF-8 编码,否则中文可能会乱码。
如果使用 CSV 格式(逗号分隔),则用户信息列不可用。
foo@undefined.moe user1 password1
+bar@undefined.moe user2 password2 temp
+test@undefined.moe user3 password3 test {"group":"class1","studentId":"123","school":"Hydro School"}
+
可以在粘贴后点击预览验证复制入的数据的有效性
这将创建三个用户:
user1
密码为 password1
, 邮箱 foo@undefined.moe
;user2
密码为 password2
,邮箱 bar@undefined.moe
,显示名为 temp
;user3
密码为 password3
,邮箱 test@undefined.moe
,显示名为 test
,学校为 Hydro School
,学号为 123
,该用户将会被分配至当前域的 class1
小组内;Note
用户创建后无法删除,请谨慎操作
使用一键安装脚本安装的 Hydro 使用 PM2 对进程进行管理。
可以通过下面的指令查看当前 PM2 正在管理的所有进程。
pm2 ls
+
一键安装脚本默认会创建下面几个进程:
hydrooj
: Hydro 主进程hydro-sandbox
: Hydro 评测沙箱mongodb
: MongoDB 数据库caddy
: 反向代理后文的指令中将用 <name>
替代此处的进程名称,用 <id>
替代进程 ID(进程 ID 可通过 pm2 ls
查看)。(尖括号同样需要替换)
pm2 ls # 查看进程列表
+pm2 start <id> # 启动进程
+pm2 stop <id> # 关闭进程
+pm2 restart <id> # 重启进程
+pm2 del <id> # 删除进程
+pm2 log <id/name> --lines=100 # 查看该进程的后 100 行日志
+pm2 attach <id> # 与进程交互
+pm2 save # 保存对 PM2 进行的修改(在添加、修改、删除进程后需要执行该指令)
+
在部分环境(常见于 lxc 容器或是精简版系统)下,服务器重启后 Hydro 可能不能正常自启动,这时请使用 pm2 resurrect
手动载入进程列表。
如果手动修改进程列表且已经覆盖掉保存的原列表,请使用 pm2 stop all && pm2 del all
清空所有进程之后重新运行安装脚本。原有数据不会丢失。
Hydro 主进程同样支持多进程启动,但在中低端服务器(不超过4核)中没有必要使用多进程启动 Hydro,会降低性能且成倍提高内存占用。
pm2 start hydrooj -i <n> # 以 n 进程启动 Hydro 主进程
+
Hydro 系统会不定期发布更新,可以使用下面的命令获取更新。
无特殊情况请 不要更新PM2 !此操作可能导致进程列表丢失!
yarn global upgrade-interactive --latest # 在交互式界面中选择想要更新的组件
+pm2 restart hydrooj # 更新完后需重启 hydrooj
+
cd `yarn global dir` && yarn list --pattern hydrooj
+
yarn cache clean && nix-collect-garbage
+
复制题目可以帮助用户将任何有权提交的题目复制到有权创建题目的域中以在比赛/作业/训练计划中使用这些题目。
域功能类似团队,允许在一套系统中创建多个环境(如不同班级,或是不同功能,等等)
用户可以创建多个域。(需要用户有 PRIV_CREATE_DOMAIN 权限,默认仅开放给管理员账户)。 域间数据完全独立,仅用户信息相通(注册账户后,在该实例的所有域中均有效)。
登录账号后,在“我的”选项卡中找到“我的域”,并点击“创建域”,填入以下信息:
gravatar:email
或 qq:id
或 github:name
或 url:link
的格式添加。将会在“我的域”界面内显示。创建域后,您将在此域中拥有管理员权限,可以在域内进行添加题目/创建比赛等操作。
您可以在“管理域”选项卡中点击“初始化讨论节点”按钮初始化讨论节点。
未登录用户将默认使用 guest
权限,登录用户将默认使用 default
权限。(所以将登陆用户设为 default
权限后并不会显示在“管理用户”页内,这也表示所有用户默认不会出现在管理列表中)
所以将一个用户的权限设为 default
和将用户移出该域是等价的。
对于不在列表中的用户,点击右上角“添加用户”,在左侧选中用户,右侧选择权限组,再点击“确定”即可。
若您想要创建比赛/作业,您可以在“比赛”或“作业”选项卡中,在页面右侧找到“创建”按钮, 题目一栏支持根据题目ID或是题目名自动筛选。设置完后可点击“创建”按钮创建比赛(描述这类的框不知道写啥就随便填,不能留空)。
Tips
若因为删除作业/比赛内题目导致无法打开,可以通过 /contest/<id>/edit
或 /homework/<id>/edit
(即在无法打开的页面页面后加上 /edit
)直接访问编辑页并修正。
若您想要创建训练,您可以在“训练” 项卡中点击“新建训练计划”,填写以下信息:
[
+ {章节详细信息},
+ {章节详细信息},
+ ...
+ {章节详细信息}
+]
+
其中,“章节详细信息”的包含以下部分:
_id
:章节数字编号;title
:章节标题;requireNids
:训练此章节之前需要完成的章节数字编号,若无要求则留空,若有多个则使用逗号分隔;pids
:此章节中包含的题目的 ID,若有多个则使用逗号分隔。举例:若要在训练中创建三个章节,章节中分别有 ID 为 1,2,3 的题目。其中章节一、二无前置条件,章节三需要同时完成章节一、二后才能进行,则格式如下:
[
+ {
+ "_id": 1,
+ "title": "入门",
+ "requireNids": [],
+ "pids": [1]
+ },
+ {
+ "_id": 2,
+ "title": "精通",
+ "requireNids": [],
+ "pids": [2]
+ },
+ {
+ "_id": 3,
+ "title": "大师",
+ "requireNids": [1,2],
+ "pids": [3]
+ }
+]
+
Tips
若因为删除训练计划内题目导致训练计划无法打开,可以通过 /training/<id>/edit
(即训练计划页面后加上 /edit
)直接访问训练计划编辑页并修正配置文件。
Hydro 中题目的难度,根据递交数、通过率以及每个递交的递交时间和评测结果,通过算法计算得出。
您可以在比赛的详细界面内点击“参与比赛”按钮进行参与。 比赛过程中“成绩表”会根据比赛规则显示排名。 在比赛截止之后,您仍然可以订正其中的题目,但“成绩表”将停止更新。
若您想发布一个讨论,请先进入一个讨论节点,之后点击“创建一个讨论”按钮并填写:
之后点击“创建”按钮进行发布。
您可以在作业的详情页面中,点击“认领作业”。
在作业开始之前,您无法查看作业中的题目。
在作业持续时间内,您与他人的做题情况会被实时统计在“成绩表”内。
在作业进入延期阶段后,您仍然可以提交题目,但成绩表内的分数将根据延期扣分规则按百分比折算。
在作业截止之后,您仍然可以订正其中的题目,但“成绩表”将停止更新。
作者: laomai
qq: 29985091
网址: http://82.157.98.222:8888/
日期: 2022/03/16
本文为作者使用hydro时的实验记录,希望对大家有帮助.包括如下内容
零. hydro题目存储格式
一. 制做最简单的oj题
二. 含有自定义头文件的oj题,即函数交互式的题目
三. 半自动对拍的oj题,即不需要录入预期输出的题
四. 完全对拍题,即不需要录入输入数据和输出数据的题
五. 指定输入文件和输出文件
六. 多个子任务
七. 客观题,即制做有标准答案的填空和选择题.
如果想在本地建立好题目,然后批量上传的话. 下面的格式应该对你有帮助
每个题目应自占一个目录,目录名为题目编号比如1 ,2,3,4等等.
每个题目目录下一般有下面的元素:problem_zh.md
这个文件是就是题目的内容,即题目的描述,是一个markdown格式的文档.probelm.yaml
文件.这个是题目的配置信息. 比如标题和标签等。
testdata 子目录,对应网站里的测试数据部分里的文件,
里面至少有一个config.yaml文件 用来说明测试的类型.具体内容见后面的例子
如果题目有测试用例,则每个用例至少要提供一个in文件和一个输出文件(但有些测试类型不用,详情见后)
additional_file子目录用来存放给做题者用的额外文件,比如头文件,图片,pdf文档等,在题目的 markdown 文档中可以用下面的格式为这些文件提供下载链接:[提示文字](file://xxx.txt)
下面为手工录入各种题型的步骤,即在网站登录后点创建题目之后的操作.如下图所示
我们假定题目内容均为下面的markdown文档
# 要求
+
+输入两个整数,输出他们的和
+
+# 样例
+
+```input1
+123 500
+```
+
+```output1
+623
+```
+
题目网址: http://82.157.98.222:8888/p/P10000
1.in
, 表示用例 1 的输入1.in
的内容为两个整数比如 2 和 3 ,空格分开,如下图所示,然后点确定1.out
文件,内容为 5 ,注意数字编号必须和 in 文件一致,创建之后的文件列表和下面类似.#include <iostream>
+using namespace std;
+int main(int argc,char* argv[]){
+ int a,b;
+ cin>>a>>b;
+ cout <<(a+b);
+ return 0;
+}
+
题目网址: http://82.157.98.222:8888/p/P10001
本类型和类型一的区别在于出题者要向做题者提供一个额外的头文件,做题者的主函数里可以包含这个头文件以调用出题者提供的某些函数,或者实现头文件里指定的函数.
tools.h
和 config.yaml
,如下图所示tools.h
的内容为
#include<iostream>
+using namespace std;
+
+int add(int x,int y); //留待做题者实现
+
+int main(int argc,char* argv[]){
+ int a,b;
+ cin>>a>>b;
+ cout << add(a,b);
+ return 0;
+}
+
这个头文件里实现了一个主函数,并且声明了需要做题者实现的函数add,当然,出题者应该在题目要求里写明这个函数的原型以及把tools.h文件上传到附加文件列表中,以方便做题者.
题面里可以用如下格式为用户提供下载链接,中括号内的内容可以自己写,[tools.h](file://tools.h) 如下面所示
config.yaml文件的内容为
type: default
+filename: null
+user_extra_files:
+ - tools.h
+
本题的ac代码为
#include "tools.h"
+int add(int x,int y){
+ return x+y;
+}
+
可见本类型的题目,做题者包含给定的头文件后,可以不需要自己实现主函数,只需要专心实现给定的函数即可.
例题网址:http://82.157.98.222:8888/p/P10002
本题型的特点是不需要手工给出每个用例的预期输出,但是要自己编写一个样本程序,测试时会把用户的输出和样本程序的输出进行对比。
仍以两数求和为例
checker.cc
,内容如下:#include "testlib.h"
+int main(int argc, char * argv[]) {
+ registerTestlibCmd(argc, argv);
+ int a = inf.readInt(); // 读取输入流的第一个整数
+ int b = inf.readInt(); // 读取输入流的下一个整数
+ int d = a+b;
+ int c = ouf.readInt(); // 读取输出流的下一个整数
+ if (a+b != c)
+ quitf(_wa, "%d + %d expected %d, found %d", a, b,d,c); //输出错误的具体信息,便于做题者调试
+ else
+ quitf(_ok, "answer of %d + %d is %d",a,b,c);
+}
+
checker_type: testlib
+checker: checker.cc
+cases:
+ - input: 1.in
+ output: /dev/null # 无输出
+
最终的测试文件列表如下所示:
当程序有错误时,输出的效果如下
可见这里输出了错误细节,便于做题者调试
本题的ac代码和类型一的一样,内容为
#include <iostream>
+using namespace std;
+int main(int argc,char* argv[]){
+ int a,b;
+ cin>>a>>b;
+ cout <<(a+b);
+ return 0;
+}
+
如果不希望自己录入输入数据,而是在每次测试时自动动态生成的话,可以将题目类型设为interactive
,并提供一个对拍程序.仍以求和为例
例题网址: http://82.157.98.222:8888/p/P10005
最后测试数据部分的文件列表如下图所示
checker.cc
,内容为:#include "testlib.h"
+#include <iostream>
+using namespace std;
+
+int main(int argc, char* argv[]) {
+ setName("Interactor A+B");
+ registerInteraction(argc, argv);
+ //自动生成两个随机整数
+ rnd.setSeed(time(NULL));
+ int a = rnd.next(1000);
+ int b = rnd.next(1000);
+ int d = a+b;
+ // 本程序的输出将作为用户程序的输入
+ cout << a << " " << b << endl;
+ int c;
+ // 用户程序的最后输出将作为本程序的输入
+ cin >> c;
+ //对比用户结果和预期结果
+ if (a+b != ans)
+ quitf(_wa, "%d + %d expected %d, found %d", a, b,d,c); //输出错误的具体信息,便于做题者调试
+ else
+ quitf(_ok, "answer of %d + %d is %d",a,b,c);
+}
+
config.yaml
文件的内容为:type: interactive
+interactor: checker.cc
+cases:
+- input: /dev/null # no input and no output, dynamic generated
+ output: /dev/null
+- input: /dev/null # no input and no output, dynamic generated
+ output: /dev/null
+
AC 代码和类型一中的相同
例题网址:http://82.157.98.222:8888/p/P10003
有时希望指定输入和输出文件,此时测试文件 1.in
和 1.out
和类型一类似, 但是要提供config.yaml文件,内容类似于下
file: test
+
则运行时测试环境会自动把每个输入文件复制到test.in中,输出内容和test.out的内容进行对比. ac的代码如下:
#include <fstream>
+using namespace std;
+int main(int argc,char* argv[]){
+ int a,b;
+ ifstream ifs("test.in");
+ ifs>>a>>b;
+ ofstream ofs("test.out");
+ ofs <<(a+b);
+ return 0;
+}
+
例题网址: https://hydro.ac/d/system_test/p/7
data<id>-<数字>
id为子任务编号time: 100ms
+memory: 8m
+subtasks:
+ - score: 20
+ id: 0
+ cases:
+ - input: data1-1.in
+ output: data1-1.ans
+ - input: data1-2.in
+ output: data1-2.ans
+ - input: data1-3.in
+ output: data1-3.ans
+ - input: data1-4.in
+ output: data1-4.ans
+ - input: data1-5.in
+ output: data1-5.ans
+ - score: 20
+ id: 1
+ cases:
+ - input: data2-1.in
+ output: data2-1.ans
+ - input: data2-2.in
+ output: data2-2.ans
+ - input: data2-3.in
+ output: data2-3.ans
+ - input: data2-4.in
+ output: data2-4.ans
+ - input: data2-5.in
+ output: data2-5.ans
+ - score: 20
+ id: 2
+ cases:
+ - input: data3-1.in
+ output: data3-1.ans
+ - input: data3-2.in
+ output: data3-2.ans
+ - input: data3-3.in
+ output: data3-3.ans
+ - input: data3-4.in
+ output: data3-4.ans
+ - input: data3-5.in
+ output: data3-5.ans
+ - score: 20
+ id: 3
+ if: [2]
+ cases:
+ - input: data4-1.in
+ output: data4-1.ans
+ - input: data4-2.in
+ output: data4-2.ans
+ - input: data4-3.in
+ output: data4-3.ans
+ - input: data4-4.in
+ output: data4-4.ans
+ - input: data4-5.in
+ output: data4-5.ans
+ - score: 20
+ id: 4
+ if: [1, 3]
+ cases:
+ - input: data5-1.in
+ output: data5-1.ans
+ - input: data5-2.in
+ output: data5-2.ans
+ - input: data5-3.in
+ output: data5-3.ans
+ - input: data5-4.in
+ output: data5-4.ans
+ - input: data5-5.in
+ output: data5-5.ans
+
可以看出if 用来指定前置子任务.
此外,如果某个子任务没有提供cases部分时,测试时会自动寻找类似于 data<id>-x.in
和data<id>-x.out
的文件,id为子任务编号
上面的例子故意设计为子任务编号和用例文件中的编号不同,所有每个子任务都需要手工指定对应的cases.
注意新版的客观题,格式已经更新。
例题网址:http://82.157.98.222:8888/p/P10004
客观题只需要题面和config.yaml文件. 例子如下:
1. 填空题
+
+1+1 = {{ input(1) }}
+
+2. 选择题
+
+{{ select(2) }}
+- 1+1=2
+- 1+1=3
+- 1+1=4
+
+3. 多选题
+
+{{ multiselect(3) }}
+- A
+- B
+- C
+
+
上传的 config.yaml
内容为
type: objective # 表明该题为客观题
+answers: # 列举出每一题的正确选项与对应的得分
+ '1': ['2', 50]
+ '2': [['A', 'B'], 30] # 填空题支持多答案,满足其一得分
+ '3': [['A', 'B'], 20] # 多选题答案为数组,有部分分
+
+
题目运行效果如下:
做完之后点提交,效果如下
可见评分结果正确.
对所有编程题目,题面是必须录入的,如果指定了测试程序时,可以不需要录入输出数据.
如果设置测试方式为interactive,输入数据也不需要手工录入.
想指特殊的测试方式时,一般需要上传一个config.yaml文件,并设置对应字段的值.
对编程题,本文档中用到的字段有
type字段一般为default, 对全自动对拍题,设为interactive
checker_type: testlib checker: checker.cc 用来指定自定义的测试程序,即对拍程序
filename: test用来指定对test.in文件和test.out文件进行读写.
cases:
用来指定测试用例.
更详细的介绍见 https://hydro.js.org/docs/user/testdata/
为了便于系统间进行数据交换,Hydro 定义了一种基于 zip 的标准格式用于题目传输。压缩包内文件结构如下:
喵? tree
+.
+├── 任意文件名的文件夹
+│ ├── problem.yaml
+│ ├── problem_zh.md
+│ ├── testdata
+│ │ ├── config.yaml
+│ │ ├── a1.in
+│ │ ├── a1.out
+│ │ ├── a2.in
+│ │ ├── a2.out
+│ │ ├── a3.in
+│ │ └── a3.out
+│ └── additional_file
+│ ├── a.png
+│ └── b.pdf
+└── ...
+
其中 problem.yaml 内容如下:
title: 题目名
+tag:
+- 标签1
+- 标签2
+pid: 题号(字母+数字)
+
problem_*.md 中为 markdown 格式的题面,语言代号支持完整形式(如 zh_CN),也支持短形式(如 zh)。若同时存在多个语言的题面,Hydro 将会自动识别并提供切换功能。
testdata 文件夹中存放所有测试数据文件,命名规则和配置文件格式请参照【测试数据格式】章节。
additional_file 中存储附加文件,通常用于存放图片,PDF 等文件。这些文件可以在题面中使用 file://文件名 的路径访问。
拥有 PERM_CREATE_PROBLEM 的用户均可以新建题目。
请点击题库页面右下角的 创建题目
按钮。
Tips
题目 ID 不能全为数字。若留空则使用自动分配的数字题号。
详见下方题面编辑部分,以及 laomai 编写的说明
上传 Hydro 导出的题目压缩包即可。
如果您的压缩包较大无法上传我们也提供cli导入方法:
hydrooj cli problem import <domainId> <file/path> # 将 <file(压缩文件)/path(解压后的文件夹)> 的Hydro格式题目包导入至 <domainId> 域中。
+
Hydro 提供了一个小工具 loj-download,可从基于原版 SYZOJ/SYZOJ-NG 搭建的源站下载到符合Hydro格式的题目压缩包。
工具使用方法请前往使用教程查看,自行摸索并确保在网络通畅的环境下使用。
见插件 fps-importer。
见插件 import-qduoj。
题面使用 Markdown 语法,并进行了部分扩展。
支持对样例数据分组显示:
```input1
+1 2
+```
+
+```output1
+3
+```
后接的数字为测试点编号,将自动合并,并左右分栏显示。
支持从附加文件引用资源。(您可以先创建题目,上传相关文件后再编辑该题目)
[file](file://input.in)
![img](file://foo.jpg)
@[pdf](file://foo.pdf)
(部分情况下若无法使用,请尝试 @[pdf](file://foo.pdf?noDisposition=1)
)@[doc](file://foo.docx)
(依赖 onlyoffice 插件)题面支持合并表格:
| 1 | 1 | 3 | 4 | 5 |
+| --- | --- | --- | --- | --- |
+| 1 | 1 | 2 | 2 | 6 |
+| 1 | 1 | 2 | 2 | 7 |
+| 1 | 4 | 3 | 5 | 5 |
+
将被渲染为:
支持内嵌 HTML:(用来对付部分 Markdown 搞不定的东西)
<span bgcolor="red">foo</span>
+
可点击右侧分类面板快速添加标签,也可以用英文半角逗号分隔多个标签。
您可以在题目右侧“文件”面板上传测试数据和附加文件。(支持拖拽文件至相应位置进行上传)
测试数据格式。
1. 填空题
+
+1+1 = {{ input(1) }}
+
+2. 选择题
+
+{{ select(2) }}
+- 1+1=2
+- 1+1=3
+- 1+1=4
+
+3. 多选题
+
+{{ multiselect(3) }}
+- A
+- B
+- C
+
仅需要配置 config.yaml 即可,不需要上传其他文件。
type: objective # 表明该题为客观题
+answers: # 列举出每一题的正确选项与对应的得分
+ '1': ['2', 50] # 填空题/选择题,单答案
+ '2': # 填空题/选择题,多答案,不同答案对应不同分数,注意空格缩进
+ 'A': 30 # 也可以使用相同分数,即同时存在多个正确答案
+ 'B': 10
+ '3': [['A', 'B'], 20] # 多选题答案为数组,有部分分
+
Tips
您可以直接选择文件(支持多选)上传或将文件拖拽至相应位置上传。
若上传文件为 zip 格式,将会自动进行解压操作。
对于一般的题目,您只需提供 .in
和 .out/.ans
文件,以下是一个例子。
请务必确保文件名中含有数字。形如 sample.in
的文件是不会被自动识别的。
喵? tree
+.
+├── a1.in
+├── a1.out
+├── a2.in
+├── a2.out
+├── a3.in
+└── a3.out
+
测试数据将被自动识别,并使用 1S 256MB 的限制。
Tips
推荐您通过 评测设置 在线编辑题目配置,可以拥有更好的编辑体验。
上传 config.yaml
文件即可,文件格式如下(下方所有样例均为可选项,若无说明则预填写的内容即为默认值):
# 题目类型,可以为 default(比对输出,可以含spj), objective(客观题), interactive(交互题)
+type: default
+
+# 全局时空限制(此处的限制优先级低于测试点的限制)
+time: 1s
+memory: 128m
+
+# 输入输出文件名(例:使用 foo.in 和 foo.out),若使用标准 IO 删除此配置项即可
+filename: foo
+
+# 此部分设置当题目类型为 default 时生效
+# 比较器类型,支持的值有 default(直接比对,忽略行末空格和文件末换行), ccr, cena, hustoj, lemon, qduoj, syzoj, testlib(比较常用)
+checker_type: default
+# 比较器文件(当比较器类型不为 default 时填写)
+# 文件路径(位于压缩包中的路径)
+# 将通过扩展名识别语言,与编译命令处一致。在默认配置下,C++ 扩展名应为 .cc 而非 .cpp
+checker: chk.cc
+
+# 此部分设置当题目类型为interactive时生效
+# 交互器路径(位于压缩包中的路径)
+interactor: interactor.cc
+
+# Extra files 额外文件
+# These files will be copied to the working directory 这些文件将被复制到工作目录。
+# 提示:您无需手动上传 testlib.h。
+user_extra_files:
+ - extra_input.txt
+judge_extra_files:
+ - extra_file.txt
+
+# Test Cases 测试数据列表
+# If neither CASES or SUBTASKS are set(or config.yaml doesn't exist), judge will try to locate them automaticly.
+# 如果 CASES 和 SUBTASKS 都没有设置或 config.yaml 不存在, 系统会自动尝试识别数据点。
+# We support these names for auto mode: 自动识别支持以下命名方式:
+# 1. [name(optional)][number].(in/out/ans) RegExp: /^([a-zA-Z]*)([0-9]+).in$/
+# examples:
+# - c1.in / c1.out
+# - 1.in / 1.out
+# - c1.in / c1.ans
+# 2. input[number].txt / output[number].txt RegExp: /^(input)([0-9]+).txt$/
+# - example: input1.txt / input2.txt
+#
+# The CASES option has higher priority than the SUBTASKS option!
+# 在有 CASES 设置项时,不会读取 SUBTASKS 设置项!
+#
+# The CASES option has been deprecated in the new version, please use the more personalized SUBTASKS!
+# CASES 已于新版本中被废弃,请使用个性化程度更高的SUBTASKS!
+# score: 50 # 单个测试点分数
+# time: 1s # 时间限制
+# memory: 256m # 内存限制
+# cases:
+# - input: abc.in
+# output: def.out
+# - input: ghi.in
+# output: jkl.out
+# 或使用Subtask项:
+subtasks:
+ - score: 30
+ type: min # 可选 min/max/sum,分别表示取所有测试点最小值、所有测试点最大值、所有测试点之和
+ time: 1s
+ memory: 64m
+ cases:
+ - time: 0.5s
+ memory: 32m # 可对单个测试点单独设置时间限制和内存限制
+ input: a.in
+ output: a.out
+ - input: b.in
+ output: b.out
+ - score: 70
+ time: 0.5s
+ memory: 32m
+ if: [0] # 可选,传入数组,表示仅在subtask0通过时此subtask才计分
+ cases:
+ - input: c.in
+ output: c.out
+ - input: d.in
+ output: d.out
+
+# 提交语言限制
+# 列举出所有本题允许使用的语言对应的代码(需要和评测机 lang.yaml 内的语言代码相同)
+# 使用语言ID而非名称!对于有子类的选项,请详细至子分类!
+langs:
+ - c
+ - cc
+ - cc.cc11o2
+ - pas
+
+# 时间内存倍率
+# 对某语言设置时间或内存倍率(需要和评测机 lang.yaml 内的语言代码相同)
+# 部分语言默认已存在倍率,请前往控制面板中查看!
+# 使用语言ID而非名称!对于有子类的选项,请详细至子分类!
+time_limit_rate:
+ py.py3: 2
+memory_limit_rate:
+ java: 1.5
+
可以在 此题库 中找到各种类型题目的配置示例。
安装 Elasticsearch 后安装 @hydrooj/elastic-search
插件。
进入 HydroOJ 控制面板,在系统设置内正确填写endpoint。
然后在脚本管理中找到重建题目索引,点击运行,参数留空即可。
至此,搜索功能应当可以正常使用。
在题库右侧“创建题目”一栏中选择“从 FPS 文件导入”。
在打开的窗口中,您可以上传:
由于防止解析 fps 文件消耗大量内存,将拒绝导入超过 64MiB 的文件。
xml 文件需要为 UTF8 编码,否则可能出现中文题面乱码;
若您的文件超过大小限制,可考虑先在本地使用 Easy-Fps-Viewer 等工具进行拆分。
插件需要 MaxMind 的 Geolite2-City.mmdb
支持,在安装插件后需要将mmdb拷贝至插件同一目录下方可启用,如果你不知道这是什么,请勿安装。
Tips
您可以通过一键安装脚本快速安装独立评测机,详情请前往 部署Hydro 查看。
在配置评测机之前,请确认您的站点已经可以访问并正常登录/注册。
您应该预先下载您所要支持的语言的编译器,若您在配置完评测机后 升级/重新安装 了编译器,您需要重新启动沙箱。
关于编译器说明,请参照 编译器 章节。
如果不使用自动脚本,您需要按照如下方式手动安装沙箱服务:
前往 criyle/go-judge 下载 executorserver。 Executorserver 需要在后台以 root 权限运行并监听 127.0.0.1:5050
。 可使用 pm2 进行管理。
Tips
由于用附加组件安装的评测机与 Hydro 必须在同一台服务器上,每一个 Hydro 实例最多只能有一台评测机由附加组件安装。
在安装 Hydro 的机器上运行下面的指令安装 @hydrooj/hydrojudge
:
yarn global add @hydrooj/hydrojudge
+hydrooj addon add @hydrooj/hydrojudge
+
重启 Hydro 后 hydrojudge 即可正常运行。
Tips
该方法可以帮助您在任意服务器上部署评测机。
首先需要创建一个有 PRIV_JUDGE 权限的账户,具体方法参照 此处。(在部署 Hydro 的服务器上运行)
然后在运行评测机的服务器上安装 HydroJudge :
. <(curl https://hydro.ac/setup.sh) --judge
+
创建目录 ~/.config/hydro
,在该目录下创建文件 judge.yaml
,配置文件格式如下:
hosts:
+ localhost:
+ type: hydro # vj4 用户请在此处填写 vj4
+ server_url: http://localhost/ # Hydro 运行的网址
+ uname: judge # 评测账号用户名
+ password: abc123 # 评测账号密码
+ detail: true # 默认为 true
+
设置完之后,使用下面的指令即可开始运行(可以使用 pm2 管理):
hydrojudge
+
HydroJudge 会发布不定期更新。您可以使用 yarn global upgrade-interactive --latest
来检测并进行更新。
在 系统设置>hydrojudge 中有一栏 Disable builtin judge,将它勾上,然后重启 Hydro 即可。
根据开启的方法关闭即可。
关闭后运行下面指令即可。
yarn global remove @hydrooj/hydrojudge
+hydrooj addon remove @hydrooj/hydrojudge
+
在 系统设置>hydrojudge 修改对应的参数,然后重启 Hydro 和 hydrojudge 即可。
如果有需要修改单题测试点数量上限等设置,可以在 ~/.config/hydro/judge.yaml
的末尾添加下列内容:
testcases_max: 100 # 单题最多测试点数量
+total_time_limit: 120 # 单题最大总测试时长
+parallelism: 2 # 单评测机评测进程数量
+# 更多可选配置均可添加在此处,格式与前面的三排类似
+
在 此处 的设置均可添加到此处。
对于已安装内置评测机的用户(无论内置评测机是否启动),在 控制面板>系统设置 中修改 judge.langs 配置项即可;对于没有安装内置评测机的用户,需要在 ~/.config/hydro/langs.yaml
中配置。
文件格式参照 此处 。
如果您添加了新的语言,您还需要前往 控制面板>系统设置 中修改 Language Highlight ID 与 Monaco language modes。
分别表示选择对应的语言后的高亮设置(基于 PrismJS)和 Monaco 编辑器语法规则设置。
修改完后请重启 Hydro 和 hydrojudge 。
参照 测试数据格式 配置。
Note
如果不调整沙箱的空间大小,当您的评测使用文件 IO 且输入输出文件较大时可能会引发错误。
对于 2022/8/12 前安装的用户:
在服务器上运行下面的代码找到 hydro-sandbox 的运行目录:
pm2 info hydro-sandbox | grep "exec cwd"
+
将 mount.yaml 下载并放置在 sandbox 的运行目录下。然后修改第 64 行和第 68 行的 size
和 nr_inodes
的大小至您想要的大小,保存后重启 sandbox 即可完成更改。
对于 2022/8/12 后安装的用户:
编辑 /root/.hydro/mount.yaml
,修改 size 即可。
-fdiagnostics-color=always
例:
c:
+ compile: /usr/bin/gcc -O2 -Wall -std=c99 -o ${name} foo.c -lm -fdiagnostics-color=always
+
2022/8/12 后安装的实例默认已开启无限栈空间,无需手动操作
在很多时候系统默认为程序提供的栈空间并不能满足我们的需求,此时我们需要手动为用户程序提供更大的栈空间。
修改 pm2 中 hydro-sandbox 的启动参数为 ulimit -s unlimited && /usr/bin/hydro-sandbox
:
pm2 del hydro-sandbox
+pm2 start bash --name hydro-sandbox -- -c "ulimit -s unlimited && hydro-sandbox"
+
禁用 CPU 频率缩放与 Intel 睿频加速技术,防止 CPU 频率波动。
禁用内存地址空间随机化,以使得存在内存寻址错误的代码能够得到更多可重复的结果,可以通过在 /etc/sysctl.conf
中添加下面这行并运行 sudo sysctl -p
应用:
kernel.randomize_va_space = 0
+
部分 Linux 设备默认使用 cgroup2,而 cgroup2 中移除了精确计量内存消耗的接口。 若要获得更精确的内存计量,推荐启用 cgroup v1 (您可以通过检查 /sys/fs/cgroup/memory/memory.memsw.usage_in_bytes
是否存在来验证是否当前系统是否启用了 cgroup v1 ):
以 Ubuntu 的默认引导器 GRUB 2 为例,编辑 /etc/default/grub
: 在其中
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
+
后,加入 cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0 syscall.x32=y
,变为:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0 syscall.x32=y"
+
运行以下命令更新 GRUB 2 的配置,然后重新启动。
update-grub && reboot
+
Hydro 支持使用插件扩展自身所支持的功能。
Note
插件对站点的所有内容具有完全的访问权限,请不要启用来历不明的插件。
Tips
部分斜体字插件需配合额外支持软件,如您只安装插件是无法使用的,详情请前往左侧插件详情查看。
如您为旧版本升级失去博客功能,请直接安装 @hydrooj/blog
,原数据不会丢失。
Hydro 官方目前提供了以下附加组件:
ID | 描述 |
---|---|
@hydrooj/blog | 博客功能 |
@hydrooj/fps-importer | 导入 fps 格式的题目 |
@hydrooj/geoip | 显示用户登录地(需要IP库支持) |
@hydrooj/hydrojudge | 评测组件 |
@hydrooj/import-qduoj | 导入 QDUOJ 导出的题库 |
@hydrooj/login-with-github | 允许用户使用 GitHub 登录 |
@hydrooj/login-with-google | 允许用户使用 Google 登录 |
@hydrooj/migrate | 从 vijos4/HustOJ/SYZOJ/UniversalOJ 升级 |
@hydrooj/recaptcha | 注册时启用 reCAPTCHA 验证码 |
@hydrooj/ui-default | Hydro 的默认用户界面 |
@hydrooj/onlyoffice | 显示 doc/docx 格式题目 |
@hydrooj/sonic | 基于 sonic 的题目搜索增强 |
@hydrooj/elastic-search | 基于 Elastic 的题目搜索增强 |
@hydrooj/vjudge | Codeforces/SPOJ/UOJ/POJ/Luogu |
@hydrooj/prom-client | 导出系统状态至 Prometheus |
大部分插件的配置均可在安装后于 控制面板>系统设置 中找到。
部分插件若安装后没有正确配置可能会影响系统正常使用!
先全局安装所需模块,再向 hydrooj 注册即可。例:安装 @hydrooj/geoip
yarn global add @hydrooj/geoip
+hydrooj addon add @hydrooj/geoip
+
或者,如果你正在安装一个其他途径获取的插件(自行创建等),请直接使用文件夹 绝对路径: (文件夹路径即为 包含 package.json 的文件夹)
hydrooj addon add /root/xxx
+
请不要将插件与插件嵌套摆放,否则可能会导致一些不可预知的问题。
安装完插件后需要重启 hydrooj 以使插件生效。
hydrooj addon list
+
yarn global upgrade-interactive --latest
+
yarn global remove @hydrooj/geoip
+hydrooj addon remove @hydrooj/geoip
+
Note
迁移会删除当前 Hydro 的所有数据(含用户账户信息)并移入 HUSTOJ 的数据。
请注意备份相关文件。
请先完成 Hydro 的部署并完成对文件服务的配置(setting_file)。 在迁移数据之前,请先停止正在运行的 HUSTOJ 服务,仅保留其数据库开启。 请注意 Hydro 所在的数据库不应和源 HUSTOJ 数据库相同。
安装插件后,您应当能够在 控制面板>脚本管理 中找到名为 migrateHustoj
的脚本。 其参数格式如下:
{"host":"localhost","port":3306,"name":"jol","username":"","password":"","domainId":"system","contestType":"","dataDir":"","uploadDir":""}
+
jol
system
oi
或者 acm
,视情况而定/home/judge/src/web/upload/
,如果此路径与您路径相同,请不要填写此项)当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 HUSTOJ 的管理员账号登录实例。
SYZOJ 与 HUSTOJ 迁移方法类似,迁移脚本应运行名为 migrateSyzoj
的脚本外,SYZOJ无需 contestType
参数。 其参数格式如下:
{"host":"localhost","port":3306,"name":"syzoj","username":"","password":"","domainId":"system","dataDir":""}
+
syzoj
system
由于SYZOJ脚本会将原站所有数据迁移,所以运行耗时较长。 当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 SYZOJ 的管理员账号登录实例。
UniversalOJ (常称作UOJ社区版)与前两者迁移方法类似,迁移脚本应运行名为 migrateUniversalOJ
的脚本。
由于其升级过程较为麻烦,安装脚本已提供自动升级服务,如您需要可运行安装脚本一键迁移,手动迁移请在开发群中提问。
由于UniversalOJ脚本会将原站所有数据迁移,所以运行耗时较长。 当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 UniversalOJ 的管理员账号登录实例。
Note
迁移会删除当前 Hydro 的所有数据(含用户账户信息)并移入 vj4 的数据。
请注意备份相关文件。
请先完成 Hydro 的部署并完成对文件服务的配置(setting_file)。
在迁移数据之前,请先停止正在运行的 vj4 服务,仅保留其数据库开启。
请注意 Hydro 所在的数据库不应和源 vj4 数据库相同。
若您使用 vj4-docker ,可更改 docker-compose.yml
将数据库映射至其他本机端口。
安装插件后,您应当能够在 控制面板>脚本管理 中找到名为 migrateVijos
的脚本。
其参数格式如下:
{"host":"localhost","port":27017,"name":"vj4","username":"","password":""}
+
Tips
vj4-docker 中数据库名为 vj4,无账号密码。
当脚本运行完成后,请重启 Hydro 实例,会自动完成之后的升级操作。 迁移后,请使用原 vj4 的管理员账号登录实例。
Note
若您的 vj4 是由 vj2 或 tyvj 升级而来,在迁移完成后请不要卸载该插件,否则可能导致部分用户无法登录。
Tips
我们采用 reCAPTCHA v3 来检验注册者是否是人类,在注册过程中没有看见传统验证码属正常现象。
前往 https://www.google.com/recaptcha/admin/create 创建 reCAPTCHA 密钥。
reCAPTCHA 类型请选择“reCAPTCHA 第 3 版”。
创建成功后将客户端密钥和服务端密钥分别填入系统设置 recaptcha 栏下的 key
和 secret
中,重启 Hydro 后 reCAPTCHA 即可正常工作。
使用 root 用户执行如下命令:
nix-env -iA nixpkgs.sonic-server
+
使用 root 用户执行如下命令:
yarn global add @hydrooj/sonic
+hydrooj addon add @hydrooj/sonic
+
在 /root/.sonic/config.cfg
(没有的自行建立,也可以换成其他的你喜欢的路径)按照以下配置示例写入配置。
配置示例:
# Sonic
+# Fast, lightweight and schema-less search backend
+# Configuration file
+# Example: https://github.com/valeriansaliou/sonic/blob/master/config.cfg
+
+
+[server]
+
+log_level = "error"
+
+
+[channel]
+
+inet = "127.0.0.1:1491" # 默认监听本机
+tcp_timeout = 300
+
+auth_password = "SecretPassword"
+
+[channel.search]
+
+query_limit_default = 10
+query_limit_maximum = 100
+query_alternates_try = 4
+
+suggest_limit_default = 5
+suggest_limit_maximum = 20
+
+
+[store]
+
+[store.kv]
+
+path = "/data/sonic/store/kv/"
+
+retain_word_objects = 1000
+
+[store.kv.pool]
+
+inactive_after = 1800
+
+[store.kv.database]
+
+flush_after = 900
+
+compress = true
+parallelism = 2
+max_files = 100
+max_compactions = 1
+max_flushes = 1
+write_buffer = 16384
+write_ahead_log = true
+
+[store.fst]
+
+path = "/data/sonic/store/fst/"
+
+[store.fst.pool]
+
+inactive_after = 300
+
+[store.fst.graph]
+
+consolidate_after = 180
+
+max_size = 2048
+max_words = 250000
+
执行如下命令:
pm2 start sonic -- -c /root/.sonic/config.cfg
+pm2 save
+
进入 HydroOJ 控制面板,配置 sonic 后端地址。
如果您直接复制配置示例,那么按照以下内容配置:
127.0.0.1
1491
SecretPassword
修改完成后,重启 HydroOJ。
执行命令 pm2 restart hydrooj
。
进入 HydroOJ 控制面板,在脚本管理中找到重建题目索引,点击运行,参数留空即可。
至此,搜索功能应当可以正常使用。
请更新到 HydroOJ 最新版本后,再运行重建题目索引。
Note
此文档已过时,仅作留存使用,请前往 FAQS 查看使用指南。
由于 vjudge 更新了反爬虫机制,Codeforces RemoteJudge 需要一些特殊手段才能正常工作。
详情请 阅读源码
安装插件后创建名为 codeforces 的域,进入数据库 db.domain.updateOne({_id:'codeforces'},{$set:{mount:'codeforces'}});
在 codeforces 的域设置中,将 allowedLangs
如下配置(在新版即在允许提交的语言中选中所有 codeforces
开头的语言):
codeforces,codeforces.43,codeforces.52,codeforces.50,codeforces.54,codeforces.59,codeforces.61,codeforces.65,codeforces.9,codeforces.28,codeforces.32,codeforces.12,codeforces.60,codeforces.36,codeforces.48,codeforces.19,codeforces.3,codeforces.4,codeforces.51,codeforces.13,codeforces.6,codeforces.7,codeforces.31,codeforces.40,codeforces.41,codeforces.67,codeforces.49,codeforces.20,codeforces.34,codeforces.55
+
在 vjudge 表中插入如下条目:
{type:'codeforces', handle:'<codeforces login handle>', password:'<codeforces login password>'}
+
将如下配置添加至 langs 设置末尾:
codeforces:
+ execute: none
+ display: Codeforces
+ domain:
+ - codeforces # Allow domain 'codeforces' to use these languages
+codeforces.43:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU GCC C11 5.1.0
+ comment: //
+codeforces.52:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: Clang++17 Diagnostics
+ comment: //
+codeforces.50:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU G++14 6.4.0
+ comment: //
+codeforces.54:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU G++17 7.3.0
+ comment: //
+codeforces.59:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: Microsoft Visual C++ 2017
+ comment: //
+codeforces.61:
+ highlight: cpp astyle-c
+ monaco: cpp
+ display: GNU G++17 9.2.0 (64 bit, msys 2)
+ comment: //
+codeforces.65:
+ highlight: cpp astyle-cs
+ monaco: csharp
+ display: C# 8, .NET Core 3.1
+ comment: //
+codeforces.9:
+ highlight: cpp astyle-cs
+ monaco: csharp
+ display: C# Mono 6.8
+ comment: //
+codeforces.28:
+ highlight: d
+ monaco: plain
+ display: D DMD32 v2.091.0
+ comment: //
+codeforces.32:
+ highlight: go
+ display: Go 1.15.6
+ comment: //
+codeforces.12:
+ highlight: haskell
+ display: Haskell GHC 8.10.1
+ comment: --
+codeforces.60:
+ highlight: java astyle-java
+ monaco: java
+ display: Java 11.0.6
+ comment: //
+codeforces.36:
+ highlight: java astyle-java
+ monaco: java
+ display: Java 1.8.0_241
+ comment: //
+codeforces.48:
+ highlight: kotlin
+ display: Kotlin 1.4.0
+ comment: //
+codeforces.19:
+ highlight: ocaml
+ monaco: plain
+ display: OCaml 4.02.1
+ comment: ['(*','*)']
+codeforces.3:
+ highlight: pascal
+ display: Delphi 7
+ comment: //
+codeforces.4:
+ highlight: pascal
+ display: Free Pascal 3.0.2
+ comment: //
+codeforces.51:
+ highlight: pascal
+ display: PascalABC.NET 3.4.2
+ comment: //
+codeforces.13:
+ highlight: perl
+ display: Perl 5.20.1
+ comment: '#'
+codeforces.6:
+ highlight: php
+ display: PHP 7.2.13
+ comment: //
+codeforces.7:
+ highlight: python
+ display: Python 2.7.18
+ comment: '#'
+codeforces.31:
+ highlight: python
+ display: Python 3.9.1
+ comment: '#'
+codeforces.40:
+ highlight: python
+ display: PyPy 2.7 (7.3.0)
+ comment: '#'
+codeforces.41:
+ highlight: python
+ display: PyPy 3.7 (7.3.0)
+ comment: '#'
+codeforces.67:
+ highlight: ruby
+ display: Ruby 3.0.0
+ comment: '#'
+codeforces.49:
+ highlight: rust
+ display: Rust 1.49.0
+ comment: //
+codeforces.20:
+ highlight: scala
+ display: Scala 2.12.8
+ comment: //
+codeforces.34:
+ highlight: javascript
+ display: JavaScript V8 4.8.0
+ comment: //
+codeforces.55:
+ highlight: javascript
+ display: Node.js 12.6.3
+ comment: //
+
之后再重启 Hydro 即可。
+ |
+ Priority | +Change Frequency | +Last Updated Time | +
---|---|---|---|
+ |
+
+ |
+
+ |
+
+ |
+