2020/05,江端さんの忘備録

新型コロナウイルス対応で、社会的な ―― マクロで、そして、ミクロな ―― 対応が求められています。

Corresponding to the new coronavirus, social "macro and micro" response is required.

それは、個人の行動変容と統計確率だけで、海外の都市封鎖に匹敵する成果を得る、という壮大な社会実験です。

That is a grand social experiment of "using only individual behavioral changes and statistical probabilities to achieve results comparable to overseas blockades"

-----

我が国は、1560年の、織田なんとかという地方の大名が、桶狭間というところで、奇襲に成功させことに端を発して以来、

Since 1560, when the feudal lord of a region called Oda Somehow started a successful surprise attack in Okehazama, Japan has been, we Japanese people have loved

「奇を衒(てら)った作戦」

"a strange strategy"

が大好きな国です。

歴史を遡ってみれば、真珠湾攻撃、バルチック艦隊撃破、二百三高知占領、江戸城無血開城、などの成功例もあります。

Looking back in history, there are some success stories such as the attack on Pearl Harbor, the defeat of the Baltic fleet, the occupation of 223 Kochi, and the bloodless opening of Edo Castle.

しかし、その裏には、膨大な数の失敗した作戦もあります。

However, there is also a huge number of failed operations behind it.

ともあれ、すでに作戦は発動しています。

Anyway, the operation has already been activated.

発動したからには、この闘い、「勝利」しなければ、意味がありません。

Since it was activated, this fight is meaningless unless we get "victory".

もっとも、この「勝利」とは、私の中では「医療崩壊させないまま、2年間持ち堪える」という意味であり、「COVID-19の根絶」ではありません。

This "victory" means in my sense "to endure for two years without disintegrating medical care", not to "eradication of COVID-19".

-----

とは言え、この作戦に成功したら、少くとも、兵士200万人を動員した「史上最大の作戦」、かの「ノルマンディー上陸作戦」を、はるかに越える成果として、世界史に残るだろうと、私は確信しています。

However, if this operation succeeds, it will remain in world history as a result that far surpasses at least the "largest operation in history" that mobilized 2 million soldiers, "Normandy landing operation". I'm sure.

―― なにしろ、作戦動員数が、1億2650万人

"After all, the number of operational mobilization is 126.5 million people"

―― 戦術はソーシャルディスタンスとステイホーム

"Tactics are social distance and stay home"

―― 武装なし(特効薬もない)

"No armament (and no silver bullet)"

まあ、私、こういう国威高揚のような物言いは、好きではないのですが、

Well, I don't like sayings such as uplifting national power,

1億を越える人間による「統計確率」に基づく作戦行動 ―― これに、私が「萌えない」訳がない。

Operational actions based on "statistical probability" by more than 100 million people --- I can't help getting excited.

この作戦の成功の暁には、

After the success of this operation, I believe that

■数学(特に、確率と統計)の復権(?)と、

- Restoration (?) Of mathematics (in particular, probability and statistics),

■数学不要論を唱えてきた知識人と言われるやつらの一斉粛清

- Simultaneous purge of intellectuals who have advocated the theory of unnecessary mathematics

が行われることを、確信しています。

それはさておき。

By the way,

-----

江端家でも、新型コロナ対応として、色々と対応を迫られています。

Even the Ebata family is under pressure to make various changes to support the new corona.

例えば、一昨日、無線LANのアクセスポイントの増設を行いました。

For example, the day before yesterday, I added wireless LAN access points.

これまで、家の中、特に子ども部屋での無線LANの接続不良が発生していたのですが、あまり真剣には対応してきませんでした。

Until now, there was a poor wireless LAN connection in the house, especially in the children's room, but I was not serious.

ところが、長女の就職活動(リモート面接)、次女の就学(オンライン授業)に影響があると聞き、この対応が、最高のプラオリティに跳ね上がりました。

However, I heard that this had an impact on my job hunting activity (remote interview) for my eldest daughter and school attendance (online lesson) for my second daughter, and this response jumped to the highest priority.

15年前、家を建てた時に、ひそかに家の中に仕込んでおいたLANケーブル(100Mbps)が、この土壇場で発揮されることになりました。

15 years ago, when I built this house, the LAN cable (100 Mbps) that was secretly stored inside the house will be demonstrated at this last minute.

Amazonに注文したWiFiアクセスポイントが、自宅到着したその日に、子ども部屋のタンスの裏のLANのモジュラージャックから、LANケーブルを引き出して、長女の部屋の天井に設置しました。

At the day, a new WiFi access point I ordered to Amazon is coming, I pulled out the LAN cable from the LAN modular jack on the back of the closet in the children's room, and installed it on the ceiling of my eldest daughter's room.

15年前には、PCによる就職活動や、オンライン授業は ―― そういう「話」は山ほどありましたが ―― 実施例は、ほぼ絶無と言っていい状態でした。

Fifteen years ago, there was a lot of talk about job hunting and online classes using PC. But the practical examples were almost inexhaustible.

私たち、ITエンジニアが20年間以上唱え続けても、まったく変化のなかった社会が、ここ、たった1~2ヶ月の間で次々と現実に動き出しています。

Even if we, IT engineers, have been asking for more than 20 years, a society has not changed at all. However, society is going to change dynamically in the last 1-2 months.

結局ところ、「"命"を人質に取られている」ということは、こんなにも「強い」ということです。

After all, "surviving" means being "strong" like this.

2020/05,江端さんの忘備録

本日は、コラムがリリースされた日なので、日記はお休みです。

Today, new my column is released, so I take a day off.

世界を「数字」で回してみよう(63) 番外編:

1ミリでいいからコロナに反撃したいエンジニアのための“仮想特効薬”の作り方

Let's turn over the world by number (63). Extra:

How to make a "virtual silver bullet" for engineers who want to counterattack Corona, even kicking against the pricks

-----

前回のコラムのテーマは、「絶望」と「諦観」でした。

The theme of the previous column was "despair" and "resignation."

現在、各国政府が行っている「封じ込め戦略」は、なかなか出口の見えない闘いです。

Currently, the "containment strategy" that is being implemented by the governments of each country is a battle that we cannot see the exit easily.

見えない敵が暴れ回り、悔しくても、辛くても、できることは「雌伏」だけ。

Invisible enemies rampage, and even if we are frustrated or painful, the only thing we can do is "endure".

このような、専守防衛に徹するだけの闘いは、個人にとっても社会にとっても、大きな不安とストレスになります。

This kind of struggle to dedicate to exclusive defense causes great anxiety and stress for both individuals and society.

経済をたった1日止めるだけでも、簡単に金融不安になりかねない現代社会で、実質2ヶ月以上の停滞が続いています。

In today's society, even if the economy is stopped for only one day, financial instability can easily occur. However it has been stagnant for more than 2 months.

これで不安にならない人間がいるとすれば、それはもう「人間」ではない何かでしょう。

If there are people who aren't anxious about this, it's something that is no longer "human".

-----

比して、今回のコラムのテーマは、「反撃」と「希望」です。

In contrast, the theme of this column is "Counterattack" and "Hope".

専守防衛でなく、具体的な反攻の実施例を提示しました。

We presented specific examples of counterattacks, not dedicated defense.

―― 新型コロナウイスルを、ズタズタに切り裂く"siRNA"の塩基配列は、もう分かっている

"The base sequence of the "siRNA" that cuts the new coronavirus into pieces is already known"

―― やつら(COVID-19)を、殲滅する設計図は、私たちの手の中にある

"The blueprint for destroying them (COVID-19) is in our hands"

これは、私にとって大きな希望です。

This is a great hope for me.

-----

SF作家の故小松左京先生の最高傑作「さよならジュピター」は、

The masterpiece "Bye bye Jupiter" by the late science fiction writer Sakyo Komatsu-sensei, is that

太陽に突進してくるブラックホールに対して、人類の一部が ―― 正確に言うと、宇宙開発に携わる現場のエンジニアが ―― 驚くべきアイデアで対峙する

Part of the human race ――To be precise, the field engineers involved in space development ―― confronts the black hole rushing to the sun with an amazing idea

という、壮大なSF小説です。

a magnificent science fiction novel.

当初、200億人の人類に対して、最大1億人の脱出計画(しかも「とりあえず脱出のみ」という計画)しかなかったところに、あるエンジニアから、「ブラックホールとの闘い方」が提示されます。

At first, there was a plan to escape up to 100 million people to 20 billion human beings (and a plan to "exit only for the time being"), but one engineer presents "how to fight against black holes" .

それによって、現場に活気が戻り、その「闘い」が、人類の最後の切り札となります。

As a result, vitality returns to the field, and the "fight" becomes the last trump card of humankind.

―― 今までは、何しろ、逃げる以外に手の打ちようがなくて、誰と誰がどれだけ逃げ出せるか、誰を逃がして誰が残るかという問題だけだったが、

Until now, I had no choice but to escape, and t was just a problem that "Who and who can escape" and "Who will escape and who will remain"

―― ただ、受け身の立場としで右往左往するだけではなく、あの"黒い怪物"にたとえ一太刀でもきりつける事ができそうだとなったら、またはりきる奴も出てくるだろう

However, as a passive person, going right and left, If we can use a single sword that cut against that "black monster", someone will stand-up again.

―― たとえ無駄でも、"X"野郎に一矢むくい、横っつらの一つでもはりとばしてやれるとなるとな・・・

Even if it's in vain, if we can get a blow in the "X" bastard, and knock him.

-----

まだ、我々は負けていません。

We haven't lost yet.

専守防衛だけではない、積極的攻撃の闘いは、これからです。

The battle for aggressive attacks, not just defense, will start now.

「たとえ無駄でも」 ―― ではありません。

"Even if it's in vain" is NOT

私たちは、"COVID-19"を、一つ残らず、この地球上から抹殺するのです。

We will wipe out all "COVID-19" from this planet.

2020/05,江端さんの忘備録

(昨日の続きです)

(Continuation from yesterday)

まあ、いずれにしろ、この状態が続けば(続かなくても)、特措法の改正が行われることは確実で、さらに、国民感情も追い風になって、相当に強い罰則も入ってくるでしょう。

Well, in any case, if this situation continues (even if it doesn't continue), this special law will surely be revised, and the public sentiment will be a tailwind, and considerably strong penalties will be installed.

野党も、形式的には「遺憾の意」を表明しながら、与党と共同で法改正に傾くでしょう。

The opposition parties will also formally express their "regrets" and support the revision of the law jointly with the ruling party.

戦前の「国家総動員法」のようなプロセスが、着々と進行して行く ――

A process like the “National Total Mobilization Law” before the war is steadily progressing.

図らずも、私たちは、戦前の戦時体制を、リアルタイムで体験しているのです。

Unexpectedly, we are experiencing the prewar war regime in real time.

-----

今回の新型コロナ対策についての政府方針や立法手続や内容については「理」がある、と私は思っています。

I think that there is "reason" about the government policy, legislative procedures, and the content of this new corona countermeasure.

しかし、戦前の「対米英戦」だって、十分な「理」はあったのです。

However, even before the war, there was sufficient "reason" in the "Pacific War" against U.S and U.K.

私は、この新型コロナウイルスとの闘いを、

I think that the fight against new coronavirus is

「『国民を戦場に送り込まず』『頭上から爆弾を投下されない』という部分が違うだけの戦争」

"the war that differs from "people are not sent to the battlefield "and "no bombs dropped from overhead""

と考えています。

私個人としては、戦争なんぞ体験することなく、一生を終えたかったです。

Personally, I want to end my life without experiencing this war.

-----

てなことを書きながら、ふと思ったのですが、

When I was writing, I suddenly thought,

―― なんで、パチンコ店の前に、右翼カーが出張ってこないのかな?

"Why isn't the right wing car come in front of the pachinko parlor?"

私は、彼らが『集会妨害のエキスパート』であり、『パチンコ屋にやってくる客は非国民と認定するだろう』と思っているのですが、もう、そういうコンセプトは、古いのでしょうか(裏のコネで動けない可能性を疑っています)

I think they are "experts that destroy rallies," and will "certify the guests coming to a pachinko parlor as" non-national". I wonder if such a concept has alreay been old (I suspect that there are connections in the back)

―― パチンコ屋に、爆弾テロ予告をする左翼組織は出てこないのかな?

"Isn't a left-wing organization coming out to the pachinko parlor to announce the bomb terrorism?"

『資本家打倒』という旗印では、これほどヒットする対象はなく、しかも、今なら好感度アップの可能性すらあります。

With the flag of "overthrowing the capitalist," this might be a big hit, and even now, there is a possibility of increasing the favorable impression.

ちなみに、

By the way,

―― 新型コロナウイルス禍を利用した布教活動

"Propagation activity using the new coronavirus"

は当然のように、うっとうしいほど登場してきています。

Naturally, those religious groups are appearing annoyingly.

例えば、『コロナウィルス感染撃退祈願』とか検索エンジンで探してみてください。

For example, try searching with a search engine, by the words "prayer for repelling coronavirus infection"

いや、探さない方がいいかもしれません ―― 私は、最も醜悪な腐敗物を見せつけられた気分になりましたから。

No, you should not. Because I felt as if I had been shown the most ugly decay.

私は、汚いものから目を背けるように、発見後3秒後に、そのページを閉じました。

I closed the page in 3 seconds, like running away from the ugly decay.

-----

そもそも、科学とは、「観察→仮説→実験→検証」をループとする、累積的進歩のプロセスのことです。

In the first place, science is a process of cumulative progress, which is a loop of "observation → hypothesis → experiment → verification".

こういう低能宗教団体が「科学」を称呼することは、不正競争防止法の「品質誤認」の違法行為に該当すると思いますが ―― それ以上に、

I think that using the word "science" by such incompetent religious groups, is to correspond to the illegal act of "misidentification of quality" under the Unfair Competition Prevention Act. Beyond that,

「科学」という言葉の「誤用」「濫用」であり、

This is the "misuse" and "abuse" for the word "science",

科学に対する「不遜」であり、酷い「侮辱」「汚辱」だと思います。

I think that it is "irreparable" to science, and it is a severe "insult" to science.

江端さんの忘備録

以前、

Before, I wrote a diary whose title was

「ただ、条文上、メイド喫茶や深夜のバーの営業自粛は「要請」することはできても、「禁止」することはできないようです。」

However, according to the provisions, it seems that maid cafes and bar bars at midnight can be "requested" but not "prohibited"

という日記を書きました。

残念ながら、現在、「パチンコ店」で、この問題が顕在化しています。

Unfortunately, this problem is now becoming apparent at “pachinko parlors”.

具体的には、

In particular,

(1)新型コロナ感染症の感染リスクの高いパチンコ店が、営業を続けていることに対して、

(1) For pachinko stores that are at high risk of infection with new corona infections,

(2)その制裁として、自治体が「店名公表」を行い、

(2) As a sanction, the local government will "publish the store name",

(3)その結果、それらのパチンコ店に、遠方から客が殺到する

(3) As a result, those pachinko parlors are flooded with customers from afar.

という、負の連鎖が発生しています。

The negative chain happens.

-----

以前の日記にも記載したように、「自粛"要請"」から「自粛"指示"」になったところで、新型インフルエンザ等対策特別措置法には、罰則規定がありません。

As mentioned in the previous diary, there is no penal provision in the Enforcement Ordinance for the Special Measures Law for Countermeasures against New Influenza, etc., even when "self-restraint "request" changed to self-restraint "instruction".

少なくとも、この法律では、パチンコ店の営業は止められません。

At least this law doesn't stop the operation of pachinko parlors.

とすると、別の法律との「合わせ技」で対応することになりそうです。

If so, it seems that it will be handled by a "matching technique" with another law.

ざっと調べてみたところ、「行政手続法」の、第2条4項の「不利益処分」が、地方自治体の対抗手段になるのかなーと思っています ――

After a quick survey, I think it will be a countermeasure of local governments, "Disadvantageous Disposition" in Article 2.4 in the “Administrative Procedures Act”,

いわゆる「営業停止処分」です。

This is so-called “business suspension”.

しかし、パチンコ店の営業許認可は、店舗所在地の管轄の警察署にあるようです。

However, the pachinko parlor's business license seems to be at the police station that has jurisdiction over the store location.

また、今回のケースでの、営業停止処分は前例がないこともあり、そもそも法律上の解釈に関する検討は絶無です(当然ですが)。

In addition, there is no precedent for the suspension of operations in this case, so there is absolutely no need to consider legal interpretations (of course,not).

そう考えていくと、 ―― 民意はさておき ―― 行政権の濫用、という考え方もできます。

If you think so, you can think of abuse of administrative power ---- People's will aside.

-----

地方自治体:「営業停止処分」

Local government: “Business suspension”

パチンコ店:「行政不服審査法に基づく意義申立ての請求」(行服法に、即時抗告みたいなものがあるのかは、私は知りません)

Pachinko parlors: "Request for significance based on the Administrative Appeal Law" (I don't know if there is an immediate appeal in the Administrative Law)

地方自治体:「意義申立の棄却」

Local government: "Rejection of significance claim"

パチンコ店:「裁判所に対する、行政命令無効の仮処分の申立」

Pachinko parlors: "Petition for provisional disposition of administrative order against the court"

という流れになって、

Then,

「泥沼」確実。

"Mudification" will be confirmed.

(続く)

(To be continued)

2020/05,江端さんの忘備録

昨日の日記を読んで頂いた、私のプログラミングの師匠であられるところのSさんからメールを頂きました。

I received an e-mail from Mr. S, who is my programming mentor and has read the diary yesterday.

メールの要旨を抜粋させて頂きます(本人無許諾だけど、多分許して頂けると思っています)

The following is a summary of the email ((I have not given permission Mr.S , but I think that he will give it to me)

■『"Excel "で十分じゃん?(by 江端)』 まさに「これ」、かと思いました。

- "Isn't" Excel "enough? (by Ebata) ”I thought it was exactly this.

■私も、ティーンエージャー(大学生未満)はプログラムを書かないほうがよい、と考えています。

- I also think teenagers (under college) should not write programs.

-----

加えて、

In addition,

■うちの子供達には、行列と微分積分だけはどの世界に行っても必要なので手を抜かせないようにしています。

- I try to make my children study only matrices and calculus because they need to be in any world.

この最後の一文が、私の胸を貫きました。

I was impressed with this last sentence.

―― 行列と微分積分なくして、世界を理解する手段なし

"Without matrix and calculus, there is no way to understand the world"

これは、世界中のどんな優れた哲学や宗教のドグマも敵わない、絶対的真理です。

This is an absolute truth that overwhelms any great philosophical or religious dogma in the world.

-----

話を戻しますが、本当にエクセルだけで、コンピュータで行う計算のかなりの部分を網羅できるんです。

Let's get back to it. only Excel can cover a great deal of computer calculations.

「Excelで操る! ここまでできる科学技術計算」

"Operate with Excel! Scientific calculation that can be done up to here"

「はじめての人工知能 Excelで体験しながら学ぶAI」

"First artificial intelligence, AI to learn while experiencing with Excel"

には、コラム執筆の際には、大変お世話になりましたし、

These two books are very helpful to me when I wrote the column,

Sさんから紹介頂いた、

Mr. S introduceded me, the following two book.

「Excelでわかる機械学習 超入門 AIのモデルとアルゴリズムがわかる」

"Understanding machine learning, super introduction, AI models and algorithms understood in Excel"

「Excelでわかるディープラーニング超入門 【RNN・DQN編】」

"Deep Learning Super Intro to Excel [RNN / DQN Edition]"

などもあります。

これらの本の凄いところは、数式なんぞ理解できなくても、エクセルの動きを見ているだけで、計算の動きが「目で見える」ということなのです。

The great thing about these books is that even if I can't understand mathematical formulas, I can "see" the motion of calculation just by looking at the motion of Excel.

その後に数式を読むと『あ! そういうことか!!』と理解できるようになる、という点が良いのです。

After that, when I read the formula, "Ah! That kind of thing! !! It is good that I can understand.

つまり、子どもであれ、大人であれ、数式の理解の心理的な壁の高さを、限りなくゼロまで下げるのです。

In other words, whether you are a child or an adult, the height of psychological barriers to understanding mathematical formulas is lowered to zero.

-----

うん、なんか使命感に燃えてきました。

Yeah, I was burning with a sense of mission.

我が国を、世界に冠たる

In order to make Japan a world-class

「スプレッドシート(Excel)リテラシー国(×プログラミングリテラシー国)」

"spreadsheet (Excel) literacy country (not programming literacy country),"

にする為にも、

I have to write a book whose title is

『夏休みの最後の3日間で、それらしい自由研究レポートを作成する為のEXCEL入門』

"Introduction to EXCEL to create such a free research report in the last three days of summer vacation"

の執筆が急がれると、思っています ―― 勝手に。

urgently and selfishly

ネタはあります。

I have several stories.

出版業界の皆様の、お声がけをお待ちしております。

I look forward to inviting me from everyone in the publishing industry.

2020,江端さんの忘備録

以前の仕事の見直しが必要となっていますが、その作業を開始すると、その時の恐怖が再現されてきて、心臓がバクバクして、呼吸が苦しくなります。

I need to review my previous work. When you start the work, the fear at that time is reproduced, my heart beats, and breathing becomes difficult.

こういう症状が出るような場合、「初動」が大切なことは、自分でこれまで色々調べて知っていました。

I know that "initial response" is important, whenever I have such symptoms. Because I wrote about it in my columns.

すぐに、心療内科への電話予約を試みましたが、全ての医院で『初診は6月以降』と言われてしまいました。

Immediately, I tried to make a telephone reservation to several clinics of Department of Psychiatry and Internal Medicine, however at all the clinics I was told that my first visit was after June.

これも、連鎖的な医療崩壊の一端かもしれません。

This may also be part of the chain of medical collapse.

というか、

Rather, I am thinking that,

―― 国家レベルの「心の崩壊」が始まっている

The national level "disintegration of mental health" has begun

のかもしれないなぁ、と考えています。

2020,江端さんの忘備録

(昨日の続きです)

(Continuation from yesterday)

つまるところ、「人が集まらなければ、クラスターは発生することができない」のです。

After all, "If you don't get together, you can't create a cluster."

そして、今のところ、我が国に関しては、この「クラスター潰し」が、ぎりぎり有効な範囲にあります。

And so far, in Japan, this "cluster crushing" is in a marginally effective range.

もっとも、我が国でも、この「クラスター潰し」も限界に近くなってきているようです。

However, in Japan, this "cluster crushing" seems to be reaching its limit.

これが「感染経路が分からない」というものです。

This is "we cannot know the route of infection."

ちなみに、欧米では、「クラスター潰し」のフェーズを離れ、手遅れになっているのはご存知の通りです。

By the way, as you know, in Europe and the United States, it is too late to leave the "cluster-crushing" phase.

今、これから発生する数万人規模の死体収容所を作っている ―― って、どこの世界線(ディストピア)の話? と聞きたくなります

Now, they are building tens of thousands of corpses in the future. I want to ask what the worldline (dystopia) is.

-----

「クラスター潰し」の観点から、学校、会社、集会、イベントを自粛するのは、(個人の経済的事情を無視するのであれば)最適戦略です。

From a "cluster crushing" perspective, refraining from schools, companies, gatherings, and events is an optimal strategy (if you ignore the economics of an individual).

しかし、学校、会社、集会、イベントを自粛しながら「夜の歓楽街に出掛ける」という人間は、はっきり言わせて貰えば、

However, persons who "are going to the red light district at night" while restraining schools, companies, gatherings, and events,

『あんたたち、脳があるのか?』

"Do you have a brain?"

と尋ねたくなるくらい、理解不能です。

I do not understand them at all.

-----

そこで、私、経済活動をそこそこのレベルで維持しつつ、クラスター発生を防止する一つの案を提案したいと思います。

Therefore, I would like to propose a proposal to prevent clusters while maintaining economic activity at a reasonable level.

「音声コミュニケーションの絶対的な禁止命令」

"Absolute ban on voice communication"

です。

■いかなる状況であっても、対人間で会話をしてはならない(例外なし)。

- You should not speak to anyone at any place.

■コミュニケーションは、一定の距離(数メートル)を維持しながら、全て、SNS等のリアルタイムのテキストメッセージのみでで実施する。

- All communication is carried out using only real-time text messages such as SNS while maintaining a certain distance (a few meters).

■スマホが使えない人は、メッセージボードで会話をする。

- Talk on the message board if you can't use a smartphone.

■どうしても音声による会話が必要であれば、全て電話で行う、または、電話会議を実施する。

- If a voice conversation is absolutely necessary, you can use a phone or telephone conference.

これは、現時点で多くの会社で実施されています。

Many companies are currently implementing this.

この条件が満たされるのであれば、私は、バー、キャバクラ、メイド喫茶は再開しても良いと考えています。

If this condition is fulfilled, I will resume bar, cabaret club, and maid cafe.

-----

ところで、

By the way,

江端家は、一度、家族4人で、秋葉原にあるメイド喫茶なるものに行ったことがあります。

The Ebata family once went to a maid cafe in Akihabara.

待ち列に並んでいる最中、家族全員が、不快と酸欠で顔色が悪くなっていました。

While in the queue, the whole family had turned pale due to discomfort and lack of oxygen.

私が「離脱」を提案したところ、1秒後に家族全員が同意して、5秒後に店の外に敗走しました。

When I proposed "withdrawal," the whole family agreed one second later and broke out of the store five seconds later.

その時、シュタインズゲートの中に登場していた「メイド喫茶」への幻想は、私の中で、完全に破壊されました。

At that time, the illusion of a "maid cafe" that appeared in Steins Gate was completely destroyed in me.

閑話休題

Restless talk

(続く)

(To be continued)

2020,江端さんの技術メモ

江端の環境でのローカルな場所 C:\Users\ebata\Desktop\bouncy2

C:\Users\ebata>cd C:\Users\ebata\Desktop\bouncy2
cd /c/Users/ebata/Desktop/bouncy2
python3 httpsrv.py
http://localhost:8080 で起動

 

  • 2020/03/14

     

  • 背景
    • ■これからは「C/C++ → Go」と「JavaScript → Webassembly」とシフトしながら色々やっていこうと思っている■正直、どっちも難しい。そういう時は、サンプルをパクって勉強するのが正解 ―― と思っている
  • 目的
  • 環境
    • ■Windows7 or 10
    • ■Go と python3 をインストールしてある
  • やってみたこと
    • ■サンプルプログラムをダウンロードして、コンパイルしてみた
      • ~ https://github.com/stdiopt/gowasm-experiments/tree/master/bouncy
      • コマンドプロンプトからこんな感じでコンパイルできた
        • $ set GOOS=js
        • $ set  GOARCH=wasm
        • $ go build -o main.wasm main.go
        • ちなみに、bashの環境があれば、build.shでコンパイルできる
      • 実際には、コンパイルしなくても、バイナリコード(main.wasm)も、ダウンロードの中に入っているので、コンパイルは不要だったが
    • ■ローカルサーバとしては、色々試してみた
      • python3 -m http.server 8080
      • Goでサーバも作ってみた
      • Perlでもやってみた
    • ■動画がどうしても出てこない
    • ■ここから丸2日間の格闘のスタート
      • もう、色々探しまくった
  • 確認していた問題点
    • ■Google Chromoから、→ 「その他のツール」 → 「ディベロッパーツール」 → 
      • Uncaught (in promise) TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'.この「Expected 'application/wasm'.」がどうにも、不味いらしい ―― が、解決方法が、どうにも見つからない
    • ■kobore.netのサーバに上げても、改善が見られず
  • ローカルサーバを作ってみた
    • ■httpsrv.pyを作った
      #!/usr/bin/env python3
      import http.server
        import socketserver
        
        PORT =
      8080
        
        Handler = http.server.SimpleHTTPRequestHandler
        Handler.extensions_map.update({
        '.wasm': 'application/wasm',
        })
        
        socketserver.TCPServer.allow_reuse_address =
      True
      with socketserver.TCPServer(("", PORT), Handler) as httpd:
        httpd.allow_reuse_address =
      True
      print("serving at port", PORT)
        httpd.serve_forever()
      
  • ■httpsrv.pyを起動した
    • $ python3 httpsrv.py
  • http://localhost:8080 で起動
    • ■動いた
      ■動かなかったら、chromo → 「設定」 → 「閲覧履歴データの削除」でキャッシュをクリアてみること

以上

httpsrv.py

#!/usr/bin/env python3

import http.server
import socketserver

PORT = 8080

Handler = http.server.SimpleHTTPRequestHandler
Handler.extensions_map.update({
    '.wasm': 'application/wasm',
})

socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    httpd.allow_reuse_address = True
    print("serving at port", PORT)
    httpd.serve_forever()

main.go

//Wasming
// compile: GOOS=js GOARCH=wasm go build -o main.wasm ./main.go
package main

import (
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"syscall/js"
)

var (
	width      float64
	height     float64
	mousePos   [2]float64
	ctx        js.Value    // "syscall/js"から引っ張られる
	lineDistSq float64 = 100 * 100
)

func main() {

	// Init Canvas stuff
	doc := js.Global().Get("document")
	canvasEl := doc.Call("getElementById", "mycanvas")
	width = doc.Get("body").Get("clientWidth").Float()
	height = doc.Get("body").Get("clientHeight").Float()
	canvasEl.Set("width", width)
	canvasEl.Set("height", height)
	ctx = canvasEl.Call("getContext", "2d")

	done := make(chan struct{}, 0)

	dt := DotThing{speed: 160, size: 6}

	mouseMoveEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		mousePos[0] = e.Get("clientX").Float()
		mousePos[1] = e.Get("clientY").Float()
		return nil
	})
	defer mouseMoveEvt.Release()

	// Event handler for count range
	countChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		intVal, err := strconv.Atoi(evt.Get("target").Get("value").String())
		if err != nil {
			println("Invalid value", err)
			return nil
		}
		dt.SetNDots(intVal)
		return nil
	})
	defer countChangeEvt.Release()

	// Event handler for speed range
	speedInputEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		fval, err := strconv.ParseFloat(evt.Get("target").Get("value").String(), 64)
		if err != nil {
			println("invalid value", err)
			return nil
		}
		dt.speed = fval
		return nil
	})
	defer speedInputEvt.Release()

	// Event handler for size
	sizeChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		intVal, err := strconv.Atoi(evt.Get("target").Get("value").String())
		if err != nil {
			println("invalid value", err)
			return nil
		}
		dt.size = intVal
		return nil
	})
	defer sizeChangeEvt.Release()

	// Event handler for lines toggle
	lineChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		dt.lines = evt.Get("target").Get("checked").Bool()
		return nil
	})
	defer lineChangeEvt.Release()

	// Event handler for dashed toggle
	dashedChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		dt.dashed = evt.Get("target").Get("checked").Bool()
		return nil
	})
	defer dashedChangeEvt.Release()

	doc.Call("addEventListener", "mousemove", mouseMoveEvt)
	doc.Call("getElementById", "count").Call("addEventListener", "change", countChangeEvt)
	doc.Call("getElementById", "speed").Call("addEventListener", "input", speedInputEvt)
	doc.Call("getElementById", "size").Call("addEventListener", "input", sizeChangeEvt)
	doc.Call("getElementById", "dashed").Call("addEventListener", "change", dashedChangeEvt)
	doc.Call("getElementById", "lines").Call("addEventListener", "change", lineChangeEvt)

	dt.SetNDots(100)
	dt.lines = false
	var renderFrame js.Func
	var tmark float64
	var markCount = 0
	var tdiffSum float64

	renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		now := args[0].Float()
		tdiff := now - tmark
		tdiffSum += now - tmark
		markCount++
		if markCount > 10 {
			doc.Call("getElementById", "fps").Set("innerHTML", fmt.Sprintf("FPS: %.01f", 1000/(tdiffSum/float64(markCount))))
			tdiffSum, markCount = 0, 0
		}
		tmark = now

		// Pull window size to handle resize
		curBodyW := doc.Get("body").Get("clientWidth").Float()
		curBodyH := doc.Get("body").Get("clientHeight").Float()
		if curBodyW != width || curBodyH != height {
			width, height = curBodyW, curBodyH
			canvasEl.Set("width", width)
			canvasEl.Set("height", height)
		}
		dt.Update(tdiff / 1000)

		js.Global().Call("requestAnimationFrame", renderFrame)
		return nil
	})
	defer renderFrame.Release()

	// Start running
	js.Global().Call("requestAnimationFrame", renderFrame)

	<-done

}

// DotThing manager
type DotThing struct {
	dots   []*Dot
	dashed bool
	lines  bool
	speed  float64
	size   int
}

// Update updates the dot positions and draws
func (dt *DotThing) Update(dtTime float64) {
	if dt.dots == nil {
		return
	}
	ctx.Call("clearRect", 0, 0, width, height)

	// Update
	for i, dot := range dt.dots {
		dir := [2]float64{}
		// Bounce
		if dot.pos[0] < 0 {
			dot.pos[0] = 0
			dot.dir[0] *= -1
		}
		if dot.pos[0] > width {
			dot.pos[0] = width
			dot.dir[0] *= -1
		}

		if dot.pos[1] < 0 {
			dot.pos[1] = 0
			dot.dir[1] *= -1
		}

		if dot.pos[1] > height {
			dot.pos[1] = height
			dot.dir[1] *= -1
		}
		dir = dot.dir

		ctx.Set("globalAlpha", 0.5)
		ctx.Call("beginPath")
		ctx.Set("fillStyle", fmt.Sprintf("#%06x", dot.color))
		ctx.Set("strokeStyle", fmt.Sprintf("#%06x", dot.color))
		// Dashed array ref: https://github.com/golang/go/blob/release-branch.go1.11/src/syscall/js/js.go#L98
		ctx.Call("setLineDash", []interface{}{})
		if dt.dashed {
			ctx.Call("setLineDash", []interface{}{5, 10})
		}
		ctx.Set("lineWidth", dt.size)
		ctx.Call("arc", dot.pos[0], dot.pos[1], dt.size, 0, 2*math.Pi)
		ctx.Call("fill")

		mdx := mousePos[0] - dot.pos[0]
		mdy := mousePos[1] - dot.pos[1]
		d := math.Sqrt(mdx*mdx + mdy*mdy)
		if d < 200 {
			ctx.Set("globalAlpha", 1-d/200)
			ctx.Call("beginPath")
			ctx.Call("moveTo", dot.pos[0], dot.pos[1])
			ctx.Call("lineTo", mousePos[0], mousePos[1])
			ctx.Call("stroke")
			if d > 100 { // move towards mouse
				dir[0] = (mdx / d) * 2
				dir[1] = (mdy / d) * 2
			} else { // do not move
				dir[0] = 0
				dir[1] = 0
			}
		}

		if dt.lines {
			for _, dot2 := range dt.dots[i+1:] {
				mx := dot2.pos[0] - dot.pos[0]
				my := dot2.pos[1] - dot.pos[1]
				d := mx*mx + my*my
				if d < lineDistSq {
					ctx.Set("globalAlpha", 1-d/lineDistSq)
					ctx.Call("beginPath")
					ctx.Call("moveTo", dot.pos[0], dot.pos[1])
					ctx.Call("lineTo", dot2.pos[0], dot2.pos[1])
					ctx.Call("stroke")
				}
			}
		}

		dot.pos[0] += dir[0] * dt.speed * dtTime
		dot.pos[1] += dir[1] * dt.speed * dtTime
	}
}

// SetNDots reinitializes dots with n size
func (dt *DotThing) SetNDots(n int) {
	dt.dots = make([]*Dot, n)
	for i := 0; i < n; i++ {
		dt.dots[i] = &Dot{
			pos: [2]float64{
				rand.Float64() * width,
				rand.Float64() * height,
			},
			dir: [2]float64{
				rand.NormFloat64(),
				rand.NormFloat64(),
			},
			color: uint32(rand.Intn(0xFFFFFF)),
			size:  10,
		}
	}
}

// Dot represents a dot ...
type Dot struct {
	pos   [2]float64
	dir   [2]float64
	color uint32
	size  float64
}

build.sh

#!/bin/sh

GOOS=js GOARCH=wasm go build -o main.wasm ./main.go

wasm_exec.js

// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

(() => {
	// Map multiple JavaScript environments to a single common API,
	// preferring web standards over Node.js API.
	//
	// Environments considered:
	// - Browsers
	// - Node.js
	// - Electron
	// - Parcel

	if (typeof global !== "undefined") {
		// global already exists
	} else if (typeof window !== "undefined") {
		window.global = window;
	} else if (typeof self !== "undefined") {
		self.global = self;
	} else {
		throw new Error("cannot export Go (neither global, window nor self is defined)");
	}

	if (!global.require && typeof require !== "undefined") {
		global.require = require;
	}

	if (!global.fs && global.require) {
		global.fs = require("fs");
	}

	const enosys = () => {
		const err = new Error("not implemented");
		err.code = "ENOSYS";
		return err;
	};

	if (!global.fs) {
		let outputBuf = "";
		global.fs = {
			constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
			writeSync(fd, buf) {
				outputBuf += decoder.decode(buf);
				const nl = outputBuf.lastIndexOf("\n");
				if (nl != -1) {
					console.log(outputBuf.substr(0, nl));
					outputBuf = outputBuf.substr(nl + 1);
				}
				return buf.length;
			},
			write(fd, buf, offset, length, position, callback) {
				if (offset !== 0 || length !== buf.length || position !== null) {
					callback(enosys());
					return;
				}
				const n = this.writeSync(fd, buf);
				callback(null, n);
			},
			chmod(path, mode, callback) { callback(enosys()); },
			chown(path, uid, gid, callback) { callback(enosys()); },
			close(fd, callback) { callback(enosys()); },
			fchmod(fd, mode, callback) { callback(enosys()); },
			fchown(fd, uid, gid, callback) { callback(enosys()); },
			fstat(fd, callback) { callback(enosys()); },
			fsync(fd, callback) { callback(null); },
			ftruncate(fd, length, callback) { callback(enosys()); },
			lchown(path, uid, gid, callback) { callback(enosys()); },
			link(path, link, callback) { callback(enosys()); },
			lstat(path, callback) { callback(enosys()); },
			mkdir(path, perm, callback) { callback(enosys()); },
			open(path, flags, mode, callback) { callback(enosys()); },
			read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
			readdir(path, callback) { callback(enosys()); },
			readlink(path, callback) { callback(enosys()); },
			rename(from, to, callback) { callback(enosys()); },
			rmdir(path, callback) { callback(enosys()); },
			stat(path, callback) { callback(enosys()); },
			symlink(path, link, callback) { callback(enosys()); },
			truncate(path, length, callback) { callback(enosys()); },
			unlink(path, callback) { callback(enosys()); },
			utimes(path, atime, mtime, callback) { callback(enosys()); },
		};
	}

	if (!global.process) {
		global.process = {
			getuid() { return -1; },
			getgid() { return -1; },
			geteuid() { return -1; },
			getegid() { return -1; },
			getgroups() { throw enosys(); },
			pid: -1,
			ppid: -1,
			umask() { throw enosys(); },
			cwd() { throw enosys(); },
			chdir() { throw enosys(); },
		}
	}

	if (!global.crypto) {
		const nodeCrypto = require("crypto");
		global.crypto = {
			getRandomValues(b) {
				nodeCrypto.randomFillSync(b);
			},
		};
	}

	if (!global.performance) {
		global.performance = {
			now() {
				const [sec, nsec] = process.hrtime();
				return sec * 1000 + nsec / 1000000;
			},
		};
	}

	if (!global.TextEncoder) {
		global.TextEncoder = require("util").TextEncoder;
	}

	if (!global.TextDecoder) {
		global.TextDecoder = require("util").TextDecoder;
	}

	// End of polyfills for common API.

	const encoder = new TextEncoder("utf-8");
	const decoder = new TextDecoder("utf-8");

	global.Go = class {
		constructor() {
			this.argv = ["js"];
			this.env = {};
			this.exit = (code) => {
				if (code !== 0) {
					console.warn("exit code:", code);
				}
			};
			this._exitPromise = new Promise((resolve) => {
				this._resolveExitPromise = resolve;
			});
			this._pendingEvent = null;
			this._scheduledTimeouts = new Map();
			this._nextCallbackTimeoutID = 1;

			const setInt64 = (addr, v) => {
				this.mem.setUint32(addr + 0, v, true);
				this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
			}

			const getInt64 = (addr) => {
				const low = this.mem.getUint32(addr + 0, true);
				const high = this.mem.getInt32(addr + 4, true);
				return low + high * 4294967296;
			}

			const loadValue = (addr) => {
				const f = this.mem.getFloat64(addr, true);
				if (f === 0) {
					return undefined;
				}
				if (!isNaN(f)) {
					return f;
				}

				const id = this.mem.getUint32(addr, true);
				return this._values[id];
			}

			const storeValue = (addr, v) => {
				const nanHead = 0x7FF80000;

				if (typeof v === "number") {
					if (isNaN(v)) {
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 0, true);
						return;
					}
					if (v === 0) {
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 1, true);
						return;
					}
					this.mem.setFloat64(addr, v, true);
					return;
				}

				switch (v) {
					case undefined:
						this.mem.setFloat64(addr, 0, true);
						return;
					case null:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 2, true);
						return;
					case true:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 3, true);
						return;
					case false:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 4, true);
						return;
				}

				let id = this._ids.get(v);
				if (id === undefined) {
					id = this._idPool.pop();
					if (id === undefined) {
						id = this._values.length;
					}
					this._values[id] = v;
					this._goRefCounts[id] = 0;
					this._ids.set(v, id);
				}
				this._goRefCounts[id]++;
				let typeFlag = 1;
				switch (typeof v) {
					case "string":
						typeFlag = 2;
						break;
					case "symbol":
						typeFlag = 3;
						break;
					case "function":
						typeFlag = 4;
						break;
				}
				this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
				this.mem.setUint32(addr, id, true);
			}

			const loadSlice = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return new Uint8Array(this._inst.exports.mem.buffer, array, len);
			}

			const loadSliceOfValues = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				const a = new Array(len);
				for (let i = 0; i < len; i++) {
					a[i] = loadValue(array + i * 8);
				}
				return a;
			}

			const loadString = (addr) => {
				const saddr = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
			}

			const timeOrigin = Date.now() - performance.now();
			this.importObject = {
				go: {
					// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
					// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
					// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
					// This changes the SP, thus we have to update the SP used by the imported function.

					// func wasmExit(code int32)
					"runtime.wasmExit": (sp) => {
						const code = this.mem.getInt32(sp + 8, true);
						this.exited = true;
						delete this._inst;
						delete this._values;
						delete this._goRefCounts;
						delete this._ids;
						delete this._idPool;
						this.exit(code);
					},

					// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
					"runtime.wasmWrite": (sp) => {
						const fd = getInt64(sp + 8);
						const p = getInt64(sp + 16);
						const n = this.mem.getInt32(sp + 24, true);
						fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
					},

					// func resetMemoryDataView()
					"runtime.resetMemoryDataView": (sp) => {
						this.mem = new DataView(this._inst.exports.mem.buffer);
					},

					// func nanotime1() int64
					"runtime.nanotime1": (sp) => {
						setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
					},

					// func walltime1() (sec int64, nsec int32)
					"runtime.walltime1": (sp) => {
						const msec = (new Date).getTime();
						setInt64(sp + 8, msec / 1000);
						this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
					},

					// func scheduleTimeoutEvent(delay int64) int32
					"runtime.scheduleTimeoutEvent": (sp) => {
						const id = this._nextCallbackTimeoutID;
						this._nextCallbackTimeoutID++;
						this._scheduledTimeouts.set(id, setTimeout(
							() => {
								this._resume();
								while (this._scheduledTimeouts.has(id)) {
									// for some reason Go failed to register the timeout event, log and try again
									// (temporary workaround for https://github.com/golang/go/issues/28975)
									console.warn("scheduleTimeoutEvent: missed timeout event");
									this._resume();
								}
							},
							getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
						));
						this.mem.setInt32(sp + 16, id, true);
					},

					// func clearTimeoutEvent(id int32)
					"runtime.clearTimeoutEvent": (sp) => {
						const id = this.mem.getInt32(sp + 8, true);
						clearTimeout(this._scheduledTimeouts.get(id));
						this._scheduledTimeouts.delete(id);
					},

					// func getRandomData(r []byte)
					"runtime.getRandomData": (sp) => {
						crypto.getRandomValues(loadSlice(sp + 8));
					},

					// func finalizeRef(v ref)
					"syscall/js.finalizeRef": (sp) => {
						const id = this.mem.getUint32(sp + 8, true);
						this._goRefCounts[id]--;
						if (this._goRefCounts[id] === 0) {
							const v = this._values[id];
							this._values[id] = null;
							this._ids.delete(v);
							this._idPool.push(id);
						}
					},

					// func stringVal(value string) ref
					"syscall/js.stringVal": (sp) => {
						storeValue(sp + 24, loadString(sp + 8));
					},

					// func valueGet(v ref, p string) ref
					"syscall/js.valueGet": (sp) => {
						const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
						sp = this._inst.exports.getsp(); // see comment above
						storeValue(sp + 32, result);
					},

					// func valueSet(v ref, p string, x ref)
					"syscall/js.valueSet": (sp) => {
						Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
					},

					// func valueDelete(v ref, p string)
					"syscall/js.valueDelete": (sp) => {
						Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
					},

					// func valueIndex(v ref, i int) ref
					"syscall/js.valueIndex": (sp) => {
						storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
					},

					// valueSetIndex(v ref, i int, x ref)
					"syscall/js.valueSetIndex": (sp) => {
						Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
					},

					// func valueCall(v ref, m string, args []ref) (ref, bool)
					"syscall/js.valueCall": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const m = Reflect.get(v, loadString(sp + 16));
							const args = loadSliceOfValues(sp + 32);
							const result = Reflect.apply(m, v, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 56, result);
							this.mem.setUint8(sp + 64, 1);
						} catch (err) {
							storeValue(sp + 56, err);
							this.mem.setUint8(sp + 64, 0);
						}
					},

					// func valueInvoke(v ref, args []ref) (ref, bool)
					"syscall/js.valueInvoke": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.apply(v, undefined, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueNew(v ref, args []ref) (ref, bool)
					"syscall/js.valueNew": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.construct(v, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueLength(v ref) int
					"syscall/js.valueLength": (sp) => {
						setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
					},

					// valuePrepareString(v ref) (ref, int)
					"syscall/js.valuePrepareString": (sp) => {
						const str = encoder.encode(String(loadValue(sp + 8)));
						storeValue(sp + 16, str);
						setInt64(sp + 24, str.length);
					},

					// valueLoadString(v ref, b []byte)
					"syscall/js.valueLoadString": (sp) => {
						const str = loadValue(sp + 8);
						loadSlice(sp + 16).set(str);
					},

					// func valueInstanceOf(v ref, t ref) bool
					"syscall/js.valueInstanceOf": (sp) => {
						this.mem.setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
					},

					// func copyBytesToGo(dst []byte, src ref) (int, bool)
					"syscall/js.copyBytesToGo": (sp) => {
						const dst = loadSlice(sp + 8);
						const src = loadValue(sp + 32);
						if (!(src instanceof Uint8Array)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					// func copyBytesToJS(dst ref, src []byte) (int, bool)
					"syscall/js.copyBytesToJS": (sp) => {
						const dst = loadValue(sp + 8);
						const src = loadSlice(sp + 16);
						if (!(dst instanceof Uint8Array)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					"debug": (value) => {
						console.log(value);
					},
				}
			};
		}

		async run(instance) {
			this._inst = instance;
			this.mem = new DataView(this._inst.exports.mem.buffer);
			this._values = [ // JS values that Go currently has references to, indexed by reference id
				NaN,
				0,
				null,
				true,
				false,
				global,
				this,
			];
			this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
			this._ids = new Map();  // mapping from JS values to reference ids
			this._idPool = [];      // unused ids that have been garbage collected
			this.exited = false;    // whether the Go program has exited

			// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
			let offset = 4096;

			const strPtr = (str) => {
				const ptr = offset;
				const bytes = encoder.encode(str + "\0");
				new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
				offset += bytes.length;
				if (offset % 8 !== 0) {
					offset += 8 - (offset % 8);
				}
				return ptr;
			};

			const argc = this.argv.length;

			const argvPtrs = [];
			this.argv.forEach((arg) => {
				argvPtrs.push(strPtr(arg));
			});
			argvPtrs.push(0);

			const keys = Object.keys(this.env).sort();
			keys.forEach((key) => {
				argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
			});
			argvPtrs.push(0);

			const argv = offset;
			argvPtrs.forEach((ptr) => {
				this.mem.setUint32(offset, ptr, true);
				this.mem.setUint32(offset + 4, 0, true);
				offset += 8;
			});

			this._inst.exports.run(argc, argv);
			if (this.exited) {
				this._resolveExitPromise();
			}
			await this._exitPromise;
		}

		_resume() {
			if (this.exited) {
				throw new Error("Go program has already exited");
			}
			this._inst.exports.resume();
			if (this.exited) {
				this._resolveExitPromise();
			}
		}

		_makeFuncWrapper(id) {
			const go = this;
			return function () {
				const event = { id: id, this: this, args: arguments };
				go._pendingEvent = event;
				go._resume();
				return event.result;
			};
		}
	}

	if (
		global.require &&
		global.require.main === module &&
		global.process &&
		global.process.versions &&
		!global.process.versions.electron
	) {
		if (process.argv.length < 3) {
			console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
			process.exit(1);
		}

		const go = new Go();
		go.argv = process.argv.slice(2);
		go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
		go.exit = process.exit;
		WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
			process.on("exit", (code) => { // Node.js exits if no event handler is pending
				if (code === 0 && !go.exited) {
					// deadlock, make Go print error and stack traces
					go._pendingEvent = { id: 0 };
					go._resume();
				}
			});
			return go.run(result.instance);
		}).catch((err) => {
			console.error(err);
			process.exit(1);
		});
	}
})();