=================================================================== RCS file: /home/cvs/OpenXM/doc/Attic/genkou19991125.tex,v retrieving revision 1.26 retrieving revision 1.109 diff -u -p -r1.26 -r1.109 --- OpenXM/doc/Attic/genkou19991125.tex 1999/12/20 16:01:35 1.26 +++ OpenXM/doc/Attic/genkou19991125.tex 1999/12/27 06:59:25 1.109 @@ -1,324 +1,519 @@ \documentclass{jarticle} -\title{\bf Open XM($B%?%$%H%kL$Dj(B)} -\author{ -%Maekawa, Masahide (Oct., 1999 -- : CVS server) \\ -$BA0@n(B $B$^$5$R$G(B, -%$B",4A;zD4$YCf!#(B commit $B8+$?$i65$($F$M!#$I$&$;L@F|J9$1$k$1$I!#(B -%Noro, Masayuki (Jan., 1996 -- : OpenXM Protocol, asir99) \\ -$BLnO$(B $B@59T(B, -%Ohara, Katsuyoshi (Jan., 1998 -- : ox\_math) \\ -$B>.86(B $B8yG$(B, \\ -%Okutani, Yukio (Oct., 1999 -- : asir contrib) \\ -$B1|C+(B $B9,IW(B, -%Takayama, Nobuki (Jan., 1996 -- : OpenXM Protocol, kan) \\ -$B9b;3(B $B?.5#(B, -%Tamura, Yasushi (Nov., 1998 -- : OpenMath proxy) \\ -$BEDB<(B $B63;N(B -} -\date{1999$BG/(B11$B7n(B25$BF|(B} +%% $OpenXM: OpenXM/doc/genkou19991125.tex,v 1.108 1999/12/26 16:25:13 tam Exp $ -%\pagestyle{empty} +\usepackage{jssac} +\title{OpenXM プロジェクトの現状について} +\author{奥 谷   行 央\affil{神戸大学大学院自然科学研究科} + \mail{okutani@math.sci.kobe-u.ac.jp} + \and 小 原   功 任\affil{金沢大学理学部} + \mail{ohara@kappa.s.kanazawa-u.ac.jp} + \and 高 山   信 毅\affil{神戸大学理学部} + \mail{takayama@math.sci.kobe-u.ac.jp} + \and 田 村   恭 士\affil{神戸大学大学院自然科学研究科} + \mail{tamura@math.sci.kobe-u.ac.jp} + \and 野 呂   正 行\affil{富士通研究所} + \mail{noro@para.flab.fujitsu.co.jp} + \and 前 川   将 秀\affil{神戸大学理学部} + \mail{maekawa@math.sci.kobe-u.ac.jp} +} +\art{} + \begin{document} \maketitle -\section{OpenXM $B$N7W;;%b%G%k(B} +\section{OpenXMとは} -OpenXM $B$O?t3X%=%U%H4V$G%a%C%;!<%8$r8r49$9$k$?$a$N5,Ls$G$"$k!#(B -$B?t3XE*$J%G!<%?$r4^$s$@%a%C%;!<%8$J$I$rMQ$$$F(B -$B?t3X%=%U%H4V$G%a%C%;!<%8$r$d$j$H$j$5$;$k$3$H$K$h$j!"(B -$B$"$k?t3X%=%U%H$+$iB>$N?t3X%=%U%H$r8F$S=P$7$F7W;;$r9T$J$C$?$j!"(B -$BB>$N%^%7%s$G7W;;$r9T$J$o$;$?$j$9$k$3$H$,L\E*$G$"$k!#(B -$BH/C<$OLnO$@59T$H9b;3?.5#$K$h$j!"(B asir $B$H(B kan/sm1 $B$r(B -$BAj8_$K8F$S=P$95!G=$r$N?t3X%=%U%H$r;H$($k$h$&$K$9$k$3$H$G$"$k!#(B -$B$J$*!"(B OpenXM $B$H$O(B Open message eXchange protocol for Mathematics $B$N(B -$BN,$G$"$k!#(B +OpenXM は数学プロセス間でメッセージを交換するための規約である. 数学プロ +セス間でメッセージをやりとりすることにより, ある数学プロセスから他の数学 +プロセスを呼び出して計算を行なったり, 他のマシンで計算を行なわせたりする +ことが目的である. なお, OpenXM とは Open message eXchange protocol for +Mathematics の略である. OpenXM の開発の発端は野呂と高山により, asir と +kan/sm1 を相互に呼び出す機能を実装したことである. -$BH/C<$H$J$C$?(B asir $B$H(B kan/sm1 $B$G$N$N7A<0$r$b(B -$B07$($k$h$&$K$7$F$"$k!#(B -$B$J$*!"8=:_$N(B OpenXM $B5,Ls$G$O!"(B -$BA0=R$N%3%^%s%IJ8;zNs$b(B CMO $B7A<0$J$I$N2?$i$+$N%G!<%?7A<0$NCf$N(B -$BJ8;zNs$H$7$FI=8=$7$FAw$kI,MW$,$"$k!#(B +現在の OpenXM 規約では共通表現形式によるメッセージを用いている. 上記の +文字列を送る方法の利点を生かすため, OpenXM 規約では共通表現形式の中の文 +字列として, ローカル言語の文法に従った文字列を用いたメッセージの交換も可 +能となっている. -OpenXM $B5,Ls$G$N%a%C%;!<%8$N8r49$O%5!<%P$H%/%i%$%"%s%H$N4V$G9T$J$o$l$k!#(B -$B%5!<%P$O%9%?%C%/%^%7%s$G$"$k$H2>Dj$5$l$F$*$j!"(B -$B%5!<%P$,%/%i%$%"%s%H$+$i$lFHN)$K7h$a$i$l$k$h$&$K$J$C$F$$$k!#(B -$B$b$7!"(B OpenXM $B5,Ls$G%a%C%;!<%8$N$d$j$H$j$r9T$J$$$?$$$,!"(B -$B$^$@5,Ls$GDj5A$5$l$F$$$J$$%G!<%?7A<0$r;H$$$?$$>l9g$O!"(B -tag $B$r$^$@;H$o$l$F$J$5$=$&$JCM(B -($B%7%9%F%`8GM-$NI=8=$N$?$a$K?d>)$5$l$F$$$kCM$,$"$k(B) -$B$K@_Dj$7!"(B body $B$NItJ,$K%G!<%?$rKd$a9~$a$P$h$$!#(B -$B$J$*!"$9$Y$F$N%a%C%;!<%8$K(B body $B$,I,MW$H$$$&$o$1$G$O$J$/!"(B -body $B$N$J$$%a%C%;!<%8$b(B OpenXM $B5,Ls$K$OB8:_$9$k$3$H$K(B -$BCm0U$7$J$1$l$P$J$i$J$$!#(B +OpenXM 規約で規定されているメッセージはバイトストリームとなっており, 次 +のような構造になっている. +\begin{center} +\begin{tabular}{|c|c|} +\hline +ヘッダ & \hspace{10mm} ボディ \hspace{10mm} \\ +\hline +\end{tabular} +\end{center} +ヘッダの長さは 8 バイトであると定められている. ボディの長さはメッセージ +ごとに異なっているが, 長さは $0$ でもよい. -$B%5!<%P$KBP$9$kF0:n$KBP1~$7$?%G!<%?$O(B SM $B7A<0$H$7$FDj5A$5$l$F$$$k!#(B -SM $B7A<00J30$N%G!<%?$G$O!"%5!<%P$Ol9g!"(B -$B%/%i%$%"%s%H$+$i%5!<%P$X7W;;$5$;$?$$%G!<%?$r%a%C%;!<%8$H$7$FAw$j!"(B -$B$=$7$F$=$N7k2L$r%5!<%P$+$i%a%C%;!<%8$Ge$2$F$$$/$@$1$G!"%5!<%P$O7W;;$r9T$J$*$&$H$O$7$J$$!#(B -$Bl9g!"(B -$B%5!<%P$OF0:n$N7k2L$r%9%?%C%/$K@Q$s$G$$$k!#(B -$B%5!<%P$K9T$J$o$;$?F0:n$N7k2L$r%/%i%$%"%s%H$,CN$j$?$$>l9g!"(B -$B%9%?%C%/$+$i%G!<%?$rl9g!"(B -CMO $B7A<0$GDj5A$5$l$F$$$kB?G\D9@0?t$rM}2r$7$F$*$/$H!"(B -CMO $B7A<0$NB>$N%G!<%?9=B$$@$1$G$J$/!"(B OX $B7A<0!"(B SM $B7A<0$N%G!<%?$r(B -$BM}2r$9$k=u$1$K$J$k$H;W$($k$N$G!"(B CMO $B7A<0$NB?G\D9@0?t$N(B -$B%G!<%?9=B$$K$D$$$F@bL@$9$k!#(B +OpenXM 規約ではサーバはスタックマシンであると定義している. 以下, OpenXM +スタックマシンと呼ぶ. この節ではOpenXM スタックマシンの構造について説明 +しよう. -CMO $B7A<0$GDj5A$5$l$F$$$k%G!<%?$OB?G\D9@0?t0J30$K$b(B -$BJ8;zNs$d%j%9%H9=B$$J$I$,$"$k!#$I$N$h$&$J%G!<%?$G$"$k$+$O(B -$B%G!<%?$N@hF,$K$"$k(B tag $B$r8+$l$PH=JL$G$-$k$h$&$K$J$C$F$$$k!#(B -$B$3$l$O%a%C%;!<%8$N%G!<%?$NH=JL$N;EJ}$H$*$J$8$G$"$k!#(B -$B$J$*!"(B tag $B$O3F%G!<%?Kh$K(B 32 bit $B$N@0?t$GI=$5$l$F$*$j!"(B -$BB?G\D9@0?t$O(B 20 $B$H$J$C$F$$$k!#(B -$B$3$3$G(B 32 bit $B$N@0?t$NI=8=J}K!$K$D$$$F@bL@$9$kI,MW$,$"$k!#(B -OpenXM $B$G$O(B 32 bit $B$N@0?t(B 20 $B$r%P%$%HNs$G(B {\tt 00 00 00 14} $B$HI=$9J}K!$H(B -{\tt 14 00 00 00} $B$HI=$9J}K!$,$"$k!#(B -$B$3$NI=8=J}K!$N0c$$$O%/%i%$%"%s%H$H%5!<%P$N:G=i$N@\B3;~$K(B -$BAPJ}$N9g0U$G7hDj$9$k$3$H$K$J$C$F$$$k!#(B -$B$J$*!"9g0U$,$J$$>l9g$K$O(B -$BA0l9g$N7e?t$r(B $n$ $B$H(B -$B$7$?$H$-!"l9g$N7e?t$H$H$C$F$b$h$$!#(B -$B$?$@$7!"I=8=$7$?$$?t$,Ii$N>l9g$O(B $[(n+31)/32]$ $B$r(B 32 bit $B$N@0?t$GI=$7$?CM$r(B - 2 $B$NJd?tI=8=$GIi$K$7$F!"@5$N>l9g$H6hJL$9$k!#(B +次に OpenXM スタックマシンの命令コードについて説明する. OpenXM スタック +マシンにおけるすべての命令は 4 バイトの長さを持つ. OpenXM 規約の他の規 +定と同様に, 4 バイトのデータは32ビット整数と見なされるので, この論文でも +その表記にしたがう. OpenXM スタックマシンに対する命令はスタックに積まれ +ることはない. 現在のところ, OpenXM 規約では以下の命令が定義されている. -$BI=8=$7$?$$B?G\D9@0?t$N@dBPCM$,(B $2^{32}$ $B?J?t$G(B $(b_0 b_1 ... b_k)_{2^{32}}$ -$B$HI=$;$k$H$-!"$l(B 32 bit $B$N@0?t$GI=8=$7$?CM$H$J$k!#(B -%$B0J2<$O=q$-D>$7$NI,MW$,$"$k$+$b(B... -$B$J$*!"(B GNU MP LIBRARY $B$rMQ$$$k$H!"(B -C $B8@8l$+$iB?G\D9@0?t$dG$0U@:EYIbF0>.?t$r07$&$3$H$,$G$-$k!#(B -$b_0$, $b_1$, $\cdots$, $b_k$ $B$r$=$l$>$l(B 32 bit $B@0?t$GI=8=$7$?CM$O(B -$B$3$N(B GNU MP LIBRARY $B$GMQ$$$i$l$F$$$kB?G\D9@0?t$G;H$o$l$F$$$k7A<0$r(B -$B;29M$K$7$F9g$o$;$F$"$k!#(B +\begin{verbatim} +#define SM_popSerializedLocalObject 258 +#define SM_popCMO 262 +#define SM_popString 263 +#define SM_mathcap 264 +#define SM_pops 265 +#define SM_setName 266 +#define SM_evalName 267 +#define SM_executeStringByLocalParser 268 +#define SM_executeFunction 269 +#define SM_beginBlock 270 +#define SM_endBlock 271 +#define SM_shutdown 272 +#define SM_setMathCap 273 +#define SM_executeStringByLocalParserInBatchMode 274 +#define SM_getsp 275 +#define SM_dupErrors 276 +#define SM_DUMMY_sendcmo 280 +#define SM_sync_ball 281 +#define SM_control_kill 1024 +#define SM_control_to_debug_mode 1025 +#define SM_control_exit_debug_mode 1026 +#define SM_control_ping 1027 +#define SM_control_start_watch_thread 1028 +#define SM_control_stop_watch_thread 1029 +#define SM_control_reset_connection 1030 +\end{verbatim} -$B$3$3$G6qBNNc$r$@$=$&!#(B -$4294967298 = 1 \times 2^{32} + 2$ $B$r(B network byte order $B$NB?G\D9@0?t$G(B -$BI=8=$9$k$H!"(B +スタックマシンに対する命令の中には実行によって結果が返ってくるものがある. +結果が返ってくる命令を実行した場合, サーバはその結果をスタックに積む. +たとえば, 命令 SM\_executeStringByLocalParser はスタックに積まれているオ +ブジェクトをサーバ側のローカル言語の文法に従った文字列とみなして計算を行 +なうが, 行なった計算の結果はスタックに積まれる. + +なお, 命令の実行中にエラーが起こり, 結果が得られなかった場合には, +エラーオブジェクトがスタックに積まれる. + +\section{CMO のデータ構造}\label{sec:cmo} + +OpenXM 規約では, 数学的オブジェクトを表現する方法として CMO 形式(Common +Mathematical Object format)を定義している. この CMO 形式にしたがったデー +タは, 識別子が OX\_DATA であるようなメッセージのボディになることを想定し +ている. + +CMO 形式におけるデータ構造は次のような構造をもつ. \begin{center} - {\tt 00 00 00 14 00 00 00 02 00 00 00 02 00 00 00 01} +\begin{tabular}{|c|c|} +\hline +ヘッダ & \hspace{10mm} ボディ \hspace{10mm} \\ +\hline +\end{tabular} \end{center} -$B$H$J$k!#$^$?!"F1$8I=8=J}K!$G(B $-1$ $B$rI=8=$9$k$H!"(B -\begin{center} - {\tt 00 00 00 14 ff ff ff ff 00 00 00 01} -\end{center} -$B$H$J$k!#(B +ヘッダは4バイトである. ボディの長さはそれぞれのデータによって異なるが, +0でもよい. +メッセージと同様にヘッダは4バイト単位に管理される. すなわち, CMO では +ヘッダは一つだけの情報を含む. この4バイトのヘッダのことをタグともいう. +さて, CMO では, タグによってボディの論理的構造が決定する. すなわち, タ +グはそれぞれのデータ構造と1対1に対応する識別子である. それぞれの論理的 +構造は\cite{OpenXM-1999} に詳述されている. 現在の OpenXM 規約では以下の +CMO が定義されている. -\section{MathCap $B$K$D$$$F(B} +\begin{verbatim} +#define CMO_ERROR2 0x7f000002 +#define CMO_NULL 1 +#define CMO_INT32 2 +#define CMO_DATUM 3 +#define CMO_STRING 4 +#define CMO_MATHCAP 5 +#define CMO_ARRAY 16 +#define CMO_LIST 17 +#define CMO_ATOM 18 +#define CMO_MONOMIAL32 19 +#define CMO_ZZ 20 +#define CMO_QQ 21 +#define CMO_ZERO 22 +#define CMO_DMS_GENERIC 24 +#define CMO_DMS_OF_N_VARIABLES 25 +#define CMO_RING_BY_NAME 26 +#define CMO_RECURSIVE_POLYNOMIAL 27 +#define CMO_LIST_R 28 +#define CMO_INT32COEFF 30 +#define CMO_DISTRIBUTED_POLYNOMIAL 31 +#define CMO_POLYNOMIAL_IN_ONE_VARIABLE 33 +#define CMO_RATIONAL 34 +#define CMO_64BIT_MACHINE_DOUBLE 40 +#define CMO_ARRAY_OF_64BIT_MACHINE_DOUBLE 41 +#define CMO_128BIT_MACHINE_DOUBLE 42 +#define CMO_ARRAY_OF_128BIT_MACHINE_DOUBLE 43 +#define CMO_BIGFLOAT 50 +#define CMO_IEEE_DOUBLE_FLOAT 51 +#define CMO_INDETERMINATE 60 +#define CMO_TREE 61 +#define CMO_LAMBDA 62 +\end{verbatim} -$B%5!<%P$*$h$S%/%i%$%"%s%HAPJ}$H$b$K(B OpenXM $B$G5,Dj$5$l$F$$$k(B -$B%a%C%;!<%8$NCf$N%G!<%?7A<0$r$9$Y$FM}2r$G$-$k$o$1$G$O$J$$!#(B -$B$7$+$b!"(B OpenXM $B5,Ls$G5,Dj$5$l$F$$$k%G!<%?7A<0$@$1$,(B -$BpJs$,F~$C$F$$$k!#(B -%$B$3$N:G=i$NMWAG$,$^$?%j%9%H9=B$$H$J$C$F$*$j!"(B -$B:G=i$NMWAG$O%P!<%8%g%s%J%s%P!<$r!"A0$rI=$7$F$$$k!#(B +\section{mathcap について} -$BpJs$rI=$7$F$$$k$H$$$C$?$3$H$,M}2r$G$-$k$3$H$H!"(B -$B%G!<%?$,o$K@\B3$rBT$D$3$H$K$h$C$F!"(B -$B?/F~Z$r9T$J$&$h$&$K$J$C$F$$$k!#$3$N%Q%9%o!<%I$O0lC6;HMQ$5$l$k$H(B -$BL58z$K$J$k$N$G!"$b$72>$K$J$s$i$+$Ne$G$O$3$N%Q%9%o!<%I$,$o$+$C$F$7$^$&$?$a!"(B -$BF10l$N%3%s%T%e!<%?>e$K0-0U$N$"$k%f!<%6$O$$$J$$$H2>Dj$7$F$$$k(B -$B$3$H$KCm0U$7$J$1$l$P$J$i$J$$!#(B +第三要素 $c$ は以下のような cmo\_list であり, オブジェクトの送受信を制御 +するために用いられる. 送受信の制御はメッセージの種類ごとに行われる. +\begin{quote} +(CMO\_LIST, {\sl int32} $m$, {\sl cmo\_list} $\ell_1$, $\ldots$, +{\sl cmo\_list} $\ell_m$) +\end{quote} +各 $\ell_i$ が制御のための情報を表す. どの $\ell_i$ も一つ以上の要素を +持っており, 第一要素は必ず cmo\_int32 となっていなければならない. これ +は制御すべきメッセージの識別子を入れるためである. -$B$J$*!"@\B3$,3NN)$7$?8e$N%a%C%;!<%8$NAw$N%W%m%8%'%/%H(B} -$BB>$N%W%m%8%'%/%H$K$D$$$F4v$D$+>R2p$9$k!#(B +\section{セキュリティ対策} -OpenMath $B%W%m%8%'%/%H$O?t3XE*$J%*%V%8%'%/%H$r(B -$B%3%s%T%e!<%?>e$GI=8=$9$kJ}K!$r7hDj$7$F$$$k!#(B -$B3F%=%U%H%&%'%"4V$G%*%V%8%'%/%H$r8r49$9$k:]$N(B -$B%*%V%8%'%/%H$NJQ49$l(B ox\_asir, ox\_sm1, ox\_math $B$H$$$&L>A0$GDs6!$5$l$F$$$k!#(B -$B$^$?!"(B OpenMath $B5,3J$N(B XML $BI=8=$GI=8=$5$l$?%G!<%?$H(B CMO $B7A<0$N(B -$B%G!<%?$rJQ49$9$k%=%U%H%&%'%"$,(B JAVA $B$K$h$C$FA0$GDs6!$5$l$F$$$k!#(B +\section{OpenXM 以外のプロジェクト}\label{sec:other} + +OpenXM 以外にも数式処理システム間の通信や数学データの共通表現を目指した +プロジェクトは存在する. ここでは他のプロジェクトについても触れておこう. + +\begin{itemize} +\item ESPRIT OpenMath Project + +http://www.openmath.org/omsoc/ + +数学的対象の SGML 的表記の標準化を目指した大規模なプロジェクト. このプ +ロジェクトでは数学データを数学的意味を保ったままで如何に表現すべきかとい +う問題を追求している. したがって既存の表現, 例えば \TeX による数式の表 +現と OpenMath による数式の表現とでは, 本質的に意味が異なる. OpenMath で +定義された表現は, 異なる種類の数式処理システムの間で情報を交換するときに +利用することができる. しかしながら, 数学システム同士の通信, 例えばある +数学システムから別の数学システムを呼び出して計算させる方法などは, このプ +ロジェクトの対象外である. + +OpenXM 規約の CMO 形式の定義は OpenMath 規約の content dictionary の概念 +に似ている(もちろん OpenMath の方がもっと大掛かりで厳密な規定である). +また, 共通データ形式と数学システム固有のオブジェクトとの変換は OpenMath +規約の Phrasebook と同じアイデアを用いている. + +\item NetSolve + +http://www.cs.utk.edu/netsolve/ + +NetSolve はクライアント・サーバ型の分散システムであり, 単なる計算システ +ム以上のものを目指している. クライアントは必要に応じて, サーバを呼び出 +して計算をさせる. NetSolve の特徴は, サーバの呼び出しに Agent というソ +フトウェアを介在させることである. Agent は呼び出し先などを決定するデー +タベース的役割を果たす. また Agent によって負荷分散が可能になる. 現在 +の NetSolve は RPC を基礎にして実装されている. + +\item MP (Multi Project) + +http://symbolicnet.mcs.kent.edu/SN/areas/protocols/mp.html + +科学技術計算を行なうソフトウェア間で数学的なデータの交換を +目的とするプロジェクト. +MP は数学的なオブジェクトを構文木を用いて表現する. +そして, この木構造を交換するためのプロトコルの作成を目標にしている. +OpenMath と同様, このプロジェクトでは計算させる方法等には +触れていない. +プロトコルは通信方法に依存しないが, 速度面に配慮しており, +並列計算に使用することも考えている. +すでに C 言語で利用可能なライブラリが提供されており, +現在は Lisp による実装も計画されている. + +\item MCP (Mathematical Computation Protocol) + +http://horse.mcs.kent.edu/\~{}pwang/ + +数学的な計算を行なうための HTTP に似たプロトコル. クライアント・サーバ +モデルを採用しており, ピアツーピアのストリームコネクションを行なう. 交 +換に用いられる数学データの表現方法についての規定はない. したがって数学 +的なデータの表現には MP や OpenXM で定められたものを利用する. 実際, 数 +学データの表現に OpenMath の XML 表現を用いた実装があり, GAP と Axiom の +間で通信が行われている. この場合 MCP によって送信されるデータは, 本文に +OpenMath 形式で数式を記述したテキストである. + +\end{itemize} + + +\section{現在提供されているソフトウェア} + +現在 OpenXM 規約に対応しているクライアントにはasir, sm1, Mathematica が +ある. これらのクライアントから OpenXM 規約に対応したサーバを呼び出すこ +とができる. また OpenXM 規約に対応しているサーバには, asir, sm1, +Mathematica, gnuplot, PHC pack などがあり, それぞれ ox\_asir, ox\_sm1, +ox\_math, ox\_sm1\_gnuplot, ox\_sm1\_phc という名前で提供されている. +さらに OpenMath 規約の XML 表現で表現されたオブジェクトと CMO 形式のオブ +ジェクトを相互変換するソフトウェアが JAVA によって実装されており, +OMproxy という名前で提供されている. + +\begin{thebibliography}{99} +\bibitem{Ohara-Takayama-Noro-1999} +小原功任, 高山信毅, 野呂正行: +{Open asir 入門}, 1999, 数式処理, +Vol 7, No 2, 2--17. (ISBN4-87243-086-7, SEG 出版, Tokyo). + +\bibitem{OpenXM-1999} +野呂正行, 高山信毅: +{Open XM の設計と実装 +--- Open message eXchange protocol for Mathematics}, +1999/11/22 +\end{thebibliography} \end{document}