Molybdenum

あれこれ記録がてらに書いていきます。

【Rust】完全数をRustで求めてみる

あらまし

たまたまインターネットで完全数の魅力みたいなものを見たので、ちょっとはコンピュータを計算機らしい使い方しようと思い、Rustで求めてみました。

完全数とは

完全数とは、自分自身を除く正の約数の和と等しくなる自然数のことです。
出典:完全数とその魅力について-「博士の愛した数式」を観て、改めて数字の持つ奥深さに魅せられました- |ニッセイ基礎研究所
要するに、ある数について正の約数の和が2倍になるか、もしくはその数引いたら等しくなるか、ということです。

ロジック

以下の処理を繰り返すだけ。
1. 約数の合計を求める
2. 元の数を減算し一致するか確認
3. 一致したら出力

なお、約数を求める際は、以下の効率的な解法を利用いたします。

あるNの約数を列挙する際は、i = 1から\sqrt{N}まで以下の処理を繰り返す。
N \div i = x \cdots 0ならば、ixを約数として保存する。

これは要するに、 N = \sqrt{N} \times \sqrt{N} が成立する場合をイメージしていただけるとわかりやすいかもしれません。
全部の数字に対する探索だとO(N)ですが、この解法だとO(\sqrt{N})に削減できます。

ソースコード

fn divisors(n: usize) -> usize {
    let mut sum: usize = 0;
    for i in 1..=(f64::sqrt(n as f64) + 1e-9) as usize {
        if n % i == 0 {
            sum += i;
            if i != n / i {
                sum += n / i;
            }
        }
    }
    return sum;
}

fn main() {
    for i in 1.. 10000 {
        let x = divisors(i);
        if (x - i) == i {
            println!("It's perfect number: {}", i);
        }
    }
}

実装の際の参考にしたサイト:素数表を使わない約数の列挙 - Qiita

出力結果

$ rustc main.rs
$ time ./main
It's a perfect number: 6
It's a perfect number: 28
It's a perfect number: 496
It's a perfect number: 8128
./main  0.04s user 0.00s system 9% cpu 0.431 total

まとめ

簡単ですが、完全数をRustで求めてみました。普段だととりあえずC++で書いてみるかとなってしまうのですが、勉強中ということもあり練習がてらにサクッと書きました。 たまには数学のこういう問題をサクッと解くのも気分転換になって楽しいですね。

GitHubにもコードを上げています github.com

フィンテックエンジニア養成勉強会8(新しい仲介法制)[Fintech協会 後援] 参加記 #finengine

5/7開催のため今更にはなりますが、前々から興味あったフィンテックエンジニア養成勉強会に参加したのでメモ書きします。

きっかけ

フィンテックエンジニア養成読本が出版された際に、著者の1人である阿部さんと編集の高屋さんのツイートから出版記念のイベントを知ったことがきっかけでした。
以降、参加したいとは思いつつも仕事やプライベートのイベントとバッティングしたりして中々難しく、ずっと様子見という具合でした。

gihyo.jp

ちなみに、高屋さんは私が大学院生の頃に携わった、データサイエンティスト養成読本 登竜門編の編集もご担当されていました。これも縁ですね。

gihyo.jp

勉強会の内容

令和2年割賦販売法改正についてです。割賦販売法とは、いわゆるローン払いやクレジット払いに対する法律で、購入者がうける損害や管理の適正化等を目的としたものです。
改正の背景はFInTech企業が増加・拡大し、異業種からの決済分野への参入による市場変化に伴い、利用者が安全かつ安心に決済手段を利用できるよう環境を整備することが要請されているためです。
主な内容としては、少額の分割後払い規制の導入、審査手法の高度化への対応、QRコード決済事業者等へのセキュリティ対策強化の3点になります。

少額の分割後払い規制の導入

極度(包括枠)10万円以下の少額における分割後払いサービスの事業者に対して登録制度を設けるものです。
クレジットカードでは、これまでは高額商品の購入が前提でしたが、近年は日用品や趣味といった上記金額の範囲内での利用が増加しつつあります。
現行の包括信用購入あっせんに係る規制(クレジットカード会社等にかかっている規制)も同様に従来型の利用を想定した重い規制が課されています。
そこで少額包括信用購入あっせん業者を創設し、要件を満たした場合は極度額が少額以下ならば従来の登録包括信用購入あっせん業としての規定に拘らず包括信用購入あっせん業を行うことが出来る、というものです。
おそらくですが、純資産に対する要件や資本金に対する要件が従来比で緩和されることから、某ファッションECサイトのようなツケ払いみたいな事業展開がより拡大するのではないかと考えられます。

審査手法の高度化への対応

従来の与信判断材料としては静的情報(年収や預貯金、債務等)による包括支払可能見込額を算出し、その90%以下が利用限度額となっておりました。
近年では、支払・購入といった動的なトランザクションを取得し解析できるようになったことから統計学機械学習等を用いた与信審査の高度化が実現可能となっています。
そのため、高度化された与信審査によって従来とは異なる包括支払可能見込額の算出(=与信判断)を可能とし、その際のチェックを要するというものです。
情報技術やデータを用いた与信審査手法に対する認定制度を設ける事前チェックと、定期報告義務による事後チェックの二段構成です。
おそらくですが、従来からあるクレジットカード業者においても包括支払可能見込額の算出ロジックが変更される可能性があり、より精緻なものにつながると考えられます。

QRコード決済事業者等へのセキュリティ対策強化

ご存知、〜Pay系のQRコード決済に対して、クレジットカード番号等の適切な管理を義務化するものです。
平成20年、平成28年の改正時には、イシュア(利用者が使うクレジットカードの発行業者)と立替払取次業者(アクワイアラー、利用者からの提示によって加盟店が決済する際の窓口である業者)、そして加盟店に対しては、情報の適切管理義務を導入していましたが、今回は更に中間にいるコード決済の事業者やECモールの事業者にも適切管理義務を課すものとなります。

他にもその他、書面交付の電子化等の改定事項がございますが、割愛します。

感想

工学系と理学系であり、ビジネスサイドとして就職したが故に少しかじった程度なので法律について全然明るくはない人間でありますが、〜な背景・要請・変化があるから、・・・な改定・規定を創設、それに伴って〜なリスクがあるから・・・な制限を設ける、といった原理原則な部分が、すごく明快でより興味を持てました。先日も民法改正等ありましたが、それらについてもきちんと関係省庁から出されている資料を読み込んだらより理解できるはずなので調べることを継続していきたいと思いました。
またエンジニアという観点では、審査手法の高度化や少額分割後払い規制といった新形態なサービス開発の可能性があると考えております。

駄文になりましたが・・・また次回分も参加したいと思いました。

っていってたら申込済みでした。

fintech-engineer.connpass.com

Zoom APIを触ってみる

先日、とあるオンライン勉強会にて、運営の方がZoom APIを使って来場者とConnpassページとの突合を楽にしたいと仰っていました。
たぶんAPI提供されてるんじゃないかなと踏んでいたら予想通りあり、簡単に取得できたのでメモ書きします。

今回の場合、勉強会の運営側でレジストレーション機能を使って氏名やConnpass IDの入力後に入室できるようにしているみたいなので、少なくともその登録内容からconnpass IDとZoom上での名前を全員分取得できたらゴールとします。
なお、レジストレーション機能の詳細はこちら

ZoomのAPIリファレンスはこちらにありました。APIとかDeveloper向けサイトでは定番の英語で書かれているやつです。 探してみるとレジストレーションに関するAPIを発見しましたのでリファレンスに書かれている内容に従って叩いていきましょう。
なお、本稿の内容は2020年5月24日現在のものであるため今後変更が発生するかもしれません。

どうやらGETでパラメータを添えてリクエスト投げると帰ってくるみたいなので、とりあえずcurlでやってみましょう。
ZoomのApp Marletplaceにアクセスし、ログインした上で右の "Develop" からBuild Appを押して、作成画面へいきます。

f:id:forestython:20200524213109p:plain

JWTのCreateボタンを押します(下記画面は作成後のため一部異なります)。
※ JWTはJson Web Token の略です。

f:id:forestython:20200524212612p:plain

App Credentials の下部にある、View JWT Tokenで JWT Tokenをコピーします。

f:id:forestython:20200524212910p:plain

コピーしたJWT Token(<JWT Token>)と、取得したいミーティングのID(<Meeting ID>)を下記のコマンドにいれて実行。

kmori@kmoriMacbookPro $ curl -H 'Authorization: Bearer <JWT Token>' https://api.zoom.us/v2/meetings/<Meeting ID>/registrants

そうするとあれこれ情報が返ってくるはずです。
下記はサンプルですが、実際の戻り値を改行やインテンドによる整形をしたものです。

{
    "page_count":1,
    "page_number":1,
    "page_size":30,
    "total_records":90,
    "registrants":[
        {
            "id":"idABCD",
            "first_name":"AB",
            "last_name":"CD",
            "email":"abcd@hogehoge.com",
            "address":"",
            "city":"",
            "country":"",
            "state":"",
            "phone":"",
            "industry":"",
            "org":"",
            "job_title":"",
            "purchasing_time_frame":"",
            "role_in_purchase_process":"",
            "no_of_employees":"",
            "comments":"",
            "custom_questions":[
                {
                    "title":"connpass IDを記入してください。",
                    "value":"id_ABCD"
                }
            ],
            "status":"approved",
            "create_time":"2020-05-23T18:36:52Z",
            "join_url":"https://us02web.zoom.us/w/honyarara"
        },
        {
            "id":"idXYZ",
            "first_name":"XYZ",
            "email":"xyz@hogehoge.com",
            "address":"",
            "city":"",
            "country":"",
            "zip":"",
            "state":"",
            "phone":"",
            "industry":"",
            "org":"",
            "job_title":"",
            "purchasing_time_frame":"",
            "role_in_purchase_process":"",
            "no_of_employees":"",
            "comments":"",
            "custom_questions":[
                {
                    "title":"connpass IDを記入してください。",
                    "value":"id_XYZ"
                }
            ],
            "status":"approved",
            "create_time":"2020-05-23T18:36:52Z",
            "join_url":"https://us02web.zoom.us/w/honyarara"
        }
    ]
}

どうやらレスポンス自体は、

Responses
page_count
page_number
page_size
total_records
registrants
status
create_time
join_url

の8項目で構成されており更に、registrantsの中に

registrants
id
first_name
last_name
email
address
city
country
state
phone
industry
org
job_title
purchasing_time_frame
role_in_purchase_process
no_of_employees
comments
custom_questions

が内包されていて、更にcustom_questionsの中に

custom_questions
title
value

が内包されている模様です。
今回の場合、カスタムの質問でconnpassのIDを聞いているため、custom_questionsの中身まで必要となりますが、ここはパース芸でなんとかなる話なので、ゴールとしているconnpass IDと名前は取得できるので無事におしまい。

さてこのAPIですが、1回で返せる値の個数を示すpage_sizeがデフォルトだと30となっています。 そのため、デフォルトのままだと30人より多い会議では全員分取得できません。なので参加者数に応じてここは調整しましょう(なお最大値は300となっています)。
今回の勉強会では定員が100人であったためpage_sizeを100としてGETしてみましょう。

kmori@kmoriMacbookPro $ curl -H 'Authorization: Bearer <JWT Token>' https://api.zoom.us/v2/meetings/<Meeting ID>/registrants\?page_size=100

これで無事に全員分取得できましたとさ。めでたしめでたし。

他にもZoomのAPIでは会議を開催したり、IMのメッセージを取得・送信したりできるそうです。また、MarketplaceではSlack連携やWebhook等もできるみたいなので、可能性が拡がっていますね。

Enjoy!

【備忘録】 Raspberry Piでの設定あれこれ

はじめに

大学院修了時に友人から初代Raspberry Piを戴いたので色々遊んでいました。
初代のRaspberry Piはスペックが大したものではないですが、bot作ったりして遊ぶ分には便利です。
大したことはしてないのですが、備忘代わりのTips集を書きます。
Raspberry Piをいじっていると、Linux環境での各種セットアップ方法とかサーバ構築技術が身についたりするので個人的にはオススメです。

Tips集

見当たらないとき向けにarpで探す

Raspberry Piを探すのはめんどくさいので、ethernetでつながった状態でIPアドレスを探します。

kmori@edelweiss ~/Desktop $ arp -a

これをラズパイを繋ぐ前と後で差分を取ればIPアドレスがわかります。

IPアドレスを固定化

pi@raspberrypi:~ $ sudo vim /etc/dhcpcd.conf

一番下に挿入(下記は無線LANを使う前提)

interface wlan0
static ip_address=192.168.0.xxx/24
static routers=192.168.0.xxx
static domain_name_servers=192.168.0.xxx

static ip_addressに固定化したIPアドレスを設定します。
static routersとstatic domain_name_serversはルータのIPアドレスを設定(ブリッジしてる場合はそっちのIPアドレスを設定)します。

Watchdogの導入

エラー時にリブートしてくれる番犬こと、Watchdogを設定します。

 私の環境だとうまくいかなったパターン

pi@raspberrypi:~ $ sudo apt install watchdog
pi@raspberrypi:~ $ sudo vim /lib/systemd/system/watchdog.service

最下部のinstallの設定を変えます。
この変更では、複数ユーザ環境がベースとなるターゲットへ指定しており、多くのサービスのターゲットがこのターゲットに紐付いています。

[Install]
#WantedBy=default.target
WantedBy=multi-user.target

Watchdogの自動起動を設定します。

pi@raspberrypi:~ $ sudo update-rc.d watchdog enable

モジュールをロードの上、設定変更を実施します。

pi@raspberrypi:~ $ sudo modprobe bcm2835_wdt
pi@raspberrypi:~ $ sudo vim /etc/watchdog.conf

下記のように一部コメントアウトを外した上、タイムアウトを設定して有効化します。

#max-load-1 = 24
max-load-1 = 24
(中略)
#watchdog-device = /dev/watchdog
watchdog-device = /dev/watchdog

リブートします。

pi@raspberrypi:~ $ sudo reboot

最後にfork bombで動作確認をします。

pi@raspberrypi:~ $ :(){ :|:& };:

ってやってもうまくいかなかった・・・。

リトライ

調べたところ、標準の機能でいけるみたいなので、更にいじります。

pi@raspberrypi:~ $ sudo vim /boot/config.txt

で一番下に

dtparam=watchdog=on

を追記します。更に、

pi@raspberrypi:~ $ sudo vim /etc/modprobe.d/bcm2835-wdt.conf

で作成を行い、この1行を挿入します。

options bcm2835_wdt heartbeat=15 nowayout=0

これで15秒間隔でwatchdogがheartbeatを期待する設定になりました。

さらにシステム側で heartbeat信号を出力する設定をします。

pi@raspberrypi:~ $ sudo vim /etc/systemd/system.conf

で開いて、

#RuntimeWatchdogSec=0
RuntimeWatchdogSec=10

とします。これで10秒以内の間隔でheartbeatが行われます。この状態で再起動。

pi@raspberrypi:~ $ dmesg | grep bcm2835-wdt
[    0.764255] bcm2835-wdt 20100000.watchdog: Broadcom BCM2835 watchdog timer

無事に起動を確認。 最後のfork bombを打ち込んで無事に動くことを確認します。なんだかんだで長い道のりです。

自動再起動

当然ながらRaspberry PiもPCなので、稼働し続けていると調子が悪くなります。
例えば、走らせているスクリプトが落ちる上、SSHしても繋がらなかったり・・・。
ということで、再起動する設定をします。おなじみにcronでやります。

pi@raspberrypi:~ $ sudo crontab -e

で設定可能ですが、何も設定していない状態だと下記のようにでます。

pi@raspberrypi:~ $ sudo crontab -e
no crontab for root - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/ed
  2. /bin/nano        <---- easiest
  3. /usr/bin/vim.basic
  4. /usr/bin/vim.tiny

なので、下記の指定をしてcrontabをインストールしてあげましょう。

Choose 1-4 [2]: 3
crontab: installing new crontab

Vimが出てきたら、一番下にこれを挿入してあげましょう。

00 3 * * * /sbin/reboot

cronの設定方法は説明書きに以下のように書いています。

# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command

つまり、 "分 時 日 月 曜日 コマンド" で可能です。
設定が完了したら、crontabを確認しましょう。

pi@raspberrypi:~ $ sudo crontab -l

作成したscriptをservice化

service化して起動時から動かします。

$ sudo vim /etc/systemd/system/runMybots.service
[Unit]
#Description
Description = Running My bots

[Service]
# Path
ExecStart =  /home/pi/program/start.sh
#Restart setting
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target

start.shの中身は以下に設定。

#!/bin/bash
python3 /home/pi/program/reboot.py
python3 /home/pi/program/runbot01.py

reboot.pyでリブートした旨をSlackに通知しています。

先程追加したrumMybotsがリストにいるか確認。

$ sudo systemctl list-unit-files --type=service | grep runMybots

いることをみつけたら、自動起動にします。

$ sudo systemctl enable runMybots.service

最後に起動します。

$ sudo systemctl start runMybots.service

これで途中で落ちても大丈夫です。

おまけRaspberry Piで走らせている/いたScript

温度&湿度自動投稿

学生時代に住んでいた部屋はユニットバスだったためか、湿度が異様に高く、窓にカビがすぐはえてしまっていたので、湿度への警戒心が高くなりました。
そのため、部屋の温度と湿度のセンサをSlackへ投稿するbotを作っていました。
なお、このbotはセンサが壊れたため現在は動いていません。

人感センサ

赤外線センサによる、人感センサのbotを動かしていました。
大した金品はないのですが、警備会社の装置みたいなものを作られるのでは・・・?と考えたのがきっかけです。

電車運行情報

Webページを定期的にクローリングして、遅れていたらSlackに投稿するbotです。
私が普段利用している、東京メトロ東西線は混雑で遅れることが多いため便利でした。
現在は、Alexaのレシピが優秀で勝手に教えてくれるのでやめました。

各Scriptについては需要があれば書きます。

参照したサイト

Raspberry Pi 3B+を定期的に再起動する方法(cron) | あとをしNOTE Raspberry PiにハードウェアWatchdogを設定してみましょう | CANDY LINE Blog Raspberry piでslackbotを動かす - Qiita

Javaのクラスファイルをバイナリで読んでみたい

やりたいこと

Javaのクラスファイル読解入門として、バイナリから出力内容の場所を特定する。

環境

端末: MacBook Pro (Retina, 13-inch, Mid 2014) OS: macOS 10.14 Mojave

本題

基本的なHello Worldのコードは次の通りです。

public class HelloWorld {
  public static void main(String[] args){
    System.out.println("Hello World!");
  }
}

実際にコンパイルして実行すると、言うまでもないですが次のとおりになります。

kmori@edelweiss ~/Desktop/javahex $ javac HelloWorld.java
kmori@edelweiss ~/Desktop/javahex $ java HelloWorld
Hello World!

さてここからが本題。Macでバイナリを読むには「hexdump」が便利です。
早速clasファイルを読んでみましょう。

kmori@edelweiss ~/Desktop/javahex $ hexdump -C HelloWorld.class

出力結果(見やすくするためにわざとスペースを追加しております)

00000000    ca fe ba be 00 00 00 34 00 1d 0a 00 06 00 0f 09    |…….4……..|
00000010    00 10 00 11 08 00 12 0a 00 13 00 14 07 00 15 07    |…………….|
00000020    00 16 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29    |…..<init>…()|
00000030    56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e    |V…Code…LineN|
00000040    75 6d 62 65 72 54 61 62 6c 65 01 00 04 6d 61 69    |umberTable…mai|
00000050    6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67    |n…([Ljava/lang|
00000060    2f 53 74 72 69 6e 67 3b 29 56 01 00 0a 53 6f 75    |/String;)V…Sou|
00000070    72 63 65 46 69 6c 65 01 00 0f 48 65 6c 6c 6f 57    |rceFile…HelloW|
00000080    6f 72 6c 64 2e 6a 61 76 61 0c 00 07 00 08 07 00    |orld.java…….|
00000090    17 0c 00 18 00 19 01 00 0c 48 65 6c 6c 6f 20 57    |………Hello W|
000000a0    6f 72 6c 64 21 07 00 1a 0c 00 1b 00 1c 01 00 0a    |orld!………..|
000000b0    48 65 6c 6c 6f 57 6f 72 6c 64 01 00 10 6a 61 76    |HelloWorld…jav|
000000c0    61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 01 00 10    |a/lang/Object…|
000000d0    6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d    |java/lang/System|
000000e0    01 00 03 6f 75 74 01 00 15 4c 6a 61 76 61 2f 69    |…out…Ljava/i|
000000f0    6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b 01 00    |o/PrintStream;..|
00000100    13 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74    |.java/io/PrintSt|
00000110    72 65 61 6d 01 00 07 70 72 69 6e 74 6c 6e 01 00    |ream…println..|
00000120    15 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72    |.(Ljava/lang/Str|
00000130    69 6e 67 3b 29 56 00 21 00 05 00 06 00 00 00 00    |ing;)V.!……..|
00000140    00 02 00 01 00 07 00 08 00 01 00 09 00 00 00 1d    |…………….|
00000150    00 01 00 01 00 00 00 05 2a b7 00 01 b1 00 00 00    |……..*…….|
00000160    01 00 0a 00 00 00 06 00 01 00 00 00 01 00 09 00    |…………….|
00000170    0b 00 0c 00 01 00 09 00 00 00 25 00 02 00 01 00    |……….%…..|
00000180    00 00 09 b2 00 02 12 03 b6 00 04 b1 00 00 00 01    |…………….|
00000190    00 0a 00 00 00 0a 00 02 00 00 00 03 00 08 00 04    |…………….|
000001a0    00 01 00 0d 00 00 00 02 00 0e                      |……….|
000001aa

目的の文字列は00000090のところにありそうですね。
JavaのClassFileは以下の構造になっており、上記のバイナリに1対1で対応する模様です。

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

uxのxはバイト数。つまり、 u4 magic;は4バイトのマジックナンバーであり、上記のバイナリならばca fe ba beがそれを意味します。
出力内容だと、可変長であるためそこからあたりをつけます。 今回出力されている文字列はcp_info付近にありそう。ってことで更に見てみます。

cp_info {
    u1 tag;
    u1 info[];
}

tagがなんの変数なのかを示すものであり、その後のinfoにて情報が記述されています。cp_infoには00 1dより29-1=28個ある模様。

Constant Type Value
CONSTANT_Class 7
CONSTANT_Fieldref 9
CONSTANT_Methodref 10
CONSTANT_InterfaceMethodref 11
CONSTANT_String 8
CONSTANT_Integer 3
CONSTANT_Float 4
CONSTANT_Long 5
CONSTANT_Double 6
CONSTANT_NameAndType 12
CONSTANT_Utf8 1
CONSTANT_MethodHandle 15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic 18

つまり、Stringの部分は、valueが8のところを探して、下記に照らし合わせたindexを探せばよさそう。

CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}

そんな具合でたどっていけば見つかる・・・はずだと思います。

次回はもっと細かく見ていきたいと思います。