OmniAICreator/Qiita-1.07M huggingface dataset:
paizaラーニング問題集「【シミュレーション 2】perfect shuffle」を解いてみた

https://paiza.jp/works/mondai/b_rank_new_level_up_problems/b_rank_new_level_up_problems__perfect_shuffle

▼考え方

ポイントになる考え方1.～4.を以下に示します。
基本的には、問題文に書いてある通りにコードを書きました。

1.現在のトランプの山札を辞書T、perfect shuffle後をTSで示します。keyは山札の一番上(0)から何枚目のカードかを数値で示し、valueは絵柄のアルファベットとカードの数字をタプル示します。最終的にTSが答えです。以下に例を示します。

T[0] = ('S',1)
T[1] = ('S',2)
...
T[51] = ('C',13)

2.題意より、K=0(perfect shuffleが行われない)があり得るため、TをTSにコピーしておきます。

3.K>=1ならばperfect shuffleが行われます。以下のように、Tの内容をTSにコピーします。

TS[51] ← T[51]
TS[50] ← T[25]
TS[49] ← T[50]
TS[48] ← T[24]
TS[47] ← T[49]
TS[46] ← T[23]
...
TS[1] ← T[26]
TS[0] ← T[0]

4.K>=2(perfect shuffleが複数行われる)があり得るため、次のperfect shuffleに備えTSをTにコピーしておきます。

▼コード
```py
########## 処理0(準備) インプット,リスト,辞書の定義 ###########

K = int(input())

# a:トランプの山札の全枚数
a = 52

# 考え方1.
egara = ["S","H","D","C"]
T = {}
TS = {}
for i in range(a):
    if i % 13 == 0:
        j = 1
        
    T[i] = (egara[i//(a//4)],j)
    j += 1

# 考え方2.
TS = T.copy()

########## 処理1 perfect shuffleと、 perfect shuffle後の山札の出力###########

# 考え方3.
# k:TSのkeyをうまく調整するための変数
k = 0
for i in range(K):
    for j in range(1,a//2+1):
        TS[a-j-k] = T[a-j]
        TS[a-j-1-k] = T[a-(a//2+j)]
        k += 1
    k = 0

    # 考え方4.
    T = TS.copy()

for i in range(a):
    print(TS[i][0],"_",TS[i][1],sep="")
```
ipa形式iOSのアプリをiPhoneにインストールする方法

## 対象者
iOSアプリをビルドしたけど、ipa形式のアプリをどうやってiPhoneにインストールしたらいいのか悩んでいる人

## 方法1
一つ目の方法にして、おそらく結論。これ以外の方法を使うことはないと思う。

### 手順
- ipaファイルをAirDropでインストールしたいiPhoneに送信する
以上

## 方法2
iPhoneを有線でmacに接続し、Xcode経由でインストールする方法

### 手順
[Xcodeを使ってipaをiPhoneにインストールする](https://qiita.com/0ba/items/f0c52b73d40f0ac33d25)を参照
Gitコミットを一つ前に戻す方法

## 方法
以下２パターン。
両方とも、現在のコミットから1つ前のコミット（`HEAD^`）にリセットするコマンドですが、影響範囲が異なります。

### 1. `git reset --hard HEAD^`
- コミットを一つ前の状態に戻す
- コミット前の変更は全て破棄される（全てのファイルが一つ前のコミットの状態に戻る）


### 2. `git reset --soft HEAD^`
- コミットを一つ前の状態に戻す
- コミット前の変更したファイル（ステージングされたファイル）は保持される
※コミットをやり直したいとき（例えばコミットメッセージを修正したい場合）に便利

## まとめ
- **`--hard`**: すべての変更を破棄して、過去のコミットに戻す。
- **`--soft`**: コミットだけを取り消し、ステージングされた変更やワーキングディレクトリの変更は保持。
MainLayoutでイベントが発火しない問題の解決方法

## 現象
各コンポーネントにおいては、イベントが発火するのに対して、MainLayoutでイベントが発火しない件で少しハマってしまった。

## 原因
各コンポーネントでは、以下の定義を記載していたが、あまり意識していなかった。
きちんと一行一行理解して記載するのは基本中の基本なので反省……。

~~~ C#:各コンポーネント
@rendermode InteractiveServer
~~~

この記載によって、対話型レンダーモードのサポートが有効化される。
[公式ドキュメント](https://learn.microsoft.com/ja-jp/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0#render-modes)にもある通り、これを有効化しないと、「静的サーバー側レンダリング」となるため、当然ながらイベントは発火しない。

## 修正方法
今回は、サーバー側のレンダリングモードにする。
レイアウトにおいては、上記@rendermodeを定義できないので、「App.razor」または「Routes.razor」のどちらかに以下の定義を追加する。

~~~ C#:App.razor
<Routes @rendermode="InteractiveServer" />
~~~

~~~ C#:Routes.razor
@rendermode InteractiveServer
~~~

なお、どちらに追加した場合でも、最上位に定義することになるため、アプリケーション全体に適用され、各コンポーネントごとに定義する必要はなくなる。
応用情報　覚えられない単語メモ

応用情報の学習において、個人的に覚えられなかった用語のメモです。分野別にも難易度順にも整理されていません。思いついた順に書いてます。

・インシデント管理、問題管理の違い
インシデント管理:起こったインシデントを解決まで管理する。応急処置
問題管理:インシデントの原因を究明し、二度と起こらない対策を考える。根本解決

・ソートの種類
バブルソート:隣り合う要素の値を比較して、代償逆順になっていたら交換する。計算量:O(n^2)

単純選択法:未整列要素から最も小さい(大きい)要素を選択し、未整列部分の先頭の要素と入れ替える。O(n^2)

単純挿入法:未整列要素の先頭の要素を取り出し、その要素を整列済み要素の中に正しい位置で挿入する。O(n^2)

クイックソート:中間的な基準値を作り、基準値より小さい/大きいデータの区分に分割する。それぞれの区分で同じことを繰り返す。計算量:O(nlog2(n))

ヒープソート・未整列のデータを、各節の値に「親が持つデータ≦子が持つデータ」という関係の順序木を作り、配列で表現ヒープの根を最小値として取り出し、再構築する。この操作を繰り返す。計算量:O(nlog2(n))

マージソート:データ列を分割し、比較しながら併合する。計算量:O(nlog2(n))


・スタック領域とヒープ領域
スタック領域:プログラムの実行中のサブルーチン情報を格納しておく。サブルーチン終了後の戻りアドレスや局所変数を保存しておく。
ヒープ領域:プログラム上から動的に確保できるメモリ領域。動的にメモリの確保と開放を繰り返すのでガーベジが発生。


・ページインアウト
ページフォールト:処理に必要なページが主記憶に存在しないこと
ページイン:ページを主記憶に読み込むこと
ページアウト:主記憶から追い出して補助記憶に書き出すこと

・プリエンプティ
プリエンプティブ:OSの判断でタスクが切り替わる
ノンプリエンプティブ:プログラムの判断でタスクが切り替わる


・ロールバック、ロールフォワード
ロールバック:障害発生時、やりかけの処理を取り消す
ロールフォーワード:障害発生時、処理をやり直す

・SRAMとDRAM
SRAM:少量、高速、単価高い→キャッシュメモリ
DRAM:高集積化、単価安い→主記憶
Layout Parser を Windows で使う方法

# Layout Parser とは
ドキュメント画像を分析して、レイアウト（ここはテキスト、ここは画像、のような感じ）を検出してくれるやつです。
https://github.com/Layout-Parser/layout-parser

# この記事の位置づけ
Layout Parser をWindowsで使うのに、一筋縄ではいかない状態なのだが、その回避方法が断片的に散らばってたのでまとめてみる。
基本的なインストール方法は本家の[ここ](https://github.com/Layout-Parser/layout-parser/blob/main/installation.md)に書かれている。
Windowsへのインストールに関してはもごもごしている。

# Layout Parserのインストール
## LayoutParser ライブラリをインストールする
まずは、本体をインストール

```
pip install layoutparser
```

## Detectron2 レイアウト モデル バックエンドをインストールする
このDetectoron2をwindowsにインストールするのを躓いている人が多い（俺も）。
以下のサイトを参考に進めた。

https://note.com/tori29umai/n/n908f3483c1f4

### Install Cython and COCO Tools

```
pip install cython
pip install pycocotools
```

### Install Detectron2
detectron2を落としてくる。
```
git clone https://github.com/facebookresearch/detectron2.git
```

#### コードの修正
```detectron2/setup.py```の75～87行目あたりに以下を追記します。
(行番号は新しいバージョンでは変更される可能性があります)
```python
        if not is_rocm_pytorch:
            define_macros += [("WITH_CUDA", None)]
            extra_compile_args["nvcc"] = [
                "-O3",
                "-DCUDA_HAS_FP16=1",
                "-D__CUDA_NO_HALF_OPERATORS__",
                "-D__CUDA_NO_HALF_CONVERSIONS__",
                "-D__CUDA_NO_HALF2_OPERATORS__",
                "-allow-unsupported-compiler",  # この行を追加
            ]
        else:
            define_macros += [("WITH_HIP", None)]
            extra_compile_args["nvcc"] = [
                "-allow-unsupported-compiler",  # この行を追加
            ]
```

```detectron2/setup.py```の最後226～227行目あたりをコメントアウトする。
(行番号は新しいバージョンでは変更される可能性があります)
```python
    # ext_modules=get_extensions(),
    # cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},
```

インストールを実行する。
```
pip install -e detectron2
```

## iopath を修正する

このままでは、モデルを読み込む際にエラーが発生する。原因は```iopath```というライブラリのようです。

https://qiita.com/Kaiya_ppp/items/71c9c4911ca8c0d6cb18

iopathを落としてくる
```
git clone https://github.com/facebookresearch/iopath
```

#### コードの修正
```iopath/iopath/common/file_io.py```の924行目あたりを書き換えます。
(行番号は新しいバージョンでは変更される可能性があります)

```python
# filename = path.split("/")[-1]
filename = parsed_url.path.split("/")[-1] # 書き換え
```

インストールを実行する。

```
pip install -e iopath
```

## OCR対応

### tesseractをインストールする
最新のインストーラーをダウンロードしてきてインストールする。

https://github.com/UB-Mannheim/tesseract/wiki

### ```layoutparser[ocr]```をインストールする。
```
pip install layoutparser[ocr]
```
以下のサイトから言語ファイルを落としてくる。

https://github.com/tesseract-ocr/tessdata

日本語の場合、```jpn.traineddata```をダウンロードし、tesseractのインストールフォルダ（C:\Program Files\Tesseract-OCR\tessdata）に保存する。


以上

# まとめ
コンパイラとかパスとか、OS依存なところはまだまだなかなかな感じですね。
 
「自分が問題にぶつかって解決に10分以上使った内容は記事にして書く」という教えに従って書いてみました。ここ最近、ChatGPT等のおかげで10分以上はまることが少なくなりましたが、この記事で誰かのためになれば嬉しいです。
C# の 64bit アプリケーションで SendInput を使うときのワナ

## 背景
これまで C# を使って 32bit で作成していた Windows アプリケーションを 64bit に変更したら、キー入力が反映されなくなった。ログを追ってみると、どうも SendInput で送出しているキー入力が無視されているようだ。

調査に当たっては以下の記事が参考になったので、備忘として残しておく。

- [C#コンソールアプリでSendInputを扱いたい](https://ja.stackoverflow.com/questions/93631/c%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%81%A7sendinput%E3%82%92%E6%89%B1%E3%81%84%E3%81%9F%E3%81%84)
- [C# - SendInput(Windows API)によるキー入力のサンプル(32/64bit対応)](https://qiita.com/kob58im/items/ca3b61e72111dfbab8a8)

## 改修ポイント
32bit の時は、上の記事と同じように、SendInput に渡す構造体を次のように定義していた。

```csharp
    [StructLayout(LayoutKind.Explicit)]
    private struct INPUT
    {
        [FieldOffset(0)] public int type;
        [FieldOffset(4)] public MOUSEINPUT mi;
        [FieldOffset(4)] public KEYBDINPUT ki;
        [FieldOffset(4)] public HARDWAREINPUT hi;
    };
```

これだと、先頭の `type` と次の `mi`や`ki` との間が 4 バイトしか差がないことになるが、64bit アプリケーションでは、ここにパディングが入って 8バイトのオフセットが必要だった。

下の記事では、32bit/64bit 両方に対応するため、次のような定義になっている。
```csharp
    [StructLayout(LayoutKind.Sequential)]
    struct Input
    {
        public int Type;
        public InputUnion ui;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct InputUnion
    {
        [FieldOffset(0)]
        public MouseInput Mouse;
        [FieldOffset(0)]
        public KeyboardInput Keyboard;
        [FieldOffset(0)]
        public HardwareInput Hardware;
    }
```

もう一点、次の修正も必要だった。
```csharp
    [StructLayout(LayoutKind.Sequential)]
    private struct KEYBDINPUT
    {
        public short wVk;
        public short wScan;
        public int dwFlags;
        public int time;
//      public int dwExtraInfo;
        public IntPtr ExtraInfo;    // IntPtr に変更
    };
```

`int` から `IntPtr` に変更することで、バイト幅は 4バイトから 8バイトに増えるわけだが、その明確な理由は見つけられなかった。理由をご存じの方はご教示いただけると幸いです。
【Java】拡張for文と通常のfor文の違いを解説してみた

Javaのループ処理には、よく使われる 通常のfor文 と 拡張for文（"enhanced for loop"）の2種類があります。この記事では、それぞれの特徴や使い方、使い分けについて詳しく解説します。

# 通常のfor文とは？
通常のfor文は、インデックスを使って配列やリストの要素にアクセスするため、繰り返し処理を細かく制御することができます。具体的には、開始点・終了点・ステップを指定してループを回すことが可能です。

```java:基本構造
for (初期化; 条件; 更新) {
    // 繰り返し処理
}
```

```java:例
String[] names = {"shun", "yamashita"};
for (int i = 0; i < names.length; i++) {
    System.out.println("名前: " + names[i]);
}
```
上記の例では、インデックス i を使って配列 names の要素にアクセスしています。配列の長さまでインデックスを1ずつ増やしながらループを回し、各要素を順番に出力しています。


:::note 
通常のfor文のメリット
- インデックスを使って要素を細かく制御可能
- 特定の範囲やステップを自由に設定できる
- 逆順や飛ばしながら処理することも可能
:::

# 拡張for文とは？
拡張for文（"enhanced for loop"）は、Java 5以降で導入された新しいループの構文です。この構文は、コレクション（配列やリストなど）の全要素を1つずつ処理する際に便利です。通常のfor文に比べて、コードがシンプルで読みやすくなります。

```java:基本構造
コードをコピーする
for (要素の型 変数名 : コレクション) {
    // 繰り返し処理
}
```
```java:例
コードをコピーする
String[] names = {"shun", "yamashita"};
for (String name : names) {
    System.out.println("名前: " + name);
}
```
この例では、names 配列の各要素を順番に name 変数に代入し、ループ内でそのまま出力しています。インデックスを意識せずに、配列やリストの全要素にアクセスできるのが特徴です。

:::note 
拡張for文のメリット
- コードがシンプルで可読性が高い
- インデックスを気にする必要がない
- 全要素を順番に処理するのに最適
:::

# 通常のfor文 vs 拡張for文の比較
| 項目 | 通常のfor文 | 拡張for文 |
|:-----------|------------:|:------------:|
| インデックス | 必要        | 不要      |
| 柔軟性     | 高い     | 低い       |
| 使いどころ| 範囲指定や飛ばし処理など、細かい制御が必要な場合|          シンプルな全要素処理|
| 逆順処理  | 可能         | 不可能        |
| 可読性   |やや複雑       | シンプルで読みやすい       |

### 逆順処理		
		
例: 逆順でのループ
通常のfor文 では、インデックスを操作して逆順に処理が可能です。

```java:java
String[] names = {"shun", "yamashita"};
for (int i = names.length - 1; i >= 0; i--) {
    System.out.println("名前: " + names[i]);
}
```
拡張for文 では逆順処理はできません。順番に全要素を処理することに特化しています。

# どちらを使うべき？

### 通常のfor文 を使うべき場面

要素にインデックスでアクセスしたい場合
逆順や飛ばし処理など、細かい制御が必要な場合
ループ回数を自由に制御したい場合

### 拡張for文 を使うべき場面

全要素を順番に処理したい場合
シンプルで可読性の高いコードが求められる場合
インデックスを意識せずに書ける場合

# まとめ
拡張for文は、シンプルで全要素を順に処理する際に非常に便利です。一方で、インデックスを使った柔軟な制御が必要な場合は通常のfor文を使います。状況に応じて、これら2つのループ処理を使い分けることで、効率的で読みやすいコードを書くことができます。


参考リンク
公式ドキュメント - Java for文
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
素数を判定するのを作ってみよう！(前編)

素数判定機を作るとjsの基本的な機能がわかります！
作ってみましょう！

# どうやって作る？
### 素数とは何？
素数→1ではない割り切れる数がその数と1しかない数
素数ではないやつ→合成数と1

合成数は、1を含まないn×mを満たす数字(n,mは自然数)

...
入力される値がaとすると、
### a = 1 または aを割り切れる数が 2 ~ a-1 (自然数)にあれば素数ではない！
ということがわかります。コードを書いていきましょう！

```math
a = 1 またはa \equiv 0 \mod (2 	\sim a-1)_{自然数}
```

# 書いていこう
```
素数判定


a は 1?

　そうだったら素数ではない
 
　そうじゃなかったらこれやる
　　2からa-1まで　しらみつぶし(そのしらみつぶししてる数をiにする)
　　　aはiで割り切れる？
   　　そうだったら素数ではない
     　そうではなかったら素数の可能性がある

素数か素数じゃないかを出す
```
必要な関数とか計算とか書いていきます。
## javascriptの書き方
### 関数を作る
```js
function 名前(引数1,引数2,...){//引数は数学でいうf(x,y)のxとyのこと
    //コード
}

//素数は英語でPrimeという。　関数名をIs_primeとして使うとこうなる
function Is_prime(a){
    //コード
}
```
### 演算記号
```js
//足し算
  1 + 1
  n + 5
  n + n
//+4
+ 4
//引き算
  1 - 1
  n - 5
  n - n
//-4
- 4
//掛け算
  1 * 1
  n * 5
  n * n
//割り算
  1 / 1
  n / 5
  n / n
//割ったあまり
  1 % 1
  n % 5
  n % n

//混合
    6 / 2 + 4 * 2  //11 ちゃんと*(掛け算)や/(割り算)、%(割ったあまり)から計算される
    6 / (2 + 4) * 2// 2 ちゃんと括弧から計算される
    6 / ((2 + 4) * 2)//0.5 {} []ではなく、一律して()を使う
```
### 変数の扱い
```js
let 名前;//名前という名前の変数を作れる。
名前 = 数字とか文字とか;//名前という名前の変数に数字とか文字とかを入れる
let 名前 = 数字とか文字とか;//[let 名前]と[名前 = 数字とか文字とか]を合体したもの
名前 + 1;//名前という名前の変数に1を足したやつ
```
### ~が~なら~~する
```js
if(なんかの数字とか文字 条件 なんかの数字とか文字){
    //コード
}

if(a == 3){
    //aが3なら実行するコード
}

if(a - 8 == 3){
    //a-8が3なら実行するコード
}

if(a - 8 > 3){
    //a-8が3より大きいなら実行するコード
}

if(a % 8 == 3){
    //aを8で割ったあまりが3なら実行するコード
}

== 同じ
>  大きい
<  小さい
>= 以上
<= 以下
!= 同じではない
```
### ~が~なら~\~する　そうではないなら~\~する
```js
if(なんかの数字とか文字 条件 なんかの数字とか文字){
    //コード
}

if(a == 3){
    //aが3なら実行するコード
}else{
    //そうではない時に実行するコード
}

if(a - 8 == 3){
    //a-8が3なら実行するコード
}else{
    //そうではない時に実行するコード
}

if(a - 8 > 3){
    //a-8が3より大きいなら実行するコード
}else{
    //そうではない時に実行するコード
}

if(a % 8 == 3){
    //aを8で割ったあまりが3なら実行するコード
}else{
    //そうではない時に実行するコード
}
```
## コードを書こう！
```js
function Is_prime(a){//素数判定


    if(a == 1){
        //そうだったら素数ではない
    }else{
        //そうじゃなかったらこれやる
        //2からa-1まで　しらみつぶし(そのしらみつぶししてる数をiにする)
            //aはiで割り切れる？
                //そうだったら素数ではない
                //そうではなかったら素数の可能性がある
    }
    

//素数か素数じゃないかを出す
}
```
今日はここまで！！！　！！　！！　！！！！！！？

次回

https://qiita.com/yamina-Tjatmg-Gui-Creater/items/dc632e14fc676a76bfe0
M5S3)Servo(サーボ)で遊ぶ。(PWM)(M5Samp S3)


M5S3)Servo(サーボ)で遊ぶ。(PWM)(M5Samp S3)


Arduino Servo サーボモータ M5stack



x 過去ログ見よ!!
x 業務連絡　マルチメディア飲み会あるよ(絶滅メディア博物館)　なんのこと?
(いって、わかった事、ソフトがしょぼいとハードは、売れない)

x サーボを繋ぐ時は、必ず絶縁してください。
x サーボをパソコンに繋いだままやるとパソコンが破壊されます。
x モーター、コイル等、逆起電力で高電圧が発生する時の安全対策は、各自実行してください。
x 最低でも、試す時は、モバイルバッテリを使用してください。
x モータ系の力学装置系をなぜ、あまりやらないのかは、まず、物理的にお金がかかる、いろいろ、危険であるから


目的
旗を振る

nanoc6との違い

```c++

#define PWM_PIN 15

```



結果

![o_coq510.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/4e4536c1-b78d-ad51-74ce-a8c0d50324c5.jpeg)

![o_coq511.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/bc7031eb-d94e-4634-a152-ff65b1ce9698.jpeg)



プログラム



```c++


//PWM_ledcWrite_servo_S3_1


//インクルド
#include<Arduino.h>


//定義
//使い方　SOのマクロに0から180を設定すると
//PWMの間隔の値が返る
//        a = SO(i);
//        //a = SO(180-i);
#define V_0    500
#define V_180 2400
#define Width_0_180   (V_180-V_0)
#define Wavelength_1count (((1000000/50)*1024)/4096)
#define T_MIN ((V_0*1024)/Wavelength_1count)
#define T_MAX ((V_180*1024)/Wavelength_1count)
#define HH  ((T_MAX-T_MIN)*1024)/180
#define SO(AA) (T_MIN+(((AA)*(HH))>>10))

//PWMのポート、周期、分解度のビット数
#define PWM_PIN 15
#define FREQUENCY 50
#define RESOLUTION 12


//初期化
void setup() {

  //出力ピンの初期化
  pinMode(PWM_PIN, OUTPUT);

  // PWM設定（ピン番号, 周波数, 分解能）
  ledcAttach(PWM_PIN, FREQUENCY, RESOLUTION);
  
} //setup


//メインループ
void loop() {

  // DUTY比を指定して、ポートに対して実行 0°
  ledcWrite(PWM_PIN, SO(0));
  delay(2000); //2秒待つ

  // DUTY比を指定して、ポートに対して実行 180°
  ledcWrite(PWM_PIN, SO(180));
  delay(2000); //2秒待つ

} //loop


```
【Ruby】uniqは重複を除外するだけではないよ

# はじめに

こんにちは。ProgateでRubyを勉強したのち、「ゼロからわかるRuby超入門」でさらに学習を進めています。
この本で uniq メソッドについて学んだ際、配列の中の重複要素を除外する機能が紹介されていましたが、実はそれ以外にも興味深い機能があることに気づきました。今回は、その意外な機能について共有します。

Rubyを勉強し始めたばかりなので、Rubyに詳しい方にとっては当たり前のことかもしれませんが、この記事はRuby初心者向けということでお手柔らかにお願いします。

環境：Ruby 3.3.5

# uniqとは

`uniq` は配列から重複した要素を取り除いた新しい配列を返します。

```ruby:uniq1.rb
[1,1,1].uniq 
# [1]
[1,3,2,2,3].uniq
# [1,3,2]
```
ほかにも`uniq!`といった重複の削除を破壊的に行うコマンドもありますが、本題ではないので触れないでおきます

# uniqのもう一つの機能とは？

では以下のコードを見てどういう結果が出力されるか確認しましょう。
皆さんも一緒に考えてみてください

```ruby:uniq2.rb
p [1, 3, 2, "2", "3"].uniq { |n| n.to_s }
```

このコードでは、|n| n.to_s という部分が、各要素を文字列に変換しています。

さて、どんな結果が返ってくると思いますか？

答え
```ruby
p [1, 3, 2, "2", "3"].uniq { |n| n.to_s }
# [1, 3, 2]
```
えっ！？ 文字列ではなく元の数値が出力されています。

私はこれを`["1", "3", "2"]`と予想していましたが、なぜこのような結果になったのでしょうか？
実は、これには uniq の特別な性質が関係しています。

# uniq の重要な性質

`uniq`は、ブロック内で評価された結果に基づいて重複を除外しますが、最終的に元の要素が出力されるという性質を持っています。

具体的には、[1, 3, 2, "2", "3"] という配列に対して、`|n| n.to_s`で全ての要素が文字列に変換されるため、結果として ["1", "3", "2", "2", "3"] となります。

この時、`"2"`と `"3"` が重複しているので削除され、最終的に ["1", "3", "2"] という形になります。
ただし、実際に返される配列には、元の要素がそのまま返るため、結果は [1, 3, 2] になるのです。

もう一つ例を見てみましょう。


```ruby:uniq3.rb
p ["1", "3", "2", 2, 3].uniq { |n| n.to_s }
# ["1", "3", "2"]
```
これも同様に、`|n| n.to_s` によってすべての要素が文字列として扱われ、結果的に重複する要素が除外されます。
最終的な配列の要素は、元の配列に含まれていた ["1", "3", "2"] となります。

# まとめ
`uniq` メソッドは単に重複を除去するだけでなく、ブロックを用いて処理した結果に基づいて重複を除去し、元の要素を保持したまま返すという興味深い機能を持っています。
わたしもRuby初心者なので、Rubyをさらに深く理解するためにも、このような細かい挙動にも注目して学習を進めていきましょう！
IAM Identity Centerを使ったマルチアカウント運用。

## はじめに

初投稿の記事で「IAM Identity Center」を扱いましたが、CLIメインのお話でした。

今回は少し別視点でマルチアカウントの場合はどんな感じになるのかを検証してみました。

## IAM Identity Centerの設定方法

### OrganizetionsへのAWSアカウント追加

紐づけたいAWSアカウントは事前にOrganiztionsへの登録が必要です。

今回は`mori`というアカウントを追加しておきました。

![スクリーンショット 2024-10-11 5.19.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/7c76b7fa-d593-50a8-aa01-e69cb240ec83.png)

### アカウントとの紐付けのみ

紐づけたいアカウントを選んで、`ユーザーまたはグループを割り当て`を選択。

![スクリーンショット 2024-10-11 5.19.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/d6690b51-b734-f1a2-c1f6-8fbb5822034e.png)

割り当てるIAM Identity Center側で作ったユーザ（またはグループ）を設定。

![スクリーンショット 2024-10-11 5.20.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/b8f3971e-01eb-57f2-d79c-5fcb8a2beed8.png)

紐づける権限セットを付与。

![スクリーンショット 2024-10-11 5.20.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/814d1722-54a2-e6ca-bb6e-11fbf18d5456.png)

これだけで、AWS access portalからログインできるようになります！

![スクリーンショット 2024-10-11 5.22.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/3ba75962-2979-d755-e800-641f9266546c.png)

## ショートカット機能

AWS access portalには、ショートカット作成機能というものがありました。

これは特定のアカウントで、特定のサービスへ直接アクセスできるURLを作成する機能の模様。

![スクリーンショット 2024-10-11 5.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/b82fc8e9-ce88-2f7f-99f2-307cf1c521e3.png)

本番系アカウントのCloudWatch Logs等、よく使うような場所をショートカットに入れておくと便利そうです！

## CLIも。

マルチアカウント状態でCLIのセットアップもしてみました。

`aws configure sso`を使った設定時にどっちのアカウントを使うかの選択肢がでるようになってました。

```console
% aws configure sso             
SSO session name (Recommended): test
SSO start URL [None]: https://d-xxxxxxxxxxx.awsapps.com/start/#
SSO region [None]: ap-northeast-1
SSO registration scopes [sso:account:access]: sso:account:access
Attempting to automatically open the SSO authorization page in your default browser.
If the browser does not open or you wish to use a different device to authorize this request, open the following URL:

https://device.sso.ap-northeast-1.amazonaws.com/

Then enter the code:

KZQC-MTZG
There are 2 AWS accounts available to you.
> mori, mori.xxxxx@gmail.com (111111111111)               
  chelky, doanxxxxx@gmail.com (222222222222)  
```

それぞれのアカウントで別のprofile名を付けておき、それぞれで`aws sso login`をしていると、同時に複数アカウントへの操作が可能です！

## IAM Identity Centerが使えないときは？

### Privateウインドウ

会社のアカウント等で、IAM Identity Centerが使えないときは、プライベートウインドウ（シークレットウインドウ）がおすすめです！

一つのブラウザで複数のアカウントに同時ログインできるので、マルチアカウントでログインし直したりする必要がなくなります！

## まとめ

機能は概ね事前に知っていましたが、いざ構築してみるととっても便利！

でも、今の職場のアカウントだと使えなくて、業務改善できないのは少し残念。
別シートへのリンクテキストをシート名に更新するGAS

# はじめに
最近、業務でスプレッドシートを使って資料や仕様書を作成する機会が増え、便利なスクリプトをいくつも書くようになりました。しかし、新たに気づいた不便な点がありました。それは、目次を作成した後にシート名が変更された際に、目次のシート名が自動で更新されないという問題です。

たとえば、最初に目次を作り、シート名を設定しても、仕様変更などでシート名が変わると、目次に反映されません。この問題を解決するために、今回はシート名の変更を目次に反映させるスクリプトを作成しました。

# コード
```javascript
function myFunction() {
  const activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  // シート一覧
  const sheets = activeSpreadsheet.getSheets();

  function getSheetByGid(gid) {
    var sheet = null
    for (var i = 0; i < sheets.length; i++) {
      if (sheets[i].getSheetId() == gid) {
        sheet = sheets[i]; // 一致するシートを返す
        break
      }
    }
    return sheet; // 一致するシートがない場合はnullを返す
  }

  // 選択しているセルを取得
  const activeSheet = activeSpreadsheet.getActiveSheet();
  const selection = activeSheet.getActiveRange(); // 選択しているセルの範囲を取得
  const numRows = selection.getNumRows();
  const numCols = selection.getNumColumns();

  for (let row = 1; row <= numRows; row++) {
    for (let col = 1; col <= numCols; col++) {
      
      const cell = selection.getCell(row, col);
      
      
      var link = cell.getRichTextValue()?.getLinkUrl();
      var link_match = link?.match(/#gid=([0-9]+)/) 
      if(link && link_match){
        // リンクがあり、そのリンクと同じシートが存在するか
        var gid = link_match[1]
        var targetSheet = getSheetByGid(gid)
        if(targetSheet){
          var richTextBuilder = cell.getRichTextValue().copy()
          var richTextLink = cell.getRichTextValue().getLinkUrl()
          richTextBuilder.setText(targetSheet.getName()).setLinkUrl(richTextLink)
          cell.setRichTextValue(richTextBuilder.build())
        }
      } else {
        // リンクは一致しないが、同じ名前のシートが存在する時
        var cellText = cell.getRichTextValue() ? cell.getRichTextValue().getText() : cell.getValue()
        var targetSheet = activeSpreadsheet.getSheetByName(cellText)
        if(targetSheet){
          var richTextBuilder = cell.getRichTextValue().copy()
          richTextBuilder.setText(targetSheet.getName()).setLinkUrl("#gid="+ targetSheet.getSheetId())
          cell.setRichTextValue(richTextBuilder.build())
        } else {
          // 処理されなかったシートの一覧は名前が被らないシートを先頭に作成してリストで出力する予定(未実装)
        }
      }
    }
  }

}

```

# 使い方
下記画像のように目次シート内で範囲選択をした状態で上記のコードを実行します。
![スクリーンショット 2024-10-12 015121.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821767/288c8627-7989-2033-2892-225a34734cb4.png)


# 処理内容
行っている処理は下記の2つです。
- すでにリンクが設定されており、リンク先のシートが存在するセルはテキストをシート名に変更(最新のシート名を適用)
- セルに入力されているテキストのシート名が存在している場合はリンクを設定(リンクの設定)

# おわりに
扱うシート数が少しなら困りませんが数十以上のシートを扱うようになった場合管理がとても難しくなります。今回のGASを使うことで目次の管理がかなり楽になると思います。
使用用途は限定的ですがおすすめのスクリプトです。

今回のようなスクリプトに需要がありそうならシート一覧の目次を自動生成するスクリプトなども紹介しようかなと思います。

今回作ったスクリプトは少し説明が難しかったので分かりづらかったらすみません。
ejsを使うときに必須コード！

## 書こうと思った理由



EJS を使っているときに、`res.render()` ではなく `express.static()` でやっていたから、ほかのコードなどでなぜかダウンロードされる現象を少なくしたいから。

`res.render()`は、例えばこんな風に使います：

```js
app.get('/hoge', (req, res) => {
  res.render(path.join(__dirname, '/src/hoge.ejs')); // <--ここ
});
```

**これがポイント！**

1. **`res.render('テンプレート名')`**
   - `'hoge'` は、テンプレートファイルの名前です。EJS では、通常 `.ejs` という拡張子を持つテンプレートファイルを使用します。この場合だと `src/hoge.ejs` というファイルが読み込まれます。

2. **データを渡す**
   - テンプレートにデータを渡したい場合、第二引数にオブジェクトを渡すことができます。この例では、`{ title: 'hogehoge' }` というオブジェクトが渡されて、テンプレート内で `title` として使えます。

例えば、`hoge.ejs` ではこんな感じで使えます：

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><%= title %></title>
</head>
<body>
  <h1>Welcome to the hoge Page!</h1>
</body>
</html>
```

上記のコードで `<%= title %>` は、`res.render()` で渡された `title` の値（この場合は「hogehoge」）が挿入されます。

**注意すること！**

- テンプレートファイルのパスが正しいか確認！
  - 例えば、EJS のデフォルトでは `src` ディレクトリにテンプレートが保存されるので、`src/hoge.ejs` というようにディレクトリ構造があっているか確認しましょう。
  
- `res.render()` でテンプレートファイルが見つからない場合、エラーメッセージが表示されるので、その際はファイルの場所や名前をもう一度チェックすることが大事です！

これで `res.render()` が少しでも身近に感じられると嬉しいです。

## node.jsサーバー起動用コード

```js
const express = require('express');
const path = require('path');
const app = express();
const PORT = 3000;


// 静的ファイルの提供
app.use(express.static(path.join(__dirname, 'public')));
app.set('view engine', 'ejs');

// ルート設定
app.get('/hoge', (req, res) => {
  res.render(path.join(__dirname, '/src/hoge.ejs')); // <--ここ
});

// ポート指定
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
```
http://localhost:3000 行くと表示されます。
terraformのflattenを使ってネストのオブジェクトを取り扱う


## flatten
ネストしていない通常のオブジェクトであれば、`for_each`でループさせることで各要素にアクセスできる。だけども、オブジェクトがネストしている場合、二重ループなどができない（難しい）のでその場合などに使える。

## サンプル
下記の場合、pilotの部分がネストのオブジェクトになっている。
pilotを直接for_eachで取り扱うことができないので、一度local変数にflatten化した中間リソースを持つようにする。
なお、flattenはリストにのみ対応しているので、forでリストにしている。

```main.tf
locals {
  flattened_settings = flatten([
    for machine_key, machine_number in var.settings : [
      for pilot_key, pilot_value in machine_number.pilot : {
        machine_key = machine_key
        pilot_key   = pilot_key
        pilot_name  = pilot_value.name
        pilot_stage = pilot_value.stage
      }
    ]
  ])
}

output "flattened_resources" {
  value = local.flattened_settings
}
```

```variables.tf
variable "settings" {
  type = map(object({
    name = string
    pilot = map(object(
      {
        name  = string
        stage = number
    }))
    }
    )
  )
}
```


```terraform.tfvars
settings = {
  x131 = {
    name = "calamity"
    pilot = {
      orga = {
        name  = "orga subnac"
        stage = 2
      }
    }
  }
  x252 = {
    name = "forbidden"
    pilot = {
      shani = {
        name  = "shani andras"
        stage = 4
      }
    }
  }
  x370 = {
    name = "raider"
    pilot = {
      crot = {
        name  = "crot buel"
        stage = 4
      }
    }
  }
}

```

実行結果

```sh
Outputs:

flattened_resources = [
  {
    "machine_key" = "x131"
    "pilot_key" = "orga"
    "pilot_name" = "orga subnac"
    "pilot_stage" = 2
  },
  {
    "machine_key" = "x252"
    "pilot_key" = "shani"
    "pilot_name" = "shani andras"
    "pilot_stage" = 4
  },
  {
    "machine_key" = "x370"
    "pilot_key" = "crot"
    "pilot_name" = "crot buel"
    "pilot_stage" = 4
  },
]
```


## 参考
https://developer.hashicorp.com/terraform/language/functions/flatten
ブレゼンハムのアルゴリズムをかなり噛み砕いて理解する

ブレゼンハムのアルゴリズムが何で上手くいくかわからなくて悩んだので、備忘録として導出をまとめました。

## ブレゼンハムのアルゴリズムとはなんですか？

ブレゼンハムのアルゴリズムは、コンピュータで直線を高速に描画するためのアルゴリズムです。浮動小数点演算や複雑な乗算を使わずに整数のみで計算を行うため、高速で計算が可能です。

# 擬似コード

```c
// 傾きが0以上1未満のとき用
void drawLine(int x0, int y0, int x1, int y1){
	int x = x0;
	int y = y0;
	int e = -(x1-x0);
	while(x<=x1){
		drawPoint(x,y);//(x,y)のピクセルを塗りつぶす関数
		x++;
		e += 2*(y1-y0);
		if(e >= 0){
			y++;
			e -= 2*(x1-x0);
		}
	}
}
```

# 何をやっているの？
やりたいのは、始点から終点までの線分を連続した整数座標で近似することです。

始点$(x_0,y_0$)から終点$(x_1,y_1)$を繋ぐ直線の式は、

$$
\frac{x-x_0}{x_1-x_0}=\frac{y-y_0}{y_1-y_0}
$$

これを一次関数の形に書き直すと、

$$
y= \frac{y_1-y_0}{x_1-x_0} (x-x_0)+y_0
$$

ここで、傾きが$\frac{y_1-y_0}{x_1-x_0}$であることに注目してください。

$x$を1増やすと$、y$は$\frac{y_1-y_0}{x_1-x_0}$増加します。これを繰り返すと、

$(x_0,y0)$→$(x_0+1,y_0+\frac{y_1-y_0}{x_1-x_0})$→$(x_0+2,y_0+2\cdot \frac{y_1-y_0}{x_1-x_0})$→…

と増加していきます。$x$の方は既に整数なので、$y$の方を四捨五入すれば、いい感じに整数値で近似できそうですね、

ただ、この方法には問題があります。

傾きを何度も足すことで、誤差が大きくなってしまうことです。

そこで、その代わりとして、$y$を四捨五入した値$y’$と、真の値との誤差$e=y-y’$を保持することにします。

$e$を誤差として初期値を0にし、$\frac{y_1-y_0}{x_1-x_0}$ずつ増加させていき、0.5を超えたら$y$を1増やして$e$を1減らすことで、四捨五入を再現します。

こうすれば、$y’$は整数だし、-$0.5<e<0.5$なので、加算を繰り返しても誤差はそれほど大きくなりません。

このアルゴリズムで擬似コードを書いてみます。

```c
void drawLine(int x0, int y0, int x1, int y1){
	int x = x0;
	int y = y0;
	int dx = x1 - x0;
	int dy = y1 - y0;
	double dydx = (double)dy/dx;
	double e = 0.0;
	while(x<=x1){
		drawPoint(x,y);//(x,y)のピクセルを塗りつぶす関数
		x++;
		e += dydx;
		if(e >= 0.5){
			y++;
			e -= 1.0;
		}
	}
}
```

さて、先ほどは誤差がそれほど大きくならないと言いましたが、実はこのコードにもまだ問題があります。

$e$に浮動小数点を使っていることによる誤差です。できれば整数だけで計算を行いたいです。

そこで、誤差$e$に$2(x_1-x_0)$をかけた値を代わりに保持します。
そうすると、eに加算する値は$\frac{y_1-y_0}{x_1-x_0}\cdot 2(x_1-x_0) =2(y_1-y_0)$になり、
条件式は$e >= 0.5 \cdot 2(x_1-x_0)=x1-x0$になり、$e$をint型で扱えるようになります。

このやり方に書き換えたコードが以下です。

```c
void drawLine(int x0, int y0, int x1, int y1){
	int x = x0;
	int y = y0;
	int e = 0;
	while(x<=x1){
		drawPoint(x,y);//(x,y)のピクセルを塗りつぶす関数
		x++;
		e += 2*(y1-y0);
		if(e >= (x1-x0)){
			y++;
			e -= 2*(x1-x0);
		}
	}
}
```

さらに、$e$の初期値を$-(x1-x0)$にすることで、条件分岐をシンプルにしましょう。

```c
void drawLine(int x0, int y0, int x1, int y1){
	int x = x0;
	int y = y0;
	int e = -(x1-x0);
	while(x<=x1){
		drawPoint(x,y);//(x,y)のピクセルを塗りつぶす関数
		x++;
		e += 2*(y1-y0);
		if(e >= 0){
			y++;
			e -= 2*(x1-x0);
		}
	}
}
```

これで、上記の擬似コードが得られました。

# 参考

[https://ja.wikipedia.org/wiki/ブレゼンハムのアルゴリズム](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%AC%E3%82%BC%E3%83%B3%E3%83%8F%E3%83%A0%E3%81%AE%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0)

https://mmxsrup.hatenablog.com/entry/2019/06/24/193919
AndroidやLinuxでも全デバイスを連携させよう！【Tailscale・KDE Connect・(NextDNS)】

:::note warn
Cloudflareではうまく動いていなかったため修正しました
:::

# 手持ちデバイスを全て連携させたい！

- ラップトップ (NixOS)
- デスクトップ (NixOS)
- スマホ1 (Android)
- スマホ2 (iOS)
- タブレット (Android)

以上が私の使用しているデバイスですが、

「デバイス間でファイルを送るのに毎回 Discordや Google Drive を経由させるのはもう嫌だ！」
「Apple みたく全部同期してくれ！」

そんな日頃の夢をようやく叶えました。

# KDE Connect

まずは KDE Connect です。主に Linux-スマホ間の連携のためのソフトで、

- Linux 版
- Android 版
- iOS 版

があります。iOS ではかなり機能が制限されてしまいますが、

- 再生中のメディアの操作やボリューム操作
- 通知の同期
- クリップボードの同期
- 画像やその他ファイルの送受信
- マウスポインタの送信 
- その他様々な機能

などをローカルネットワーク経由で行えます。Linux-Android 間だけでなく、Linux-Linux 間や Android-Android 間でも使えます。

ただし、このままではデバイスを同じWi-Fiに繋げている時しか使えません。しかも、大学の学内 LAN では UDP Broadcast がブロックされている他、IP アドレスもすぐ変わるため、まともに使えません。この制限を克服するため、全デバイスを同じ VPN に接続する必要があります。

# Tailscale

無料枠の広すぎる VPN サービスです。超簡単にプライベートネットワークを構築・接続できます。

全デバイスを Tailscale に接続し、振られたIP アドレスを接続先として KDE Connect で入力するだけです。これでどのデバイスがどこにいても同期することができます。

ポートをグローバルに開放しなくてもsshできたり、Taildrop というファイルのシェア機能もあり、Tailscale だけでも結構便利です。

# DNS Filtering

さて、デバイス間連携は既にできたのですが、個人的に発生した問題があります。それは、

「**VPN を Cloudflare Zero Trust からTailscale に変えた結果、DNS フィルタリングが効かなくなった**」

ことです。Android 版 Chrome は拡張で広告ブロッカーを入れることができないため、これはかなり致命的です。

ちなみに Cloudflare Zero Trust はCloudflare でドメインを買っていると使えるサービスで、私は広告ブロックのための VPN として利用していました。(Zero Trust のVPNは、 Tailscale とは異なり、Tunnel を通さないとお互いのデバイスにアクセスできないため、KDE Connect と併用することはできません。)

~~色々と調べた結果、Tailscale の Public Name Server として Zero Trust を指定できれば良いのではないかという結論に至り、Zero Trust → Gateway → DNS Location から DNS サーバーのIPv6アドレスをTailscale の DNS → Public Name Server に指定し、Override Local DNS にチェックをつけたところ、正しく広告をブロックできるようになりました。めでたい~~

追記: どうやらCloudflaeの方はDoHでないとDefault Locatoinが機能しないようで、結局NextDNSのDNSサーバーをネームサーバーに設定しました.Cloudflare、かゆいところに手が届かないがち...

# まとめ

- KDE ConnectとTailscaleで手持ちのデバイスを全部連携できた
- 地味にネックだった広告ブロッカーとTailscaleの併用がうまく行った

それではみなさん快適なLinuxライフをお楽しみください！
オムライス指数 LINE Botのスピードアップ！

こんにちは、ただしです。
オムライス探索家兼エンジニアです。
クラウドファンディングのリターンとして、皆さんにご提供するアプリ『オムクエ』を現在開発中です。

https://camp-fire.jp/projects/778439/view?list=project_instant_search_results

## オムライス指数について
これは、オムクエで最も重要な要素で、ある場所周辺で昔ながらのオムライスを出す店が存在する確度を示します。

その計算方法や実装について、こちらの記事で解説しています。

https://qiita.com/tadokuno/items/377c9e301368b738ba45

## 時々Botが何も返答しない
特に、大きな駅など周辺にお店がたくさんある駅を指定した際に、リプライが無い場合があります。

原因は、使用しているNetlifyのタイムアウトだと分かっています。

お店の情報を取得するのにGoogle MAPのNearby Searchを使っているのですが、一回の呼び出しで20件まで取り出せます。一回呼び出しした後は、2秒ほどウエイトを入れないと、エラーになることが分かっています。


オムライス指数の計算には、喫茶店と町中華で検索を掛けており、その処理のためのウエイトは最大8秒になってしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212141/3d0fd3bc-24d9-31bc-596f-7cdc053aec9f.png)

さらに、OpenAIにも聞いているので、こういった外部のAPI呼び出しを完了するのに10秒以上掛かる場合があります。

netlifyは約10秒でタイムアウトとなって処理を終了してしまうので、Botへのリプライができなくなります。

オムライス指数に必要なパラメーターは今後増やす予定なので、この状況は無視できません。

## 呼び出し順序を変更する

最初に作成したロジックでは、以下のソースのように、まずGoogleのAPIを呼び出して、その処理が完了してから、OpenAIのAPIを呼び出していました。

```javascript
// Google MAP APIの呼び出し
const result = await getOmuIndex(stationName);

// result を使った処理
...

// OpenAI APIの呼び出し
const result2 = await calculateOmuIndex(stationName);

// result2を使った処理
...
```
Google APIを呼び出している```getOmuIndex()``` の呼び出しが完了するまで待って、OpenAI APIを呼び出している```calculateOmuIndex()```を呼び出しているので、これらの処理はシーケンシャルに実行されるので、実行時間は足し算となります。

これを、以下のように変更しました。

```javascript
// Openai API の呼び出し
const openaiPromise = calculateOmuIndex(stationName); 

// Google MAP API APIの呼び出し
const result = await getOmuIndex(stationName); 

// result を使った処理
...

const result2 = await openaiPromise;

// result2を使った処理
...
```
こうすることで、OpenAI APIの呼び出しと並行して、Googleの呼び出しを行うことができるようになりました。

ちょっとした工夫でしたが、この修正を行った後は、Botがリプライを返さないということがなくなったので、効果が出たことが確認できました。

今後パラメータが増大したときには、10秒以上必要になってしまうことが考えられるので、根本的な解決にはなっていませんが、当面はこれで凌いで、UIの作成など動くβ版を作ることを優先しようと考えています。

これをご覧の皆さんで、解決するアイデアを思い付いた方は、ぜひコメントをお願いします。

以上
[iOS] [リモート通知] APNSサーバーの認証処理を実装するときに役立ったツール

# 目的
iOSでリモートプッシュ通知を実装する際、APNSサーバー（Appleのプッシュ通知サーバー）にリクエストを送るための認証処理で苦労したので、その時に役立ったツールを紹介します。この記事は備忘録を兼ねており、同じ問題に直面している方々にとっても参考になれば幸いです。

# APNSサーバーでの認証について
リモートプッシュ通知を送るためには、APNSというAppleのサーバーに対してHTTPSリクエストを送る必要がありますが、リクエストがどのアプリのものかを識別するため、認証が必要です。不正なリクエストやなりすましを防ぐためにも重要なステップです。

Appleが提供している認証方法は以下の2つです：
- [証明書による認証](https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns)
- [トークンベースの認証](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns)

この記事では、トークンベースの認証に関して役立つツールを紹介します。
リモートプッシュ通知の全体的な仕組みについては、以下の記事が非常に分かりやすいので参考にしてください。

https://zenn.dev/chocoyama/articles/5b07eead5ae0aa

# トークンベースの認証でのサーバー実装で困ったこと
Appleのドキュメント通りにJWTトークンを生成しましたが、生成されたトークンが正しく有効であるかを確認する方法が分からず、APNSに正しくリクエストを通すのに苦労しました。以下にJWT生成に関するAppleのドキュメントを参考として貼っておきます。

https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns#Create-and-encrypt-your-JSON-token

参考までに実際に実装したサーバーのリポジトリが以下になります。

https://github.com/stotic-dev/FreTreBackEnd

# JWTトークンのバリデーションツール
本記事の主題である、JWTトークンの検証に役立つツールを紹介します。Appleが公式に提供しているもので、簡単に使用できます。以下の手順を参考にしてください。

①Xcodeにて対象のTARGETのPushNotificationのcapabilityから`Push Notification Console`ボタンを押下します。
![スクリーンショット 2024-10-11 23.41.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/062c9f49-ed0c-27c2-d273-7432b646353a.png)

②Apple Developerのサインインが完了すると下記画面が表示されます。
右上の赤枠箇所が意図したBundle IDになっていることを確認して、上部タブのToolsボタンを押下します。
![スクリーンショット 2024-10-11 23.44.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/1add0949-1630-5931-4726-1266814c7c4d.png)

③JWT Validatorという画面があるので、そこで赤枠のボックスに生成したJWTトークンを入力すると検証してくれます。
![スクリーンショット 2024-10-11 23.46.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/f9393abf-373a-80bf-f59b-613ce24f5d4c.png)

④検証に失敗した例として、以下のようになります。
ここでは"iat"が古いと言われているので、"iat"の時間を現在時刻にすることで検証が成功します。
検証が成功した場合は緑色のチェックマークが表示されるはずです。
![スクリーンショット 2024-10-11 23.49.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/6516da6b-a5f0-873f-460f-4f385629217d.png)

上記手順でJWTトークンの検証が手軽に行えました！

# おわり
以上がAPNSサーバーの認証処理を実装するときに役立ったツールの紹介でした。
今回はJWTトークンのバリデーションツールの紹介でしたが、上記キャプチャの画面にもある通りJWTの生成やDevice Tokenのバリデーションツールも同梱されているので、今後こちらも機会があれば使用してみたいです。
JavaScriptにおけるデータ型を意識した最適なコーディング習慣

まず初めに、JavaScriptでは、TypeScriptやC#のように型の概念がそこまで厳密ではありません。
しかし、データ型を意識することでコードのバグを防ぎ、可読性の高いコードを書くことができるので、複数人で行うプロジェクトでは非常に重要になります。
こちらのブログでは、データ型を明確にすることで得られるメリットを解説します。

### データ型とは？
型とは、データの種類のことです。例えば、数値型や文字列型などがあります。
### JavaScriptで扱う基本的なデータ型
今回は、特に使用例の高い５つに絞って説明します。
#### １．String(文字列型)
特徴：Stringは、テキストデータを扱うためのデータ型であり、シングルクォートやダブルクォートで定義されます。
  使用例：下記のコードでは、message変数の中の文字をHTML内で使用しています。
 ```
<template>
  <div>
    <h1>{{ message }}</h1>  <!-- message変数をHTML内で使用 -->
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Nuxt.js!'  // 文字列型
    };
  }
}
</script>
  ```
#### ２．Number(数値型)
  特徴：数値を扱うデータ型です。加減乗除などの数学的操作に使用されたり、レスポンスの値を設定する際に使用されたりします。
   使用例：下記のコードでは、ageInFiveYears()という関数が任意の場所で呼び出されることで、25 + 5が実行されます。
   ```
   export default {
  data() {
    return {
      age: 25,  // 数値型
    };
  },
  methods: {
    ageInFiveYears() {
      return this.age + 5;  // 数値の演算
    }
  }
};
```
#### ３．Boolaen(真偽値型)
特徴：true,falseの２つの値のみ持っているデータ型です。条件式やフラグとして使用されます。
使用例：１．文字列型と組み合わせて使用してみます。
buttonをクリックすると、toggleLogin()関数が呼び出され、isLoggedInがtrueに変更されます。trueのときのみmessageが表示されるようになります。
```
<template>
  <div>
    <button @click="toggleLogin">
        {{ isLoggedIn ? 'Logout' : 'Login' }}
    </button>
    <div v-if="isLoggedIn">
      <h1>{{ message }}</h1> 
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Nuxt.js!',  // 文字列型
      isLoggedIn: false  // 真偽値型
    };
  },
  methods: {
    toggleLogin() {
      this.isLoggedIn = !this.isLoggedIn;  // true と false を切り替える
    }
  }
}
</script>
```
#### ４．Object(オブジェクト型)
特徴：キーと値をペアでデータを保管でき、かつそのデータを複数で１つの変数にまとめる際に使用します。Objectは、{}でデータを保管します。
使用例：初期表示で画面には、太郎、30歳で表示されます。更新ボタンをクリックすることで、updateUser()が実行され次郎、28歳に更新されます。
```
<template>
  <div>
    <h1>個人名簿</h1>
    <p>氏名: {{ user.name }}</p>
    <p>年齢: {{ user.age }}</p>

    <!-- ユーザー情報の更新ボタン -->
    <button @click="updateUser('次郎', 28)">更新</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // オブジェクト型のuser変数を作成
      user: {
        name: '太郎',
        age: 30
      }
    };
  },
  methods: {
    // userオブジェクトのプロパティを更新するメソッド
    updateUser(newName, newAge) {
      this.user.name = newName;
      this.user.age = newAge;
    }
  }
}
</script>
```
#### ５．Array（配列型)
特徴：こちらのデータ型も複数のデータを保管できます。インデックス番号を使用してアクセスすることができます。[]でデータを保管します。
使用例：data配列の中に複数のオブジェクトを格納するのことができます。今回は、data配列の中にnameとageを保管しています。checkName()関数では、data配列の0番目のnameが太郎であるかどうかを判定しています。
```
export default {
  data() {
    return {
      data: [
        { name: '太郎', age: 25 },
        { name: '花子', age: 22 }
      ]
    };
  },
  methods: {
    checkName() {
      if (this.data[0].name === "太郎") {
        console.log('0番目の名前は太郎です');
      } else {
        console.log('違う名前です');
      }
    }
  }
};
```
Arduinoで豆電球を点滅させる方法

電子工作の初歩的な取り組みとしてLEDをチカチカ点滅させる、通称”Lチカ”があります。このLチカは、LEDをチカチカさせるので、略してLチカと呼んでいます。
このLチカはなぜLEDでやる必要があるのでしょうか？考えたことはありますか？電気を流して光るものと言えば、LEDのほかに豆電球を思い浮かべないでしょうか？
その豆電球、実はマイコンボードで点滅させようと思うと面倒なことが発生します。具体的には、マイコンボードでは豆電球を光らせるほどの電流値が出せないのです。
そのことについて詳しく書いたのがこの記事です。
https://www.hobbyhappyblog.jp/why-does-l-chica-use-leds
面倒と書きましたが、もちろん正しいやり方をすれば、豆電球もLEDと同様に点滅させることは可能です。
LEDのかわりに豆電球でLチカですね。この方法についても詳しく書いています。電流が足りないので、別のところから大きな電流をスイッチングして持ってくることで豆電球を光らせることが可能です。
https://www.hobbyhappyblog.jp/blinking-a-miniature-light-bulb-using-arduino-uno-r4-and-mosfet
【PWM出力】ArduinoでふんわりLEDを点滅させる方法

Arduinoをはじめとするマイコンボードには、1か0(HIGHかLOW)の出力以外にも、疑似的に0と1の間の出力ができます。
それを実現しているのがPWMという機能です。
PWMとは、Pulse Width Modulationの略です。
アナログ値のように出力が変えられるように見えますが、
実際にはHIGHになっている時間を調整することによって、疑似的に電圧が下がっているように見せています。
LEDを点滅させるのに、PWM出力を使ったのがこちらの記事です。
https://www.hobbyhappyblog.jp/explaining-how-to-softly-light-leds-with-arduino

また、PWMの原理についてオシロスコープで波形を可視化したときの記事がこちらです。
https://www.hobbyhappyblog.jp/pwm-frequency-arduino
Tauri 2.0でダイアログを使いたいがNode.jsはインストールしたくない

# Tauri 2.0.0でダイアログ(Node.js未使用)

Tauri 1.0の時はパッケージマネージャがなくてもAPIの利用には特に問題はなかったのだけど2.0になったらパッケージマネージャが無いとAPIの導入が出来なくなったかもしれない。<br>

使い方が変わりすぎていて目がまわりそうになりつつ、なんとか状況を把握して結論に至りました。でも実は違うのかも、よくわからないですがとりあえずそういう理解におちつきました。<br><br>

…RustでGUIを使いたいだけなのにNode.jsをインストールしないといけないなんて！　すごく嫌だ！！<br><br><br>

というわけなので抗います。<br><br><br>

## rfdで何とかする。

Rustでダイアログといえばrfdです。

簡単に使えて超便利。

問題があるとするとTauriで利用する際にメインウィンドウの裏側にダイアログが隠れてしまう心配があるということで逆にいえばそれ以外に不安要素がない。Rustでダイアログに用事があるなら全部rfdでやるといい。<br><br>

rfdはcargoでインストールしておきます

```sh
cargo add rfd
```



懸念事項のダイアログがメインウィンドウの裏側に隠れてしまうことの無いようにするためのコードがこちら

```rust
use rfd::FileDialog;
use tauri::{AppHandle, Manager};

//～略～

#[tauri::command]
async fn open_file_dialog(handle: AppHandle) -> String {
    let window = handle.get_webview_window("main").unwrap();
    let file_path = rfd::FileDialog::new().set_parent(&window).pick_file();
    
    if file_path != None {
        let file_path = file_path.unwrap();
        //～処理～
    } else {
        //～キャンセルされた～
    }

//～略～
```

とりあえず雑ですがこういう感じで、AppHandleを受け取ります。<br>JS側でinvokeする際には特に引数を渡す必要が無くて、これはTauriが自動的に渡してくれる値になります。

これでrfdは親ウィンドウがわかるのでダイアログをモーダレスに動作させることが出来ました。<br>ウィンドウの裏側にダイアログが回り込むこともありません。

<br>

やりました、これで Node.jsをインストールすることなく無事にやり過ごせましたよ！

※モバイルとかターゲットにするならNode.jsを素直にインストールしたほうが多分幸せかと思います。
【初心者向け】ArduinoIDEのシリアルモニタの使い方

# arduinoIDEに搭載されているシリアルモニタとは？
シリアルモニタとは、ArduinoとPCの通信を可視化してくれるツールです。
このツールは、とくにArduinoが出力する内容を表示するとき、Arduinoへ文字列を入力するときに使います。
初見の場合はどうやって使ったらいいの？ツールとして存在することは知っているけど、何が便利なの？
そんな疑問に答える記事を書いています。
https://www.hobbyhappyblog.jp/thorough-explanation-of-how-to-use-the-serial-monitor-included-with-arduinoide
ジオメトリノードで画像のアルファからメッシュを作成する

昨日のカメラ深度の画像でメッシュを作成する方法のテストをしている時に
画像のアルファでメッシュ化する新しいものを思いついたのでメモとして残しておきます

Blender3.3での業務で利用したいので3.3で出来る内容で進めます

## 画像のアルファでグリッドを切り抜く
まずはグリッド上のアルファ値を利用してオブジェクトを切り抜いてみます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/daa2acf9-b724-1396-fd07-55ee3149611e.png)

キャプチャ中のノードの大半は　実用のためにグリッドの縦横比を調整して正方形で分割されるようにする計算です
**ジオメトリ削除**で　画像のアルファが一定値以下（透明）な部分を削除しています

ここは　デフォルトの画像を繰り返す設定では画面端で想定外の値が出るので
画像テクスチャの　2つ目のプルダウンの設定を延長にしています
（UVが0-1を超える場合にどういう値を出すかの設定で　縁から外は同じ値が引き延ばされて出る）

# 階段状のエッジの軽減
グリッドを基準にした面の切り抜きでは　どうしても境界部分が階段状になってしまいます
見た目にも目立ちますし　無駄に頂点数が増えることになってしまうので対処したいです

そこで　グリッドの頂点を階段状に選択して 移動させたものを三角化する計算をしてみました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/f7495a69-26aa-69cb-3ab0-448d6f7ed73e.png)
面の構成する頂点のうち1つだけ移動した面がある場合に三角化すると
残った頂点だけで三角になるように辺ができて　辺が滑らかになっています

ノードで同様の処理をしたものの比較でも　境界が滑らかになりました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/392733a1-f355-e984-05c9-4894465d97a3.png)
## アウトラインのカーブ化とフィル
グリッドベースのメッシュ化では　内部のポリゴンは過剰になりがちなので
以前書いた　シェイプを立体化する時に使った方法を利用してメッシュ化します

https://qiita.com/yukimituki11/items/8a6064cd6b0e9396ffd9

まずは今ある平面の境界をカーブに変換します
ジオメトリノードでは　「**辺の共有面数**」で辺に接続している面の数を取得できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/477807a5-3515-3e51-5b36-726b2f19b30a.png)
境界部分の辺は接続している面が1つなので　境界部分をフィルタして取得できます

この条件で「**メッシュのカーブ化**」をすると　境界のカーブが作れます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/ff9fdd5c-9073-edc9-b5dc-e04abbb205e5.png)
面のあった部分にポイントを配置して　カーブのポイントを配置することで
カーブフィルで　ポイント位置を頂点にしたメッシュが作られます

:::note info
面の中のポイントの配置自体は現状ランダムなので　実用にはもう少し検討が必要です
:::

後は属性転送で元のメッシュからUVを取得したり
カメラ基準の座標に変換したりそれぞれの用途に合わせて調整することになるかと思います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/c0a70b9b-7625-7ef0-1822-b7910596e7a8.png)
## 4.2の例
ここまでの例では境界ギリギリの部分でメッシュを作る形になっていましたが
用途によっては確実に全ての不透明ピクセルがメッシュの中にしたいこともあります
Bleder 3.6以降「**属性ブラー**」のノードがあるので値を隣接部分にぼかすことで　範囲を拡張できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/cb4270f4-6ad7-0e6c-dcf4-4ec859234968.png)
このほかの調整も加えて4.2ではこのようにしてみました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/8a7a1fa0-9530-bb33-7fd0-5ab1c2c131b2.png)

どこまで需要があるか分かりませんが　何かの参考になれば幸いです
Reactのコンポーネントに引数付きの関数を渡したら思ってたのと違う挙動になった

こんにちは！
普段Webアプリの開発を行っているn-kokiと申します。

タイトルの通り、Reactのコンポーネントにpropsで引数付きの関数を渡したら、思っていたのと違う挙動をしたので、その内容と解決策について紹介します。

# 何がしたかったのか
Reactのpropsで変数の他に、関数も渡すことができることは、多くの方がご存じかと思います。私はこれを使って、ボタンのコンポーネントに対してonClickで発火する関数を渡そうとしました。引数の無い関数では思い通りに動くのですが、引数をつけるとボタンを押さずともページが読み込まれた瞬間に実行されていしました...

```react:App.js
import Button from './Button'

function App() {
  const func1 = () => {
    alert("ボタンが押されたよ！")
  }

  const func2 = ( data ) => {
    alert("ボタンが押されたよ！\n" + data + "っていうデータも受け取ったよ！" )
  }

  return (
    <div className="App">
      {/* ↓ 動画では使わない方にコメントをしています。 */}
      <Button func={ func1 }></Button>
      <Button func={ func2("data") }></Button>
      <a href="">
        <button className="button">リロード</button>
      </a>
    </div>
  )
}

export default App
```

```react:Button.jsx
const Button = ( props ) => {
  return (
    <button className="button" onClick={ props.func }>ボタンを押してね！</button>
  )
}

export default Button
```

## 引数なしの場合(思い通りにいった)
![](https://storage.googleapis.com/zenn-user-upload/c84eacc45533-20241011.gif)
動画のように引数なしの場合は、リロードしても特に何も起こらず、左のボタンを押すと関数が実行されます。

## 引数ありの場合(思い通りにいかなかった...)
![](https://storage.googleapis.com/zenn-user-upload/84796e02b33b-20241011.gif)
動画のように引数をつけると、リロードしたときに関数が実行されます。さらに、左のボタンを押しても何も反応しません。

# 解決策
App.jsとButton.jsxの内容をそれぞれ少し変更します。

まずApp.jsのほうでは、引数にした値を別のpropsとしてコンポーネントに渡します。
```diff react:App.js
- <Button func={ func2("data") }></Button>
+ <Button data="data" func={ func2 }></Button>
```

Button.jsxの方では、onClick内の関数の呼び出し方を以下のように変更します。
```diff react:Button.jsx
- <button className="button" onClick={ props.func }>ボタンを押してね！</button>
+ <button className="button" onClick={ () => props.func(props.data) }>ボタンを押してね！</button>
```

# できた！
![](https://storage.googleapis.com/zenn-user-upload/2273496cca33-20241011.gif)
無事、引数付きの関数を実行することができました！

ではまた。
C言語　メモリ管理理解への第一歩！（ヒープ領域、スタック領域、静的データ領域）

# はじめに
42Tokyoの生徒です。ヒープ領域、スタック領域という用語があまりわかっていなかったので、調べてみました。

# メモリ領域の種類
主に4つの領域に分けられるようです。
・ヒープ領域
・スタック領域
・静的データ領域
・テキスト領域
※本記事は、ヒープ領域、スタック領域を中心に記載します。

## ヒープ領域とは
・プログラム実行中に動的に確保・解放できる領域。
・プログラムの実行中に大きいサイズのメモリや柔軟にメモリを管理したい場合に利用する。
・malloc関数などを用いて、必要な時に必要なサイズのメモリを確保できる。
・確保したメモリはfree関数で明示的に解放する必要がある。

## スタック領域とは
・関数呼び出し時に自動的に確保され、関数呼び出しが終わると自動的に解放される領域。
・関数内のローカルなデータの格納などに用いられる。
・後入れ先出し（LIFO）方式で管理される

## 静的データ領域とは
・プログラムの開始時に確保され、プログラム終了まで解放されない領域。
・グローバル変数や静的変数の格納などに用いられる。

## テキスト領域とは
・プログラムの機械語コードが格納される領域。
・プログラムの実行中に変更されることはない。

# （素人ながらの疑問)メモリの解放を忘れるので全てスタック領域出メモリ操作すればいいのでは？
ヒープ領域を使ったほうがいい場合もあるらしい。

<B>①データサイズの制限：</B>
一般的にヒープ領域よりもサイズが小さく、固定されているので、大きなデータや、実行時にサイズが変動するデータを扱う場合、スタックオーバーフローが発生する可能性が高くなる。

<B>②動的なメモリ管理の難しさ：</B>
ヒープ領域は、malloc関数などで動的にメモリを確保できるため、プログラムの実行中に柔軟にメモリを管理できる。スタック領域のみを使用する場合、事前に必要なメモリサイズを正確に予測する必要があり、動的なメモリ管理が難しくなる。

# メモリ管理に関するその他用語
・メモリリーク
ヒープ領域でメモリを解放し忘れることによって発生する問題。

・スタックオーバーフロー
スタック領域が不足して発生するエラー。

・メモリ断片化:
ヒープ領域が小さな空き領域に分割されてしまい、大きなメモリを確保できなくなる現象。

・メモリ破壊:
解放済みのメモリにアクセスしたり、確保されたメモリ領域の外にアクセスしたりすると、予期せぬ動作やクラッシュを引き起こす。

# メモリの解放を忘れていないかチェックするには
以下のコードで確認可能らしい。ただし、Macではvalgridがサポートされていないらしいです(涙

```
valgrind --tool=memcheck ./my_program
```
memcheckの部分を変えるとメモリリーク以外にもさまざまな確認が可能。

・memcheck: メモリリーク、メモリ破損、未定義の動作などを検出する。
・cachegrind: キャッシュミスの検出とプロファイリングを行う。
・callgrind: カールグラフを作成し、プログラムの呼び出し関係を可視化する。
・helgrind: マルチスレッドプログラムのスレッドエラーを検出する。
・massif: ヒーププロファイラで、ヒープメモリ使用量を分析する。

※Macの場合や上記実行できない場合は、以下記事を参考にで確認可能でした。

https://qiita.com/ryo_manba/items/0ecffc15ead63e4900e8
【DAY32】Swaggerについて

## はじめに
インターンや勉強会で、フロントエンドの開発とバックエンドの開発が連携するためにSwaggerというものを使うと聞きました。今までは個人開発をしてきたので使用する機会がありませんでしたが、今後使う機会はあるかと思うので、どのようなものか調べてみました。

## Swaggerとは
Swaggerとは、REST API を定義するためのオープン仕様です。もっとざっくりいうと、「API仕様書を記述するための便利ツール」であるようです。

## API仕様書とは
API仕様書とは、そのAPIの目的や種類、リクエストやレスポンスの形式などが記載されたドキュメントです。基本的には、APIの種類、認証形式、エンドポイント、パラメータなどの情報を記載します。

## API仕様書の例
API仕様書がどのようなものかよく分からなかったため、chatGPTに書いてもらいました。この例をもとに解説します。誤った点があったらすいません。

### エンドポイント：`POST/ api/users
#### 概要：新しいユーザーを作成します。
#### メソッド：`POST`
#### リクエスト例：
```
POST /api/users
Content-Type: application/json
Authorization: Bearer <token>
```
* リクエストボディ：
```
{
  "name": "John Doe",
  "email": "john.doe@example.com",
  "password": "securepassword123"
}
```
#### レスポンス例：
* 成功(201 Created)：
```
{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com",
  "created_at": "2024-10-11T10:00:00Z"
}
```
* エラー(400 Bad Request)：
```
{
  "error": "Invalid email address"
}
```
#### 認証：必須 (Bearer Token)

#### ステータスコード：
* 201 Created: 成功時
* 400 Bad Request: 入力が不正
* 401 Unauthorized: 認証に失敗

#### 制限：1分あたり100リクエストまで

## API仕様書に含まれる主な項目
#### エンドポイントのURL
APIの各機能にアクセスするためのURLを記載します。
#### HTTPメソッド
GET、POSTといったメソッドを記述します。
#### リクエストパラメータ
APIに送信するデータ（クエリパラメータやボディパラメータ）を定義します。
#### レスポンスフォーマット
APIから返されるデータの形式や構造を記述します。
#### ステータスコード
成功やエラーの際に返されるコードを定義します。
#### 認証・認可
APIへのアクセスが制限されている場合、認証方法やアクセス制御に関する説明を含めます。
#### エラーメッセージ
失敗したリクエストに対して返されるエラーメッセージの内容を定義します。
#### 制限
APIの呼び出し回数の制限や頻度について記述します。
CaddyLogによる個人サイトの攻撃実態分析

今回は私の個人サイト「[たこやきさんのつぶやき](https://takoyaki3.com)」をホストしているCaddyのアクセスログを用いて解析し、明らかになった攻撃の実態についてまとめます。このサイトはReact+Viteを使ったSPA（シングルページアプリケーション）として構築されており、CaddyによってCloudflarePagesへリバースプロキシとして転送しています。アクセスログを解析することで、多くの攻撃を受けていたことが分かりました。

今回は分析方法が主なテーマではないので触れませんが、分析用のコードはこちらに残しています。
https://github.com/takoyaki-3/caddy-logs

## 攻撃の概要：無差別スキャンの存在

Caddyのログ解析を行ったところ、7041個のユニークIPアドレスがサイトにアクセスしてくださってる中で、160個のIPアドレスが存在しないパスへのアクセスを試みていました。これらのアクセスは、攻撃ツールによる無差別なスキャンや、特定の脆弱性を狙ったものだと推測されます。

特に目立ったのは、WordPressを狙った攻撃です。私のサイトはWordPressを使っていないにも関わらず、`/wp-admin/` や `/wp-login.php` といったWordPress特有の管理画面やログインページへのアクセスが大量に確認されました。攻撃者はインターネット上のあらゆるサイトに対し、脆弱なWordPressサイトを探すために自動スキャンを行っている可能性があります。

## 攻撃対象となった具体的なURL

解析結果から、攻撃者が試みたURLの多くは、WordPress固有のものや、一般的な脆弱性を狙うものが目立ちました。例えば、次のようなパスが確認されています：

- `/file.php`
- `/lol.php`
- `/wp`
- `/templates/system/error.php`
- `/administrator/templates/bluestork/index.php`
- `/stindex.php`
- `/olux.php`
- `/wp-admin/css/`
- `/test/`
- `/sites/default/files/`
- `/wp-head.php`
- `/bk`
- `/www.php`
- `/u.php`
- `/wp.php`
- `/wordpress`
- `/.well-known/traffic-advice`
- `/tagList`
- `/wp-includes/Requests/Text/admin.php`
- `/templates/rhuk_milkyway/index.php`

存在しないページへのアクセス像全体は[こちら](https://github.com/takoyaki-3/caddy-logs/blob/master/doc/caddy_logs_summary_with_countries.sample.json)から見ることができます。

これらのパスは、実際には存在しないため、404エラーを大量に発生させていました。特に、特定の期間に404エラーの数が急増していることが分かり、攻撃が集中して行われた可能性があります。例えば、2024年10月6日には404エラーが474件も発生しており、攻撃が激化していたことが見受けられました。

## 日毎の攻撃傾向

攻撃の傾向を日毎に分析した結果、いくつかの特定の日に攻撃が集中していることが判明しました。以下の日には、特に多くの404エラーが記録されています：

* **2024年6月15日～6月17日:**  この期間も404エラーが集中しており、攻撃が継続的に発生していたと考えられます。
* **2024年9月2日:** 404エラーが118件発生しており、この日に攻撃が集中していたことが分かります。
* **2024年10月6日～10月9日:** この期間には、404エラーが非常に多く発生しており、特に10月6日には474件、10月7日には455件発生しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/faddd083-b1ad-f95d-62c4-e3b78ed8ab6e.png)

**2024-10-14追記**
10月以降に404エラーが増加しているのは、SPAのURLにおいて、ページの表記方法をパラメータ表記（`?page_id={page_id}`）からパス表記（`/{page_id}`）へ変更したにもかかわらずトップレベルの`404.html`を削除していなかったことが原因でした。
このままでは、404なのかページが実在するのかがステータスコードだけからは判断できなくなってしまう為、今後のトレースでは存在するURLリストとログをマッチングさせるなどの処理を挟む必要がありそうです。

## 攻撃元の国分析

IPアドレスを基に`ip-api.com` APIを用いて、攻撃を行った可能性のあるIPアドレスの国情報を取得しました。すると、以外にも攻撃元は日本が最上位でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/846695dd-cab7-fb7c-4109-0064f899e838.png)

## まとめ

今回のCaddyログ解析から、私のReact+Vite SPAサイトが無差別なスキャンや脆弱性を狙った攻撃の対象となっていることが明らかになりました。個人サイトであっても、セキュリティ対策を怠らず、常に最新の対策を講じることが重要なのだと改めて実感しました。