OmniAICreator/Qiita-1.07M huggingface dataset:
paiza.ioでmysql その3

# 概要
paiza.ioでmysqlやってみた。
練習問題やってみた。

# 練習問題
fizzbuzzを実行せよ。

# サンプルコード

```
CREATE TABLE test
    SELECT @n := 1 num
    UNION
    SELECT @n := @n + 1
    FROM information_schema.COLUMNS
    LIMIT 100;
 
#SELECT * FROM test; 
    
SELECT
    case
        when num % 15 = 0 then 'FizzBuzz'
        when num % 3 = 0 then 'Fizz'
        when num % 5 = 0 then 'Buzz'
        else num
    end 
num FROM test;





```

# 実行結果

```
num
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
```

# 成果物

https://paiza.io/projects/4UMntv2WuGaJ8iynTvxpOg


以上。
G検定の受験メモ

## はじめに
最近、ディープラーニングについては会社でも日常的に使用するようになり、かなり身近な存在となってきました。
ディープラーニングを含めたAIの技術については進化の速度が速く、世界中で日々新しい技術が開発されているのが現状です。
身近になってきたこともあり、このままではエンジニアとして取り残されてしまう不安もあり、意を決してG検定について勉強してみようと思いました。4月ごろから勉強し始めて、9/7にG検定を受講しました。

## G検定とは
日本ディープラーニング協会（JDLA）が運営している資格試験。
以下のWebページによると、ディープラーニングの基礎知識を有し、事業活用する能力を検定する試験のことです。
https://www.jdla.org/certificate/general/

## 学習内容
1. G検定の公式テキスト第3版をひたすら読み、頭に入れる
2. G検定問題集第2版の問題を繰り返し解く（2週繰り返し）
3. アプリを使って問題をひたすら解く
  平均して300問ぐらいは連続正解できるようになった。
4. [Youtube](https://www.youtube.com/@IT_Engineer_Noi/videos)のG検定100問解くまで帰れません動画

## 試験の傾向
問題集に載っているような比較的簡単な問題も出題されますが、複数のモデルを比較したときのメリットなど細かい点について問われる問題も多く出題されます。ある程度詳しく出てくる用語の意味などを理解しておかないと、解くのは難しい印象でした。また、120分で200問の出題があるので、1問にそれほど時間をかけられないという点も難点なところ。

## 結果
合格しました。比較的今回は合格率は高かったようです。
エンジニアとしてはE資格を取得すべきなんでしょうけど、本業で今のところ、ディープラーニングは使わないので、少し様子見ですね。受験費用も高額ですし。。。
PHPStanのエラー箇所へ1クリックで飛べるようにする設定

# 初めに
PHPStanで解析をした際、エラーの箇所をいちいち手動で開くのってちょっと面倒ですよね。
今回は、エラーの箇所にすぐに飛べるようになる設定をご紹介します。

# エラーの箇所に飛びたい！
PHPStanでエラーを検出すると以下のような結果が出力されます。

```sh
$ ./vendor/bin/phpstan analyse
Note: Using configuration file C:\path\to\project\phpstan.neon.
 3/3 [============================] 100%

 ------ --------------------------------------------------------------------------
  Line   level\Level0.php
 ------ --------------------------------------------------------------------------
  :759     Method Util\Level0::knownMethod() invoked with 0 parameters, 1 required.
 ------ --------------------------------------------------------------------------
 
 [ERROR] Found 1 error
```

このエラーを確認するためには、**該当ファイルを開き、指定の行数にジャンプする**というちょっと面倒な方法をとらなければなりません:weary:

# `editorUrl`でエラー箇所へのリンクを出力できる
phpstan.neonファイルで`editorUrl`を設定すると、出力にエラー箇所へのリンクを含めることができます。

```neon: phpstan.neon
parameters:
    editorUrl: 'vscode://file/%%file%%:%%line%%'
    errorFormat: table
```

実行してみた結果がこちら。
分かりづらいですが、✏️ マークの横にある`level\Level0.php`がリンクになっています。
```sh
$ ./vendor/bin/phpstan analyse
Note: Using configuration file C:\path\to\project\phpstan.neon.
 3/3 [============================] 100%

 ------ --------------------------------------------------------------------------
  Line   level\Level0.php
 ------ --------------------------------------------------------------------------
  :759     Method Util\Level0::knownMethod() invoked with 0 parameters, 1 required.
         ✏️  level\Level0.php
 ------ --------------------------------------------------------------------------
 
 [ERROR] Found 1 error
```

CLI上でカーソルを合わせると、リンク先が表示されていますね。
以下はVScode上で表示をしてくれるリンクになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430841/1da9f73e-b177-dd5b-9411-e5e3940cd7d5.png)


# IDE別の設定
[PHPStanの公式ドキュメント](
https://phpstan.org/user-guide/output-format#opening-file-in-an-editor)を見ると、IDE別の設定方法が載っていました。

|エディター or IDE|設定値|
|--|--|
|PhpStorm| 'phpstorm://open?file=%%file%%&line=%%line%%'|
|Visual Studio Code| 'vscode://file/%%file%%:%%line%%'|
|Atom| 'atom://core/open/file?filename=%%file%%&line=%%line%%'|


# 終わりに
エラーが出るたびにファイルを開いて行数に飛ぶのって、手だとちょっと面倒ですよね。
これで楽になりそうです。

ここまで読んでいただきありがとうございました！
VSCodeの型ヒント表示を無効化する

# 課題
VSCodeバージョンアップで、関数の引数や返値の型ヒントが自動的に表示されるようになった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20427/a1e85986-ac6c-f390-09ae-aadaafc7da8a.png)

邪魔なことが多いので非表示にしたいが、設定方法の発見に意外と時間がかかったのでメモ。

# 解決
<b>変更する設定 : `Editor › Inlay Hints: Enabled`</b>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20427/0fa711c3-23dd-c784-2aa2-45fd30302bc4.png)

`off`にすると非表示にできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20427/7b701769-cba4-0899-6c7f-6613027254b4.png)

また、以下も必要に応じて設定可能。
- デフォルトで非表示・Ctrl+Altで表示
- デフォルトで表示・Ctrl+Altで非表示

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20427/20ec9bb8-43fe-2b43-a9f3-b1689cf6945c.png)

# 余談
Inlay (インレー)は、「はめ込む」「象嵌する」を表す単語。
象嵌（ぞうがん）は、土台を削って装飾をはめ込む工芸技法だそうな。

[Wikipedia: 象嵌](https://ja.wikipedia.org/wiki/%E8%B1%A1%E5%B5%8C)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20427/88cf3e9d-585d-3138-3d93-5e3a66ad2b55.png)
Windowsアクセス権を操作するとき用のメモ


icacls使うときのメモ。継承関係の設定をよく忘れるので。

```
rem フォルダにユーザー/グループ権限をつける(CI)とかつけるれる
icacls "folderpath" /grant "domain\usernama:(CI)(OI)(F)"

rem ファイルにユーザー/グループ権限をつける
icacls "filepath" /grant "domain\username:(F)"

rem 継承関係の設定
icacls "path" /inheritance:r
```

カレントディレクトリ直下の権限確認（サブツリー含めるのは今度調べたい。）
```
icacls *
```

使い方の詳細確認はコマンドのみ入力
```
icacls
```
Flutterで別アプリ起動する手順（カスタムURLスキームだけ）

## 想定している要件
- カスタムURLスキーム（CustomURLScheme）でインストール済みの別アプリへ遷移させたい
- Androidのアプリリンク(App Links)は使用しない
- iOSのユニバーサルリンク(Universal Links)は使用しない
- そのほかFirebaseDynamicLinksなども使用しない
- ユーザーは必ず遷移先のアプリをインストール済みである

## 遷移先のアプリの事前確認事項
### Android
- `<!-- 追加する部分 -->`が存在するか確認し、ない場合は追加、ある場合は`android:scheme`の値を確認する
- `android:exported="true"`になっているか確認する
    - 記述がない場合はfalse扱いとなるためtrueを明示的に記述する
- `yourcustomscheme`は衝突しないようにユニークな名前を付ける

```android/app/src/main/AndroidManifest.xml
<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTop"
    android:theme="@style/LaunchTheme"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">

    <!-- 追加する部分 -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="yourcustomscheme"
            android:host="yourhost" />
    </intent-filter>

</activity>
```
### iOS
- 下記の`CFBundleURLTypes`が存在するか確認し、ない場合は追加、ある場合は`CFBundleURLSchemes`の値を確認する
- `yourcustomscheme`は衝突しないようにユニークな名前を付ける
```info.plist
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string>com.yourcompany.yourapp</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>yourcustomscheme</string>
    </array>
  </dict>
</array>
```

## 遷移元のアプリの設定事項
### パッケージのインストール
- [url_launcher](https://pub.dev/packages/url_launcher)

```pubspeck.yml
dependencies:
  url_launcher: ^6.3.0
```
### Flutterのコード
```dart
import 'dart:io' show Platform;
import 'package:url_launcher/url_launcher.dart';

void _openYourApp() async {
    const androidUrl = 'yourcustomscheme://'; // YouTubeなら'youtube://'
    const iosUrl ='yourcustomscheme://'; //  YouTubeなら'vnd.youtube://'
    
    final targetUrlStr = Platform.isAndroid ? androidUrl : iosUrl;
    final targetUrl = Uri.parse(targetUrlStr);

    if (await canLaunchUrl(targetUrl)) { // インストール済みでAppを開けるか判定
        await launchUrl(url);
    } else {
      print('yourAppを開くことができませんでした。');
    }
  }
}
```
## 遷移先のスキーマが分からない場合
有名なものや、標準アプリに関しては調べればわかりますが（instagram,twitter等)、個人が開発したいアプリに遷移をさせたい場合は、開発者に問い合わせると早いかと思います。

## 参考リンク

https://pub.dev/packages/url_launcher
https://note.com/toridori_inc/n/nb7237a217a51
https://developer.android.com/privacy-and-security/risks/android-exported?hl=ja#overview
https://zenn.dev/quicksilversel/articles/7062da63e549fd
streamlitで現在ページの再トリガーを行う方法が変わっている

# 背景
Hugging Face Spacesでstreamlitのアプリをデプロイしてみたら、現在のページの再実行をトリガーする`st.experimental_rerun`でAttribute Errorのエラーが発生し、streamlitには`st.experimental_rerun`は存在しない旨が告げられてしまった。
しかしローカルでこのアプリの動作確認を行ってみると、普通に`st.experimental_rerun`の部分を問題なく通過する。
まあまず考えるのはローカルとHugging Face Spacesでのstreamlitのバージョンが合っていないのだろうなというところである。
しかし、Hugging Face Spacesでstreamlitアプリをデプロイする時には、`requirements.txt`に記載されている依存ライブラリをインストールしてくれるはずだから、streamlitのバージョンは合っているはずと思った。

# 結論と解決策
[`st.experimental_rerun`はstreamlitのバージョン`1.38.0`から廃止されて](https://docs.streamlit.io/develop/api-reference/execution-flow/st.experimental_rerun)おり、代わりに`st.rerun`を使うようにすれば良い。

そもそもなぜ`requirements.txt`で指定したローカルと同じバージョンのstreamlitがインストールされていなかったのかというと、`requirements.txt`でインストールが行われた後に、Hugging Face Spacesでデフォルトに用意されているDockerfileでビルドが行われるからだった。
そのデフォルトのDockerfileにはstreamlitをバージョン`1.38.0`でインストールする指示が含まれており、こっちが優先されたようだった(下図)。
![スクリーンショット 2024-09-27 22.10.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708192/11e219eb-4588-6692-fcfa-120e965f6d5c.png)

streamlitのバージョンを下げたDockerfileを作り、それで新しく「カスタムDocker環境」を使っても解決できますが、それだと一から環境を作り直す必要があったので、おとなしく新しいバージョンのstreamlitに合わせてコード修正した方が楽そう。
AtCoder 勉強記録[C++] その65

AtCoderで入茶を目指して勉強しています。
勉強を継続するために投稿を始めました。
もともとアカウントを作成していましたが、今年の4月から本格的に勉強を始めました。
一応自分用に解法を書いていますが雑です、自分で読み返して困ったら修正します。
[私のアカウント](https://atcoder.jp/users/nanami_brother)
[解いた問題](https://kenkoooo.com/atcoder/#/table/nanami_brother)
# 本日解いた問題
## A - delete .
[A - delete .](https://atcoder.jp/contests/abc372/tasks/abc372_a)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define mod 998244353

int main() {
  string s;
  cin >> s;
  string res;
  for(int i = 0; i < s.size(); i++){
    if(s[i] != '.') res.push_back(s[i]);
  }
  cout << res << endl;
}
```
### 解法
解答を入れておく配列`res`を用意しておく。文字列`S`の文字が`.`以外の場合は`res`に文字を入れることで結果が求まる。
## B - 3^A
[B - 3^A](https://atcoder.jp/contests/abc372/tasks/abc372_b)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define mod 998244353

int main() {
  int m;
  cin >> m;
  vector<int> res;
  while(m>0){
    for(int i = 0; i < 11; i++){
      int tmp = pow(3, i);
      int nex = pow(3, i+1);
      if(m < nex) {
        m -= tmp;
        res.push_back(i);
        break;
      }
    }
  }
  cout << res.size() << endl;
  for(int v:res) cout << v << " ";
}
```
### 解法
0から10までの3のべき乗を考え、`M`よりも小さい場合に`A`に加えていくことで結果が求まる。
## C - Count ABC Again
[C - Count ABC Again](https://atcoder.jp/contests/abc372/tasks/abc372_c)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define mod 998244353

int main() {
  int n, q;
  cin >> n >> q;
  string s;
  cin >> s;
  int cnt = 0;
  for(int j = 0; j <= n-3; j++){
    string tmp = s.substr(j, 3);
    if(tmp == "ABC") cnt++;
  }
  for(int i = 0; i < q; i++){
    int x; 
    string c;
    cin >> x >> c;
    x--;
    int add = 0, sub = 0;
    if(x-2>=0 && s.substr(x-2,3) == "ABC") sub++;
    if(x-1>=0 && s.substr(x-1,3) == "ABC") sub++;
    if(s.substr(x, 3) == "ABC") sub++;
    s.replace(x, 1, c);
    if(x-2>=0 && s.substr(x-2,3) == "ABC") add++;
    if(x-1>=0 && s.substr(x-1,3) == "ABC") add++;
    if(s.substr(x, 3) == "ABC") add++;
    cnt -= sub;
    cnt += add;
    cout << cnt << endl;
  }
}
```
### 解法
始めに`S`に含まれる`ABC`の数をカウントしておく。その後、クエリごとに変更する前の文字の前後の部分文字列が`ABC`である場合にカウントを減らし、変更後の文字の前後の部分文字列が`ABC`の場合にカウントを増やす。
M1 Mac に .NET 8 の MAUI を入れて iOS と Android アプリを試してみた

.NET6 で MAUI がプレビューの時にも試してみましたが、改めて .NET8 で MAUI を試してみました。前提となる Xcode や Android Studio はインストール済みで、Visual Studio Code には MAUI や C# の拡張は無い状態（未インストール）で動きました。

https://qiita.com/mnrst/items/a43a32424b84ed2fd432

## .NET に MAUI をインストール

```zsh:zsh
% sudo dotnet workload install maui
```

## MAUI アプリを作成

```zsh:zsh
% dotnet new maui -o mnrmaui

% cd mnrmaui
```

## iOS でアプリを実行

```zsh:zsh
% dotnet build -t:Run -f net8.0-ios
```

## iPad Pro 13-inch (M4) の画面

![dotnet8-maui-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/af880431-1b87-f489-2642-9ddd5c3e478f.png)

## iOS シミュレーターの UDID を探す

```zsh:zsh
% /Applications/Xcode.app/Contents/Developer/usr/bin/simctl list | grep "16 Pro"
iPhone 16 Pro (com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro)
iPhone 16 Pro Max (com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro-Max)
    iPhone 16 Pro (5F1B731E-73C1-4185-8BA0-B94C7443B360) (Shutdown) 
    iPhone 16 Pro Max (A9671D82-F7BF-4B10-A200-9FA0460D881D) (Shutdown) 
```

## iPhone 16 Pro シミュレーターで動かす

```zsh:zsh
% dotnet build -t:Run -f net8.0-ios -p:_DeviceName=:v2:udid=5F1B731E-73C1-4185-8BA0-B94C7443B360
```

## iPhone 16 Pro の画面

![dotnet8-maui-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/70b3204d-f822-fd7c-cc25-3f23c02459dd.png)

## iPhone 16 Pro Max シミュレーターで動かす

```zsh:zsh
% dotnet build -t:Run -f net8.0-ios -p:_DeviceName=:v2:udid=A9671D82-F7BF-4B10-A200-9FA0460D881D
```

## iPhone 16 Pro Max の画面

![dotnet8-maui-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/aa83e509-42c7-f778-5c6e-51c42101ba75.png)

## Android シミュレーターで動かす

Android の場合はあらかじめシミュレーターを起動しておきます。

```zsh:zsh
% dotnet build -t:Run -f net8.0-android
```

## Android の画面

![dotnet8-maui-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/457d1cea-7f01-c999-f1fa-3e0db8699413.png)

## 参考

https://learn.microsoft.com/ja-jp/dotnet/maui/get-started/installation?view=net-maui-8.0&tabs=visual-studio-code
sqlc記事まとめ

最近書いた[sqlc](https://github.com/sqlc-dev/sqlc)関連の記事をまとめておきます。これくらい知っていればストレスなく使えるんじゃないかなぁと思います。

1. [Go のちょっと変わったORM sqlc の紹介](https://qiita.com/ktat/items/2ed13afcd29f49aee6a1) ... sqlcの基本的な使い方
1. [sqlcで動的なWHEREを作る](https://qiita.com/ktat/items/ddf0e5e510279503feee) ... sqlc は動的にSQLを組み立てることは出来ないので、その対策
1. [Go の ORM sqlcでBulk Insertを行う(MySQL)](https://qiita.com/ktat/items/94b6e84f07ffefa80b84) ... ↑と似た話で、Bulk Insertも動的なので、その対策
1. [sqlcでType Overrides の活用とgo-optionalの話](https://qiita.com/ktat/items/74c9f78e96e57282fc5d) ... Type Overridesは便利だよという話と、go-optionalは良いぞという話
1. [GoのORM sqlcのType Overridesを自動生成する(MySQL用)](https://qiita.com/ktat/items/75096d8f2c9014b99754) ... Type Overridesは便利だけどめんどいので自動化しようという話

関連記事として、GoのQuery Builder、[goqu](https://github.com/doug-martin/goqu)について以下に書いています。↑の2と3の話の中で言及しています。

1. [GoのSQL Query Builder goquの紹介](https://ktat.hatenadiary.jp/entry/2024/08/20/082437) ... goqu の基本的な使い方
2. [Goでテーブル情報を自動生成してgoquで使う](https://ktat.hatenadiary.jp/entry/2024/08/20/214627) ... goquを使う際にテーブル名やカラム名をハードコーディングしたくない人向け
macOS15でSTM32CubeIDEデバッガが起動しない！一時的な対応策

## macOS15 (macOS Sequoia) へのアップグレード

Appleから新しいOSが発表されました。
業務で使うなどしていると、安易なアップグレードは危険ですよね。
しかし、やっぱり新しいOSを早く体験したい。
新しもののエンジニアとしては、すぐにでもアップグレードしたい気持ちはわかります。
私もそうなので。
仮想環境などを利用して主要アプリの動作を確認してたのですが、STM32CubeIDEのデバッガは盲点だった。

## デバッガを起動するとエラーが出てデバッグできない

デバッグボタンをクリックするとこんなエラーが出ます。

```
ST-Link firmware verification
St-Link Server is required to launch the debug session.
Please download it on www.st.com
```
![スクリーンショット 2024-09-28 7.58.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202602/12cb4f1f-26f1-fbda-cf5b-406d276c365a.png)

この件はSTのコミュニティでも話題になっています。
一時的な回避策がコミュニティで投稿されていました。

## CubeIDEをターミナルで起動する
```
sudo /Applications/STM32CubeIDE.app/Contents/MacOS/STM32CubeIDE
```
対策がされるまで、ひとまずこれで乗り切る感じですね...
stackoverflowでは恒久対策らしいことが書かれていますが、STのアナウンス待ちですね。

慎重派の方は、macOS Sequoia へのアップグレードはしばらくお預け。

以下のサイトで話題になっていました。
[STのコミュニティ](https://community.st.com/t5/stm32cubeide-mcus/stlink-server-with-macos-15/td-p/705089)
[stackoverflow](https://stackoverflow.com/questions/78996974/stm32cubeide-debugger-no-longer-works-in-macos-15-0-sequoia)
SAMLResponseの解析方法


# はじめに
この記事は、SAMLResponseの中身を取り出して、シングルサインオン(SSO)の問題を解決したい方向けの記事です。
認証が上手くいかない原因を解決するために、認証サーバーから送られてきたSAMLResponseの詳細を解析するための方法をまとめました。


1. 認証サーバー宛てにRequestは送れておりResponseが返ってきているものの、サービス側で認証ができない
2. 原因を追究するためにSAMLResponseの中身を具体的に確認したい



上記で悩んでいる人の手助けになればと思い、記事を書きました。
細かい仕様やIT用語は所々省いていますが、ご了承ください。

# SAMLResponseのデータを取り出そう

何はともあれSAMLResponseのデータを取得する必要があります。

1. ブラウザの開発者ツールを開き、Networkタブを表示
2. 事象を再現し、通信内容を確認
3. 画像のようにpayloadタブを選択しておく
4. 「SAMLResponse：」があるやりとりを見つけ、画像のようにpayloadにSAMLResponseが表示されていることを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/d6993310-53ee-40cd-aa56-4bd59789faaa.png)

:::note info
文字列の上で右クリックを押すと「Copy value」が表示されます。クリックすることで、SAMLResponseデータを全文コピーできます。
:::

これでSAMLResponseのデータを取得できました。

が、このままでは何が何やらわからないので、VSCodeを使ってデコードしていきます。

# VSCodeの設定
VSCodeのインストール方法は調べれば出てくるので、頑張ってください。

必要な拡張機能をインストールしましょう

* SAML Extension
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/f34d9bc4-f3ec-71e5-dc42-d7baa197f549.png)
→SAMLResponseデータをデコードするために使います。


* XML Tools
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/75b63e1d-59f2-6e9b-dbc8-254db8b50a33.png)
→デコードしたSAMLResponseデータを見やすく整えてくれます。


これで準備完了です！

# SAMLResponseの中身を見てみよう

### 1. 新しいテキストファイルを開く（フォルダとか好きにしてください。とりあえず最短の手順を言います）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/bf7a1ec1-ffd4-0b0b-7174-a7bbe49923bb.png)

### 2. SAMLResponseデータを貼り付け
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/1eadc5bd-e36c-cbde-976d-2e92fbf086f6.png)

### 3. ``Ctrl + Shift + P``でコマンドパレットを表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/a664c13d-096a-4fe0-d8dc-0325b0fea284.png)

### 4. ``SAML: Decode element``を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/a15ac11f-8647-ebb4-1891-149ef83bcbfc.png)
これでSAMLResponseデータが読めるようになりました！ただこのままだと見にくいので、XML Toolsを使ってFormatしましょう。

### 5. ``Ctrl + Shift + P``でコマンドパレットを表示し、``XML Tools: Format as XML``を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/82d52618-ce05-a8b8-45cb-56b6baa65363.png)

### 6. これでSAMLResponseが見やすくなりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620187/c34dd1a4-d971-48f3-5abf-d7cf1ca57c4d.png)


# うまくデコードできないときは…
SAMLResponseデータがURLエンコードされているものをコピーすると、デコードできません。
データに``%2``などがないことを確認してみてください。

# SAMLRequestもデコード可能
ここでは詳細書きませんが、同じようにSAMLRequestも確認することができます。
``SAML: Decode and inflate element``を選択すればOKです。


# おわり
自分はこの方法でSSOに関する問い合わせ調査をやっています。個人情報が含まれるのでくれぐれも取り扱いに注意してくださいね。

SAML-tracerというchromeの拡張機能を使えばもっとわかりやすいとのことですが、社用PCではブロックされました。。。
【Salesforce】Bulk API

最近、大量データを扱うことがあったので、Bulk APIについて概要をまとめました。

## Bulk Apiとは

Salesforceで大量のデータを扱う際のプログラムオプションです。
たとえば、2,000件以上のレコード（データ）を一度に追加したり、更新したりする場合に非同期で処理されます。
現在、Bulk API 2.0 と Bulk APIの2種類があります。

## Bulk API 2.0 と Bulk API の違い
Bulk API 2.0は、大量のデータを効率的にSalesforceに読み込んだり、データを一括でクエリ（検索）できる方法です。前のバージョンと比べて、コードを書く手間を減らし、ジョブの進行状況を簡単に確認できるようになっています。また、失敗したデータの自動再試行や、複数の処理を同時に進められる並列処理にも対応しているため、大量のデータを管理する作業が簡単になります。

## 使い分け 
もし扱うレコードが2,000件以下なら、「REST API」や「SOAP API」での同期処理がおすすめです。簡単に言うと、少ないデータならその場で確認しながら進めるやり方、大量のデータなら裏でまとめて処理する非同期があります。

### 非同期処理
わかりやすくいうと、データをまとめて送信してすぐに結果を待たずに裏で処理を進める方法です。
【Blazor】サイドバーに表示するアイコンの種類を増やす方法

## はじめに

サイドバーに表示されるアイコンの種類を増やす方法についてネット上を調べてもやり方が出てこなかったので調べてみました。

## 問題

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783169/908c4891-f1d5-20ec-07b1-641d68a5d5e9.png)

Blazorのデフォルトプロジェクトを起動すると表示されるサイドバーのアイコンが初期状態だと３つしか用意されていないため同じ形式で追加をしたい。

【追加後】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783169/5809419c-7ceb-52ac-3fd2-ce8dbaf61068.png)

## 解決策

`NavMenu.razor.css`にデフォルトで記載されている内容をもとに以下のように修正する。

修正前
``` css
.bi-list-nested-nav-menu {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
```

修正後
``` css
.bi-list-task-nested-nav-menu {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-check' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3.854 2.146a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 3.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 7.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 0 1 .708-.708l.146.147 1.146-1.147a.5.5 0 0 1 .708 0z'/%3E%3C/svg%3E");
}
```

具体的に修正する点はいかになります

- `class`にBoostrapのホームページ記載のクラスを記載
- `d`にBoostrapのホームベース記載の`d`の値を記載
- CSSの名前を適当なものに変更

基本的なイメージとしてはBoostrapのホームページの「HTMLをコピー」欄の情報をコピーするイメージで大丈夫です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783169/d775ee28-7979-3b4d-7be5-de9e75b31a13.png)

これでサイドバーの既存アイコンと同じ形式のアイコンを追加することができます。

::: note warn
下記のように`Path`タグが複数あるようなアイコンについて上記の手順だとうまくいかないです。一工夫でうまくいく気がしますが、Boostrapには似たようなアイコンもたくさんあり代用も簡単であるため本記事では対象外とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783169/d3f00e08-a116-c6a9-1860-5372a6ed4657.png)
:::

## おわり

Blazorのサイドバーで使用できるアイコンの種類を増やす方法を解説いたしました。
試してみたところ意外と簡単にできました。

ぜひ皆さんもやってみてください。

## 参考

https://icons.getbootstrap.jp/

https://designup.jp/bootstrap5-icon.html
Go言語でWake On Lanパケットを送信する

#　はじめに

ネットワークにマジックパッケットを送信してPCの電源をONにする技術Wake On Lan


https://qiita.com/gam0022/items/4c2a3fdc29ac4977b51c

のマジックパケットをGo言語から送信する話です。

# Wake on LANパケットを送信するプログラム

```go
package main

import (
	"log"
	"net"
)

func main() {
	err := SendWakeOnLanPacket("78:84:3c:2a:ac:08")
	if err != nil {
		log.Fatalln(err)
	}

}

// SendWakeOnLanPacket : send wake on lan magic packet to mac address
func SendWakeOnLanPacket(mac string) error {
	ra, err := net.ResolveUDPAddr("udp4", "255.255.255.255:9")
	if err != nil {
		return err
	}
	la, err := net.ResolveUDPAddr("udp4", ":0")
	if err != nil {
		return err
	}
	c, err := net.DialUDP("udp4", la, ra)
	if err != nil {
		return err
	}
	defer c.Close()
	hw, err := net.ParseMAC(mac)
	if err != nil {
		return err
	}
	packet := []byte{}
	prefix := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
	packet = append(packet, prefix...)
	for i := 0; i < 16; i++ {
		packet = append(packet, hw...)
	}
	_, err = c.Write(packet)
	return err
}

```

ポイントは

### UDPの破棄ポート(9)へブロードキャストで送信する

```go
ra, err := net.ResolveUDPAddr("udp4", "255.255.255.255:9")
	if err != nil {
		return err
	}
	la, err := net.ResolveUDPAddr("udp4", ":0")
	if err != nil {
		return err
	}
	c, err := net.DialUDP("udp4", la, ra)
	if err != nil {
		return err
	}
	defer c.Close()

```

### 文字列のMACアドレスをバイナリーにする

```go
hw, err := net.ParseMAC(mac)
	if err != nil {
		return err
	}
```

### マジックパケットの作成

```go
packet := []byte{}
	prefix := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
	packet = append(packet, prefix...)
	for i := 0; i < 16; i++ {
		packet = append(packet, hw...)
	}
	_, err = c.Write(packet)
```

# 送信結果

パケットキャプチャーすると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874862/0e4a6016-c79a-f821-0c7a-9d48467125fc.png)

# 余談

この機能は旧版TWSNMPのユーザーからの要望に対応してTWSNMP FC/FKをWake On Lan対応した時に作ったものです。

https://note.com/twsnmp/n/n5ff261ebc85a
マージしたブランチを全部消す方法

## 結論
```sh
git branch --list --format='%(refname:short)' | \
  xargs -I{} -- git -c advice.forceDeleteBranch=false branch -d '{}'
```

## 何をしているか
### `git branch --list --format='%(refname:short)'`

`git branch --list` はローカルに存在するブランチを全列挙します。しかし、それだと現在のブランチを意味するインジケーターが行の先頭について邪魔なので`--format='%(refname:short)'`で抑制します。

### `xargs -I{} --` ...

`-I {}` は後続のコマンドに出現する`{}` をパイプされてきた文字列で置換します。これは Git のブランチ名による[コマンドインジェクション](https://jmty-tech.hatenablog.com/entry/2023/01/23/170748) を防ぐために重要です。単純にspreadしてしまうと、途中でセミコロンを挟むことによりコマンドが終了してしまいます。

### `git -c advice.forceDeleteBranch=false branch -d`

`-c advice.forceDeleteBranch=false` はエラーメッセージを減らすための呪文です:
```
hint: If you are sure you want to delete it, run 'git branch -D integrate'
hint: Disable this message with "git config advice.forceDeleteBranch false"
```

それを無視すれば、単純に`git branch -d`で、マージされているブランチならば消すという働きをします。
ChatGPT-3.5先生へ「数学の過去問に、決選投票の問題文がありますか。 」を聞きました。(回答省略)

以下お好みでどうぞ。コメントをお待ちしております。

数学の過去問に、決選投票の問題文がありますか。
算数の過去問に、決選投票の問題文がありますか。
決選投票で数学の問題文を作って下さい。
決選投票で算数の問題文を作って下さい。
決選投票は誰が考えたのですか。

決選投票でsympyで解ける数学の問題文を作って下さい。

解答もお願いします。

**(回答省略)　問題文が長いのと私が内容を理解できないため。**

# DeepL翻訳
　
https://www.deepl.com/ja/translator#ja/en-us/勝ち馬に乗る
　
# 参考

勝ち馬に乗る 反対語
https://www.google.co.jp/search?sca_esv=8536d3cccb765549&sca_upv=1&q=%E5%8B%9D%E3%81%A1%E9%A6%AC%E3%81%AB%E4%B9%97%E3%82%8B%E3%80%80%E5%8F%8D%E5%AF%BE%E8%AA%9E&oq=%E5%8B%9D%E3%81%A1%E9%A6%AC%E3%81%AB%E4%B9%97%E3%82%8B%E3%80%80%E5%8F%8D%E5%AF%BE%E8%AA%9E&aqs=heirloom-srp..0l1

https://en.wikipedia.org/wiki/Runoff_voting

https://ja.wikipedia.org/wiki/決選投票

https://ja.wikipedia.org/wiki/自由民主党総裁選挙

決選投票 数学
https://www.google.com/search?q=%E6%B1%BA%E9%81%B8%E6%8A%95%E7%A5%A8+%E6%95%B0%E5%AD%A6&oq=%E6%B1%BA%E9%81%B8%E6%8A%95%E7%A5%A8%E3%80%80%E6%95%B0%E5%AD%A6&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIKCAEQABiABBiiBDIKCAIQABiABBiiBDIKCAMQABiABBiiBDIKCAQQABiABBiiBNIBCTE5NzMwajBqN6gCALACAA&client=ms-android-huawei&sourceid=chrome-mobile&ie=UTF-8

決選投票 メリット デメリット
https://www.google.com/search?client=ms-android-huawei&sca_esv=8536d3cccb765549&sca_upv=1&sxsrf=ADLYWILDGypFMOp3eXf6Z88ldWuhgrSZEQ:1727457760235&q=%E6%B1%BA%E9%81%B8%E6%8A%95%E7%A5%A8+%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88+%E3%83%87%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88&sa=X&ved=2ahUKEwjj7qbR0eOIAxXLsFYBHfedPe4Q1QJ6BAglEAE&biw=1128&bih=628&dpr=1.5
RectTransformとTransform.positionの違いについて

# RectTransformとTransform.positionの違いについて
RectTransformとTransform.positionの違いについての理解が曖昧で、特にUIの位置を決めるときによく問題が起きることがあったので、違いをまとめてみました。

## RectTransformとは
RectTransformとはUI要素専用のコンポーネントで2D 空間におけるサイズと位置を制御します。
具体的にはスクリーン座標や相対的な座標を基に位置やサイズを管理します。

## Transform.positionとは
Transform.positionは3D 空間でオブジェクトの位置を指定するために使用されます。
これは、全てのオブジェクト（3Dモデルやカメラなど）に関連付けられた通常の Transform コンポーネントで、Unityのワールド座標系（3D空間）でオブジェクトの位置を管理します。

## まとめ
UIの位置を決める場合はRectTransform、3Dのオブジェクトの座標を決める場合はTransform.positionを使用しよう。
【コピペでできる】Kotlinで画像合成

# はじめに
Kotlinで画像を任意のテキストと合成しその結果を出力したい場合、以下のようにするとよいです

## ディレクトリ構成
以下のような構成にする
今回はDockerを使ってやってみる。Main.jarはコンパイル時にできるものなので作成しなくて良い
![スクリーンショット 2024-09-28 2.09.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227176/9bdf70e5-b229-6db3-12b8-d035ab60d4a9.png)

## ソースコード

``` Dockerfile:Dockerfile
FROM zenika/kotlin
# 作業ディレクトリを作成
WORKDIR /app

# プロジェクトファイルをコンテナにコピー
COPY . .

CMD ["sh", "-c", "kotlinc Main.kt -include-runtime -d Main.jar && java -jar Main.jar"]
```

```Kotolin:Main.kt
import java.awt.*
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO

fun addTextToImage(imagePath: String, outputPath: String, text: String) {
    // 画像を読み込む
    val imageFile = File(imagePath)
    val image: BufferedImage = ImageIO.read(imageFile)

    // 描画用に新しいBufferedImageを作成
    val outputImage = BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_ARGB)
    val graphics = outputImage.createGraphics()

    // 元の画像を描画
    graphics.drawImage(image, 0, 0, null)

    // テキストのスタイルを設定
    val font = Font("Arial", Font.BOLD, 48)  // フォントとサイズを設定
    graphics.font = font
    graphics.color = Color.WHITE  // テキストの色

    // テキストの位置を計算（右下に配置）
    val fontMetrics = graphics.fontMetrics
    val x = image.width - fontMetrics.stringWidth(text) - 20  // 右下に余白を作るためのマージン
    val y = image.height - fontMetrics.height + fontMetrics.ascent - 20

    // テキストを画像に描画
    graphics.drawString(text, x, y)

    // リソースを解放
    graphics.dispose()

    // 結果を保存
    val outputFile = File(outputPath)
    ImageIO.write(outputImage, "png", outputFile)
    
}

fun main() {
    val imagePath = "input_image.png"  // 入力画像のパス
    val outputPath = "output_image.png"  // 出力画像の保存先
    val text = "Sample Text"  // 合成するテキスト

    addTextToImage(imagePath, outputPath, text)

    println("テキストを追加した画像を保存しました: $outputPath")
}
```

## 実行

以下コマンドを実行しビルド
```
docker build -t kotlin-image-processing .
```

以下コマンドを実行し合成された画像を出力
```
docker run --rm -v $(pwd):/app kotlin-image-processing
```

input_image.pngに「Sample Text」が合成されてoutput_image.pngに出力されました。
DataSyncエージェントのアクティブ化（メモ書き）

# DataSyncエージェントのアクティブ化

AWS DataSync エージェントの作成を完了するには、エージェントをアクティブ化する必要があります。
アクティブ化にはデプロイしたDataSync Agent のアクティベーションキーを取得する必要があります。
キーの取得にはいくつか方法があります。
自分が知る限りでは以下2つ。
①マネジメントコンソールにより自動取得。
②DataSync エージェントにアクセスし手動取得。

①の方法についてはパブリック環境にデプロイした上で、マネジメントコンソールから
エージェントにhttp接続する。
②の方法はプライベート環境にデプロイした上で、他VPCもしくはパブリックサブネットに
デプロイした踏み台サーバ等を経由して、エージェントにSSH接続し、
エージェントのローカルコンソールから取得する、もしくはエージェントにCurlコマンドを実行し取得する。

当記事では②の方法でキーを取得し、エージェントの作成までを行います。

以下の構成で環境を構築します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563691/6fc6157c-a52c-f68e-9b14-aedccca9d79d.png)

まず踏み台インスタンスを構築します。
※構築方法については省略
パブリックサブネットに配置し、パブリックIPを付与します。
Teraterm等でキーペアを用いてSSH接続します。
SSH接続できない場合はネットワークACL、セキュリティグループの設定を確認してください。

踏み台からエージェントへSSH接続するためにTeratermのSCP機能を使って、キーペアを踏み台サーバに配置しておきます。
※エージェントは踏み台と同じキーペアを選択して構築しています。

下記コマンドで踏み台からエージェントにアクセスします。
`sudo ssh -i <yourkeypair> admin@DataSyncAgentIPAddres`

成功すると以下のようなコンソールが表示されます。
推奨していないインスタンスタイプを使用しているので、その旨のエラーメッセージが表示されていますね。
とりあえず気にせず進めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563691/5aefbd78-e652-7389-c349-3d6216f6dd9a.png)

アクティベーションキーを取得するので「0」を押してEnterをクリックすると、リージョンの入力が求められます。
当記事では東京リージョンを使用しているので「ap-northeast-1」と入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563691/68c19f9b-3894-abbf-37b8-fd8a2cc2a5eb.png)

エンドポイントタイプを選択します。DataSyncエージェントはプライベートサブネットにデプロイしているので、
「3」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563691/dfc6b58d-4918-c160-f55c-729889d95b1a.png)

VPCエンドポイントのプライベートIPアドレスの入力が求められます。
マネジメントコンソールで`com.amazonaws.ap-northeast-1.datasync`のVPCエンドポイントのIPアドレスを確認し、入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563691/70f9117a-816f-2a0f-f1c4-509c3faaba58.png)

無事にアクティベーションキーを取得することができました。
表示されたキーはコピーして保管しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563691/43a18a3a-04f7-728d-1fab-be81c556693b.png)

Enterをクリックし元の画面？に戻り、「x」を入力しエージェントのローカルコンソールとのセッションを終了します。

マネジメントコンソールからDataSyncの画面を開き、エージェントの作成を進めます。
保管したアクティベーションキーを入力し、エージェントを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563691/e14b49d5-a265-c562-ced6-7ebef7f6fe81.png)

当記事は以上となります。
この記事が誰かのお役に立てば幸いです。
モノレポ ( Turborepo ) の Azure Functions（Node Typescript）をESBuildを使ってBundleしてからデプロイしたメモ

# 概要
[前回](https://qiita.com/hibohiboo/items/a678e8af01b6fef21709)はフォルダを作成してその中に必要ファイルをコピーしていた。
今回はBundleすることによりその手間を減らすこととする。

[ソースコード](https://github.com/hibohiboo/async-ttrpg/blob/d27335b83124ec0f0e777d1510b801992600cd2e/apps/api/package.json)

# ソースコード

ビルドスクリプトをesbuildを使ったものに修正。
`@azure/functions-core`はexternalに指定しないとエラーとなる。

```diff:apps/api/package.json
{
  "name": "@async-ttrpg/api",
  "version": "0.0.0",
  "description": "",
  "scripts": {
    "prebuild": "npm run clean && npm run prisma-generate",
+    "build": "esbuild src/functions/index.ts --bundle --platform=node --target=node20 --outdir=dist/src/functions --external:@azure/functions-core --minify",
    "watch": "tsc -w",
    "clean": "rimraf dist",
    "predev": "npm run build",
    "dev": "func start --port 7071",
    "lint": "eslint",
    "prisma-pull-local": "dotenv -e .env.local -- bash -c 'npx prisma db pull'",
    "prisma-pull": "prisma db pull",
    "prisma-generate": "prisma generate",
+    "predeploy": "npm run build",
+    "deploy": "dotenv -e .env -- bash -c 'func azure functionapp publish $APP_NAME --subscription $AZURE_SUBSCRIPTION_ID'",
    "ncu": "ncu -u"
  },
  "dependencies": {
    "@azure/functions": "^4.5.1",
    "@azure/identity": "^4.4.1",
    "@azure/storage-blob": "^12.25.0",
    "@azure/storage-queue": "^12.24.0",
    "@hono/swagger-ui": "^0.4.1",
    "@hono/zod-openapi": "^0.16.2",
    "@marplex/hono-azurefunc-adapter": "^1.0.0",
    "@prisma/client": "^5.20.0",
    "date-fns": "^4.1.0",
    "hono": "^4.6.3",
    "mssql": "^11.0.1",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@async-ttrpg/eslint-config-custom": "*",
    "@async-ttrpg/tsconfig": "*",
    "@types/mssql": "^9.1.5",
    "@types/node": "^22.7.3",
    "dotenv-cli": "^7.4.2",
    "esbuild": "^0.24.0",
    "prisma": "^5.20.0",
    "rimraf": "^6.0.1",
    "typescript": "^5.6.2",
    "zod-prisma-types": "^3.1.8"
  },
  "optionalDependencies": {
    "@rollup/rollup-linux-x64-gnu": "^4.22.5"
  },
  "main": "dist/src/{app.js,functions/*.js}"
}

```

バンドルの対象ファイルを作成

```ts:apps/api/src/functions/index.ts
import './httpTrigger';
import './openAPI';
import './queueTrigger';
```

node_modulesはバンドルされた結果不要になるのでignoreに追加。

```diff:apps/api/.funcignore
*.js.map
*.ts
.git*
.vscode
__azurite_db*__.json
__blobstorage__
__queuestorage__
local.settings.json
test
tsconfig.json
.turbo
.env
.env.sample
+ node_modules
```


# 参考

[Nxモノレポ上でAzure Functionsをビルド・デプロイできるようにするメモ](https://zenn.dev/naas/scraps/60f944146eacd9)
[esbuild](https://esbuild.github.io/api/)
注釈機能が最強！仕事効率を劇的にアップさせるスクリーンショットツール

## はじめに
こちらはANGEL Calendar28日目の記事になります！

皆さんは、作業証跡の残す際や、チームメンバーとの情報共有の際に、スクリーンショットを活用されていますか？

私も、日々、Webページやアプリの画面をキャプチャし、資料に貼り付けたり、チャットで共有したりする機会が多いです。

しかし、標準ツールでは、画像に直接書き込みをしたり、動画と音声の同時キャプチャをしたりといった、より高度な機能が求められる場面も少なくありません。

そんな時に出会ったのが、CleamShot Xです。
このツールを使い始めてから、私の作業効率は劇的に向上しました。
この記事では、そんなCleamShot Xの魅力をお伝えします。

## CleamShot Xとは？
[CleamShot X](https://cleanshot.com/) は Mac OS で動作するスクリーンショットツールで公式サイトでは以下のように書かれています。
> It feels like 7 apps in one.
>CleanShot X provides over 50 features making it the ultimate screen capturing tool.

### 搭載されている機能
- クイックアクセス
- 注釈
- クリーンショットクラウド
- スクロールキャプチャ
- スクリーン録画
- 背景ツール
- テキスト認識
- スクリーンショットピン留め
- デスクトップアイコン非表示
...etc

## CleamShot Xの魅力！
上記でCleamShot X に搭載されている機能をいくつか紹介しましたが
ここでは筆者が実際に使って便利だと思った機能を抜粋して紹介しようと思います。

### 注釈機能
CleanShot Xは、とても注釈機能が充実しており、矢印や図形・モザイクはもちろん
文字のスタイルなども豊富で、簡単に画像の注釈を行うことが可能です。
![CleanShot 2024-09-28 at 00.31.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/310e2c65-9e6e-f4c3-93b9-517eaf7ee759.png)


### クイックアクセス機能
クイックアクセス機能は、撮影したスクリーンショットや動画に対して、
CopyやSaveはもちろん。注釈・ピン留め・アップロードといった操作も素早く行うことが出来るため、標準ツールのようにデスクトップを開き撮影したスクリーンショットを開くという動作がなくなりました。

![CleanShot 2024-09-28 at 00.39.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/7d35caf7-183f-e638-03b9-f9dcbb2941a2.png)

### その他
他にも上げていないだけで、素晴らしい機能がCleanShot Xには搭載されています。
興味があれば一度公式サイトを覗いてみてください！
https://cleanshot.com/features


## 価格について

様々な無料スクリーンショットのツールがある中で \\$29 や \\$8/月 と考えると高めですが
これだけの機能があると考えたら、ある意味安かったのかなと感じています。

筆者は買い切りタイプのものを購入したのですが、全く後悔していません。

![CleanShot 2024-09-25 at 16.56.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456939/ae66f8ce-9e3d-1568-2f67-96f8b1461990.png)

https://cleanshot.com/pricing

## まとめ

いかがでしたでしょうか。
標準のスクリーンショットでは物足りないと感じていた方の参考になれば幸いです！
jQuery Validation + Choices.jsでエラーチェックを行えるようにする方法

フロントエンドでバリデーションチェックを行える[jQuery Validation](https://jqueryvalidation.org/)
セレクトボックスに絞り込み検索を追加できる[Choices.js](https://choices-js.github.io/Choices/)

後者のほうはselect2も有名ですが、どちらも便利で使っているのですが、
Choicesで作ったセレクトボックスではjQuery Validationが働かなったので、やや強引に解決しました。

https://stackoverflow.com/questions/76130922/how-to-add-is-invalid-class-on-choices-js

によると、select2なら解決出来るようなことが書いてあったので、
使うプラグインを変更できるようでしたら、そうしたほうが早いかもしれないです。

# 解決方法

## jQuery Validationのignoreを使用

ignoreというオプションで、エラーチェックを無視する対象を選べて、
デフォルト値は`:hidden`になっており、
これにより、非表示状態の入力項目はエラーチェックしなくなっています。

Choicesはセレクトボックスを加工すると元々のセレクトボックスを`display: none`で隠してしまうので、
このルールを書き換えます。

```js
// Choicesの対象セレクトボックス
new Choices('#select');

// jQuery Validationの設定
$('#form').validate({
  ignore: ':hidden:not(#select)', // ここにChoicesの対象を記載
  rules: {
    'select': {
      required: true
    }
  },
  messages: {
    'select': {
      required: '入力してね'
    }
  }
});
```

上記の記述でチェックを無視する対象が「`#select`以外のhidden」という設定に変わるので、
Choicesで作られたセレクトボックスもチェックされるようになります。

## 入力切り替え時のエラーチェック処理

エラーはチェックしてくれてもセレクトボックスの選択を切り替えてもリアルタイムでバリデーション表示が切り替わりません。

それを解決するにはセレクトボックス側のchangeイベントで対応します。
上記のソースを以下のようにします。

```js
// 操作するので変数にする
const selectBox = new Choices('#select');

$('#form').validate({
  ...(バリデーションは特に変更なし)...
});

// selectBoxを切り替えたときの処理
$(selectBox.passedElement.element).on('change', async function () {
  // セレクトボックスのバリデーションを呼び出す
  $('#select').valid();
});
```

# おまけ

ググっても出てこなかったのでドキュメント見ながら対応していたのですが、
ChatGPTに聞いてみたら、
断片的ながら

* Choicesのカスタムイベントを使う
* jQuery Validationのignoreオプションを使う

というような回答が得られたので、かなりのヒントが出てました。
※最初から使えばよかった。

ネットで検索する時代ももう少しで終わるのかもと思った次第です。
投稿 89 TailwindCSS CDNを使う（最も簡単） 2024/09/27

本日の勉強時間　5H


方法1: TailwindCSS CDNを使う（最も簡単）
TailwindCSSをすぐに使いたい場合は、公式のCDNをHTMLファイルに追加するだけで、簡単にTailwindCSSの機能を使えます。これは開発中に試してみたいときに最適です。
手順:
TailwindCSSのCDNリンクをHTMLの<head>内に追加します。
html
```
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TailwindCSS Example</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
  <h1 class="text-3xl font-bold underline text-blue-500">Hello, TailwindCSS!</h1>
</body>
</html>
```
これで、クラスをHTMLに直接追加してTailwindCSSのスタイルを適用できます。

▪️利点
簡単導入: TailwindCSSのファイルをダウンロードする必要がなく、すぐに試せます。
軽量開発: 細かい設定なしで、HTMLとJSに直接組み合わせられます。

▪️欠点
パフォーマンスの問題: 全てのCSSが読み込まれるため、本番環境では不要なスタイルが多く含まれる可能性があります。
カスタマイズできない: 既定の設定に従うしかなく、ビルドプロセスを通じてカスタマイズができません。
windowsのコマンドプロンプトでFizzBuzzバッチ例

単発小ネタ　Windowsバッチ

# 20240928_windowsのコマンドプロンプトでFizzBuzzバッチ例  
  
## お題  
-  1から100までの整数をプリントするプログラムを書け。
   ただし3の倍数のときは数の代わりに｢Fizz｣と、
   5の倍数のときは｢Buzz｣とプリントし、
   3と5両方の倍数の場合には｢FizzBuzz｣とプリントすること。

```FizzBuzz_v001.bat
@echo off
@Rem/||(
 1から100までの整数をプリントするプログラムを書け。
 ただし3の倍数のときは数の代わりに｢Fizz｣と、
 5の倍数のときは｢Buzz｣とプリントし、
 3と5両方の倍数の場合には｢FizzBuzz｣とプリントすること。
)
echo.
echo **** **** **** **** 
setlocal EnableDelayedExpansion
for /l %%i in (1, 1, 100) do (
  set /a mod3=%%i %% 3
  set /a mod5=%%i %% 5
  set /a mod15=%%i %% 15
  if !mod15!==0 (
    echo FizzBuzz
  ) else if !mod5!==0 (
    echo Buzz
  ) else if !mod3!==0 (
    echo Fizz
  ) else (
    echo %%i
  )
)
echo **** **** **** **** 
echo.
pause
```

## 別解、横並びcsv版
```FizzBuzz_v002.bat
@echo off
@Rem/||(
 1から100までの整数をプリントするプログラムを書け。
 ただし3の倍数のときは数の代わりに｢Fizz｣と、
 5の倍数のときは｢Buzz｣とプリントし、
 3と5両方の倍数の場合には｢FizzBuzz｣とプリントすること。
)
echo.
echo **** **** **** **** 
setlocal EnableDelayedExpansion
for /l %%i in (1, 1, 100) do (
  set /a mod3=%%i %% 3
  set /a mod5=%%i %% 5
  set /a mod15=%%i %% 15
  if !mod15!==0 (
    @echo|set /p=FizzBuzz
  ) else if !mod5!==0 (
    @echo|set /p=Buzz
  ) else if !mod3!==0 (
    @echo|set /p=Fizz
  ) else (
    @echo|set /p=%%i
  )
  if not %%i==100 (
    @echo|set /p=", "
  )
)
echo.
echo **** **** **** **** 
echo.
pause
```

## その他
- いつか出会うと思われる「遅延環境変数展開」と戯れることができます。
- 無理すればワンライナーにできそうです。

以上
windowsのコマンドプロンプトでhash一覧作成するバッチ例

単発小ネタ　Windowsバッチ

# 20240928_windowsのコマンドプロンプトでhash一覧作成するバッチ例  
  
## 機能  
- 実行すると、バッチの実行ディレクトリ以下再帰的に全てのファイルのhashをログに記録する。
- certutil -hashfileを利用。SHA1以外も指定できるので任意で変更してください。
- ファイル名的に無理だったり、オープンされたファイルはエラーになるなどあるので、ログ見て別途個別に確認いただく感じでご了承ください。

```get_hashlist_v001.bat
@echo off

rem **** **** **** **** **** **** **** **** **** **** **** 
rem **** **** **** **** **** **** **** **** **** **** **** 
echo.
echo ################################################################
echo.
echo ハッシュ値収集バッチです。実行しますか？
echo 中止する場合はCtl+C
echo 実行する場合は
pause

echo.
echo 実行します
echo.
echo ################################################################
echo.

echo.
echo ************ 処理開始 ************
echo.

rem **** **** **** **** **** **** **** **** **** **** **** 
rem **** **** **** **** **** **** **** **** **** **** **** 
rem 時間取得
set CDATE=%date:~-10,4%-%date:~-5,2%-%date:~-2,2%
set CTIME=%time: =0%
set CTIME=%CTIME:~0,2%-%CTIME:~3,2%-%CTIME:~6,2%

rem ログファイル設定
set CDIR=%~dp0
set LFILE=%CDIR%hashlist_%CDATE%_%CTIME%.txt
set LFILEL=%CDIR%hashlist_%CDATE%_%CTIME%_lite.txt
set LFILELODD=%CDIR%hashlist_%CDATE%_%CTIME%_lite_odd.txt
set LFILELEVN=%CDIR%hashlist_%CDATE%_%CTIME%_lite_evn.txt

echo. >> %LFILE%
echo search : %CDIR% >> %LFILE%
echo start  : %DATE% %TIME% >> %LFILE%
echo ******************************************** >> %LFILE%

rem **** **** **** **** **** **** **** **** **** **** **** 
rem **** **** **** **** **** **** **** **** **** **** **** 
for /f "delims=;" %%i in ('dir /s /b /a-d') do (
  echo. >> %LFILE%
  certutil -hashfile "%%i" >> %LFILE%
  if ERRORLEVEL 0 (
    echo > nul
  ) else (
    echo err : %%i >> %LFILE%
  )
)

echo. >> %LFILE%
echo ******************************************** >> %LFILE%
echo end    : %DATE% %TIME% >> %LFILE%
echo by %0 >> %LFILE%

rem findstr /v "hashfile" %LFILE% | findstr /r /c:"[a-z]" | findstr /v "^search" | findstr /v "^start"  | findstr /v "^end"  | findstr /v "^by" > %LFILEL%
findstr /v "hashfile" %LFILE% | findstr /r /c:"[a-z]" | findstr /v "^search" | findstr /v "^start"  | findstr /v "^end"  | findstr /v "^by"  | findstr /n "SHA Cert" > %LFILELODD%
findstr /v "hashfile" %LFILE% | findstr /r /c:"[a-z]" | findstr /v "^search" | findstr /v "^start"  | findstr /v "^end"  | findstr /v "^by"  | findstr /n /v "SHA Cert" > %LFILELEVN%
goto paste

:end
rem **** **** **** **** **** **** **** **** **** **** **** 
rem **** **** **** **** **** **** **** **** **** **** **** 
echo.
echo ************ 処理終了 ************
echo.

echo 10秒後に閉じます
ping 127.0.0.1 -n 10 2> nul 1>&2
exit

:paste
setlocal
set N=0
set S=
for /F "delims=" %%A in (%LFILELODD%) do call :sub "%%A"
goto end

:sub
for /F "%S% delims=" %%B in (%LFILELEVN%) do >>%LFILEL% echo.%~1 %%B&goto next

:next
set /A N+=1
set S=skip=%N%
```

## その他
- 1行でファイル名とhashがほしいなと思って編集しています。
- 最後のhash一覧以外不要とかなら途中ファイルは削除するよう改変してみてください。
- paste処理のforの/nは偶数奇数チェック用なので不要ならオプション消してください。
- Linuxに持って行った方が早い感はありますが、Windowsオンリーな環境もあるよね、という時の試作品でした。かなり遅いと思います。
- 今ならwsl使えるならそれを利用するとか、PowerShellを模索するとか、他の手法も検討しましょう。

以上
エンジニアが投資で成功できるヤバい理由

## エンジニアは投資で成功できる

この記事を書いている私は決して投資で成功しているわけではないですが、エンジニアは投資で成功できると思っています。

この記事では、その理由を書いていきたいと思います

## エンジニアが投資で成功できる理由①感情に左右されない

投資というものにはタイムフレームがあります。

私は約15年くらい投資をしてますが、いい環境の時と悪い環境の時があります。

実はどちらの環境も同じくらい大事です。

逆に投資にとっていい環境の場合買いはおすすめできません。

なぜなら投資対象が十分高くなってしまっているからです。

逆に投資にとって悪い環境の方が良い投資先が安い値段で買えて後々利益が出るのでおすすめです。

エンジニアはきっと投資よりも自分の仕事の方が忙しくて投資には無関心だと思うので、良い環境の時に飛び乗ることもないし、悪い環境の時に悲観しすぎることもありません。

## エンジニアが投資で成功できる理由②財務諸表を読もうとする姿勢がある

一般的な投資家は話題性だけで投資先を選んでしまいがちです。

何より私がそうでした。

たとえばアメリカのユニコーンと呼ばれ利益が出ていないのにもてはやされていた企業が、ピークの100分の1以下になってしまったなんていうことがありました。

財務諸表を数年分読めばその企業の稼ぐ力がわかってくると思います。

特に投資家が重視しているキャッシュフローの指標EBITDAが大幅に成長している企業やPEGレシオが2を切っている企業は好況時により上がりやすい気がしてます。

エンジニアは、そういった勝つ可能性の高い銘柄を吟味して買うことができます。

## エンジニアが投資で成功できる理由③本業の収入が割と安定しているので投資をほったらかしにできる

エンジニアは忙しいですが、飯の種はたくさんあります。AIが仕事を奪うというよりか、AIを導入したことで発生する運用保守でさらに仕事が増えているようなこともあると思います。

お金を使うような時間もないし、案件は年中あるので何かお金を使う趣味に熱中していなければ、お金に困ることはなさそうです。

それが長期投資を可能にし、分散投資もできて、安定したポートフォリオを築くことができます。

## さいごに

私の15年間の投資の経験から、脳死でMicrosoftなどの超優良株を定期で買っておけば10年後に5倍から10倍になると思います。

エンジニアの方はNISAなどから始めて投資もやってみた方がいいかと思います。
Angular v18 toSignal()のrejectErrorsオプションの挙動

Angular v18で提供される `toSignal()` ユーティリティ関数には `ToSignalOptions` を渡すことができます。

https://angular.jp/api/core/rxjs-interop/toSignal
https://angular.jp/api/core/rxjs-interop/ToSignalOptions

`ToSignalOptions` の `rejectErrors` 指定によって挙動がどう変わるのか、試したことを記事に残します。

コードはこちらです。
https://stackblitz.com/edit/stackblitz-starters-a3lr3a?file=src%2Ffoo.component.ts

# toSignal()

なんらかの値が不定期に流れるストリームを例にします。
実際にはクリックやリスト選択などユーザーインタラクションを受け取るケースが想定できます。

```ts
service = inject(SomeService);
value = toSignal(this.service.observable$, { rejectErrors: false });
```

クリックでインタラクションを発生させて `signals` の値をテンプレートにレンダリングします。また、エラーハンドリングの擬似的な役割としてコンストラクタに `console.log()` を起きます。

```ts
@Component({
  template: `
    <button (click)='onNext()'>next</button>
    <button (click)='onError()'>error</button>
    <p>{{value()}}</p>
  `,
  ...
})
export class FooComponent {
  private readonly service = inject(SomeService);
  value = toSignal(this.service.observable$, { rejectErrors: false });
  
  constructor() {
    effect(() => {
      console.log(this.value());
    });
  }
  
  onNext(): void {
    this.service.next();
  }

  onError(): void {
    this.service.error();
  }
}
```

サービスは `next()` をコールするたびにカウントを流すシンプルなものです。

```ts
@Injectable({ providedIn: 'root' })
export class SomeService {
  private count = 0;
  private readonly subject$ = new Subject();

  get observable$() {
    return this.subject$.asObservable();
  }

  next(): void {
    this.count++;
    this.subject$.next(this.count);
  }

  error(): void {
    this.subject$.error(new Error('error'));
  }
}
```

# rejectErrors:false

```ts
toSignal(this.service.observable$, { rejectErrors: false });
```

この指定はデフォルトです。
`ToSignalOptions` を省略しても同じ挙動になります。

まず[1,2,3]を流します。

![1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102284/c1e0fd7d-5397-3fa4-299c-2d1a37f4239f.gif)

今度は[1,2,Error]を流します。

![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102284/10a8968c-78dd-fcc7-0375-fe54e4de8f4c.gif)

Errorの後、インタラクションが発生するたびに `signals` の読み取りで `console.log()` が実行されます。

エラーハンドリングの処理でモーダル表示などがあると、インタラクションが発生するたびに連続で表示され、意図しない挙動になるかもしれません。

# rejectErrors:true

```ts
toSignal(this.service.observable$, { rejectErrors: true });
```

[1,2,Error]を流します。

![3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102284/3331fb7f-c8b6-b704-232d-d0e9f07c2023.gif)

Errorの後にインタラクションが発生しても `console.log()` は実行されません。

エラーハンドリングが初回のみ実行され `signals` は最後の値を保持します。

# rejectErrors

Angularのドキュメントでは以下のように説明されています。

> Whether toSignal should throw errors from the Observable error channel back to RxJS, where they'll be processed as uncaught exceptions.

`rejectErrors` はストリームのエラーを `Observable` のエラーチャンネルに送出するデフォルトの挙動をリジェクトするか、という指定のようですね。

エラーをリジェクトというのが二重否定のようでイメージしづらかったのですが「エラーの伝搬を止める：TRUE or FALSE」と読み替えて理解できました。
LeetCode 1011. Capacity To Ship Packages Within D Days

## はじめに

勉強会でLeetCodeの問題を解いてきました。

https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/description/

## 問題文

A conveyor belt has packages that must be shipped from one port to another within `days` days.

The $i^{th}$ package on the conveyor belt has a weight of `weights[i]`. Each day, we load the ship with packages on the conveyor belt (in the order given by `weights`). We may not load more weight than the maximum weight capacity of the ship.

Return the least weight capacity of the ship that will result in all the packages on the conveyor belt being shipped within `days` days.

:::note warn
ホンマにこれが苦行。
LeetCodeって全編英語なんですよね・・・
スッと入ってこないので、だいたい条件を落としてしまう。
:::

### 要するに
与えられた`weights`配列を先頭から順番に`days`に収まるように切断する。
切断される最小値を求める

### 具体例
**Example 1:**
>Input: weights = [1,2,3,4,5,6,7,8,9,10], days = 5
Output: 15
Explanation: A ship capacity of 15 is the minimum to ship all the packages in 5 days like this:
1st day: 1, 2, 3, 4, 5
2nd day: 6, 7
3rd day: 8
4th day: 9
5th day: 10
>
>Note that the cargo must be shipped in the order given, so using a ship of capacity 14 and splitting the packages into parts like (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) is not allowed.


## 考え方

与えられた`days`で`weights`がさばけるのか。
**さばけるのであれば、1日あたりの積載量を小さく**していけばいい。

`days`でさばけるかどうかは置いといて、1日あたりの積載量について考える。

1日あたりの積載量の最小は、`weights`配列の最大値となる。Example 1だと$10$
1日あたりの積載量の最大は、`weights`配列の合計となる。Example 1だと$55$

つまり$10～55$で`days`でさばける最小値が答えとなる。
有限の線形になっているので、二分探索で求める。

## 解答例
```python:1011. Capacity To Ship Packages Within D Days
class Solution(object):
    def shipWithinDays(self, weights, days):
        # days以内でさばけるか
        def can_ship_within_days(capacity_a_day):
            spend_days = 1
            total_weight_a_day = 0
            for weight in weights:
                if total_weight_a_day + weight > capacity_a_day:
                    spend_days += 1
                    total_weight_a_day = weight
                else:
                    total_weight_a_day += weight

                if spend_days > days:
                    return False
            return True

        min_a_day = max(weights)
        max_a_day = sum(weights)

        # 二分探索
        while min_a_day < max_a_day:
            median = (min_a_day + max_a_day) // 2
            if can_ship_within_days(median):
                max_a_day = median
            else:
                min_a_day = median + 1
    
        return min_a_day
```

## おわりに
この問題は、読みかえるのが難しいです。
最終的に、有限の線形探索を思いつくほど経験値がないと痛感しました。