OmniAICreator/Qiita-1.07M huggingface dataset:
MeePet に Node-Red と Aedes MQTT broker をインストールする

## Ulanzi TC001 を有効活用するために
先日 [Ulanzi TC001](https://www.ulanzi.jp/products/ulanzi-pixel-smart-clock-2882) を注文したが、このスマートクロックを動作させる方法として、純正のファームウェアを使う方法と、 [AWTRIX 3](https://blueforcer.github.io/awtrix3/) のファームウェアをインストールして使う方法がある。

純正ファームウェアでも YouTube チャンネル登録者数や天気などの情報をネットから取得して表示させることができる。ただし、SNSカウンターとして対応しているのは他に Bilibili, Weibo, Douyin と中国系ばかりで、 Facebook, Instagram, X には非対応。

一方、AWTRIX 3 だと外部から情報をプッシュすることで様々な内容を表示させることができるようになる。ただし、基本的には情報を外からプッシュさせる必要があるため、外部に何らかの仕組みを作る必要がある。

プッシュさせるデータ形式は JSON でよいので、 curl や Python を使って行う人もいるが、

https://cysec-journeys.com/ulanzi-desktop-pixel-clock-tc001-review/

海外の活用例を見ると、 MQTT という仕組みを使うのが一般的のようだ。
外部センサー情報などもプッシュして TC001 に表示させることができる。
MQTT の仕組みはこちらを参照。

https://qiita.com/kbys-fumi/items/3ebb31a94fd3f9cc0b7a

MQTT アプリで動作する、 Awtrix 用のスクリプトを集めたサイトも公開されている。

https://flows.blueforcer.de/

ここで公開されているスクリプトの多くは [Home Assistant](https://www.home-assistant.io/) や [Node-Red](https://nodered.org/) で動作するため、どちらかの環境を入れておくとよさそうだ（ちなみに Home Asssistant はアドオンで Node-Red を入れることができるらしい）。

今回は MeePet（Android）に入れやすい Node-Red をインストールすることにした。

## インストールは非常に簡単
MeePet にはすでに Termux をインストールしてあるので、公式サイトのインストール方法を順に進めるだけであっという間にインストールが完了する。

https://nodered.org/docs/getting-started/android

補足するとしたら、 `node-red` で起動するとフォアグラウンドで動くため、バックグラウンドで起動し続けるためには `&` オプションを使う必要がある程度である（この件は後段あり）。

## Node-Red を試す
ビジュアルプログラミングはほとんどやったことがないので、いくつかのチュートリアルを参考に Node-Red を試してみる。

https://qiita.com/egplnt/items/222a3ae289472bb759b1

![nodered_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/e11f25c8-23f8-807f-98b0-f5accdd2700a.png)
![nodered_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/8e19fb16-2efb-1112-4eb5-763e3477cb16.png)
![nodered_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/f8a14729-04fb-fc04-ba4f-963d5d759999.png)

いちおうできたことはできたが、 Android か Termux の仕様なのか、バックグラウンドだと動かしている最中に Node-Red が何度も落ちてしまった。
フォアグラウンドで動かすと落ちずに使えているので、しばらくこれで運用してみる。

## あれ？ Brokerは別途必要？
別の情報で MQTT 通信の設定は色々めんどくさいものがありそうだったが、 Node-Red にそれが無いのが気になっていた。
どうやら Node-Red には mqtt in と mqtt out しかなく、MQTT Broker は別途用意しなければいけないようだ。

https://zenn.dev/tseigo/books/node-red-with-arduino-device-2023/viewer/mqtt-node-red-mqtt-broker-connect-and-subscribe

とりあえずこちらのテキストにあった Beebotte のアカウントを取得しつつ、 mosquitto をインストールすることも考える。

## Aedes MQTT broker のインストール
mosquitto をインストールした後に気がついたのだが、 Node-Red にも MQTT Broker の機能を持たせる node-red-contrib-aedes というノードがあることを見つけた。

https://qiita.com/airpocket/items/641a23c67369b4f5d24a

Node-Red 内での通信、うまくいっている（隠れてしまっているが、下に mqtt in ノードと debug ノードを置いてある）。
![nodered_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/0e5e15fe-1693-ebb1-8868-c0ea2af6508a.png)

PC に MQTTX を入れてみた。これも受信成功。
![nodered_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/edd33457-7e0b-6819-cff8-7b4e6d903bb7.png)

最後にスマホの MyMQTT から。
最初は接続に失敗したが、接続方式を MQTT V5 から MQTT V3 に変更したところ接続できた。
![nodered_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943774/ff32f420-567d-931d-97f6-93256e228250.png)

ということで、あとは Ulanzi TC001 の到着を待つばかり。
Alembic コマンド早見表

## はじめに
PythonでORMライブラリを使用するときにSQLAlchemy + alembicの組み合わせが多いと思います。
過去新規アサインしたエンジニアが、alembicにてマイグレーションする方法が分からず、環境構築で止まっており、そのときにまとめた手順が好評だったため共有できればと思い記事を書きました。

## Alembicとは
Alembicは、Pythonで書かれたデータベーススキーマのバージョン管理ツールです。
主にSQLAlchemyを使用したデータベースマイグレーションに利用されます。 
このドキュメントでは、Alembicの基本的なコマンドとその使い方について説明します。

## インストール
Alembicを使用するには、Pythonのパッケージマネージャーであるpipを使ってインストールします。

```bash
pip install alembic
```

## **セットアップ**
Alembicの初期設定を行います。
```bash
alembic init <ディレクトリ名>

# 
# 例) alembic init alembic
#
# 以下ディレクトリとファイルが生成される
# alembic/
#   | - README
#   | - env.py
#   | - script.py.mako
#   | - versions/
# alembic.ini
```
生成されたディレクトリ内の **`alembic.ini`** ファイルを編集し、データベースの接続情報を設定します。



## **主要なコマンド**

以下コマンド実行する場合は、**alembic.ini**と同じディレクトリ階層にて実行すること。

### **マイグレーションスクリプトの作成**

新しいマイグレーションスクリプトを作成します。

```bash
alembic revision -m "マイグレーション名"

# 
# 例) alembic revision -m "add users table"
#
# 以下のマイグレーションファイルが生成される
# マイグレーション名の前にランダムな英数文字が入る
# alembic/
#   | - README
#   | - env.py
#   | - script.py.mako
#   | - versions/
#   |       | - 2f265898d6dd_add_users_table.py
# alembic.ini
```

### マイグレーションバージョン一覧

作成したマイグレーションスクリプトのバージョン一覧を表示します。

```bash
alembic history

# 以下形式のバージョン一覧を確認できる
# 例）になるため実際のマイグレーションバージョン一覧とは異なります。
# 
# a2c7dc5d156b-> 2f265898d6dd (head), add users table
# <base> -> a2c7dc5d156b, maigration init
```

### 現在のステータス確認

現在のデータベースの状態や適用されていないマイグレーションを確認します。

```bash
alembic current

# 以下形式の現在の適用されているマイグレーションを確認できる
# 上記、マイグレーションバージョン一覧のadd users tableの英数字と値が一致するはずです。
# a2c7dc5d156b (head)

```

### **マイグレーションの実行**

作成したマイグレーションスクリプトをデータベースに適用します。

```bash
alembic upgrade head

# headの場合、最新のマイグレーションが適用されます。
# １つ上のマイグレーションを適用したい場合は、`alembic upgrade +1`を実行します。
```

### **マイグレーションの巻き戻し**

直近のマイグレーションを取り消します。

```bash
alembic downgrade -1

# -1の場合、適用されているマイグレーションのひとつ前のマイグレーションが適用されます。
# 初期のマイグレーションを適用したい場合は、`alembic downgrade base`を実行します。
```

### **その他のコマンド**

他にも様々なコマンドがあります。詳細は公式ドキュメントを参照してください。

https://alembic.sqlalchemy.org/en/latest/


## **注意事項**

- マイグレーションを他の開発者と共有する際は、コードレビューを行うことを推奨します。
日本語かな漢字変換を実装してみた

この記事は、[UniMagic Advent Calendar 2024](https://adventar.org/calendars/10877) 20日目の記事です。

---

日本語のかな→漢字変換を実装したよという記事です。
具体的には、VRChat(Unity)で動く、以下のような変換機能付きキーボードを作成しました。
（VRChatから離れるのでメンテできない＆コードが汚いため、公開予定はありません）

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320900/471b6a85-3a04-b9d4-41ff-33cf668b8dd8.png">


# かな漢字変換
日本語は、ひらがな・カタカナ・漢字・その他記号などで構成されており、その文字数は膨大です。
それら全てをキーボード上に配置するのは現実的でありません。

そこで出てくるのが、かな漢字変換の技術です。
その名の通り、ひらがなを漢字に変換する技術ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320900/5ff2d616-fd65-765e-f687-ab6fbb16aa12.png)

実際に使われているIME（日本語入力システム）では、先を予測する機能や、入力履歴を参照する機能などが含まれていますが、今回は最小限の機能を実装しました。


# 変換の難しさ

かな漢字変換は、ぱっと見簡単そうですが、実はかなり複雑な技術です。

例えば、「かそうのせかい」というひらがなの変換を考えてみましょう。

- 仮想の世界
- 仮装乗せ界
- か草能勢貝

どれも「かそうのせかい」とは読めます。
しかし、多くの人が1番目の「仮想の世界」が一番自然だと感じるはずです。

では、どうやってプログラムで自然である変換結果を出力するのでしょうか？

# 単語の抽出
まずは、元となるひらがなから、単語の一覧を抽出します。

例えば、「かそう」というひらがなの変換候補を全て探すには、
- か
- かそ
- かそう
- そ
- そう
- う


の合計6通りの部分文字列に対応する単語が必要です。

これを高速に行えるのが、Trie木による共通接頭辞検索です。

https://algo-logic.info/trie-tree/

今回はtrie木の中でもダブル配列というデータ構造を利用します。
既に素晴らしい実装があるので、今回はそれを使わせてもらいました。
（ダブル配列を読みこんで検索する部分を、U#に移植しました）

https://github.com/s-yata/darts-clone

また、単語のデータについては、mozcのものを使用しました。

https://github.com/google/mozc

# ラティスの構築
共通接頭辞検索で引っ張ってきた単語を並べ、ラティスというものを構築します。

冒頭の「かそうのせかい」の例だと、こんな感じになります。
（本当はもっと単語がありますが、ここでは省略します）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320900/06bedbc1-f292-d46c-bb8a-a58d9fabce2e.png)

左端から右端まで矢印をたどっていくと、最終的な変換候補となります。
今回は、全部で15パターンありますね。
- 仮想能勢貝
- 仮装能勢貝
- か草能勢貝
- 仮想乗せ貝
- 仮装乗せ貝
- か草乗せ貝
- 仮想能勢界
- 仮装能勢界
- か草能勢界
- 仮想乗せ界
- 仮装乗せ界
- か草乗せ界
- 仮想の世界
- 仮装の世界
- か草の世界

ここに「単語の出現しやすさ」と「単語の繋がりやすさ」を表す「コスト」の情報を追加します。
（図内のコストは適当な値です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320900/5e231dbb-e125-08fd-0a70-7304507943f1.png)

そして、先頭から末尾までのコスト合計を求めます。
例えば、「仮想の世界」だと、「仮想(100)」「→(10)」「の(100)」「→(10)」「世界(150)」で、合計370です。
このコスト合計の小さい候補が「自然な変換候補」となります。

# 最短経路検索

しかし、全通りのコストを計算していては時間が足りません。
そこで出てくるのが最短経路検索の技術です。

## Veterbiアルゴリズム
動的計画法の一種です。

「この単語までの最小コスト」と「コストが最小になる時の1つ前の単語への参照」を、ラティスの先頭から計算・保存していきます。

ラティスの最後までたどり着いた後、参照を前向きにたどっていけば、最小コストの変換候補が分かります。

https://shinoarchive.com/contents/1703/

## 前向きDP後ろ向きA*アルゴリズム
しかし、Veterbiアルゴリズムだと、最小コストの場合しか分かりません。
かな漢字変換の場合は、2番目や3番目以降にコストが小さい変換候補も欲しくなります。
（「かそう」で変換した時、「仮想」だけ出てきても「仮装」「下層」と変換したい人は困ってしまいますよね）

N番目にコストが小さい結果まで出すことができるのがこのアルゴリズムです。
前半はVeterbiアルゴリズムと似たような感じですが、後半で優先度付きキューを使ってラティスの一番後ろから前へと探索していくのが特徴です。

https://jetbead.hatenablog.com/entry/20160119/1453139047

# U#での実装
ここまでのアルゴリズムを、全てU#で実装します。
なお、優先度付きキューはUdonに公開されてないので、自前で実装する必要があります。

また、Udonはとても遅いので、処理をタイムスライスするようにしています。
入力したひらがなが長くなければ、おおむね0.5秒以内に結果が返ってきます。

# キーボード部分の実装
残念ながら、この変換ギミックを組み込めそうなキーボードが無かったため、キーボード部分も実装しました。
ローマ字からひらがなへの変換ですが、「ん」の処理と「っ」の処理を先に行い、後はローマ字の文字数順に処理していきました。

どうでもいい事ですが、「ヵ」は「lka」「xka」、「ヶ」は「lke」「xke」で入力できるということを、この処理の実装で初めて知りました。

# 余談
かな漢字変換の処理は、自然言語処理でよく使われる「Mecab」の形態素解析と良く似ています。
詳しく知りたい人は、Mecabを解説しているブログ等を参照すると良いかもしれません。

https://techlife.cookpad.com/entry/2016/05/11/170000
TiDB v8.5.0 アップデート

## はじめに

TiDB v8.5.0 がリリースされました ㊗️

https://docs.pingcap.com/tidb/stable/release-8.5.0

本記事では、v8.5.0 のアップデートのうち、主な機能と思うものについて解説していきます。詳細はリリースノートを参照ください。

なお、v8.5.0 はTiUP Playgroundで試すことができます。

```bash
tiup playground v8.5.0
```

## パフォーマンス

高負荷環境下でのTiDBの性能向上機能が実装されています。下記が代表的なものです。

### TSO取得の並列処理化 
従来はTSO取得をバッチで行っていたもの。これをバッチ待ち時間を半分に、そのかわりリクエストを2並列で実施するようにしたもの（4並列のモードもあり）

### MVCCのインメモリエンジン
MVCCは更新履歴を保持してトランザクションの独立性を保つのに利用されている。定期的なGC処理により不要となった履歴は削除されているが、高負荷環境下では履歴が大量に蓄積し、最新の情報を取得するパフォーマンスが劣化していた。
インメモリエンジンはこの最新履歴をメモリに保持するようにしたもの。

### PDの負荷分散（リージョン情報取得処理をフォロワーに分散）
PDではTiKVリージョン情報を管理しているため、TiDBが処理を行う際にリージョン情報取得のAPI呼び出しが発生する。高負荷状態ではPDリーダーの負荷が高くなってしまい、TSOの取得などの重要な処理に影響が出ていた。この機能はリージョン情報取得処理をPDフォロワーに分散させる。

### グローバルインデックス
TiDB(MySQL)のパーティションは、パーティションキーはユニークインデックス（PK含む）の一部でなければなりませんでした。グローバルインデックスは、この制約を外すものです。

下記リンクの設計ドキュメントがわかりやすいと思います。パーティションテーブルで、インデックスもパーティションで分割される（ローカルインデックス）場合、パーティション内でのみ一意性を保つため、異なるパーティション間で重複が発生する可能性があります。

https://github.com/pingcap/tidb/blob/master/docs/design/2020-08-04-global-index.md

### その他

プロジェクション(SELECTで出力列を指定）のプッシュダウンや、必要な列のみの統計情報を取得するような機能が追加されています。

## 安定性

### PDのマイクロサービス化

PDの機能を分割し、TSOやスケジューリングを複数のPDで分担できるようにしたものです。

### リソースコントロールの機能強化

リソースコントロールによるRunaway Query(実行時間が長いためキャンセルさせるクエリ）判定条件の追加や、バックグラウンドジョブのリソース制限機能などが追加されました。

### TiProxy

TiProxyのスケジューリングアルゴリズムの種類が増え、負荷に応じたロードバランス機能が追加されました。また、トラフィックをキャプチャして別のTiProxyに送るトラフィックキャプチャ機能が実装されています。

## SQL

### FKのGA化

今までベータだったんですね・・・

### ベクター型のサポート

TiDB Cloud Serverlessで利用できていたベクター型が、ついにTiDBでも利用可能になりました。

## その他

TiDB/TiKVのCPU使用率がinformation_schemaから取得できるようになったり、バックアップデータをクライアント側で暗号化できるようになりました。

## 廃止/Deprecatedされた機能

### TiDB Binlog

MySQLのBinlogのTiDB版ですが、deprecatedを経てついに廃止されました。

## おわりに

TiDB v8.5.0 の特徴はユーザーフィードバックを元に強化されたパフォーマンス機能ということができると思います。TiDB Cloudで利用できるようになるのは少し先ですが、TiUPでぜひ試してみてください。
VScode上でconda activateしても環境変数が反映されない【VSCode × Conda】

この前、MacのVSCode内でCondaを使う際に、統合ターミナルで`conda activate`しても環境変数が正しく反映されない問題に遭遇した。

```zsh
$ conda activate my_env
$ echo $PATH
# Conda環境のパスが先頭に来ていない！
```

デフォルトのターミナルの方でも `echo $PATH` を実行してみると、そもそもターミナルで実行した結果とVSCode内で実行した結果が違った。

結論から言うと、VSCodeの統合ターミナルの設定が原因だった。

## **解決策**
1. VSCodeの設定 (`Cmd + ,` または `Ctrl + ,`) を開く。
2. 検索バーに" **Terminal Integrated Inherit Env** "と入力する。
3. " **Terminal > Integrated: Inherit Env** "のチェックを外す。
4. VSCodeの統合ターミナルを再起動する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218959/3e66e35e-c189-ea8e-761f-5558e0b86f94.png)

この設定をオフにすることで、VSCodeの統合ターミナルが通常のターミナルと同じ動作をするようになった。

## **原因**

「**Terminal > Integrated: Inherit Env**」が有効になっていると、VSCodeを起動した時のシステム環境変数を統合ターミナルに継承し固定する。
`conda activate`は、環境変数を変更してPATHの先頭にConda環境のパスを追加するなどの処理を行うが、VSCodeがシステム環境変数を固定しているため変更が反映されなかったようだ。

`Inherit Env`をオフにすることで、VSCode内でも`conda activate`が正しく動作した。

:::note warn
特定の拡張機能やプロジェクトがVSCode起動時のシステム環境変数に依存している場合、`Inherit Env`をオフにすると問題が発生するかもしれないので注意。
:::
iPhoneのクイックスタート

<!-- iPhoneのクイックスタート -->

※この記事は[Udon Advent Calendar 2024 - Adventar](https://adventar.org/calendars/10764)の20日目の記事です。

## はじめに

以前iPhoneSE3の64GBからiPhone13の128GBに変えました。その時にデータの移行を行ったので、手順をまとめておこうと思います。

その前の機種変がiPhone6sからiPhoneSE3だったのですが、その時は一回データをiCloudにバックアップしてから新しいiPhoneにデータを移行しました。対して今回はクイックスタートを使ってデータを移行しました。

## クイックスタートとは

既存のiPhoneを用いて、新しく買ったiPhoneのセットアップを行うという機能です。iOS12.4以降のiPhoneでないと使えないので、事前に新旧両方のiPhoneのOSを確認する必要があります。

## 手順

1. 新しいiPhoneが初期化されているか確認
2. 新しいiPhoneの言語設定を行う。その後、「クイックスタート」が表示されるのを待つ
3. 古いiPhoneのBluetoothをオンにする
4. 2台のiPhoneを近づけておく
5. 古いiPhoneに「新しいiPhoneを設定」の画面が表示されるので、「続ける」をタップする
6. 新しいiPhoneに表示される青い円形のアニメーションを古いiPhoneのカメラに写す(カメラが使用できない場合は手動で認証できる)
7. 古いiPhoneの画面に「新しいiPhoneの設定を完了」のメッセージが出るので、古いiPhoneのパスワードを入力する
8. 新しいiPhoneでTouch IDまたはFace IDを設定する
9. データ転送が完了するまで待つ

以上でデータの移行が完了します。

また、その後自分がやったことも列挙しておきます。

1. SIMカードを古いiPhoneから取り出して新しいiPhoneに入れる
2. 古いiPhoneに入っていたアプリをインストールしなおす(アカウントはそのまま使えたのでデータの問題はなかったが、一応古いiPhoneのデータを消す前に行うべき)
3. Googleアカウントのログインがされていないので、Google系のアプリでログインしなおす
4. 銀行のアプリやPayPayなどでログインされていない場合はログインしなおす
5. LINEの引き継ぎをする。クイックスタートでデータの移行はできているので、新しいiPhoneでアプリを開き、本人確認(SMS認証+パスワード認証)すればOK。
6. その他、ログアウトしているようなアプリがあればログインしなおす

そのままログイン情報が引き継げたものもあれば、そうでなかったものもありました。基準はよくわからないです。

大方復旧ができたら、古いiPhoneを初期化します。バックアップとして置いておくこともできますが、何かがバグりそうで嫌だったので初期化しました。

1. 古いiPhoneで「このiPhoneを探す」をオフにする
2. 古いiPhoneでApple IDからサインアウトする
3. 古いiPhoneをリセットする

以上でデータが片方のiPhoneに移行され、もう片方のiPhoneは初期化されます。古い方は新しい方と同期されないので、初期化しても問題ないです。

初期化しないでいると、新旧どちらにも入っているアプリが同期され、通知が二重になったりするので、初期化した方が良いと思います。何か不具合も起こりそうですしね。

## 注意点

古いiPhoneに電子マネーが登録されていると、移行時にデータの不具合が発生することがあるそうです。事前に古いiPhoneから電子マネーを削除しておくと良いでしょう。

また、クイックスタート中は二台のiPhoneが同時に使用できなくなるので、これも注意が必要です。時間に余裕があるときに行うと良いでしょう。

また、アプリにログインしなおす時にSMS認証が必要な場合は、先にSIMカードを入れておく必要があるので注意が必要です。

## おわりに

今回はクイックスタートの手順などを書いてみました。データの移行が簡単にできたので良かったです。しかし、アプリの再インストールやログインなどで意外と手間取りました。

ともあれ、無事にデータの移行ができたので満足でした。二台をワイヤレスかつP2Pで繋ぐのでとてもお手軽です。iTunesを使うよりも楽だし、iCloudは課金しないと容量があまりないので、この方法が適切そうです。

以上です。もしiPhoneからiPhoneに機種変する時は参考にしていただければと思います。

ではまた。明日の記事でお会いしましょう。

## 参考文献

[クイックスタートを使って新しい iPhone や iPad にデータを転送する - Apple サポート (日本)](https://support.apple.com/ja-jp/HT210216)

[iPhoneのデータ移行はクイックスタートで！](https://network.mobile.rakuten.co.jp/sumakatsu/contents/articles/2021/00022/)

[LINEの引き継ぎ方法は？事前準備や注意点を紹介](https://network.mobile.rakuten.co.jp/sumakatsu/contents/articles/2024/00159/)
20日目: SCCを実装する

[競プロ用ライブラリを作る Advent Calendar 2024](https://qiita.com/advent-calendar/2024/yukkku-ribitol)の20日目です.

https://qiita.com/advent-calendar/2024/yukkku-ribitol

# SCCって?

有向グラフに関するアルゴリズムです. 日本語では「強連結成分分解」です.

まず, 「強連結成分」を次のように定義します.
- 二つの頂点$u$, $v$について, $u$から$v$へのパスと$v$から$u$へのパスが存在するとき, かつそのときに限り$u$と$v$は同じ強連結成分に属する

数学的な書き方をするなら「頂点全体を"両方向のパスが存在する"という同値関係で割った同値類」ですね.
一応同値関係の性質を満たしていることを軽く示しておきます.
- 自明なパスを考えると任意の頂点$v$で$v\sim v$なので, 反射律が成り立ちます
- $u\sim v$が成り立つなら2つのパスが入れ替えて$v\sim u$を示せるので, 対称律が成り立ちます
- $u\sim v$と$v\sim w$が成り立つならパスをくっつけて$v\sim w$を示せるので, 推移律が成り立ちます

こういう感じのグループ分けをするのがSCCです. これを線形時間でやるアルゴリズムがあります.

# 仕組み

とりあえず, まずは「指定された頂点の属する強連結成分」を計算する方法を見ます.

1. その頂点から深さ優先探索で到達できる頂点を調べる
2. 辺の向きを逆にしたグラフで同様に深さ優先探索する
3. 1と2でどちらも到達できる頂点が同じ強連結成分に属している

1では「指定された頂点から到達できる頂点」を, 2では「指定された頂点に到達できる頂点」を調べます. どちらの条件も満たすときに同じ強連結成分に属するので, これで目的は達成できます.

これを全部の頂点を強連結成分に分けられるまでやればとりあえず目的は達成できますが, 各探索で最悪$O(N)$で, 連結成分の個数が最大$N$個なので合わせて最悪$O\left(N^2\right)$です.

これをベースに高速化していきます.　まず, 2で全範囲の頂点を探索していたのを「1で到達できた頂点だけ」に絞って探索します. これで2では各連結成分の要素しか見なくなったので, 2での計算量は合計でも$O(N)$になりました.
<details><summary>細かい話</summary>

この高速化を行うには「同じ強連結成分の要素同士のパスは, その強連結成分に属している頂点だけで構成される」ということを示す必要があるので, 示します.

同じ強連結成分に属している2頂点$v, u$について, $v$から別の強連結成分に含まれる頂点$w$を通って$u$に向かうパスが存在したとします.
すると, 同じ強連結成分に属しているので$u\to v$のパスが存在し, また$v\to w\to u$のパスの一部分として$v\to w$, $w\to u$のパスが存在しているので, $v\to w$のパスと, $w\to v$のパス($w\to u$と$u\to v$をくっつけます)が存在します. 強連結成分の定義から, $w$は$v$や$u$と同じ強連結成分に属していると分かりますが, これは$w$が$u$や$v$と別の強連結成分に属しているという仮定と矛盾します.

ということで別の強連結成分の頂点を通過するパスは存在しないと示せました.
</details>

1の計算量も削ります. 1で行った探索を他の強連結成分でも使いまわすことを考えます.

さて, 強連結成分ごとに1つの頂点にまとめて新しいグラフを作ると, これはDAG(有向非巡回グラフ)になります. DAGとはループがない有向グラフのことです.

DAGの特徴ですが, グラフを深さ優先探索して帰りがけ順に頂点を並べると, 前に来た頂点から後ろに来た頂点へのパスは存在しません.

<details><summary>ちゃんと示す</summary>

頂点$u$, $v$を適当に選んで, $u$が前, $v$が後に来たとします.
$v\to u$のパスが存在するなら, DAGにループが存在しないことから$u\to v$のパスは存在できません (存在したら$v\to u$のパスとくっつけてループにできます)
$v\to u$のパスが存在しないなら, 探索木上では子孫関係にないことが分かります. そして$u$と$v$の最近共通祖先を見ると, $u$の枝より$v$の枝の方が後に探索されているはずです. $u$の枝より前に$v$には訪れていないので, DFSの性質から$u\to v$のパスが存在するなら$u$の子に頂点$v$も含まれるはずですが, これは「子孫関係にない」ということと矛盾します. よって, $u\to v$のパスは存在しません.
</details>
ということで, 一旦強連結成分ごとに帰りがけ順に要素を並べておいて, それを後ろから強連結成分を調べていけば, 別の強連結成分に入り込む心配なく要素を取り出すことが出来ます.

# 実装

- [`scc.rs`](https://github.com/Yukkku/ribitol/blob/main/src/scc.rs)

頑張りましょう. 気分でイテレータを返すようにしてみました.

# まとめ

いかがでしたか?

明日は定数倍が軽い対数時間を実装します.
SplitTextFile

```vbs:SplitTextFile.vbs
'ANSI形式で保存
arg         = WScript.Arguments(0)
Set objFSO  = WScript.CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(arg)
lineCount = 0
fName   = ""
flag = 1
path = objFSO.getParentFolderName(WScript.ScriptFullName) & "\"

Do While objFile.AtEndOfStream <> True 

    rbuf = objFile.ReadLine
    If lineCount <> 0 and InStr(rbuf, "Attribute VB_Name = ") <> 0 And InStr(rbuf, """Attribute VB_Name = ") = 0 Then
        newFile.Close
        flag = 1
    End If

    If flag = 1 Then
        fName = Mid(rbuf, InStr(rbuf, "=") + 3)
        fName = Left(fName, Len(fName) - 1)
        Set newFile = objFSO.CreateTextFile(path & fName & ".bas", true)
        flag = 0
    End If

    lineCount = lineCount + 1

    If InStr(rbuf, "Attribute VB_Name = ""m") <> 0 Then
        rbuf = Replace(rbuf, "Attribute VB_Name = ""m", "Attribute VB_Name = ""M")
    End If

    newFile.WriteLine(rbuf)

Loop

newFile.Close
objFile.Close
Set objFile = Nothing
Set objFSO  = Nothing
Set newFile = Nothing
```
量子コンピュータについて【量子コンピューター Qiita Advent Calendar 19日目】

# はじめに
今回は、量子コンピュータの世界的な状況について個人の意見や感想を書きたいと思います。

なお、以前QCoder #1に参加したので、その記事を使いまわそうと思ったんですが、Advent Calendarに載せられるのはその年の11月1日以降という制限があったため、日が変わる前に急いでこの記事を書いています。

ちなみにQCoder #1の参加報告はこちら。

https://qiita.com/wagyan/items/12522bafc9d1cd7774af

# 量子コンピュータに対する期待と現実
　さて、量子コンピューティングについては、概ね5年ほど前から存在を知って、Nielsen Chaung本や、嶋田さんの本を買って読んだりしていたのですが、一方で当初はHWについては余り知りませんでした。

 その後、なんやかんやでHWについて知ってくると、量子ビット数が全然足りないし、ビットやゲートのエラー率が高くて、産業応用出来るような大規模化には、まだまだ時間が掛かることが分かって来ました。

　研究者の努力の甲斐もあって、実現までの時間は100年から50年へ、50年から20年へ、さらには量子コンピュータの大手企業は、あと5年ほどで一定の規模の量子コンピュータが出来るというRoadmapを描き始めました。これは本当にすごいことだと思います。

 　一方で、理論的には分かるのですが、実装してみたら思いがけない課題が見つかるのが世の常です。実際、5年後に量子コンピュータが出来たとしても、様々な障害や現実問題への落とし込みを行う際の壁にぶち当たることでしょう。そうなって初めて、産業応用の可能性を語れる段階まで進んだことになるのではないでしょうか。
  
  あるコンソーシアムでは、「AIブームは第3フェイズまであり、今回のブームは冬を迎えないだろう。量子COMに当てはめてみると、まだ第1フェイズではないか」というお話がありました。実際、量子コンピュータは、まだ産業界の実課題を解決しようという試みはほぼ生じていません。良い産業上のユースケースを見つけることが現状の一番の課題で、逆に言うと、良いユースケースが見つかれば、たちまち広がるポテンシャルを秘めているとも言えるのではないかと思います。

# 量子コンピュータのこれから
　量子コンピュータは、まだまだこれからの可能性に満ちあふれています。

　実は、量子情報を勉強するには、量子力学そのものではなく、線形代数や確率論が重要になります。要は、現在流行りのLLMを理解するために必要となる数学が共通するということです。ビジネス化が進んでいるLLMに比べて、量子コンピュータは、まだまだこれからです。

　しかし、LLMは正直やることは決まっているし、研究者も技術者も人数が多過ぎてレッドオーシャンになることは目に見えているので、学生さんや若い方々は、ブルーオーシャンである量子コンピュータにも手を出しておくと、ゆくゆくは有利になったりするんじゃないかなと思います。

# おわりに
　今回は、エッセイというかポエムというか、単なる感想みたいなものを記事にしてしまいましたが、偶にはこういうのも良いかなと思いました。
　まあでも、Qiitaなので、いつになるかは分からないけれど、今後はもうちょっと数学とかプログラミングの話も書いてみたいなと思っています。

　それでは！お疲れ様でした！
フロント側のログをファイル出力する方法(node.js)

フロント側でのログをファイルに書き出す要件があり、
やり方を調べてみました。

APIやミドルウェアの知見がなく、
間違えがあるかもですが、整理します。

まず、ブラウザの仕様として、フロント側から直接ファイル出力はできないらしい。
なので、フロントからサーバ側にデータを送りつけて、
それを出力するみたいな感じにしました。

サーバ立てるのに、express(node.js)フレームワークを使いました。
/save_logという、エンドポイントを受けたら、
log.txtというファイルを作り、リクエストボディを書き出しますよって感じです。
```
const express = require("express");
const fs  = require("fs")
const port = 3000

const app = express();
app.use(express.json())

app.post("/save_log", (req, res) => {
  const { log } = req.body;

  fs.appendFile("log.txt", log + "\n", (err) => {
    console.log("エラー", err);
  });
});

app.listen(port, () => {
  console.log(`server is running at localhost:${port}`)
});
```
フロント側はfetchapiで、エンドポイントにpostを投げる処理になっている。
```
fetch("http://localhost:3000/save_log", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ log: "entry point for log"}),
})
  .then(response => {
    if (response.ok) {
      console.log("saved log accurately");
    }
  })
    .catch(error => {
      console.error("faild to save log", error);
  });
```
ここで1つ勘違いしたのは、
サーバ起動して、エンドポイントをブラウザで叩けばデータ送れるだろうと思ってしまったこと。
送れないことに、10秒くらい考えて気づきました。

ブラウザでURLを叩く作業は、GETリクエストなので、
POSTとしては動かないのです。
なので、curlなどで送る必要がある。
```
curl -X POST http://localhost:3000/save_log \
  -H "Content-Type: application/json" \
  -d '{"log": "This is a test log"}'
```
curlも少し調べたら、
-Xはリクエストメソッド(post、get)を指定し、
-Hはヘッダーを指定、
-dは、リクエストボディ(データ)ということで一連の流れの基礎を学びました。

仕事で使うときは、APIなりJSONなり、
サーバとフロントのデータ送受信は、必須だなと思いました。
SQSから「Reason: Message must be shorter than 262144 bytes.」と怒られたら

シンプルな話なのでメモがてらざっくり結論だけ書いてここに残す。

## ざっくり結論
- SQSでメッセージに256KB以上のデータを送るとエラーになるよ
- そのための迂回策として、その規模のサイズのなんらかの引数（超巨大なテキストファイルとか、バイナリとか）はS3など一時的にどこかしらのストレージに保管してやって、実際に処理で必要になった時にストレージからダウンロードする形式で使うといいよ

下記で分かりやすく説明している通り、256KB超の大きいデータはS3におくとして、キューに送るメッセージは、そのS3のキーとかにしてやるといい感じ

https://stackoverflow.com/questions/43738341/amazon-sqs-more-than-2gb-data
[備忘録]新規プロジェクトをGitHubに追加とその他設定

※GitHubDesktopを使用する
1．GitHubDesktopを開き「File」→「Add local repository」を選択し、Add local repositoryの画面を表示する
2．Local pathにGitHubへ追加したいプロジェクトのルートディレクトリを入力
3．Add repositoryボタン押下するが、Git repositoryを作成していないため以下の警告が表示される

:::note alert
This directory does not appear to be a Git repository.
Would you like to ***create a repository*** here instead?
:::

4．create a repositoryを押下し、Create a new repositoryの画面を表示する
5．Create a new repositoryの各項目に任意の値を入力してCreate repositoryを押下すると新規のrepositoryが作成される
6．再度、Add repositoryボタン押下して追加完了

・.gitignoreの設定
初期状態では.gitignoreファイルは無いため作成する必要がある
1．ルートディレクトリで右クリックし、「Open Git Bash here」を選択しコマンド画面を表示する
2．以下のコマンドを入力してファイルの完成
```text
touch .gitignore
```
3．以下の例文に沿ってプッシュ対象外のフォルダ、ファイルを設定する
```text
# 以下はコミット対象外
# フォルダ指定
bin/ 
obj/
.vs/

# 拡張子指定
*.suo
*.log

-----
よく無視されるファイルやディレクトリの例
IDEやエディタのキャッシュファイル:
.idea (IntelliJ IDEA)
.vscode (Visual Studio Code)
*.suo (Visual Studio)
ビルド生成物:
/build/
/dist/
*.class
*.exe
ログファイル:
*.log
一時ファイル:
*.tmp
*.swp
環境設定ファイル:
.env
.local
```
4．保存して設定完了
応用情報技術者試験合格体験記 (R6秋)

## はじめに
こんにちは、Be-kazuです！
昨年の秋に行なわれた、応用情報技術者試験（以下AP）を受験し合格しました。
この記事では、応用情報技術者試験の合格に向けて、私が実践した勉強方法や試験当日のお話などを紹介していきます。是非最後までご覧ください。

:::note info
対象読者
- これからAPの受験を検討している人
- 現在APの学習を行なってる人
- その他APに関連した悩みや疑問を持っている人
:::

## 受験の動機
APの受験を決めた動機は2つあります。

1つ目は、ITエンジニアとしての知識や技術を向上させたかったからです。基本情報技術者試験（以下FE）は以前に合格していたので、よりレベルの高いAPに挑戦しようと思いました。

2つ目は、早いうちにAPを合格したいと思ったからです。APの合格者平均年齢は約28歳でおおよそ社会人6年目前後の合格が多いと予想されます。しかし社会人6年目となると、業務内容へのより深い専門性が求められると思います。そのような状況では私はAPよりも、自身の専門性を深めるような学習に尽力したいと考え、APを早めに取得したいと感じました。（筆者は社会人6年目未満の若手エンジニアです。）

https://www.ipa.go.jp/shiken/reports/toukei.html

## 勉強方法

#### 勉強期間・時間
勉強期間は**6ヶ月**ほど、平日は**2～3時間**、休日はそれに＋αの時間で勉強しました。他の資格の勉強も平行していたので、合計勉強時間は概算で**300~400時間**ほどだと思います。

「資格の学校TAC」のサイトでは
> 応用情報技術者の合格に必要な勉強時間は、情報処理技術者試験の未経験者であれば500時間程度といわれています。プログラマーやシステムエンジニアなどの実務経験者や、基本情報技術者試験の合格者であれば、基礎知識を習得しているため、200時間程度の勉強時間で合格できるといわれています

https://www.tac-school.co.jp/kouza_joho/joho_ap/ap_study_method.html

と記載されています。私はITエンジニアとしての実務経験は浅いですが、FEは合格済みなので、概ね妥当な勉強時間と思います。

#### 勉強方法

##### 午前

- **参考書を一通り読む**
参考書を読むことで、試験に必要な知識を大まかにインプットしました。
また参考書は以下のものを使用しました。この参考書は内容が詳細に書かれており、試験本番で所見の問題に出会う確率を抑えられると思い選択しました。一方で文字量が多く、イラストが他の参考書と比較して少なめなので苦手な方はいらっしゃるかもしれません。他にも様々な参考書があるので一度書店に足を運び、実際に見てみるのをおすすめします。

https://gihyo.jp/book/2023/978-4-297-13865-3

- **過去問道場での演習**
参考書での知識のインプットが完了したら、お馴染みの過去問道場で演習を行ないました。
私は過去20回分の試験範囲を選択して、過去問演習を行ないました。その際に過去問道場のチェック機能を使用することで、問題に対する理解度を把握しながら、1つ1つ解説を読んだり、自分で調べながら問題への理解を深めました。
最終的には演習時の正答率が80%を超えるくらいまで演習に取り組みました。

https://www.ap-siken.com/apkakomon.php

:::note alert
問題演習時の注意点
問題をそのまま覚えてしまうと、他の問題で応用しにくいので、しっかりと理解しながら進める事が重要です。
:::

##### 午後

- **「午後問題の重点対策」をやりこむ**
この参考書はAPの午後対策として有名な書籍です。APの午後過去問を演習しながら、解答の導出過程まで詳細に解説されています。繰り返し取り組むことで、解答の道筋を立てやすくなりました。
始めに全分野の問題を数問解答しました。その後、試験本番で解答する予定の分野と予備の分野を決定し、それらの分野に絞って演習を行ないました。

https://www.itec.co.jp/store/products/detail.php?product_id=3852&srsltid=AfmBOorQJGYpYNW2Pzw6TjSLo4ExZ_GNker4m2yKxBkyqk02mHgysn43

- **試験1回分を時間計測して何度か取り組む**
APの午後試験は150分で大問を5つ解答する必要があります。かなりの長丁場なので、経験として何度か時間を測定して試験1回分の演習を行ないました。
加えて、APの午後試験は分野ごとの難易度に差があります。解答する予定だった分野が難しく、予定を変更してほかの分野を解答することも想定されました。実際に時間を測定しながら試験問題に取り組む事で、時間間隔や適切な問題を選択する練習にもなりました。

## 受験本番

#### 午前（選択式）
過去問道場で何度も演習を行なっていたからか、スムーズに解答を行なう事ができました。しかしFEを受験したときと比較して、過去問からの流用は少なかったです。

また、昨今流行のAIやセキュリティに関する問題が多く出題されていたような気がします。

ちなみに、午前試験と午後試験の間に、自身の解答を入力して、予測点数を知ることができます。余裕を持って合格基準に達していたら良いのですが、基準ギリギリの場合メンタルが午後試験にも影響するかもしれないので、自身のない方は自己採点を控えたほうがいいかもしれません。

#### 午後（記述式）
受験前は午後の解答科目を
- 情報セキュリティ（必須）
- プログラミング
- システムアーキテクチャ
- 組み込み開発
- 情報システム開発

を予定し、予備の分野を
- データベース
- ネットワーク

として、準備していました。本番ではシステムアーキテクチャの問題が難しく、また予備で解答したデータベースの問題もいまいちだったので、ネットワークを解答しました。するとネットワークが非常に簡単で得点源の分野になりました。（運が良かったです。）

そして実際に一回分の試験を時間を計って解答する練習をしていたことで、本番の急な変更やそれによる時間配分も問題なく対応する事ができました。

## 受験後
各予備校が発表した解答速報を見ながら自己採点を行ない、以下の結果でした。
- 午前：76点前後
- 午後：63点前後

午前問題は問題なく合格だろうと思いましたが、午後問題に自信がなかったので。試験が終わって1週間はX（旧Twitter）や過去問道場の掲示板を往復する日々が続きました。

実際の得点は以下の結果でした。
- 午前：77.5点
- 午後：64点

蓋を開けてみると、自己採点とほとんど差のない結果でした。

今回午後試験ではマネジメント系、ストラテジ系の分野（所謂文系セット）は選択しませんでした。なので論述問題も少なくなり、自己採点との差が少なくなったのかなと思います。

## まとめ
ここまで私のAP合格体験記を書きました。拙い文章ではありますが、少しでも参考になれば幸いです。

今後はAPで学んだ情報を実務で生かしながら自己研鑽に励んでいきたいです。最後までご覧いただきありがとうございました。
ログインとOAuthの基本概念

**Webアプリケーションにおいて、ユーザーが自分自身を認証してサービスを利用できるようにする仕組みが「ログイン」です。一方、「OAuth（Open Authorization）」は、他のサービスやアプリケーションとの安全な連携を可能にする認可フレームワークです。以下でそれぞれの基本概念と使用例について詳しく説明します。**

# 1. ログインとは？
ログインは、ユーザーがサービスにアクセスする際に、自分自身を証明するプロセスです。主に以下のステップで構成されます：

ユーザー認証情報の入力

ユーザーがメールアドレスやパスワードを入力します。

認証情報の確認

サーバー側で入力された情報をデータベースと照合します。

セッションまたはトークンの発行

ユーザーが認証されると、セッションIDやJWT（JSON Web Token）を発行してログイン状態を維持します。

**使用例：
電子メールとパスワードを使用したログイン**

携帯電話番号に送信されたワンタイムパスワード（OTP）によるログイン

APIの例：
以下は、メールアドレスとパスワードを使用したログインAPIの例です。
```
POST /api/login HTTP/1.1
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "securepassword123"
}
レスポンス:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": 1,
    "name": "John Doe"
  }
}
```

# 2. OAuthの基本概念
OAuthは、ユーザーが自分の認証情報を直接共有せずに、他のアプリケーションが自身のデータにアクセスすることを許可するための仕組みです。これにより、ユーザーは安全にサービス間でデータを連携できます。

主な特徴：
ユーザーがアクセス権を付与するプロセスを簡略化

認証と認可の分離（OAuthは認可に特化している）

OAuth 2.0のフロー：
ユーザー認証と同意

ユーザーがアプリケーションAを通じて、アプリケーションB（例：Google、Facebookなど）へのアクセス権を付与します。

認可コードの取得

アプリケーションAは、ユーザーの同意を得た後、認可コードを受け取ります。

アクセストークンの発行

認可コードを使い、アクセストークンを取得します。

APIアクセス
アクセストークンを使用して、アプリケーションBのデータや機能にアクセスします。

使用例：
サードパーティログイン：
```
GoogleやFacebookを使用したワンクリックログイン

例：「Googleでログイン」ボタンを押して、ユーザーの名前やメールアドレスを取得する。

APIの例：
以下は、Google OAuthを使用したアクセストークン取得のリクエスト例です。

POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded

client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET&
code=AUTHORIZATION_CODE&
grant_type=authorization_code&
redirect_uri=YOUR_REDIRECT_URI
レスポンス:

{
  "access_token": "ya29.a0AfH6SMB...",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "email profile",
  "refresh_token": "1//0gXx6k8nJ..."
}
```

# 3. ログインとOAuthの統合使用例
多くのアプリケーションでは、ログインとOAuthを組み合わせて利用しています。

使用例：
ソーシャルログインの実装

「Googleでログイン」や「Facebookでログイン」のボタンをクリックすると、以下のプロセスが行われます：

ユーザーがOAuthの認可画面で同意を与える。

アクセストークンを取得し、ユーザー情報（例：名前、メールアドレス）を取得。

ユーザー情報をもとに、アプリ内でユーザーアカウントを作成。

アプリ間データ共有

旅行アプリがGoogleカレンダーにアクセスして予約情報を自動追加する。

APIの例：
以下は、取得したアクセストークンを使用してGoogleカレンダーAPIにアクセスする例です。
```
GET https://www.googleapis.com/calendar/v3/calendars/primary/events
Authorization: Bearer ya29.a0AfH6SMB...
レスポンス:

{
  "kind": "calendar#events",
  "items": [
    {
      "summary": "Meeting with Bob",
      "start": { "dateTime": "2025-01-10T10:00:00Z" },
      "end": { "dateTime": "2025-01-10T11:00:00Z" }
    }
  ]
}
```

# 4. セキュリティ上の注意点
**パスワードは必ずハッシュ化して保存する。**

OAuthトークンは短い有効期限を設定し、必要な範囲のみアクセスを許可する。

HTTPSを必ず使用する。

ログインとOAuthは現代のWebサービスにおいて不可欠な要素です。これらを正しく理解し、安全に実装することで、ユーザーにとって利便性が高く信頼できるサービスを提供できます。
Mapを活用してみる

## 今回の問題
https://paiza.jp/works/mondai/b_rank_new_level_up_problems/b_rank_new_level_up_problems__vision_test

以下の要件に基づいて、視力検査のテスト結果を評価するプログラム。
・視力はA, B, C, D, Eの5段階で判定
・各段階のテスト（TA, TB, TC, TD）に対して、成功（"ok"）か失敗（"ng"）を記録
・成功回数が2回に達した時点で合格とし、2回失敗するとそのテスト段階は不合格
・どの段階も合格しなかった場合、視力はEと判定

## 当初のコード
```java
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int kaisu = sc.nextInt();
        int[]clear = {0,0,0,0};//TA,TB,TC,TDのクリア回数
        int[]ng = {0,0,0,0};//TA,TB,TC,TDの失敗回数
        
        for(int i = 0;i < kaisu;i++){
            //どの検査か特定
            int x = 0;
            //ココ
            switch(sc.next()){
                case "TA":
                    x = 0;
                    break;
                case "TB":
                    x = 1;
                    break;
                case "TC":
                    x = 2;
                    break;
                case "TD":
                    x = 3;
                    break;
            }
            
            //検査結果を格納
            if(clear[x] != -1){
                String kekka = sc.next();
                if(kekka.equals("ok")){
                    clear[x] += 1; 
                }else{
                    ng[x] += 1;
                }
            }
            
            //検査結果を判定(失敗のみ)
            if(clear[x] < 2
                && ng[x] >= 2){
                    clear[x] = -1;
                }
        }
        
        //検査結果を出力(省略)
    }
}
```
テストの名前（TA, TB, TC, TD）とインデックスとの関連付けが冗長です。
これを改善するために、Mapを使ってテスト名とインデックスを関連付けてみます。

## Mapをつかった修正後のコード
```java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int kaisu = sc.nextInt();
        int[]success = new int[4];//TA,TB,TC,TDのクリア回数
        int[]ng = new int[4];//TA,TB,TC,TDの失敗回数

        //ココ
        Map<String, Integer> testIndexMap = Map.of(
            "TA", 0, 
            "TB", 1, 
            "TC", 2, 
            "TD", 3
        );
        
        for(int i = 0;i < kaisu;i++){
            //どの検査か特定
            
            String test = sc.next();
            String result = sc.next();
            int testIndex = testIndexMap.get(test);
            
            //検査結果を格納
            if(success[testIndex] != -1){
                if(result.equals("ok")){
                    success[testIndex] ++;
                }else{
                    ng[testIndex] ++;
                }
            }
            
            //検査結果を判定(失敗のみ)
            if(success[testIndex] < 2
                && ng[testIndex] >= 2){
                    success[testIndex] = -1;//検査失敗を示す
                }
        }
        
        //検査結果を出力(省略)
```
Mapを使って、TA, TB, TC, TDをそれぞれ0, 1, 2, 3に対応させることで、スイッチ文をなくし、**コードの可読性が向上**しました。
また、テスト名からインデックスを取得する処理も簡潔になりました。

## まとめ
Mapを活用することで、今まで一つ一つ判定していた冗長な処理をシンプルに記述できます。
業務でも見かける機会が多いため、今後活用していけるようにしたいと思います。
【Shell】元いたディレクトリに戻る方法

以下のコマンドを実行すると元いたディレクトリに戻れます。

```terminal
cd -
```

![画面収録 2025-01-08 22.25.41.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/87e97fee-ada4-59c8-1264-b2230df1e3a5.gif)
【JavaScript】月カレンダーでDate取得を効率化する

## まえがき
GASからカレンダー登録なんかの際に「第2金曜」や「当月最後の営業日」を取得したいといった時、どうしているでしょうか
パッと思いつく方法は、例えば以下のようなコードだと思います。

```
// 2回目に金曜(5)になるまでループ

const targetDate = new Date();
let count = 0;

targetDate.setDate(1);
targetDate.setHours(0,0,0); // 0時にする（しなくてもいい）

while(1){
  if(targetDate.getDay() === 5){
    count++;
  }
  if(count === 2){
    break;
  }
  targetDate.setDate(targetDate.getDate() + 1);
}

console.log(targetDate); // 実行月の第2金曜の日付となる
```
これでも良いと思いますが、ループをしたり、カウンタを扱うのはちょっと嫌かな…ってお気持ちです。


## 本題
### 配列を作る関数にしてしまう
まずは月の日付のDate配列を作ります。
引数を工夫すれば、指定月のカレンダー作成とかもできると思います。

```diff_javascript
function monthDates(){
  const [y,m]=(new Date).toLocaleDateString().split('/');
  return new Array(32).fill().map((_,d)=>new Date(y,m-1,d)).filter((d,i)=>d.getDate()===i)
}
```

### 使い方
この関数を実行すると、以下のような配列が得られます。

```
0: Wed Jan 01 2025 00:00:00 GMT+0900 (日本標準時) {}
1: Thu Jan 02 2025 00:00:00 GMT+0900 (日本標準時) {}
                ︙
29: Thu Jan 30 2025 00:00:00 GMT+0900 (日本標準時) {}
30: Fri Jan 31 2025 00:00:00 GMT+0900 (日本標準時) {}
length: 31
```
配列なので、フィルタするだけです。

```javascript
// 第2金曜を取得する
const secondFriday = monthDates().filter(date=>date.getDay() === 5)[1];
```
```javascript
// 最終営業日（平日）を取得する
const lastSalesDay = monthDates().filter(date=>!/[06]/.test(date.getDay()).slice(-1)[0];

// 毎週水曜日
const wednesdays = monthDates().filter(date=>date.getDay() === 3);

// 日付のテキストにする場合は、インデックスではなくparse関数を使います
monthDates().map(date=>String(date.getDate()));
// -> ['1', '2', '3', '4', '5', ...'31']

monthDates().map(date=>date.toLocaleString());
// -> ['2025/1/1 0:00:00', '2025/1/2 0:00:00', ...'2025/1/31 0:00:00']
```

### 解説
`new Array(32).fill()` で未定義の値が32個の配列を作成し
~~`map((_,d)=>new Date([y,m,d]))`~~`map((_,d)=>new Date(y,m-1,d))`でインデックスの値と、年・月の値を組み合わせてDateとして反映
`filter((d,i)=>d.getDate()===i)` で当月の日付のみになるよう、日付とインデックスが一致する日だけにする

要素を32個作成するのは、インデックスの最初の値が`0`であるためです。
31日まで無い月もあるので、フィルタで一緒に消します。
fill()しておかないと、map()が働かないみたいです。

<font color='ff0000'>※2025/01/10
コメントより、Dateの引数に配列を与えるのは不適と教示いただき修正しました。
ありがとうございます。</font>
FreeBSDでRuby 3.4をビルド・検証

2025/01/13 **追記: make test-all のエラー原因特定・修正して、make check で'check succeeded'まで進むようにできました！** 詳細はGitHubのページに書いたので、そちらをご覧ください。

portsが出ない！？
ということで、ちょっと危機感を持ち、FreeBSD 13.4環境下でRuby 3.4.1をビルドして、今どういう状態なのか検証してみました。

検証結果をまとめたページ:

https://github.com/hs/ruby34-freebsd

上記を基に作成したFreeBSD用のportsの雛形の投稿:

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=283922

結果としては、私の確認手順が間違っていなければ、
* ビルドには問題なく、普通に動く
* make check ではtest-allテストまで全てパスする(が、テスト終了メッセージ表示後にエラー停止)
* 問題だった [Bug #18613](https://bugs.ruby-lang.org/issues/18613)の不具合は、13.3-RELEASEで修正され、再発しなくなった様子

といった感じで、問題なく動きそうでした。

なんですが、

* [FreeBSDのRuby Team](https://wiki.freebsd.org/Ruby) の動きが見えない
（もしかしたら作業中なのかもしれませんが）
* Rubyの開発者の方々は [Bug #18613](https://bugs.ruby-lang.org/issues/18613) が解決しないのに痺れを切らせてしまったようにもみえる
（しばらく前に流れてきた時ちょっと解決を試みてみたんですが、私では手に余る状況でした）
* RubyのgemとFreeBSDのパッケージの管理ポリシーの相性が悪い
（ので、個人的にはRuby本体とruby-gemsしか使っていない。が、誰かが動作確認しているという意味でrubygem-* portsの存在にも意味はあると思っている、が、個人でなんとかするにはあまりにも数が多すぎる）

といった状況に悶々としており、とりあえず「問題は解決してますよ〜、ちゃんと動いてますし、このまま終わってしてしまうのは勿体無いですよ〜」と言えそうなところまで手を動かして声を上げてみました。

冬休みが終わってしまったので、ちょっと反応は鈍くなりますが、なんとかできればという気持ちはありますので、検証が足りん、これもやれ、とかあったらコメントいただけるとありがたいです。
ColorMatrixColorFilterを使って画像の色を加工して表示する

昔からあるのに知らなかったシリーズ

ImageViewではColorFilterを設定して色を変更することができます。
ColorFilterとして色を設定する場合、内部的にはPorterDuffColorFilterが使われ、Tintで指定するのと同様にアルファチャンネルを除いてすべて同じ色に塗りつぶされます（PorterDuff.Modeを指定しない場合）
アイコンに着色する目的で使う場合は、Tintを指定する場合がほとんどで、使われる機会も減っているように思います。

このColorFilterにはほかにもバリエーションがあり、ColorMatrixColorFilterもその一つです。

https://developer.android.com/reference/android/graphics/ColorMatrixColorFilter

ColorMatrixColorFilterはその名の通り、ColorMatrixを使って、色を変換して表示することができます。

https://developer.android.com/reference/android/graphics/ColorMatrix

ColorMatrixはリンク先に説明がありますが、4x5の行列で定義します。

```
  [ a, b, c, d, e,
    f, g, h, i, j,
    k, l, m, n, o,
    p, q, r, s, t ]
```

これをもとの色のRGBAに対して以下の計算を行い、色の変換を行います。

```
   R’ = a*R + b*G + c*B + d*A + e;
   G’ = f*R + g*G + h*B + i*A + j;
   B’ = k*R + l*G + m*B + n*A + o;
   A’ = p*R + q*G + r*B + s*A + t;
```

API Level1からある仕組みです

# 変化なし

これを設定する意味は全くありませんが、変換の仕組みを確認するために何も変化が起こらない行列を指定してみます。
以下の指定をすると、元の色のRGBAをそのまま採用することになるので、何も変化が起こりません。

```kotlin
imageView.colorFilter = ColorMatrixColorFilter(
    floatArrayOf(
        1f, 0f, 0f, 0f, 0f,
        0f, 1f, 0f, 0f, 0f,
        0f, 0f, 1f, 0f, 0f,
        0f, 0f, 0f, 1f, 0f,
    )
)
```

R' = R
G' = G
B' = B
A' = A
という変換で変化なしとなります。

# ネガ・ポジ反転

RGBに-1をかけ、255から引く形の行列を指定するとネガポジ反転した色で表示させることができます。

```kotlin
imageView.colorFilter = ColorMatrixColorFilter(
    floatArrayOf(
        -1f, 0f, 0f, 0f, 255f,
        0f, -1f, 0f, 0f, 255f,
        0f, 0f, -1f, 0f, 255f,
        0f, 0f, 0f, 1f, 0f,
    )
)
```

R' = 255 - R
G' = 255 - G
B' = 255 - B
A' = A
という変換ですね。

|元画像|colorFilter指定|
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/de6e0e37-bb26-1b03-cada-4845ac67c2e7.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/1566358e-a3f1-1008-e992-2507cc623fe1.png)|

# グレースケール変換

RGB各色を計算して輝度を計算します。輝度計算の係数は色空間によって異なりますが、ITU-R BT.709/sRGBの場合、0.2126, 0.7152, 0.0722が使われます。これをRGBで同じ値を指定するとグレースケール変換ができます。

```kotlin
imageView.colorFilter = ColorMatrixColorFilter(
    floatArrayOf(
        0.2126f, 0.7152f, 0.0722f, 0f, 0f,
        0.2126f, 0.7152f, 0.0722f, 0f, 0f,
        0.2126f, 0.7152f, 0.0722f, 0f, 0f,
        0f, 0f, 0f, 1f, 0f,
    )
)
```

R' = 0.2126R + 0.7152G + 0.0722B
G' = 0.2126R + 0.7152B + 0.0722G
B' = 0.2126R + 0.7152B + 0.0722G
A' = A

|元画像|colorFilter指定|
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/de6e0e37-bb26-1b03-cada-4845ac67c2e7.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/081a8916-0d1a-d38d-e621-cc1c9b919e18.png)|

---

あくまで係数と定数だけなので複雑な計算はできませんが、ちょっとした加工をしての表示はできそうですね。特にディスエーブル表示でグレースケール表示したいなどは普通にあるユースケースかと思います。

以上です。
「メイン・マニフェスト属性がありません」エラーと戦う！

はっきり理解してなかったのですが、jarファイルを実行した際に、「メイン・マニフェスト属性がありません」とのエラーが出ることがあります。

```
testusers@testusers-mac libs % ls
slotmachineXYZ-1.0-SNAPSHOT.jar
testusers@testusers-mac libs % java -jar slotmachineXYZ-1.0-SNAPSHOT.jar
slotmachineXYZ-1.0-SNAPSHOT.jarにメイン・マニフェスト属性がありません
testusers@testusers-mac libs % 
```

出たり出なかったりで、このエラーの意味するところをキチンと理解してなかったのですが、今回、腰を据えてキチンと調べました。

Chatgptに聞いたところ、下記の様に原因を答えてきました。

```
When you run into the error:

メイン・マニフェスト属性がありません
(English: “No Main Manifest Attribute”)

it means your JAR file does not have a Main-Class entry in its manifest. A runnable (executable) JAR needs two things:

1. A main method in one of your classes.
2. The JAR’s manifest must declare which class contains the main method.

Since your slotmachineXYZ class has main, you just need to configure Gradle to specify that org.example.slotmachineXYZ is the main class.
```

要は、２つの原因が考えれる。
一つは、mainクラスがない。（今回は、ちゃんとあるので、これではない。）
もう一つは、どこにmainクラスが含まれているかを、Manifestに明示した上で、JarでGradle Buildしないといけない。（今回はこちら。）

Chatgptに聞くと、Manifestに明示する方法として、下記の２つが明示されております。

１）Use the Gradle application Plugin　（自動化バージョン）
An easy solution is to use the application plugin. In your build.gradle (at the top level):

```
plugins {
    id 'java'
    id 'application'
}

group 'org.example'
version '1.0-SNAPSHOT'

application {
    mainClass = 'org.example.slotmachineXYZ'
}
```

２）Or Manually Configure the JAR Manifest　（マニュアルバージョン）
If you don’t want to use the application plugin, you can add a jar task that explicitly sets the main class attribute in your build.gradle:


```
plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

jar {
    manifest {
        attributes(
            'Main-Class': 'org.example.slotmachineXYZ'
        )
    }
}
```
Then build again:

```
./gradlew jar
```

This will produce a JAR with a Main-Class specified in its manifest. You can then run:

```
java -jar build/libs/<your-jar-name>.jar
```

要は、“No Main Manifest Attribute”って、どこにMainクラスがあるか、分からないから、きちんとManifestに明示してから、ビルドせよと言う事。
PDFからテキストを抽出するPythonプログラムの解説

Pythonを使ってPDFファイルからテキストを抽出する方法を勉強してみましたのでこちらで共有します。
特に、pdfminer.sixというライブラリを使用します。
このライブラリは、PDFの内容を解析し、テキストを取り出すのに非常に便利です。

## 1. 環境の準備
まず、pdfminer.sixをインストールする必要があります。
ターミナルを開いて、以下のコマンドを実行してください。
```python:qiita.rb
pip install pdfminer.six
```
## 2. コードの全体像
以下が、PDFからテキストを抽出するためのPythonコードです。
```python:qiita.rb
from pdfminer.high_level import extract_text
from pdfminer.layout import LAParams
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.converter import TextConverter
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
import os
import io

# 入力ファイル
pdf_folder = r"C:\example\pdf_folder"  # PDFファイルのフォルダ
pdf_filename = "example.pdf"  # PDFファイル名
# 出力ファイル
output_folder = r"C:\example\output_folder"  # Excelファイル出力先フォルダ
output_filename = "output.txt"  # テキストファイル名

def pdf_to_text_with_pdfminer(pdf_path, output_text_path):
    try:
        # PDFファイルを開く
        with open(pdf_path, 'rb') as pdf_file:
            # PDFResourceManagerの作成
            resource_manager = PDFResourceManager()
            
            # 出力ストリームを作成
            output_stream = io.StringIO()
            
            # LAParamsでレイアウト解析パラメータを設定
            laparams = LAParams()
            
            # TextConverterを作成
            device = TextConverter(resource_manager, output_stream, laparams=laparams)
            
            # PDFPageInterpreterを作成
            interpreter = PDFPageInterpreter(resource_manager, device)
            
            # 各ページを処理
            for page in PDFPage.get_pages(pdf_file):
                interpreter.process_page(page)
            
            # テキストを取得
            text = output_stream.getvalue()
            
            # テキストファイルに書き込む
            with open(output_text_path, 'w', encoding='utf-8') as text_file:
                text_file.write(text)
            
            # 後処理
            device.close()
            output_stream.close()
            
            print(f"PDFからテキストを抽出しました: {output_text_path}")
    except Exception as e:
        print(f"エラーが発生しました: {e}")

# ファイルパスの組み立て
pdf_path = os.path.join(pdf_folder, pdf_filename)
output_text_path = os.path.join(output_folder, output_filename)

# 出力フォルダが存在しない場合は作成
os.makedirs(output_folder, exist_ok=True)

# 関数を実行
pdf_to_text_with_pdfminer(pdf_path, output_text_path)
```
## 3. コードの解説
### 3.1 ライブラリのインポート
最初に必要なライブラリをインポートします。pdfminerの各コンポーネントを使ってPDFを解析します。
### 3.2 入力と出力の設定

pdf_folder: PDFファイルが保存されているフォルダのパス。
pdf_filename: 抽出したいPDFファイルの名前。
output_folder: 抽出したテキストを保存するフォルダのパス。
output_filename: 保存するテキストファイルの名前。

### 3.3 PDFからテキストを抽出する関数
pdf_to_text_with_pdfminer関数では、以下の処理を行います。

PDFファイルを開く。
PDFResourceManagerを作成し、リソースを管理します。
StringIOを使って出力ストリームを作成します。
LAParamsを使ってレイアウト解析のパラメータを設定します。
TextConverterを作成し、PDFの内容をテキストに変換します。
PDFPageInterpreterを使って各ページを処理します。
抽出したテキストを指定したファイルに書き込みます。
### 3.4 エラーハンドリング
try-exceptブロックを使って、エラーが発生した場合にメッセージを表示します。
### 3.5 実行部分
最後に、ファイルパスを組み立て、出力フォルダが存在しない場合は作成し、関数を実行します。
## 4. まとめ
このプログラムを使うことで、PDFファイルから簡単にテキストを抽出することができます。PDFの内容を分析したり、データを整理したりする際に非常に役立つツールです。ぜひ試してみてください！
Qiskit の Statevector のまとめ

現在勉強中のQiskitについて、ドキュメントを読んでいまいち理解出来なかった部分を自分なりにまとめてみます。
**間違いに気づいた方は、是非ご指摘ください!**

#### 目次
- [Qiskit におけるビット順序](#3行で弊社について)
- [Qiskit における状態ベクトルの生成と解析](#いきなり結論)
- [期待値計算](#リンク化の細かいルール)
- [まとめ](#snsに興味があるエンジニアを求む！)

## Qiskit におけるビット順序: LSB (Least Significant Bit)

Qiskit では、LSB (Least Significant Bit) 順序を採用しており、右端のビットがインデックスの小さい量子ビットに対応します。
#### Qiskit のビット列順序
- $|100\rangle$のようなビット列は、次のように解釈されます。
  - 最上位ビット（左端）$\to$ インデックス２の量子ビット
  - 中央の量子ビット $\to$ インデックス１の量子ビット 
  - 最下位ビット（右端）$\to$ インデックス０の量子ビット

#### 状態ベクトルの例
次の状態ベクトルを考えます：
```plaintext
Statevector: [0.707+0.j, 0+0.j, 0+0.j, 0+0.j, 0.707+0.j, 0+0.j, 0+0.j, 0+0.j]
```
##### LSB 順序での解釈
- 長さ8の配列は３量子ビット系を表します（$2^3=8$）。
- 配列のインデックスと基底状態は次のように対応します：
  - インデックス0 $\to$ $|000\rangle$
  - インデックス1 $\to$ $|001\rangle$
  - インデックス4 $\to$ $|100\rangle$
- 上記の状態ベクトルは、次の量子状態を表しています：
$$|\psi\rangle = \frac{1}{\sqrt{2}}\left( |000\rangle + |100\rangle \right)$$ 

##  Qiskit における状態ベクトルの生成と解析
Qiskit では、状態ベクトルをシミュレーションで計算する方法と、数学的に直接生成する方法の2つがあります。

#### 1. Statevector Simulator を用いた回路のシミュレーション 
以下の例は、量子回路を実行して状態ベクトルを計算します。
```python
import qiskit
from qiskit import QuantumCircuit, transpile
from qiskit_aer import Aer

#　量子回路の作成
qc =QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
# シミュレータを指定
simulator = Aer.get_backend('statevector_simulator')
transpile_qc = transpile(qc, simulator)
result = simulator.run(transpile_qc).result()
# 結果を取得
statevector = result.get_statevector()
# 結果を表示
print(f"Statevector: {statevector}")
display(statevector.draw("latex"))
```
```plaintext
Statevector: Statevector([0.70710678+0.j, 0.        +0.j, 0.        +0.j,
             0.70710678+0.j],
            dims=(2, 2))
```
$$\frac{\sqrt{2}}{2} |00\rangle+\frac{\sqrt{2}}{2} |11\rangle$$

#### ```.Statevector``` を用いた直接計算
以下は、```.Statevector``` を使って量子回路の状態ベクトルを直接計算する例です。
```python
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
# 状態ベクトルを生成
statevector = Statevector.from_instruction(qc)
print("Statevector:", statevector)
display(statevector.draw("latex"))
```
```plaintext
Statevector: Statevector([0.70710678+0.j, 0.        +0.j, 0.        +0.j,
             0.70710678+0.j],
            dims=(2, 2))
```
$$\frac{\sqrt{2}}{2} |00\rangle+\frac{\sqrt{2}}{2} |11\rangle$$

## 期待値計算
得られた状態ベクトルを使って、特定の演算子に対する期待値を計算できます。
##### 例: $Z_0Z_1$の期待値を計算
```python
from qiskit.quantum_info import Operator

# 演算子の定義
operator = Operator.from_label('ZZ')
# 期待値を計算
expectation_value = statevector.expectation_value(operator)
print("期待値", expectation_value)
```
```plaintext
期待値 (0.9999999999999998+0j)
```
$$|\psi\rangle = \frac{1}{\sqrt{2}}\left( |00\rangle + |11\rangle \right)$$
の場合、
$$\langle Z_0Z_1\rangle = 1$$
となり、理論値とほぼ一致します。

## まとめ

![スクリーンショット 2025-01-08 21.28.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901686/ad642298-43ea-4742-e5be-a31398d9f6a6.png)

- Qiskit の LSB 順序により、状態ベクトルやビット列の解釈は右端がインデックス0の量子ビットに対応します。
- 状態ベクトルは、```Statevector Simulator``` または ```.Statevector``` を使って計算可能です。
- 状態ベクトルを使えば、期待値や測定確率の解析を効率よく行えます。

このように、量子回路の状態をシミュレーション・解析する際には、ビット列の順序と状態ベクトルの対応に注意する必要があります。

※ バックエンドとは
Qiskitで量子回路を実行するための環境（実機やシミュレーター）。
javascript勉強#1

# 圧倒的個人的なアウトプット用記事
pythonでかなり基礎は既にあるのでそこそこ進めれたかな?()
1年前、python自体初心者of初心者なときにjsを始めようとした時とは全く感じ方が違った。htmlも学ぶのがめんどくさく感じたけど改めて今やってみると楽しい。ある程度出来るようになったらtypescriptもやってみたい。

### htmlファイル
```html
<!DOCTYPE html>
<html lang="en">
<head>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>html-test</title>
    <script src="/main.js"></script>
    <link rel="stylesheet" href="/main.css">

</head>
<body>
    <h3>サインイン</h3>
    <p>ユーザー名<input type="username" id = "login_username"></p>
    <p>パスワード<input type="password" id = "login_password"></p>
    <button onclick="login_button()">ログイン</button>
    <p id="error_data"></p>

    <h3>登録</h3>
    <p>ユーザー名<input type="username" id = "set_username"></p>
    <p>パスワード<input type="password" id = "set_password"></p>
    <button onclick="set_button()">登録</button>
</body>
</html>
```
### javascriptファイル
```javascript
let login_data = {};

function login_button(){ // ログイン
    let login_username = document.getElementById("login_username").value;
    let login_password = document.getElementById("login_password").value;
    if (login_username in login_data){
        if (login_data[login_username] == login_password){
            document.getElementById("error_data").innerText = "ログインに成功しました"
        }else{
            document.getElementById("error_data").innerText = "パスワードが違います"
        }
    }else{
        document.getElementById("error_data").innerText = "そのユーザーは存在しません"
    }
    
}

function set_button(){ // 登録
    let set_username = document.getElementById("set_username").value;
    let set_password = document.getElementById("set_password").value;
    login_data[set_username] = set_password;
}
```
picoCTF 2019 writeup dont-use-client-side

## dont-use-client-side (Web Exploitation)

Can you break into this super secure portal? `https://jupiter.challenges.picoctf.org/problem/29835/` ([link](https://jupiter.challenges.picoctf.org/problem/29835/)) or http://jupiter.challenges.picoctf.org:29835

とりあえず、記載のリンクを開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638553/b20a13de-df0c-6001-9cb9-4f329b15a236.png)
何らかの文字を入力してverifyボタンを押すと、Alertが表示されるようになっている。
DevToolでソースコードを確認すると、以下のようになっていた。
```html
<html>
<head>
<title>Secure Login Portal</title>
</head>
<body bgcolor=blue>
<!-- standard MD5 implementation -->
<script type="text/javascript" src="md5.js"></script>

<script type="text/javascript">
  function verify() {
    checkpass = document.getElementById("pass").value;
    split = 4;
    if (checkpass.substring(0, split) == 'pico') {
      if (checkpass.substring(split*6, split*7) == '723c') {
        if (checkpass.substring(split, split*2) == 'CTF{') {
         if (checkpass.substring(split*4, split*5) == 'ts_p') {
          if (checkpass.substring(split*3, split*4) == 'lien') {
            if (checkpass.substring(split*5, split*6) == 'lz_7') {
              if (checkpass.substring(split*2, split*3) == 'no_c') {
                if (checkpass.substring(split*7, split*8) == 'e}') {
                  alert("Password Verified")
                  }
                }
              }
      
            }
          }
        }
      }
    }
    else {
      alert("Incorrect password");
    }
    
  }
</script>
<div style="position:relative; padding:5px;top:50px; left:38%; width:350px; height:140px; background-color:yellow">
<div style="text-align:center">
<p>This is the secure login portal</p>
<p>Enter valid credentials to proceed</p>
<form action="index.html" method="post">
<input type="password" id="pass" size="8" />
<br/>
<input type="submit" value="verify" onclick="verify(); return false;" />
</form>
</div>
</div>
</body>
</html>
```
verify()関数でpasswordのcheckをしているようだ。
splitされているのを、数字通りに並べ替える。
フラグが得られた。
`picoCTF{no_clients_plz_7723ce}`
Silica Window Tinting Ingleburn

https://silicawindowtinting.com.au/
https://www.facebook.com/people/Silica-Window-Tinting/61563365193880/
https://www.instagram.com/silicawindowtinting/
For professional window tinting in Ingleburn, Silica Window Tinting delivers durable and stylish tints for residential and commercial spaces near Sydney.