2018年12月13日 星期四

解決 Presaria CQ40 Linux Mint wifi driver

參考文章: https://ubuntuforums.org/showthread.php?t=2181014
      sudo apt-get install firmware-b43-installer
      sudo modprobe -r b43 && sudo modprobe b43

2018年12月5日 星期三

c++ 中的別名 &, 與無名 &&, 和指標 * 等變數的複雜關係

c++ 語法中, 正常變數, 別名 & 變數和無名 &&變數的重要觀念:
1. 等號右邊(來源物件)必定是一個實體物件, 當它沒有具體名稱時稱為無名物件, 際上編譯器視需要會產生唯一識別碼當作名稱, 可以理解成用 && 來代表無名變數
2. 一旦實體化後賦予一個名稱, 就是具名物件, 也就是正常變數.
3. 如果宣告變數前置 & 符號, 代表的是別名, 它並不會產生新物件, 只是賦予一個名稱去共用等號右邊的資源, 可以理解成用 & 來代表別名變數.
4. 當用等於 = 符號指定物件時, 如果等號左邊(目標物件)的變數名稱未曾實體化過, 就會先呼叫建構式去產生物件, 再指定給該變數, 該變數就成為具名物件. 也就是正常變數
5. 如果傳進建構式的來源物件是無名物件(T  &&), 則呼叫移動型建構式, 如果是具名物件(T &)則呼叫複製型建構式
6. 如果用等於 = 符號指定物件時, 如果等號左邊的變數(目標物件)曾經實體化過, 意味著要根據來源物件去更新目標物件的內容
7.如果實體化的目標物件, 用等於 = 符號指定新物件, 則會呼叫等號 = 運算式, 根據來源物件, 如果是無名物件(T &&)則呼叫移動型運算式:  operator =(T &&), 如果是具名物件(T &)則呼叫複製型運算式: operator (T &), 去更新目標物件的內容
#include <stdio.h>
#include <iostream>
int main(){
    int a=3; // 3 是無名整數 &&,  建構新的物件 a = int(3), 之後無名變數 && 自動消滅
    int b=a;// 建構新的物件 b = int(a),  a, b 是正常變數, 但 b 與 a 不同實體
    int &c=b; // c 只是 b 的別名, b 與 c 共用一個實體
    int &&d=std::move(a); //  d 與 a 共用實體, 等同於 int &d = a; 但 d 不會消滅
    printf("\na=%d\n",a);  // 不同於無名的 3
    printf("b=%d\n",b);    //   不同於 a 的 3
    printf("c=%d\n",c);   //     等同於 b 3
    printf("d=%d\n",d);  //   等同於 a 的 3
    d=4;
    c=2;
    printf("\na=%d\n",a); // 4, a 內容跟著改變
    printf("b=%d\n",b); // 2, b 內容跟著改變
    printf("c=%d\n",c);  // 2, c 也變了
    printf("d=%d\n",d);   // 4, d 也變了

多變數指向相同位置時, 意味著該位置被多個變數參照(reference). 取出變數的參考位址要在變數前面加上  & 符號(注意與別命變數之差異: 別名變數放在目標物, 位址擷取則是在來源物), 取出指標的內容值則要在變數前慣上 * 符號.
    int    a;     // a 是一般變數, &a 就是該變數的參考位址
    int*  ptr; //  ptr 是指標變數, *ptr 就是指標的內容, 因此宣告成  int  *ptr; 也無妨
    ptr = &a;// 指標變數必須先初始化, 將 ptr 指向 a 的位置後, *ptr 與 a 的內容相同
如果覺得主角 * 太難理解, 用符號 & 可以宣告 a 的別名(alias) 變數  b(註: 必須用符號 = 同時去初始化)
    int   a;
    int&   b = a;// 也可以宣告成  int   &b=a; 也就是指定 b 是 a 的別名(名稱不同).
一旦理解了指標與別名, 那指標的別名就容易多了, 為了要取出指標物件的主角, 只要在指標前慣上星號 * 就是變數的內容:
#include "stdio.h"
int main() {
   int*   a;   //  a 是指標變數
   int*  &b = a; //  b 也是指標變數, 只不過初始化時為 a 的別名, 不管用 int*&   b = a; 或 int  *&b = a; 結果都相同
   int   c  = 3;      //  c 的內容等於 3
   b  = &c; //   既然 b 是 a 的別名且又是指標變數, 可以把 b 指向 c , 等同把 a 指向 c
   printf("*a = %d\n",  *a);// 因此取出 a 的內容等同取出  b 的內容, 也就是 c 的內容:  3
 }
宣告一個 * 的指標變數, 它就類似一維陣列, 而陣列就是一個固定連續空間, 但宣告陣列與宣告指標變數不同的是: 陣列會分配出記憶空間但指標不會, 而指標變數可以任意定位, 陣列卻不可以.因此兩者用法並不相同, 其實不可混淆.
    int   *ptr;          // 實體位置未指定, 初始化之前都不得使用
    int   dim[ ] = {1,2,3}; // 分配出 3 個整數的空間
    ptr = dim;        // 但 ptr 可以指向相同型態的陣列

至於連續兩個 ** 也就是連續的指標空間(類似儲存指標的陣列)
   int      a[ ]   = {1,2,3}; // a 內容陣列是整數, a 其實是指標, 只是不能重定位
   int      b[ ]   = {4,5,6}; // b 也是整數陣列, b 是指標
   int*  ab[ ]   = { a, b }; // ab 是儲存整數指標的陣列
   int** pab;       // 宣告整數型態的二維指標 pab
   pab = ab;       //  pab 可以指向 ab
上述 int** pab 也可宣告成  int*  *pab甚至 int   **pab, 運算結果都一樣, 當 pab 指向 ab 之後, 實際上 pab[0] 內容就是 a , pab[1] 為 b, 而 **pab 則為 pab[0][0], 因此全部內容展開就是:
   pab[0][0] = 1,  pab[0][1] =2 , pab[0][2] = 3
   pab[1][0] = 4,  pab[1][1] =5.  pab[1][2] = 6
函式的參數如果是以函數型式, 稱為無名函數(annonymous function vaule)傳進來, 就用符號 && 代表無名, 尤其在 move constructor 一定會用到. 最後要注意的是主角與別名 & 甚至無名 && 等變數代表了一個實體物件, 針對類型成員要是用句號 .  取用物件資源, 但 *,  *&, ** 等指標變數必須經過初始化後再用星號 * 轉回物件, 或用中括號取得陣列中的物件, 類型的指標變數要另外用箭號 ->  取用物件成員. 在類型中 *this 是物件主角,  但 this 是物件的指標.

2018年12月3日 星期一

有關 CMakeLists.txt 基本用法

編寫 Makefile 還要考慮到檔案相關性, 何不讓程式自動寫 Makefile 呢? cmake 就是做這種功能的, 簡單的像是編譯 tutor.cpp:
        g++   tutor.cpp   -lGLESv2   -lglfw    -lGLEW   -o   tutor
只要在根目錄用 cmake 的語法編輯檔案 CMakeLists.txt .  先替執行檔取名(例如 tutor), 把要編譯的程式檔加入 add_exetuable(...), 再把需要聯結的程式庫加進 target_link_libraries(...), 例如:
      add_executable(tutor   tutor.cpp)
      target_link_libraries(tutor GLESv2 glfw GLEW)
兩行就能搞定, 通常在專案的根目錄建個子目錄 build, 讓編譯的暫存檔放在裏面, 接著進入子目錄下命令 cmake .. 完成後自動生成 Makefile, 之後跟平常一樣, 只要進入子目錄 build 裏面,輸入 make 就可以編譯程式, 例如:
      [-d build ] || mkdir build
      cd build && cmake ..
      make
移除檔案時只要將專案的目錄 build 整個刪除就清乾淨了. 簡單寫個 Makefile 來支援 cmake 測試一下:
#Makefile to generate CMakeLists.txt
userpath= $(shell pwd)
build   = $(userpath)/build
target  = $(build)/tutor
run: $(target)
    $<

$(target): $(build) tutor.cpp
    cd $<; make
$(build): CMakeLists.txt
    @[ -d $@ ] || mkdir $@
    @[ -f $< ] &&    cd $@ && cmake .. || echo missing $<;echo
CMakeLists.txt: 
    echo "add_executable(tutor tutor.cpp)" > $@
    echo "target_link_libraries(tutor GLESv2 glfw GLEW)" >> $@
glfwlib:
    sudo apt-get install  libglfw3-dev  libgles2-mesa-dev  libglew-dev
tutor: tutor.cpp
    g++ $< -lGLESv2 -lglfw -lGLEW -o $@ && ./$@
clean:
    rm -rf build
     rm tutor
    clear

簡單的 c++ 畫圖程式碼:
// tutor.cpp
#include "stdio.h"
#include "GL/glew.h"
#include "GLFW/glfw3.h"
int    error(int errno, const char *msg){
    glfwTerminate();
    printf("Errir %d:%s",errno, msg);
    return -1;
 }
class  myShader  {  
    private:  
        struct glbuffer   { GLuint bind; int size; };//  dataclass contain 2 members  
        class  methodbind { // buffer inject myShader's method
            private:
                myShader *thispointer; // link with outer class
            public:
                glbuffer  buffer;
                methodbind(myShader  *pointer)       { thispointer = pointer; buffer.size=0; }
                void vec3(const char *name)                 { glBindBuffer(GL_ARRAY_BUFFER, buffer.bind); thispointer -> attribute(name, 3); }
         };
        int  programid, shaderid, bufferno;
        void attribute(const char *name, int size)   {
            int location = glGetAttribLocation(programid, name);
            glEnableVertexAttribArray(location);
            glVertexAttribPointer(location, size, GL_FLOAT, GL_FALSE, 0, 0);
         }      
        void detachshader(int sid)                   { glDetachShader(programid, sid); glDeleteShader(sid) ; }
        void assemble(int &&sid, const char **string){
            glShaderSource (sid, 1, string , NULL);
            glCompileShader(sid); 
            glAttachShader(programid, sid);
            if( shaderid == 0 ) shaderid = sid; // push shader id
            else {    // accumulate upto 2 shaders, ready to link
                glLinkProgram(programid);    // link with the program id
                detachshader(sid);    // detach current sid
                detachshader(shaderid);// pop previous sid and detach
                shaderid=0;            // restart to accumulate sid
            }       
         }  
        #define buffernMAX 100
        glbuffer  shaderbuffer[buffernMAX];      
        glbuffer& addbuffer(GLuint bid, int size)    {
            shaderbuffer[bufferno].bind = bid;
            shaderbuffer[bufferno].size = size;
            return shaderbuffer[bufferno++];              
         }
        auto arraydata(GLfloat *array , int size)    { 
            GLuint bid;
            glGenBuffers(1, &bid);
            glBindBuffer(GL_ARRAY_BUFFER, bid);
            glBufferData(GL_ARRAY_BUFFER, size, array, GL_STATIC_DRAW);
            return addbuffer(bid, size);
         }
    public:  // public area  
        methodbind  point = methodbind(this);// bind with this
        GLFWwindow* context;
        methodbind& pointbuffer(GLfloat *array, int size) {
            if( bufferno < buffernMAX && size > 0 ) point.buffer = arraydata(array, size);
            return point;
         }
        myShader(int width, int height,const char *vcode, const char *fcode)    {
            bufferno = 0;
            shaderid = 0;
            if( glfwInit() )  {
                glfwWindowHint(GLFW_SAMPLES, 4);
                glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
                glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
                context = glfwCreateWindow(width, height, "myShader", NULL, NULL);
                if( context ) {
                    glfwMakeContextCurrent(context);
                    if ( glewInit() == GLEW_OK ) {
                        glfwSetInputMode(context, GLFW_STICKY_KEYS, GL_TRUE);
                        glClearColor(0.0, 0.0, 0.0, 0.0);                     
                        programid = glCreateProgram();
                        assemble(glCreateShader(GL_VERTEX_SHADER)  , &vcode);
                        assemble(glCreateShader(GL_FRAGMENT_SHADER), &fcode);
                    }
                }
             }
         }
        void close() {
            for (int i = 0; i < bufferno; i ++)  glDeleteBuffers(1, &shaderbuffer[i].bind);
            bufferno = 0;
            glDeleteProgram(programid);
            glfwTerminate();
         }
        bool draw()  {
            glClear( GL_COLOR_BUFFER_BIT );
            glUseProgram(programid);
            glDrawArrays(GL_TRIANGLES, 0, point.buffer.size); // draw points
            glfwSwapBuffers(context);
            glfwPollEvents();
            if( glfwGetKey(context, GLFW_KEY_ESCAPE ) == GLFW_PRESS ) return false;
            return ! glfwWindowShouldClose(context);
         }
 };

int main( void ) {
    const char *vcode = R"EOF(// Vertex shader program:
        attribute vec3 position;
        void main() {
            gl_Position = vec4(position, 1.0);
        }
    )EOF";
    const char *fcode= R"EOF(// Fragment shader program:
        void main() {
            gl_FragColor = vec4(1,0,0,1);
        }
    )EOF";  
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f,
    };  
    auto a =  myShader(800, 600, vcode, fcode);
    if( ! a.context )  return error(0, "initGL Error");  
    a.pointbuffer(vertices,sizeof(vertices)).vec3("position");// begin to transfer vertices
    while( a.draw() );
    a.close();
    return 0;
 }

有關 Makefile 一些語法

Makefile 一些基礎摘要:
1. 用井號 # 標記為註解, 例如:
    # 這一行是註解

2. 用等號 = 賦予變數內容,  例如:
    cc = gcc

3. 變數前頭加錢號 $ 隨後將變數用小括號 ( ) 前後括住, 代表引用變數內容, 例如:
    compile = $(cc)   -c

4. $ 括號變數, 甚至能執行 shell 命令, 例如:
    user_path = $(shell pwd)

5. 用冒號 : 描述檔案相關性的規則(rule), 每條規則從開頭起, 至換行字元結束. 冒號前面是目標, 後面是來源, 代表著目標與來源相關, 不同的目標就分別寫個別的規則, 當個別規則的來源很多時, 就用空白字元分開一一列表

6. 針對目標要執行的命令就寫在該目標的下一行, 為了要與規則有所區別, 所有命令列的開頭處都要先插入一個 Tab 字元(不能用空白字元), 不同的命令就隨後寫在不同行

7. 在命令列用的常用內建$變數:
    $@      就是目標
    $<   來源中第一個
    $^       來源全數列表
    $(@D) 目標只剩目錄名
    $(@F) 目標只剩檔名(移除目錄名), 等同 $(notdir $@)
    $(*D)   目標只剩主目錄名(移除副檔名)
    $(*F)   目標只剩主檔名(移除目錄及副檔名)
    $(<D)來源中第一個, 但只剩目錄名
    $(<F)來源中第一個, 但剩檔名(移除目錄名)
    $(^D)  來源全數列表, 但只剩目錄名

 8. 隱性規則代表內含很多條規則, 顯性規則就很明確只有一條規則, 內建通用的隱姓規則
 .c: .o  代表所有副檔名 .c 與個別的 .o 相關

9. 自訂通用隱性規則, 使用 % 代表通通適用
%.cc:   %.oo  代表所有副檔名 .cc 與個別的 .oo 相關

10. 當執行 make 時, 預設會用第 1 條顯性規則執行隨後命令

11. 一個簡單的範例:
#Makefile
main    = tutor.cpp
exefile = tutor
$(exefile): $(main)
    g++ $< -lGLESv2 -lglfw -lGLEW -o $@ && ./$@
libgl:
    sudo apt-get install libglfw3-dev  libglew-dev  libgles2-mesa-dev glew-utils
glinfo:
    glewinfo | grep "GL_VERSION" || make libgl
run:
    ./tutor
clean:
    rm -f $(exefile)   *.o

2018年12月2日 星期日

C++ 類型(class): 建構法(constructor), 解構法(destructor), 動態分配(new), 釋放(delete)

宣告一個新的 c++ 類型時, 如果在類型裏面使用動態記憶空間分配(可以使用 new 運算子或是 malloc( ) 函式傳回指標加以運用),就必須知道 6 種類型的方法及使用時機, 避免造成重複刪除的窘境. 類型裡面的成員分成可記憶的變數與不具記憶的函數(函式的傳回值), 這種在類型裡面的變數稱為狀態(state),而函式則特別稱為方法(method),當類型被引用要產生新物件時第一個被執行的就是類型建構法, 此時裏面變數的狀態都是混沌未明, 透過建構法才能將它初始化,當建構法完成,就會回傳一個該類型的新物件(裏面包含了已初始過的狀態與方法), 因此建構法最後無需再用 return 之類的程式碼. 當物件以指標型式傳進來時就要用箭頭符號引用成員, 如果是物件型式就用句號引用成員, new 回傳的的是物件指標使用 new 所分配的記憶空間必須用 delete 將它釋放. 在方法中使用的 this 就是該物件本身的指標. 指標的存取是一項危險的動作, 一旦指標成為空指標時(null pointer), 不小心產生 Null Pointer Exception(俗稱 NPE), 嚴重的話造成當機.

1. 解構法(destructor),  函式的特徵是 "~類型名稱 ( )", 物件消滅前, 當成員曾經 用new 或 malloc( )分配動態記憶空間時, 要在此要用程式碼 delete 運算子或函式 free( )將它釋放出來.
        ~className ( ) { // Here free all of dynamic resouce
               // delete dynamicResource;
      }

2. 基本建構法(constructor),無傳遞參數(void), 單純初始化內部狀態用,初始類型的陣列也會用此法來建構新物件
      className ( ) {   // Here init all states ...
            // dynamicResource =NULL;
      }  // No need to use return in the end. Do automatically by the compiler

3. 複本建構法(copy constructor), 此函式的特徵是"類型名稱 (物件)",  顧名思義就是要從一份物件來源去產生新類型的物件.例如:
      className (className&  source) { // Here init and copy all state from source
                 // dynamicResource = new className( );
      }   // No need to use return in the end. Do automatically by compiler

4. 物件指定運算(copy assignment), 發動的時機是一個有 = 類型的運算式, 將產生的物件指定給一個變數. 因為運算函式並非建構法, 因此運算函式最後要用 retrun *this 或是 return this; 視傳回的型態(type)而結束. 物件指定運算的特徵是 "類型名稱 operator = (物件)", 在運算函式裏面傳進來的參數必須先判斷非本尊(this) 時, 才需要作指定運算:
      className&  operator = (classNamme  &source) {
              if(   &source != this ) { // this is a pointer, &source is source pointer
                    // Here do copy assignment ...
                    //  dynamicResource = source.dynamicResource; // share dynamicResource
              }
              return *this; // object itself
      }

5. 物件轉移運算(move assignment), 顧名思義就是一個將資產(resource)換手(transfer)的過程,把物件轉交給一個既有物件. 其特徵是 "類型& operator = (類型&& )",其發動的時機也是在一個有 = 類型的運算式, 通常是由編譯器視需求, 先產生一個暫時類型的物件(稱為匿名物件, 也許會透過 copy assignment 產生匿名物件)傳進來, 而受者本身的成員若之前使用 new 分配過動態記憶空間, 就要在此時先釋放,之後將它指向匿名物件的資源,避免記憶體洩漏(memory leak)的情況, 甚至當匿名物件的成員也帶有動態記憶空間時, 也必須最後將該成員設定為 NULL, 當交易完成,匿名物件也會跟著消滅, 這樣才不致在解構時發生重複釋放(double free)記憶體的危機, 當然轉移之前也要先判定來源,避免自我轉移的窘境:
      className& operator = (classNamme&& temp) { // temp is generated by compiler
              if(   &temp != this ) { // this is a pointer, &temp is source pointer
                   // Here do move assignment ...
                   // if(   dynamicResource != NULL ) delete dynamicResource;// free resource
                   // dynamicResource = temp.dynamicResource; // link to resource
                  // temp.dynamicResource = NULL; // temp cut off  the resource
              }
              return *this;  // object itself
      } // temp is destroy automatically by compiler

6. 交易建構法(move constructor), 函式的特徵是 "類型 (類型&& )", 同樣是將資產轉手用的, 只不過是由程式碼主動呼叫(move semantics, 可以透過 std::move(object) 來發動),他是建構法類型其中之一, 編譯器直接將物件轉化成匿名物件交由該建構法運用, 最後另外產生該類型的新物件, 要注意的是物件經轉移後, 其成員狀態, 如果有分配動態記憶空間時, 是否將成員指定為 NULL,視交易建構法程式碼而定,當然也須避免發生重複釋放的危機.
    classNamme(&& temp) { // temp is generated by compiler
         // Here do move assignment ...
        // dynamicResource = temp.dynamicResource; // link to dynamicResource                 
         // temp.dynamicResource = NULL; // Cut off the link of dynamicResource   
     } // temp is destroy automatically by compiler,
7. 範例程式碼:
// hex.c
#include "stdio.h"
#include "iostream"
#include "stdarg.h"
struct hex {
    const char *str;
    int  length;
    char id;
    int  numtable(char c) {  // λ(c)
            if ('0' <= c &&  c <= '9')         return c - '0';
            else if ('a' <=c &&  c <= 'f')  return c - 'a' + 10;
            else if ('A' <=c &&  c <= 'F')  return c - 'A' + 10;
            else return 0;
     };
    char hextable(int i) {  // λ(i)
            char ch;
            if (0 <= i &&  i <= 9)             ch = i + '0';
            else if (10 <= i &&  i <= 15)   ch = i - 10 + 'a';
            else ch = '0';
            return ch;
     };
    ~hex(void)                     { // destructor
        printf("Destroy ~ ");
        if (str) {
            printf("%s ! ",str);
            delete str;
        } else printf("Empty ! ");
        printf("finish\n"); 
     } 
    hex(void)                     { // 1 init constructor
        printf("Init constructor(): \n");
        str = NULL;
        length=0 ;
        id=0;
     }
    hex(hex &a)             {  // 2 copy  constructor     
        printf("Copy Constructor(%s): ", a.str);                
        str = string2hex(a.str); // need a new copy
        printf("finish\n"); 
     }  
    hex(const char *s)        { // 2 copy  constructor
        printf("Copy Constructor(%s)\n",s);
        str = string2hex(s);
     }      
 
    hex(hex &&a)             { // 3 move  constructor
        // printf("Move constructor(%s): ", a.str);
        str = a.str; // just steal
        length = a.length;
        id = a.id;
        a.str = NULL;
        printf("Move Constructor(%s) finish\n",str); 
     }
 
    // Assignment is an operator, need a return type, and must not assign by itself
    hex& operator =(hex &a) { // 4 copy  assignment
        printf("Copy assign = %s ", a.str);
        if( &a != this) {
            str = a.str;// point to the string
            length=0; while (a.str[length++]);// caculate the the length
            id = 0;
            tocstr();    // build a new string
        }
        printf("finish\n");
        return *this; // *this;
     }  
    hex& operator =(char *a){ // 4 copy  assignment
        printf("String assign = %s ", a);
        if( str != a) {
            str = a;    // point to new string
            length=0;    // caculate the the length
            id = 0;
            while (a[length++]);
            tocstr();    // conver to string
        }
        printf("finish\n"); 
        return *this; // *this;
     }
    hex& operator =(hex &&a){ // 5 move  assignment
        printf("Move assign = %s,  self str = %s\n", a.str, str);
        if( &a != this ) { // if pointer of a is not this
            if( str ) delete str;
            str = a.str;
            length = a.length;
            id = a.id;
            a.str = NULL; // to indicate not free a.str when destroy  a
        }
        printf("Move = finish\n"); 
        return *this;
     }

 
    hex& printf(const char *fmt, ...){// to chain printf
        va_list args;
        va_start(args, fmt);
        vprintf(fmt, args);
        va_end(args);
        return *this; // method chain if necessary
     }     
    hex& tohex(){
        if( id !=1 && length > 0) {
            int  k = length ;
            auto number = new char[(k << 1) + 1];
            int  length = 0;
            for (int i = 0; i < k; i++){
                unsigned char c = str[i];
                number[length++] = hextable((c&0xf0) >> 4);//convert to HEX char, MSB
                number[length++] = hextable(c&0xf);      //convert to HEX char, LSB
             }
            number[length++]=0; //EOS
        }
        return *this;
     }
    hex& tocstr(){
        if( id !=2 && length>0 ) {
            int  k = length;
            auto cstring = new char[(k >> 1) + 1];// include EOS, and each HEX number occupy 2 bytes
            length = 0;
            for (int i = 0; i < k; i += 2) cstring[length++] =  (numtable(str[i]) << 4) + numtable(str[i+1]);
            cstring[length++]=0;//EOS
            str = cstring; 
        }
        return *this;
     }
    const char *string2hex(const char *sx){
        id = 1;
        int k =0 ;
        while (sx[k]) k ++;        // break when EOS
       auto number = new char[(k << 1) + 1];// include EOS, and each HEX number occupy 2 bytes
        length = 0;
        for (int i = 0; i < k; i++){
            unsigned char c = sx[i];
            number[length++] = hextable((c&0xf0) >> 4);//convert to HEX char, MSB
            number[length++] = hextable(c&0xf);      //convert to HEX char, LSB
         }
        number[length++]=0; //EOS
        return number;
     }
    const char *hex2string(const char *number) {
        id = 2;
        int  k = 0 ;
        while (number[k]) k ++;     // break when EOS
        auto cstring = new char[(k >> 1) + 1];// include EOS, and each HEX number occupy 2 bytes
        length = 0;
        for (int i = 0; i < k; i += 2) cstring[length++] =  (numtable(number[i]) << 4) + numtable(number[i+1]);
        cstring[length++]=0;//EOS
        return cstring;
     }    
};
int main(){
    printf("\n1.\n");
    hex aaa;

    printf("\n2.\n");
    auto sss = hex("123"); 
    auto strhex = hex("中文也可以"); 

    printf("\n3.\n");
    sss = strhex.str;
 
    printf("\n4.\n");
    auto bbb = std::move(strhex);

    printf("\nfinal\n");
}
執行 g++ hex.c && ./a.out 輸出結果:
1.
Init constructor():

2.
Copy Constructor(123)
Copy Constructor(中文也可以)

3.
Copy Constructor(e4b8ade69687e4b99fe58fafe4bba5)
Move assign = 653462386164653639363837653462393966653538666166653462626135,  self str = 313233
Move = finish
Destroy ~ Empty ! finish

4.
Move Constructor(e4b8ade69687e4b99fe58fafe4bba5) finish

final
Destroy ~ e4b8ade69687e4b99fe58fafe4bba5 ! finish
Destroy ~ Empty ! finish
Destroy ~ 653462386164653639363837653462393966653538666166653462626135 ! finish
Destroy ~ Empty ! finish

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 ).

2018年10月29日 星期一

理解 html 上的 canvas 直接透過 webgl API 繪 3D 圖

<html><head><meta charset="utf-8"/></head><body><script>
// webGL z-axis 稱為 depth, 以螢幕當基準點 0, 螢幕後面是負值, 前面是正, 螢幕左右是 x 軸, 上下是 y 軸, 螢幕中心是 (0, 0, 0)
// webGL 用的座標稱為卷座標 (clip coordinate), 座標值 x, y, z 都介於 -1 與 1 之間的浮點數
// 物件以原點(0,0,0)當參考點, 稱為世界座標 (world coordinate)
// Vertices 是 3D 物件的頂點座標
// 世界座標可以經由矩陣轉換成 webGL 的卷座標: clip_coordinate = Pmatrix * Vmatix * Mmatrix * world_coordinate
//   Mmatrix 是物體移動的矩陣,讓物件可以在世界中自由變換位置甚至旋轉及放大(Translate, scale, rotate)
//   Vmatrix 可以當成觀景矩陣,如果將相機觀景點當成原點,以相對觀點來看,等同將世界座標的零點座標(0,0,0)逆轉換(invere transform)也就是移動世界
//   Pmatrix 是投射矩陣, 將世界座標最後投射到 GL 用的 -1 到 1 之間的繪圖座標點, 同時可以限定在 canvas 的畫布之內
// vertex and index buffer 用於描述模型的幾何結構, mesh 稱為網目(面), 一般會用三角形的碎形面(fragment)建構物體的表面
// ESSL : Embedded System Shader Language 內嵌系統渲染程式語言
// GLSL : OpenGL ES Shader Language 必須包括點及碎面的渲染程式(Vertex and Fragment shader program)
// Shader Language 用來定義 vertices, transformations, materials, lights, camera 的交互作用下產生的一幅影像
// vertex Shader 點渲染作用於 3D 座標點及線上 , 碎面渲染(Fragment Shader)作用於充實物件表面
// drawElements(),drawArrays() 單元或陣列繪畫
// 參考資料:
//      1.  https://webglfundamentals.org/webgl/lessons/webgl-drawing-multiple-things.html
//      2.  https://www.tutorialspoint.com/webgl/
//      3.  https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.1.10.pdf
// Init time:
//      1. create shader program, lookup attribute and uniform location
//      2. create buufers and upload data
//      3. create textures and upload data
// Rander time:
//      1. useProgram
//      2. setup attribute and uniform
//      3. drawArrays or drawElements
// GLSL 所有變數使用前必須先宣告資料型態, 基本資料型態:
// void, float, bool, int, vect2, vect3, vect4, bvect2, bvect3, bvect4, ivect2, ivect3, ivect4, mat2, mat3, mat4, sampler1D, sampler2D, sampler3D, samplerCube, sampler1Dshadow, sampler2Dshadow
// void 是忽視的資料, float 是浮點數資料, bool 是布林數資料, int 是整數資料  (V FBI)
// float 可以使用 E 或 e 在科學記號表示式中作為 10 的指數(10^), 句點分離整數與小數
// bool 僅可以用 true, false 賦值
// int 整數前置可以用 0 代表 8 進制整數, 用 0x 或 0X 代表 16 進制整數, 否則一般就是 10 進制整數
// vect 是浮點數的向量, bvect 是布林數的向量, ivect 是整數值的向量
// 方陣是以行為主(column-major order),也就是向量陣列的方陣,  mat2 是 2x2 方陣, mat3 是 3x3 方陣, mat4 是 4x4 方陣
// 資料整合可以用 struct newType { } 群聚起來成為新的資料型態(newType), 往後資料宣告時就可加以利用
// 資料型態修飾詞(Type Qualifiers) 可以用 const, attribute, uniform, varying, in, out, inout 關鍵字來修飾變數
// 在函式外部宣告的變數, 它是整體區域(Global scope)變數, 僅可以加上 const, attribute, uniform, varying 其中之一的修飾詞
// 函式輸出入參數只能用 in, out, inout, const 其中之一的修飾詞
// const 顧名思義就是不能修改的常數
// attribute 修飾詞用於將變數當成參數傳遞(像是將 vertices 從 OpenGL 一一傳給 vertext shader)時來使用, 對 vertex shader 而言它是常數
// attirbute 只可以用來修飾 float, vect, mat 等浮點數, 它不能用於修飾陣列(array)或是結構(struct)的資料型態.
// uniform 作用於整體區域的宣告, 基本上它是唯讀的, 可以直接透過 API 或是間接經由 OpenGL 初始化.
// varying 提供 vertex shadow 與 fragement shadow 及固定函式之間作為資料交換介面使用
const π = Math.PI;
let tan = Math.tan;
let cos = Math.cos;
let sin = Math.sin;
class matriX4 { // 4x4 方陣
    constructor()   { this.m = [1,0,0,0,  0,1,0,0,  0,0,1,0,  0,0,0,1];  }
    translate(x,y,z){ // bind this with objet I4
        this.m[12] += x;
        this.m[13] += y;
        this.m[14] += z;
        return this;
    }
    scale(sx,sy,sz) { // scale depend on sx,sy,sz)
        this.m[0] *= sx;
        this.m[5] *= sy;
        this.m[10]*= sz;
        return this;
    }
    translatex(s)   { this.m[12] += s; return this;}
    translatey(s)   { this.m[13] += s; return this;}
    translatez(s)   { this.m[14] += s; return this;}
    scalex(s)       { this.m[0]  *= s; return this;}
    scaley(s)       { this.m[5]  *= s; return this;}
    scalez(s)       { this.m[10] *= s; return this;}
    rotatex(θ)      {
        let c = cos(θ);
        let s = sin(θ);
        let m = this.m;
        let mv1 = m[1], mv5 = m[5], mv9 = m[9];
        m[1]  = m[1]*c - m[2]*s;
        m[5]  = m[5]*c - m[6]*s;
        m[9]  = m[9]*c - m[10]*s;
        m[2]  = m[2]*c + mv1*s;
        m[6]  = m[6]*c + mv5*s;
        m[10] = m[10]*c+ mv9*s;
        return this;
    }   
    rotatey(θ)      {
        let c = cos(θ);
        let s = sin(θ);
        let m = this.m;
        let mv0 = m[0], mv4 = m[4], mv8 = m[8];
        m[0]  = c*m[0] + s*m[2];
        m[4]  = c*m[4] + s*m[6];
        m[8]  = c*m[8] + s*m[10];
        m[2]  = c*m[2] - s*mv0;
        m[6]  = c*m[6] - s*mv4;
        m[10] = c*m[10]- s*mv8;
        return this;
    }      
    rotatez(θ)      {
        let c = cos(θ);
        let s = sin(θ);
        let m = this.m;
        let mv0 = m[0], mv4 = m[4], mv8 = m[8];
        m[0] = c*m[0] - s*m[1];
        m[4] = c*m[4] - s*m[5];
        m[8] = c*m[8] - s*m[9];
        m[1] = c*m[1] + s*mv0 ;
        m[5] = c*m[5] + s*mv4 ;
        m[9] = c*m[9] + s*mv8 ;
        return this;
    }
    projector(θ, near, far, width, height) {
        if (near < 1e-4) near = 1e-4;   
        let depth = far - near;
        let factor = tan((π-θ)/2); //  0< θ < π
        this.width = width;
        this.height= height;
        this.m[0]  = factor*height/width;
        this.m[5]  = factor;
        this.m[10] = -(near+far)/depth;// width > height
        this.m[11] = -1;
        this.m[14] = -2*near*far/depth;
        this.m[15] = 0 ;
        return this;
    }
}
class canvas3D{ 
    constructor(width,height,depth) {
        let div    = document.createElement("div");
        let canvas = document.createElement("canvas");   
        this.context = canvas.getContext('webgl'); 
        this.border = 8;
         document.body.style= "background-color: #080808;";
        canvas.style.border = this.border+"px solid";
        document.body.appendChild(div);              
        div.align="center";   
         div.appendChild(canvas);
        canvas.width = width;
        canvas.height= height;
        this.width   = canvas.width;
        this.height  = canvas.height;
        this.depth   = depth;     
        this.shaderGPU= 0;
        this.program = this.context.createProgram();
    }   
    set assemble(str)   {
        this.context.shaderSource(this.shader, str);
        this.context.compileShader(this.shader);
        this.context.attachShader(this.program, this.shader);
        if( ++this.shaderGPU == 2 ) {
            this.context.linkProgram(this.program);
            this.context.useProgram(this.program);
            this.context.enable(this.context.DEPTH_TEST);
            this.context.viewport(0, 0, this.width, this.height);
            console.log("Shader Run: "+this.shaderGPU)
        }
    }
    set vertext(code)   {
        this.shader = this.context.createShader(this.context.VERTEX_SHADER);
        this.assemble = code;
    }
    set fragment(code)  {
        this.shader = this.context.createShader(this.context.FRAGMENT_SHADER);
        this.assemble = code;
    }   
    set scale(name)     { // remember the scalename to be used in projector
        let location = this.context.getUniformLocation(this.program, name);
        this.context.uniform3f(location, this.width, this.height, this.depth);
        this.scalename = name;
    }
    uniform(name)       { // use fat arrow function ()=>{} to bind this with canvas3D object
        let location = this.context.getUniformLocation(this.program, name);
        return { transfer:(m) => { this.context.uniformMatrix4fv(location, false, m); } }
    }
    attribute(name)     { // transfer the attributes one by one
        let location = this.context.getAttribLocation(this.program, name);
        return { transfer:(size) => {
                this.context.vertexAttribPointer(location, size, this.context.FLOAT, false, 0, 0);
                this.context.enableVertexAttribArray(location);
            }
        }
    }
    adddata(array)      {
        let buffer = this.context.createBuffer();
        this.context.bindBuffer(this.context.ARRAY_BUFFER, buffer);
        this.context.bufferData(this.context.ARRAY_BUFFER, new Float32Array(array), this.context.STATIC_DRAW);
        return buffer;
    }
    indexbuffer(array)  {
        this.index = this.context.createBuffer();
        this.context.bindBuffer(this.context.ELEMENT_ARRAY_BUFFER, this.index);
        this.context.bufferData(this.context.ELEMENT_ARRAY_BUFFER, new Uint16Array(array), this.context.STATIC_DRAW);
        this.length = array.length;
        return this;
    }
    pointbuffer(array)  {
        this.buffer = this.adddata(array);
        this.context.bindBuffer(this.context.ARRAY_BUFFER, this.buffer);
        return this;
    }
    colorbuffer(array)  {
        this.color = this.adddata(array);
        this.context.bindBuffer(this.context.ARRAY_BUFFER, this.color);
        return this;
    }  
    get draw()      {
        this.context.bindBuffer(this.context.ELEMENT_ARRAY_BUFFER, this.index);
        this.context.drawElements(this.context.TRIANGLES , this.length , this.context.UNSIGNED_SHORT,0);
    }
    get clear()         { this.context.clear(this.context.COLOR_BUFFER_BIT | this.context.DEPTH_BUFFER_BIT); return this; }       
    projector(θ, near, far) {
        return  new matriX4().projector(θ, near, far, this.width, this.height);
    }   
}
let vertex = [
            -1,-1,-1,
             1,-1,-1,
             1, 1,-1,
            -1, 1,-1,
            -1,-1, 1,
             1,-1, 1,
             1, 1, 1,
            -1, 1, 1,
            -1,-1,-1,
            -1, 1,-1,
            -1, 1, 1,
            -1,-1, 1,
             1,-1,-1,
             1, 1,-1,
             1, 1, 1,
             1,-1, 1,
            -1,-1,-1,
            -1,-1, 1,
             1,-1, 1,
             1,-1,-1,
            -1, 1,-1,
            -1, 1, 1,
             1, 1, 1,
             1, 1,-1,
];
let colors = [
            5,3,7, 5,3,7, 5,3,7, 5,3,7,
            1,1,3, 1,1,3, 1,1,3, 1,1,3,
            0,0,1, 0,0,1, 0,0,1, 0,0,1,
            1,0,0, 1,0,0, 1,0,0, 1,0,0,
            1,1,0, 1,1,0, 1,1,0, 1,1,0,
            0,1,0, 0,1,0, 0,1,0, 0,1,0
]; 
let indices= [
            0,1,2, 0,2,3, 4,5,6, 4,6,7,
            8,9,10, 8,10,11, 12,13,14, 12,14,15,
            16,17,18, 16,18,19, 20,21,22, 20,22,23
];
var my = new canvas3D(500, 500, 500);
my.vertext     = 'attribute vec3 position;attribute vec3 color;'+
                 'uniform mat4 Pmatrix;uniform mat4 Vmatrix;uniform mat4 Mmatrix;'+
                 'varying vec3 vColor;'+
                 'void main(void) { gl_Position  = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);vColor = color;}';
my.fragment    = 'precision mediump float;'+
                 'varying vec3 vColor;'+
                 'void main(void) { gl_FragColor = vec4(vColor, 1.);}';
let abs = (x) => { return x < 0 ? -x:x;}
let z = -10;            // back camera off in z direction to see target
let θ = π/2 ;               // field of view angle  = 90 degree for the camera
let near = 1;               // nearest distance between camera and target in z direction
let far  = near + abs(z);   // farest distance between camera and target in z direction
let Δz = z;                 // camera back off
let Δy = abs(Δz)*tan(θ/2);  // camera pull up
my.indexbuffer(indices);
my.colorbuffer(colors).attribute('color').transfer(3);          // 3 per time
my.pointbuffer(vertex).attribute('position').transfer(3);          // 3 per time
my.uniform("Pmatrix").transfer(my.projector(θ, near, far).m);   // perspective project
my.uniform("Vmatrix").transfer(new matriX4().translate(0,Δy,Δz).rotatex(θ).m);// camera matrix
let mat4GPU = my.uniform("Mmatrix");                            // get GPU mat4 location
let matrix  = new matriX4().rotatex(π/4).rotatey(π/4);          // matrix of the target to use in animatiton
    let Δθ    = π/5;
    let Δs    = 1;
    var animatiton = function() {     
            mat4GPU.transfer(matrix.translatez(Δs).rotatez(Δθ).m);
            my.clear.draw;         
            z += Δs ;
            console.log(abs(z)+":"+abs(Δz))  ; 
            if( abs(z) <= abs(Δz) ) setTimeout(animatiton, 500);
    }
    animatiton();   
</script></body></html>