2018年11月30日 星期五

Kotlin 與 c 相互呼叫的程式碼範例

1. 先編寫一個標頭檔
// bigint.h
#ifndef big_integer_h
#define big_integer_h
#include "gmp.h"
#include "malloc.h"
#include "stdio.h"
char *testgmp(char *str);
void  freegmp(void);
#endif

2. 編寫 c 程式碼, 主要呼叫 gmp 去計算大數, 傳入字串, 計算完答案, 轉成字串再回傳
// bigint.c
#include "bigint.h"
char *hexdigit;
char  *testgmp(char *str) {
    mpz_t z;
    mpz_init(z); 
    mpz_set_str(z, str, 16);
    gmp_printf("Receive: %s, GMP caculate: 0x%Zx * 3: \n", str, z);
    mpz_mul_si(z, z, 3);
    hexdigit = malloc( mpz_sizeinbase(z, 16) * sizeof(char) + 1 );// string buffer = new char[mpz_sizeinbase(z, base) + 1], include EOS
    sprintf(hexdigit, "%s", mpz_get_str(NULL, 16, z)); // string copy
    if (z) mpz_clear(z);   
    return hexdigit;
}
void freegmp(void) { if (hexdigit) free(hexdigit);  }

3.  c 主程式呼叫測試碼
// testbig.c
#include "bigint.h"
int main(){
    printf("return: %s\n", testgmp("123"));
    freegmp();
}

4. 編譯  c 程式, 做成程式庫, 將測試程式與程式庫連結, 看是否成功:
gcc   -c  bigint.c  -o  bigint.o
ar   rcs   libbigint.a   bigint.o
gcc  testbig.c  -L .   -l  bigint  -o  testbig   &&   ./testbig

5. 準備讓 kotlin 來呼叫, 先連接好所需要的相關程式庫:
ln   -sf   /usr/local/lib/libgmp.a   libgmp.a
ln   -sf   /usr/local/include/gmp.h   gmp.h
ln   -sf   libbigint.a   bigint.a

6. 編輯橋接檔案 bigint.def, 將以下內容存檔:
headers =  bigint.h
compilerOpts.linux = -I.
linkerOpts.linux   = -L.  bigint.a libgmp.a

7.用 cinterop 分析上述標頭檔, 產生程式庫:
cinterop   -def   bigint.def   -o  bigint.klib

8. 編寫 Kotlin 程式碼導入程式庫,呼叫由 c 所寫的副程式:
// main.kt
import bigint.*
import kotlinx.cinterop.*
fun main(args: Array) {
  var digit = "123"
  val test  = testgmp(digit.cstr)!!.toKString()
  println("testgmp return $test")
  freegmp();
}

9. 編譯 main.kt 連結程式庫, 執行看是否成功呼叫並回傳答案, 結果應該要與步驟 4 答案一致:
konanc   main.kt   -l  bigint.klib   -o   bigint  &&  ./bigint.kexe

10 後記:
1. 若將 gmp 直接做成(klib), 從 kotlin 呼叫會產生不明錯誤訊息, 只好接間接透過 c 呼叫 GMP.
2. 不能用 g++ 去編譯 c 程式, 否則會產生找不到符號的錯誤訊息
3. Kotlin native compiler (konanc)編譯的速度很慢, 因此在寫 c 程式的階段,就應該用 c 程式碼去測試該程式碼的完整性,最後才整合至 kotlin 程式庫,以節省寶貴時間

2018年11月27日 星期二

長除法求商數及餘數

//pseudo code, 最高位數放在低位置(Most Significant Byte first, big-endian), 比較容易
int    *digit   = number.array ;   // 被除數, 例如  int a[4]  = { 3, 2, 5, 6 } 代表 10 進制  325610
int     length= number.length; // 被除數長度, 例如 10 進制 325610, 長度是 4
number divider = number.divider ; // 除數
number q; // 商數, 最高位數, 對齊被除數
number r;  // 餘數, 最低位數, 對齊除數
if (number(digit)  == divider)      { q = 1; r = 0; } // 如果被除數等於除數
else if (number(digit) < divider) { q = 0; r = number(digit); }// 如果被除數小於除數
else while (length --) {// 其它: 被除數大於除數, 一次處理一個數, 直到數量為 0 才結束
        r.push( *digit ) ; // 將被除數從陣列中取出放入餘數, 累積數值
        if (r  <  divider)  q.push(0) ; // 當累積的數值比除數小, 該位數的商等於 0, 直接推入商數
        else { // 否則當累積的數值大於或等於除數, 計算該位數的商
              int temp = 0;  // 初始為 0, 如果是 10 進位, 最多 9 次就可找到商
               do {
                       temp ++;       // 累加商
                       r  -= divider; // 使用最簡單的減法找商
               } while (!(r  < divider)); // 測試直到找出商, 也就是當餘數小於除數時
               q.push(temp) ; // 該位數的商推入商數, 餘數 r 會保留, 待後續處理
         }
         digit ++; // 被除數, 進展至下一位數
} //迴圈
// 商數 q 最後要把開頭多餘的 0 移除掉, 就是答案

如果只取餘數, 不要商數, 可以簡化流程:
if (number(digit)  == divider)           r = 0; // 如果被除數等於除數
else if (number(digit) < divider)  r = number(digit);// 如果被除數小於除數
else while (length --) {// 其它: 被除數大於除數, 一次處理一個數, 直到數量為 0 才結束
         r.push(*digit); // 將被除數從陣列中取出放入餘數, 累積數值
         while (r >= divider) r -= divider;//當累積數值大於或等於除數時, 直接刪掉除數因子
         digit ++; // 被除數, 進展至下一位數
} //迴圈
return r; // 剩下的就是餘數

2018年11月24日 星期六

用 C++ 運算 ECC, 學習 class 的運作

#include "gmp.h"
#include "stdio.h"
#include "stdarg.h"
typedef unsigned long int ulongint;
class Curve383187{// Curve383187::  y2 = x3 + 229969 * x2 + x  mod p
    private: static const BigInteger p;// divider for modulus number is a prime number, it is a bitInt. p = 2383 - 187
    struct modNumber : BigInteger {// class extend from BigInteger, re-define operator + - * / ^ for modulus operation
        modNumber()                          : BigInteger( ) { }
        modNumber(const BigInteger &num)  : BigInteger(num.z) { }// convert BigInteger to modNumber
        modNumber(const char *s, int b=10): BigInteger(s, b) { }// default base 10 is convenient for ECC
        modNumber operator + (modNumber a) { return add(a).mod(p);              }
        modNumber operator - (modNumber a) { return sub(a).mod(p);              }
        modNumber operator * (modNumber a) { return mul(a).mod(p);              }
        modNumber operator / (modNumber a) { return mul(a.modinv(p)).mod(p); }
        modNumber operator ^ (ulongint  e) { return pwr(e).mod(p);              }
        modNumber operator * (ulongint  e) { return mul(e).mod(p);              }
      };
    modNumber a, b, x, y;
    public :   
    Curve383187(BigInteger key = BigInteger("1")) {// key is a scalar
        a = modNumber("229969");
        b = modNumber("1");
        x = modNumber("5");
        y = modNumber("4759238150142744228328102229734187233490253962521130945928672202662038422584867624507245060283757321006861735839455");
        if( key !=  1 )    scale(modNumber(key));   
     }
    bool isPoint()   { return (y^2) == ((x^3) + a*(x^2) + b*x); }
    void listpoint() {
        if( isPoint() )  printf("Yes\n");
        else             printf("No!\n");
        x.printf("\tx=").toHex();
        y.printf("\ty=").toHex();
     }
    Curve383187 operator + (Curve383187 ca)    {
         auto copy = *this;            // copy of this
         return copy.selfadd(ca);
     }
    Curve383187  operator * (modNumber n)          {
        auto copy = *this;      // copy of this
        return copy.scale(n); // return new object
     }
    Curve383187& selfadd(Curve383187 g)     { // R = P + Q
        auto s  = (y - g.y)/(x - g.x); // slope = (y - Gy)/(x - Gx)
        x = (s^2) - x - g.x - a;       // x = s2 - x - Gx - a
        y = s*(g.x - x) - g.y;             // y = s*(Gx - x) - Gy
        return *this; // return c object
     }  
    Curve383187& scale(modNumber num) {
        int k = 1024;    // number of bits
        if (num ==  0) { printf("Error, scale num is 0"); return *this;  }
        while (k-- > 0)  if (num.testBit(k))  break;// scan to MSB
        auto gx = x; 
        auto gy = y;
        while (k-- > 0){// Double And Add algorithm to scale G(x,y)  
            // Doubler slope = ( 3x2 + 2ax + b)/(2y)            
            auto px = x;                // save x to caculate y  
            auto s  = ((x^2)*3 + a*x*2 + b)/(y*2);   
            x = (s^2) - x*2 - a;         // x = s2 - 2x - a
            y = s*(px - x) - y;         // y = s * (px - x) - py 
            if (num.testBit(k)) {         // Adder slope = (y - Gy)/(x-Gx)
                s = (y - gy)/(x - gx);    // slope = (y - Gy)/(x - Gx)
                x = (s^2) - x - gx - a; // x = s2 - x - Gx - a
                y = s*(gx - x) - gy;      // y = s*(Gx - x) - Gy
              }   
        }
        return *this; // return self object
     }
 };
const BigInteger Curve383187 :: p = (BigInteger("2",10)^383) - BigInteger("187",10); // static member must be init outside

int main(){
    auto keyA  = (BigInteger("9",10)^87) - BigInteger("65",10); // Alice pickup a key
    auto keyB  = (BigInteger("4")^32)    - BigInteger("10",10); // Bob pickup a key
    auto key   = keyA*keyB;         // share key nobody knowns
    auto pubA  = Curve383187(keyA); // Alice send public key to Bob
    auto pubB  = Curve383187(keyB); // Bob send public key to Alice
    auto keyECC= Curve383187(key);  // Caculate the ECC of the secret to double check
    auto Alice = pubB * keyA;       // Alice use self key to decode pubB sent from Bob
    auto Bob   = pubA * keyB;       // Bob use self key to decode pubA sent from Alice  
    keyECC.listpoint();               // should be the same;
    Alice.listpoint() ;                // Alice decode
    Bob.listpoint()   ;                // Bob decode
}
運算結果:
Yes
    x=0x381dcd3a1bdea34760212d276c7d21940d315a4d547af3c520aba80a2ff4e38e0e548dd9d0038c1ce5c3eb36acf6960b
    y=0x3491be994e90f4e9b28352b5c0495c5f395dba99fe2fa870553351be77e363c67392e3c3feb2fed711b5d187c63afbbb
Yes
    x=0x381dcd3a1bdea34760212d276c7d21940d315a4d547af3c520aba80a2ff4e38e0e548dd9d0038c1ce5c3eb36acf6960b
    y=0x3491be994e90f4e9b28352b5c0495c5f395dba99fe2fa870553351be77e363c67392e3c3feb2fed711b5d187c63afbbb
Yes
    x=0x381dcd3a1bdea34760212d276c7d21940d315a4d547af3c520aba80a2ff4e38e0e548dd9d0038c1ce5c3eb36acf6960b
    y=0x3491be994e90f4e9b28352b5c0495c5f395dba99fe2fa870553351be77e363c67392e3c3feb2fed711b5d187c63afbbb

2018年11月21日 星期三

利用 c++ 運算子(operator)包裝 GMP 成為 bigInteger

//bigint.c 
#include "gmp.h"
#include "stdio.h"
#include "stdarg.h"
struct bigInteger{   
    mpz_t z;
    mpz_t m;
    ~bigInteger()                           { mpz_clear(z);                       }
    bigInteger()                             { mpz_init(z) ;                         }   
    bigInteger(const mpz_t& t)                { mpz_init(z) ; mpz_set(z, t);          }   
    bigInteger(const bigInteger& a)        { mpz_init(z) ; mpz_set(z, a.z);        }   
    bigInteger(const char *str, int base=16){ mpz_init(z) ; mpz_set_str(z, str, base);}// default base 16
    bigInteger& operator += (bigInteger a)  { mpz_add(z, z, a.z); return *this; }
    bigInteger  operator +  (bigInteger a)  {
        bigInteger ans;
        mpz_add(ans.z, z, a.z); 
        return ans; // return new bigInteger
     }
    bigInteger& operator ++ ()      { mpz_add_ui(z, z, 1); return *this;    }// ++ prefix
    bigInteger  operator ++ (int)            { // postfix ++                   
            auto ans = bigInteger(z);             // copy of z
            mpz_add_ui(z, z, 1);                     // self inc 1
            return ans;                                      // return oringal copy
     }
    bigInteger& operator -= (bigInteger a)  { mpz_sub(z, z, a.z); return *this;  }
    bigInteger  operator -  (bigInteger a)  {
        bigInteger ans;
        mpz_sub(ans.z, z, a.z); 
        return ans; // return new bigInteger
     }
    bigInteger& operator -- ()        { mpz_sub_ui(z, z, 1); return *this;}// ++ prefix
    bigInteger  operator -- (int)              { // postfix --                   
            auto ans = bigInteger(z);             // copy of z
            mpz_sub_ui(z, z, 1);                     // self dec 1
            return ans;                                     // return oringal copy
     }
    bigInteger& operator *= (bigInteger a)  { mpz_mul(z, z, a.z); return *this;        }
    bigInteger  operator *  (bigInteger a)  {
        bigInteger ans;
        mpz_mul(ans.z, z, a.z);
        return ans; // return new bigInteger
     }
    bigInteger& operator /= (bigInteger a)  { mpz_tdiv_q(z, z, a.z); return *this;    }
    bigInteger  operator /  (bigInteger a)  {
        bigInteger ans;
        mpz_tdiv_q(ans.z, z, a.z);
        return ans; // return new bigInteger
     }
    bigInteger& operator %= (bigInteger a)  { mpz_mod(z, z, a.z); return *this;        }
    bigInteger  operator %  (bigInteger a)  {
        bigInteger ans;
        mpz_mod(ans.z, z, a.z);
        return ans; // return new bigInteger
     }
 /*  -- todo
    bigInteger& operator ^= (bigInteger a)  { mpz_powm(z, z, a.z, m); return *this;    }
    bigInteger  operator ^  (bigInteger a)  {
        bigInteger ans;
        mpz_powm(ans.z, z, a.z, m);
        return ans; // return new bigInteger
     }
*/
    bigInteger& operator ^=(unsigned long int e){ mpz_pow_ui(z, z, e); return *this;}
    bigInteger  operator ^ (unsigned long int e){
        bigInteger ans;
        mpz_pow_ui(ans.z, z, e);
        return ans; // return new bigInteger
     }
    bigInteger& printf(const char *fmt, ...){ // avoid waring for incorrect type
        va_list args;
        va_start(args, fmt);
        vprintf(fmt, args);
        va_end(args);
        return *this; // method chain if necessary
     }   
    bigInteger& toHex() {// using mpz internal memory alloc
        printf("0x%s\n",mpz_get_str(NULL, 16, z)); //or  gmp_printf("0x%Zd\n",z);
        return *this;
     }
    bigInteger& toDec() { // using mpz internal memory alloc,
        printf("%s\n",mpz_get_str(NULL, 10, z)); //or  gmp_printf("%Zd\n",z);
        return *this;
     }
};
// bool bigInteger::staticflag = false; // prevent waring: forbids in-class initialization of non-const static member
int main(){
  auto a = bigInteger("10", 10); // base 10
  auto b = bigInteger("ABC3"); // base 16
  auto c = b ^ 3;
  c.printf("c=").toDec();
  a.printf("a=").toDec();
  b.printf("b=").toDec();
  (a % b).printf("a %% b = ").toDec();
}
編譯並執行 g++ bigint.c -lgmp  && ./a.out
c=85015678987611
a=10
b=43971
a % b = 10


安裝大數運算程式庫 GMP

1. 下載程式庫:
      https://gmplib.org/download/gmp/gmp-6.1.2.tar.lz
2. 安裝解壓縮程式:
     sudo apt-get install lzip
3. 解壓縮:
    tar --lzip -xvf gmp-6.1.2.tar.lz
4. 進入程式庫目錄編譯程式, 並安裝到 linux 系統:
     cd gmp-6.1.2
     ./configure
     make
     sudo make install
     # make check
     備註: 如果編譯時出現錯誤訊息: : No usable M4 in $PATH , 需要事先安裝巨集 m4:
               sudo apt-get install m4
5. 寫一個測試程式:
     // testgmp.c
    #include "gmp.h"
    #include "stdio.h"
    int main(){
           mpz_t t;           // t 是一個容器, 宣告成整數, 使用 mpz_ 的函式運算
           mpz_init(t);     // 初始化 t
           mpz_set_str(t, "123ABCF", 16); // 讓 t = 16 進制整數字串 123ABCF
           gmp_printf("t = % Zx\n", t);// 輸出 16 進制整數字串
           mpz_clear(t);// 釋放 t 的空間
     }
6. 編譯並執行:
     gcc testgmp.c -lgmp && ./a.out
7. 整數運算 mpz, 宣告資料型態 mpz_t , 參考: https://gmplib.org/manual/Integer-Functions.html#Integer-Functions
      + 加法 mpz_add(t, op1, op2) 答案 t, 被加數 op1 , 加數 op2, t = op1 + op2
      - 減法 mpz_sub(t, op1, op2)  答案 t, 被減數 op1 , 減數 op2, t = op1 - op2
      * 乘法 mpz_mul(t, op1, op2) 答案 t, 被乘數 op1 , 乘數 op2, t = op1 * op2     
      / 除法 mpz_cdiv_qr(q, r, t, divider)  商數 q, 餘數 r, 被除數 t, 除數 divider
      / 除法  mpz_cdiv_qr(q, t, divider) 商數 q, 被除數 t, 除數 divider,  q = t / divider
      / 除法 mpz_cdiv_r(r, t, divider) 餘數 r, 被除數 t, 除數 divider, r = t %  divider
      % 餘數 mpz_mod(r, t, divider) 餘數 r, 被除數 t, 除數 divider, r = t %  divider
     除法運算時,針對商及餘數包含小數時, 處理成整數的方式有好幾種方式, 其中 cdiv 前置 c 是 ceil , 而 fdiv 的 f 是 floor, tdiv 的 t 則是 truncate 的意思:
             ceil : 大於的(up towards)最接近整數, 例如 ceil(1.2) = 2, ceil(-1.2) = -1
             floor: 小於的(down towards)最接近整數, 例如 floor(1.2) = 1, floor(-1.2) = -2
             truncate: 無條件捨去成為 0, truncate(1.2) = 1, truncate(-1.2) = -1            
8. 分數運算 mpq, 資料型態 mpq_t , 參考: https://gmplib.org/manual/Rational-Number-Functions.html#Rational-Number-Functions
9. 浮點運算 mpf, 資料型態 mpf_t, 參考: https://gmplib.org/manual/Floating_002dpoint-Functions.html#Floating_002dpoint-Functions

2018年11月20日 星期二

c++ 超簡單的取餘數


// mod.c
#include "stdio.h"
int mod(int a, int b) {
    while (a>=b)    a -= b;    
    return a;
}
int main( ) {
   printf("%d %% %d = %d\n", 10, 3, mod(10,3) );
}
編譯程式並執行 g++ mod.c && ./a.out
10 % 3 = 1
當 小的數字 a 沒有問題, 但數字大時就很慢!

c++ 的 method chain

在 c++ class 宣告中, 如果方法(method)傳回的是 this 指標, 該方法傳回型態就必須宣告成物件指標 *, 之後可以方便利用箭頭符號 -> 繼續串接(chain)該物件的其他方法, 但如果想用句點來串接又不希望複製物件本身, 那就要將該方法的傳回型態宣告成物件別名 &, 最後 return *this, 這樣就能利用句點符號繼續串接呼叫其他方法:
// method chain example
class myObject {
    myObject( ) {
        // constructor
    }
    myObject* self_pointer( ){
         // ...
         return this; // method chain by pointer ->
    }
    myObject& self_object( ){
         // ...
         return *this; // method chain by object  .
    }
    void other_method( ) {
         // other method
    }
}
int main(void ) {
   auto ptr = new myObject( ); // return this is a pointer
   auto obj = myObject( );         // return *this is an object
   ptr -> self_pointer() -> other_method( ) ; // call other method by pointer ->
   obj.self_object().other_method( ) ; // call other method by object .
}

2018年11月19日 星期一

求兩個整數(a,b)的最大公約數 gcd

// Euclidean Algorithm to find gcd(a, b):
// 1. find gcd(b,r) when  a = b⋅q + r, gcd(a, b) = gcd(b, r)
// 2. substitute a with b, b with r to the recusive step
// 3. if a = 0, gcd(a, b) = b or if b = 0, gcd(a, b) = a
int gcd(int a, int b) {
    int q, r;
    while( a && b){
        q = a / b;
        r = a - q * b;
        a = b;
        b = r;
    }
    if( b==0 ) return a;
    if( a==0 ) return b;
}
#include "stdio.h"
int main(){
    printf("GCD=%d\n", gcd(10, 5));
}

計算整數指數的模數

    // modpwr.c
    // to caculate:  ab  mod m =  ab  %  m
    // 1. convert b to binary,  odd: 2k+... + 1, or even:  2k+... +21
    // 2. only when bit in b is 1, it need to be caculated
    // 3. if power of 2,  we can build a lookup table to reduce the caculations - todo
    // 4. Expand the series by modular multiplication rules, combine the result finally:
    //      ab % m = a(2k+... ) % m = a(2k) * a(...) % m = (a(2k) % m) * (a(...) % m) % m
#include "stdio.h"
bool testbit(int x, int k){
    int table[8] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80};
    int q = k / 8;      // number of byte   
    int r = k - q * 8;  // number of bit
    if (q>0) x >>= q << 3;// shift to byte 0, x = x >> q*8
    return x & table[r];// test the mask bit
}
int modpwr(int a, int b, int m){ // ab mod m
    int bits = sizeof(b) * 8 ; // total bits in b
    int ans = 1;
    int n, an;
    for(int i = 0, power2 = 1; i < bits; i++) { // 20 = 1
        if (testbit(b, i))  { // only bit=1 need to be caculated
            n  = power2; // exponent of a = 2i
            an = 1;      // initialize to caculate power series of a => an
            while (n--) an = an*a % m; // an = apower2 % m to prevent overflow
            ans = ans * an % m;  // combine all result
        }
        power2 *= 2; // caculate power series of 2
     }
     return ans;
}
int main() {
    printf("%d \n", modpwr(3,3,6));
}

2018年11月16日 星期五

c++ 實作一個 stack array 可以 push 及 pop

//stack.cc
#include <stdio.h>
#include <stdarg.h>
#include <typeinfo>
template <typename anyType>
struct myArray{
    anyType *array;
    int  length; // 32-bits length is huge enough
    myArray()                {
        if( typeid(anyType) != typeid(int) && typeid(anyType) != typeid(double) ) printf("va_arg(args, type) must be int or double for type\n");       
        array=NULL;
        length =0;   
     }
    ~myArray()               { if( ! array ) delete array; }
    template <typename... anyList>
    myArray* push(anyList... parameters) { __push(sizeof...(parameters), parameters...); }   
    myArray* __push(int argc, ...)       {
        anyType* newptr = new anyType[length + argc]; // new array need more space for parameters
        if( array != NULL ) { // array copy to a new array first
            for(int i = 0; i < length; i++) newptr[i] = array[i];
            delete array; // free useless array
        }
        va_list args;
        va_start(args, argc);      
        while (argc-- > 0) newptr[length++]=va_arg(args, anyType);// add element to tail one by one
        va_end(args);
        array = newptr; // point to new array
        return this;    // method chain if necessary
     }
    anyType pop(){
        if(  length>0 ) length--;  // decrease one element
        return          array[length];  // pop element from last one
     }   
    myArray* printf(const char *fmt, ...){ // avoid waring for incorrect type
        va_list args;
        va_start(args, fmt);
        vprintf(fmt, args);
        va_end(args);
        return this; // method chain if necessary
     }   
    myArray* listall(bool eol = true) { // default End Of Line is true
        for (int i = 0;i < length; i++) {
            if( typeid(anyType) == typeid(int)  )                   printf("%d",array[i]);
            else if( typeid(anyType) == typeid(double)  )  printf("%f",array[i]);               
            if( i < length-1 ) printf(", "); // if not last one, add comma and space
        }
        if (eol) printf("\n"); // default print EOL
        return this; // method chain if necessary
     }
 };
int main(void){
    auto cc = new myArray
<double>;
    cc->push(1.0, 2.0)->push(3.0, 4.0)->push(5.0)->push(6.0, 7.0, 8.0)->listall();
    printf("pop %f\n", cc->pop());
    cc->listall();
}

編譯程式並執行 g++  stack.c   &&  ./a.out
1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 6.000000, 7.000000, 8.000000
pop 8.000000
1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 6.000000, 7.000000
 

2018年11月14日 星期三

c++ 實作輸入不定數量參數(variadic parameter)的方法(method)

c 語言用一些巨集 va_list, va_start,  va_arg, va_end 去實現不定數量參數的函式功能,  參數不定量使用省略符號 ... (連續3個句點)來表示, 同時必須明示或暗示後續輸入參數的數量, 像是常用 printf  就可以透過第一個字串參數去得知後續還有多少參數需要擷取利用. 實際上編譯器(compiler)可以得知輸入參數的數量, 只是不易操作, 對程式設計師而言, 不定數量參數有其方便使用的地方, c++ 利用 parameter pack 像是 sizeof...( ) 運算元計算得知輸入有多少數量的參數,  利用 sizeof...( ) 並將函式包裝起來, 可以實現一個不需參數數量的函式, 同時理解樣版函式(template function)的運作方式:
// variadic.c
#include <stdio.h>
#include <stdarg.h>
void __expricitFunction(size_t argc, ...) {// 明示後續還有 argc 個數量的參數
    va_list args;
    va_start(args, argc);
    for (int i = 0; i < argc; i++)    printf("%d\n", va_arg(args, int) );  // 用整數解釋輸入參數   
    va_end(args);
}
template <typename... List> // 使用逗號分開列表,  通用的型態名稱使用 List 當佔位符
void  variadicParameter(List... parameter){// 這是樣版函式, 由 compiler 決定函式型態
    __expricitFunction(sizeof...(parameter), parameter...);// 呼叫真正的函式
}
int main( ) {
     variadicParameter(1, 2, 3); // 呼叫樣版函式
     variadicParameter(4, 5, 6, 7, 8); // 呼叫另一種樣版函式
     variadicParameter('a', 'b', 'c', 'd', 'e'); // 呼叫另一種樣版函式
}
上述  typename... List 通常稱為 template parameter pack(一串型態表列, 有些人將通用型態取名為 Args, Rest ...等等都是隨性而取名), 作用是呼叫樣版函式時才指定表列型態(type), 名稱只是一個佔位符號(place holder), 而後續的 List... parameter 則稱為 parameter pack  (型態是 List 的表列參數), 用 g++ 編譯並執行:
      g++  variadic.c && ./a.out
這是輸出結果:
1
2
3
4
5
6
7
8
97
98
99
100
101


2018年11月8日 星期四

探索 openGL ES 的 fragment shader 程式

 openGL ES 直接利用 fragment shader 就可以點亮函式圖型, 但要注意的是必須將座標值規一化(normalize)至  0 ~1 區間, 參考資料: https://thebookofshaders.com
1. 下載 glslViewer: https://github.com/patriciogonzalezvivo/glslViewer
2. 下載 glfw:   https://www.glfw.org
3. 先解壓縮 glfw-3.2.1.zip, 編譯 glfw-3.2.1, 並安裝相關程式庫(library) 至 linux 作業系統
unzip  glfw-3.2.1.zip
cd  glfw-3.2.1
mkdir build
cd build
sudo su
apt-get update
apt-get install g++ libx11-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxrandr-dev libxext-dev libxcursor-dev libxinerama-dev libxi-dev cmake
cmake ..
make all
make install
5. 進入 glslViewer 去編譯程式, 完成後執行檔會放在 ./bin 目錄內, 將它加入 PATH 環境變數
make
export PATH=./bin:$PATH
6. 編寫一個程式檔(副檔名務必是  .frag), 畫出中心線與 sin, cos 圖, 將它存檔 test.frag:
uniform vec2 u_resolution;
#define pi2   6.283185308  // 常數 2π
#define fc     0.5                    // 直線 y = 0.5
void main() {   
    vec4  color;
    float thick = 1.0/min(u_resolution.x, u_resolution.y);
    vec2  p = gl_FragCoord.xy/u_resolution;
    float f1 = cos(pi2*p.x)/2.0 + 0.5;             // 函式座標值 y = cos
    float f2 = sin(pi2*p.x)/2.0 + 0.5;              // 函式座標值 y = sin
    if( fc-thick < p.y && p.y< fc+thick )          color = vec4(1.0, 1.0, 1.0, 1.0);// 白色
    else if( f1-thick < p.y && p.y< f1+thick ) color = vec4(0.0, 1.0, 0.0, 1.0);// 綠色
    else if( f2-thick < p.y && p.y< f2+thick ) color = vec4(1.0, 0.0, 0.0, 1.0);// 紅色
    else                                                         color=  vec4(0.0, 0.0, 0.0, 1.0);// 黑色
    gl_FragColor = color; // 指定給 fragment shader
}
7. 用 glslViewer 開啟上述檔案, 就可以看到圖線
glslViewer test.frag

2018年11月5日 星期一

openGL 基礎

參考資料: http://math.hws.edu/graphicsbook/c3/s1.html
openGL 定義了頂點(vertices), 它就是一個在立體世界的一個座標, 其所參照出的圖形稱之為 primitives, 一個 vertice 可以呈現(reder)出一個點, 兩個 vertices 連結成一條線, 3個 vertices 就能建構出一個面, openGL 透過 API 將這些繪圖指令傳給 GPU, 讓他雖然只能繪出畫出原始(primitive)的點(point)·線(line)·三角形面(triangle),但透過數學運算就能建構出其他複雜的圖形來.
一個 vertices 的座標在空間中有3個分量分別是(x,y,z),  vertices 呈現(render) 點(GL_POINT)的尺寸大小可以透過指令改變,直線(LINE)處理方式分成單一線段(GL_LINES),連結片斷線 (GL_STRIP),及封閉線(GL_LINE_LOOP),且線條尺寸也可以加以改變,三角形面的處理方式也有三種,分別是獨立三角形面(GL_TRIANGLES), 三角形聯立面(GL_TRIANGLE_STRIP), 扇狀三角形面(GL_TRIANGLE_FAN)
處理隱藏面:
openGL 不使用繪圖者演算法(painter's algorithm 後畫優先,蓋住背景,繪出前景),而是利用景深測試法(GL_DEPTH_TEST),利用觀景者與物體間的距離,當物件重疊時,距離遠的(smaller depth)將不會呈現出來. openGL 對於每個座標都會賦予一個景深分量 w, 因此一個 vertices 實際上有4個分量(x,y,z,w), openGL 內部有個景深返衝區(depth buffer)負責追蹤每個點的幕前景深作為顯示(render)與否的根據,當需要在幕前呈現時,就會將該點的顏色點放出來並且更新景深返衝區,如果無需呈現,就不做任何事情,這就解決了隱藏面的問題,預設當 z 軸在 -1 與 1 之外的距離都會被隱藏不見, DEPTH_TEST 演算法(前景覆蓋背景)產生了一個新問題:透明的效果無法呈現,這也只能用繪圖者演算法加來已解決. openGL 的座標軸右方是 +x 軸(螢幕左右軸), 上方是 +y 軸(螢幕上下軸), 螢幕前面是 +z 軸(垂直螢幕前後軸), 也就是握半拳把拇指往自己比的右手座標系統(right-handed coordinate system ).