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月30日 星期五
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; // 剩下的就是餘數
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
#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
#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
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 .
}
// 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));
}
// 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));
}
// 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
#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
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();
}
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
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:
7. 用 glslViewer 開啟上述檔案, 就可以看到圖線
glslViewer test.frag
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 ).
訂閱:
文章 (Atom)