diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..d3a8d3b9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,86 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## プロジェクト概要 + +マルチ言語・マルチAIによる競技プログラミング学習リポジトリ。各問題に対して**2社 × Nモデル × 3言語 × 3ドキュメント階層**で成果物を生成する。 + +## 開発コマンド + +```bash +# セットアップ +make setup # venv + Jupyterカーネル設定 +make install # pip + bun install + +# テスト +make test # pytest + vitest 両方実行 +bunx vitest run # JS/TSテストのみ +bunx vitest run path/to/file.test.ts # 単一テスト + +# リント・フォーマット +make lint # ruff + black + prettier + eslint +make fmt # ruff --fix + black + prettier +bunx prettier -c . # prettier チェックのみ +bunx prettier -w . # prettier 修正 + +# 実行 +bunx tsx path/to/file.ts # TypeScript実行 +make lab # JupyterLab起動 +``` + +パッケージマネージャは**Bun**。`npm`ではなく`bun install`を使用。 + +## アーキテクチャ + +### 6階層ディレクトリ構造 + +``` +{Domain}/{Subcategory}/{Platform}/{Problem}/{AIProvider}/{Artifact} +``` + +- **Domain**: `Algorithm/`, `DataStructures/`, `Mathematics/`, `SQL/`, `Shell/`, `Concurrency/` +- **Platform**: `leetcode/`, `hackerrank/`, `atcoder/`, `codeforces/` +- **AIProvider**: `Claude Sonnet 4.5/`, `Claude Code Sonnet 4.6 extended/`, `gpt-4o/` など +- **Artifact**: `*.py`, `*.ts`, `*.js`, `README.md`, `README.html`, `README_react.html` + +**例外**: `JavaScript/` ディレクトリは LeetCode 30-Day JS Challenge 専用で、上記6階層に従わない。`SQL/` ドメインはAIプロバイダーが`gpt/`単一フォルダで`.ipynb`形式。 + +### デュアルAI実装哲学 + +- **Claude実装**: 競技最適化、型アノテーション信頼、単一メソッド、50-150 LOC +- **GPT実装**: 本番堅牢性、ランタイム検証、複数メソッド、80-200 LOC + +### 3階層ドキュメントシステム + +| ファイル | スタック | 用途 | +| ------------------- | ------------------------------- | ------------------------------------------------------------------------------------ | +| `README.md` | 純粋Markdown | 5セクション構造(Overview / Algorithm / Complexity / Implementation / Optimization) | +| `README.html` | Prism.js + Tailwind CSS | ステップコントロールUI、SVGフローチャート | +| `README_react.html` | React 18 UMD + Babel Standalone | リアルタイム入力操作、AI比較 | + +### コード構造パターン + +**Python** (Claude): `class Solution: def methodName(self, ...) -> ReturnType:` +**TypeScript**: `function functionName(...): ReturnType { ... }` +**JavaScript**: `var functionName = function(...) { ... }; module.exports = { functionName };` + +## 依存関係ポリシー + +- **Algorithm/DataStructures/Mathematics**: 標準ライブラリのみ(`typing`, `collections`, `itertools`, `math`, `heapq`)。外部ライブラリ禁止 +- **JS/TS実装**: ビルトインのみ。lodash等の外部ライブラリ禁止 +- **SQLドメインのみ**: Pandas/NumPy許可(`.ipynb`形式) + +## コードスタイル + +- **TypeScript**: `strict: true`, `noImplicitAny: true`, target ES2022 +- **Prettier**: semi, singleQuote, tabWidth: 4, printWidth: 100 +- **Python**: ruff + black + +## SVGフローチャートガイドライン + +`.agent/workflows/svg_flowchart_guidelines.md` に詳細あり。主要ポイント: + +- `refX` はarrowhead長未満に設定(arrowheadがノードに隠れる問題を防止) +- viewBoxに30-50pxのpadding追加 +- Prism.jsコピーボタンはTailwindのpreflightで消えるため `!important` オーバーライドが必要 diff --git a/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/FunctionComposition_TS.ipynb b/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/FunctionComposition_TS.ipynb new file mode 100644 index 00000000..6ed100f5 --- /dev/null +++ b/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/FunctionComposition_TS.ipynb @@ -0,0 +1,117 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a7571108", + "metadata": {}, + "source": [ + "## 1. 問題の分析\n", + "\n", + "**競技プログラミング視点**\n", + "- 配列を右から左へ1パスするだけで解決可能。`reduceRight` が最適。\n", + "- 追加メモリ不要(クロージャで `x` を畳み込む)。\n", + "\n", + "**業務開発視点**\n", + "- 空配列 → 恒等関数という仕様を型安全に表現できる。\n", + "- `readonly` 修飾子で入力配列の不変性を保証。\n", + "\n", + "**TypeScript特有の考慮点**\n", + "- `F = (x: number) => number` という型エイリアスがすでに与えられているため、ジェネリクス不要。\n", + "- `reduceRight` の型推論が自然に効く。\n", + "\n", + "---\n", + "\n", + "## 2. アルゴリズムアプローチ比較\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |\n", + "|---|---|---|---|---|---|---|\n", + "| `reduceRight` | O(n) | O(1) | 低 | 高 | 高 | 最もシンプル |\n", + "| `for` ループ(右→左) | O(n) | O(1) | 低 | 高 | 中 | 命令的 |\n", + "| 再帰 | O(n) | O(n) | 中 | 高 | 中 | スタックオーバーフローリスク |\n", + "\n", + "---\n", + "\n", + "## 3. 選択したアルゴリズムと理由\n", + "\n", + "- **選択**: `reduceRight`\n", + "- **理由**:\n", + " - 数学的な「右から左への関数合成」を宣言的に表現でき、可読性・意図の明確さが最高。\n", + " - O(n) / O(1) で計算量も最適。\n", + " - TypeScriptの型推論が自然に効き、型注釈の追加が不要。\n", + "\n", + "---\n", + "\n", + "## 4. 実装コード" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "implementation", + "metadata": {}, + "outputs": [], + "source": [ + "// Analyze Complexity\n", + "// Runtime 56 ms\n", + "// Beats 55.84%\n", + "// Memory 56.88 MB\n", + "// Beats 46.50%\n", + "type F = (x: number) => number;\n", + "\n", + "/**\n", + " * 関数配列の右から左への合成を返す\n", + " * @param functions - 合成する関数の配列\n", + " * @returns 合成された関数。空配列の場合は恒等関数\n", + " * @complexity Time: O(n), Space: O(1)\n", + " */\n", + "function compose(functions: readonly F[]): F {\n", + " return function (x: number): number {\n", + " return functions.reduceRight(\n", + " (acc: number, fn: F): number => fn(acc),\n", + " x\n", + " );\n", + " };\n", + "}\n", + "\n", + "// 動作確認\n", + "const fn1 = compose([x => x + 1, x => x * x, x => 2 * x]);\n", + "console.log(\"Example 1 (x=4): 2*4=8 -> 8*8=64 -> 64+1=65 :\", fn1(4));\n", + "\n", + "const fn2 = compose([x => 10 * x, x => 10 * x, x => 10 * x]);\n", + "console.log(\"Example 2 (x=1): 10 -> 100 -> 1000 :\", fn2(1));\n", + "\n", + "const fn3 = compose([]);\n", + "console.log(\"Example 3 (x=42): 42 :\", fn3(42));\n", + "\n", + "// Interactive checks\n", + "compose([x => x + 1, x => 2 * x])(4)" + ] + }, + { + "cell_type": "markdown", + "id": "points", + "metadata": {}, + "source": [ + "**ポイント:**\n", + "- `functions` を `readonly F[]` とすることで入力配列の不変性を型レベルで保証。\n", + "- `reduceRight` の初期値 `x` が空配列時の恒等関数の役割を自然に担うため、空配列の特別処理が不要。\n", + "- コールバック内の引数 `acc`・`fn` に型注釈を付与し、strict mode 下でも型推論が確実に機能。" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "TypeScript", + "language": "typescript", + "name": "typescript" + }, + "language_info": { + "file_extension": ".ts", + "mimetype": "text/typescript", + "name": "typescript", + "version": "5.9.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/README.md b/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/README.md new file mode 100644 index 00000000..6d6a240e --- /dev/null +++ b/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/README.md @@ -0,0 +1,201 @@ +# Function Composition - Right-to-Left Pipeline via reduceRight + +## 目次(Table of Contents) + +- [概要](#overview) +- [アルゴリズム要点(TL;DR)](#tldr) +- [図解](#figures) +- [正しさのスケッチ](#correctness) +- [計算量](#complexity) +- [TypeScript 実装](#impl) +- [TypeScript / V8 最適化ポイント](#typescript-v8) +- [エッジケースと検証観点](#edgecases) +- [FAQ](#faq) + +--- + +

概要

+ +**LeetCode 2629 – Function Composition** + +関数の配列 `[f1, f2, ..., fn]` を受け取り、**右から左** へ順に適用する合成関数 `fn` を返す。 + +``` +compose([f, g, h])(x) = f(g(h(x))) +``` + +| 要件 | 内容 | +| ------ | ------------------------------------------------- | +| 正当性 | 空配列のとき恒等関数 `x => x` を返す | +| 安定性 | 各関数は副作用なし・純粋関数を前提 | +| 制約 | `0 ≤ functions.length ≤ 1000`, `-1000 ≤ x ≤ 1000` | + +--- + +

アルゴリズム要点(TL;DR)

+ +- **戦略**: `Array.prototype.reduceRight` で配列を右端から畳み込む。 +- **データ構造**: 追加データ構造なし。クロージャが `functions` 参照を保持するのみ。 +- **時間計算量**: 合成関数呼び出し時 O(n)(n = 関数配列の長さ) +- **空間計算量**: O(1)(クロージャのポインタ1本のみ) +- **空配列**: `reduceRight` の初期値 `x` がそのまま返るため、特別処理不要。 +- **型安全性**: `type F = (x: number) => number` で入出力を厳密に束縛。 + +--- + +

図解

+ +### フローチャート + +```mermaid +flowchart TD + Start[compose called] --> RetFn[Return closure fn x] + RetFn --> Call[fn x is called] + Call --> RR[Start reduceRight with initial value x] + RR --> Step{More functions?} + Step -- Yes --> Apply[Apply current fn to acc] + Apply --> Step + Step -- No --> Out[Return final acc] +``` + +> `compose` は呼ばれた時点でクロージャを返すだけ(O(1))。 +> 実際の計算は返された関数 `fn(x)` が呼ばれたときに行われる(O(n))。 + +--- + +### データフロー図 + +```mermaid +graph LR + subgraph Input + A[functions array] --> B[compose] + X[x value] --> C[returned fn] + end + subgraph Execution + B --> C + C --> D[reduceRight] + D --> E[h x] + E --> F[g h x] + F --> G[f g h x] + end + G --> H[Output number] +``` + +> 配列の末尾から先頭に向かって関数が順に適用される。各ステップの出力が次のステップの入力となる。 + +--- + +

正しさのスケッチ

+ +**不変条件** +`reduceRight` の各ステップで、`acc` は「現在のインデックスより右側の全関数を適用した結果」を保持する。 + +**基底条件** + +- 空配列:`reduceRight` はコールバックを一度も呼ばず、初期値 `x` を返す → 恒等関数として正しい。 +- 要素1個:`reduceRight` は1回だけコールバックを呼び、`fn(x)` を返す。 + +**網羅性** + +- 全関数は `F = (x: number) => number` 型を満たすため、型レベルで入出力の整合性が保証される。 +- `acc` の型は常に `number` であり、コンパイル時に検証される。 + +**終了性** + +- `reduceRight` は有限配列を走査するため必ず終了する。 + +--- + +

計算量

+ +| フェーズ | 時間計算量 | 空間計算量 | 備考 | +| ---------------------- | ---------- | ---------- | ------------------ | +| `compose` 呼び出し | O(1) | O(1) | クロージャ生成のみ | +| 返された関数の呼び出し | O(n) | O(1) | n = 関数配列の長さ | + +> **in-place vs Pure**: +> 本実装は Pure(副作用なし)。`functions` 配列を変更せず、クロージャは参照のみ保持する。 + +--- + +

TypeScript 実装

+ +```typescript +// LeetCode 2629 - Function Composition +// TypeScript strict mode 対応 + +type F = (x: number) => number; + +/** + * 関数配列の右から左への合成を返す。 + * 空配列の場合は恒等関数を返す。 + * + * @param functions - 合成する関数の配列(右端から順に適用) + * @returns 合成された関数 + * @complexity Time: O(n) per call, Space: O(1) + */ +function compose(functions: readonly F[]): F { + // reduceRight の初期値 x が空配列時の恒等関数を自然に実現する + return function (x: number): number { + return functions.reduceRight((acc: number, fn: F): number => fn(acc), x); + }; +} + +/** + * const fn = compose([x => x + 1, x => 2 * x]); + * fn(4); // 9 (2*4=8, 8+1=9) + */ +``` + +**主要ポイント** + +- `readonly F[]` で入力配列の不変性を型レベルで保証。 +- コールバック引数 `acc: number`, `fn: F` に明示的型注釈を付与し、`strict` モード下での推論を確実化。 +- 空配列の特別分岐が不要 → 分岐ゼロ・コードが簡潔。 + +--- + +

TypeScript / V8 最適化ポイント

+ +| 観点 | 内容 | +| --------------------------- | ----------------------------------------------------------------------------------------- | +| **クロージャコスト** | `compose` 呼び出しごとにクロージャを1つ生成するのみ。追加オブジェクトなし。 | +| **`reduceRight` vs ループ** | V8 の組み込みメソッドは JIT 最適化されやすい。可読性も高く推奨。 | +| **配列参照の共有** | クロージャは `functions` の参照を保持するのみ(コピーなし)。大配列でもメモリ効率が良い。 | +| **型注釈の効果** | コンパイル時に型エラーを除去することで、実行時の型チェック分岐が不要になる。 | +| **`readonly` 修飾子** | TypeScript コンパイラが配列の書き換えをコンパイル時にブロック。実行時オーバーヘッドなし。 | + +> `for` ループで書き換えても計算量は変わらないが、`reduceRight` の方が数学的意図が明確で保守性が高い。 + +--- + +

エッジケースと検証観点

+ +| ケース | 入力例 | 期待出力 | 対応 | +| ---------------- | ------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------ | +| 空配列 | `functions = [], x = 42` | `42` | `reduceRight` の初期値がそのまま返る | +| 関数1つ | `functions = [x => x * 2], x = 5` | `10` | コールバック1回だけ実行 | +| 全関数が恒等関数 | `functions = [x => x, x => x], x = 7` | `7` | 変換なし | +| 境界値 x = -1000 | `functions = [x => x + 1], x = -1000` | `-999` | 制約内で正常動作 | +| 境界値 x = 1000 | `functions = [x => x - 1], x = 1000` | `999` | 制約内で正常動作 | +| 関数1000個 | 全て `x => x + 1`, `x = 0` | `1000` | O(n) で完走 | +| 非可換な合成順序 | `[x => x + 1, x => x * x], x = 3` | `10` (3²=9, 9+1=10) vs `[x => x * x, x => x + 1]` → `16` (3+1=4, 4²=16) | 右から左の順が正しく守られる | + +--- + +

FAQ

+ +**Q. なぜ `reduceRight` を使うのか? `reduce` ではだめなのか?** +A. 数学の合成関数 `f∘g∘h` は右から左に評価される(`h` を先に適用)。`reduceRight` はその順序を自然に表現できる。`reduce` を使うと左から右の適用になり、問題の仕様と逆になる。 + +**Q. 空配列の場合、なぜ特別分岐が不要なのか?** +A. `reduceRight(callback, initialValue)` は配列が空のとき、`callback` を一度も呼ばずに `initialValue` を返す。これが恒等関数 `x => x` の動作と一致する。 + +**Q. `functions` 配列の要素を変更するとどうなるか?** +A. クロージャは参照を保持するため、外部から配列の要素を書き換えると合成結果も変わる。`readonly F[]` はコンパイル時にこの書き換えをブロックするが、実行時の完全な防御が必要な場合は `Object.freeze(functions)` を追加する。 + +**Q. ループ実装と `reduceRight` 実装でパフォーマンス差はあるか?** +A. 制約(`functions.length ≤ 1000`)の範囲では計測上の差はほぼない。可読性・意図の明確さから `reduceRight` を推奨する。 + +**Q. TypeScript の `strict` モードで問題なく動作するか?** +A. はい。`reduceRight` のコールバック引数に明示的型注釈 `(acc: number, fn: F): number` を付与しているため、`strict` / `noImplicitAny` 下でもコンパイルエラーは発生しない。 diff --git a/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/README_react.html b/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/README_react.html new file mode 100644 index 00000000..6ffd1e06 --- /dev/null +++ b/JavaScript/2629. Function Composition/Claude Code Sonnet 4.6 extended/README_react.html @@ -0,0 +1,1349 @@ + + + + + + LeetCode 2629 - Function Composition + + + + + + + + + + + + + + + +
+ + + + +
+

+ 概要 +

+
+
+

問題の要件

+

+ 関数の配列 + functions = [f1, f2, ..., fn] + を受け取り、 + 右から左へ順に適用する合成関数を返す。 +

+
+
// 合成の定義
+
compose([f, g, h])(x)
+
// = f(g(h(x)))
+
// 空配列 → 恒等関数
+
compose([])(x) === x
+
+

制約条件

+
    +
  • + + -1000 ≤ x ≤ 1000 +
  • +
  • + + 0 ≤ functions.length ≤ 1000 +
  • +
  • + 各関数は整数→整数の変換 +
  • +
+
+
+

入出力例

+
+
+
Example 1
+
+ functions = [x=>x+1, x=>x*x, x=>2*x] +
+
x = 4
+
+ // 2*4=8 → 8²=64 → 64+1=65 +
+
Output: 65
+
+
+
Example 2
+
+ functions = [x=>10*x, x=>10*x, x=>10*x] +
+
x = 1
+
+ // 10*1=10 → 10*10=100 → 10*100=1000 +
+
Output: 1000
+
+
+
Example 3
+
functions = []
+
x = 42
+
+ // 恒等関数 → そのまま返す +
+
Output: 42
+
+
+
+
🔑 核心アイデア
+
+ reduceRight の初期値 + x が、 + 空配列時に恒等関数として自然に機能する。特別な分岐が不要。 +
+
+
+
+
+ + +
+

+ ステップバイステップ可視化 +

+

+ 例: + functions = [x=>x+1, x=>x*x, x=>2*x], x = 4 +

+
+
+ + +
+

+ TypeScript 実装 +

+
type F = (x: number) => number;
+
+/**
+ * 関数配列の右から左への合成を返す。
+ * 空配列の場合は恒等関数を返す。
+ *
+ * @param functions - 合成する関数の配列(右端から順に適用)
+ * @returns 合成された関数
+ * @complexity Time: O(n) per call, Space: O(1)
+ */
+function compose(functions: readonly F[]): F {
+    // reduceRight の初期値 x が空配列時の恒等関数を自然に実現する
+    return function (x: number): number {
+        return functions.reduceRight(
+            (acc: number, fn: F): number => fn(acc),
+            x
+        );
+    };
+}
+
+/**
+ * const fn = compose([x => x + 1, x => x * x, x => 2 * x]);
+ * fn(4); // 65  (2*4=8 → 8²=64 → 64+1=65)
+ *
+ * const id = compose([]);
+ * id(42); // 42  (恒等関数)
+ */
+
+ + +
+

+ フローチャート +

+
+ + + + + + + + + + + + + + + + + + compose が呼ばれる + + + + + + + + + クロージャを生成して返す + + + + + + + + + 返された fn(x) が呼ばれる + + + + + + + + + functions + + + 空配列? + + + + + + はい + + + + + + x をそのまま返す + + + // 恒等関数 + + + + + + + + + いいえ + + + + + + reduceRight 開始 + + + acc = x(初期値) + + + + + + + + + + + + + まだ関数が + + + 残っている? + + + + + + はい + + + + + + acc = fn(acc) + + + + + + + 次の関数へ + + + + + + いいえ + + + + + + acc を返す + + + // f(g(h(x))) の結果 + + +
+
+

+ フロー解説:
+ 1. + compose + が呼ばれると即座にクロージャを返す(O(1))
+ 2. 返された関数 + fn(x) + が呼ばれたとき実際の計算が始まる(O(n))
+ 3. 空配列の場合は reduceRight の初期値 x + がそのまま返る → 恒等関数
+ 4. 要素がある場合は右端から fn を順に acc に適用し、最終 acc を返す +

+
+
+ + +
+

+ 計算量分析 +

+
+
+
O(n)
+
時間計算量
+
+ n = 関数配列の長さ。
compose呼び出しは O(1)、
返された関数の実行が + O(n)。 +
+
+
+
O(1)
+
空間計算量
+
+ クロージャが functions への参照を
1本保持するのみ。
配列のコピーなし。 +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ アプローチ + + 時間計算量 + + 空間計算量 + + 型安全性 + + 可読性 +
reduceRight ✓O(n)O(1)⭐⭐⭐⭐⭐⭐
for ループ(右→左)O(n)O(1)⭐⭐⭐⭐⭐
再帰O(n)O(n)⭐⭐⭐⭐
+
+
+
+ + + + diff --git a/calc_sri_fix.sh b/calc_sri_fix.sh index 0647e073..675e6908 100644 --- a/calc_sri_fix.sh +++ b/calc_sri_fix.sh @@ -24,8 +24,18 @@ calculate_sri() { local hash hash=$(openssl dgst -sha384 -binary < "$temp_file" | openssl base64 -A) echo "$url sha384-$hash" - rm -f "$temp_file" } -calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js" -calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js" \ No newline at end of file +calculate_sri "https://unpkg.com/react@18/umd/react.production.min.js" || true +calculate_sri "https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" || true +calculate_sri "https://unpkg.com/@babel/standalone/babel.min.js" || true +# calculate_sri "https://cdn.tailwindcss.com" || true # Skipped: dynamic CDN incompatible with SRI +calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" || true +calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css" || true +calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js" || true +calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-typescript.min.js" || true +calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js" || true +calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js" || true +# Google Fonts returns dynamic CSS, SRI might be unstable but we check just in case or skip if needed. User instruction implies to check. +calculate_sri "https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&family=Fraunces:wght@700;900&display=swap" || true +calculate_sri "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js" || true