OmniAICreator/Qiita-1.07M huggingface dataset:
円周率を計算する　その１

円周率をPCで計算する。
実際、円周率計算ソフトなどをつかって円周率を求めることができるが  
スクラッチ状態から円周率プログラムを組んだことがなかったので実際にやってみた。  
  
どうやって円周率を求めるのか、そのアルゴリズムを検索。  
「円周率　アルゴリズム」でググってみると最初に出てくるのが 

ガウス＝ルジャンドルのアルゴリズム 
https://ja.wikipedia.org/wiki/%E3%82%AC%E3%82%A6%E3%82%B9%EF%BC%9D%E3%83%AB%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%89%E3%83%AB%E3%81%AE%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0

なるほど、
初期値  
```math
  a_0 = 1\\
  b_0=\frac{1}{\sqrt{2}}\\
  t_0 =\frac{1}{4}\\
  p_0 = 1\\
```
反復式  
```math
 a_{n+1}=\frac {a_{n}+b_{n}}{2}\\
 b_{n+1}=\frac {2}{\sqrt{a_nb_n}}\\
 t_{n+1}=t_n-p_n(a_n-a_{n+1})^2\\
 p_{n+1}=2p_n\\
```
を何回か繰り返して

PIを計算
```math
  \pi\approx\frac{(a+b)^2}{4t}
```
する。

ループなしで一回のみ計算(n+1回目のみ)してみて円周率をもとめてみた。  
以下がそのソース。

<!-- more -->

```cpp
#include <math.h>
#include <iostream>
#include <iomanip>

void gauss_Legendre_algorithm()
{
    std::cout << "Start.." << std::endl;

    //初期値
    double a0 = 1.0;
    double b0 = 1.0 / sqrt(2.0);
    double t0 = 1.0 / 4.0;
    double p0 = 1.0;

    //１回目
    double a1 = (a0 + b0) / 2.0;
    double b1 = sqrt(a0 * b0);
    double t1 = t0 - p0 * pow((a0 - a1), 2.0);
    double p1 = 2 * p0;

    //PI計算
    double pi = 0.0;
    pi = pow((a1 + b1), 2.0) / (4.0 * t1);
    std::cout << " pi = "
              << std::fixed
              << std::setprecision(15)
              << pi
              << std::endl;

    std::cout << "End.." << std::endl;
    std::cin.ignore();
}

int main(int argc, char *argv[])
{
    gauss_Legendre_algorithm();
    return 0;
}
```

結果
```
Start..
 pi = 3.140579250522169
End..
```
実際は　3.141592... なので  
3.14 まではあっている。

以外に簡単な計算式(アルゴリズム)なのだな。
（内容の理解はともかく）

回数を増やせば精度が上がるので、次回は繰り返して計算できるようにソースを改変してみる。
OpenCVを使ってある色以外は白黒にしてみる

OpenCVを使ってある色以外は白黒にしてみる。

よくある処理に、ある色のみカラーにして他の色は白黒にして  
ちょっと雰囲気のある写真にしたててみる。

オレンジ色のみ残して他の色を無くす（グレースケール）にしてみた。  
ある色といってもRGBの数値でオレンジと示されても人間にはわからんので  
直観的ででわかりやすいHSV色空間へいったん変更する。  
  
https://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93  
OpenCVでは色相(Hue)は0 - 180の範囲なので注意！！  
  
HSV色空間へ変換した画像の色相(Hue)０〰20くらいまでのピクセル以外の  
他の色のピクセルを全て彩度(Saturation)を０にすると色が無くなるのでグレースケールになる。  
HSV色空間へ変換したあとRGB色空間へ戻すのをお忘れなく！！  
  

```cpp
//画面をだすよ
void display(cv::Mat image) {
    //名前をつける
    std::string windowName = "windowName";
    cv::namedWindow(windowName);
    //画面出た！！
    cv::imshow(windowName, image);
    //なにかキーをおして～
    cv::waitKey(1000 * 10);
    //整理整頓
    cv::destroyWindow(windowName);
}

//
// ここから
//
int main(int argc, char** argv) {

    cv::Mat image_ = cv::imread("fruits.jpg");
    cv::Mat ret_mat, hsv_image, dst_img;

    //HSV色空間へ
    cv::cvtColor(image_, hsv_image, CV_BGR2HSV);
    //HSV分解
    std::vector<cv::Mat> mat_channels;
    cv::split(hsv_image, mat_channels);

    //取り出す色を決定 0 - 20 まで
    int hue_min = 0;
    int hue_max = hue_min + 20;

    int hsv_value;
    int cols = hsv_image.cols;
    int rows = hsv_image.rows;
    for (int row = 0; row < rows; row++) {
        for (int col = 0; col < cols; col++) {
            hsv_value = static_cast<int>(mat_channels[0].at<uchar>(row, col));
            //色相(Hue)
            if (!((hue_min <= hsv_value) && (hsv_value <= hue_max))) {
                //彩度(Saturation)を0に
                mat_channels[1].at<uchar>(row, col) = 0;
            }
        }
    }
    //マージ
    cv::merge(mat_channels, dst_img);
    //RGB色空間へ (BGR)
    cv::cvtColor(dst_img, ret_mat, CV_HSV2BGR);

    //画面へ
    display(ret_mat);

    return 0;
}
```

オリジナル画像
![AA.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2589501/8d49be23-e5b8-6629-7445-5d6b35e7f868.jpeg)

オレンジだけカラー画像
![ bb.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2589501/8b577f23-0dd9-5ffd-cd02-34c9688a4135.jpeg)
OpenCVを使って画像を縮小、拡大してみる

画像を小さくしたり、大きくしたりしたいけど、正方形なら　cv::resize　を  
使えばいいのだけど  長方形の画像などでは縦横比を維持したまま縮小、拡大したい。  

```cpp
//画面をだすよ
void display(cv::Mat image) {
    //名前をつける
    std::string windowName = "windowName";
    cv::namedWindow(windowName);
    //画面出た！！
    cv::imshow(windowName, image);
    //なにかキーをおして～
    cv::waitKey(1000 * 10);
    //整理整頓
    cv::destroyWindow(windowName);
}

//
// ここから
//
int main(int argc, char** argv) {

    //500x500の画像に変換
    int width = 500;

    cv::Mat convert_mat, work_mat;
    //前処理　余った余白は黒で埋める
    work_mat = cv::Mat::zeros(cv::Size(width, width), CV_8UC3);
    //画面読み込み
    cv::Mat target_mat= cv::imread("starry_night.jpg");

    //縦横どっちか長い方は？
    int big_width = target_mat.cols > target_mat.rows ? target_mat.cols : target_mat.rows;
    //割合
    double ratio = ((double)width / (double)big_width);

    //リサイズ
    cv::resize(target_mat, convert_mat, cv::Size(), ratio, ratio, cv::INTER_NEAREST);

    //中心をアンカーにして配置
    cv::Mat Roi1(work_mat, cv::Rect((width - convert_mat.cols) / 2, (width - convert_mat.rows) / 2, 
    convert_mat.cols, convert_mat.rows));
    convert_mat.copyTo(Roi1);

    target_mat = work_mat.clone();

    //画面へ
    display(target_mat);
    return 0;
}
```

変換前画像 (752x600)
![20190220114225.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2589501/e9c0b822-f904-645c-d8d8-6547eedca21d.png)

変換後画像 (500x500)
![20190220114236.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2589501/35101348-9210-6a18-1757-0656c5a13c06.png)
OpenCVを使ってLenaさんを減色にする

Lenaさん画像は256x256x256の16777216色だが、減色して64色にしてみる。  
256を64で割ると各色４種類。4x4x4=64色、減色にともなって暗くなるので  
ちょっと明るくする加工を少し追加。

```cpp

//画面をだすよ
void display(cv::Mat image) {
    //名前をつける
    std::string windowName = "windowName";
    cv::namedWindow(windowName);
    //画面位置固定
    cv::moveWindow(windowName,100,100);
    //画面出た！！
    cv::imshow(windowName, image);
    //なにかキーをおして～
    cv::waitKey(0);
    //整理整頓
    cv::destroyWindow(windowName);
}

//
// ここから
//
int main(int argc, char** argv) {

    //lenaさん登場！！
    cv::Mat lena = cv::imread("lena.jpg");
    int cols = lena.cols;
    int rows = lena.rows;

    int reduce = 256 / 4;
    for (int j = 0; j < rows-10; j++) {
        for (int i = 0; i < cols ; i++) {
            for (int k = 0; k < 3 ; k++) {
                image.at<cv::Vec3b>(j, i)[k] =  lena.at<cv::Vec3b>(j, i)[k] / reduce * reduce + reduce/2;
            }
        }
    }

    //画面に出して!!
    display(image);
}

```
オリジナルLenaさん
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/t/treehitsuji/20150125/20150125104314.png" alt="f:id:treehitsuji:20150125104314p:plain" title="f:id:treehitsuji:20150125104314p:plain" class="hatena-fotolife" itemprop="image"></span></p>

減色Lenaさん
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/t/treehitsuji/20150125/20150125104327.png" alt="f:id:treehitsuji:20150125104327p:plain" title="f:id:treehitsuji:20150125104327p:plain" class="hatena-fotolife" itemprop="image"></span></p>
【Google Colab】選択セル内のみで検索/置換する方法

# 概要
[Google Colab](https://research.google.com/colaboratory/)で**選択したセル内のみ**で検索/置換する方法を紹介します。この方法は著者がGoogle Colabをいじっているときに偶然発見したものであり、著者の実力ではググってもヒットしなかったため、もしかするとあまり知られていないと思い記事にしました。

なお、ページ全体の検索/置換するには、Google Colabページ内の``「編集」>「検索と置換」``から行うか、ショートカットキーの``(Windows/macOS） Ctrl/cmd + H``から行う方法があります。

# 選択したセル内のみで検索/置換する方法
1. Google Colabページ内で検索や置換したいセルを選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/607e40a1-a220-0964-0bf8-2c9936311ab2.png)

1. ショートカットキー``(Windows/macOS） Ctrl/cmd + E``を入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/8c2d9fbf-b194-da36-bebb-9128add83612.png)

1. ``Find``に検索したい文字を入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/7c0d7974-4afb-77a1-c51d-37a263c20adf.png)

1. 置換したい場合は、検索窓の右の``>``をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/b9bc7cc1-fe4d-802b-f5ae-737742061d60.png)

1. ``Replace``に置換したい文字を入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/a4330db4-b7a6-92a5-c7bd-3a5370e99e6b.png)

1. 置換窓の右枠から置換を実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/04eae0b4-9284-986f-78c0-ad724368d3a2.png)
これで、お馴染みの形になりました。


# 関連資料
[Google Colaboratory ショートカット｜覚え方付き！](https://www.yutaka-note.com/entry/colaboratory_shortcut)
[【Google Colaboratory】作業が捗るショートカットキーを厳選して紹介！](https://blog.kikagaku.co.jp/google-colaboratory-shortcut)
node.jsでpresigned_urlを使用して画像データをs3に保存、表示する方法


この記事ではpresigned URLを使用してs3に画像を保存、表示する方法を学んでいきます。


### Presigned Urlとは？

presigned URLは、特定のS3オブジェクトへの一時的なアクセスを許可するために,
ユーザーに提供することができるURLです。URLを使用して、ユーザーはオブジェクトをREADするか、オブジェクトをWRITEする（または既存のオブジェクトを更新する）ことができます。


### 環境,事前準備
- "aws-sdk": "^2.1116.0"
- バックエンド(express（Node.js)), フロント（vue, react, angluar, Svelte　etc)
- awsのアクセスキー　（IAMでs3の操作権限を持つユーザーを作成し、アクセスキーを取得してください）
- s3にbucketを作成



### Presigned Urlを作成するAPI

```
const AWS = require("aws-sdk");
const uuid = require("uuid/v4");

const s3 = new AWS.S3({
  accessKeyId: "Your AWS accessKeyId",
  secretAccessKey: "Your secretAccessKey",
  region: "your region(東京を選んでいる場合はap-northeast-1)",
});

module.exports = async (app) => {
  app.get("/api/upload", async (req, res) => {
    const key = `${req.user.id}/${uuid()}.jpeg`;
    const url = s3.getSignedUrl("putObject", {
      Bucket: "Your bucket name",
      ContentType: "image/*",
      Key: key,
    });
    console.log(key);
    res.send({ url, key });
  });
};

```

### フロント（JavaScript)　/api/uploadを叩いて画像をs3に保存する処理
```
//apiからurlとkeyを取得
const uploadConfig = await axios.get("/api/upload");

//取得したurlをもとにs3に画像を保存（引数fileには<input type="file">で取得した画像が入る）
try {
    await axios.put(uploadConfig.data.url, file, {
      headers: {
        "Content-Type": "image/*",
      },
    });
  } catch (err) {
    console.log(err);
  }
```

**画像保存時にCORSエラーが表示されるため、s3のCORS設定を下記画像のように変更する

![ダミー画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1076701/d4ab1a7c-6479-9ab4-23c8-b05372b6a65a.png)


### web上に画像を表示させる
```
//東京の場合　your selected region は　ap-northeast-1 になります。
//keyはapiで作成したkeyが入ります。（const key = `${req.user.id}/${uuid()}.jpeg`;）
const imageUrl = `https://<your bucket name>.s3.<your selected region>.amazonaws.com/<key>`;

<img src={imageUrl} alt="blog" />
```

**もし画像が表示できない場合,Bucket policyが原因かも知れません。下記のようにBucket policyを変更するとweb上で保存した画像を閲覧することができます。
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicRead",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": "arn:aws:s3:::test123ta/*"
        }
    ]
}
```

s3上に画像、ファイルを保存することは実務でもよく使うかと思います！この記事で得た技術を応用し、使用していだだけたら幸いです！お読みいただきありがとうございます！
【Rails】画像と動画、どちらかを表示する方法

# content_typeはどうなっているのか？
前提として、今回はcarrierwaveやuploaderを使用していません。今回はactive_storageで画像や動画を保存しているので、今回はactive_storageとcontent_typeを使って画像と動画、どちらかの表示をしていきます。
Sequel Proでは以下のようになっています
[![Image from Gyazo](https://i.gyazo.com/3a1d810fddb0ffbd30b4e929495e8f1f.png)](https://gyazo.com/3a1d810fddb0ffbd30b4e929495e8f1f)
content_typeに注目してください。ファイルの拡張子がjpgにはimage/　、mp4にはvideo/　と記載されているのがわかるかと思います。
画像を表示するか、または動画を表示するかはこれを使ってコードを記述していきます。

# どうやって分けて表示するのか？
結論としては以下のようなコードになります。
```ruby:diary/index.html.erb
<% if diary.image.content_type.include?('image/')%>
   <%= image_tag diary.image,class:"card-img-top image-fluid" %>
<% else %>
   <%= video_tag rails_blob_path(diary.image), class: "card-img-top image-fluid",controls: true %>
<% end%>
```
これでどうしてできるようになるのか詳しく見てみましょう。(imageはカラム名です)
まずif構文の条件式をみてください。これはimageカラムに保存さているもののcontent_typeがimage/　かどうかを判別するための条件式です。もしcontent_typeがimage/ なら、Trueなのでimage_tagで画像が表示されます。
一方、imageカラムに保存されているものがimage/　では無い場合、video_tagで動画が表示されるようになります。

ただし、imageカラムが無い場合imageはnilになってしまい、エラーが発生してしまうので、以下のように記述します。
```ruby:index.html.erb
<% if diary.image.present?%>
  <% if diary.image.content_type.include?('image/')%>
    <%= image_tag diary.image,class:"card-img-top image-fluid" %>
  <% else %>
    <%= video_tag rails_blob_path(diary.image), class: "card-img-top image-fluid",controls: true %>
  <% end%>
<% end %>
```
これでimageカラムが無い場合、画像か動画を表示するための条件式が適応されないのでエラーが発生しなくなります。

# 終わりに
補足や修正点がございましたら、コメントをお願いします。
Unityで当たり判定を追加する

2022.4.18 5日目　今日はUnityで、読み込んだExcelデータを表示した後、そのデータを条件によってソートしてみます。

## 準備
以前にやったExcelデータを表示して以下GIFのような動作を実装しておきます。

![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1940156/774e21fe-c5b5-5bdd-ac7c-e0b13195b299.gif)

ポップアップで表示しているcellは以下のようになっております。
「ID　Name　Level」の順で表示しています。
![bandicam 2022-04-18 12-18-17-020.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1940156/fb6c2c32-f59e-8661-92fb-e262934d2c76.jpeg)

データはExcelで管理しております。
![bandicam 2022-04-18 12-22-07-222.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1940156/a222d3a4-95bf-f35d-5b81-b050cd314c25.jpeg)

今回は、IDかLevelでソートできるようにしていきます。

## ソートポップアップの準備
以下のように、ソートのポップアップを表示したら、ポップアップの外側をクリックすると非アクティブになるようにします。

以下のサイトを参考にしました。（感謝）
https://unity-senpai.hatenablog.com/entry/2019/03/27/012403

当たり判定をPopupにつけました。
BoxCollider2Dを付けて、IsTriggerにチェックを付けました。
![bandicam 2022-04-18 17-37-52-744.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1940156/bfb3d035-dee4-e8b4-1979-b95e8ede334d.jpeg)
![bandicam 2022-04-18 17-38-15-506.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1940156/2eb604b6-0b42-2f9d-8acd-fce7a0c1a69a.jpeg)


以下のようなスクリプトを作成し、Popupにアタッチします。
```C#:SortPopup.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SortPopup: MonoBehaviour {

    [SerializeField] public GameObject sortPopup;

    // Start is called before the first frame update
    void Start() {

    }

    // Update is called once per frame
    void Update() {
        //ソートポップアップが無かったらreturn
        if( sortPopup.activeSelf == false ) {
            return;
        }

        //マウスでクリックしたら
        if( Input.GetMouseButtonDown( 0 ) ) {

            //Rayを飛ばして
            Ray ray = Camera.main.ScreenPointToRay( Input.mousePosition );

            //Rayの先に当たり判定のあるオブジェクトがあるかどうか
            RaycastHit2D rayHit2D = Physics2D.Raycast( ( Vector2 ) ray.origin, ( Vector2 ) ray.direction );
            
            //なかったらソートポップアップを消す
            if( !rayHit2D ) {
                sortPopup.SetActive( false );
            }
        }

    }
}
```

これでソートのポップアップを表示したら、ポップアップの外側をクリックすると非アクティブになるようになりました。

## ソートを実装する（仮）
```C#
public void OnClickLevelSortButton() {
        
        for( int i = 0; i < cellNum; i++ ) {
            GameObject temp = null;
            if( _instance[ i ].GetComponent<Cell>().getCellLevel() > _instance[ i + 1 ].GetComponent<Cell>().getCellLevel() ) {
                temp = _instance[ i ];
                _instance[ i ] = _instance[ i + 1 ];
                _instance[ i + 1 ] = temp;
            }
        }
}
```

こんな感じで書いてみましたが、別の場所でエラーが出てしまったので、明日はそこを修正して、動作を確認します。

今日は終わりです。
Laravel9から大幅にルーティングの書き方が変わった



```routes\web.php
<?php
use App\Http\Controllers\HomeController;//名前空間

Route::get('/home',[HomeController::class,'index']);
```

ルートを変えたらキャッシュクリアしないと反映されないです。
```
$ php artisan route:cache
```



```Homecontroller.php
class HomeController extends Controller
{
    //
   public function index()
   {
     $message = 'Hello World';
     return view('home.index',['message' =>$message]);
   }
}
```

```views\home\index.blade.php
<html>
<body>
     <h1>{{$message}}</h1>
</body>
</html
```

urlは　```http://127.0.0.1:8000/home```で表示されました。
【Rails】Slimでのifを利用する方法

# 初めに

Rails でポートフォリオを作成中に、条件によってクラスをつける方法を忘れてしまっていたので忘備録として残しておきます。

## 環境

Rails 7.0.2
Ruby 3.1.1
Slim 4.1.0

# 本文

erb での後置 if の書き方は多くありましたが slim でのやり方が少なかったので記事にしておきます。

こちらは erb での書き方です。

```erb: if.html.erb
<div class='btn-list'>
  <% if @result == true %>
    <%= link_to "Topへ", words_path,{class:'btn-bule'} %>
  <% end %>
  <div class= "btn <%= 'btn-red' if @result == false>"></div>
</div>
```

こちらは slim での書き方です。

```slim:if.html.slim
.btn-list
  - if @result == true_
    = link_to "Topへ", words_path,class: 'btn-bule'
  div.btn class="btn" = if @result == false
```

slim では class が記述が簡単になっていますが、if を利用するときは記述しないとエラーができます。

# 参考

[公式](http://slim-lang.com/)
もしシステムエンジニアが「おもてなし幻想」を読んだら

## 概要

[「おもてなし幻想」](https://www.amazon.co.jp/dp/B07GKRF8NW/)という本を読んだので感想を書く。

## 感想

基本的にはオペレーター向け、カスタマーサポート向けに書かれた本の内容であった。
しかし小売りにも通用するといった他業種の話題もあり、システムエンジニアにも通ずるものがあるのではないか？と思ったので、そのあたりを深く掘り下げて感想を書く。

### タクシーの例

まず概略として日本の文化である「おもてなし」は今の時代には適していない、という趣旨の内容であった。
本にはタクシーの例が載っていた。
日本のタクシーは驚くほど多くの決済手段が用意されていて、お客さんは好きな決済で支払いができる。
しかし自身が現金しか持っていないのであればそれらは「おせっかい」でしかない。
またカードや電子決済であってもサインが必要であったり、領収書が印刷されるのをじっと待つ必要があったりする。
それに対してUBERは一切何の手間もかからずに乗車して降車することができる。
> 日本のタクシーは丁寧だけれど手間を強いるのだ

これがこの本で述べている「おもてなし」≒「おせっかい」の図である。
そしてこれはエンジニア界隈にも同じことが言えるのではないか？と考えた。

### 顧客努力の低減

顧客努力指数（CES）が大事だよという話。
丁寧な説明やおもてなしをするよりも、ユーザーにいかにストレスを感じさせずに（努力いらずに）システム/製品を使ってもらえるか、が1番ロイヤリティに寄与するという話。

### システムエンジニアの例

例えば今自分が担当しているアプリケーションに新人エンジニアがアサインされた。
よくある問い合わせをまとめ、ログの見方やDBの見方をまとめて教育をした。
これはとても丁寧なおもてなしだと思う。
ただ実際には新人はその手順を読んで自分で理解する努力が必要だし、アサイン時に説明されたことを実際の問い合わせ時まで覚えているかと言われると難しい。
この場合に本当に必要なものは「よくある問い合わせをなくすこと」である。
そうすればその問い合わせ自体を引き継ぐ必要はなくなり、新人は開発に専念することができる。
（実際にはそんなに簡単じゃないことは重々承知だが）

開発する時も同様で「とにかくたくさんログを出す」というおもてなしより、「この機能でエラーが出たら自動でリトライし、可能な限り手動リカバリをさせない」などの努力低減の方が大事である。
ユーザーに「エラーが発生しました！」のメッセージを丁寧に出したところでユーザーからすると「で、どうすりゃええねん」という話。
エラーを丁寧に出すよりも「エラーを発生させない」「発生しても自動でリトライ/リカバリする」の方が余程有用である。（もちろんその機能による）

開発者からする顧客とは
* 実際にシステムを使うユーザー
* オペレーター
* 後任の担当者
* しばらくして開発時の記憶をなくした自分自身

であることを忘れてはならない。

## まとめ

「おもてなし」という手厚いケアを頑張るべきではなく
頑張らなくていいように頑張るべきだ、という結論になった。
これはまさにエンジニア（プログラマー）の3大美徳である「怠慢・短気・傲慢」そのものじゃないか、と私は思う。
AWSCloudFormation is not authorized to perform: eks:TagResource エラーの回避

## 概要

- AWS CloudFormationでEKS Clusterリソースを作成する際、EKSへのタグ付けに失敗した。
- cloudformationのcreate-stack実行時に付与したRoleに、`eks:TagResource`のポリシーが不足していたことが原因だった。

## 発生事象

- CFnからEKS Clusterを作成するため、以下のコマンドを実行。

```
aws cloudformation create-stack --stack-name ${STACK_NAME} \
--role-arn arn:aws:iam::${ACCOUNT_ID}:role/<RoleName>\
--template-body file://01_template/${TEMPLATE_FILE} \
--parameters file://02_parameter/${PARAMETER_FILE}
```

- CFnの以下のエラーが発生していた。

> Resource handler returned message: "User: arn:aws:sts::xxxxx:assumed-role/<RoleName>/AWSCloudFormation is not authorized to perform: eks:TagResource on resource: arn:aws:eks:ap-northeast-1:xxxxx:cluster/<cluster_name> (Service: Eks, Status Code: 403, Request ID: xxxxx)" (RequestToken: xxxxx, HandlerErrorCode: AccessDenied)

## 原因・対処

- EKSに任意のタグをつけたかったので、CFnのTemplateファイルでTagsを使用していたが、EKS Clusterへのタグ付与には以下のポリシーが必要だった。

`eks:TagResource`

- 上記ポリシーをcreate-stackオプションの`--role-arn arn:aws:iam::${ACCOUNT_ID}:role/<RoleName>\`で指定したRoleに追加して、再度実行したところ正常にEKS Cluster作成が完了した。
dockerのmysqlコンテナが起動しなかった話

https://www.engilaboo.com/laravel-docker/
上記記事に沿ってコマンドをポチポチしていたところ、`php artisan migrate`コマンドでDB接続を確認したときにエラーになった。

## 原因特定までの過程
本当にDB接続エラーなのかの確認
`php artisan tinker`
tinkerとは入力した内容を即座に評価して出力してくれる対話型の入出力環境

`DB::select('select 1');`
かんたんなSELECT文が通るか実行して確認。
同じエラーがでたのでDB接続エラーであることは断定。

`restart: always`をつけて失敗しても再起動させるようにした。
```docker-compose.yaml
version: '3'
services:
  app:
    build: ./docker
    ports:
      - 80:80
    volumes:
      - ./app:/var/www/app
    working_dir: /var/www/app
  db:
    image: mysql:8.0
    restart: always
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: database
      MYSQL_USER: user
      MYSQL_PASSWORD: pass
```
コンテナを立ち上げ直す
一度コンテナを止めて削除しなくても下記コマンドでアップデートできるようだ（もしかしたら一度止めて削除して、立ち上げを一発でやってくれてるのかも）
`docker-compose up -d --build`
docker psで確認
```
vanshiMacBookPro15inch:app suzukitaishi$ docker ps
CONTAINER ID   IMAGE                COMMAND                  CREATED          STATUS                         PORTS                               NAMES
90613db9ee47   laravel-docker_app   "docker-php-entrypoi…"   19 seconds ago   Up 14 seconds                  0.0.0.0:80->80/tcp, :::80->80/tcp   laravel-docker_app_1
4b6ec132459a   mysql:8.0            "docker-entrypoint.s…"   19 seconds ago   Restarting (1) 2 seconds ago                                       laravel-docker_db_1
```
mysqlコンテナ起動はしたが、STATUSがRestartingにしていて起動はしていても何かでつまづいてはいるようだ。

## 原因
`docker logs {コンテナID}`でログを確認
ログの確認方法
https://qiita.com/mom0tomo/items/35dfacb628df1bd3651e

```
2022-04-18 04:56:10+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.28-1debian10 started.
2022-04-18 04:56:10+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2022-04-18 04:56:10+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.28-1debian10 started.
2022-04-18 04:56:10+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
```
docker-composeのパスワードの設定まわりが間違っている様子。

# 解決
docker-compose.yamlファイルにMYSQL_ROOT_PASSWORDが必要だった。
https://qiita.com/takepan/items/0cbf13af3a0bb2c243ab

とはいえ、またDB接続の違うエラーがでて解消中。
コンピュータの構成

復習回数1
## コンピュータの構成
コンピュータは、<font color="Blue">制御装置・演算装置・記憶装置・入力装置・出力装置</font>の五つで構成されている。制御装置・演算装置を合わせて、<font color="Blue">中央処理装置(CPU)</font>という。<br>
### 各装置の役割
<font color="Blue">制御装置</font> - 記憶装置からプログラムの命令を取り出して解読し、各装置に指示を与える。
<font color="Blue">演算装置</font> - 四則演算や論理演算などを行う
<font color="Blue">記憶装置</font> - データやプログラムを記憶する
<font color="Blue">入力装置</font> - コンピュータの外部からプログラムやデータを読み込む
<font color="Blue">出力装置</font> - コンピュータの内部で処理されたデータを外部へ書き出す

![6884a465454f7ac58c2aee739c90f61c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600460/57ed3e2c-78fe-71eb-219a-5d00a4050526.png)
## ハードウェアとソフトウェア
上記の五つの装置は<font color="Blue">ハードウェア</font>という、物理的に存在するものである。
ハードウェアに対してプログラムやアプリなどの<font color="Blue">ソフトウェア</font>がある。
ソフトウェアは物理的に存在せず、記憶装置に「1」と「0」の信号という形で存在する。

## プログラム記憶方式
<font color="Blue">主記憶装置に記憶されたプログラムをCPUが取り出し、解読・実行する方式</font>
ハードウェアを変更せずにすむ

参考文献 :  イメージ＆クレバー方式でよくわかる 栢木先生の基本情報技術者教室
Nuxt 2 から Nuxt Bridgeへの移行

# はじめに
基本的には↓の通り進める。
https://v3.nuxtjs.org/bridge/overview
うまくいかなかったところがあったので、以下にまとめる。
# nitroを無効
SSR無効にすると、nuxiコマンドに失敗する。
```nuxt.config.js
  ...,
  bridge: {
    nitro: false,
  },
```
でnitroを無効にするのが手っ取り早い。
https://github.com/nuxt/bridge/issues/27

# localhostのhttps対応
nuxt2では、mkcertコマンドなどで自己署名入り証明書をつくってnuxt.configのserver.httpsプロパティに設定していたが、コマンド引数で指定する
```
$ nuxi dev --https  --ssl-cert cert.pem --ssl-key cert-key.pem
```

# nuxt generateコマンド
`nuxi generate`コマンドが期待している出力をしてくれないので、nuxt2の`nuxt generate`コマンドは移行せずそのままつかうようにした。

# 参考
- [nuxt.configのssrをfalseにするとnuxt(nuxi) generateに失敗する](https://minerva.mamansoft.net/Notes/nuxt.config%E3%81%AEssr%E3%82%92false%E3%81%AB%E3%81%99%E3%82%8B%E3%81%A8nuxt(nuxi)+generate%E3%81%AB%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B)
- https://v3.nuxtjs.org/api/commands/dev
【Ubuntu】Jupyter LabでDeep Learningできる環境（Dockerコンテナ）を構築・Imageに保存（軽量版）【深層学習】

# 1. 背景
前回投稿した記事（[【LAN内 or WANから】ssh接続したUbuntuマシンのDockerコンテナ内でJupyter Labを起動・カスタマイズ【深層学習用】](https://qiita.com/greentiger/items/7c71708cc108f73707df))では，Anacondaをインストールするなどして，様々な環境を作りやすくしましたが，完成したDocker Imageのサイズは20GBとなり，コンテナを大量に作成すると，PCの容量を圧迫してしまうということや，最初のセットアップで大量のインストールをするため時間がかかってしまう，といったデメリットがありました．

それを解消すべく，Docker Imageのサイズが約4.4GBとなる軽量版の構築方法を試したので備忘録として記述します．
Anacondaにこだわらない方向けです．

前回の記事には記述していた「ssh接続の準備」などの部分や大まかな流れは特に変わらないので一部省略しています．
主に前回との変更点を記述しています．
# 2. 前提
## PC環境
GPUマシンは前回の記事のものとは違うマシンを用いたためスペックが違います（下がっています）．

- GPUマシン（サーバー側）
    - Ubuntu 20.04 LTS
    - Intel(R) Core(TM) i9-7900X CPU @ 3.30GHz
    - GeForce GTX 1080 Ti
    - メモリ64GB
    - GPUドライバーインストール済み
    - openssh-serverインストール済み
    - Dockerインストール済み
- ローカルPC（クライアント側）
    - MacBook Pro (13-inch, 2020, Thunderbolt 3ポートx 4)
    - メモリ16GB
    - macOS 12.2.1
    - 2.3GHzクアッドコアIntel Core i7

# 3. コンテナの作成，カスタマイズ，保存
ここからはGPUマシン内のterminalで操作しています（ローカルPCからssh接続でterminalを操作してOKです）．
## やりたいこと
- Dockerの中に入れるもの
    - cuda 10.1
    - Python 3.8.10
    - PyTorch 1.8.1
    - Jupyter Lab
    - ＋好きなライブラリなど

前回の記事と比較して，ライブラリのバージョンが古いなどの違いがありますが，セットアップしたGPUマシンのGPUが古かったため，対応しているGPUドライバーのバージョンなどの問題などから以上のバージョンでセットアップすることになりました．

## 手順
任意のバージョンのcudaを入れておいてください．
### Dockerイメージをpull
```bash:terminal
docker pull ubuntu:20.04
```

### 新規コンテナ作成 ＆ run
```bash:terminal
docker run -it --gpus all --name <コンテナ名> -v ~/Desktop/user1:/container/user1 ubuntu:20.04
```
### コンテナに各種ライブラリをインストール
ここで，2つのcommandファイルを作成し，実行を行うことでまとめて必要なライブラリや好きなライブラリを追加インストールします．

```bash:gpumachinesetup1.command
apt update
apt -y upgrade
apt install -y python3-pip
pip install torch==1.8.1+cu101 torchvision==0.9.1+cu101 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
pip install jupyterlab
apt install curl
apt install vim
apt install wget
apt install ffmpeg
apt install imagemagick
apt install nodejs npm
npm install -g n
n stable
apt purge nodejs npm
exec $SHELL -l
source ~/.bashrc
```

```bash:gpumachinesetup2.command
pip install jupyterlab-language-pack-ja-JP
pip install lckr-jupyterlab-variableinspector
pip install jupyterlab_nvdashboard
pip install ipywidgets
pip install black
pip install yapf
pip install isort
pip install jupyterlab_code_formatter
pip install jupyterlab_tabnine
pip install jupyterlab-lsp
pip install 'python-lsp-server[all]'
pip install pprintpp
pip install jupyterlab-spreadsheet-editor
pip install ipympl
pip install opencv-python
```

この2つのファイルをローカルマシンで作っておき，sshfsなどを用いてGPUマシンのコンテナで同期しているディレクトリに入れてコンテナ内からアクセスできるようにしておきます．

インストール時は以下のコマンドで実行できます．
```bash:コンテナ内terminal
bash <commandファイル名.command>
```

### Docker Imageの保存
全ての追加インストールが完了した後，コンテナから抜けます．
```bash:terminal
exit
```
現在のdockertestの環境を保存しておきます．
```bash:terminal
docker commit dockertest <新規Image名>
```
`新規Image名`は好きに決めてOKです．

保存が完了したら，あとは前回の記事と同じようにコンテナの作成やJupyter Labの実行ができます．

# 4. まとめ
今回は前回の記事の改良版（軽量化版）なので，細かい点の説明は省いています．
もしもわからなかったら[前回の記事](https://qiita.com/greentiger/items/7c71708cc108f73707df)を参考にしてみてください．

# 最後に
ミスなどありましたら，コメントでご指摘の程よろしくお願いします．

また，より便利な方法などございましたら是非教えて下さい．

ここまで読んでくださり，ありがとうございました．
情報の表現

復習回数1
## 情報量の単位
コンピュータ内部では、全ての情報は、電気信号の「ON」「OFF」のように扱われており、2進数の「0」と「1」に対応させ表現している。
コンピュータでの最小の単位を <font color="Blue">ビット(bit)</font>といい、2進数一桁に相当する。<br>
8個のビットを一つにまとめたものを<font color="Blue">バイト(byte)</font>といい、2進数八桁に相当する。また、byteは情報量の基本単位となっている。
「1」と「0」の羅列は、ビットパターンと言われてる。
1byte = 8bit
2byte = 16bit<br>
## 表現可能な情報量の公式
nビットでは<font color="Blue">2ⁿ通り</font>の表現ができる
## 大きい数値を表す接頭語
<font color="Blue">k(キロ) - 10³ </font>または<font color="Blue">  1k = 1024(2¹⁰)
M(メガ) - 10⁶
G(ギガ) - 10⁹
T(テラ) - 10¹²</font>
## 小さい数値を表す接頭語
<font color="Blue">m(ミリ) - 10 -³
μ(マイクロ) - 10 -⁶
n(ナノ) - 10 -⁹
p(ピコ) - 10 -¹² </font>
## 文字の表現
コンピュータが文字を扱うことができるのは、文字毎に2進数で表現された<font color="Blue">文字コード</font>という識別番号が割り振られているから。
文字化けは文字コードの違いから発生する。
### 代表的な文字コード
<font color="Blue">ASCII </font> - 英数字・記号・制御文字のみ。漢字・仮名は無い
<font color="Blue">シフトJIS  </font>- ASCIIに漢字・仮名を加えたもの
<font color="Blue">EUC  </font>- UnixやLinuxなどで使用される。漢字・仮名も可能
<font color="Blue">Unicode </font> - 世界の文字を体系にしたもの。UTF-8などがある。

参考文献 : イメージ＆クレバー方式でよくわかる 栢木先生の基本情報技術者教室
Databricks Delta LakeのRESTOREコマンド

[RESTORE \(Delta Lake on Databricks\) \| Databricks on AWS](https://docs.databricks.com/spark/latest/spark-sql/language-manual/delta-restore.html) [2022/1/14時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

:::note info
**注意**
Databricksランタイム7.4以降で利用できます。
:::

Deltaテーブルを以前の状態にレストアします。以前のバージョン番号、タイムスタンプへのレストアがサポートされています。

# 構文

```sql:SQL
RESTORE [TABLE] table_name [TO] time_travel_version
```

# パラメーター

- [table_name]([table\_name](https://docs.databricks.com/spark/latest/spark-sql/language-manual/sql-ref-names.html#table-name))

レストアするテーブルを指定します。テーブル名に[temporal specification](https://docs.databricks.com/spark/latest/spark-sql/language-manual/sql-ref-names.html#table-name)(@によるタイムスタンプ、バージョン指定)を使用することはできません。

```sql:SQL
time_travel_version
 { TIMESTAMP AS OF timestamp_expression |
   VERSION AS OF version }
```

ここでは、

- `timestamp_expression`は以下のいずれかとなります。
    - '2018-10-18T22:15:12.013Z'、すなわち、タイムスタンプにキャストできる文字列です。
    - `cast('2018-10-18 13:36:32 CEST' as timestamp)`
    - `'2018-10-18'`、すなわち日付文字列です。
    - Databricksランタイム以降では以下も利用できます。
        - `current_timestamp() - interval 12 hours`
        - `date_sub(current_date(), 1)`
        - タイムスタンプにキャストできる他の表現
- `version`は、`DESCRIBE HISTORY table_spec`の出力から得られるLONG値となります。

`RESTORE`コマンドの詳細に関しては、[Deltaテーブルを前の状態に復旧する](https://qiita.com/taka_yayoi/items/152a31dc44bda51eeecd#delta%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E5%89%8D%E3%81%AE%E7%8A%B6%E6%85%8B%E3%81%AB%E5%BE%A9%E6%97%A7%E3%81%99%E3%82%8B)を参照ください。

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
Pandasインストールの備忘録

https://www.youtube.com/watch?v=XfoYeWCzjac
こちらの動画を見ながら実際にpandasを使うためにjupyterlabを使用
拡張機能 
$ jupyter labextension install @lckr/jupyterlab_variableinspectorを実行したところ
node.jsがインストールされていないためエラー発生。

ならばと
$conda install nodejs.
を実行。
しかし下記のエラーが発生。

PackagesNotFoundError: The following packages are not available from current channels:

調べるとインストールしようとしているチャンネルには該当ライブラリが無いことが原因のよう
参考：https://hitokageblog.com/condalisterror/
なので、https://anaconda.org/conda-forge/nodejs
上記のページから
$conda install -c conda-forge nodejs を実行。


これでnode.jsをインストールできたので再度
$jupyter labextension install @lckr/jupyterlab_variableinspectorを行なったところ無事に機能拡張ができた見たい。
DataTablesが日本語化されない時の対処法


結論からお伝えするとhttpからhttpsに変更するだけです。多分下記のような感じでScriptを書いていると思います。

    <script>
        jQuery(function($){
            $.extend( $.fn.dataTable.defaults, {
                language: { url: "http://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Japanese.json" }
            });

            $("#foo-table").DataTable();
        });
    </script>


その中のurlのhttpをhttpsに変えるだけで機能すると思います。下記のような感じ。

    <script>
        jQuery(function($){
            $.extend( $.fn.dataTable.defaults, {
                language: { url: "https://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Japanese.json" }
            });

            $("#foo-table").DataTable();
        });
    </script>

参考の一つとなれば幸いです。
やっぱビッグエンディアンは音がいい

どうにかこうにか使えるようになった[pcmドライバー](https://qiita.com/yamori813/items/2299a81ed21775752a1d)のその後です。

flashが4Mだったのでmpg123を入れることができず、flashを8Mに張り替えました。ところが元のu-bootをコピーしたのですが書き込みができません。もともとeFONの4MだったのですがMXの8Mにしたのが原因のようです。あいにくeFONの8Mは手持ちがありませんし、u-boot_modを使う方法も考えたのですが、いろいろやってパターンをはがしたりして壊すともともこうもなくなるので結局元に戻して、[二階建てflash](https://qiita.com/yamori813/items/77ef5e96cc719d29a5e7)のままで8Mにすることにしました。

![写真（2022-04-23 12.57）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/c2376b04-8e30-a7b2-e924-558ff1fa0f13.jpeg)

GPIOは12をSPI_CS_1にしてResetボタンのGPIO17をSPDIF_OUTにすることにしました。

Resetボタン付近の回路は以下のようになっています。

![写真（2022-04-18 5.41） #2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/697e808d-3e17-06f1-7ca9-49ab0de356d1.jpeg)

チャタリング防止のC56をはがして、プルアップぽいR32もはがしました。これでGPIO17直結になるのですが、オシロスコープで見ても全く信号が出てません。

~~余談ですがR33にもGPIOが出ているような気がします。~~ R33の先はハードウエアリセットでした。

以前と同じようにSPDIF_OUTを12でSPI_CS_1を17だとだとSPDIFが出力されます。何かがおかしいです。

```
# SPI_CS_1
hint.gpio.0.func.17.gpiofunc=7
hint.gpio.0.func.17.gpiomode=1
# SPDIF_OUT
hint.gpio.0.func.12.gpiofunc=25
hint.gpio.0.func.12.gpiomode=1
```

おそらくシャープの[トスリンクコネクタ](https://qiita.com/yamori813/items/4e6eaf0137ca7499148e)を付けて、UDA1355HをつかったSPDIFを受けられるDACにつないでみたのですが、音がでません。

![写真（2022-04-23 15.39）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/740d3599-b461-ac4a-0e04-2f8c609363f3.jpeg)

ひょっとして属性ビットなどの相性の問題かと思ってMacにつないだRolandのAudio If(UA-1G)につないだところ音がでました。OnkyoのSE-U55でもちゃんと音が出ました。

ZRouterに入れたテストプログラム(sin)で音を出すと聞き覚えのあるノイズになります。ちょっと考えてみたところビッグエンディアンのオプションを付けたことを思い出して試したら、正しくサイン波が鳴るようになりました、

32Kの出力ですがノイズもなくmpg123でWBGOが聞けていい感じです。

秋月の掘り出し物処分特価品の74HC04のバッファを入れていたのですが、UDA1355Hはダメでした。

![写真（2022-04-23 11.52）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/787f4a34-7088-be47-5b8b-4935def46b7c.jpeg)

SPDIFからアナログ変換でSE-U55はスタンドアローンでは使えないので、USBがある他のモジュールにつないで、カーネルモジュールのsnd_uaudioをロードする必要があります。SE-U55は消費電力が大きいので、アダプタをつないで、セルフパワーにします。またmixerコマンドでvolやpcmを大きくしなと音が小さいです。

```
# mixer vol 100 pcm 100
Setting the mixer vol from 75:75 to 100:100.
Setting the mixer pcm from 75:75 to 100:100.
```

SE-U55は元祖USBオーディオの製品で、今となってみるとDACやOPアンプなどあまり良いチップは使ってないのですが、ストリーミングを再生するには十分です。

![IMGP0003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/42606ce4-6b51-b80f-8b31-991eea9de33e.jpeg)

SE-U55のUSB Audioで鳴らせばいいと思われる方がいるかもしれませんが、これを接続しているモジュールはMIPS 24KなSOCでFreeBSDのsoundがまともに動きません。

USBオーディオの製品もいち早く出していたり、WolfsonのDACを採用していた、ONKYOさんがうまくいかなくなったのは至極残念です。

当初うまく動かなかった、48Kの再生も上記の74HC04のバッファやケーブリングのやり直しが効いたのか、再生できるようになりました。

トスリンクコネクタはネジ止めできる東芝の物に代えてフリスクのケースに入れてみました。

![IMGP0007.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/bf73f4da-5be6-befa-5182-b548405449ec.jpeg)

いつのまにかUDA1355Hでも鳴るようになってました。

ファンレスだし良いストリーミングプレーヤだと思います。このターゲットはたしかアダプタなしで108円だった思います。

IPはビッグエンディアンなのでストリーミングラジオはビッグエンディアンがいいですね。
どのプログラミング言語を勉強すればいいか分からない時にみる画像 

![main-qimg-d5279a67971e0d64ff2fa29eb0501789.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282788/2e720066-7729-e705-79fd-3680d209ea3c.png)
安価なDFPlayer miniを使ってmicro:bitで音声を出力してみた

# micro:bitによる音声出力

教育向けマイコンボードmicro:bitは手軽に利用できるものの、テキストをしゃべらせたり、録音音声を再生したりすることが標準機能では対応できず、何かに反応して声を出すといった仕掛けが手軽には作れなかった。
そこでAmazonで見つけた、DFPlayer miniという１個220円※のモジュールを使ってmicro:bitによる音声出力を検証してみた。

※DFPlayer miniの価格は執筆当時の価格です。

# DFPlayer miniとは
DFRobot社による小型・安価なアンプ内蔵MP3 Playerモジュールで、電源とスピーカーを取り付ければ、マイコン制御可能なMP3 Playerが出来上がる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696263/ed881184-4c90-f282-0a4c-33134ce8dc31.png" width="40%">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696263/89f36e4c-6650-3875-bc72-88ab4bcef147.png" width="75%">

上記仕様は[Dfrobot社HP](https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299)から引用

# 工作工程

【用意したもの】

- パソコン
- 拡張ボード(Keyestudioセンサーシールド)
- micro:bit本体
- DFPlayer mini
- micro SDカード
- 100均スピーカー
- USB micro Bケーブル
- ブレッドボード
- ジャンパーケーブル
- ワニ口クリップ
- カーボン抵抗 1kΩ

## 1. 回路を組む
DFPlayerの電源供給は3.2V～5.0Vなので拡張ボードから3.3Vを供給することにする。
マイコン側とのインタフェースにはRx/Txピンを用いて、UARTによるシリアル通信でコマンド制御を行うが、推奨回路図を見ると、Rxには1kΩの抵抗を介すとあるので、それに従う。

出来上がった回路はこちら。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696263/c267b1c2-eee4-a952-1c96-6604d03c376f.png" width="36%">

## 2. コードを組む

### 1)ライブラリを取り込む
DFPlayer miniの専用ライブラリがあるので取り込む。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696263/0297aed8-6a39-b523-23b5-72f7e15c8fb6.png" width="30%">

このライブラリを用いると指定ファイルやすべてのファイルの再生、停止、ポーズ、ボリュームの調整、イコライザー調整（Normal、Pop、Rock、Jazz、Classic、Base）が可能。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696263/4f400dcf-7ac6-c087-fed6-023e8d515057.png" width="60%">

### 2)ブロックによるコード
シンプルにmicro:bitのボタン操作に対応するコードを組んでみた。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696263/b93d4af6-f9fa-3220-9be4-0a4e6f5d67c2.png" width="80%">

## 3. 録音音声を用意する
マイクロSDカードに任意のフォルダ「voice_files」を用意して、シリアル番号を付してmp3ファイルを8個格納してみた。

# 完成

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696263/bf6e7729-d77d-26b6-2709-59e694917e02.png" width="50%">

思ったより、簡単なコードでMP3を指定再生できたのが印象的。
センサに反応させて、特定のセリフを言わせたり、効果音を流したりなんてことも手軽にできそう。
micro:bitに標準実装されているミュージック機能では、楽譜から１音ずつコードに落としてゆかねばならなかったので、録音音声が使えるとなると格段にお手軽さが増す。


# マイコン不要でも操作ができる
データシートを見ると、I/Oモード、AD keyモードが用意されていて、どうやらマイコン無しでも制御ができる製品になっている。
試しにIO_1とIO_2ピンにタクトスイッチを２つ取り付けてみると、見事にマイコン無しで動作した。
他にもAD Keyを使って抵抗値の違いによる識別で色々な操作も可能だそうで、これを使えば本格的なMP3 Playerが簡単に作れそう。時間があるときに試してみたい。

いかがでしたか？
今回は安価なDFPlayer miniを使ってmicro:bitで音声を出力してみました。
【LINE Messaging API, GAS】「漢字を構成に含む漢字」を扱う（LINE Bot に新機能追加）

## はじめに
こちらの LINE Bot に新たな機能を追加した。

https://qiita.com/probabilityhill/items/d3ae1c862bad592cd28c


## 検索方法
「Xを構成に含む漢字」は `<X>` と表す。（全角記号対応）

（比較として）**月**＋2文字：`漢字ー月？？` → **月**曜日, **月**見草 など
**月を構成に含む漢字**＋2文字：`漢字ー＜月＞？？` → **消**防士, **静**脈血 など

## 実装
[kanjivg-radical](https://github.com/yagays/kanjivg-radical) を使用する。
[![Gradient](https://assets.paperspace.io/img/gradient-badge.svg)](https://console.paperspace.com/github/yagays/kanjivg-radical/blob/master/example/basic_usage.ipynb)
以下のページで詳しく説明されている。

https://yag-ays.github.io/project/kanjivg-radical/

### 1. JSON ファイルを Google Drive にアップロード
https://github.com/yagays/kanjivg-radical

git clone または zip ファイルを [ダウンロード](https://github.com/yagays/kanjivg-radical/archive/refs/heads/master.zip) し、`data` フォルダの中にある `element2kanji.json` を Google Drive にアップロード。

### 2. JSON ファイルの ID を取得
JSON ファイルの共有リンク `https://drive.google.com/file/d/[ID]/view?usp=sharing` から ID を取得。

### 3. GAS のコード

```javascript:gs
const e2kId = "JSONファイルのID";
const e2kFile = DriveApp.getFileById(e2kId);
const e2k = JSON.parse(e2kFile.getBlob().getDataAsString("UTF-8"));

function getE2kRgx(str){
  return "("+e2k[str].join("|")+")";
}

// 確認
console.log(getE2kRgx("月"));
// (溯|齟|蔡|羂|肝|育|胆|朦|輸|梢|脱|愈|壓|瀛|撒|臓|愉|峭|縢|筋|朧|柤|廟|肓|砠|體|鵑|肌|背|蜻|肱|畳|臑|祖|壟|兪|脾|狷|脳|臟|哨|胄|宜|肋|悁|宵|酳|蔣|謂|髓|騰|藤|龕|肛|絹|鶺|肴|情|蛆|脣|厭|贏|静|逍|有|肩|悄|組|鷂|龍|肚|鶻|胃|肺|胎|橢|脈|脯|愬|臚|沮|徂|囿|鞘|撚|狙|脉|鍮|肖|羆|倩|謄|髑|姐|猜|胥|岨|腆|莇|揃|膰|膿|腓|潮|胤|胸|襲|窰|骼|楡|瑤|胝|鏘|剪|膝|繃|膽|腑|腹|臆|遙|趙|消|萠|擦|塑|餬|腟|腋|腸|膳|蘢|膾|繖|膊|膓|錆|胞|籐|糊|精|覦|髀|骨|髏|髄|骰|渝|粗|胚|膚|屑|繝|膃|膺|腔|菁|遯|揄|遡|態|腕|蛸|臍|臂|膂|耡|蝓|胖|翦|磆|清|胛|胯|膏|膀|臀|腎|膨|炙|租|且|捐|霸|霄|膩|膵|膕|膜|蝟|胡|察|硝|踰|然|寵|侑|咀|膸|膤|腦|諭|胱|胙|蝴|雎|陏|骭|滕|渭|癇|偸|籘|棚|腰|腫|腿|弸|腱|腥|覇|削|能|楜|楕|隨|燗|隴|癒|硼|随|際|渣|隋|苴|俎|墮|萌|擺|擶|腴|明|膣|潸|助|猾|瑜|前|槊|榾|籠|骸|滑|散|瑚|胼|瓏|惰|崩|銷|臉|霰|郁|涓|萓|搖|婿|淆|臈|腮|膠|腺|轍|膈|胴|湖|奬|盟|朞|肪|獎|疽|靖|堋|脹|脅|鋤|誼|疊|朝|燃|謠|醐|蹐|罷|肘|朋|勝|喻|期|聾|詛|脇|臘|誚|脊|冐|嫺|徹|靜|逾|靨|撤|豚|祭|冑|爼|箙|臙|脆|肥|喩|肬|鵬|煎|阻|瘉|股|朕|勗|將|黶|脂|壻|賄|瀧|睛|娟|脚|晴|鮹|望|朏|肯|朔|宥|喟|瘠|羸|熊|朖|肢|肭|服|魘|箭|鮪|脩|嘲|葫|青|堕|徭|脛|疂|漿|請|査|朗|稍)
```

`getE2kRgx(str)` で `str` を構成に含む漢字の正規表現（漢字1 or 漢字2 or …）が得られる。

他の部分の実装は以下の記事をご参考に。

https://qiita.com/probabilityhill/items/d3ae1c862bad592cd28c
【集合論】ℝと単位円 S¹ の濃度が等しいことの証明

## はじめに
集合論を復習していたところ，ニューラルネットワークの活性化関数などに使われる**シグモイド関数**
が利用できそうだなーっていう証明を思いついたので紹介します。

## 問題
実数の集合$\mathbb{R}$と単位円$S^1:=\\{(x,y)\in\mathbb{R}^2\ |\ x^2+y^2=1\\}$の濃度が等しいことを示せ。

:::note
集合$A,B$の**濃度が等しい** $\left(|A|=|B|\right)$
$\iff$ $A$から$B$への全単射が存在する
$\iff$ $A$から$B$への単射と$B$から$A$への単射がともに存在する（**Bernsteinの定理**）
$\iff$ $|A|\leq |B|$ かつ $|B|\leq |A|$

**$A$から$B$への単射が存在するとき，$|A|\leq |B|$とかく。
:::
## 証明

写像
```math
\begin{align*}
S^1 &\to \mathbb{R}\\
(\cos x,\sin x) &\mapsto x\ \ \ \ (0 \leq x < 2\pi)
\end{align*}
```
は単射だから，$|S^1|\leq|\mathbb{R}|$。

一方，写像$f$を
```math
\begin{align*}
\mathbb{R} &\to (0,1)\\
x &\mapsto f(x) := \frac{1}{1+e^{-x}}\ \ \ (\text{シグモイド関数})
\end{align*}
```
とすると$f$は単射。
（下図をみると，シグモイド関数は実数全体を開区間(0,1)に写す関数だとわかる）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1900599/69ce7fe4-6af2-eeef-afbe-8cb37be7da42.png)


よって写像
```math
\begin{align*}
\mathbb{R} &\to S^1\\
x &\mapsto (\cos2\pi f(x), \sin2\pi f(x))
\end{align*}
```
も単射だから$|\mathbb{R}|\leq|S^1|$。

したがってBernsteinの定理から，$|\mathbb{R}|=|S^1|\ \ \ \Box$
同一ファイル名のファイルが同一のディレクトリに現れるトラブル

同一ファイル名のファイルが同一のディレクトリに現れる
---
言葉で説明するよりも見たほうがわかりやすので、下記のようなコマンドと結果が返るとします。
※前提として”同一ファイル名で同一のディレクトリに作成することはできません”ということを念頭に。

状況の調べ方
---

```
$ ls -li
```
```
268947 -rw-r--r-- 1 root root  8 Dec 20 12:32 app
268944 -rw-r--r-- 1 root root 24 Dec 20 12:33 app
```

この場合どうやって状況を調べますか？
iノードは別なので同一ファイルを間違って表示しているのではないように見えます。
まずはファイルの名前にフォーカスして疑ってみましょう。
- ファイル名の最後に空白がついている
- ファイル名がUNICODEのファイル名で、ASCIIの'aaa'とUNICODEの'aaa'で異なっている
などが経験的に可能性があります。ではこれをどうやって検証するかですが、

```
$ ls -m            
app, app , test.sh
```
のような結果が得られれば、最後に空白がついているのが一目瞭然！です。
また
```
$ ls -m | od
```
のようにしてHEXの結果を得ることにより、空白を確実に見つけることもできます。
また別のパターンとして、UNICODEとASCIIで異なるような場合は、LANG=CとしてEnvironment Variableを変更の上lsコマンドを実行します。
このとき、Unicodeのコードの場合表示が変化します。
```
268947 -rw-r--r-- 1 root root  8 Dec 20 12:32 app
268944 -rw-r--r-- 1 root root 24 Dec 20 12:33 a\321\200\321\200
```

同じファイルが２個もある、という状況にもし遭遇するようなことがあったら頭に入れておくと混乱を避けられるかと思います。
競馬で資産運用するなら、的中率は大事です

# まえがき
こんにちは。馬風(うまかぜ)と申します。
競馬AIを自作し、資産運用を目論んでいます。
今回は、資産運用するにあたって、回収率だけでなく的中率も結構大事ということをお伝えします。
# 資産運用の仕方から考える
資産の増幅を最大化する方法として、**ケリーの公式**があります。
これは、複利運用をする際に、総資産の何%を賭ければよいか導き出すものです。
この公式から的中率の重要さが見えてきます。
# ケリーの公式とは
__f* = 期待値 / 最大オッズ__
で表されます。
ここでいう期待値は競馬では長期の回収率のプラス分に置き換えられます。
回収率120%なら、1.2 - 1 = 0.2ということです。
最大オッズは、競馬では買い目のなかで最もオッズが高い組み合わせということになります。

# 的中率は大事
単勝1点勝負、賭けるオッズは一定とすると、**ケリーの公式は的中率と近い関係**であることがわかります!
**的中率 * オッズ = 回収率**
より、
**的中率 = 回収率 / オッズ**
となります。

回収率から元の1を引くかどうかの違いですね。
実際は賭けるオッズは一定ではありませんが、その傾向がわかります。

# シミュレーション
実際に簡易シミュレーションしてみます。計算のため、一定のタイミングで的中することにします。

- 総資産 100万円スタート
- 100レース勝負

ケース1
**回収率120%、的中率5%**
![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632128/68281663-8d7e-cef3-58b7-6fe1f9c723a6.png)
最終総資産：1 085 800円

ケース2
**回収率120%、的中率10%**
![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632128/df5580f9-c36a-3ef6-a900-2ff5df710c7e.png)
最終総資産：1 188 000円

ケース3
**回収率120%、的中率20%**
![ダウンロード (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632128/023693f1-90a1-ae4a-beaf-dde85366d77e.png)
最終総資産：1 449 400円

ケース4
**回収率150%、的中率5%**
![ダウンロード (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632128/7547da86-8a83-038e-b461-60cd6ff9d135.png)
最終総資産：1 456 400円

的中率5%と20%では、大きな差が出るのがわかると思います!
的中率の違いによっては回収率120%と150%で最終総資産がほとんど変わらないのは驚きですよね!

# あとがき
今回は、複利運用するなら的中率も馬鹿にならないというお話でした。
私も競馬AIを改良するとき、大穴狙いにはせず、的中率を安定して出せるモデルを目指して試行錯誤しています。

[誰でもかんたん！ 自分の競馬予想AIを作ろう！](https://zenn.dev/umakaze/books/312ddc2bbf3601)
↑
競馬AIの作り方を販売しています。まずはコピペで**即実行可能**、さらに、説明を加えて理解も進む内容です。
カスタマイズも可能です。世界に1つだけのオリジナル競馬AIを作りましょう!
BATでサービスを開始を確認、開始していない場合はサービスを開始　バッチ　サービス　自動

```bat:service.bat
@echo off
REM FOR文で遅延変数を使用するため
SETLOCAL enabledelayedexpansion

REM テキストに開始するサービス名を記載””でくくる
set SERVICELIST=C:\Service_Start_list.txt

REM for文で全てのサービスを開始させる
for /f "delims=,;" %%i in (%SERVICELIST%) do (
  REM 問題なく処理が終わればここでfor文を抜ける
  if %%i equ "" goto end
  set SRVC_STAT=0
  REM サービス起動状態確認
  net start | find %%i 1>NUL
  if !errorlevel! equ 0 (set SRVC_STAT=1)

  REM サービスが開始している場合はSRVC_STAT=2にしてforの中を通過
  if !SRVC_STAT! equ 1 (
    echo %%iサービスは既に開始しています
    set SRVC_STAT=2
  )

  REM サービスが開始していない場合は開始、開始できなかったらSRVC_STAT=1に設定
  if !SRVC_STAT! equ 0 (
    echo %%iサービスはまだ開始していません
    net start %%i
    if !errorlevel! neq 0 (set SRVC_STAT=1)
  )

  if !SRVC_STAT! equ 1 (
    echo %%iの開始に失敗しました。
    REM 問題有りで失敗した場合ここでfor文を抜ける
    goto err_end
  ) 
)
:end
echo "全てのサービスの開始が完了いたしました"
pause
exit
:err_end
echo "サービスの開始を失敗いたしました。"
pause
exit
```
ブラックジャックのなんちゃって設計(js) part3 プロパティとかメソッドをもう少し詳しく

# この記事について
各クラスのプロパティ､メソッドの役割を説明したり
メソッドとかの動きをフローチャートとかにしてみた
ただほぼ1行でかけてしまうものは省略
以下`c()`はコンストラクタから値を受け取ることを指す
コードはこちらにあります

https://github.com/s19013/webBlackJack-jsOnly-

# Player
**説明**
プレイヤーに必要な動きをまとめたクラス
### プロパティ
| 変数/定数 | 名前 |型| 説明 | 初期値 |
|:-:|:-:|:-:|:-:|:-:|
|変数|myDeck|[Card]|受け取ったCardクラスを保管|[`空`]|
|変数|sum|int|通常時の手札の合計値を保管|0|
|変数|sumAIn|int|手札に[A]がある時の<br>手札の合計値を保管|0|
|変数|AInMyDeckFlag|boolean|手札に[A]があるかどうかを管理|false|
|変数|tenInMyDeckFlag|boolean|手札に[10点札]が<br>あるかどうかを管理|false|
|変数|burstFlag|boolean|手札の合計値が21を<br>超えていないか管理|false|
|変数|burstAInFlag|boolean|手札に[A]がある時の手札の<br>合計値が21を超えていないか管理|false|
|変数|bjFlag|boolean|ブラックジャックが<br>成立しているか管理|false|

### メソッド
#### addCard(card)
**役割**
引数に受け取った`Card`クラスを`List`加える

#### sumCard()
**役割**
`List`の中にあるすべての`Card`クラスの`value`を`sum`に足す
![sumCard.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421721/7efa2207-f88e-b4d3-4bc2-9eff14d9d59a.png)

#### sumCardAIn()
**役割**
`List`の中に`A`があった時`sumAIn`に`sum`に10加えた値を入れる

#### checkAInMyDeck()
**役割**
`myDeck`の中に`A`が入っているか確認する
![checkAinMyDeck.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421721/d7cd326f-d34f-d7ff-c54e-15a51a6a022f.png)

#### checkTenInMyDeck()
**役割**
`myDeck`の中に`10点札`が入っているか確認する

#### checkBJ()
**役割**
ブラックジャックが成立しているか確認する
![checkBJ.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421721/c2cec3dc-12b5-3a47-3bb4-cc8993374e02.png)

#### checkBurst()
**役割**
`sum`が21を超えていないか確認

#### checkBJ()
**役割**
`sumAIn`が21を超えていないか確認

# Computer
**役割**
`Player`クラスを継承して､コンピューターが使う動作を加えたクラス

### メソッド
#### isSumUnder16()
**役割**
`sum`が16を下回っているか調べる

#### isSumAInUnder16()
**役割**
`sumAIn`が16を下回っているか調べる

## Q&A 
Q:なぜ`sumCard()`で最初に`sum = 0`をしているの?
A:そうしないと､古い計算結果に新しい計算結果を足すことになるから
アドレスクラスとは(基本情報技術者試験)

今回はアドレスクラスについての理解を深める為に記事を書いてみました。

そもそもIPアドレス自体全然分からないよという方は本記事の前にこちらの記事から先にご覧くださいませ。

https://qiita.com/Haruki-Sakamoto/items/6bdf99f135d37043879a

# アドレスクラス
IPアドレスをネットワークの規模によって5種類のクラス(クラスA~クラスE)に分ける為のもの。
クラスA~Cの3つはネットワーク用で利用されるIPアドレスで、クラスDとクラスEは別の特殊な使い方をするらしい。

## クラスA
大規模ネットワーク用でホスト1677万7214台分
0.0.0.0 ~ 127.255.255.255
## クラスB
中規模ネットワーク用でホスト6万5534台分
128.0.0.0 ~ 191.255.255.255
## クラスC
小規模ネットワーク用でホスト254台分
192.0.0.0 ~ 223.255.255.255
## クラスD
マルチキャスト用のアドレスで使われている。決められた複数のホストに送信するアドレス。
224.0.0.0 ~ 239.255.255.255
## クラスE
将来の実験用で使われるアドレスで今の所は使われていないらしい。
240.0.0.0 ~ 255.255.255.255
# ネットワークアドレス
ネットワークアドレスとは、<font color="red">ホストアドレス部</font>が全て0になるアドレスでネットワーク自体のアドレスを指す。
192.168.0<font color="red">.0</font>/24

※ /24は2進数の左から24桁目までが<font color="blue">ネットワークアドレス部</font>を指す表記の事。

# ブロードキャストアドレス
<font color="red">ホストアドレス部</font>が全て1になるアドレスでネットワーク内のアドレス全てに送信できるアドレス。
192.168.0<font color="red">.1</font>/24

# まとめ
アドレスクラスは5種類に分類されて一般的に使われるのはクラスA~Cまで。
ネットワークアドレスとブロードキャストアドレスはホストの台数として含めることが出来ない。


# 参照
https://wa3.i-3-i.info/word11984.html
https://www.geekpage.jp/technology/ip-base/class.php
https://livra.geolocation.co.jp/iplearning/181/
MySQLでデータを一括削除してidを1から振りなおす

# [<font color="#333">やる事</font>](#-やる事)
データベースのテーブル内の情報を有無を言わさず全削除。そしてidを1から振り直します。
外部キー制約付いてると一括削除できません。


```
//一括削除
truncate table テーブル名;
```

```
//idを1から振りなおす
ALTER TABLE テーブル名 AUTO_INCREMENT =1;
```
ていう感じです。
[3分クッキング]NestJSテストコード

# はじめに

NestJSのテストコードの作り方を簡単に解説します。

# やること

1. ファイルを作成する
1. describeを作成する
1. itを作成する
1. expectを作成する
1. テストを実行する


# ファイルを作成する

NestJSは、*.spec.tsというファイル拡張子を自動でテストコードと識別します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533960/d5067b06-4a9b-a11d-97a2-5d8add86dfca.png)
src配下にexample.spec.tsを用意した図。


以下コマンドを実行した際、ファイルに記述したテストコードを実行してくれます。
```sh
npm run test
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533960/6bb3bedc-c6f6-e09d-be19-38984cf230ef.png)
テスト実行後の画面


# describeを作成する

ファイルを作成した後、describeというメソッドを用意します。
describeによって関連するテストをまとめるために使用します。

第1引数は、テストの説明
第2引数は、テストケースを記述するためのコールバック関数

```ts
describe('サンプルテスト', () => {
  // ここにテストケースを追加する
})
```

# itを作成する

describeでテストブロックを作成後、itというメソッドを用意します。
itメソッドは、実際のテストケース名を記述します。

第1引数は、テストの説明
第2引数は、テスト内容を記述するための、コールバック関数
第3引数は、タイムアウト（単位：ミリ秒） ※デフォルトタイムアウトは５秒。省略可能。

```ts
describe('サンプルテスト', () => {
 it('戻り値がtrueとなる', () => {
   // ここにテストコードを記述する
 })})
```


# expectを用意する

最後にexpectメソッドを作成します。
テスト結果を評価するメソッドとなります。

```ts
describe('サンプルテスト', () => {
 it('戻り値がtrueとなる', () => {
    expect(true).toBe(true); // 成功する例
    expect('Hello').toBe('hellooo'); //失敗する例
 })})
```

# 実際にテストコードのサンプル

テストファイルを作成して、以下のテストコードを記述します。
２つの数字を足し算するメソッドを用意して、そのテストコードを作成しました。

```ts:example.spec.ts
function addNumbers(num1: number, num2: number) {
  return num1 + num2;
}

describe('加算する', () => {
  it('２つの数字を加算します', () => {
    expect(addNumbers(2, 2)).toEqual(4);
  });
});
```

実行してみます。

```
npm run test
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533960/27b62252-95fc-0971-c619-99ea048c9782.png)

無事に期待した処理結果が返ってきて、テストがPASSした図。


以上です。
M1 Macにpodmanいれてみた

## はじめに
諸処の事情でDocker desktopをいれることができなくなったので、podmanで代用することにしました。
導入自体は先達のみなさまのおかげでさくっとできましたが、CPUのアーキテクチャーで躓きました。
その備忘録です。

## podomanのインストール


brewを使ってのインストール

```
$ brew install podman
```

podman導入の確認
```
$ podman -v
podman version 4.0.2
```

podman machineの導入

```
$ podman machine init
```

podmanのmachineの起動

```
$ podman machine start
(略)
 Machine "podman-machine-default" started successfully
```
(Op)podmanのmachineのリスト

```
$ podman machine list
NAME                     VM TYPE     CREATED      LAST UP            CPUS        MEMORY      DISK SIZE
podman-machine-default*  qemu        10 days ago  Currently running  1           2.147GB     107.4GB
```

(op)podman machineの停止

```
$ podman machine stop
```

## CPUアーキテクチャーの違いによる躓き

Dockerfileからコンテナイメージをbuildし、、、buildはできたのですが、コンテナを動かすとエラーがでるのです。。。
intel系(Linux)のSW(このときはocコマンド)をコンテナイメージに導入するときに、以下のようなエラーが出て使えません。


```
$ podman exec -it <コンテナID> bash
bash-4.4$ oc
bash: /usr/local/bin/oc: cannot execute binary file: Exec format error
```
理由はCPUがintelではないから、ということがわかりました。
そのため、podman machineにrpm-ostreeコマンドでqemu-user-staticを導入し、build時にオプションをつけています。

podman machineへ追加機能の導入

```
podman machine ssh sudo rpm-ostree install qemu-user-static
```

podman machineの再起動
```
podman machine ssh sudo systemctl reboot
```

container imageのbuild  
`--platform linux/amd64`をオプションとして追加

```
podman build --platform linux/amd64 -t <docker-image名> .
```

# 参考

[macOSでPodmanをDockerの代わりに使う](https://zenn.dev/polyomino/articles/podman-introduction)

[M1 MacでビルドしたコンテナイメージをOpenShiftにデプロイするまで](https://qiita.com/shin7446/items/6d3efe82abf6f3b9c71d)