/*========================================================================= * 多倍長整数演算ライブラリ * created by Rayce based on mod2273 * * ■ int型を越える桁数の数字を演算するための関数です。 * 10000進数として配列に格納して計算を行います。 * 配列上限128個×4 = 512桁まで利用可能です。 * 基本的には一般的なアルゴリズムをAthenaのスクリプトとして書き起こした * だけに過ぎませんので、ロジックそのものに独自性はありません。 * ただし配列[0]を最下桁として扱っているので逆読みになります。 * これはgetarraysizeを使って最上桁を比較的高速に割り出すためです。 * * e.g 111222233334444 * -> 111 | 2222 | 3333 | 4444 * [3] [2] [1] [0] ← 配列の進む方向 * * ■ ここで用意している関数は全て値が正であることを前提としています。 * 負の値を用いた場合の挙動は未定義です。 * またそれぞれの関数に書かれている注釈に従わない場合の挙動も未定義です。 * 簡略化と速度優先のために不正チェックは行いませんので注意してください。 * * ■ 除算ではinfinity loopを意図的に避けるためにsleepを使っています。 * RIDが必要な場合は必ず呼び出し元でRIDを保存しておき、演算完了後に * アタッチし直さなければなりません。 *------------------------------------------------------------------------- */ /*========================================== * 文字列整数値を拡張整数へセット *  callfunc "lset",,; *------------------------------------------ */ function script lset { set '@str$,getarg(1); set '@len,getstrlen('@str$); for(set '@i,0; '@len > 0; set '@i,'@i+1) { if('@len > 4) set getelementofarray(getarg(0),'@i),substr('@str$,'@len-4,4); else set getelementofarray(getarg(0),'@i),substr('@str$,0,'@len); set '@len,'@len-4; } return; } /*========================================== * 拡張整数を文字列に変換して返す *  callfunc("ldisp",) *------------------------------------------ */ function script ldisp { set '@max,getarraysize(getarg(0))-1; for(set '@i,0; '@i<'@max; set '@i,'@i+1) { set '@val$,getelementofarray(getarg(0),'@i); switch(getstrlen('@val$)) { case 1: set '@val$,"0000"; break; case 2: set '@val$,"00" + '@val$; break; case 3: set '@val$,"0" + '@val$; break; } set '@str$,'@val$ + '@str$; } // 最上桁は0補正せずそのまま set '@str$,getelementofarray(getarg(0),'@i) + '@str$; return '@str$; } /*========================================== * 比較、> : 1, < ; -1, == : 0 *  callfunc "lcmp"(,) *------------------------------------------ */ function script lcmp { set '@size_a,getarraysize(getarg(0)); set '@size_b,getarraysize(getarg(1)); // 配列のサイズだけで決まる場合 if('@size_a > '@size_b) return 1; if('@size_a < '@size_b) return -1; // 配列のサイズが同じなので比較処理する for(set '@i,'@size_a-1; '@i>=0; set '@i,'@i-1) { if(getelementofarray(getarg(0),'@i) > getelementofarray(getarg(1),'@i)) return 1; if(getelementofarray(getarg(0),'@i) < getelementofarray(getarg(1),'@i)) return -1; } return 0; } /*========================================== * 加算 *  callfunc "ladd",,,; *------------------------------------------ */ function script ladd { set '@size_a,getarraysize(getarg(1)); set '@size_b,getarraysize(getarg(2)); set '@max,('@size_a > '@size_b)? '@size_a+1: '@size_b+1; for(set '@i,0; '@i<'@max; set '@i,'@i+1) { set '@r,getelementofarray(getarg(1),'@i) + getelementofarray(getarg(2),'@i) + '@carry; set '@carry,'@r / 10000; set getelementofarray(getarg(0),'@i),'@r % 10000; } if('@carry) debugmes "ladd: over flow !!"; return; } /*========================================== * 減算 * 左辺値が右辺値より大であること *  callfunc "lsub",,,; *------------------------------------------ */ function script lsub { set '@size_a,getarraysize(getarg(1)); set '@size_b,getarraysize(getarg(2)); set '@max,('@size_a > '@size_b)? '@size_a: '@size_b; for(set '@i,0; '@i<'@max; set '@i,'@i+1) { set '@r,getelementofarray(getarg(1),'@i) - getelementofarray(getarg(2),'@i) - '@borrow; if('@r < 0) { set '@borrow,1; set getelementofarray(getarg(0),'@i),'@r+10000; } else { set '@borrow,0; set getelementofarray(getarg(0),'@i),'@r; } } if('@borrow) debugmes "lsub: arg1 < arg2 !!"; return; } /*========================================== * 乗算(拡張整数×整数4桁) * 必ず4桁以内であること *  callfunc "lmul_h",,,; *------------------------------------------ */ function script lmul_h { set '@max,getarraysize(getarg(1))+1; set '@num,getarg(2); for(set '@i,0; '@i<'@max; set '@i,'@i+1) { set '@r,getelementofarray(getarg(1),'@i) * '@num + '@carry; set '@carry,'@r / 10000; set getelementofarray(getarg(0),'@i),'@r % 10000; } if('@carry) debugmes "lmul_h: over flow !!"; return; } /*========================================== * 乗算(拡張整数×拡張整数) *  callfunc "lmul",,,; *------------------------------------------ */ function script lmul { copyarray '@a,getarg(1),128; // 元のデータを壊さないようにコピー set '@max,getarraysize(getarg(2)); while(1) { callfunc "lmul_h",'@result,'@a,getelementofarray(getarg(2),'@i); callfunc "ladd",getarg(0),getarg(0),'@result; if('@i >= '@max) break; // 4桁上位シフト if(getarraysize('@a) >= 128) { debugmes "lmul: over flow !!"; return; } copyarray '@a[1],'@a[0],127; set '@a[0],0; set '@i,'@i+1; } return; } /*========================================== * 除算(拡張整数÷整数4桁) * 必ず4桁以内であること *  callfunc "ldiv_h",,,; *------------------------------------------ */ function script ldiv_h { set '@num,getarg(2); for(set '@i,getarraysize(getarg(1))-1; '@i>=0; set '@i,'@i-1) { set '@t,getelementofarray(getarg(1),'@i) + '@rest; set getelementofarray(getarg(0),'@i),'@t / '@num; set '@rest,('@t % '@num) * 10000; } return; } /*========================================== * 除算(拡張整数÷拡張整数) * RIDが必要なら呼び出し元で復帰処理すること *  callfunc "ldiv",,,; *------------------------------------------ */ function script ldiv { if(callfunc("lcmp",getarg(1),getarg(2)) < 0) return; // 0は確定 set '@size_a,getarraysize(getarg(1)); set '@size_b,getarraysize(getarg(2)); if('@size_b == 0) { debugmes "ldiv: arg2 is 0 !!"; return; } if('@size_b == 1) { // ldiv_hを実行するだけでよい callfunc "ldiv_h",getarg(0),getarg(1),getarg(2); return; } // 元のデータを壊さないようにコピー copyarray '@a,getarg(1),'@size_a; copyarray '@b,getarg(2),'@size_b; // 先頭位置を揃える set '@diff,'@size_a - '@size_b; if('@diff > 0) { copyarray '@b['@diff],'@b[0],'@size_b; cleararray '@b[0],0,'@diff; } for(set '@i,'@size_a-1; ; set '@i,'@i-1) { set '@ans,0; set '@top_val,0; for(set '@j,'@size_a-1; '@j >= '@i; set '@j,'@j-1) { set '@top_val,'@top_val * 10000 + '@a['@j]; } set '@div_val,'@top_val / '@b['@i]; // 除算の概算値 if('@div_val > 9999) set '@div_val,9999; while('@div_val > 0) { callfunc "lmul_h",'@result,'@b,'@div_val; // 除算値×除数 while(callfunc("lcmp",'@a,'@result) >= 0) { callfunc "lsub",'@a,'@a,'@result; set '@ans,'@ans + '@div_val; } set '@div_val,'@div_val / 2; } // cmdcountがリミットに達しやすいのでここで処理分割する sleep 1; cleararray '@buf,0,128; callfunc "lset",'@buf,""+'@ans; // 文字列に変換して引数に渡す callfunc "ladd",getarg(0),getarg(0),'@buf; if('@i < '@size_b) break; // bの位置まで計算したら終了 if('@a['@size_a-1] == 0) set '@size_a,'@size_a-1; // 先頭位置更新 // 除数を4桁下位シフト set '@max,getarraysize('@b); copyarray '@b[0],'@b[1],'@max; set '@b['@max-1],0; // 最終結果を4桁上位シフト copyarray getelementofarray(getarg(0),1),getelementofarray(getarg(0),0),127; set getelementofarray(getarg(0),0),0; } return; } /*========================================== * デバッガ *------------------------------------------ */ prontera.gat,156,191,4 script LongNum#Debug 112,{ callfunc "lset",'@a,"260110234518136036460348"; callfunc "lset",'@b,"4532508591"; mes "左辺 = " + callfunc("ldisp",'@a); mes "右辺 = " + callfunc("ldisp",'@b); mes " "; mes "cmp : " + callfunc("lcmp",'@a,'@b); callfunc "ladd",'@add,'@a,'@b; mes "add : " + callfunc("ldisp",'@add); callfunc "lsub",'@sub,'@a,'@b; mes "sub : " + callfunc("ldisp",'@sub); callfunc "lmul",'@mul,'@a,'@b; mes "mul : " + callfunc("ldisp",'@mul); // ldivは内部でsleepするのでアタッチできるようにする set '@rid,getcharid(3); callfunc "ldiv",'@div,'@a,'@b; if(!attachrid('@rid)) end; mes "div : " + callfunc("ldisp",'@div); next; mes "左辺"; input '@str$; mes '@str$; callfunc "lset",'@left,'@str$; mes "右辺"; input '@str$; mes '@str$; callfunc "lset",'@right,'@str$; switch( select("比較","加算","減算","乗算","除算","終了")) { case 1: mes "比較"; set '@result,callfunc("lcmp",'@left,'@right); mes '@result +" ( "+ (('@result > 0)? ">": ('@result < 0)? "<": "==") +" )"; close; case 2: mes "加算"; callfunc "ladd",'@result,'@left,'@right; break; case 3: mes "減算"; callfunc "lsub",'@result,'@left,'@right; break; case 4: mes "乗算"; callfunc "lmul",'@result,'@left,'@right; break; case 5: mes "除算"; set '@rid,getcharid(3); callfunc "ldiv",'@result,'@left,'@right; if(!attachrid('@rid)) end; break; case 6: mes "終了"; close; } mes callfunc("ldisp",'@result); close; }