mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-12 22:31:25 -07:00
big: add shl1, shr1.
This commit is contained in:
+127
-1
@@ -8,7 +8,7 @@ package big
|
||||
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
|
||||
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||
|
||||
This file contains basic arithmetic operations like `add` and `sub`.
|
||||
This file contains basic arithmetic operations like `add`, `sub`, `div`, ...
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
@@ -337,6 +337,132 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
||||
return clamp(dest);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
dest = src / 2
|
||||
dest = src >> 1
|
||||
*/
|
||||
int_halve :: proc(dest, src: ^Int) -> (err: Error) {
|
||||
dest := dest; src := src;
|
||||
if err = clear_if_uninitialized(dest); err != .None {
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
Grow destination as required.
|
||||
*/
|
||||
if dest != src {
|
||||
if err = grow(dest, src.used); err != .None {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
old_used := dest.used;
|
||||
dest.used = src.used;
|
||||
|
||||
/*
|
||||
Carry
|
||||
*/
|
||||
fwd_carry := DIGIT(0);
|
||||
|
||||
for x := dest.used; x >= 0; x -= 1 {
|
||||
/*
|
||||
Get the carry for the next iteration.
|
||||
*/
|
||||
src_digit := src.digit[x];
|
||||
carry := src_digit & 1;
|
||||
/*
|
||||
Shift the current digit, add in carry and store.
|
||||
*/
|
||||
dest.digit[x] = (src_digit >> 1) | (fwd_carry << (_DIGIT_BITS - 1));
|
||||
/*
|
||||
Forward carry to next iteration.
|
||||
*/
|
||||
fwd_carry = carry;
|
||||
}
|
||||
|
||||
zero_count := old_used - dest.used;
|
||||
/*
|
||||
Zero remainder.
|
||||
*/
|
||||
if zero_count > 0 {
|
||||
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
||||
}
|
||||
/*
|
||||
Adjust dest.used based on leading zeroes.
|
||||
*/
|
||||
dest.sign = src.sign;
|
||||
return clamp(dest);
|
||||
}
|
||||
halve :: proc { int_halve, };
|
||||
shr1 :: halve;
|
||||
|
||||
/*
|
||||
dest = src * 2
|
||||
dest = src << 1
|
||||
*/
|
||||
int_double :: proc(dest, src: ^Int) -> (err: Error) {
|
||||
dest := dest; src := src;
|
||||
if err = clear_if_uninitialized(dest); err != .None {
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
Grow destination as required.
|
||||
*/
|
||||
if dest != src {
|
||||
if err = grow(dest, src.used + 1); err != .None {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
old_used := dest.used;
|
||||
dest.used = src.used + 1;
|
||||
|
||||
/*
|
||||
Forward carry
|
||||
*/
|
||||
carry := DIGIT(0);
|
||||
for x := 0; x < src.used; x += 1 {
|
||||
/*
|
||||
Get what will be the *next* carry bit from the MSB of the current digit.
|
||||
*/
|
||||
src_digit := src.digit[x];
|
||||
fwd_carry := src_digit >> (_DIGIT_BITS - 1);
|
||||
|
||||
/*
|
||||
Now shift up this digit, add in the carry [from the previous]
|
||||
*/
|
||||
dest.digit[x] = (src_digit << 1 | carry) & _MASK;
|
||||
|
||||
/*
|
||||
Update carry
|
||||
*/
|
||||
carry = fwd_carry;
|
||||
}
|
||||
/*
|
||||
New leading digit?
|
||||
*/
|
||||
if carry != 0 {
|
||||
/*
|
||||
Add a MSB which is always 1 at this point.
|
||||
*/
|
||||
dest.digit[dest.used] = 1;
|
||||
}
|
||||
zero_count := old_used - dest.used;
|
||||
/*
|
||||
Zero remainder.
|
||||
*/
|
||||
if zero_count > 0 {
|
||||
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
||||
}
|
||||
/*
|
||||
Adjust dest.used based on leading zeroes.
|
||||
*/
|
||||
dest.sign = src.sign;
|
||||
return clamp(dest);
|
||||
}
|
||||
double :: proc { int_double, };
|
||||
shl1 :: double;
|
||||
|
||||
/*
|
||||
==========================
|
||||
Low-level routines
|
||||
|
||||
@@ -58,12 +58,17 @@ demo :: proc() {
|
||||
defer destroy(a, b, c);
|
||||
|
||||
err = set(a, 512);
|
||||
err = set(b, a);
|
||||
err = set(b, 1);
|
||||
for x := 0; x < 11; x += 1 {
|
||||
err = shl1(b, b);
|
||||
print("b", b, 16);
|
||||
}
|
||||
fmt.println();
|
||||
err = set(c, -4);
|
||||
|
||||
print("a", a, 2);
|
||||
print("b", b, 2);
|
||||
print("c", c, 2);
|
||||
print("a", a, 10);
|
||||
print("b", b, 10);
|
||||
print("c", c, 10);
|
||||
|
||||
fmt.println("=== a = a & b ===");
|
||||
err = and(a, a, b);
|
||||
|
||||
@@ -11,6 +11,7 @@ package big
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
|
||||
/*
|
||||
Deallocates the backing memory of one or more `Int`s.
|
||||
*/
|
||||
@@ -366,6 +367,49 @@ count_bits :: proc(a: ^Int) -> (count: int, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Counts the number of LSBs which are zero before the first zero bit
|
||||
*/
|
||||
count_lsb :: proc(a: ^Int) -> (count: int, err: Error) {
|
||||
if err = clear_if_uninitialized(a); err != .None {
|
||||
return 0, err;
|
||||
}
|
||||
|
||||
lnz := []u8{4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
|
||||
|
||||
q: DIGIT;
|
||||
|
||||
/*
|
||||
Early out for zero.
|
||||
*/
|
||||
if z, _ := is_zero(a); z {
|
||||
return 0, .None;
|
||||
}
|
||||
|
||||
/*
|
||||
Scan lower digits until non-zero.
|
||||
*/
|
||||
for count = 0; (count < a.used && a.digit[count] == 0); count += 1 {}
|
||||
q = a.digit[count];
|
||||
count *= _DIGIT_BITS;
|
||||
|
||||
/*
|
||||
Now scan this digit until a 1 is found.
|
||||
*/
|
||||
if q & 1 == 0 {
|
||||
p: DIGIT;
|
||||
for {
|
||||
p = q & 15;
|
||||
count += int(lnz[p]);
|
||||
q >>= 4;
|
||||
if p != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count, .None;
|
||||
}
|
||||
|
||||
/*
|
||||
Internal helpers.
|
||||
*/
|
||||
@@ -402,7 +446,6 @@ clamp :: proc(a: ^Int) -> (err: Error) {
|
||||
if err = clear_if_uninitialized(a); err != .None {
|
||||
return err;
|
||||
}
|
||||
|
||||
for a.used > 0 && a.digit[a.used - 1] == 0 {
|
||||
a.used -= 1;
|
||||
}
|
||||
@@ -410,6 +453,5 @@ clamp :: proc(a: ^Int) -> (err: Error) {
|
||||
if z, _ := is_zero(a); z {
|
||||
a.sign = .Zero_or_Positive;
|
||||
}
|
||||
|
||||
return .None;
|
||||
}
|
||||
@@ -212,4 +212,36 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) {
|
||||
dest.used = used;
|
||||
dest.sign = .Negative if neg else .Zero_or_Positive;
|
||||
return clamp(dest);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
dest = ~src
|
||||
*/
|
||||
int_complement :: proc(dest, src: ^Int) -> (err: Error) {
|
||||
/*
|
||||
Check that src and dest are usable.
|
||||
*/
|
||||
if err = clear_if_uninitialized(src); err != .None {
|
||||
return err;
|
||||
}
|
||||
if err = clear_if_uninitialized(dest); err != .None {
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
Temporarily fix sign.
|
||||
*/
|
||||
old_sign := src.sign;
|
||||
z, _ := is_zero(src);
|
||||
|
||||
src.sign = .Negative if (src.sign == .Zero_or_Positive || z) else .Zero_or_Positive;
|
||||
|
||||
err = sub(dest, src, 1);
|
||||
/*
|
||||
Restore sign.
|
||||
*/
|
||||
src.sign = old_sign;
|
||||
|
||||
return err;
|
||||
}
|
||||
complement :: proc { int_complement, };
|
||||
Reference in New Issue
Block a user