clear

// ExtendedEuclidean

// 欧几里得算法又称辗转相除法,是指用于计算两个非负整数a,b的最大公约数。应用领域有数学和计算机两个方面。
// 计算公式gcd(a,b) = gcd(b,a mod b)

a       hex1997 )
b       hex615 )
t       call gcd$a$b )

// 扩展欧几里得算法(Extended Euclidean algorithm)是欧几里得算法的扩展。
// 已知整数a、b,必存在整数x、y使得ax + by = gcd(a,b).

// 扩展欧几里得可以求出:
// a·x + m·y = gcd(a, m)
// 如果 gcd(a,m)=1,则变成:
// a·x + m·y = 1
// 把右边的 1 看成 mod m 的等式:
// a·x ≡ 1 (mod m)
// 所以 x 就是逆元

a       hex10 )
m       hex17 )
flag    ""

x       call modinv$a$m&flag )
if $flag == 00
    
tmp big_mul$a$x )
    
tmp big_mod$tmp$m )
else
    
tmp big_mul$m$x )
    
tmp big_mod$tmp$a )
endif
if $tmp != 01
    
? "求逆失败"
    
pause
endif

m       hex10 )
a       hex17 )
flag    ""

x       call modinv$a$m&flag )
if $flag == 00
    
tmp big_mul$a$x )
    
tmp big_mod$tmp$m )
else
    
tmp big_mul$m$x )
    
tmp big_mod$tmp$a )
endif
if $tmp != 01
    
? "求逆失败"
    
pause
endif



a       int2hex0987654321012345678909876543210123456789 )
m       int2hex1000000000000000000000000000000000000037 )

x       call modinv$a$m&flag )
if $flag == 00
    
tmp big_mul$a$x )
    
tmp big_mod$tmp$m )
else
    
tmp big_mul$m$x )
    
tmp big_mod$tmp$a )
endif
if $tmp != 01
    
? "求逆失败"
    
pause
endif

a       int2hex0314159265358979323846264338327950288419 )
m       int2hex0271828182845904523536028747135266249775 )

x       call modinv$a$m&flag )
if $flag == 00
    
tmp big_mul$a$x )
    
tmp big_mod$tmp$m )
else
    
tmp big_mul$m$x )
    
tmp big_mod$tmp$a )
endif
if $tmp != 01
    
? "求逆失败"
    
pause
endif


end

gcd:
    
prompt off
    
local a getpara
    
local b getpara
    
local tmp
    
local r

    
if $a < $b
        
tmp $a
        
a   $b
        
b   $tmp
    
endif

    
r       big_mod$a$b )
    
if $r == 00
        
r   $b
    
else
        
r   call gcd$b$r )
    
endif
    
prompt on
return $r

ExtEuclid:
    
prompt off
    
local a         getpara
    
local b         getpara
    
local x_abs     getpara
    
local x_sign    getpara
    
local y_abs     getpara
    
local y_sign    getpara

    
local x
    
local g
    
local tmp


    
if $b == 00
        
x       set_value$x_abs01 )
        
x       set_value$x_sign01 )
        
x       set_value$y_abs00 )
        
x       set_value$y_sign01 )
        
g       $a
    
else
        
local x1_abs
        
local y1_abs
        
local x1_sign
        
local y1_sign
        
tmp     big_mod$a$b )
        
g       call exteuclid$b$tmp&x1_abs&x1_sign&y1_abs&y1_sign )

        
x       set_value$x_abs$y1_abs )
        
x       set_value$x_sign$y1_sign )

        
local k
        
local t_abs
        
local t_sign
        
local bigA
        
local bigAS
        
local bigB
        
local bigBS

        
k       big_div$a$b )
        
t_abs   big_mul$y1_abs$k )
        
t_sign  $y1_sign


        
bigA    $x1_abs
        
bigAS   $x1_sign

        
bigB    $t_abs
        
bigBS   $t_sign
        
if $bigbs == 01
            
bigbs   00
        
else
            
bigbs   01
        
endif

        
if $bigas == $bigbs
            
tmp big_add$biga$bigb )
            
x   setvalue$y_abs$tmp )
            
x   setvalue$y_sign$bigas )
        
else
            
if $biga >= $bigb
                
tmp big_sub$biga$bigb )
                
x   setvalue$y_abs$tmp )
                
x   setvalue$y_sign$bigas )
            
else
                
tmp big_sub$bigb$biga )
                
x   setvalue$y_abs$tmp )
                
x   setvalue$y_sign$bigbs )
            
endif
        
endif
    
endif
    
prompt on
return $g

// long long ModInv(long long a, long long m)
// {
//     long long x_abs, y_abs;
//     int       x_sign, y_sign;

//     long long g = ExtEuclid(a, m,
//                             &x_abs, &x_sign,
//                             &y_abs, &y_sign);

//     if (g != 1)
//         return -1;   /* 不存在逆元 */

//     /* 还原 x,但这是最终一步,允许一次负数 */
//     if (x_sign < 0)
//         x_abs = m - (x_abs % m);

//     return x_abs % m;
// }
ModInv:
    
prompt off
    
local a         getpara
    
local m         getpara
    
local swap_flag getpara


    
local x_abs
    
local y_abs
    
local x_sign
    
local y_sign

    
local g

    
local res
    
local tmp

    
// 检查是否经过数据交易,要求a < m
    
if $a > $m
        
tmp $a
        
a   $m
        
m   $tmp
        
g   setvalue$swap_flag01 )
    
else
        
g   setvalue$swap_flag00 )
    
endif

    
g               call exteuclid$a$m&x_abs&x_sign&y_abs&y_sign )
    
if $g != 01
        
res 00
    
else
        
if $x_sign == 00        // 负数
            
tmp     big_mod$x_abs$m )
            
x_abs   big_sub$m$tmp )
        
endif
    
endif

    
res             big_mod$x_abs$m )
    
prompt on
return $res