bignum module wrapper for kokke/tiny-bignum-c
							parent
							
								
									7a29d959ce
								
							
						
					
					
						commit
						a44ba0b8a2
					
				|  | @ -0,0 +1,2 @@ | |||
| This folder contains bn.h and bn.c files | ||||
| from https://github.com/kokke/tiny-bignum-c | ||||
|  | @ -0,0 +1,664 @@ | |||
| /*
 | ||||
| 
 | ||||
| Big number library - arithmetic on multiple-precision unsigned integers. | ||||
| 
 | ||||
| This library is an implementation of arithmetic on arbitrarily large integers. | ||||
| 
 | ||||
| The difference between this and other implementations, is that the data structure | ||||
| has optimal memory utilization (i.e. a 1024 bit integer takes up 128 bytes RAM), | ||||
| and all memory is allocated statically: no dynamic allocation for better or worse. | ||||
| 
 | ||||
| Primary goals are correctness, clarity of code and clean, portable implementation. | ||||
| Secondary goal is a memory footprint small enough to make it suitable for use in | ||||
| embedded applications. | ||||
| 
 | ||||
| 
 | ||||
| The current state is correct functionality and adequate performance. | ||||
| There may well be room for performance-optimizations and improvements. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdbool.h> | ||||
| #include <assert.h> | ||||
| #include "bn.h" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* Functions for shifting number in-place. */ | ||||
| static void _lshift_one_bit(struct bn* a); | ||||
| static void _rshift_one_bit(struct bn* a); | ||||
| static void _lshift_word(struct bn* a, int nwords); | ||||
| static void _rshift_word(struct bn* a, int nwords); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* Public / Exported functions. */ | ||||
| void bignum_init(struct bn* n) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     n->array[i] = 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_from_int(struct bn* n, DTYPE_TMP i) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
| 
 | ||||
|   bignum_init(n); | ||||
| 
 | ||||
|   /* Endianness issue if machine is not little-endian? */ | ||||
| #ifdef WORD_SIZE | ||||
|  #if (WORD_SIZE == 1) | ||||
|   n->array[0] = (i & 0x000000ff); | ||||
|   n->array[1] = (i & 0x0000ff00) >> 8; | ||||
|   n->array[2] = (i & 0x00ff0000) >> 16; | ||||
|   n->array[3] = (i & 0xff000000) >> 24; | ||||
|  #elif (WORD_SIZE == 2) | ||||
|   n->array[0] = (i & 0x0000ffff); | ||||
|   n->array[1] = (i & 0xffff0000) >> 16; | ||||
|  #elif (WORD_SIZE == 4) | ||||
|   n->array[0] = i; | ||||
|   DTYPE_TMP num_32 = 32; | ||||
|   DTYPE_TMP tmp = i >> num_32; /* bit-shift with U64 operands to force 64-bit results */ | ||||
|   n->array[1] = tmp; | ||||
|  #endif | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int bignum_to_int(struct bn* n) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
| 
 | ||||
|   int ret = 0; | ||||
| 
 | ||||
|   /* Endianness issue if machine is not little-endian? */ | ||||
| #if (WORD_SIZE == 1) | ||||
|   ret += n->array[0]; | ||||
|   ret += n->array[1] << 8; | ||||
|   ret += n->array[2] << 16; | ||||
|   ret += n->array[3] << 24;   | ||||
| #elif (WORD_SIZE == 2) | ||||
|   ret += n->array[0]; | ||||
|   ret += n->array[1] << 16; | ||||
| #elif (WORD_SIZE == 4) | ||||
|   ret += n->array[0]; | ||||
| #endif | ||||
| 
 | ||||
|   return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_from_string(struct bn* n, char* str, int nbytes) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
|   require(str, "str is null"); | ||||
|   require(nbytes > 0, "nbytes must be positive"); | ||||
|   require((nbytes & 1) == 0, "string format must be in hex -> equal number of bytes"); | ||||
| 
 | ||||
|   bignum_init(n); | ||||
| 
 | ||||
|   DTYPE tmp;                        /* DTYPE is defined in bn.h - uint{8,16,32,64}_t */ | ||||
|   int i = nbytes - (2 * WORD_SIZE); /* index into string */ | ||||
|   int j = 0;                        /* index into array */ | ||||
| 
 | ||||
|   /* reading last hex-byte "MSB" from string first -> big endian */ | ||||
|   /* MSB ~= most significant byte / block ? :) */ | ||||
|   while (i >= 0) | ||||
|   { | ||||
|     tmp = 0; | ||||
|     sscanf(&str[i], SSCANF_FORMAT_STR, &tmp); | ||||
|     n->array[j] = tmp; | ||||
|     i -= (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) back in the string. */ | ||||
|     j += 1;               /* step one element forward in the array. */ | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_to_string(struct bn* n, char* str, int nbytes) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
|   require(str, "str is null"); | ||||
|   require(nbytes > 0, "nbytes must be positive"); | ||||
|   require((nbytes & 1) == 0, "string format must be in hex -> equal number of bytes"); | ||||
| 
 | ||||
|   int j = BN_ARRAY_SIZE - 1; /* index into array - reading "MSB" first -> big-endian */ | ||||
|   int i = 0;                 /* index into string representation. */ | ||||
| 
 | ||||
|   /* reading last array-element "MSB" first -> big endian */ | ||||
|   while ((j >= 0) && (nbytes > (i + 1))) | ||||
|   { | ||||
|     sprintf(&str[i], SPRINTF_FORMAT_STR, n->array[j]); | ||||
|     i += (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) forward in the string. */ | ||||
|     j -= 1;               /* step one element back in the array. */ | ||||
|   } | ||||
| 
 | ||||
|   /* Count leading zeros: */ | ||||
|   j = 0; | ||||
|   while (str[j] == '0') | ||||
|   { | ||||
|     j += 1; | ||||
|   } | ||||
|   | ||||
|   /* Move string j places ahead, effectively skipping leading zeros */  | ||||
|   for (i = 0; i < (nbytes - j); ++i) | ||||
|   { | ||||
|     str[i] = str[i + j]; | ||||
|   } | ||||
| 
 | ||||
|   /* Zero-terminate string */ | ||||
|   str[i] = 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_dec(struct bn* n) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
| 
 | ||||
|   DTYPE tmp; /* copy of n */ | ||||
|   DTYPE res; | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     tmp = n->array[i]; | ||||
|     res = tmp - 1; | ||||
|     n->array[i] = res; | ||||
| 
 | ||||
|     if (!(res > tmp)) | ||||
|     { | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_inc(struct bn* n) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
| 
 | ||||
|   DTYPE res; | ||||
|   DTYPE_TMP tmp; /* copy of n */ | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     tmp = n->array[i]; | ||||
|     res = tmp + 1; | ||||
|     n->array[i] = res; | ||||
| 
 | ||||
|     if (res > tmp) | ||||
|     { | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_add(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   DTYPE_TMP tmp; | ||||
|   int carry = 0; | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     tmp = (DTYPE_TMP)a->array[i] + b->array[i] + carry; | ||||
|     carry = (tmp > MAX_VAL); | ||||
|     c->array[i] = (tmp & MAX_VAL); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_sub(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   DTYPE_TMP res; | ||||
|   DTYPE_TMP tmp1; | ||||
|   DTYPE_TMP tmp2; | ||||
|   int borrow = 0; | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     tmp1 = (DTYPE_TMP)a->array[i] + (MAX_VAL + 1); /* + number_base */ | ||||
|     tmp2 = (DTYPE_TMP)b->array[i] + borrow;; | ||||
|     res = (tmp1 - tmp2); | ||||
|     c->array[i] = (DTYPE)(res & MAX_VAL); /* "modulo number_base" == "% (number_base - 1)" if number_base is 2^N */ | ||||
|     borrow = (res <= MAX_VAL); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_mul(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   struct bn row; | ||||
|   struct bn tmp; | ||||
|   int i, j; | ||||
| 
 | ||||
|   bignum_init(c); | ||||
| 
 | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     bignum_init(&row); | ||||
| 
 | ||||
|     for (j = 0; j < BN_ARRAY_SIZE; ++j) | ||||
|     { | ||||
|       if (i + j < BN_ARRAY_SIZE) | ||||
|       { | ||||
|         bignum_init(&tmp); | ||||
|         DTYPE_TMP intermediate = ((DTYPE_TMP)a->array[i] * (DTYPE_TMP)b->array[j]); | ||||
|         bignum_from_int(&tmp, intermediate); | ||||
|         _lshift_word(&tmp, i + j); | ||||
|         bignum_add(&tmp, &row, &row); | ||||
|       } | ||||
|     } | ||||
|     bignum_add(c, &row, c); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_div(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   struct bn current; | ||||
|   struct bn denom; | ||||
|   struct bn tmp; | ||||
| 
 | ||||
|   bignum_from_int(¤t, 1);               // int current = 1;
 | ||||
|   bignum_assign(&denom, b);                   // denom = b
 | ||||
|   bignum_assign(&tmp, a);                     // tmp   = a
 | ||||
| 
 | ||||
|   const DTYPE_TMP half_max = 1 + (DTYPE_TMP)(MAX_VAL / 2); | ||||
|   bool overflow = false; | ||||
|   while (bignum_cmp(&denom, a) != LARGER)     // while (denom <= a) {
 | ||||
|   { | ||||
|     if (denom.array[BN_ARRAY_SIZE - 1] >= half_max) | ||||
|     { | ||||
|       overflow = true; | ||||
|       break; | ||||
|     } | ||||
|     _lshift_one_bit(¤t);                //   current <<= 1;
 | ||||
|     _lshift_one_bit(&denom);                  //   denom <<= 1;
 | ||||
|   } | ||||
|   if (!overflow) | ||||
|   { | ||||
|     _rshift_one_bit(&denom);                  // denom >>= 1;
 | ||||
|     _rshift_one_bit(¤t);                // current >>= 1;
 | ||||
|   } | ||||
|   bignum_init(c);                             // int answer = 0;
 | ||||
| 
 | ||||
|   while (!bignum_is_zero(¤t))           // while (current != 0)
 | ||||
|   { | ||||
|     if (bignum_cmp(&tmp, &denom) != SMALLER)  //   if (dividend >= denom)
 | ||||
|     { | ||||
|       bignum_sub(&tmp, &denom, &tmp);         //     dividend -= denom;
 | ||||
|       bignum_or(c, ¤t, c);              //     answer |= current;
 | ||||
|     } | ||||
|     _rshift_one_bit(¤t);                //   current >>= 1;
 | ||||
|     _rshift_one_bit(&denom);                  //   denom >>= 1;
 | ||||
|   }                                           // return answer;
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_lshift(struct bn* a, struct bn* b, int nbits) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(nbits >= 0, "no negative shifts"); | ||||
| 
 | ||||
|   bignum_assign(b, a); | ||||
|   /* Handle shift in multiples of word-size */ | ||||
|   const int nbits_pr_word = (WORD_SIZE * 8); | ||||
|   int nwords = nbits / nbits_pr_word; | ||||
|   if (nwords != 0) | ||||
|   { | ||||
|     _lshift_word(b, nwords); | ||||
|     nbits -= (nwords * nbits_pr_word); | ||||
|   } | ||||
| 
 | ||||
|   if (nbits != 0) | ||||
|   { | ||||
|     int i; | ||||
|     for (i = (BN_ARRAY_SIZE - 1); i > 0; --i) | ||||
|     { | ||||
|       b->array[i] = (b->array[i] << nbits) | (b->array[i - 1] >> ((8 * WORD_SIZE) - nbits)); | ||||
|     } | ||||
|     b->array[i] <<= nbits; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_rshift(struct bn* a, struct bn* b, int nbits) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(nbits >= 0, "no negative shifts"); | ||||
|    | ||||
|   bignum_assign(b, a); | ||||
|   /* Handle shift in multiples of word-size */ | ||||
|   const int nbits_pr_word = (WORD_SIZE * 8); | ||||
|   int nwords = nbits / nbits_pr_word; | ||||
|   if (nwords != 0) | ||||
|   { | ||||
|     _rshift_word(b, nwords); | ||||
|     nbits -= (nwords * nbits_pr_word); | ||||
|   } | ||||
| 
 | ||||
|   if (nbits != 0) | ||||
|   { | ||||
|     int i; | ||||
|     for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i) | ||||
|     { | ||||
|       b->array[i] = (b->array[i] >> nbits) | (b->array[i + 1] << ((8 * WORD_SIZE) - nbits)); | ||||
|     } | ||||
|     b->array[i] >>= nbits; | ||||
|   } | ||||
|    | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_mod(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   /*
 | ||||
|     Take divmod and throw away div part | ||||
|   */ | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   struct bn tmp; | ||||
| 
 | ||||
|   bignum_divmod(a,b,&tmp,c); | ||||
| } | ||||
| 
 | ||||
| void bignum_divmod(struct bn* a, struct bn* b, struct bn* c, struct bn* d) | ||||
| { | ||||
|   /*
 | ||||
|     Puts a%b in d | ||||
|     and a/b in c | ||||
| 
 | ||||
|     mod(a,b) = a - ((a / b) * b) | ||||
| 
 | ||||
|     example: | ||||
|       mod(8, 3) = 8 - ((8 / 3) * 3) = 2 | ||||
|   */ | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   struct bn tmp; | ||||
| 
 | ||||
|   /* c = (a / b) */ | ||||
|   bignum_div(a, b, c); | ||||
| 
 | ||||
|   /* tmp = (c * b) */ | ||||
|   bignum_mul(c, b, &tmp); | ||||
| 
 | ||||
|   /* c = a - tmp */ | ||||
|   bignum_sub(a, &tmp, d); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_and(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     c->array[i] = (a->array[i] & b->array[i]); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_or(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     c->array[i] = (a->array[i] | b->array[i]); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_xor(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     c->array[i] = (a->array[i] ^ b->array[i]); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int bignum_cmp(struct bn* a, struct bn* b) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
| 
 | ||||
|   int i = BN_ARRAY_SIZE; | ||||
|   do | ||||
|   { | ||||
|     i -= 1; /* Decrement first, to start with last array element */ | ||||
|     if (a->array[i] > b->array[i]) | ||||
|     { | ||||
|       return LARGER; | ||||
|     } | ||||
|     else if (a->array[i] < b->array[i]) | ||||
|     { | ||||
|       return SMALLER; | ||||
|     } | ||||
|   } | ||||
|   while (i != 0); | ||||
| 
 | ||||
|   return EQUAL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int bignum_is_zero(struct bn* n) | ||||
| { | ||||
|   require(n, "n is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     if (n->array[i]) | ||||
|     { | ||||
|       return 0; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_pow(struct bn* a, struct bn* b, struct bn* c) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
|   require(c, "c is null"); | ||||
| 
 | ||||
|   struct bn tmp; | ||||
| 
 | ||||
|   bignum_init(c); | ||||
| 
 | ||||
|   if (bignum_cmp(b, c) == EQUAL) | ||||
|   { | ||||
|     /* Return 1 when exponent is 0 -- n^0 = 1 */ | ||||
|     bignum_inc(c); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     struct bn bcopy; | ||||
|     bignum_assign(&bcopy, b); | ||||
| 
 | ||||
|     /* Copy a -> tmp */ | ||||
|     bignum_assign(&tmp, a); | ||||
| 
 | ||||
|     bignum_dec(&bcopy); | ||||
|   | ||||
|     /* Begin summing products: */ | ||||
|     while (!bignum_is_zero(&bcopy)) | ||||
|     { | ||||
| 
 | ||||
|       /* c = tmp * tmp */ | ||||
|       bignum_mul(&tmp, a, c); | ||||
|       /* Decrement b by one */ | ||||
|       bignum_dec(&bcopy); | ||||
| 
 | ||||
|       bignum_assign(&tmp, c); | ||||
|     } | ||||
| 
 | ||||
|     /* c = tmp */ | ||||
|     bignum_assign(c, &tmp); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void bignum_isqrt(struct bn *a, struct bn* b) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(b, "b is null"); | ||||
| 
 | ||||
|   struct bn low, high, mid, tmp; | ||||
| 
 | ||||
|   bignum_init(&low); | ||||
|   bignum_assign(&high, a); | ||||
|   bignum_rshift(&high, &mid, 1); | ||||
|   bignum_inc(&mid); | ||||
| 
 | ||||
|   while (bignum_cmp(&high, &low) > 0)  | ||||
|   { | ||||
|     bignum_mul(&mid, &mid, &tmp); | ||||
|     if (bignum_cmp(&tmp, a) > 0)  | ||||
|     { | ||||
|       bignum_assign(&high, &mid); | ||||
|       bignum_dec(&high); | ||||
|     } | ||||
|     else  | ||||
|     { | ||||
|       bignum_assign(&low, &mid); | ||||
|     } | ||||
|     bignum_sub(&high,&low,&mid); | ||||
|     _rshift_one_bit(&mid); | ||||
|     bignum_add(&low,&mid,&mid); | ||||
|     bignum_inc(&mid); | ||||
|   } | ||||
|   bignum_assign(b,&low); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void bignum_assign(struct bn* dst, struct bn* src) | ||||
| { | ||||
|   require(dst, "dst is null"); | ||||
|   require(src, "src is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     dst->array[i] = src->array[i]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Private / Static functions. */ | ||||
| static void _rshift_word(struct bn* a, int nwords) | ||||
| { | ||||
|   /* Naive method: */ | ||||
|   require(a, "a is null"); | ||||
|   require(nwords >= 0, "no negative shifts"); | ||||
| 
 | ||||
|   int i; | ||||
|   if (nwords >= BN_ARRAY_SIZE) | ||||
|   { | ||||
|     for (i = 0; i < BN_ARRAY_SIZE; ++i) | ||||
|     { | ||||
|       a->array[i] = 0; | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   for (i = 0; i < BN_ARRAY_SIZE - nwords; ++i) | ||||
|   { | ||||
|     a->array[i] = a->array[i + nwords]; | ||||
|   } | ||||
|   for (; i < BN_ARRAY_SIZE; ++i) | ||||
|   { | ||||
|     a->array[i] = 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void _lshift_word(struct bn* a, int nwords) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
|   require(nwords >= 0, "no negative shifts"); | ||||
| 
 | ||||
|   int i; | ||||
|   /* Shift whole words */ | ||||
|   for (i = (BN_ARRAY_SIZE - 1); i >= nwords; --i) | ||||
|   { | ||||
|     a->array[i] = a->array[i - nwords]; | ||||
|   } | ||||
|   /* Zero pad shifted words. */ | ||||
|   for (; i >= 0; --i) | ||||
|   { | ||||
|     a->array[i] = 0; | ||||
|   }   | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void _lshift_one_bit(struct bn* a) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = (BN_ARRAY_SIZE - 1); i > 0; --i) | ||||
|   { | ||||
|     a->array[i] = (a->array[i] << 1) | (a->array[i - 1] >> ((8 * WORD_SIZE) - 1)); | ||||
|   } | ||||
|   a->array[0] <<= 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void _rshift_one_bit(struct bn* a) | ||||
| { | ||||
|   require(a, "a is null"); | ||||
| 
 | ||||
|   int i; | ||||
|   for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i) | ||||
|   { | ||||
|     a->array[i] = (a->array[i] >> 1) | (a->array[i + 1] << ((8 * WORD_SIZE) - 1)); | ||||
|   } | ||||
|   a->array[BN_ARRAY_SIZE - 1] >>= 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,123 @@ | |||
| #ifndef __BIGNUM_H__ | ||||
| #define __BIGNUM_H__ | ||||
| /*
 | ||||
| 
 | ||||
| Big number library - arithmetic on multiple-precision unsigned integers. | ||||
| 
 | ||||
| This library is an implementation of arithmetic on arbitrarily large integers. | ||||
| 
 | ||||
| The difference between this and other implementations, is that the data structure | ||||
| has optimal memory utilization (i.e. a 1024 bit integer takes up 128 bytes RAM), | ||||
| and all memory is allocated statically: no dynamic allocation for better or worse. | ||||
| 
 | ||||
| Primary goals are correctness, clarity of code and clean, portable implementation. | ||||
| Secondary goal is a memory footprint small enough to make it suitable for use in | ||||
| embedded applications. | ||||
| 
 | ||||
| 
 | ||||
| The current state is correct functionality and adequate performance. | ||||
| There may well be room for performance-optimizations and improvements. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| 
 | ||||
| /* This macro defines the word size in bytes of the array that constitues the big-number data structure. */ | ||||
| #ifndef WORD_SIZE | ||||
|   #define WORD_SIZE 4 | ||||
| #endif | ||||
| 
 | ||||
| /* Size of big-numbers in bytes */ | ||||
| #define BN_ARRAY_SIZE    (128 / WORD_SIZE) | ||||
| 
 | ||||
| 
 | ||||
| /* Here comes the compile-time specialization for how large the underlying array size should be. */ | ||||
| /* The choices are 1, 2 and 4 bytes in size with uint32, uint64 for WORD_SIZE==4, as temporary. */ | ||||
| #ifndef WORD_SIZE | ||||
|   #error Must define WORD_SIZE to be 1, 2, 4 | ||||
| #elif (WORD_SIZE == 1) | ||||
|   /* Data type of array in structure */ | ||||
|   #define DTYPE                    uint8_t | ||||
|   /* bitmask for getting MSB */ | ||||
|   #define DTYPE_MSB                ((DTYPE_TMP)(0x80)) | ||||
|   /* Data-type larger than DTYPE, for holding intermediate results of calculations */ | ||||
|   #define DTYPE_TMP                uint32_t | ||||
|   /* sprintf format string */ | ||||
|   #define SPRINTF_FORMAT_STR       "%.02x" | ||||
|   #define SSCANF_FORMAT_STR        "%2hhx" | ||||
|   /* Max value of integer type */ | ||||
|   #define MAX_VAL                  ((DTYPE_TMP)0xFF) | ||||
| #elif (WORD_SIZE == 2) | ||||
|   #define DTYPE                    uint16_t | ||||
|   #define DTYPE_TMP                uint32_t | ||||
|   #define DTYPE_MSB                ((DTYPE_TMP)(0x8000)) | ||||
|   #define SPRINTF_FORMAT_STR       "%.04x" | ||||
|   #define SSCANF_FORMAT_STR        "%4hx" | ||||
|   #define MAX_VAL                  ((DTYPE_TMP)0xFFFF) | ||||
| #elif (WORD_SIZE == 4) | ||||
|   #define DTYPE                    uint32_t | ||||
|   #define DTYPE_TMP                uint64_t | ||||
|   #define DTYPE_MSB                ((DTYPE_TMP)(0x80000000)) | ||||
|   #define SPRINTF_FORMAT_STR       "%.08x" | ||||
|   #define SSCANF_FORMAT_STR        "%8x" | ||||
|   #define MAX_VAL                  ((DTYPE_TMP)0xFFFFFFFF) | ||||
| #endif | ||||
| #ifndef DTYPE | ||||
|   #error DTYPE must be defined to uint8_t, uint16_t uint32_t or whatever | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| /* Custom assert macro - easy to disable */ | ||||
| #define require(p, msg) assert(p && #msg) | ||||
| 
 | ||||
| 
 | ||||
| /* Data-holding structure: array of DTYPEs */ | ||||
| struct bn | ||||
| { | ||||
|   DTYPE array[BN_ARRAY_SIZE]; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* Tokens returned by bignum_cmp() for value comparison */ | ||||
| enum { SMALLER = -1, EQUAL = 0, LARGER = 1 }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* Initialization functions: */ | ||||
| void bignum_init(struct bn* n); | ||||
| void bignum_from_int(struct bn* n, DTYPE_TMP i); | ||||
| int  bignum_to_int(struct bn* n); | ||||
| void bignum_from_string(struct bn* n, char* str, int nbytes); | ||||
| void bignum_to_string(struct bn* n, char* str, int maxsize); | ||||
| 
 | ||||
| /* Basic arithmetic operations: */ | ||||
| void bignum_add(struct bn* a, struct bn* b, struct bn* c); /* c = a + b */ | ||||
| void bignum_sub(struct bn* a, struct bn* b, struct bn* c); /* c = a - b */ | ||||
| void bignum_mul(struct bn* a, struct bn* b, struct bn* c); /* c = a * b */ | ||||
| void bignum_div(struct bn* a, struct bn* b, struct bn* c); /* c = a / b */ | ||||
| void bignum_mod(struct bn* a, struct bn* b, struct bn* c); /* c = a % b */ | ||||
| void bignum_divmod(struct bn* a, struct bn* b, struct bn* c, struct bn* d); /* c = a/b, d = a%b */ | ||||
| 
 | ||||
| /* Bitwise operations: */ | ||||
| void bignum_and(struct bn* a, struct bn* b, struct bn* c); /* c = a & b */ | ||||
| void bignum_or(struct bn* a, struct bn* b, struct bn* c);  /* c = a | b */ | ||||
| void bignum_xor(struct bn* a, struct bn* b, struct bn* c); /* c = a ^ b */ | ||||
| void bignum_lshift(struct bn* a, struct bn* b, int nbits); /* b = a << nbits */ | ||||
| void bignum_rshift(struct bn* a, struct bn* b, int nbits); /* b = a >> nbits */ | ||||
| 
 | ||||
| /* Special operators and comparison */ | ||||
| int  bignum_cmp(struct bn* a, struct bn* b);               /* Compare: returns LARGER, EQUAL or SMALLER */ | ||||
| int  bignum_is_zero(struct bn* n);                         /* For comparison with zero */ | ||||
| void bignum_inc(struct bn* n);                             /* Increment: add one to n */ | ||||
| void bignum_dec(struct bn* n);                             /* Decrement: subtract one from n */ | ||||
| void bignum_pow(struct bn* a, struct bn* b, struct bn* c); /* Calculate a^b -- e.g. 2^10 => 1024 */ | ||||
| void bignum_isqrt(struct bn* a, struct bn* b);             /* Integer square root -- e.g. isqrt(5) => 2*/ | ||||
| void bignum_assign(struct bn* dst, struct bn* src);        /* Copy src into dst -- dst := src */ | ||||
| 
 | ||||
| 
 | ||||
| #endif /* #ifndef __BIGNUM_H__ */ | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,195 @@ | |||
| module bignum | ||||
| 
 | ||||
| // Wrapper for https://github.com/kokke/tiny-bignum-c
 | ||||
| 
 | ||||
| #flag -I @VROOT/thirdparty/bignum | ||||
| #flag @VROOT/thirdparty/bignum/bn.o | ||||
| #include "bn.h" | ||||
| 
 | ||||
| const ( | ||||
| 	STRING_BUFFER_SIZE = 8192 | ||||
| ) | ||||
| 
 | ||||
| pub struct Number { | ||||
| 	array [32]u32 | ||||
| } | ||||
| 
 | ||||
| fn C.bignum_init( n &Number ) | ||||
| fn C.bignum_from_int( n &Number, i u64 ) | ||||
| fn C.bignum_to_int( n &Number ) int | ||||
| fn C.bignum_from_string( n &Number, s byteptr, nbytes int) | ||||
| fn C.bignum_to_string( n &Number, s byteptr, maxsize int) | ||||
| 
 | ||||
| fn C.bignum_add( a &Number, b &Number, c &Number) // c = a + b
 | ||||
| fn C.bignum_sub( a &Number, b &Number, c &Number) // c = a - b
 | ||||
| fn C.bignum_mul( a &Number, b &Number, c &Number) // c = a * b
 | ||||
| fn C.bignum_div( a &Number, b &Number, c &Number) // c = a / b
 | ||||
| fn C.bignum_mod( a &Number, b &Number, c &Number) // c = a % b
 | ||||
| fn C.bignum_divmod( a &Number, b &Number, c &Number, d &Number)  // c = a/b d=a%b
 | ||||
| 
 | ||||
| fn C.bignum_and( a &Number, b &Number, c &Number) // c = a & b
 | ||||
| fn C.bignum_or( a &Number, b &Number, c &Number)  // c = a | b
 | ||||
| fn C.bignum_xor( a &Number, b &Number, c &Number) // c = a xor b
 | ||||
| fn C.bignum_lshift( a &Number, b &Number, nbits int) // b = a << nbits
 | ||||
| fn C.bignum_rshift( a &Number, b &Number, nbits int) // b = a >> nbits
 | ||||
| 
 | ||||
| fn C.bignum_cmp( a &Number, b &Number) int  | ||||
| fn C.bignum_is_zero( a &Number) int | ||||
| fn C.bignum_inc(n &Number) | ||||
| fn C.bignum_dec(n &Number) | ||||
| fn C.bignum_pow( a &Number, b &Number, c &Number) // c = a ^ b
 | ||||
| fn C.bignum_isqrt( a &Number, b &Number) // b = integer_square_root_of(a)
 | ||||
| fn C.bignum_assign( dst &Number, src &Number) // copy src number to dst number
 | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////
 | ||||
| // conversion actions to/from big numbers:
 | ||||
| pub fn new_bignum() Number { | ||||
| 	return Number{} | ||||
| } | ||||
| pub fn from_int(i int) Number { | ||||
| 	n := Number{} | ||||
| 	C.bignum_from_int( &n, i) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| pub fn from_u64(u u64) Number { | ||||
| 	n := Number{} | ||||
| 	C.bignum_from_int( &n, u) | ||||
| 	return n | ||||
| } | ||||
| pub fn from_string(s string) Number { | ||||
| 	n := Number{} | ||||
| 	C.bignum_from_string(&n, s.str, s.len) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| pub fn (n Number) int() int { | ||||
| 	r := C.bignum_to_int(&n) | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| pub fn (n Number) str() string { | ||||
| 	// TODO: return a decimal representation of the bignumber n.
 | ||||
| 	// A decimal representation will be easier to use in the repl
 | ||||
| 	// but will be slower to calculate. Also, it is not implemented
 | ||||
| 	// in the bn library.
 | ||||
| 	return 'Number (in hex): ' + n.hexstr() | ||||
| } | ||||
| 
 | ||||
| pub fn (n Number) hexstr() string { | ||||
| 	mut buf := [STRING_BUFFER_SIZE]byte | ||||
| 	C.bignum_to_string( &n, buf, STRING_BUFFER_SIZE) | ||||
| 	// NB: bignum_to_string , returns the HEXADECIMAL representation of the bignum n
 | ||||
| 	s := tos_clone( buf ) | ||||
| 	if s.len == 0 { return '0' } | ||||
| 	return s | ||||
| } | ||||
| ////////////////////////////////////////////////////////////
 | ||||
| // overloaded ops for the numbers:
 | ||||
| pub fn (a Number) + (b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_add(&a, &b, &c) | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| pub fn (a Number) - (b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_sub(&a, &b, &c) | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| pub fn (a Number) * (b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_mul(&a, &b, &c) | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| pub fn (a Number) / (b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_div(&a, &b, &c) | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| pub fn (a Number) % (b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_mod(&a, &b, &c) | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| pub fn divmod( a &Number, b &Number, c &Number) Number { | ||||
| 	d := Number{} | ||||
| 	C.bignum_divmod( a, b, c, &d) | ||||
| 	return d | ||||
| } | ||||
| ////////////////////////////////////////////////////////////
 | ||||
| pub fn cmp(a Number, b Number) int { | ||||
| 	return C.bignum_cmp(&a,&b) | ||||
| } | ||||
| pub fn (a Number) is_zero() bool { | ||||
| 	return int(C.bignum_is_zero(&a)) != 0 | ||||
| } | ||||
| pub fn (a mut Number) inc() { | ||||
| 	C.bignum_inc(a) | ||||
| } | ||||
| pub fn (a mut Number) dec() { | ||||
| 	C.bignum_dec(a) | ||||
| } | ||||
| pub fn pow(a Number, b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_pow(&a,&b,&c) | ||||
| 	return c | ||||
| } | ||||
| pub fn (a Number) isqrt() Number { | ||||
| 	b := Number{} | ||||
| 	C.bignum_isqrt(&a,&b) | ||||
| 	return b | ||||
| } | ||||
| ////////////////////////////////////////////////////////////
 | ||||
| pub fn b_and(a Number, b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_and(&a,&b,&c) | ||||
| 	return c | ||||
| } | ||||
| pub fn b_or(a Number, b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_or(&a,&b,&c) | ||||
| 	return c | ||||
| } | ||||
| pub fn b_xor(a Number, b Number) Number { | ||||
| 	c := Number{} | ||||
| 	C.bignum_xor(&a,&b,&c) | ||||
| 	return c | ||||
| } | ||||
| pub fn (a Number) lshift(nbits int) Number { | ||||
| 	b := Number{} | ||||
| 	C.bignum_lshift(&a,&b,nbits) | ||||
| 	return b | ||||
| } | ||||
| pub fn (a Number) rshift(nbits int) Number { | ||||
| 	b := Number{} | ||||
| 	C.bignum_rshift(&a,&b,nbits) | ||||
| 	return b | ||||
| } | ||||
| pub fn (a Number) clone() Number { | ||||
| 	b := Number{} | ||||
| 	C.bignum_assign(&b,&a) | ||||
| 	return b | ||||
| } | ||||
| ////////////////////////////////////////////////////////////
 | ||||
| pub fn factorial(nn bignum.Number) bignum.Number { | ||||
| 	mut n := nn.clone() | ||||
| 	mut a := nn.clone() | ||||
| 	n.dec() | ||||
| 	mut i:=1 | ||||
| 	for !n.is_zero() { | ||||
| 		res := a * n | ||||
| 		n.dec() | ||||
| 		a = res | ||||
| 		i++ | ||||
| 	} | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| pub fn fact(n int) bignum.Number { | ||||
| 	return factorial( bignum.from_int(n) ) | ||||
| } | ||||
|  | @ -0,0 +1,86 @@ | |||
| import bignum | ||||
| 
 | ||||
| fn test_new_bignum(){ | ||||
| 	n := bignum.new_bignum() | ||||
| 	assert sizeof( bignum.Number ) == 128 | ||||
| 	assert n.hexstr() == '0' | ||||
| } | ||||
| 
 | ||||
| fn test_from_int(){ | ||||
| 	assert bignum.from_int(255).hexstr() == 'ff' | ||||
| 	assert bignum.from_int(127).hexstr() == '7f' | ||||
| 	assert bignum.from_int(1024).hexstr() == '400' | ||||
| 	assert bignum.from_int(2147483647).hexstr() == '7fffffff' | ||||
| 	assert bignum.from_int(-1).hexstr() == 'ffffffffffffffff' | ||||
| } | ||||
| 
 | ||||
| fn test_from_u64(){ | ||||
| 	assert bignum.from_u64(255).hexstr() == 'ff' | ||||
| 	assert bignum.from_u64(127).hexstr() == '7f' | ||||
| 	assert bignum.from_u64(1024).hexstr() == '400' | ||||
| 	assert bignum.from_u64(4294967295).hexstr() == 'ffffffff' | ||||
| 	assert bignum.from_u64(4398046511104).hexstr() == '40000000000' | ||||
| 	assert bignum.from_u64(-1).hexstr() == 'ffffffffffffffff' | ||||
| } | ||||
| 
 | ||||
| fn test_plus(){ | ||||
| 	a := bignum.from_u64(2) | ||||
| 	b := bignum.from_u64(3) | ||||
| 	c := a + b | ||||
| 	assert c.hexstr() == '5' | ||||
| 	assert (bignum.from_u64(1024) + bignum.from_u64(1024)).hexstr() == '800' | ||||
| } | ||||
| 
 | ||||
| fn test_minus(){ | ||||
| 	a := bignum.from_u64(2) | ||||
| 	b := bignum.from_u64(3) | ||||
| 	c := b - a | ||||
| 	assert c.hexstr() == '1' | ||||
| 	e := bignum.from_u64(1024) | ||||
| 	ee := e - e | ||||
| 	assert ee.hexstr() == '0' | ||||
| } | ||||
| 
 | ||||
| fn test_divide(){ | ||||
| 	a := bignum.from_u64(2) | ||||
| 	b := bignum.from_u64(3) | ||||
| 	c := b / a | ||||
| 	assert c.hexstr() == '1' | ||||
| 	assert (b % a ).hexstr() == '1' | ||||
| 	e := bignum.from_u64(1024) // dec(1024) == hex(0x400)
 | ||||
| 	ee := e / e | ||||
| 	assert ee.hexstr() == '1' | ||||
| 	assert (e / a).hexstr() == '200' | ||||
| 	assert (e / (a*a)).hexstr() == '100' | ||||
| } | ||||
| 
 | ||||
| fn test_multiply(){ | ||||
| 	a := bignum.from_u64(2) | ||||
| 	b := bignum.from_u64(3) | ||||
| 	c := b * a | ||||
| 	assert c.hexstr() == '6' | ||||
| 	e := bignum.from_u64(1024) | ||||
| 	e2 := e * e | ||||
| 	e4 := e2 * e2 | ||||
| 	e8 := e2 * e2 * e2 * e2 | ||||
| 	e9 := e8 + bignum.from_u64(1) | ||||
| 	d  := ((e9 * e9) + b) * c | ||||
| 	assert e4.hexstr() == '10000000000' | ||||
| 	assert e8.hexstr() == '100000000000000000000' | ||||
| 	assert e9.hexstr() == '100000000000000000001' | ||||
| 	assert d.hexstr() == '60000000000000000000c00000000000000000018' | ||||
| } | ||||
| 
 | ||||
| fn test_mod(){ | ||||
| 	assert (bignum.from_u64(13) % bignum.from_u64(10) ).int() == 3 | ||||
| 	assert (bignum.from_u64(13) % bignum.from_u64(9) ).int()  == 4 | ||||
| 	assert (bignum.from_u64(7) % bignum.from_u64(5) ).int() == 2 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fn test_factorial(){ | ||||
| 	f5 := bignum.factorial( bignum.from_u64(5) ) | ||||
| 	assert f5.hexstr() == '78' | ||||
| 	f100 := bignum.factorial( bignum.from_u64(100) ) | ||||
| 	assert f100.hexstr() == '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000' | ||||
| } | ||||
|  | @ -448,34 +448,29 @@ fn (p mut Parser) expression() string { | |||
| 		// Make sure operators are used with correct types
 | ||||
| 		if !p.pref.translated && !is_str && !is_ustr && !is_num { | ||||
| 			T := p.table.find_type(typ) | ||||
| 			if tok_op == .plus { | ||||
| 				if T.has_method('+') { | ||||
| 					p.cgen.set_placeholder(ph, typ + '_plus(') | ||||
| 					p.gen(')') | ||||
| 				} | ||||
| 				else { | ||||
| 					p.error('operator + not defined on `$typ`') | ||||
| 				} | ||||
| 			} | ||||
| 			else if tok_op == .minus { | ||||
| 				if T.has_method('-') { | ||||
| 					p.cgen.set_placeholder(ph, '${typ}_minus(') | ||||
| 					p.gen(')') | ||||
| 				} | ||||
| 				else { | ||||
| 					p.error('operator - not defined on `$typ`') | ||||
| 				} | ||||
| 			} | ||||
| 			if      tok_op == .plus {  p.handle_operator('+', typ, 'op_plus', ph, T)  } | ||||
| 			else if tok_op == .minus { p.handle_operator('-', typ, 'op_minus', ph, T) } | ||||
| 		} | ||||
| 	} | ||||
| 	return typ | ||||
| } | ||||
| 
 | ||||
| fn (p mut Parser) handle_operator(op string, typ string, cpostfix string, ph int, T &Type) { | ||||
| 	if T.has_method( op ) { | ||||
| 		p.cgen.set_placeholder(ph, '${typ}_${cpostfix}(') | ||||
| 		p.gen(')') | ||||
| 	} | ||||
| 	else { | ||||
| 		p.error('operator $op not defined on `$typ`') | ||||
| 	} | ||||
| }   | ||||
| 
 | ||||
| fn (p mut Parser) term() string { | ||||
| 	line_nr := p.scanner.line_nr | ||||
| 	//if p.fileis('fn_test') {
 | ||||
| 		//println('\nterm() $line_nr')
 | ||||
| 	//}
 | ||||
| 	ph := p.cgen.add_placeholder() | ||||
| 	typ := p.unary() | ||||
| 	//if p.fileis('fn_test') {
 | ||||
| 		//println('2: $line_nr')
 | ||||
|  | @ -491,11 +486,28 @@ fn (p mut Parser) term() string { | |||
| 		// is_mul := tok == .mod
 | ||||
| 		p.next() | ||||
| 		p.gen(tok.str())// + ' /*op2*/ ')
 | ||||
| 		oph := p.cgen.add_placeholder() | ||||
| 		p.fgen(' ' + tok.str() + ' ') | ||||
| 		if (is_div || is_mod) && p.tok == .number && p.lit == '0' { | ||||
| 			p.error('division or modulo by zero') | ||||
| 		} | ||||
| 		expr_type := p.unary() | ||||
| 
 | ||||
| 		if !is_primitive_type(expr_type) && expr_type == typ { | ||||
| 			p.check_types(expr_type, typ) | ||||
| 			T := p.table.find_type(typ) | ||||
| 			// NB: oph is a char index just after the OP
 | ||||
| 			before_oph := p.cgen.cur_line[..oph-1] | ||||
| 			after_oph := p.cgen.cur_line[oph..] | ||||
| 			p.cgen.cur_line = before_oph + ',' + after_oph | ||||
| 			match tok { | ||||
| 				.mul {   p.handle_operator('*', typ, 'op_mul', ph, T) } | ||||
| 				.div {   p.handle_operator('/', typ, 'op_div', ph, T) } | ||||
| 				.mod {   p.handle_operator('%', typ, 'op_mod', ph, T) } | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		 | ||||
| 		if is_mod { | ||||
| 			if !(is_integer_type(expr_type) && is_integer_type(typ)) { | ||||
| 				p.error('operator `mod` requires integer types') | ||||
|  |  | |||
|  | @ -265,7 +265,7 @@ fn (p mut Parser) fn_decl() { | |||
| 		p.register_var(receiver) | ||||
| 	} | ||||
| 	// +-/* methods
 | ||||
| 	if p.tok in [.plus, .minus, .mul] { | ||||
| 	if p.tok in [.plus, .minus, .mul, .div, .mod] { | ||||
| 		f.name = p.tok.str() | ||||
| 		p.next() | ||||
| 	} | ||||
|  |  | |||
|  | @ -228,9 +228,15 @@ fn (table mut Table) fn_gen_name(f &Fn) string { | |||
| 	if f.is_method { | ||||
| 		name = '${f.receiver_typ}_$f.name' | ||||
| 		name = name.replace(' ', '') | ||||
| 		name = name.replace('*', '') | ||||
| 		name = name.replace('+', 'plus') | ||||
| 		name = name.replace('-', 'minus') | ||||
| 		if f.name.len == 1 { | ||||
| 			match f.name[0] { | ||||
| 				`+` { name = name.replace('+', 'op_plus') } | ||||
| 				`-` { name = name.replace('-', 'op_minus') } | ||||
| 				`*` { name = name.replace('*', 'op_mul') } | ||||
| 				`/` { name = name.replace('/', 'op_div') } | ||||
| 				`%` { name = name.replace('%', 'op_mod') } | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Avoid name conflicts (with things like abs(), print() etc).
 | ||||
| 	// Generate v_abs(), v_print()
 | ||||
|  |  | |||
|  | @ -6,6 +6,35 @@ module math | |||
| 
 | ||||
| #include <math.h> | ||||
| 
 | ||||
| fn C.acos(x f64) f64 | ||||
| fn C.asin(x f64) f64 | ||||
| fn C.atan(x f64) f64 | ||||
| fn C.atan2(y f64, x f64) f64 | ||||
| fn C.cbrt(x f64) f64 | ||||
| fn C.ceil(x f64) f64 | ||||
| fn C.cos(x f64) f64 | ||||
| fn C.cosh(x f64) f64 | ||||
| fn C.erf(x f64) f64 | ||||
| fn C.erfc(x f64) f64 | ||||
| fn C.exp(x f64) f64 | ||||
| fn C.exp2(x f64) f64 | ||||
| fn C.floor(x f64) f64 | ||||
| fn C.fmod(x f64, y f64) f64 | ||||
| fn C.hypot(x f64, y f64) f64 | ||||
| fn C.log(x f64) f64 | ||||
| fn C.log2(x f64) f64 | ||||
| fn C.log10(x f64) f64 | ||||
| fn C.lgamma(x f64) f64 | ||||
| fn C.pow(x f64, y f64) f64 | ||||
| fn C.round(x f64) f64 | ||||
| fn C.sin(x f64) f64 | ||||
| fn C.sqrt(x f64) f64 | ||||
| fn C.tgamma(x f64) f64 | ||||
| fn C.tan(x f64) f64 | ||||
| fn C.tanh(x f64) f64 | ||||
| fn C.trunc(x f64) f64 | ||||
| 
 | ||||
| 
 | ||||
| // NOTE
 | ||||
| // When adding a new function, please make sure it's in the right place.
 | ||||
| // All functions are sorted alphabetically.
 | ||||
|  | @ -18,8 +47,6 @@ pub fn abs(a f64) f64 { | |||
| 	return a | ||||
| } | ||||
| 
 | ||||
| fn C.acos(a f64) f64 | ||||
| 
 | ||||
| // acos calculates inverse cosine (arccosine).
 | ||||
| pub fn acos(a f64) f64 { | ||||
| 	return C.acos(a) | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| import os | ||||
| 
 | ||||
| fn test_aaa_setup(){ | ||||
| 	cleanup_leftovers() assert true | ||||
| } | ||||
| 
 | ||||
| fn test_setenv() { | ||||
|   os.setenv('foo', 'bar', true) | ||||
|   assert os.getenv('foo') == 'bar' | ||||
|  | @ -114,12 +118,11 @@ fn test_walk() { | |||
| } | ||||
| 
 | ||||
| fn test_cp() { | ||||
|   $if windows { | ||||
|     old_file_name := './example.txt' | ||||
|     new_file_name := './new_example.txt' | ||||
| 
 | ||||
|     old_file_name := 'cp_example.txt' | ||||
|     new_file_name := 'cp_new_example.txt' | ||||
|      | ||||
|     os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐') | ||||
|     result := os.cp(old_file_name, new_file_name) or { panic('$err: errcode: $errcode') } | ||||
|     os.cp(old_file_name, new_file_name) or { panic('$err: errcode: $errcode') } | ||||
| 
 | ||||
|     old_file := os.read_file(old_file_name) or { panic(err) } | ||||
|     new_file := os.read_file(new_file_name) or { panic(err) } | ||||
|  | @ -127,13 +130,11 @@ fn test_cp() { | |||
| 
 | ||||
|     os.rm(old_file_name) | ||||
|     os.rm(new_file_name) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| fn test_cp_r() { | ||||
|   //fileX -> dir/fileX
 | ||||
|  // TODO clean up the files
 | ||||
|  /* | ||||
|   // NB: clean up of the files happens inside the cleanup_leftovers function
 | ||||
|   os.write_file('ex1.txt', 'wow!') | ||||
|   os.mkdir('ex') | ||||
|   os.cp_r('ex1.txt', 'ex', false) or { panic(err) } | ||||
|  | @ -148,7 +149,6 @@ fn test_cp_r() { | |||
|   assert old2 == new2 | ||||
|   //recurring on dir -> local dir
 | ||||
|   os.cp_r('ex', './', true) or { panic(err) } | ||||
|  */ | ||||
| } | ||||
| 
 | ||||
| //fn test_fork() {
 | ||||
|  | @ -173,3 +173,25 @@ fn test_cp_r() { | |||
| //    println(cpid)
 | ||||
| //  }
 | ||||
| //}
 | ||||
| 
 | ||||
| fn test_zzz_cleanup(){ | ||||
| 	cleanup_leftovers() assert true | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // this function is called by both test_aaa_setup & test_zzz_cleanup
 | ||||
| // it ensures that os tests do not polute the filesystem with leftover
 | ||||
| // files so that they can be run several times in a row.
 | ||||
| fn cleanup_leftovers(){ | ||||
| 	// possible leftovers from test_cp
 | ||||
| 	os.rm('cp_example.txt') | ||||
| 	os.rm('cp_new_example.txt') | ||||
| 	 | ||||
| 	// possible leftovers from test_cp_r
 | ||||
| 	os.rm('ex/ex2/ex2.txt') | ||||
| 	os.rm('ex/ex2') | ||||
| 	os.rm('ex/ex1.txt') | ||||
| 	os.rm('ex')   | ||||
| 	os.rm('ex2/ex2.txt') | ||||
| 	os.rm('ex2')   | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue