#define METHOD_IS(name) (!sbstrncmp(method_name, name, sizeof(name)))
+#define BIGINT_PIECE_MAX 1000000000
+
/* reserve high bit for sign bit.
* second-highest bit marks this as a handle to a bigint, which
* means normally integers have to be less than +/- 2^62. */
#define NUM_PER_BLOCK 256
-sbBuffer g_bigint_blocks = {0};
+typedef u32 Piece;
+
+/* TODO don't want to have these global i think */
+sbPool g_bigint_pool;
typedef struct bigint {
- flag allocated;
+ flag allocated : 1;
+ flag sign_bit : 1;
hInteger handle;
- flag sign_bit;
sbBuffer buf;
} bigint;
-typedef struct intblk {
- usize id;
- usize used_count;
- usize last_index;
- bigint entries[NUM_PER_BLOCK];
-} intblk;
-
flag is_bigint(hInteger n);
-static intblk *alloc_new_block();
-static intblk *find_free_block();
-static intblk *get_block(usize index);
-static bigint *find_free_entry(intblk *blk);
+static usize int_size(bigint *bi);
+static i8 int_sign(bigint *bi, hInteger h);
static void set_bigint_size(bigint *i, usize new_size);
-static bigint *find_int_for_handle(hInteger handle);
+static bigint *new_bigint_to_fit(bigint *a, bigint *b);
+static bigint *find_bigint_for_handle(hInteger handle);
static bigint *new_bigint(usize initial_size);
+static u64 int_piece(bigint *bi, hInteger h, usize index);
+static void trim_bigint(bigint *bi);
void sbInteger_sys_init() {
- sbBuffer_initialize(&g_bigint_blocks, sizeof(intblk*));
+ sbPool_initialize(&g_bigint_pool, sizeof(bigint), NUM_PER_BLOCK);
+}
+
+void sbInteger_sys_deinit() {
+ //sbPool_deinitialize(&g_bigint_pool);
}
hInteger sbInteger_new(i64 value) {
hInteger sbInteger_sum(hInteger a, hInteger b) {
if (!is_bigint(a) && !is_bigint(b)) {
- /* if the sum is too big, this will create a bigint. we don't need to
- * worry about signed overflow, because non-bigints are limited to
- * 62 bits of magnitude, so the sum of two can't be bigger than 2^63 */
return sbInteger_new(a + b);
+ } else {
+ bigint *biga = find_bigint_for_handle(a);
+ bigint *bigb = find_bigint_for_handle(b);
+ if (int_sign(biga, a) < 0 || int_sign(bigb, b) < 0) {
+ PANIC("I haven't implemented this one yet!");
+ }
+
+ bigint *result = new_bigint_to_fit(biga, bigb);
+
+ usize asize = int_size(biga);
+ usize bsize = int_size(bigb);
+ u64 carry = 0;
+ /* + 1 here because we might need to carry into another slice */
+ for (int i = 0; i < asize || i < bsize; i++) {
+ u64 a_piece = int_piece(biga, a, i);
+ u64 b_piece = int_piece(bigb, b, i);
+
+ u64 digit_sum = (u64)a_piece + (u64)b_piece + carry;
+ BUFFER_INDEX_SET(result->buf, Piece, i, digit_sum % BIGINT_PIECE_MAX);
+ carry = 0;
+ if (digit_sum > BIGINT_PIECE_MAX) {
+ carry = digit_sum / BIGINT_PIECE_MAX;
+ }
+ }
+
+ trim_bigint(result);
+
+ return result->handle;
}
- PANIC("I haven't implemented this yet!");
}
hInteger sbInteger_diff(hInteger a, hInteger b) {
}
if (is_bigint(a)) {
/* i will use this later */
- bigint *i = find_int_for_handle(a);
+ bigint *i = find_bigint_for_handle(a);
(void)i;
}
PANIC("I haven't implemented this yet!");
}
+double sbInteger_as_double(hInteger a) {
+ if (!is_bigint(a)) {
+ return (double)a;
+ } else {
+ PANIC("agh! i haven't done this!");
+ }
+}
+
hInteger sbInteger_mul(hInteger a, hInteger b) {
if (!is_bigint(a) && !is_bigint(b)) {
flag zero_ok = (a == 0);
return sbInteger_new(a * b);
}
}
- PANIC("I haven't implemented this yet!");
+
+ bigint *biga = find_bigint_for_handle(a);
+ bigint *bigb = find_bigint_for_handle(b);
+
+ usize asize = int_size(biga);
+ usize bsize = int_size(bigb);
+ bigint *result = new_bigint(asize + bsize);
+ result->sign_bit = int_sign(biga, a) < 0 || int_sign(bigb, b) < 0;
+
+ u64 carry = 0;
+ for (int bi = 0; bi < bsize; bi++) {
+ /* + 1 because we might need to carry into an extra piece after both numbers finish */
+ for (int ai = 0; ai < asize + 1; ai++) {
+ u64 a_piece = int_piece(biga, a, ai);
+ u64 b_piece = int_piece(bigb, b, bi);
+
+ u64 digit_sum = a_piece * b_piece + carry;
+ BUFFER_INDEX_SET(result->buf, Piece, ai + bi, digit_sum % BIGINT_PIECE_MAX);
+ carry = 0;
+ if (digit_sum > BIGINT_PIECE_MAX) {
+ carry = digit_sum / BIGINT_PIECE_MAX;
+ }
+ }
+ }
+
+ trim_bigint(result);
+
+ return result->handle;
}
hInteger sbInteger_floordiv(hInteger a, hInteger b) {
PANIC("I haven't implemented this yet!");
}
-void sbInteger_method(hVm vm) {
- hV *target = sbVm_pop(vm);
- if (target->type != IT_INTEGER) {
- CHECK("can't call sbInteger_method on something that isn't an integer");
- }
- hV *argc = sbVm_pop(vm);
- if (argc->type != IT_INTEGER) {
- CHECK("argc of send should be integer!");
- }
-
- /* subtract 1 because the method name is itself a param */
- usize num_params = argc->integer - 1;
- hV *method_name_val = sbVm_peek(vm, num_params);
- if (method_name_val->type != IT_SYMBOL) {
- /* TODO this may become not true */
- PANIC("method name for list must be symbol!");
- }
-
- const char *method_name = sbSymbol_name(method_name_val->symbol);
- /* TODO: Need a better way of resolving these */
- /* also TODO shouldn't be 'to_string' */
- if (METHOD_IS("to_string")) {
+void sbInteger_fprint(FILE *out, hInteger a) {
+ if (!is_bigint(a)) {
+ fprintf(out, "%lld", (long long)a);
} else {
- PANIC("unknown method name for integer");
+ bigint *big = find_bigint_for_handle(a);
+ i8 sgn = int_sign(big, a);
+ if (sgn == 0) {
+ fprintf(out, "0");
+ return;
+ } else if (sgn < 0) {
+ fprintf(out, "-");
+ }
+ isize sz = int_size(big);
+ for (isize i = sz - 1; i >= 0; i--) {
+ if (i == sz - 1) {
+ fprintf(out, "%lld", (long long)int_piece(big, a, i));
+ } else {
+ fprintf(out, "%09lld", (long long)int_piece(big, a, i));
+ }
+ }
}
}
return (n > 0 && (n & FLAG_BIGINT)) || (n < 0 && !(n & FLAG_BIGINT));
}
-static intblk *alloc_new_block() {
- usize nblocks = g_bigint_blocks.size / sizeof(intblk*);
- intblk *new_block = calloc(1, sizeof(intblk));
- new_block->id = nblocks;
- sbBuffer_append(&g_bigint_blocks, &new_block, sizeof(intblk*));
- return new_block;
-}
-
-static intblk *find_free_block() {
- usize nblocks = g_bigint_blocks.size / sizeof(intblk*);
- intblk *free_block = NULL;
- for (usize i = 0; i < nblocks; i++) {
- if (((intblk**)g_bigint_blocks.data)[i]->used_count < NUM_PER_BLOCK) {
- free_block = ((intblk**)g_bigint_blocks.data)[i];
- break;
- }
- }
-
- if (free_block == NULL) {
- free_block = alloc_new_block();
- }
-
- return free_block;
+static void set_bigint_size(bigint *bi, usize new_size) {
+ sbBuffer_set_size(&bi->buf, new_size * sizeof(Piece));
}
-static intblk *get_block(usize index) {
- usize nblocks = g_bigint_blocks.size / sizeof(intblk*);
- if (index > nblocks) PANIC("request for a bigint block (#%zu) that does not exist!", index);
- return ((intblk**)g_bigint_blocks.data)[index];
+static usize int_size(bigint *bi) {
+ if (bi == NULL) return 1;
+ return bi->buf.size / sizeof(Piece);
}
-static bigint *find_free_entry(intblk *blk) {
- if (blk->used_count >= NUM_PER_BLOCK) PANIC("request for new entry in full intblk!");
-
- while (blk->entries[blk->last_index].allocated) {
- blk->last_index++;
- blk->last_index %= NUM_PER_BLOCK;
+static i8 int_sign(bigint *bi, hInteger h) {
+ if (bi == NULL) {
+ if (h == 0) {
+ return 0;
+ } else if (h < 0) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } else {
+ /* scan to see if it is zero, but short circuit if it isn't */
+ usize len = int_size(bi);
+ for (usize i = 0; i < len; i++) {
+ if (int_piece(bi, h, i) != 0) {
+ if (bi->sign_bit) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ }
+ return 0;
}
- blk->entries[blk->last_index].allocated = 1;
- blk->used_count ++;
-
- bigint *result = &blk->entries[blk->last_index];
-
- result->handle = ((blk->id * NUM_PER_BLOCK + blk->last_index) | FLAG_BIGINT);
-
- return result;
}
-static void set_bigint_size(bigint *i, usize new_size) {
- sbBuffer_set_size(&i->buf, new_size * sizeof(u32));
-}
-
-static bigint *find_int_for_handle(hInteger handle) {
- hInteger h = handle & ~FLAG_BIGINT;
- intblk *block = get_block(h / NUM_PER_BLOCK);
- return &block->entries[h % NUM_PER_BLOCK];
+static bigint *find_bigint_for_handle(hInteger handle) {
+ if (!is_bigint(handle)) {
+ return NULL;
+ } else {
+ hInteger h = handle & ~FLAG_BIGINT;
+ return sbPool_get_entry(&g_bigint_pool, h);
+ }
}
static bigint *new_bigint(usize initial_size) {
- bigint *i = find_free_entry(find_free_block());
+ usize index;
+ bigint *i = sbPool_alloc(&g_bigint_pool, &index);
+ sbBuffer_initialize(&i->buf, initial_size * sizeof(u32));
set_bigint_size(i, initial_size);
+ i->handle = index | FLAG_BIGINT;
return i;
}
+static bigint *new_bigint_to_fit(bigint *a, bigint *b) {
+ usize a_size = int_size(a);
+ usize b_size = int_size(b);
+ return new_bigint(a_size > b_size ? a_size + 1 : b_size + 1);
+}
+static void trim_bigint(bigint *a) {
+ if (!a) return;
+ usize trim_count = 0;
+ usize sz = int_size(a);
+ for (isize i = sz - 1; i >= 0; i --) {
+ if (BUFFER_INDEX(a->buf, Piece, i) != 0) {
+ break;
+ }
+ trim_count ++;
+ }
+ if (trim_count > 0) {
+ set_bigint_size(a, sz - trim_count);
+ }
+}
+
+static u64 int_piece(bigint *bi, hInteger h, usize index) {
+ if (!bi) {
+ if (index == 0) {
+ return h;
+ } else {
+ return 0;
+ }
+ } else if (index < int_size(bi)) {
+ return BUFFER_INDEX(bi->buf, Piece, index);
+ } else {
+ return 0;
+ }
+}